{% extends "base.html" %}

{% block title %}Unit Detail - Seismo Fleet Manager{% endblock %}

{% block content %}
<div class="mb-6">
    <!-- Breadcrumb Navigation -->
    <nav class="flex mb-4 text-sm" aria-label="Breadcrumb">
        <ol class="inline-flex items-center space-x-1 md:space-x-3">
            <li class="inline-flex items-center">
                <a href="/" class="text-gray-500 hover:text-seismo-orange dark:text-gray-400">
                    Dashboard
                </a>
            </li>
            <li>
                <div class="flex items-center">
                    <svg class="w-4 h-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
                        <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
                    </svg>
                    <a href="/roster" class="ml-1 text-gray-500 hover:text-seismo-orange dark:text-gray-400">
                        Fleet Roster
                    </a>
                </div>
            </li>
            <li aria-current="page">
                <div class="flex items-center">
                    <svg class="w-4 h-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
                        <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
                    </svg>
                    <span class="ml-1 text-gray-700 dark:text-gray-300 font-medium" id="breadcrumb-unit">Unit</span>
                </div>
            </li>
        </ol>
    </nav>

    <div class="flex justify-between items-center">
        <div class="flex items-center gap-2">
            <h1 class="text-3xl font-bold text-gray-900 dark:text-white" id="pageTitle">Loading...</h1>
            <button onclick="copyToClipboard(window.currentUnitId, this)" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors p-1" title="Copy Unit ID">
                <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
                </svg>
            </button>
        </div>
        <div class="flex gap-3">
            <button id="editButton" onclick="enterEditMode()" class="px-4 py-2 bg-seismo-orange hover:bg-orange-600 text-white rounded-lg transition-colors flex items-center gap-2">
                <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
                </svg>
                Edit Unit
            </button>
            <button onclick="deleteUnit()" class="px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-lg transition-colors flex items-center gap-2">
                <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
                </svg>
                Delete Unit
            </button>
        </div>
    </div>
</div>

<!-- Loading state -->
<div id="loadingState" class="rounded-xl shadow-lg bg-white dark:bg-slate-800 p-12 text-center">
    <div class="animate-pulse">
        <div class="h-4 bg-gray-200 dark:bg-gray-700 rounded w-3/4 mx-auto mb-4"></div>
        <div class="h-4 bg-gray-200 dark:bg-gray-700 rounded w-1/2 mx-auto"></div>
    </div>
</div>

<!-- Main content (hidden until loaded) -->
<div id="mainContent" class="hidden space-y-6">
    <!-- Status Card -->
    <div class="rounded-xl shadow-lg bg-white dark:bg-slate-800 p-6">
        <div class="flex justify-between items-start mb-6">
            <h2 class="text-xl font-semibold text-gray-900 dark:text-white">Status</h2>
            <div class="flex items-center space-x-2">
                <span id="statusIndicator" class="w-3 h-3 rounded-full"></span>
                <span id="statusText" class="font-semibold"></span>
            </div>
        </div>
        <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
            <div>
                <span class="text-sm text-gray-500 dark:text-gray-400">Last Seen</span>
                <p id="lastSeen" class="font-medium text-gray-900 dark:text-white">--</p>
            </div>
            <div>
                <span class="text-sm text-gray-500 dark:text-gray-400">Age</span>
                <p id="age" class="font-medium text-gray-900 dark:text-white">--</p>
            </div>
            <div>
                <span class="text-sm text-gray-500 dark:text-gray-400">Deployed</span>
                <p id="deployedStatus" class="font-medium text-gray-900 dark:text-white">--</p>
            </div>
            <div>
                <span class="text-sm text-gray-500 dark:text-gray-400">Unit Status</span>
                <p id="retiredStatus" class="font-medium text-gray-900 dark:text-white">--</p>
            </div>
        </div>
    </div>

    <!-- Location Map -->
    <div id="mapCard" class="rounded-xl shadow-lg bg-white dark:bg-slate-800 p-6 hidden">
        <h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">Location</h2>
        <div id="unit-map" style="height: 400px; width: 100%;" class="rounded-lg mb-4"></div>
        <p id="locationText" class="text-sm text-gray-500 dark:text-gray-400"></p>
    </div>

    <!-- View Mode: Unit Information (Default) -->
    <div id="viewMode" class="rounded-xl shadow-lg bg-white dark:bg-slate-800 p-6">
        <h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-6">Unit Information</h2>

        <div class="space-y-6">
            <!-- Basic Info Grid -->
            <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                <div>
                    <label class="text-sm font-medium text-gray-500 dark:text-gray-400">Device Type</label>
                    <p id="viewDeviceType" class="mt-1 text-gray-900 dark:text-white font-medium">--</p>
                </div>
                <div>
                    <label class="text-sm font-medium text-gray-500 dark:text-gray-400">Unit Type</label>
                    <p id="viewUnitType" class="mt-1 text-gray-900 dark:text-white font-medium">--</p>
                </div>
                <div>
                    <label class="text-sm font-medium text-gray-500 dark:text-gray-400">Project</label>
                    <p id="viewProjectContainer" class="mt-1">
                        <a id="viewProjectLink" href="#" class="text-seismo-orange hover:text-orange-600 font-medium hover:underline hidden">
                            <span id="viewProjectText">--</span>
                        </a>
                        <span id="viewProjectNoLink" class="text-gray-900 dark:text-white font-medium">Not assigned</span>
                    </p>
                </div>
                <div>
                    <label class="text-sm font-medium text-gray-500 dark:text-gray-400">Deployment Location</label>
                    <p id="viewLocationContainer" class="mt-1">
                        <a id="viewLocationLink" href="#" class="text-seismo-orange hover:text-orange-600 font-medium hover:underline hidden">
                            <span id="viewLocationText">--</span>
                        </a>
                        <span id="viewLocationNoLink" class="text-gray-500 dark:text-gray-400 italic">Not deployed</span>
                    </p>
                </div>
                <div>
                    <label class="text-sm font-medium text-gray-500 dark:text-gray-400">Address</label>
                    <p id="viewAddress" class="mt-1 text-gray-900 dark:text-white font-medium">--</p>
                </div>
                <div class="md:col-span-2">
                    <label class="text-sm font-medium text-gray-500 dark:text-gray-400">Coordinates</label>
                    <p id="viewCoordinates" class="mt-1 text-gray-900 dark:text-white font-medium font-mono text-sm">--</p>
                </div>
            </div>

            <!-- Seismograph Info -->
            <div id="viewSeismographFields" class="border-t border-gray-200 dark:border-gray-700 pt-6">
                <h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Seismograph Information</h3>
                <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                    <div>
                        <label class="text-sm font-medium text-gray-500 dark:text-gray-400">Date of Last Calibration</label>
                        <p id="viewLastCalibrated" class="mt-1 text-gray-900 dark:text-white font-medium">--</p>
                    </div>
                    <div>
                        <label class="text-sm font-medium text-gray-500 dark:text-gray-400">Next Calibration Due</label>
                        <p id="viewNextCalibrationDue" class="mt-1 text-gray-900 dark:text-white font-medium inline-flex items-center gap-2">--</p>
                    </div>
                    <div>
                        <label class="text-sm font-medium text-gray-500 dark:text-gray-400">Deployed With Modem</label>
                        <p id="viewDeployedWithModemContainer" class="mt-1">
                            <a id="viewDeployedWithModemLink" href="#" class="text-seismo-orange hover:text-orange-600 font-medium hover:underline hidden">
                                <span id="viewDeployedWithModemText">--</span>
                            </a>
                            <span id="viewDeployedWithModemNoLink" class="text-gray-900 dark:text-white font-medium">--</span>
                        </p>
                    </div>
                </div>
            </div>

            <!-- Modem Info -->
            <div id="viewModemFields" class="hidden border-t border-gray-200 dark:border-gray-700 pt-6">
                <h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Modem Information</h3>
                <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                    <div>
                        <label class="text-sm font-medium text-gray-500 dark:text-gray-400">IP Address</label>
                        <p id="viewIpAddress" class="mt-1 text-gray-900 dark:text-white font-medium">--</p>
                    </div>
                    <div>
                        <label class="text-sm font-medium text-gray-500 dark:text-gray-400">Phone Number</label>
                        <p id="viewPhoneNumber" class="mt-1 text-gray-900 dark:text-white font-medium">--</p>
                    </div>
                    <div>
                        <label class="text-sm font-medium text-gray-500 dark:text-gray-400">Hardware Model</label>
                        <p id="viewHardwareModel" class="mt-1 text-gray-900 dark:text-white font-medium">--</p>
                    </div>
                    <div id="viewModemLoginSection" class="hidden">
                        <label class="text-sm font-medium text-gray-500 dark:text-gray-400">Management Interface</label>
                        <p class="mt-1">
                            <a id="viewModemLoginLink" href="#" target="_blank"
                                class="inline-flex items-center gap-2 text-seismo-orange hover:text-orange-600 font-medium">
                                <span id="viewModemLoginText">--</span>
                                <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path>
                                </svg>
                            </a>
                        </p>
                    </div>
                </div>
            </div>

            <!-- Sound Level Meter Info -->
            <div id="viewSlmFields" class="hidden border-t border-gray-200 dark:border-gray-700 pt-6">
                <h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Sound Level Meter Information</h3>
                <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                    <div>
                        <label class="text-sm font-medium text-gray-500 dark:text-gray-400">Model</label>
                        <p id="viewSlmModel" class="mt-1 text-gray-900 dark:text-white font-medium">--</p>
                    </div>
                    <div>
                        <label class="text-sm font-medium text-gray-500 dark:text-gray-400">Serial Number</label>
                        <p id="viewSlmSerialNumber" class="mt-1 text-gray-900 dark:text-white font-medium">--</p>
                    </div>
                    <div>
                        <label class="text-sm font-medium text-gray-500 dark:text-gray-400">Frequency Weighting</label>
                        <p id="viewSlmFrequencyWeighting" class="mt-1 text-gray-900 dark:text-white font-medium">--</p>
                    </div>
                    <div>
                        <label class="text-sm font-medium text-gray-500 dark:text-gray-400">Time Weighting</label>
                        <p id="viewSlmTimeWeighting" class="mt-1 text-gray-900 dark:text-white font-medium">--</p>
                    </div>
                    <div>
                        <label class="text-sm font-medium text-gray-500 dark:text-gray-400">Measurement Range</label>
                        <p id="viewSlmMeasurementRange" class="mt-1 text-gray-900 dark:text-white font-medium">--</p>
                    </div>
                    <div>
                        <label class="text-sm font-medium text-gray-500 dark:text-gray-400">Deployed With Modem</label>
                        <p id="viewSlmDeployedWithModemContainer" class="mt-1">
                            <a id="viewSlmDeployedWithModemLink" href="#" class="text-seismo-orange hover:text-orange-600 font-medium hover:underline hidden">
                                <span id="viewSlmDeployedWithModemText">--</span>
                            </a>
                            <span id="viewSlmDeployedWithModemNoLink" class="text-gray-900 dark:text-white font-medium">--</span>
                        </p>
                    </div>
                </div>
            </div>

            <!-- Paired Device (for modems only) -->
            <div id="viewPairedDeviceSection" class="hidden border-t border-gray-200 dark:border-gray-700 pt-6">
                <h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Paired Device</h3>
                <div id="pairedDeviceInfo">
                    <p class="text-sm text-gray-500 dark:text-gray-400">Loading...</p>
                </div>
            </div>

            <!-- Connectivity (for modems only) -->
            <div id="viewConnectivitySection" class="hidden border-t border-gray-200 dark:border-gray-700 pt-6">
                <h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Connectivity</h3>
                <div class="flex items-center gap-4 mb-4">
                    <button onclick="pingModem()" id="modemPingBtn"
                            class="px-4 py-2 bg-blue-100 hover:bg-blue-200 text-blue-700 dark:bg-blue-900/30 dark:hover:bg-blue-900/50 dark:text-blue-300 rounded-lg flex items-center gap-2 transition-colors">
                        <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0"></path>
                        </svg>
                        Ping Test
                    </button>
                    <span id="modemPingResult" class="text-sm text-gray-500 dark:text-gray-400">--</span>
                </div>

                <!-- Future Diagnostics Placeholders -->
                <div class="grid grid-cols-1 md:grid-cols-3 gap-4 opacity-60">
                    <div class="bg-gray-100 dark:bg-gray-700 rounded-lg p-4">
                        <p class="text-xs text-gray-500 dark:text-gray-400">Signal Strength</p>
                        <p class="text-xl font-semibold text-gray-400 dark:text-gray-500">-- dBm</p>
                        <p class="text-xs text-gray-400 dark:text-gray-500 mt-1">ModemManager pending</p>
                    </div>
                    <div class="bg-gray-100 dark:bg-gray-700 rounded-lg p-4">
                        <p class="text-xs text-gray-500 dark:text-gray-400">Data Usage</p>
                        <p class="text-xl font-semibold text-gray-400 dark:text-gray-500">-- MB</p>
                        <p class="text-xs text-gray-400 dark:text-gray-500 mt-1">ModemManager pending</p>
                    </div>
                    <div class="bg-gray-100 dark:bg-gray-700 rounded-lg p-4">
                        <p class="text-xs text-gray-500 dark:text-gray-400">Uptime</p>
                        <p class="text-xl font-semibold text-gray-400 dark:text-gray-500">--</p>
                        <p class="text-xs text-gray-400 dark:text-gray-500 mt-1">ModemManager pending</p>
                    </div>
                </div>
            </div>

            <!-- Notes -->
            <div class="border-t border-gray-200 dark:border-gray-700 pt-6">
                <label class="text-sm font-medium text-gray-500 dark:text-gray-400">Notes</label>
                <p id="viewNote" class="mt-1 text-gray-900 dark:text-white whitespace-pre-wrap">--</p>
            </div>

            <!-- Deployment Timeline (Phase 4 unified view — derived from
                 unit_assignments + unit_history + SFM event overlay) -->
            <div class="border-t border-gray-200 dark:border-gray-700 pt-6">
                <div class="flex justify-between items-center mb-4 flex-wrap gap-2">
                    <h3 class="text-lg font-semibold text-gray-900 dark:text-white">Deployment Timeline</h3>
                    <div class="flex items-center gap-2">
                        <button onclick="openAddAssignmentModal()" class="px-3 py-1.5 text-sm rounded-lg bg-seismo-orange hover:bg-orange-600 text-white">
                            + Add deployment record
                        </button>
                        <button onclick="loadDeploymentTimeline()" class="px-3 py-1.5 text-sm rounded-lg border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300">
                            ↻ Refresh
                        </button>
                    </div>
                </div>

                <!-- Gantt chart — visual timeline of all deployments.  Click
                     a bar to jump to its row in the list below. -->
                <div id="deploymentGantt" class="mb-4 hidden">
                    <div class="bg-gray-50 dark:bg-slate-900/40 rounded-lg p-3">
                        <svg id="deploymentGanttSvg" class="w-full" style="height: 140px;" preserveAspectRatio="none"></svg>
                        <div id="deploymentGanttLegend" class="flex flex-wrap items-center gap-x-4 gap-y-1 mt-2 text-xs text-gray-500 dark:text-gray-400"></div>
                    </div>
                </div>

                <div id="deploymentTimeline" class="space-y-3">
                    <p class="text-sm text-gray-500 dark:text-gray-400">Loading timeline…</p>
                </div>
            </div>

            <!-- Edit-assignment modal -->
            <div id="editAssignmentModal" class="hidden fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4">
                <div class="bg-white dark:bg-slate-800 rounded-xl shadow-2xl w-full max-w-md max-h-[90vh] overflow-y-auto">
                    <div class="sticky top-0 bg-white dark:bg-slate-800 border-b border-gray-200 dark:border-gray-700 px-5 py-4 flex items-center justify-between">
                        <h3 class="text-lg font-semibold text-gray-900 dark:text-white">Edit deployment record</h3>
                        <button onclick="closeEditAssignmentModal()" class="text-2xl text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">&times;</button>
                    </div>
                    <div class="p-5 space-y-4">
                        <div class="text-sm text-gray-500 dark:text-gray-400">
                            <span id="editAssignmentLocation">—</span>
                            <span class="text-xs">·</span>
                            <span id="editAssignmentProject" class="text-xs">—</span>
                        </div>
                        <label class="block">
                            <span class="text-sm font-medium text-gray-700 dark:text-gray-300">Assigned at</span>
                            <input id="editAssignedAt" type="datetime-local" step="60"
                                   class="mt-1 w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white">
                        </label>
                        <label class="block">
                            <span class="text-sm font-medium text-gray-700 dark:text-gray-300">Assigned until</span>
                            <div class="mt-1 flex items-center gap-2">
                                <input id="editAssignedUntil" type="datetime-local" step="60"
                                       class="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white">
                                <label class="inline-flex items-center gap-1 text-xs text-gray-700 dark:text-gray-300 whitespace-nowrap">
                                    <input id="editAssignedUntilOpen" type="checkbox" onchange="_toggleEditOpenEnded()">
                                    open-ended
                                </label>
                            </div>
                            <p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Check "open-ended" to mark this assignment active (no end date).</p>
                        </label>
                        <label class="block">
                            <span class="text-sm font-medium text-gray-700 dark:text-gray-300">Notes</span>
                            <textarea id="editAssignmentNotes" rows="2"
                                      class="mt-1 w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white"></textarea>
                        </label>
                        <div id="editAssignmentError" class="hidden text-sm text-red-600"></div>
                    </div>
                    <div class="sticky bottom-0 bg-white dark:bg-slate-800 border-t border-gray-200 dark:border-gray-700 px-5 py-3 flex items-center justify-between gap-2">
                        <button onclick="deleteAssignmentFromModal()" class="px-3 py-2 text-sm rounded-lg border border-red-300 text-red-700 dark:border-red-700 dark:text-red-300 hover:bg-red-50 dark:hover:bg-red-900/20">
                            Delete
                        </button>
                        <div class="flex gap-2">
                            <button onclick="closeEditAssignmentModal()" class="px-4 py-2 text-sm rounded-lg border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700">
                                Cancel
                            </button>
                            <button id="editAssignmentSaveBtn" onclick="saveEditAssignment()" class="px-4 py-2 text-sm rounded-lg bg-seismo-orange hover:bg-orange-600 text-white">
                                Save
                            </button>
                        </div>
                    </div>
                </div>
            </div>

            <!-- Add-historical-assignment modal -->
            <div id="addAssignmentModal" class="hidden fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4">
                <div class="bg-white dark:bg-slate-800 rounded-xl shadow-2xl w-full max-w-md max-h-[90vh] overflow-y-auto">
                    <div class="sticky top-0 bg-white dark:bg-slate-800 border-b border-gray-200 dark:border-gray-700 px-5 py-4 flex items-center justify-between">
                        <h3 class="text-lg font-semibold text-gray-900 dark:text-white">Add deployment record</h3>
                        <button onclick="closeAddAssignmentModal()" class="text-2xl text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">&times;</button>
                    </div>
                    <div class="p-5 space-y-4">
                        <p class="text-xs text-gray-500 dark:text-gray-400">
                            Create a deployment record for this unit — usually to backfill a historical window so orphan events get attributed.
                        </p>
                        <label class="block">
                            <span class="text-sm font-medium text-gray-700 dark:text-gray-300">Project</span>
                            <select id="addAssignmentProject" onchange="_addAssignmentProjectChanged()"
                                    class="mt-1 w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white">
                                <option value="">Loading…</option>
                            </select>
                        </label>
                        <label class="block">
                            <span class="text-sm font-medium text-gray-700 dark:text-gray-300">Location</span>
                            <select id="addAssignmentLocation"
                                    class="mt-1 w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white" disabled>
                                <option value="">Pick a project first</option>
                            </select>
                        </label>
                        <label class="block">
                            <span class="text-sm font-medium text-gray-700 dark:text-gray-300">Assigned at</span>
                            <input id="addAssignedAt" type="datetime-local" step="60"
                                   class="mt-1 w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white">
                        </label>
                        <label class="block">
                            <span class="text-sm font-medium text-gray-700 dark:text-gray-300">Assigned until</span>
                            <div class="mt-1 flex items-center gap-2">
                                <input id="addAssignedUntil" type="datetime-local" step="60"
                                       class="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white">
                                <label class="inline-flex items-center gap-1 text-xs text-gray-700 dark:text-gray-300 whitespace-nowrap">
                                    <input id="addAssignedUntilOpen" type="checkbox" onchange="_toggleAddOpenEnded()">
                                    open-ended
                                </label>
                            </div>
                        </label>
                        <label class="block">
                            <span class="text-sm font-medium text-gray-700 dark:text-gray-300">Notes</span>
                            <textarea id="addAssignmentNotes" rows="2"
                                      placeholder="e.g. Backfilled to attribute orphan events 2026-03-16 — 2026-03-25"
                                      class="mt-1 w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white"></textarea>
                        </label>
                        <div id="addAssignmentError" class="hidden text-sm text-red-600"></div>
                    </div>
                    <div class="sticky bottom-0 bg-white dark:bg-slate-800 border-t border-gray-200 dark:border-gray-700 px-5 py-3 flex justify-end gap-2">
                        <button onclick="closeAddAssignmentModal()" class="px-4 py-2 text-sm rounded-lg border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700">
                            Cancel
                        </button>
                        <button id="addAssignmentSaveBtn" onclick="saveAddAssignment()" class="px-4 py-2 text-sm rounded-lg bg-seismo-orange hover:bg-orange-600 text-white">
                            Create
                        </button>
                    </div>
                </div>
            </div>

            <!-- SFM Events (seismographs only) -->
            <div id="sfmEventsSection" class="border-t border-gray-200 dark:border-gray-700 pt-6 hidden">
                <div class="flex justify-between items-center mb-4">
                    <h3 class="text-lg font-semibold text-gray-900 dark:text-white">SFM Events</h3>
                    <button onclick="loadUnitEvents()" class="px-3 py-1.5 text-sm rounded-lg border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300">
                        ↻ Refresh
                    </button>
                </div>

                <!-- KPI tiles -->
                <div class="grid grid-cols-2 md:grid-cols-4 gap-3 mb-4">
                    <div class="bg-gray-50 dark:bg-slate-900/50 rounded-lg p-3 flex flex-col">
                        <span class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider">Total Events</span>
                        <span id="ue-stat-total" class="text-2xl font-bold text-gray-900 dark:text-white mt-1">—</span>
                    </div>
                    <div class="bg-gray-50 dark:bg-slate-900/50 rounded-lg p-3 flex flex-col">
                        <span class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider">Unattributed</span>
                        <span id="ue-stat-unattr" class="text-2xl font-bold text-gray-900 dark:text-white mt-1">—</span>
                        <span class="text-xs text-gray-500 dark:text-gray-400 mt-1">outside any assignment window</span>
                    </div>
                    <div class="bg-gray-50 dark:bg-slate-900/50 rounded-lg p-3 flex flex-col">
                        <span class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider">Overall Peak</span>
                        <span id="ue-stat-peak" class="text-2xl font-bold text-gray-900 dark:text-white mt-1">—</span>
                        <span id="ue-stat-peak-when" class="text-xs text-gray-500 dark:text-gray-400 mt-1">—</span>
                    </div>
                    <div class="bg-gray-50 dark:bg-slate-900/50 rounded-lg p-3 flex flex-col">
                        <span class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider">Last Event</span>
                        <span id="ue-stat-last" class="text-base font-bold text-gray-900 dark:text-white mt-1">—</span>
                    </div>
                </div>

                <!-- Filters -->
                <div class="flex flex-wrap items-end gap-3 mb-4">
                    <div class="flex flex-col gap-1">
                        <label class="text-xs text-gray-500 dark:text-gray-400">Bucket</label>
                        <select id="ue-filter-bucket" onchange="loadUnitEvents()"
                                class="px-3 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white">
                            <option value="all">All Events</option>
                            <option value="attributed">Attributed Only</option>
                            <option value="unattributed">Unattributed Only</option>
                        </select>
                    </div>
                    <div class="flex flex-col gap-1">
                        <label class="text-xs text-gray-500 dark:text-gray-400">From</label>
                        <input type="datetime-local" id="ue-filter-from" onchange="loadUnitEvents()"
                               class="px-3 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white">
                    </div>
                    <div class="flex flex-col gap-1">
                        <label class="text-xs text-gray-500 dark:text-gray-400">To</label>
                        <input type="datetime-local" id="ue-filter-to" onchange="loadUnitEvents()"
                               class="px-3 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white">
                    </div>
                    <div class="flex flex-col gap-1">
                        <label class="text-xs text-gray-500 dark:text-gray-400">False Triggers</label>
                        <select id="ue-filter-ft" onchange="loadUnitEvents()"
                                class="px-3 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white">
                            <option value="">All</option>
                            <option value="false">Real Only</option>
                            <option value="true">FT Only</option>
                        </select>
                    </div>
                    <div class="flex flex-col gap-1">
                        <label class="text-xs text-gray-500 dark:text-gray-400">Limit</label>
                        <select id="ue-filter-limit" onchange="loadUnitEvents()"
                                class="px-3 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white">
                            <option value="100">100</option>
                            <option value="250">250</option>
                            <option value="500" selected>500</option>
                            <option value="1000">1000</option>
                        </select>
                    </div>
                    <button onclick="clearUnitEventFilters()"
                            class="px-3 py-1.5 text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white">
                        Clear
                    </button>
                </div>

                <!-- Bulk false-trigger flagging -->
                <div id="ue-bulk-actions" class="flex flex-wrap items-center gap-2 mb-3 text-sm">
                    <span id="ue-bulk-selected" class="text-gray-600 dark:text-gray-400">0 selected</span>
                    <button id="ue-bulk-flag-ft" onclick="flagSelectedUnitEvents(true)" disabled
                            class="px-3 py-1.5 text-sm rounded-lg border border-yellow-300 dark:border-yellow-700 text-yellow-700 dark:text-yellow-300 hover:bg-yellow-50 dark:hover:bg-yellow-900/30 disabled:opacity-40 disabled:cursor-not-allowed">
                        🚩 Flag as false trigger
                    </button>
                    <button id="ue-bulk-clear-ft" onclick="flagSelectedUnitEvents(false)" disabled
                            class="px-3 py-1.5 text-sm rounded-lg border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-40 disabled:cursor-not-allowed">
                        ✓ Clear false trigger
                    </button>
                    <span class="text-xs text-gray-500 dark:text-gray-400 ml-2">For deletion / DB cleanup, use the <a href="/admin/events" class="text-seismo-orange hover:underline">Event DB Manager</a>.</span>
                </div>

                <!-- Event table -->
                <div id="ue-events-container" class="overflow-x-auto rounded-lg border border-gray-200 dark:border-gray-700">
                    <div class="text-center py-12 text-gray-500 dark:text-gray-400 text-sm">
                        Loading events…
                    </div>
                </div>
            </div>


            <!-- Photos -->
            <div class="border-t border-gray-200 dark:border-gray-700 pt-6">
                <div class="flex justify-between items-start mb-4">
                    <h3 class="text-lg font-semibold text-gray-900 dark:text-white">Photos</h3>
                    <div class="flex flex-col sm:flex-row gap-2">
                        <!-- Take Photo Button (Camera) -->
                        <label for="photoCameraUpload" class="px-4 py-2 bg-seismo-orange hover:bg-orange-600 text-white rounded-lg transition-colors cursor-pointer flex items-center gap-2 text-sm sm:text-base">
                            <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z"></path>
                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 13a3 3 0 11-6 0 3 3 0 016 0z"></path>
                            </svg>
                            <span class="hidden sm:inline">Take Photo</span>
                            <span class="sm:hidden">Camera</span>
                        </label>
                        <!-- Choose from Library Button -->
                        <label for="photoLibraryUpload" class="px-4 py-2 bg-seismo-navy hover:bg-blue-800 text-white rounded-lg transition-colors cursor-pointer flex items-center gap-2 text-sm sm:text-base">
                            <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
                            </svg>
                            <span class="hidden sm:inline">Choose Photo</span>
                            <span class="sm:hidden">Library</span>
                        </label>
                        <input type="file" id="photoCameraUpload" accept="image/*" capture="environment" class="hidden" onchange="uploadPhoto(this.files[0])">
                        <input type="file" id="photoLibraryUpload" accept="image/*" class="hidden" onchange="uploadPhoto(this.files[0])">
                    </div>
                </div>
                <div id="photoGallery" class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
                    <p class="text-sm text-gray-500 dark:text-gray-400 col-span-full">Loading photos...</p>
                </div>
                <div id="uploadStatus" class="hidden mt-4 p-4 rounded-lg"></div>
            </div>
        </div>
    </div>

    <!-- Deployment Modal -->
    <div id="deploymentModal" class="hidden fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
        <div class="bg-white dark:bg-slate-800 rounded-xl shadow-2xl w-full max-w-lg">
            <div class="flex justify-between items-center p-6 border-b border-gray-200 dark:border-gray-700">
                <h3 id="deploymentModalTitle" class="text-lg font-semibold text-gray-900 dark:text-white">Log Deployment</h3>
                <button onclick="closeDeploymentModal()" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
                    <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
                    </svg>
                </button>
            </div>
            <div class="p-6 space-y-4">
                <input type="hidden" id="deploymentModalId">
                <div class="grid grid-cols-2 gap-4">
                    <div>
                        <label class="text-sm font-medium text-gray-700 dark:text-gray-300">Deployed Date</label>
                        <input type="date" id="deploymentDeployedDate" class="mt-1 w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white text-sm focus:ring-2 focus:ring-seismo-orange">
                    </div>
                    <div>
                        <label class="text-sm font-medium text-gray-700 dark:text-gray-300">Est. Removal Date</label>
                        <input type="date" id="deploymentEstRemovalDate" class="mt-1 w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white text-sm focus:ring-2 focus:ring-seismo-orange">
                    </div>
                </div>
                <div id="actualRemovalRow">
                    <label class="text-sm font-medium text-gray-700 dark:text-gray-300">Actual Removal Date <span class="text-gray-400 font-normal">(fill when returned)</span></label>
                    <input type="date" id="deploymentActualRemovalDate" class="mt-1 w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white text-sm focus:ring-2 focus:ring-seismo-orange">
                </div>
                <div>
                    <label class="text-sm font-medium text-gray-700 dark:text-gray-300">Job / Project</label>
                    <input type="text" id="deploymentProjectRef" placeholder="e.g. Fay I-80, CMU Campus" class="mt-1 w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white text-sm focus:ring-2 focus:ring-seismo-orange">
                </div>
                <div>
                    <label class="text-sm font-medium text-gray-700 dark:text-gray-300">Location Name</label>
                    <input type="text" id="deploymentLocationName" placeholder="e.g. North Gate, VP-001" class="mt-1 w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white text-sm focus:ring-2 focus:ring-seismo-orange">
                </div>
                <div>
                    <label class="text-sm font-medium text-gray-700 dark:text-gray-300">Notes</label>
                    <textarea id="deploymentNotes" rows="2" class="mt-1 w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white text-sm focus:ring-2 focus:ring-seismo-orange resize-none"></textarea>
                </div>
            </div>
            <div class="flex justify-end gap-3 p-6 border-t border-gray-200 dark:border-gray-700">
                <button onclick="closeDeploymentModal()" class="px-4 py-2 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-slate-700 rounded-lg text-sm transition-colors">Cancel</button>
                <button onclick="saveDeployment()" class="px-4 py-2 bg-seismo-orange hover:bg-orange-600 text-white rounded-lg text-sm transition-colors">Save</button>
            </div>
        </div>
    </div>

    <!-- Edit Mode: Unit Information Form (Hidden by default) -->
    <div id="editMode" class="hidden rounded-xl shadow-lg bg-white dark:bg-slate-800 p-6">
        <div class="flex justify-between items-center mb-6">
            <h2 class="text-xl font-semibold text-gray-900 dark:text-white">Edit Unit Information</h2>
            <button onclick="cancelEdit()" class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
                <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
                </svg>
            </button>
        </div>

        <form id="editForm" class="space-y-6">
            <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                <!-- Device Type -->
                <div>
                    <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Device Type</label>
                    <select name="device_type" id="deviceType" onchange="toggleDetailFields()"
                        class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
                        <option value="seismograph">Seismograph</option>
                        <option value="modem">Modem</option>
                        <option value="slm">Sound Level Meter</option>
                    </select>
                </div>

                <!-- Unit Type -->
                <div>
                    <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Unit Type</label>
                    <input type="text" name="unit_type" id="unitType"
                        class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
                </div>

                <!-- Project -->
                <div>
                    <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Project</label>
                    {% set picker_id = "-detail" %}
                    {% include "partials/project_picker.html" with context %}
                </div>

                <!-- Address / coordinates are managed on the project's
                     MonitoringLocation, not the unit itself.  Edit them on
                     the project page. -->
                <div class="md:col-span-2 rounded-lg bg-gray-50 dark:bg-slate-700/50 border border-gray-200 dark:border-gray-700 p-3 text-sm text-gray-600 dark:text-gray-400">
                    Address &amp; coordinates are set on the deployment location.
                    Open the project to edit them.
                </div>
            </div>

            <!-- Seismograph Fields -->
            <div id="seismographFields" class="space-y-4 border-t border-gray-200 dark:border-gray-700 pt-4">
                <h3 class="text-lg font-semibold text-gray-900 dark:text-white">Seismograph Information</h3>
                <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                    <div>
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Date of Last Calibration</label>
                        <input type="date" name="last_calibrated" id="lastCalibrated"
                            class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
                        <p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Next calibration due date will be calculated automatically</p>
                    </div>
                    <input type="hidden" name="next_calibration_due" id="nextCalibrationDue">
                    <div>
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Deployed With Modem</label>
                        <div class="flex gap-2">
                            <div class="flex-1">
                                {% set picker_id = "-detail-seismo" %}
                                {% set input_name = "deployed_with_modem_id" %}
                                {% include "partials/modem_picker.html" with context %}
                            </div>
                            <button type="button" onclick="openPairDeviceModal('seismograph')"
                                class="px-3 py-2 bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 rounded-lg transition-colors flex items-center gap-1"
                                title="Pair with modem">
                                <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"></path>
                                </svg>
                            </button>
                        </div>
                    </div>
                </div>
            </div>

            <!-- Modem Fields -->
            <div id="modemFields" class="hidden space-y-4 border-t border-gray-200 dark:border-gray-700 pt-4">
                <h3 class="text-lg font-semibold text-gray-900 dark:text-white">Modem Information</h3>
                <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                    <div>
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">IP Address</label>
                        <input type="text" name="ip_address" id="ipAddress" placeholder="192.168.1.100"
                            class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Phone Number</label>
                        <input type="text" name="phone_number" id="phoneNumber" placeholder="+1-555-0123"
                            class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Hardware Model</label>
                        <select name="hardware_model" id="hardwareModel"
                            class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
                            <option value="">Select model...</option>
                            <option value="RV50">RV50</option>
                            <option value="RV55">RV55</option>
                            <option value="RX55">RX55</option>
                        </select>
                    </div>
                </div>
            </div>

            <!-- Sound Level Meter Fields -->
            <div id="slmFields" class="hidden space-y-4 border-t border-gray-200 dark:border-gray-700 pt-4">
                <h3 class="text-lg font-semibold text-gray-900 dark:text-white">Sound Level Meter Information</h3>
                <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                    <div>
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Model</label>
                        <input type="text" name="slm_model" id="slmModel" placeholder="NL-43, NL-53, etc."
                            class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Serial Number</label>
                        <input type="text" name="slm_serial_number" id="slmSerialNumber" placeholder="123456"
                            class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Frequency Weighting</label>
                        <select name="slm_frequency_weighting" id="slmFrequencyWeighting"
                            class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
                            <option value="">Not set</option>
                            <option value="A">A-weighting</option>
                            <option value="C">C-weighting</option>
                            <option value="Z">Z-weighting (Flat)</option>
                        </select>
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Time Weighting</label>
                        <select name="slm_time_weighting" id="slmTimeWeighting"
                            class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
                            <option value="">Not set</option>
                            <option value="F">Fast (125ms)</option>
                            <option value="S">Slow (1s)</option>
                            <option value="I">Impulse (35ms)</option>
                        </select>
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Measurement Range</label>
                        <input type="text" name="slm_measurement_range" id="slmMeasurementRange" placeholder="30-130 dB"
                            class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">TCP Port (on modem)</label>
                        <input type="number" name="slm_tcp_port" id="slmTcpPort" placeholder="2255"
                            class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
                        <p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Default: 2255</p>
                    </div>
                    <div class="md:col-span-2">
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Deployed With Modem</label>
                        <div class="flex gap-2">
                            <div class="flex-1">
                                {% set picker_id = "-detail-slm" %}
                                {% set input_name = "deployed_with_modem_id" %}
                                {% include "partials/modem_picker.html" with context %}
                            </div>
                            <button type="button" onclick="openPairDeviceModal('slm')"
                                class="px-3 py-2 bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 rounded-lg transition-colors flex items-center gap-1"
                                title="Pair with modem">
                                <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"></path>
                                </svg>
                            </button>
                        </div>
                        <p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Select the modem that provides network connectivity for this SLM</p>
                    </div>
                </div>
            </div>

            <!-- Status Checkboxes -->
            <div class="border-t border-gray-200 dark:border-gray-700 pt-4 space-y-3">
                <div class="flex items-center gap-6 flex-wrap">
                    <label class="flex items-center gap-2 cursor-pointer">
                        <input type="checkbox" name="deployed" id="deployed" value="true"
                            class="w-4 h-4 text-seismo-orange focus:ring-seismo-orange rounded">
                        <span class="text-sm text-gray-700 dark:text-gray-300">Deployed</span>
                    </label>
                    <label class="flex items-center gap-2 cursor-pointer">
                        <input type="checkbox" name="out_for_calibration" id="outForCalibration" value="true"
                            class="w-4 h-4 text-purple-600 focus:ring-purple-500 rounded">
                        <span class="text-sm text-gray-700 dark:text-gray-300">Out for Calibration</span>
                    </label>
                    <label class="flex items-center gap-2 cursor-pointer">
                        <input type="checkbox" name="allocated" id="allocated" value="true"
                            onchange="document.getElementById('allocatedProjectRow').style.display = this.checked ? '' : 'none'"
                            class="w-4 h-4 text-orange-500 focus:ring-orange-400 rounded">
                        <span class="text-sm text-gray-700 dark:text-gray-300">Allocated</span>
                    </label>
                </div>
                <div id="allocatedProjectRow" style="display:none">
                    <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Allocated to Project</label>
                    <input type="text" name="allocated_to_project_id" id="allocatedToProjectId"
                        placeholder="Project name or ID"
                        class="w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-orange-400 text-sm">
                </div>
                <!-- Hidden field for retired — controlled by the Retire button below -->
                <input type="hidden" name="retired" id="retired" value="">
                <div id="retireButtonSection">
                    <button type="button" id="retireBtn"
                        class="px-4 py-2 text-sm font-medium rounded-lg border transition-colors"
                        onclick="toggleRetired()">
                    </button>
                </div>
            </div>

            <!-- Notes -->
            <div>
                <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Notes</label>
                <textarea name="note" id="note" rows="4"
                    class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange"></textarea>
            </div>

            <!-- Cascade to Paired Device Section -->
            <div id="detailCascadeSection" class="hidden border-t border-gray-200 dark:border-gray-700 pt-4">
                <div class="flex items-center gap-2 mb-3">
                    <svg class="w-5 h-5 text-seismo-orange" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"></path>
                    </svg>
                    <span class="text-sm font-semibold text-gray-700 dark:text-gray-300">
                        Also update paired device: <span id="detailPairedDeviceName" class="text-seismo-orange"></span>
                    </span>
                </div>
                <input type="hidden" name="cascade_to_unit_id" id="detailCascadeToUnitId" value="">
                <div class="grid grid-cols-2 md:grid-cols-3 gap-2 bg-gray-50 dark:bg-slate-700/50 rounded-lg p-3">
                    <label class="flex items-center gap-2 cursor-pointer">
                        <input type="checkbox" name="cascade_deployed" id="detailCascadeDeployed" value="true"
                            class="w-4 h-4 text-seismo-orange focus:ring-seismo-orange rounded">
                        <span class="text-sm text-gray-700 dark:text-gray-300">Deployed status</span>
                    </label>
                    <label class="flex items-center gap-2 cursor-pointer">
                        <input type="checkbox" name="cascade_retired" id="detailCascadeRetired" value="true"
                            class="w-4 h-4 text-seismo-orange focus:ring-seismo-orange rounded">
                        <span class="text-sm text-gray-700 dark:text-gray-300">Retired status</span>
                    </label>
                    <label class="flex items-center gap-2 cursor-pointer">
                        <input type="checkbox" name="cascade_project" id="detailCascadeProject" value="true"
                            class="w-4 h-4 text-seismo-orange focus:ring-seismo-orange rounded">
                        <span class="text-sm text-gray-700 dark:text-gray-300">Project</span>
                    </label>
                    <label class="flex items-center gap-2 cursor-pointer">
                        <input type="checkbox" name="cascade_note" id="detailCascadeNote" value="true"
                            class="w-4 h-4 text-seismo-orange focus:ring-seismo-orange rounded">
                        <span class="text-sm text-gray-700 dark:text-gray-300">Notes</span>
                    </label>
                </div>
                <p class="text-xs text-gray-500 dark:text-gray-400 mt-2">
                    Check the fields you want to sync to the paired device
                </p>
            </div>

            <!-- Save/Cancel Buttons -->
            <div class="flex gap-3">
                <button type="submit" class="flex-1 px-6 py-3 bg-seismo-orange hover:bg-orange-600 text-white rounded-lg font-medium transition-colors">
                    Save Changes
                </button>
                <button type="button" onclick="cancelEdit()" class="px-6 py-3 bg-gray-300 dark:bg-gray-600 hover:bg-gray-400 dark:hover:bg-gray-500 text-gray-700 dark:text-white rounded-lg font-medium transition-colors">
                    Cancel
                </button>
            </div>
        </form>
    </div>
</div>

<script>
const unitId = "{{ unit_id }}";
let currentUnit = null;
let currentSnapshot = null;
let unitMap = null;
let mapMarker = null;

// Calibration interval in days (default 365, will be loaded from preferences)
let calibrationIntervalDays = 365;

// Load calibration interval from preferences
async function loadCalibrationInterval() {
    try {
        const response = await fetch('/api/settings/preferences');
        if (response.ok) {
            const prefs = await response.json();
            calibrationIntervalDays = prefs.calibration_interval_days || 365;
        }
    } catch (e) {
        console.error('Failed to load calibration interval:', e);
    }
}

// Calculate next calibration due date from last calibrated date
function calculateNextCalibrationDue(lastCalibratedStr) {
    if (!lastCalibratedStr) return '';
    const lastCalibrated = new Date(lastCalibratedStr);
    const nextDue = new Date(lastCalibrated);
    nextDue.setDate(nextDue.getDate() + calibrationIntervalDays);
    return nextDue.toISOString().split('T')[0];
}

// Setup auto-calculation for calibration fields
function setupCalibrationAutoCalc() {
    const lastCal = document.getElementById('lastCalibrated');
    const nextCal = document.getElementById('nextCalibrationDue');
    if (lastCal && nextCal) {
        lastCal.addEventListener('change', function() {
            nextCal.value = calculateNextCalibrationDue(this.value);
        });
    }
}

// Fetch project display name (combines project_number, client_name, name)
async function fetchProjectDisplay(projectId) {
    if (!projectId) return '';
    try {
        const response = await fetch(`/api/projects/${projectId}`);
        if (response.ok) {
            const project = await response.json();
            const parts = [
                project.project_number,
                project.client_name,
                project.name
            ].filter(Boolean);
            return parts.join(' - ') || projectId;
        }
    } catch (e) {
        console.error('Failed to fetch project:', e);
    }
    return projectId;
}

// Fetch modem display name (combines id, ip_address, hardware_model)
// Also returns the actual modem ID if found (for updating picker value)
async function fetchModemDisplay(modemIdOrIp) {
    if (!modemIdOrIp) return { display: '', modemId: '' };
    try {
        // First try direct lookup by ID
        let response = await fetch(`/api/roster/${encodeURIComponent(modemIdOrIp)}`);
        if (response.ok) {
            const modem = await response.json();
            const parts = [modem.id];
            if (modem.ip_address) parts.push(`(${modem.ip_address})`);
            if (modem.hardware_model) parts.push(`- ${modem.hardware_model}`);
            return { display: parts.join(' ') || modemIdOrIp, modemId: modem.id };
        }

        // If not found, maybe it's an IP address - search for it
        response = await fetch(`/api/roster/search/modems?q=${encodeURIComponent(modemIdOrIp)}`);
        if (response.ok) {
            // The search returns HTML, so we need to look up differently
            // Try fetching all modems and find by IP
            const modemsResponse = await fetch('/api/roster/modems');
            if (modemsResponse.ok) {
                const modems = await modemsResponse.json();
                const modem = modems.find(m => m.ip_address === modemIdOrIp);
                if (modem) {
                    const parts = [modem.id];
                    if (modem.ip_address) parts.push(`(${modem.ip_address})`);
                    if (modem.hardware_model) parts.push(`- ${modem.hardware_model}`);
                    return { display: parts.join(' ') || modemIdOrIp, modemId: modem.id };
                }
            }
        }
    } catch (e) {
        console.error('Failed to fetch modem:', e);
    }
    return { display: modemIdOrIp, modemId: modemIdOrIp };
}

// Format weighting values for display
function formatWeighting(value, type) {
    if (!value) return null;
    if (type === 'frequency') {
        const labels = { 'A': 'A-weighting', 'C': 'C-weighting', 'Z': 'Z-weighting (Flat)' };
        return labels[value] || value;
    } else if (type === 'time') {
        const labels = { 'F': 'Fast (125ms)', 'S': 'Slow (1s)', 'I': 'Impulse (35ms)' };
        return labels[value] || value;
    }
    return value;
}

// Load unit data on page load
async function loadUnitData() {
    try {
        // Fetch unit roster data
        const rosterResponse = await fetch(`/api/roster/${unitId}`);
        if (!rosterResponse.ok) {
            throw new Error('Unit not found');
        }
        currentUnit = await rosterResponse.json();

        // Fetch snapshot data for status info
        const snapshotResponse = await fetch('/api/status-snapshot');
        if (snapshotResponse.ok) {
            currentSnapshot = await snapshotResponse.json();
        }

        // Load modems list for dropdown
        await loadModemsList();

        // Populate views
        populateViewMode();
        populateEditForm();

        // Hide loading, show content
        document.getElementById('loadingState').classList.add('hidden');
        document.getElementById('mainContent').classList.remove('hidden');

        // Initialize map after content is visible
        setTimeout(() => {
            initUnitMap();
        }, 100);
    } catch (error) {
        alert(`Error loading unit: ${error.message}`);
        window.location.href = '/roster';
    }
}

// Load list of modems for dropdown
async function loadModemsList() {
    try {
        const response = await fetch('/api/roster/modems');
        if (response.ok) {
            const modems = await response.json();

            // Populate both seismograph and SLM modem dropdowns
            const seismoDropdown = document.getElementById('deployedWithModemId');
            const slmDropdown = document.getElementById('slmDeployedWithModemId');

            // Clear existing options (except the first "No modem" option)
            [seismoDropdown, slmDropdown].forEach(dropdown => {
                if (!dropdown) return;
                while (dropdown.options.length > 1) {
                    dropdown.remove(1);
                }

                // Add modem options
                modems.forEach(modem => {
                    const option = document.createElement('option');
                    option.value = modem.id;
                    option.textContent = `${modem.id}${modem.ip_address ? ' (' + modem.ip_address + ')' : ''}${modem.hardware_model ? ' - ' + modem.hardware_model : ''}`;
                    dropdown.appendChild(option);
                });
            });
        }
    } catch (error) {
        console.error('Failed to load modems list:', error);
    }
}

// Populate view mode (read-only display)
function populateViewMode() {
    // Update page title and store unit ID for copy function
    window.currentUnitId = currentUnit.id;
    document.getElementById('pageTitle').textContent = `Unit ${currentUnit.id}`;
    document.getElementById('breadcrumb-unit').textContent = currentUnit.id;

    // Get status info from snapshot
    let unitStatus = null;
    if (currentSnapshot && currentSnapshot.units) {
        unitStatus = currentSnapshot.units[unitId];
    }

    // Status card
    if (unitStatus) {
        const statusColors = {
            'OK': 'bg-green-500',
            'Pending': 'bg-yellow-500',
            'Missing': 'bg-red-500'
        };
        const statusTextColors = {
            'OK': 'text-green-600 dark:text-green-400',
            'Pending': 'text-yellow-600 dark:text-yellow-400',
            'Missing': 'text-red-600 dark:text-red-400'
        };

        // If unit is not deployed (benched), show gray "Benched" status instead of health status
        if (!currentUnit.deployed) {
            document.getElementById('statusIndicator').className = 'w-3 h-3 rounded-full bg-gray-400 dark:bg-gray-500';
            document.getElementById('statusText').className = 'font-semibold text-gray-600 dark:text-gray-400';
            document.getElementById('statusText').textContent = 'Benched';
        } else {
            document.getElementById('statusIndicator').className = `w-3 h-3 rounded-full ${statusColors[unitStatus.status] || 'bg-gray-400'}`;
            document.getElementById('statusText').className = `font-semibold ${statusTextColors[unitStatus.status] || 'text-gray-600'}`;
            document.getElementById('statusText').textContent = unitStatus.status || 'Unknown';
        }

        // Format "Last Seen" with timezone-aware formatting
        if (unitStatus.last && typeof formatFullTimestamp === 'function') {
            document.getElementById('lastSeen').textContent = formatFullTimestamp(unitStatus.last);
        } else {
            document.getElementById('lastSeen').textContent = unitStatus.last || '--';
        }

        document.getElementById('age').textContent = unitStatus.age || '--';
    } else {
        const isAllocated = currentUnit.allocated && !currentUnit.deployed;
        document.getElementById('statusIndicator').className = isAllocated
            ? 'w-3 h-3 rounded-full bg-orange-400'
            : 'w-3 h-3 rounded-full bg-gray-400';
        document.getElementById('statusText').className = isAllocated
            ? 'font-semibold text-orange-500 dark:text-orange-400'
            : 'font-semibold text-gray-600 dark:text-gray-400';
        document.getElementById('statusText').textContent = isAllocated ? 'Allocated' : (!currentUnit.deployed ? 'Benched' : 'No status data');
        document.getElementById('lastSeen').textContent = '--';
        document.getElementById('age').textContent = '--';
    }

    document.getElementById('deployedStatus').textContent = currentUnit.deployed ? 'Yes' : 'No';
    if (currentUnit.retired) {
        document.getElementById('retiredStatus').textContent = 'Retired';
        document.getElementById('retiredStatus').className = 'font-medium text-red-600 dark:text-red-400';
    } else if (currentUnit.out_for_calibration) {
        document.getElementById('retiredStatus').textContent = 'Out for Calibration';
        document.getElementById('retiredStatus').className = 'font-medium text-purple-600 dark:text-purple-400';
    } else if (currentUnit.allocated && !currentUnit.deployed) {
        document.getElementById('retiredStatus').textContent = currentUnit.allocated_to_project_id
            ? `Allocated — ${currentUnit.allocated_to_project_id}`
            : 'Allocated';
        document.getElementById('retiredStatus').className = 'font-medium text-orange-500 dark:text-orange-400';
    } else {
        document.getElementById('retiredStatus').textContent = 'Active';
        document.getElementById('retiredStatus').className = 'font-medium text-gray-900 dark:text-white';
    }

    // Basic info
    document.getElementById('viewDeviceType').textContent = currentUnit.device_type || '--';
    document.getElementById('viewUnitType').textContent = currentUnit.unit_type || '--';

    // Project display with clickable link
    const projectId = currentUnit.project_id;
    const projectLink = document.getElementById('viewProjectLink');
    const projectNoLink = document.getElementById('viewProjectNoLink');
    const projectText = document.getElementById('viewProjectText');

    if (projectId) {
        // Fetch project display name and show link
        fetchProjectDisplay(projectId).then(displayText => {
            if (projectText) projectText.textContent = displayText;
            if (projectLink) {
                projectLink.href = `/projects/${projectId}`;
                projectLink.classList.remove('hidden');
            }
            if (projectNoLink) projectNoLink.classList.add('hidden');
        });
    } else {
        if (projectNoLink) {
            projectNoLink.textContent = 'Not assigned';
            projectNoLink.classList.remove('hidden');
        }
        if (projectLink) projectLink.classList.add('hidden');
    }

    // Deployment Location — comes from the active UnitAssignment →
    // MonitoringLocation.  Show project link if present, otherwise
    // "Not deployed" placeholder.
    const locLink   = document.getElementById('viewLocationLink');
    const locText   = document.getElementById('viewLocationText');
    const locNoLink = document.getElementById('viewLocationNoLink');
    const activeLoc = currentUnit.active_location;
    if (activeLoc && activeLoc.location_id) {
        if (locText) locText.textContent = activeLoc.name || activeLoc.address || 'Active location';
        if (locLink) {
            locLink.href = `/projects/${activeLoc.project_id}`;
            locLink.classList.remove('hidden');
        }
        if (locNoLink) locNoLink.classList.add('hidden');
    } else {
        if (locLink) locLink.classList.add('hidden');
        if (locNoLink) locNoLink.classList.remove('hidden');
    }

    // Address / coordinates also come from the active assignment.
    document.getElementById('viewAddress').textContent = (activeLoc && activeLoc.address) || '--';
    document.getElementById('viewCoordinates').textContent = (activeLoc && activeLoc.coordinates) || '--';

    // Seismograph fields
    document.getElementById('viewLastCalibrated').textContent = currentUnit.last_calibrated || '--';

    // Calculate next calibration due and show with status indicator
    const nextCalDueEl = document.getElementById('viewNextCalibrationDue');
    if (currentUnit.last_calibrated) {
        const nextDue = calculateNextCalibrationDue(currentUnit.last_calibrated);
        const today = new Date().toISOString().split('T')[0];
        const daysUntil = Math.floor((new Date(nextDue) - new Date(today)) / (1000 * 60 * 60 * 24));

        let dotColor = 'bg-green-500';
        let tooltip = `Calibration valid (${daysUntil} days remaining)`;
        if (daysUntil < 0) {
            dotColor = 'bg-red-500';
            tooltip = `Calibration expired ${-daysUntil} days ago`;
        } else if (daysUntil <= 14) {
            dotColor = 'bg-yellow-500';
            tooltip = `Calibration expires in ${daysUntil} days`;
        }

        nextCalDueEl.innerHTML = `<span class="w-2 h-2 rounded-full ${dotColor}" title="${tooltip}"></span>${nextDue}`;
    } else {
        nextCalDueEl.textContent = '--';
    }

    // Deployed with modem - show as clickable link
    const modemLink = document.getElementById('viewDeployedWithModemLink');
    const modemNoLink = document.getElementById('viewDeployedWithModemNoLink');
    const modemText = document.getElementById('viewDeployedWithModemText');

    if (currentUnit.deployed_with_modem_id) {
        // Fetch modem info to get the actual ID (in case stored as IP) and display text
        fetchModemDisplay(currentUnit.deployed_with_modem_id).then(result => {
            if (modemText) modemText.textContent = result.display;
            if (modemLink) {
                modemLink.href = `/unit/${encodeURIComponent(result.modemId)}`;
                modemLink.classList.remove('hidden');
            }
            if (modemNoLink) modemNoLink.classList.add('hidden');
        });
    } else {
        if (modemNoLink) {
            modemNoLink.textContent = '--';
            modemNoLink.classList.remove('hidden');
        }
        if (modemLink) modemLink.classList.add('hidden');
    }

    // Modem fields
    document.getElementById('viewIpAddress').textContent = currentUnit.ip_address || '--';
    document.getElementById('viewPhoneNumber').textContent = currentUnit.phone_number || '--';
    document.getElementById('viewHardwareModel').textContent = currentUnit.hardware_model || '--';

    // Modem management interface link
    const modemLoginSection = document.getElementById('viewModemLoginSection');
    const modemLoginLink = document.getElementById('viewModemLoginLink');
    const modemLoginText = document.getElementById('viewModemLoginText');

    if (currentUnit.ip_address && currentUnit.hardware_model) {
        let loginUrl = '';
        let loginLabel = '';

        if (currentUnit.hardware_model === 'RV50' || currentUnit.hardware_model === 'RV55') {
            // ACEmanager uses port 9191
            loginUrl = `http://${currentUnit.ip_address}:9191`;
            loginLabel = 'ACEmanager';
        } else if (currentUnit.hardware_model === 'RX55') {
            // AirLink uses HTTPS on port 443
            loginUrl = `https://${currentUnit.ip_address}:443`;
            loginLabel = 'AirLink';
        }

        if (loginUrl) {
            modemLoginLink.href = loginUrl;
            modemLoginText.textContent = loginLabel;
            modemLoginSection.classList.remove('hidden');
        } else {
            modemLoginSection.classList.add('hidden');
        }
    } else {
        modemLoginSection.classList.add('hidden');
    }

    // Notes
    document.getElementById('viewNote').textContent = currentUnit.note || '--';

    // Show/hide fields based on device type
    // Hide all device-specific sections first
    document.getElementById('viewSeismographFields').classList.add('hidden');
    document.getElementById('viewModemFields').classList.add('hidden');
    document.getElementById('viewSlmFields').classList.add('hidden');
    document.getElementById('viewPairedDeviceSection').classList.add('hidden');
    document.getElementById('viewConnectivitySection').classList.add('hidden');

    if (currentUnit.device_type === 'modem') {
        document.getElementById('viewModemFields').classList.remove('hidden');
        document.getElementById('viewPairedDeviceSection').classList.remove('hidden');
        document.getElementById('viewConnectivitySection').classList.remove('hidden');
        // Load paired device info
        loadPairedDevice();
    } else if (currentUnit.device_type === 'slm') {
        document.getElementById('viewSlmFields').classList.remove('hidden');
        // Populate SLM view fields
        document.getElementById('viewSlmModel').textContent = currentUnit.slm_model || '--';
        document.getElementById('viewSlmSerialNumber').textContent = currentUnit.slm_serial_number || '--';
        document.getElementById('viewSlmFrequencyWeighting').textContent = formatWeighting(currentUnit.slm_frequency_weighting, 'frequency') || '--';
        document.getElementById('viewSlmTimeWeighting').textContent = formatWeighting(currentUnit.slm_time_weighting, 'time') || '--';
        document.getElementById('viewSlmMeasurementRange').textContent = currentUnit.slm_measurement_range || '--';

        // Handle SLM modem link
        const slmModemLink = document.getElementById('viewSlmDeployedWithModemLink');
        const slmModemNoLink = document.getElementById('viewSlmDeployedWithModemNoLink');
        const slmModemText = document.getElementById('viewSlmDeployedWithModemText');

        if (currentUnit.deployed_with_modem_id) {
            fetchModemDisplay(currentUnit.deployed_with_modem_id).then(result => {
                if (slmModemText) slmModemText.textContent = result.display;
                if (slmModemLink) {
                    slmModemLink.href = `/unit/${encodeURIComponent(result.modemId)}`;
                    slmModemLink.classList.remove('hidden');
                }
                if (slmModemNoLink) slmModemNoLink.classList.add('hidden');
            });
        } else {
            if (slmModemNoLink) {
                slmModemNoLink.textContent = '--';
                slmModemNoLink.classList.remove('hidden');
            }
            if (slmModemLink) slmModemLink.classList.add('hidden');
        }
    } else {
        // Seismograph (default)
        document.getElementById('viewSeismographFields').classList.remove('hidden');
    }
}

// Populate edit form
function populateEditForm() {
    document.getElementById('deviceType').value = currentUnit.device_type || 'seismograph';
    document.getElementById('unitType').value = currentUnit.unit_type || '';

    // Populate project picker (uses -detail suffix)
    const projectPickerValue = document.getElementById('project-picker-value-detail');
    const projectPickerSearch = document.getElementById('project-picker-search-detail');
    const projectPickerClear = document.getElementById('project-picker-clear-detail');
    if (projectPickerValue) projectPickerValue.value = currentUnit.project_id || '';
    if (currentUnit.project_id) {
        fetchProjectDisplay(currentUnit.project_id).then(displayText => {
            if (projectPickerSearch) projectPickerSearch.value = displayText;
            if (projectPickerClear) projectPickerClear.classList.remove('hidden');
        });
    } else {
        if (projectPickerSearch) projectPickerSearch.value = '';
        if (projectPickerClear) projectPickerClear.classList.add('hidden');
    }

    document.getElementById('deployed').checked = currentUnit.deployed;
    document.getElementById('outForCalibration').checked = currentUnit.out_for_calibration || false;
    document.getElementById('retired').value = currentUnit.retired ? 'true' : '';
    updateRetireButton(currentUnit.retired);
    document.getElementById('note').value = currentUnit.note || '';
    const allocatedChecked = currentUnit.allocated || false;
    document.getElementById('allocated').checked = allocatedChecked;
    document.getElementById('allocatedToProjectId').value = currentUnit.allocated_to_project_id || '';
    document.getElementById('allocatedProjectRow').style.display = allocatedChecked ? '' : 'none';

    // Seismograph fields
    document.getElementById('lastCalibrated').value = currentUnit.last_calibrated || '';
    // Calculate next calibration due from last calibrated
    document.getElementById('nextCalibrationDue').value = currentUnit.last_calibrated
        ? calculateNextCalibrationDue(currentUnit.last_calibrated)
        : '';

    // Populate modem picker for seismograph (uses -detail-seismo suffix)
    const modemPickerValue = document.getElementById('modem-picker-value-detail-seismo');
    const modemPickerSearch = document.getElementById('modem-picker-search-detail-seismo');
    const modemPickerClear = document.getElementById('modem-picker-clear-detail-seismo');
    if (currentUnit.deployed_with_modem_id) {
        // Fetch modem display info (handles both modem ID and IP address lookups)
        fetchModemDisplay(currentUnit.deployed_with_modem_id).then(result => {
            // Update the hidden value with the actual modem ID (in case it was stored as IP)
            if (modemPickerValue) modemPickerValue.value = result.modemId || currentUnit.deployed_with_modem_id;
            if (modemPickerSearch) modemPickerSearch.value = result.display;
            if (modemPickerClear) modemPickerClear.classList.remove('hidden');
        });
    } else {
        if (modemPickerValue) modemPickerValue.value = '';
        if (modemPickerSearch) modemPickerSearch.value = '';
        if (modemPickerClear) modemPickerClear.classList.add('hidden');
    }

    // Modem fields
    document.getElementById('ipAddress').value = currentUnit.ip_address || '';
    document.getElementById('phoneNumber').value = currentUnit.phone_number || '';
    document.getElementById('hardwareModel').value = currentUnit.hardware_model || '';

    // Sound Level Meter fields
    document.getElementById('slmTcpPort').value = currentUnit.slm_tcp_port || '';
    document.getElementById('slmModel').value = currentUnit.slm_model || '';
    document.getElementById('slmSerialNumber').value = currentUnit.slm_serial_number || '';
    document.getElementById('slmFrequencyWeighting').value = currentUnit.slm_frequency_weighting || '';
    document.getElementById('slmTimeWeighting').value = currentUnit.slm_time_weighting || '';
    document.getElementById('slmMeasurementRange').value = currentUnit.slm_measurement_range || '';

    // Populate modem picker for SLM (uses -detail-slm suffix)
    const slmModemPickerValue = document.getElementById('modem-picker-value-detail-slm');
    const slmModemPickerSearch = document.getElementById('modem-picker-search-detail-slm');
    const slmModemPickerClear = document.getElementById('modem-picker-clear-detail-slm');
    if (currentUnit.deployed_with_modem_id) {
        // Fetch modem display info (handles both modem ID and IP address lookups)
        fetchModemDisplay(currentUnit.deployed_with_modem_id).then(result => {
            // Update the hidden value with the actual modem ID (in case it was stored as IP)
            if (slmModemPickerValue) slmModemPickerValue.value = result.modemId || currentUnit.deployed_with_modem_id;
            if (slmModemPickerSearch) slmModemPickerSearch.value = result.display;
            if (slmModemPickerClear) slmModemPickerClear.classList.remove('hidden');
        });
    } else {
        if (slmModemPickerValue) slmModemPickerValue.value = '';
        if (slmModemPickerSearch) slmModemPickerSearch.value = '';
        if (slmModemPickerClear) slmModemPickerClear.classList.add('hidden');
    }

    // Show/hide fields based on device type
    toggleDetailFields();

    // Check for paired device and show cascade section if applicable
    checkAndShowCascadeSection();
}

// Check for paired device and show/hide cascade section
async function checkAndShowCascadeSection() {
    const cascadeSection = document.getElementById('detailCascadeSection');
    const cascadeToUnitId = document.getElementById('detailCascadeToUnitId');
    const pairedDeviceName = document.getElementById('detailPairedDeviceName');

    if (!cascadeSection) return;

    // Reset cascade section
    cascadeSection.classList.add('hidden');
    if (cascadeToUnitId) cascadeToUnitId.value = '';
    if (pairedDeviceName) pairedDeviceName.textContent = '';

    // Reset checkboxes
    ['detailCascadeDeployed', 'detailCascadeRetired', 'detailCascadeProject',
     'detailCascadeLocation', 'detailCascadeCoordinates', 'detailCascadeNote'].forEach(id => {
        const checkbox = document.getElementById(id);
        if (checkbox) checkbox.checked = false;
    });

    let pairedUnitId = null;

    // Check based on device type
    if (currentUnit.device_type === 'modem' && currentUnit.deployed_with_unit_id) {
        // Modem is paired with a seismograph or SLM
        pairedUnitId = currentUnit.deployed_with_unit_id;
    } else if ((currentUnit.device_type === 'seismograph' || currentUnit.device_type === 'slm') && currentUnit.deployed_with_modem_id) {
        // Seismograph or SLM is paired with a modem
        pairedUnitId = currentUnit.deployed_with_modem_id;
    }

    if (pairedUnitId) {
        // Show cascade section
        cascadeSection.classList.remove('hidden');
        if (cascadeToUnitId) cascadeToUnitId.value = pairedUnitId;
        if (pairedDeviceName) pairedDeviceName.textContent = pairedUnitId;
    }
}

// Toggle device-specific fields
function toggleDetailFields() {
    const deviceType = document.getElementById('deviceType').value;
    const seismoFields = document.getElementById('seismographFields');
    const modemFields = document.getElementById('modemFields');
    const slmFields = document.getElementById('slmFields');

    // Hide all device-specific fields first
    seismoFields.classList.add('hidden');
    modemFields.classList.add('hidden');
    slmFields.classList.add('hidden');

    // Show the relevant fields
    if (deviceType === 'seismograph') {
        seismoFields.classList.remove('hidden');
    } else if (deviceType === 'modem') {
        modemFields.classList.remove('hidden');
    } else if (deviceType === 'slm') {
        slmFields.classList.remove('hidden');
    }
}

// Enter edit mode
function enterEditMode() {
    document.getElementById('viewMode').classList.add('hidden');
    document.getElementById('editMode').classList.remove('hidden');
    document.getElementById('editButton').classList.add('hidden');
}

// Cancel edit mode
function cancelEdit() {
    document.getElementById('editMode').classList.add('hidden');
    document.getElementById('viewMode').classList.remove('hidden');
    document.getElementById('editButton').classList.remove('hidden');

    // Reset form to current values
    populateEditForm();
}

function updateRetireButton(isRetired) {
    const btn = document.getElementById('retireBtn');
    if (!btn) return;
    if (isRetired) {
        btn.textContent = 'Un-Retire Unit';
        btn.className = 'px-4 py-2 text-sm font-medium rounded-lg border transition-colors bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 border-gray-300 dark:border-gray-600 hover:bg-gray-200 dark:hover:bg-gray-600';
    } else {
        btn.textContent = 'Retire Unit';
        btn.className = 'px-4 py-2 text-sm font-medium rounded-lg border transition-colors bg-red-50 dark:bg-red-900/20 text-red-700 dark:text-red-400 border-red-300 dark:border-red-700 hover:bg-red-100 dark:hover:bg-red-900/40';
    }
}

function toggleRetired() {
    const hiddenInput = document.getElementById('retired');
    const isCurrentlyRetired = hiddenInput.value === 'true';
    hiddenInput.value = isCurrentlyRetired ? '' : 'true';
    updateRetireButton(!isCurrentlyRetired);
}

// Handle form submission
document.getElementById('editForm').addEventListener('submit', async function(e) {
    e.preventDefault();

    const formData = new FormData(this);
    const deviceType = formData.get('device_type');

    // Fix: FormData contains both modem picker hidden inputs (seismo and slm).
    // We need to ensure only the correct one is submitted based on device type.
    // Delete all deployed_with_modem_id entries and re-add the correct one.
    const modemId = getCorrectModemPickerValue(deviceType);
    formData.delete('deployed_with_modem_id');
    if (modemId) {
        formData.append('deployed_with_modem_id', modemId);
    }

    try {
        const response = await fetch(`/api/roster/edit/${unitId}`, {
            method: 'POST',
            body: formData
        });

        if (response.ok) {
            alert('Unit updated successfully!');
            // Reload data and return to view mode
            await loadUnitData();
            cancelEdit();
        } else {
            const result = await response.json();
            alert(`Error: ${result.detail || 'Unknown error'}`);
        }
    } catch (error) {
        alert(`Error: ${error.message}`);
    }
});

// Get the correct modem picker value based on device type
function getCorrectModemPickerValue(deviceType) {
    if (deviceType === 'seismograph') {
        const picker = document.getElementById('modem-picker-value-detail-seismo');
        return picker ? picker.value : '';
    } else if (deviceType === 'slm') {
        const picker = document.getElementById('modem-picker-value-detail-slm');
        return picker ? picker.value : '';
    }
    // Modems don't have a deployed_with_modem_id
    return '';
}

// Delete unit
async function deleteUnit() {
    if (!confirm(`Are you sure you want to PERMANENTLY delete unit ${unitId}?\n\nThis action cannot be undone!`)) {
        return;
    }

    try {
        const response = await fetch(`/api/roster/${unitId}`, {
            method: 'DELETE'
        });

        if (response.ok) {
            alert('Unit deleted successfully');
            window.location.href = '/roster';
        } else {
            const result = await response.json();
            alert(`Error: ${result.detail || 'Unknown error'}`);
        }
    } catch (error) {
        alert(`Error: ${error.message}`);
    }
}

// Initialize unit location map (only called once)
function initUnitMap() {
    if (!currentUnit.coordinates) {
        document.getElementById('mapCard').classList.add('hidden');
        return;
    }

    const coords = parseLocation(currentUnit.coordinates);
    if (!coords) {
        document.getElementById('mapCard').classList.add('hidden');
        return;
    }

    const [lat, lon] = coords;

    // Show the map card
    document.getElementById('mapCard').classList.remove('hidden');

    // Only initialize map if it doesn't exist
    if (!unitMap) {
        // Initialize map
        unitMap = L.map('unit-map').setView([lat, lon], 13);

        // Add OpenStreetMap tiles
        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
            maxZoom: 18
        }).addTo(unitMap);

        // Force map to update its size
        setTimeout(() => {
            unitMap.invalidateSize();
        }, 100);
    }

    // Update marker (can be called multiple times)
    updateMapMarker(lat, lon);

    // Update location text — prefer the assignment's location name, fall
    // back to address, then coordinates.
    const locationParts = [];
    const loc = currentUnit.active_location;
    if (loc && loc.name) {
        locationParts.push(loc.name);
    }
    if (currentUnit.address) {
        locationParts.push(currentUnit.address);
    }
    locationParts.push(`Coordinates: ${lat.toFixed(6)}, ${lon.toFixed(6)}`);
    document.getElementById('locationText').textContent = locationParts.join(' • ');
}

// Update map marker with current status
function updateMapMarker(lat, lon) {
    // Remove old marker if it exists
    if (mapMarker) {
        mapMarker.remove();
    }

    // Get status color
    let statusColor = 'gray';
    let status = 'Unknown';
    if (currentSnapshot && currentSnapshot.units && currentSnapshot.units[unitId]) {
        const unitStatus = currentSnapshot.units[unitId];
        status = unitStatus.status || 'Unknown';
        statusColor = status === 'OK' ? 'green' : status === 'Pending' ? 'orange' : status === 'Missing' ? 'red' : 'gray';
    }

    // Add new marker
    mapMarker = L.circleMarker([lat, lon], {
        radius: 10,
        fillColor: statusColor,
        color: '#fff',
        weight: 3,
        opacity: 1,
        fillOpacity: 0.8
    }).addTo(unitMap).bindPopup(`
        <div class="p-2">
            <h3 class="font-bold text-lg">${currentUnit.id}</h3>
            <p class="text-sm">Status: <span style="color: ${statusColor}">${status}</span></p>
            <p class="text-sm">Type: ${currentUnit.device_type}</p>
        </div>
    `).openPopup();
}

function parseLocation(location) {
    if (!location) return null;

    // Try to parse as "lat,lon" format
    const parts = location.split(',').map(s => s.trim());
    if (parts.length === 2) {
        const lat = parseFloat(parts[0]);
        const lon = parseFloat(parts[1]);
        if (!isNaN(lat) && !isNaN(lon) && lat >= -90 && lat <= 90 && lon >= -180 && lon <= 180) {
            return [lat, lon];
        }
    }

    return null;
}

// Load and display photos
async function loadPhotos() {
    try {
        const response = await fetch(`/api/unit/${unitId}/photos`);
        if (!response.ok) {
            throw new Error('Failed to load photos');
        }

        const data = await response.json();
        const gallery = document.getElementById('photoGallery');

        if (data.photos && data.photos.length > 0) {
            gallery.innerHTML = '';
            data.photo_urls.forEach((url, index) => {
                const photoDiv = document.createElement('div');
                photoDiv.className = 'relative group';
                photoDiv.innerHTML = `
                    <img src="${url}" alt="Unit photo ${index + 1}"
                         class="w-full h-48 object-cover rounded-lg shadow cursor-pointer hover:shadow-lg transition-shadow"
                         onclick="window.open('${url}', '_blank')">
                    ${index === 0 ? '<span class="absolute top-2 left-2 bg-seismo-orange text-white text-xs px-2 py-1 rounded">Primary</span>' : ''}
                `;
                gallery.appendChild(photoDiv);
            });
        } else {
            gallery.innerHTML = '<p class="text-sm text-gray-500 dark:text-gray-400 col-span-full">No photos yet. Add a photo to get started.</p>';
        }
    } catch (error) {
        console.error('Error loading photos:', error);
        document.getElementById('photoGallery').innerHTML = '<p class="text-sm text-red-500 col-span-full">Failed to load photos</p>';
    }
}

// Upload photo with EXIF metadata extraction
async function uploadPhoto(file) {
    if (!file) return;

    const statusDiv = document.getElementById('uploadStatus');
    statusDiv.className = 'mt-4 p-4 rounded-lg bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200';
    statusDiv.textContent = 'Uploading photo and extracting metadata...';
    statusDiv.classList.remove('hidden');

    const formData = new FormData();
    formData.append('photo', file);

    try {
        const response = await fetch(`/api/unit/${unitId}/upload-photo`, {
            method: 'POST',
            body: formData
        });

        if (!response.ok) {
            throw new Error('Upload failed');
        }

        const result = await response.json();

        // Show success message with metadata info.  Location is on the
        // assignment's MonitoringLocation now, so we just surface what GPS
        // came in — the backend no longer mutates the unit row.
        let message = 'Photo uploaded successfully!';
        if (result.metadata && result.metadata.coordinates) {
            message += ` GPS location detected: ${result.metadata.coordinates}`;
        } else {
            message += ' No GPS data found in photo.';
        }

        statusDiv.className = 'mt-4 p-4 rounded-lg bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200';
        statusDiv.textContent = message;

        // Reload photos
        await loadPhotos();

        // Hide status after 5 seconds
        setTimeout(() => {
            statusDiv.classList.add('hidden');
        }, 5000);

        // Reset both file inputs
        document.getElementById('photoCameraUpload').value = '';
        document.getElementById('photoLibraryUpload').value = '';

    } catch (error) {
        console.error('Error uploading photo:', error);
        statusDiv.className = 'mt-4 p-4 rounded-lg bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200';
        statusDiv.textContent = `Error uploading photo: ${error.message}`;

        // Hide error after 5 seconds
        setTimeout(() => {
            statusDiv.classList.add('hidden');
        }, 5000);
    }
}

// Legacy timeline loader — Phase 4 unified the timeline view.  Now a shim
// that delegates to loadDeploymentTimeline() so existing callers from modal
// save handlers still trigger a refresh of the visible section.
async function loadUnitHistory() {
    if (typeof loadDeploymentTimeline === 'function') {
        return loadDeploymentTimeline();
    }
}
async function _legacy_loadUnitHistory_unused() {
    try {
        const response = await fetch(`/api/roster/history/${unitId}`);
        if (!response.ok) {
            throw new Error('Failed to load history');
        }

        const data = await response.json();
        const timeline = document.getElementById('historyTimeline');

        if (data.history && data.history.length > 0) {
            timeline.innerHTML = '';
            data.history.forEach(entry => {
                const timelineEntry = createTimelineEntry(entry);
                timeline.appendChild(timelineEntry);
            });
        } else {
            timeline.innerHTML = '<p class="text-sm text-gray-500 dark:text-gray-400">No history yet. Changes will appear here.</p>';
        }
    } catch (error) {
        console.error('Error loading history:', error);
        document.getElementById('historyTimeline').innerHTML = '<p class="text-sm text-red-500">Failed to load history</p>';
    }
}

// Create a timeline entry element
function createTimelineEntry(entry) {
    const div = document.createElement('div');
    div.className = 'flex gap-3 p-3 rounded-lg bg-gray-50 dark:bg-slate-700/50';

    // Icon based on change type
    const icons = {
        'note_change': `<svg class="w-5 h-5 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
        </svg>`,
        'deployed_change': `<svg class="w-5 h-5 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
        </svg>`,
        'retired_change': `<svg class="w-5 h-5 text-orange-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
        </svg>`
    };

    const icon = icons[entry.change_type] || `<svg class="w-5 h-5 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
    </svg>`;

    // Format change description
    let description = '';
    if (entry.change_type === 'note_change') {
        description = `<strong>Note changed</strong>`;
        if (entry.old_value) {
            description += `<br><span class="text-xs text-gray-500 dark:text-gray-400">From: "${entry.old_value}"</span>`;
        }
        if (entry.new_value) {
            description += `<br><span class="text-xs text-gray-600 dark:text-gray-300">To: "${entry.new_value}"</span>`;
        }
    } else if (entry.change_type === 'deployed_change') {
        description = `<strong>Status changed to ${entry.new_value}</strong>`;
    } else if (entry.change_type === 'retired_change') {
        description = `<strong>Marked as ${entry.new_value}</strong>`;
    } else {
        description = `<strong>${entry.field_name} changed</strong>`;
        if (entry.old_value && entry.new_value) {
            description += `<br><span class="text-xs text-gray-500 dark:text-gray-400">${entry.old_value} → ${entry.new_value}</span>`;
        }
    }

    // Format timestamp
    const timestamp = new Date(entry.changed_at).toLocaleString();

    div.innerHTML = `
        <div class="flex-shrink-0">
            ${icon}
        </div>
        <div class="flex-1 min-w-0">
            <div class="text-sm text-gray-900 dark:text-white">
                ${description}
            </div>
            <div class="text-xs text-gray-500 dark:text-gray-400 mt-1">
                ${timestamp}
                ${entry.source !== 'manual' ? `<span class="ml-2 px-2 py-0.5 bg-gray-200 dark:bg-gray-600 rounded text-xs">${entry.source}</span>` : ''}
            </div>
        </div>
        <div class="flex-shrink-0">
            <button onclick="deleteHistoryEntry(${entry.id})" class="text-gray-400 hover:text-red-500 transition-colors" title="Delete this history entry">
                <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
                </svg>
            </button>
        </div>
    `;

    return div;
}

// Delete a history entry
async function deleteHistoryEntry(historyId) {
    if (!confirm('Are you sure you want to delete this history entry?')) {
        return;
    }

    try {
        const response = await fetch(`/api/roster/history/${historyId}`, {
            method: 'DELETE'
        });

        if (response.ok) {
            // Reload history
            await loadUnitHistory();
        } else {
            const result = await response.json();
            alert(`Error: ${result.detail || 'Unknown error'}`);
        }
    } catch (error) {
        alert(`Error: ${error.message}`);
    }
}

// Load paired device info for modems
async function loadPairedDevice() {
    try {
        const response = await fetch(`/api/modem-dashboard/${unitId}/paired-device-html`);
        if (response.ok) {
            const html = await response.text();
            document.getElementById('pairedDeviceInfo').innerHTML = html;
        }
    } catch (error) {
        console.error('Error loading paired device:', error);
        document.getElementById('pairedDeviceInfo').innerHTML = '<p class="text-red-500 text-sm">Failed to load paired device info</p>';
    }
}

// Ping modem and show result
async function pingModem() {
    const btn = document.getElementById('modemPingBtn');
    const resultSpan = document.getElementById('modemPingResult');

    // Show loading state
    const originalText = btn.innerHTML;
    btn.innerHTML = `
        <svg class="w-5 h-5 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
        </svg>
        Pinging...
    `;
    btn.disabled = true;
    resultSpan.textContent = 'Testing connection...';
    resultSpan.className = 'text-sm text-gray-500 dark:text-gray-400';

    try {
        const response = await fetch(`/api/modem-dashboard/${unitId}/ping`);
        const data = await response.json();

        if (data.status === 'success') {
            resultSpan.innerHTML = `<span class="inline-block w-2 h-2 bg-green-500 rounded-full mr-1"></span>Online (${data.response_time_ms}ms)`;
            resultSpan.className = 'text-sm text-green-600 dark:text-green-400';
        } else {
            resultSpan.innerHTML = `<span class="inline-block w-2 h-2 bg-red-500 rounded-full mr-1"></span>${data.detail || 'Offline'}`;
            resultSpan.className = 'text-sm text-red-600 dark:text-red-400';
        }
    } catch (error) {
        resultSpan.textContent = 'Error: ' + error.message;
        resultSpan.className = 'text-sm text-red-600 dark:text-red-400';
    }

    // Restore button
    btn.innerHTML = originalText;
    btn.disabled = false;
}

// ============================================================
// Deployment History (legacy — Phase 4 superseded by deployment_timeline)
// ============================================================

// Phase 4 shim: delegate to the unified timeline loader so existing modal
// save handlers (legacy "Log Deployment" form, edit-save callbacks) still
// trigger a refresh of the visible Deployment Timeline section.
async function loadDeploymentHistory() {
    if (typeof loadDeploymentTimeline === 'function') {
        return loadDeploymentTimeline();
    }
}
async function _legacy_loadDeploymentHistory_unused() {
    try {
        const res = await fetch(`/api/deployments/${unitId}`);
        const data = await res.json();
        const container = document.getElementById('deploymentHistory');
        const deployments = data.deployments || [];

        if (deployments.length === 0) {
            container.innerHTML = '<p class="text-sm text-gray-500 dark:text-gray-400">No deployment records yet.</p>';
            return;
        }

        container.innerHTML = '';
        deployments.forEach(d => {
            container.appendChild(createDeploymentRow(d));
        });
    } catch (e) {
        document.getElementById('deploymentHistory').innerHTML =
            '<p class="text-sm text-red-500">Failed to load deployment history.</p>';
    }
}

function formatDateDisplay(iso) {
    if (!iso) return '—';
    const [y, m, d] = iso.split('-');
    return `${parseInt(m)}/${parseInt(d)}/${y}`;
}

function createDeploymentRow(d) {
    const div = document.createElement('div');
    div.className = 'flex items-start gap-3 p-3 rounded-lg ' +
        (d.is_active
            ? 'bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800'
            : 'bg-gray-50 dark:bg-slate-700/50');

    const statusDot = d.is_active
        ? '<span class="mt-1 flex-shrink-0 w-2.5 h-2.5 rounded-full bg-green-500"></span>'
        : '<span class="mt-1 flex-shrink-0 w-2.5 h-2.5 rounded-full bg-gray-400 dark:bg-gray-500"></span>';

    const jobLabel = d.project_ref || d.project_id || 'Unspecified job';
    const locLabel = d.location_name ? `<span class="text-gray-500 dark:text-gray-400"> · ${d.location_name}</span>` : '';

    const deployedStr = formatDateDisplay(d.deployed_date);
    const estStr = d.estimated_removal_date ? formatDateDisplay(d.estimated_removal_date) : 'TBD';
    const actualStr = d.actual_removal_date ? formatDateDisplay(d.actual_removal_date) : null;

    const dateRange = actualStr
        ? `${deployedStr} → ${actualStr}`
        : `${deployedStr} → <span class="font-medium ${d.is_active ? 'text-green-700 dark:text-green-400' : 'text-gray-600 dark:text-gray-300'}">Est. ${estStr}</span>`;

    const activeTag = d.is_active
        ? '<span class="ml-2 px-1.5 py-0.5 text-xs font-medium bg-green-100 dark:bg-green-900/40 text-green-700 dark:text-green-400 rounded">In Field</span>'
        : '';

    div.innerHTML = `
        ${statusDot}
        <div class="flex-1 min-w-0">
            <div class="text-sm font-medium text-gray-900 dark:text-white">
                ${jobLabel}${activeTag}${locLabel}
            </div>
            <div class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">${dateRange}</div>
            ${d.notes ? `<div class="text-xs text-gray-500 dark:text-gray-400 mt-0.5 italic">${d.notes}</div>` : ''}
        </div>
        <div class="flex gap-1 flex-shrink-0">
            <button onclick="openEditDeploymentModal(${JSON.stringify(d).replace(/"/g, '&quot;')})"
                class="p-1.5 text-gray-400 hover:text-seismo-orange rounded transition-colors" title="Edit">
                <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                        d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
                </svg>
            </button>
            <button onclick="deleteDeployment('${d.id}')"
                class="p-1.5 text-gray-400 hover:text-red-500 rounded transition-colors" title="Delete">
                <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                        d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
                </svg>
            </button>
        </div>
    `;
    return div;
}

function openNewDeploymentModal() {
    document.getElementById('deploymentModalTitle').textContent = 'Log Deployment';
    document.getElementById('deploymentModalId').value = '';
    document.getElementById('deploymentDeployedDate').value = '';
    document.getElementById('deploymentEstRemovalDate').value = '';
    document.getElementById('deploymentActualRemovalDate').value = '';
    document.getElementById('deploymentProjectRef').value = '';
    document.getElementById('deploymentLocationName').value = '';
    document.getElementById('deploymentNotes').value = '';
    document.getElementById('deploymentModal').classList.remove('hidden');
}

function openEditDeploymentModal(d) {
    document.getElementById('deploymentModalTitle').textContent = 'Edit Deployment';
    document.getElementById('deploymentModalId').value = d.id;
    document.getElementById('deploymentDeployedDate').value = d.deployed_date || '';
    document.getElementById('deploymentEstRemovalDate').value = d.estimated_removal_date || '';
    document.getElementById('deploymentActualRemovalDate').value = d.actual_removal_date || '';
    document.getElementById('deploymentProjectRef').value = d.project_ref || '';
    document.getElementById('deploymentLocationName').value = d.location_name || '';
    document.getElementById('deploymentNotes').value = d.notes || '';
    document.getElementById('deploymentModal').classList.remove('hidden');
}

function closeDeploymentModal() {
    document.getElementById('deploymentModal').classList.add('hidden');
}

async function saveDeployment() {
    const id = document.getElementById('deploymentModalId').value;
    const payload = {
        deployed_date: document.getElementById('deploymentDeployedDate').value || null,
        estimated_removal_date: document.getElementById('deploymentEstRemovalDate').value || null,
        actual_removal_date: document.getElementById('deploymentActualRemovalDate').value || null,
        project_ref: document.getElementById('deploymentProjectRef').value || null,
        location_name: document.getElementById('deploymentLocationName').value || null,
        notes: document.getElementById('deploymentNotes').value || null,
    };

    try {
        let res;
        if (id) {
            res = await fetch(`/api/deployments/${unitId}/${id}`, {
                method: 'PUT',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify(payload)
            });
        } else {
            res = await fetch(`/api/deployments/${unitId}`, {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify(payload)
            });
        }
        if (!res.ok) throw new Error(await res.text());
        closeDeploymentModal();
        loadDeploymentHistory();
    } catch (e) {
        alert('Failed to save deployment: ' + e.message);
    }
}

async function deleteDeployment(deploymentId) {
    if (!confirm('Delete this deployment record?')) return;
    try {
        const res = await fetch(`/api/deployments/${unitId}/${deploymentId}`, { method: 'DELETE' });
        if (!res.ok) throw new Error(await res.text());
        loadDeploymentHistory();
    } catch (e) {
        alert('Failed to delete: ' + e.message);
    }
}

// Load data when page loads
loadCalibrationInterval();
setupCalibrationAutoCalc();
loadUnitData().then(() => {
    loadPhotos();
    loadDeploymentTimeline();
    if (currentUnit && currentUnit.device_type === 'seismograph') {
        document.getElementById('sfmEventsSection').classList.remove('hidden');
        loadUnitEvents();
    }
});

// ── Unified Deployment Timeline (Phase 4) ────────────────────────────────────
// Replaces the legacy loadDeploymentHistory() + loadUnitHistory() pair.
// Derives entries from unit_assignments + unit_history + SFM event overlay.

// Cache the most recent timeline payload so the merge action can look up
// which assignment_ids belong together in a mergeable group.
let _dtCurrentTimeline = { entries: [], merge_groups: [] };

async function loadDeploymentTimeline() {
    const container = document.getElementById('deploymentTimeline');
    container.innerHTML = '<p class="text-sm text-gray-500 dark:text-gray-400">Loading timeline…</p>';

    try {
        const r = await fetch(`/api/units/${currentUnit.id}/deployment_timeline`);
        if (!r.ok) throw new Error('HTTP ' + r.status);
        const d = await r.json();
        _dtCurrentTimeline = {
            entries:      d.entries || [],
            merge_groups: d.merge_groups || [],
        };
        renderDeploymentTimeline(_dtCurrentTimeline.entries, container, _dtCurrentTimeline.merge_groups);
    } catch (e) {
        container.innerHTML = `<p class="text-sm text-red-500">Failed to load timeline: ${e.message}</p>`;
    }
}

// Returns the merge_group (list of assignment_ids) that this assignment is
// part of, or null if it isn't in any mergeable group.
function _dtFindMergeGroup(assignmentId) {
    for (const group of _dtCurrentTimeline.merge_groups || []) {
        if (group.includes(assignmentId)) return group;
    }
    return null;
}

async function mergeAssignmentGroup(assignmentIds) {
    if (!Array.isArray(assignmentIds) || assignmentIds.length < 2) return;
    const msg = `Merge ${assignmentIds.length} consecutive assignment records into one?\n\n`
              + `The earliest record is kept and its window extended to span all `
              + `of them.  The other ${assignmentIds.length - 1} record(s) are deleted.\n\n`
              + `Original metadata (notes + ingest source) is preserved.  This is `
              + `logged to the unit's history as "assignment_merged".`;
    if (!confirm(msg)) return;

    try {
        // All assignments share the same project_id (validated server-side).
        // Pick the first entry's project_id from the cache.
        const first = (_dtCurrentTimeline.entries || []).find(e =>
            e.kind === 'assignment' && assignmentIds.includes(e.assignment_id)
        );
        const projectId = first ? first.project_id : null;
        if (!projectId) throw new Error('Could not resolve project id for this group');

        const r = await fetch(`/api/projects/${projectId}/assignments/merge`, {
            method:  'POST',
            headers: { 'Content-Type': 'application/json' },
            body:    JSON.stringify({ assignment_ids: assignmentIds }),
        });
        if (!r.ok) {
            const err = await r.json().catch(() => ({detail: 'HTTP ' + r.status}));
            throw new Error(err.detail || 'HTTP ' + r.status);
        }
        await loadDeploymentTimeline();
    } catch (e) {
        alert(e.message || 'Failed to merge assignments.');
    }
}

function _dtFmtDate(iso) {
    if (!iso) return '—';
    return iso.slice(0, 10);
}

function _dtFmtDateTime(iso) {
    if (!iso) return '—';
    return iso.slice(0, 19).replace('T', ' ');
}

function _dtEsc(s) {
    if (s == null) return '';
    return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}

function _dtPpvClass(v) {
    if (v == null) return 'text-gray-400';
    if (v < 0.5) return 'text-green-600 dark:text-green-400';
    if (v < 2.0) return 'text-amber-600 dark:text-amber-400';
    return 'text-red-600 dark:text-red-400 font-semibold';
}

function _dtRenderAssignment(e) {
    const start = _dtFmtDate(e.starts_at);
    const end   = e.is_active ? 'present' : _dtFmtDate(e.ends_at);
    const dur   = (e.duration_days != null)
        ? `<span class="text-xs text-gray-500 dark:text-gray-400 ml-2">(${e.duration_days.toFixed(1)} day${e.duration_days === 1 ? '' : 's'})</span>`
        : '';
    const ov = e.event_overlay || {};
    const evCount = ov.event_count ?? 0;
    const peak    = ov.peak_pvs;

    const locLink = e.location_id
        ? `<a href="/projects/${_dtEsc(e.project_id)}/nrl/${_dtEsc(e.location_id)}" class="text-seismo-orange hover:text-seismo-navy font-medium">📍 ${_dtEsc(e.location_name || 'unnamed location')}</a>`
        : `<span class="text-gray-500 dark:text-gray-400 italic">📍 (no location FK — synthesized from legacy deployment_records)</span>`;

    const projLine = e.project_name
        ? `<div class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">${_dtEsc(e.project_name)}</div>`
        : '';

    const activeBadge = e.is_active
        ? '<span class="px-2 py-0.5 rounded text-xs bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300">active</span>'
        : '';

    // If this assignment belongs to a mergeable group, show a small
    // indicator badge — the group-level "Merge" action lives in the
    // banner at the top of the section to avoid N redundant buttons.
    const mergeGroup = _dtFindMergeGroup(e.assignment_id);
    const mergeableBadge = mergeGroup
        ? `<span class="px-2 py-0.5 rounded text-xs bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300"
                title="This row is part of a ${mergeGroup.length}-record consecutive group at the same location — see the Merge banner above to combine them.">
              mergeable
           </span>`
        : '';

    const overlay = evCount > 0
        ? `<div class="mt-2 flex items-center gap-4 text-xs text-gray-600 dark:text-gray-400">
               <span><strong class="text-gray-900 dark:text-white">${evCount.toLocaleString()}</strong> event${evCount === 1 ? '' : 's'}</span>
               ${peak != null ? `<span>peak <strong class="${_dtPpvClass(peak)}">${peak.toFixed(4)} in/s</strong></span>` : ''}
               ${ov.last_event ? `<span>last ${_dtFmtDateTime(ov.last_event)}</span>` : ''}
           </div>`
        : `<div class="mt-2 text-xs text-gray-500 dark:text-gray-400 italic">No events recorded during this window.</div>`;

    const notes = e.notes
        ? `<div class="mt-2 text-xs text-gray-600 dark:text-gray-400 italic">${_dtEsc(e.notes)}</div>`
        : '';

    // Edit/delete actions live on the right of the date row.  Only shown
    // for assignment entries with a real assignment_id (synthesized legacy
    // entries without one are read-only).
    const actionButtons = e.assignment_id
        ? `<button type="button" onclick='openEditAssignmentModal(${JSON.stringify(e.assignment_id)})'
              class="text-xs text-gray-500 hover:text-seismo-orange p-1 rounded" title="Edit dates / notes">
              ✏️
           </button>`
        : '';

    return `<div class="flex gap-3 transition-shadow rounded-lg" data-assignment-row="${_dtEsc(e.assignment_id)}">
        <div class="flex flex-col items-center pt-1">
            <span class="w-3 h-3 rounded-full ${e.is_active ? 'bg-green-500' : 'bg-seismo-orange'}"></span>
        </div>
        <div class="flex-1 bg-gray-50 dark:bg-slate-900/40 rounded-lg p-3">
            <div class="flex items-center justify-between flex-wrap gap-2">
                <div class="text-sm text-gray-700 dark:text-gray-300">
                    <strong>${start}</strong> → <strong>${end}</strong>${dur}
                </div>
                <div class="flex items-center gap-2">
                    ${mergeableBadge}
                    ${activeBadge}
                    ${actionButtons}
                </div>
            </div>
            <div class="mt-1">${locLink}</div>
            ${projLine}
            ${overlay}
            ${notes}
        </div>
    </div>`;
}

function _dtRenderGap(e) {
    return `<div class="flex gap-3">
        <div class="flex flex-col items-center pt-1">
            <span class="w-3 h-3 rounded-full border-2 border-gray-400 dark:border-gray-500"></span>
        </div>
        <div class="flex-1 bg-gray-50/40 dark:bg-slate-900/20 rounded-lg p-3 border border-dashed border-gray-300 dark:border-gray-700">
            <div class="text-sm text-gray-600 dark:text-gray-400">
                <strong>${_dtFmtDate(e.starts_at)}</strong> → <strong>${_dtFmtDate(e.ends_at)}</strong>
                <span class="text-xs ml-2">(${e.duration_days.toFixed(1)} day${e.duration_days === 1 ? '' : 's'} idle)</span>
            </div>
            <div class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">No active assignment</div>
        </div>
    </div>`;
}

function _dtRenderStateChange(e) {
    // Friendly labels for known change_types.
    const labels = {
        deployed_change:           'Deployed status changed',
        retired_change:            'Retired status changed',
        calibration_status_change: 'Calibration status changed',
        last_calibrated_change:    'Last calibrated updated',
        next_calibration_due_change: 'Next calibration due updated',
        allocation_change:         'Allocation changed',
    };
    const label = labels[e.change_type] || e.change_type;

    return `<div class="flex gap-3">
        <div class="flex flex-col items-center pt-1">
            <span class="w-3 h-3 rounded-full bg-seismo-navy"></span>
        </div>
        <div class="flex-1 bg-gray-50 dark:bg-slate-900/30 rounded-lg p-3">
            <div class="text-sm text-gray-700 dark:text-gray-300">
                📅 <strong>${_dtFmtDateTime(e.starts_at)}</strong> — ${_dtEsc(label)}
            </div>
            <div class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
                ${_dtEsc(e.old_value || '—')} → <strong>${_dtEsc(e.new_value || '—')}</strong>
            </div>
            ${e.history_notes ? `<div class="text-xs text-gray-500 dark:text-gray-400 mt-1 italic">${_dtEsc(e.history_notes)}</div>` : ''}
        </div>
    </div>`;
}

// ── Gantt chart ─────────────────────────────────────────────────────────────
// Renders all assignment windows as colored horizontal bars on an SVG
// timeline.  Click a bar to scroll its detail row into view in the list
// below.  Color per location, opacity reduced for closed assignments.
// "Mergeable" groups get a unifying outline overlay so they're visible at
// a glance as one logical deployment.
const _ganttColorPalette = [
    '#f48b1c', '#142a66', '#7d234d', '#0e7490', '#15803d', '#a16207',
    '#9333ea', '#dc2626', '#0d9488', '#1d4ed8', '#be185d', '#65a30d',
];
function _ganttColorFor(locId, locColorMap) {
    if (locColorMap[locId]) return locColorMap[locId];
    const idx = Object.keys(locColorMap).length % _ganttColorPalette.length;
    locColorMap[locId] = _ganttColorPalette[idx];
    return locColorMap[locId];
}

function _ganttParseDate(iso) {
    if (!iso) return null;
    const d = new Date(iso.replace(' ', 'T'));
    return isNaN(d.getTime()) ? null : d;
}

function _ganttFmtMonth(d) {
    return d.toLocaleDateString('en-US', { month: 'short', year: '2-digit' });
}

function renderDeploymentGantt(entries, mergeGroups) {
    const wrapper = document.getElementById('deploymentGantt');
    const svg     = document.getElementById('deploymentGanttSvg');
    const legend  = document.getElementById('deploymentGanttLegend');
    if (!wrapper || !svg) return;

    const assignments = (entries || []).filter(e => e.kind === 'assignment' && e.starts_at);
    if (assignments.length === 0) {
        wrapper.classList.add('hidden');
        return;
    }
    wrapper.classList.remove('hidden');

    // Compute time domain.  Pad the end by a few days when an active
    // assignment is present so the "active" bar doesn't reach the very
    // edge of the chart.
    const now = new Date();
    let minDate = null, maxDate = null;
    for (const a of assignments) {
        const start = _ganttParseDate(a.starts_at);
        const end   = a.is_active ? now : (_ganttParseDate(a.ends_at) || now);
        if (start && (!minDate || start < minDate)) minDate = start;
        if (end   && (!maxDate || end   > maxDate)) maxDate = end;
    }
    if (!minDate || !maxDate) { wrapper.classList.add('hidden'); return; }
    // Tiny padding at both ends (3% of total span).
    const span = maxDate - minDate;
    const pad  = Math.max(span * 0.03, 24 * 3600 * 1000); // at least 1 day
    minDate = new Date(minDate.getTime() - pad);
    maxDate = new Date(maxDate.getTime() + pad);

    // Build a quick "which mergeGroup is this id in?" map.
    const idToGroup = {};
    (mergeGroups || []).forEach((g, idx) => g.forEach(id => { idToGroup[id] = idx; }));

    // Compute SVG geometry.
    const width  = Math.max(svg.clientWidth || svg.parentElement.clientWidth || 800, 400);
    const height = 140;
    const padLeft   = 8;
    const padRight  = 8;
    const padTop    = 32;   // room for month labels above the bars
    const padBottom = 18;   // room for assignment-count axis below
    const usableW = width - padLeft - padRight;
    const usableH = height - padTop - padBottom;
    const totalRange = maxDate - minDate;
    const xFor = (d) => padLeft + (d - minDate) / totalRange * usableW;

    // Choose one-row-per-bar OR stack overlapping bars.  Since same-unit
    // assignments rarely overlap (only via the brief unassign/reassign
    // race), a single row is usually fine.  But just in case, stack with
    // simple top-down packing.
    const lanes = [];   // each lane = [{x1, x2, ...}, ...]
    function placeInLane(start, end) {
        for (let i = 0; i < lanes.length; i++) {
            const last = lanes[i][lanes[i].length - 1];
            if (last.x2 + 2 < start) {
                lanes[i].push({ x1: start, x2: end });
                return i;
            }
        }
        lanes.push([{ x1: start, x2: end }]);
        return lanes.length - 1;
    }
    const placed = assignments.map(a => {
        const start = _ganttParseDate(a.starts_at);
        const end   = a.is_active ? now : (_ganttParseDate(a.ends_at) || now);
        const x1 = xFor(start);
        const x2 = xFor(end);
        const lane = placeInLane(x1, x2);
        return { a, x1, x2, lane };
    });
    const laneCount = Math.max(lanes.length, 1);
    const barH = Math.max(14, Math.min(28, Math.floor(usableH / laneCount) - 4));
    const laneSpacing = barH + 4;

    // Month gridlines + labels.  Tick on the 1st of each month inside the
    // domain.  If span > 24mo, tick every 3 months instead.
    const months = [];
    let monthCursor = new Date(minDate.getFullYear(), minDate.getMonth(), 1);
    const tickEveryMonths = (totalRange > 24 * 30 * 86400 * 1000) ? 3 : 1;
    while (monthCursor <= maxDate) {
        if (monthCursor >= minDate) months.push(new Date(monthCursor));
        monthCursor.setMonth(monthCursor.getMonth() + tickEveryMonths);
    }

    // Build the SVG string.
    const isDark = document.documentElement.classList.contains('dark');
    const gridColor   = isDark ? '#374151' : '#e5e7eb';
    const labelColor  = isDark ? '#9ca3af' : '#6b7280';
    const todayColor  = '#f48b1c';

    const locColorMap = {};
    const usedLocs = {};

    let parts = [];
    // Month gridlines.
    months.forEach(m => {
        const x = xFor(m);
        parts.push(`<line x1="${x}" y1="${padTop}" x2="${x}" y2="${height - padBottom}" stroke="${gridColor}" stroke-width="1"/>`);
        parts.push(`<text x="${x + 2}" y="${padTop - 6}" font-size="10" fill="${labelColor}" font-family="system-ui,sans-serif">${_ganttFmtMonth(m)}</text>`);
    });
    // Today marker.
    if (now >= minDate && now <= maxDate) {
        const x = xFor(now);
        parts.push(`<line x1="${x}" y1="${padTop}" x2="${x}" y2="${height - padBottom}" stroke="${todayColor}" stroke-width="2" stroke-dasharray="3 2" opacity="0.8"/>`);
        parts.push(`<text x="${x + 3}" y="${height - padBottom + 12}" font-size="9" fill="${todayColor}" font-family="system-ui,sans-serif">today</text>`);
    }

    // Bars.
    placed.forEach(p => {
        const a = p.a;
        const color = _ganttColorFor(a.location_id || '_', locColorMap);
        usedLocs[a.location_name || '(no location)'] = color;
        const y = padTop + p.lane * laneSpacing;
        const opacity = a.is_active ? 1.0 : 0.85;
        const stroke  = (a.source === 'metadata_backfill') ? '#3b82f6' : 'none';
        const strokeWidth = (a.source === 'metadata_backfill') ? 2 : 0;

        const barW = Math.max(p.x2 - p.x1, 3);
        const tipDates = `${(a.starts_at || '').slice(0,10)} → ${a.is_active ? 'active' : (a.ends_at || '').slice(0,10)}`;
        const tip = `${(a.location_name || '?').replace(/"/g, '&quot;')} (${tipDates})${a.event_overlay && a.event_overlay.event_count ? ' • ' + a.event_overlay.event_count + ' events' : ''}`;

        parts.push(`<g style="cursor: pointer;" onclick="_ganttScrollTo('${a.assignment_id}')">
            <title>${tip}</title>
            <rect x="${p.x1}" y="${y}" width="${barW}" height="${barH}" rx="3"
                  fill="${color}" opacity="${opacity}" stroke="${stroke}" stroke-width="${strokeWidth}"/>
            ${a.is_active ? `<circle cx="${p.x2 - 4}" cy="${y + barH / 2}" r="2.5" fill="#fff" opacity="0.9"/>` : ''}
        </g>`);

        // Mergeable highlight — thin dashed underline below the bar.
        if (idToGroup[a.assignment_id] !== undefined) {
            const uy = y + barH + 1;
            parts.push(`<line x1="${p.x1}" y1="${uy}" x2="${p.x2}" y2="${uy}" stroke="#3b82f6" stroke-width="1.5" stroke-dasharray="2 2" opacity="0.7"/>`);
        }
    });

    svg.innerHTML = parts.join('');
    svg.setAttribute('viewBox', `0 0 ${width} ${height}`);

    // Build legend (one swatch per distinct location).
    const legendItems = Object.entries(usedLocs).map(([name, color]) =>
        `<span class="flex items-center gap-1.5"><span class="inline-block w-3 h-2 rounded" style="background:${color}"></span>${_dtEsc(name)}</span>`
    );
    if (mergeGroups && mergeGroups.length > 0) {
        legendItems.push(`<span class="flex items-center gap-1.5"><span class="inline-block w-3 border-b-2 border-dashed border-blue-500"></span>mergeable group</span>`);
    }
    if (placed.some(p => p.a.source === 'metadata_backfill')) {
        legendItems.push(`<span class="flex items-center gap-1.5"><span class="inline-block w-3 h-2 rounded border-2 border-blue-500"></span>auto-backfilled</span>`);
    }
    legend.innerHTML = legendItems.join('');
}

// Click-on-bar handler.  Just scroll the matching list row into view +
// briefly flash it so the eye finds it.
function _ganttScrollTo(assignmentId) {
    const target = document.querySelector(`[data-assignment-row="${assignmentId}"]`);
    if (!target) return;
    target.scrollIntoView({ behavior: 'smooth', block: 'center' });
    target.classList.add('ring-2', 'ring-seismo-orange');
    setTimeout(() => target.classList.remove('ring-2', 'ring-seismo-orange'), 1500);
}

function renderDeploymentTimeline(entries, container, mergeGroups) {
    if (!entries.length) {
        container.innerHTML = '<p class="text-sm text-gray-500 dark:text-gray-400">No deployment history yet. Assign this unit to a project location to start a deployment record.</p>';
        // Hide the Gantt block too.
        const g = document.getElementById('deploymentGantt');
        if (g) g.classList.add('hidden');
        return;
    }

    // Render the Gantt chart first (above the list).
    renderDeploymentGantt(entries, mergeGroups);

    // Build the mergeable-groups banner.  Each group offers one "Merge into
    // one" button.  Skipped when no groups exist.
    let bannerHtml = '';
    if (mergeGroups && mergeGroups.length > 0) {
        const rows = mergeGroups.map(group => {
            // Look up the entries to describe what we're merging.
            const groupEntries = (entries || []).filter(e =>
                e.kind === 'assignment' && group.includes(e.assignment_id)
            );
            if (groupEntries.length === 0) return '';
            const locName = groupEntries[0].location_name || 'unnamed location';
            const earliest = groupEntries.map(e => e.starts_at).filter(Boolean).sort()[0] || '';
            const latest   = groupEntries.map(e => e.ends_at).filter(Boolean).sort().reverse()[0] || 'present';
            const idsJson = JSON.stringify(group).replace(/"/g, '&quot;');
            return `<div class="flex items-center justify-between gap-3 py-1.5">
                <div class="text-sm text-blue-900 dark:text-blue-200 min-w-0 flex-1">
                    <strong>${group.length} consecutive records</strong> at <strong>${_dtEsc(locName)}</strong>
                    <span class="text-xs text-blue-700 dark:text-blue-300 ml-2">${_dtFmtDate(earliest)} → ${_dtFmtDate(latest)}</span>
                </div>
                <button onclick='mergeAssignmentGroup(${JSON.stringify(group)})'
                        class="px-3 py-1 text-xs rounded-full bg-blue-600 hover:bg-blue-700 text-white font-medium whitespace-nowrap">
                    Merge into one
                </button>
            </div>`;
        }).join('');
        bannerHtml = `<div class="mb-4 p-3 rounded-lg bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800">
            <div class="flex items-start gap-2 mb-2">
                <svg class="w-4 h-4 text-blue-600 dark:text-blue-400 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
                </svg>
                <div class="text-xs text-blue-800 dark:text-blue-300">
                    Consecutive deployments at the same location detected.  Combine them into a single record to clean up the view (notes + ingest sources are preserved).
                </div>
            </div>
            ${rows}
        </div>`;
    }

    const html = entries.map(e => {
        if (e.kind === 'assignment')   return _dtRenderAssignment(e);
        if (e.kind === 'gap')          return _dtRenderGap(e);
        if (e.kind === 'state_change') return _dtRenderStateChange(e);
        return '';
    }).join('');

    container.innerHTML = bannerHtml + '<div class="space-y-3">' + html + '</div>';
}

// ── Deployment-timeline editor ──────────────────────────────────────────────
// Edit / delete an existing UnitAssignment row, or create a historical one
// (backfill orphan-event windows).  All three operations hit endpoints that
// already exist on the project-locations router; after a save we just
// reload the timeline + events.

let _editAssignmentCtx = null;   // { assignment_id, project_id, location_name, project_name }

function _iso_to_local_input(iso) {
    // The PATCH/POST endpoints accept "YYYY-MM-DDTHH:MM[:SS]" strings
    // (datetime.fromisoformat).  datetime-local inputs emit the same shape
    // without timezone.  We just strip the trailing "Z" if present and
    // truncate to minutes.
    if (!iso) return '';
    let s = String(iso).replace('Z', '');
    // Slice down to YYYY-MM-DDTHH:MM (16 chars).
    return s.slice(0, 16);
}

function openEditAssignmentModal(assignmentId) {
    const entry = (_dtCurrentTimeline.entries || []).find(
        e => e.kind === 'assignment' && e.assignment_id === assignmentId
    );
    if (!entry) {
        alert('Could not find this assignment in the loaded timeline.');
        return;
    }
    _editAssignmentCtx = {
        assignment_id:  entry.assignment_id,
        project_id:     entry.project_id,
        location_name:  entry.location_name || 'unnamed location',
        project_name:   entry.project_name  || '',
    };

    document.getElementById('editAssignmentLocation').textContent = _editAssignmentCtx.location_name;
    document.getElementById('editAssignmentProject').textContent  = _editAssignmentCtx.project_name;

    document.getElementById('editAssignedAt').value    = _iso_to_local_input(entry.starts_at);
    const endsAtInput  = document.getElementById('editAssignedUntil');
    const openCheckbox = document.getElementById('editAssignedUntilOpen');
    if (entry.is_active) {
        endsAtInput.value = '';
        endsAtInput.disabled = true;
        openCheckbox.checked = true;
    } else {
        endsAtInput.value = _iso_to_local_input(entry.ends_at);
        endsAtInput.disabled = false;
        openCheckbox.checked = false;
    }
    document.getElementById('editAssignmentNotes').value = entry.notes || '';
    document.getElementById('editAssignmentError').classList.add('hidden');
    document.getElementById('editAssignmentModal').classList.remove('hidden');
}

function closeEditAssignmentModal() {
    document.getElementById('editAssignmentModal').classList.add('hidden');
    _editAssignmentCtx = null;
}

function _toggleEditOpenEnded() {
    const open  = document.getElementById('editAssignedUntilOpen').checked;
    const input = document.getElementById('editAssignedUntil');
    input.disabled = open;
    if (open) input.value = '';
}

async function saveEditAssignment() {
    if (!_editAssignmentCtx) return;
    const err = document.getElementById('editAssignmentError');
    err.classList.add('hidden');

    const assignedAt = document.getElementById('editAssignedAt').value;
    if (!assignedAt) {
        err.textContent = 'Assigned-at is required.';
        err.classList.remove('hidden');
        return;
    }
    const open        = document.getElementById('editAssignedUntilOpen').checked;
    const assignedUntil = open ? null : (document.getElementById('editAssignedUntil').value || null);
    const notes       = document.getElementById('editAssignmentNotes').value;

    const btn = document.getElementById('editAssignmentSaveBtn');
    btn.disabled = true; btn.textContent = 'Saving…';

    try {
        const url = `/api/projects/${encodeURIComponent(_editAssignmentCtx.project_id)}/assignments/${encodeURIComponent(_editAssignmentCtx.assignment_id)}`;
        const r = await fetch(url, {
            method:  'PATCH',
            headers: { 'Content-Type': 'application/json' },
            body:    JSON.stringify({
                assigned_at:    assignedAt,
                assigned_until: assignedUntil,
                notes:          notes,
            }),
        });
        if (!r.ok) {
            const e = await r.json().catch(() => ({detail: 'HTTP ' + r.status}));
            throw new Error(e.detail || 'HTTP ' + r.status);
        }
        closeEditAssignmentModal();
        await loadDeploymentTimeline();
        if (typeof loadUnitEvents === 'function' && currentUnit && currentUnit.device_type === 'seismograph') {
            loadUnitEvents();
        }
    } catch (e) {
        err.textContent = e.message;
        err.classList.remove('hidden');
    } finally {
        btn.disabled = false; btn.textContent = 'Save';
    }
}

async function deleteAssignmentFromModal() {
    if (!_editAssignmentCtx) return;
    if (!confirm('Hard-delete this deployment record?\n\nThe assignment row and its history audit entry are removed.  Use this only for misclicks — to end a real deployment, edit the "assigned until" date instead.')) return;
    const err = document.getElementById('editAssignmentError');
    err.classList.add('hidden');
    try {
        const url = `/api/projects/${encodeURIComponent(_editAssignmentCtx.project_id)}/assignments/${encodeURIComponent(_editAssignmentCtx.assignment_id)}`;
        const r = await fetch(url, { method: 'DELETE' });
        if (!r.ok) {
            const e = await r.json().catch(() => ({detail: 'HTTP ' + r.status}));
            throw new Error(e.detail || 'HTTP ' + r.status);
        }
        closeEditAssignmentModal();
        await loadDeploymentTimeline();
        if (typeof loadUnitEvents === 'function' && currentUnit && currentUnit.device_type === 'seismograph') {
            loadUnitEvents();
        }
    } catch (e) {
        err.textContent = e.message;
        err.classList.remove('hidden');
    }
}

// ── Add-historical-assignment modal ─────────────────────────────────────────
let _addAssignmentProjectsCache = null;

async function openAddAssignmentModal() {
    if (!currentUnit) return;
    document.getElementById('addAssignmentError').classList.add('hidden');
    document.getElementById('addAssignedAt').value    = '';
    document.getElementById('addAssignedUntil').value = '';
    document.getElementById('addAssignedUntilOpen').checked = false;
    document.getElementById('addAssignedUntil').disabled    = false;
    document.getElementById('addAssignmentNotes').value     = '';

    const locSel = document.getElementById('addAssignmentLocation');
    locSel.disabled = true;
    locSel.innerHTML = '<option value="">Pick a project first</option>';

    const projSel = document.getElementById('addAssignmentProject');
    projSel.innerHTML = '<option value="">Loading…</option>';

    document.getElementById('addAssignmentModal').classList.remove('hidden');

    try {
        if (!_addAssignmentProjectsCache) {
            const r = await fetch('/api/projects/search-json?limit=50');
            if (!r.ok) throw new Error('HTTP ' + r.status);
            _addAssignmentProjectsCache = await r.json();
        }
        projSel.innerHTML = '<option value="">— pick project —</option>'
            + _addAssignmentProjectsCache.map(p =>
                `<option value="${_dtEsc(p.id)}">${_dtEsc(p.name)}${p.project_number ? ' (' + _dtEsc(p.project_number) + ')' : ''}</option>`
            ).join('');
    } catch (e) {
        projSel.innerHTML = `<option value="">Load failed: ${_dtEsc(e.message)}</option>`;
    }
}

function closeAddAssignmentModal() {
    document.getElementById('addAssignmentModal').classList.add('hidden');
}

function _toggleAddOpenEnded() {
    const open  = document.getElementById('addAssignedUntilOpen').checked;
    const input = document.getElementById('addAssignedUntil');
    input.disabled = open;
    if (open) input.value = '';
}

async function _addAssignmentProjectChanged() {
    const projectId = document.getElementById('addAssignmentProject').value;
    const locSel = document.getElementById('addAssignmentLocation');
    if (!projectId) {
        locSel.disabled = true;
        locSel.innerHTML = '<option value="">Pick a project first</option>';
        return;
    }
    locSel.disabled = true;
    locSel.innerHTML = '<option value="">Loading locations…</option>';
    // Match the device type to the location_type filter.
    const wantType = (currentUnit && currentUnit.device_type === 'slm') ? 'sound' : 'vibration';
    try {
        const r = await fetch(`/api/projects/${encodeURIComponent(projectId)}/locations-json?location_type=${wantType}`);
        if (!r.ok) throw new Error('HTTP ' + r.status);
        const locs = await r.json();
        if (!locs.length) {
            locSel.innerHTML = '<option value="">No matching locations in this project</option>';
            return;
        }
        locSel.disabled = false;
        locSel.innerHTML = '<option value="">— pick location —</option>'
            + locs.map(l => `<option value="${_dtEsc(l.id)}">${_dtEsc(l.name)}</option>`).join('');
    } catch (e) {
        locSel.innerHTML = `<option value="">Load failed: ${_dtEsc(e.message)}</option>`;
    }
}

async function saveAddAssignment() {
    if (!currentUnit) return;
    const err = document.getElementById('addAssignmentError');
    err.classList.add('hidden');

    const projectId  = document.getElementById('addAssignmentProject').value;
    const locationId = document.getElementById('addAssignmentLocation').value;
    const assignedAt = document.getElementById('addAssignedAt').value;
    const open       = document.getElementById('addAssignedUntilOpen').checked;
    const assignedUntil = open ? '' : document.getElementById('addAssignedUntil').value;
    const notes      = document.getElementById('addAssignmentNotes').value;

    if (!projectId)  { err.textContent = 'Pick a project.';  err.classList.remove('hidden'); return; }
    if (!locationId) { err.textContent = 'Pick a location.'; err.classList.remove('hidden'); return; }
    if (!assignedAt) { err.textContent = 'Assigned-at is required.'; err.classList.remove('hidden'); return; }

    const btn = document.getElementById('addAssignmentSaveBtn');
    btn.disabled = true; btn.textContent = 'Creating…';

    try {
        const fd = new FormData();
        fd.append('unit_id',     currentUnit.id);
        fd.append('assigned_at', assignedAt);
        if (assignedUntil) fd.append('assigned_until', assignedUntil);
        if (notes)         fd.append('notes',          notes);

        const url = `/api/projects/${encodeURIComponent(projectId)}/locations/${encodeURIComponent(locationId)}/assign`;
        const r = await fetch(url, { method: 'POST', body: fd });
        if (!r.ok) {
            const e = await r.json().catch(() => ({detail: 'HTTP ' + r.status}));
            throw new Error(e.detail || 'HTTP ' + r.status);
        }
        closeAddAssignmentModal();
        await loadDeploymentTimeline();
        if (typeof loadUnitEvents === 'function' && currentUnit.device_type === 'seismograph') {
            loadUnitEvents();
        }
    } catch (e) {
        err.textContent = e.message;
        err.classList.remove('hidden');
    } finally {
        btn.disabled = false; btn.textContent = 'Create';
    }
}

// ── SFM Events section ──────────────────────────────────────────────────────
function clearUnitEventFilters() {
    document.getElementById('ue-filter-bucket').value = 'all';
    document.getElementById('ue-filter-from').value = '';
    document.getElementById('ue-filter-to').value = '';
    document.getElementById('ue-filter-ft').value = '';
    document.getElementById('ue-filter-limit').value = '500';
    loadUnitEvents();
}

// Module-level state for the unit-events table sort.  Cache lets us re-sort
// without a refetch when the user clicks a column header.
let _ueEventsCache = [];
let _ueEventsTotal = 0;
let _ueEventsBucket = 'all';
let _ueAssignmentsTotal = 0;
let _ueSortKey = 'timestamp';
let _ueSortDir = 'desc';

async function loadUnitEvents() {
    if (!currentUnit || currentUnit.device_type !== 'seismograph') return;
    const container = document.getElementById('ue-events-container');
    container.innerHTML = '<div class="text-center py-12 text-gray-500 dark:text-gray-400 text-sm">Loading events…</div>';

    const params = new URLSearchParams();
    const bucket = document.getElementById('ue-filter-bucket').value;
    const from   = document.getElementById('ue-filter-from').value;
    const to     = document.getElementById('ue-filter-to').value;
    const ft     = document.getElementById('ue-filter-ft').value;
    const limit  = document.getElementById('ue-filter-limit').value;
    params.set('bucket', bucket);
    if (from)  params.set('from_dt', from.replace('T', ' '));
    if (to)    params.set('to_dt',   to.replace('T', ' '));
    if (ft)    params.set('false_trigger', ft);
    params.set('limit', limit);

    try {
        const r = await fetch(`/api/units/${currentUnit.id}/events?${params.toString()}`);
        if (!r.ok) {
            const err = await r.json().catch(() => ({detail: 'HTTP ' + r.status}));
            throw new Error(err.detail || 'HTTP ' + r.status);
        }
        const d = await r.json();
        _ueEventsCache = d.events || [];
        _ueEventsTotal = d.count || 0;
        _ueEventsBucket = bucket;
        _ueAssignmentsTotal = d.assignments_total || 0;
        renderUnitEventStats(d.stats);
        renderUnitEventTable(_ueEventsCache, _ueEventsTotal, container, bucket, _ueAssignmentsTotal);
    } catch (e) {
        container.innerHTML = `<div class="text-center py-12 text-red-500 text-sm">Failed to load events: ${e.message}</div>`;
    }
}

function sortUnitEvents(key) {
    if (_ueSortKey === key) {
        _ueSortDir = _ueSortDir === 'desc' ? 'asc' : 'desc';
    } else {
        _ueSortKey = key;
        _ueSortDir = 'desc';
    }
    renderUnitEventTable(_ueEventsCache, _ueEventsTotal,
        document.getElementById('ue-events-container'), _ueEventsBucket, _ueAssignmentsTotal);
}

function _ueApplySort(events) {
    const key = _ueSortKey;
    const dir = _ueSortDir === 'asc' ? 1 : -1;
    return [...events].sort((a, b) => {
        let av, bv;
        if (key === 'attribution') {
            // Sort by location name so attributed rows group together.
            av = a.attribution ? (a.attribution.location_name || '') : '';
            bv = b.attribution ? (b.attribution.location_name || '') : '';
        } else {
            av = a[key]; bv = b[key];
        }
        if (av == null && bv == null) return 0;
        if (av == null) return 1;
        if (bv == null) return -1;
        if (typeof av === 'number' && typeof bv === 'number') return (av - bv) * dir;
        return String(av).localeCompare(String(bv)) * dir;
    });
}

function _ueSortIndicator(key) {
    if (_ueSortKey !== key) return '<span class="text-gray-400 opacity-50 ml-1">↕</span>';
    return _ueSortDir === 'desc'
        ? '<span class="text-seismo-orange ml-1">↓</span>'
        : '<span class="text-seismo-orange ml-1">↑</span>';
}

function _ueSortableTh(label, key) {
    return `<th onclick="sortUnitEvents('${key}')"
                class="px-4 py-3 text-xs font-medium text-gray-700 dark:text-gray-300 uppercase tracking-wider cursor-pointer select-none hover:bg-gray-100 dark:hover:bg-slate-600 transition-colors">
        ${label}${_ueSortIndicator(key)}
    </th>`;
}

function renderUnitEventStats(stats) {
    const s = stats || {};
    document.getElementById('ue-stat-total').textContent  = (s.event_count ?? 0).toLocaleString();
    const unattrEl = document.getElementById('ue-stat-unattr');
    unattrEl.textContent = (s.unattributed_count ?? 0).toLocaleString();
    // Highlight unattributed in amber/red if non-zero — visual signal that
    // the operator has assignment-window cleanup to do.
    unattrEl.className = 'text-2xl font-bold mt-1 ' + (
        (s.unattributed_count ?? 0) > 0
            ? 'text-amber-600 dark:text-amber-400'
            : 'text-gray-900 dark:text-white'
    );

    if (s.peak_pvs != null) {
        document.getElementById('ue-stat-peak').textContent = s.peak_pvs.toFixed(4) + ' in/s';
        const when = s.peak_pvs_at ? s.peak_pvs_at.slice(0, 10) : '';
        document.getElementById('ue-stat-peak-when').textContent = when || '—';
    } else {
        document.getElementById('ue-stat-peak').textContent = '—';
        document.getElementById('ue-stat-peak-when').textContent = '—';
    }

    if (s.last_event) {
        const dt = s.last_event.slice(0, 19).replace('T', ' ');
        document.getElementById('ue-stat-last').textContent = dt;
    } else {
        document.getElementById('ue-stat-last').textContent = '—';
    }
}

function _ueFmtPPV(v) {
    if (v == null) return '—';
    return v.toFixed(4);
}

function _uePpvClass(v) {
    if (v == null) return 'text-gray-400';
    if (v < 0.5) return 'text-green-600 dark:text-green-400';
    if (v < 2.0) return 'text-amber-600 dark:text-amber-400';
    return 'text-red-600 dark:text-red-400 font-semibold';
}

function _ueEsc(s) {
    if (s == null) return '';
    return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}

function _ueAttrCell(ev) {
    // Inline links use onclick="event.stopPropagation()" so clicking the
    // project/location link navigates instead of opening the event-detail
    // modal (which fires from the row-level onclick).
    const a = ev.attribution;
    if (a) {
        const projLabel = _ueEsc(a.project_name || '—');
        const locLabel  = _ueEsc(a.location_name || '—');
        // If the attributed location has since been soft-removed, badge
        // it so operators see at a glance this is historical attribution.
        const removedBadge = a.location_removed_at
            ? '<span class="ml-1 text-[10px] uppercase tracking-wider px-1 py-0.5 rounded bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300 font-semibold" title="Location no longer actively monitored">removed</span>'
            : '';
        return `<a href="/projects/${_ueEsc(a.project_id)}/nrl/${_ueEsc(a.location_id)}"
                   onclick="event.stopPropagation()"
                   class="text-seismo-orange hover:text-seismo-navy"
                   title="${projLabel} → ${locLabel}">
                    📍 ${locLabel}
                </a>${removedBadge}
                <div class="text-xs text-gray-500 dark:text-gray-400">${projLabel}</div>`;
    }
    const n = ev.nearest_assignment;
    if (n) {
        const sign = n.delta_days < 0 ? 'before' : (n.delta_days > 0 ? 'after' : 'within boundary');
        const days = Math.abs(n.delta_days);
        const daysLabel = days < 1
            ? `<${(days * 24).toFixed(1)}h`
            : `${days.toFixed(1)}d`;
        return `<span class="px-2 py-0.5 rounded text-xs bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-300">⚠ Unattributed</span>
                <div class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
                    ${daysLabel} ${_ueEsc(sign)} <a href="/projects/${_ueEsc(n.project_id)}/nrl/${_ueEsc(n.location_id)}" onclick="event.stopPropagation()" class="text-seismo-orange hover:text-seismo-navy">${_ueEsc(n.location_name || '?')}</a>
                </div>`;
    }
    return `<span class="px-2 py-0.5 rounded text-xs bg-gray-200 text-gray-700 dark:bg-gray-700 dark:text-gray-300">⚠ No assignments</span>`;
}

function renderUnitEventTable(events, total, container, bucket, assignmentsTotal) {
    if (!events || events.length === 0) {
        let msg;
        if (bucket === 'unattributed') {
            msg = assignmentsTotal === 0
                ? 'No assignments yet — every event from this unit is unattributed. Assign it to a project location to start attributing events.'
                : '✅ All events for this unit are attributed to a project/location.';
        } else if (bucket === 'attributed') {
            msg = assignmentsTotal === 0
                ? 'No assignments yet for this unit.'
                : 'No events recorded inside any assignment window with the current filter.';
        } else {
            msg = 'No events found for this unit with the current filter.';
        }
        container.innerHTML = `<div class="text-center py-12 text-gray-500 dark:text-gray-400 text-sm">${msg}</div>`;
        return;
    }

    const sorted = _ueApplySort(events);
    const rows = sorted.map(ev => {
        const ts = ev.timestamp ? ev.timestamp.replace('T', ' ').slice(0, 19) : '—';
        const tran = _ueFmtPPV(ev.tran_ppv);
        const vert = _ueFmtPPV(ev.vert_ppv);
        const lng  = _ueFmtPPV(ev.long_ppv);
        const pvs  = _ueFmtPPV(ev.peak_vector_sum);
        const ft   = ev.false_trigger
            ? '<span class="px-2 py-0.5 rounded text-xs bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300">FT</span>'
            : '';

        const checked = _ueSelectedEventIds.has(ev.id) ? 'checked' : '';
        return `<tr class="hover:bg-gray-50 dark:hover:bg-slate-700/50 cursor-pointer ${ev.attribution ? '' : 'bg-amber-50/40 dark:bg-amber-900/10'}" onclick="showEventDetail('${_dtEsc(ev.id)}')">
            <td class="px-3 py-2.5 text-sm" onclick="event.stopPropagation()">
                <input type="checkbox" class="ue-row-check rounded border-gray-300 dark:border-gray-600"
                       data-event-id="${_dtEsc(ev.id)}" ${checked}
                       onchange="onUnitEventRowCheck(this)">
            </td>
            <td class="px-4 py-2.5 text-sm text-gray-900 dark:text-white whitespace-nowrap">${ts}</td>
            <td class="px-4 py-2.5 text-sm font-mono ${_uePpvClass(ev.tran_ppv)}">${tran}</td>
            <td class="px-4 py-2.5 text-sm font-mono ${_uePpvClass(ev.vert_ppv)}">${vert}</td>
            <td class="px-4 py-2.5 text-sm font-mono ${_uePpvClass(ev.long_ppv)}">${lng}</td>
            <td class="px-4 py-2.5 text-sm font-mono font-semibold ${_uePpvClass(ev.peak_vector_sum)}">${pvs}</td>
            <td class="px-4 py-2.5 text-sm">${ft}</td>
            <td class="px-4 py-2.5 text-sm">${_ueAttrCell(ev)}</td>
        </tr>`;
    }).join('');

    container.innerHTML = `
        <div class="text-xs text-gray-500 dark:text-gray-400 px-4 pt-3 pb-1">Showing ${events.length} of ${total.toLocaleString()} event${total === 1 ? '' : 's'}</div>
        <table class="w-full text-left">
            <thead class="bg-gray-50 dark:bg-slate-700 border-b border-gray-200 dark:border-gray-600">
                <tr>
                    <th class="px-3 py-2">
                        <input type="checkbox" id="ue-check-all"
                               class="rounded border-gray-300 dark:border-gray-600"
                               onchange="toggleAllUnitEventRows(this.checked)">
                    </th>
                    ${_ueSortableTh('Timestamp', 'timestamp')}
                    ${_ueSortableTh('Tran', 'tran_ppv')}
                    ${_ueSortableTh('Vert', 'vert_ppv')}
                    ${_ueSortableTh('Long', 'long_ppv')}
                    ${_ueSortableTh('PVS', 'peak_vector_sum')}
                    ${_ueSortableTh('Flags', 'false_trigger')}
                    ${_ueSortableTh('Attribution', 'attribution')}
                </tr>
            </thead>
            <tbody class="divide-y divide-gray-200 dark:divide-gray-700">${rows}</tbody>
        </table>`;
    _ueRefreshBulkButton();
}

// ===== Bulk false-trigger flagging =====
// Selection is keyed by event ID and persists across table re-renders, so
// users can paginate / re-sort without losing their selection.
const _ueSelectedEventIds = new Set();

function _ueRefreshBulkButton() {
    const n = _ueSelectedEventIds.size;
    const lbl  = document.getElementById('ue-bulk-selected');
    const flag = document.getElementById('ue-bulk-flag-ft');
    const clr  = document.getElementById('ue-bulk-clear-ft');
    if (lbl)  lbl.textContent = `${n} selected`;
    if (flag) flag.disabled = (n === 0);
    if (clr)  clr.disabled  = (n === 0);
}

function onUnitEventRowCheck(input) {
    const id = input.getAttribute('data-event-id');
    if (input.checked) {
        _ueSelectedEventIds.add(id);
    } else {
        _ueSelectedEventIds.delete(id);
        // If we just unchecked a row, the master "all" checkbox shouldn't stay checked.
        const master = document.getElementById('ue-check-all');
        if (master) master.checked = false;
    }
    _ueRefreshBulkButton();
}

function toggleAllUnitEventRows(checked) {
    document.querySelectorAll('.ue-row-check').forEach(cb => {
        const id = cb.getAttribute('data-event-id');
        cb.checked = checked;
        if (checked) _ueSelectedEventIds.add(id);
        else         _ueSelectedEventIds.delete(id);
    });
    _ueRefreshBulkButton();
}

async function flagSelectedUnitEvents(value) {
    // value = true  → flag as false trigger
    // value = false → clear false-trigger flag
    if (_ueSelectedEventIds.size === 0) return;
    const ids = Array.from(_ueSelectedEventIds);
    const verb = value ? 'flag as false trigger' : 'clear false-trigger flag on';
    if (!confirm(`${verb} ${ids.length} event${ids.length === 1 ? '' : 's'}?`)) {
        return;
    }

    // SFM exposes single-row PATCH only.  Fan out concurrently with a
    // modest cap so we don't open hundreds of sockets at once.
    const concurrency = 8;
    let ok = 0, failed = 0;
    let cursor = 0;
    async function worker() {
        while (cursor < ids.length) {
            const i = cursor++;
            const id = ids[i];
            try {
                const resp = await fetch(
                    `/api/sfm/db/events/${encodeURIComponent(id)}/false_trigger?value=${value ? 'true' : 'false'}`,
                    { method: 'PATCH' }
                );
                if (resp.ok) ok++;
                else         failed++;
            } catch (_) {
                failed++;
            }
        }
    }
    await Promise.all(Array.from({ length: concurrency }, worker));

    if (failed) {
        alert(`${ok} updated, ${failed} failed.  Refreshing table.`);
    }
    _ueSelectedEventIds.clear();
    loadUnitEvents();
}

// ===== Pair Device Modal Functions =====
let pairModalModems = []; // Cache loaded modems
let pairModalDeviceType = ''; // Current device type

function openPairDeviceModal(deviceType) {
    const modal = document.getElementById('pairDeviceModal');
    const searchInput = document.getElementById('pairModemSearch');
    const hideBenchedCheckbox = document.getElementById('pairHideBenched');

    if (!modal) return;

    pairModalDeviceType = deviceType;

    // Reset search and filter
    if (searchInput) searchInput.value = '';
    if (hideBenchedCheckbox) hideBenchedCheckbox.checked = false;

    // Show modal
    modal.classList.remove('hidden');

    // Focus search input
    setTimeout(() => {
        if (searchInput) searchInput.focus();
    }, 100);

    // Load available modems
    loadAvailableModems();
}

function closePairDeviceModal() {
    const modal = document.getElementById('pairDeviceModal');
    if (modal) modal.classList.add('hidden');
    pairModalModems = [];
}

async function loadAvailableModems() {
    const listContainer = document.getElementById('pairModemList');
    listContainer.innerHTML = '<div class="text-center py-4"><div class="animate-spin inline-block w-6 h-6 border-2 border-seismo-orange border-t-transparent rounded-full"></div><p class="mt-2 text-sm text-gray-500">Loading modems...</p></div>';

    try {
        const response = await fetch('/api/roster/modems');
        if (!response.ok) throw new Error('Failed to load modems');

        pairModalModems = await response.json();

        if (pairModalModems.length === 0) {
            listContainer.innerHTML = '<p class="text-center py-4 text-gray-500">No modems found in roster</p>';
            return;
        }

        // Render the list
        renderModemList();
    } catch (error) {
        listContainer.innerHTML = `<p class="text-center py-4 text-red-500">Error: ${error.message}</p>`;
    }
}

function filterPairModemList() {
    renderModemList();
}

function renderModemList() {
    const listContainer = document.getElementById('pairModemList');
    const searchInput = document.getElementById('pairModemSearch');
    const hideBenchedCheckbox = document.getElementById('pairHideBenched');

    const searchTerm = (searchInput?.value || '').toLowerCase().trim();
    const hideBenched = hideBenchedCheckbox?.checked || false;

    // Filter modems
    let filteredModems = pairModalModems.filter(modem => {
        // Filter by benched status
        if (hideBenched && !modem.deployed) return false;

        // Filter by search term
        if (searchTerm) {
            const searchFields = [
                modem.id,
                modem.ip_address || '',
                modem.phone_number || '',
                modem.note || ''
            ].join(' ').toLowerCase();
            if (!searchFields.includes(searchTerm)) return false;
        }

        return true;
    });

    if (filteredModems.length === 0) {
        listContainer.innerHTML = '<p class="text-center py-4 text-gray-500">No modems match your criteria</p>';
        return;
    }

    // Build modem list
    let html = '';
    for (const modem of filteredModems) {
        const displayParts = [modem.id];
        if (modem.ip_address) displayParts.push(`(${modem.ip_address})`);
        if (modem.note) displayParts.push(`- ${modem.note.substring(0, 30)}${modem.note.length > 30 ? '...' : ''}`);

        const deployedBadge = modem.deployed
            ? '<span class="px-2 py-0.5 bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-300 text-xs rounded">Deployed</span>'
            : '<span class="px-2 py-0.5 bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400 text-xs rounded">Benched</span>';

        const pairedBadge = modem.deployed_with_unit_id
            ? `<span class="px-2 py-0.5 bg-purple-100 dark:bg-purple-900/30 text-purple-800 dark:text-purple-300 text-xs rounded">Paired: ${modem.deployed_with_unit_id}</span>`
            : '';

        html += `
            <div class="flex items-center justify-between p-3 hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer border-b border-gray-100 dark:border-gray-700 last:border-0"
                 onclick="selectModemForPairing('${modem.id}', '${displayParts.join(' ').replace(/'/g, "\\'")}')">
                <div class="flex-1">
                    <div class="font-medium text-gray-900 dark:text-white">
                        <span class="text-seismo-orange">${modem.id}</span>
                        ${modem.ip_address ? `<span class="text-gray-400 ml-2 font-mono text-sm">${modem.ip_address}</span>` : ''}
                    </div>
                    ${modem.note ? `<div class="text-sm text-gray-500 dark:text-gray-400 truncate">${modem.note}</div>` : ''}
                </div>
                <div class="flex gap-2 ml-3">
                    ${deployedBadge}
                    ${pairedBadge}
                </div>
            </div>
        `;
    }

    listContainer.innerHTML = html;
}

function selectModemForPairing(modemId, displayText) {
    // Update the correct picker based on device type
    let pickerId = '';
    if (pairModalDeviceType === 'seismograph') {
        pickerId = '-detail-seismo';
    } else if (pairModalDeviceType === 'slm') {
        pickerId = '-detail-slm';
    }

    const valueInput = document.getElementById('modem-picker-value' + pickerId);
    const searchInput = document.getElementById('modem-picker-search' + pickerId);
    const clearBtn = document.getElementById('modem-picker-clear' + pickerId);

    if (valueInput) valueInput.value = modemId;
    if (searchInput) searchInput.value = displayText;
    if (clearBtn) clearBtn.classList.remove('hidden');

    // Close modal
    closePairDeviceModal();
}

// Clear pairing (unpair device from modem)
function clearPairing(deviceType) {
    let pickerId = '';
    if (deviceType === 'seismograph') {
        pickerId = '-detail-seismo';
    } else if (deviceType === 'slm') {
        pickerId = '-detail-slm';
    }

    const valueInput = document.getElementById('modem-picker-value' + pickerId);
    const searchInput = document.getElementById('modem-picker-search' + pickerId);
    const clearBtn = document.getElementById('modem-picker-clear' + pickerId);

    if (valueInput) valueInput.value = '';
    if (searchInput) searchInput.value = '';
    if (clearBtn) clearBtn.classList.add('hidden');

    closePairDeviceModal();
}

// ===== Modem Pair Device Modal Functions (for modems to pick a device) =====
let modemPairDevices = []; // Cache loaded devices
let modemHasCurrentPairing = false;

function openModemPairDeviceModal() {
    const modal = document.getElementById('modemPairDeviceModal');
    const searchInput = document.getElementById('modemPairDeviceSearch');

    if (!modal) return;

    modal.classList.remove('hidden');
    if (searchInput) {
        searchInput.value = '';
        searchInput.focus();
    }

    // Reset checkboxes
    document.getElementById('modemPairHidePaired').checked = true;
    document.getElementById('modemPairShowSeismo').checked = true;
    document.getElementById('modemPairShowSLM').checked = true;

    // Load available devices
    loadPairableDevices();
}

function closeModemPairDeviceModal() {
    const modal = document.getElementById('modemPairDeviceModal');
    if (modal) modal.classList.add('hidden');
    modemPairDevices = [];
}

async function loadPairableDevices() {
    const listContainer = document.getElementById('modemPairDeviceList');
    listContainer.innerHTML = '<div class="text-center py-8"><div class="animate-spin inline-block w-6 h-6 border-2 border-seismo-orange border-t-transparent rounded-full"></div><p class="mt-2 text-sm text-gray-500">Loading devices...</p></div>';

    try {
        const response = await fetch(`/api/modem-dashboard/${unitId}/pairable-devices?hide_paired=false`);
        if (!response.ok) throw new Error('Failed to load devices');

        const data = await response.json();
        modemPairDevices = data.devices || [];

        // Check if modem has a current pairing
        modemHasCurrentPairing = modemPairDevices.some(d => d.is_paired_to_this);
        const unpairBtn = document.getElementById('modemUnpairBtn');
        if (unpairBtn) {
            unpairBtn.classList.toggle('hidden', !modemHasCurrentPairing);
        }

        if (modemPairDevices.length === 0) {
            listContainer.innerHTML = '<p class="text-center py-8 text-gray-500">No devices found in roster</p>';
            return;
        }

        renderModemPairDeviceList();
    } catch (error) {
        console.error('Failed to load pairable devices:', error);
        listContainer.innerHTML = '<p class="text-center py-8 text-red-500">Failed to load devices</p>';
    }
}

function filterModemPairDeviceList() {
    renderModemPairDeviceList();
}

function renderModemPairDeviceList() {
    const listContainer = document.getElementById('modemPairDeviceList');
    const searchInput = document.getElementById('modemPairDeviceSearch');
    const hidePairedCheckbox = document.getElementById('modemPairHidePaired');
    const showSeismoCheckbox = document.getElementById('modemPairShowSeismo');
    const showSLMCheckbox = document.getElementById('modemPairShowSLM');

    const searchTerm = searchInput?.value?.toLowerCase() || '';
    const hidePaired = hidePairedCheckbox?.checked ?? true;
    const showSeismo = showSeismoCheckbox?.checked ?? true;
    const showSLM = showSLMCheckbox?.checked ?? true;

    // Filter devices
    let filteredDevices = modemPairDevices.filter(device => {
        // Filter by device type
        if (device.device_type === 'seismograph' && !showSeismo) return false;
        if (device.device_type === 'slm' && !showSLM) return false;

        // Hide devices paired to OTHER modems (but show unpaired and paired-to-this)
        if (hidePaired && device.is_paired_to_other) return false;

        // Search filter
        if (searchTerm) {
            const searchFields = [
                device.id,
                device.project_id || '',
                device.location || '',
                device.note || ''
            ].join(' ').toLowerCase();
            if (!searchFields.includes(searchTerm)) return false;
        }

        return true;
    });

    if (filteredDevices.length === 0) {
        listContainer.innerHTML = '<p class="text-center py-8 text-gray-500">No devices match your criteria</p>';
        return;
    }

    // Build device list HTML
    let html = '<div class="divide-y divide-gray-200 dark:divide-gray-700">';
    for (const device of filteredDevices) {
        const deviceTypeLabel = device.device_type === 'slm' ? 'SLM' : 'Seismograph';
        const deviceTypeClass = device.device_type === 'slm'
            ? 'bg-purple-100 dark:bg-purple-900/30 text-purple-800 dark:text-purple-300'
            : 'bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-300';

        const deployedBadge = device.deployed
            ? '<span class="px-2 py-0.5 bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-300 text-xs rounded">Deployed</span>'
            : '<span class="px-2 py-0.5 bg-amber-100 dark:bg-amber-900/30 text-amber-800 dark:text-amber-300 text-xs rounded">Benched</span>';

        let pairingBadge = '';
        if (device.is_paired_to_this) {
            pairingBadge = '<span class="px-2 py-0.5 bg-seismo-orange/20 text-seismo-orange text-xs rounded font-medium">Current</span>';
        } else if (device.is_paired_to_other) {
            pairingBadge = `<span class="px-2 py-0.5 bg-gray-200 dark:bg-gray-600 text-gray-600 dark:text-gray-300 text-xs rounded">Paired: ${device.paired_modem_id}</span>`;
        }

        const isCurrentlyPaired = device.is_paired_to_this;

        html += `
            <div class="px-6 py-3 hover:bg-gray-50 dark:hover:bg-slate-700 cursor-pointer transition-colors ${isCurrentlyPaired ? 'bg-seismo-orange/5' : ''}"
                 onclick="selectDeviceForModem('${device.id}')">
                <div class="flex items-center justify-between">
                    <div class="flex-1 min-w-0">
                        <div class="flex items-center gap-2">
                            <span class="font-medium text-gray-900 dark:text-white">${device.id}</span>
                            <span class="px-2 py-0.5 ${deviceTypeClass} text-xs rounded">${deviceTypeLabel}</span>
                        </div>
                        ${device.project_id ? `<div class="text-sm text-gray-500 dark:text-gray-400">${device.project_id}</div>` : ''}
                        ${device.location ? `<div class="text-sm text-gray-500 dark:text-gray-400 truncate">${device.location}</div>` : ''}
                    </div>
                    <div class="flex items-center gap-2 ml-4">
                        ${deployedBadge}
                        ${pairingBadge}
                    </div>
                </div>
            </div>
        `;
    }
    html += '</div>';

    listContainer.innerHTML = html;
}

async function selectDeviceForModem(deviceId) {
    const listContainer = document.getElementById('modemPairDeviceList');

    // Show loading state
    listContainer.innerHTML = '<div class="text-center py-8"><div class="animate-spin inline-block w-6 h-6 border-2 border-seismo-orange border-t-transparent rounded-full"></div><p class="mt-2 text-sm text-gray-500">Pairing device...</p></div>';

    try {
        const response = await fetch(`/api/modem-dashboard/${unitId}/pair?device_id=${encodeURIComponent(deviceId)}`, {
            method: 'POST'
        });

        const result = await response.json();

        if (result.status === 'success') {
            closeModemPairDeviceModal();
            // Reload the paired device section
            loadPairedDevice();
            // Show success message (optional toast)
            showToast(`Paired with ${deviceId}`, 'success');
        } else {
            listContainer.innerHTML = `<p class="text-center py-8 text-red-500">${result.detail || 'Failed to pair device'}</p>`;
        }
    } catch (error) {
        console.error('Failed to pair device:', error);
        listContainer.innerHTML = '<p class="text-center py-8 text-red-500">Failed to pair device</p>';
    }
}

async function unpairDeviceFromModem() {
    const listContainer = document.getElementById('modemPairDeviceList');

    // Show loading state
    listContainer.innerHTML = '<div class="text-center py-8"><div class="animate-spin inline-block w-6 h-6 border-2 border-seismo-orange border-t-transparent rounded-full"></div><p class="mt-2 text-sm text-gray-500">Unpairing device...</p></div>';

    try {
        const response = await fetch(`/api/modem-dashboard/${unitId}/unpair`, {
            method: 'POST'
        });

        const result = await response.json();

        if (result.status === 'success') {
            closeModemPairDeviceModal();
            // Reload the paired device section
            loadPairedDevice();
            // Show success message
            if (result.unpaired_device_id) {
                showToast(`Unpaired ${result.unpaired_device_id}`, 'success');
            } else {
                showToast('No device was paired', 'info');
            }
        } else {
            listContainer.innerHTML = `<p class="text-center py-8 text-red-500">${result.detail || 'Failed to unpair device'}</p>`;
        }
    } catch (error) {
        console.error('Failed to unpair device:', error);
        listContainer.innerHTML = '<p class="text-center py-8 text-red-500">Failed to unpair device</p>';
    }
}

// Simple toast function (if not already defined)
function showToast(message, type = 'info') {
    // Check if there's already a toast container
    let toastContainer = document.getElementById('toast-container');
    if (!toastContainer) {
        toastContainer = document.createElement('div');
        toastContainer.id = 'toast-container';
        toastContainer.className = 'fixed bottom-4 right-4 z-50 space-y-2';
        document.body.appendChild(toastContainer);
    }

    const toast = document.createElement('div');
    const bgColor = type === 'success' ? 'bg-green-500' : type === 'error' ? 'bg-red-500' : 'bg-gray-700';
    toast.className = `${bgColor} text-white px-4 py-2 rounded-lg shadow-lg transform transition-all duration-300 translate-x-0`;
    toast.textContent = message;

    toastContainer.appendChild(toast);

    // Auto-remove after 3 seconds
    setTimeout(() => {
        toast.classList.add('opacity-0', 'translate-x-full');
        setTimeout(() => toast.remove(), 300);
    }, 3000);
}
</script>

<!-- Pair Device Modal -->
<div id="pairDeviceModal" class="hidden fixed inset-0 z-50 overflow-y-auto">
    <div class="flex items-center justify-center min-h-screen px-4">
        <!-- Backdrop -->
        <div class="fixed inset-0 bg-black/50" onclick="closePairDeviceModal()"></div>

        <!-- Modal Content -->
        <div class="relative bg-white dark:bg-slate-800 rounded-xl shadow-xl max-w-lg w-full max-h-[80vh] overflow-hidden">
            <!-- Header -->
            <div class="flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700">
                <div>
                    <h3 class="text-lg font-semibold text-gray-900 dark:text-white">Pair with Modem</h3>
                    <p class="text-sm text-gray-500 dark:text-gray-400">Select a modem to pair with this device</p>
                </div>
                <button onclick="closePairDeviceModal()" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
                    <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
                    </svg>
                </button>
            </div>

            <!-- Search and Filter -->
            <div class="px-6 py-3 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-slate-900/50">
                <div class="flex gap-3 items-center">
                    <!-- Search Input -->
                    <div class="flex-1 relative">
                        <input type="text"
                               id="pairModemSearch"
                               placeholder="Search by ID, IP, or note..."
                               oninput="filterPairModemList()"
                               class="w-full px-4 py-2 pl-10 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange focus:border-seismo-orange text-sm">
                        <svg class="w-5 h-5 text-gray-400 absolute left-3 top-1/2 -translate-y-1/2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
                        </svg>
                    </div>
                    <!-- Hide Benched Toggle -->
                    <label class="flex items-center gap-2 cursor-pointer whitespace-nowrap">
                        <input type="checkbox"
                               id="pairHideBenched"
                               onchange="filterPairModemList()"
                               class="w-4 h-4 text-seismo-orange focus:ring-seismo-orange rounded">
                        <span class="text-sm text-gray-600 dark:text-gray-400">Deployed only</span>
                    </label>
                </div>
            </div>

            <!-- Modem List -->
            <div id="pairModemList" class="max-h-80 overflow-y-auto">
                <!-- Populated by JavaScript -->
            </div>

            <!-- Footer -->
            <div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-slate-900">
                <button onclick="closePairDeviceModal()" class="w-full px-4 py-2 bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 rounded-lg transition-colors">
                    Cancel
                </button>
            </div>
        </div>
    </div>
</div>

<!-- Include Project Create Modal for inline project creation -->
{% include "partials/project_create_modal.html" %}

<!-- Modem Pair Device Modal (for modems to pick a device) -->
<div id="modemPairDeviceModal" class="hidden fixed inset-0 z-50 overflow-y-auto">
    <div class="flex items-center justify-center min-h-screen px-4">
        <!-- Backdrop -->
        <div class="fixed inset-0 bg-black/50" onclick="closeModemPairDeviceModal()"></div>

        <!-- Modal Content -->
        <div class="relative bg-white dark:bg-slate-800 rounded-xl shadow-xl max-w-lg w-full max-h-[90vh] flex flex-col">
            <!-- Header -->
            <div class="flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700">
                <div>
                    <h3 class="text-lg font-semibold text-gray-900 dark:text-white">Pair Device to Modem</h3>
                    <p class="text-sm text-gray-500 dark:text-gray-400">Select a seismograph or SLM to pair</p>
                </div>
                <button onclick="closeModemPairDeviceModal()" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
                    <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
                    </svg>
                </button>
            </div>

            <!-- Search and Filters -->
            <div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700 space-y-3">
                <input type="text"
                       placeholder="Search by ID, project, location..."
                       class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange focus:border-transparent"
                       id="modemPairDeviceSearch"
                       autocomplete="off"
                       oninput="filterModemPairDeviceList()">
                <div class="flex items-center gap-4">
                    <label class="flex items-center gap-2 text-sm text-gray-600 dark:text-gray-400 cursor-pointer">
                        <input type="checkbox"
                               id="modemPairHidePaired"
                               onchange="filterModemPairDeviceList()"
                               checked
                               class="rounded border-gray-300 dark:border-gray-600 text-seismo-orange focus:ring-seismo-orange">
                        Hide paired devices
                    </label>
                    <label class="flex items-center gap-2 text-sm text-gray-600 dark:text-gray-400 cursor-pointer">
                        <input type="checkbox"
                               id="modemPairShowSeismo"
                               onchange="filterModemPairDeviceList()"
                               checked
                               class="rounded border-gray-300 dark:border-gray-600 text-seismo-orange focus:ring-seismo-orange">
                        Seismographs
                    </label>
                    <label class="flex items-center gap-2 text-sm text-gray-600 dark:text-gray-400 cursor-pointer">
                        <input type="checkbox"
                               id="modemPairShowSLM"
                               onchange="filterModemPairDeviceList()"
                               checked
                               class="rounded border-gray-300 dark:border-gray-600 text-seismo-orange focus:ring-seismo-orange">
                        SLMs
                    </label>
                </div>
            </div>

            <!-- Device List -->
            <div id="modemPairDeviceList" class="flex-1 overflow-y-auto max-h-80">
                <!-- Populated by JavaScript -->
            </div>

            <!-- Footer -->
            <div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-slate-900 flex gap-3">
                <button onclick="unpairDeviceFromModem()"
                        id="modemUnpairBtn"
                        class="hidden px-4 py-2 bg-red-100 hover:bg-red-200 dark:bg-red-900/30 dark:hover:bg-red-900/50 text-red-700 dark:text-red-300 rounded-lg transition-colors">
                    Unpair Current
                </button>
                <button onclick="closeModemPairDeviceModal()" class="flex-1 px-4 py-2 bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 rounded-lg transition-colors">
                    Cancel
                </button>
            </div>
        </div>
    </div>
</div>

{# Shared event-detail modal (clicking a row in the SFM Events table) #}
{% include 'partials/event_detail_modal.html' %}
<script src="/static/event-modal.js"></script>
<script>
    // Refresh the unit's events table when the modal's review form saves.
    window.addEventListener('sfm-event-review-saved', () => {
        if (typeof loadUnitEvents === 'function') loadUnitEvents();
    });
</script>

{% endblock %}
