{% extends "base.html" %}

{% block title %}Project Dashboard - Terra-View{% endblock %}

{% block content %}
<!-- Breadcrumb Navigation -->
<div class="mb-6 flex items-center justify-between gap-3">
    <nav class="flex items-center space-x-2 text-sm">
        <a href="/projects" class="text-seismo-orange hover:text-seismo-navy flex items-center">
            <svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
            </svg>
            Projects
        </a>
        <svg class="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
        </svg>
        <span class="text-gray-900 dark:text-white font-medium" id="project-name-breadcrumb">Project</span>
    </nav>

    <!-- Client portal access for this project -->
    <div class="shrink-0 flex items-center gap-2">
        <button type="button" onclick="openPortalAccess()"
                class="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg border border-slate-600 bg-slate-700/40 text-gray-200 hover:bg-slate-700 transition-colors"
                title="Manage this project's client portal access">
            <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="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path>
            </svg>
            Portal access
        </button>
        <a href="/projects/{{ project_id }}/portal-preview" target="_blank" rel="noopener"
           class="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg border border-seismo-orange/40 bg-seismo-orange/10 text-seismo-orange hover:bg-seismo-orange/20 transition-colors"
           title="Preview this project's client portal in a new tab">
            <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="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
            </svg>
            Preview
        </a>
    </div>
</div>

<!-- Header (loads dynamically) -->
<div id="project-header" hx-get="/api/projects/{{ project_id }}/header" hx-trigger="load" hx-swap="innerHTML">
    <div class="mb-8 animate-pulse">
        <div class="h-10 bg-gray-200 dark:bg-gray-700 rounded w-1/3 mb-2"></div>
        <div class="h-4 bg-gray-200 dark:bg-gray-700 rounded w-1/4"></div>
    </div>
</div>

<!-- Tab Navigation -->
<div class="mb-6 border-b border-gray-200 dark:border-gray-700">
    <nav class="flex space-x-6 overflow-x-auto">
        <button onclick="switchTab('overview')"
                data-tab="overview"
                class="tab-button px-4 py-3 border-b-2 font-medium text-sm transition-colors border-seismo-orange text-seismo-orange whitespace-nowrap">
            Overview
        </button>
        <button id="vibration-tab-btn" onclick="switchTab('vibration')"
                data-tab="vibration"
                class="tab-button hidden px-4 py-3 border-b-2 border-transparent font-medium text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:border-gray-300 dark:hover:border-gray-600 transition-colors whitespace-nowrap">
            <svg class="w-4 h-4 inline mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
            </svg>
            Vibration
        </button>
        <button id="sound-tab-btn" onclick="switchTab('sound')"
                data-tab="sound"
                class="tab-button hidden px-4 py-3 border-b-2 border-transparent font-medium text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:border-gray-300 dark:hover:border-gray-600 transition-colors whitespace-nowrap">
            <svg class="w-4 h-4 inline mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.536 8.464a5 5 0 010 7.072M12 6a7 7 0 010 14M8.464 8.464a5 5 0 000 7.072"/>
            </svg>
            Sound
        </button>
        <button onclick="switchTab('settings')"
                data-tab="settings"
                class="tab-button px-4 py-3 border-b-2 border-transparent font-medium text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:border-gray-300 dark:hover:border-gray-600 transition-colors whitespace-nowrap">
            Settings
        </button>
    </nav>
</div>

<!-- Tab Content -->
<div id="tab-content">
    <!-- Overview Tab -->
    <div id="overview-tab" class="tab-panel">
        <!-- Live monitoring (sound) — self-contained, refreshes itself every 15s.
             Kept OUTSIDE #project-dashboard so the 30s htmx swap below never
             clobbers its DOM/timer. Shown only for projects with sound NRLs. -->
        <div id="live-stats-section" class="hidden mb-6">
            <div class="flex flex-wrap items-center gap-2.5 mb-4">
                <span class="text-[10px] uppercase tracking-[0.18em] text-seismo-orange/90 font-semibold">Live monitoring</span>
                <div class="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg bg-white dark:bg-slate-800 shadow-sm">
                    <span class="relative flex h-2 w-2">
                        <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-seismo-orange opacity-75"></span>
                        <span class="relative inline-flex rounded-full h-2 w-2 bg-seismo-orange"></span>
                    </span>
                    <b id="ls-live" class="text-lg font-semibold text-seismo-orange tabular-nums">–</b>
                    <span class="text-xs text-gray-500 dark:text-gray-400">live</span>
                </div>
                <div class="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg bg-white dark:bg-slate-800 shadow-sm">
                    <span class="w-2 h-2 rounded-full bg-gray-400"></span>
                    <b id="ls-offline" class="text-lg font-semibold text-gray-700 dark:text-gray-200 tabular-nums">–</b>
                    <span class="text-xs text-gray-500 dark:text-gray-400">offline</span>
                </div>
                <div id="ls-loudest-wrap" class="hidden inline-flex items-center gap-2 px-3 py-1.5 rounded-lg bg-white dark:bg-slate-800 shadow-sm">
                    <span class="text-[10px] uppercase tracking-wider text-gray-500 dark:text-gray-400">Loudest now</span>
                    <b id="ls-loudest" class="text-lg font-semibold text-seismo-orange tabular-nums">–</b>
                    <span class="text-xs text-gray-500 dark:text-gray-400">dB</span>
                    <span id="ls-loudest-loc" class="text-xs text-gray-500 dark:text-gray-400"></span>
                </div>
                <span class="text-[11px] text-gray-400 dark:text-gray-500 ml-auto">auto-refresh 15s</span>
            </div>
            <div id="ls-tiles" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4"></div>
        </div>

        <div id="project-dashboard"
             hx-get="/api/projects/{{ project_id }}/dashboard"
             hx-trigger="load, every 30s"
             hx-swap="innerHTML">
            <div class="animate-pulse space-y-4">
                <div class="h-24 bg-gray-200 dark:bg-gray-700 rounded-xl"></div>
                <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
                    <div class="h-64 bg-gray-200 dark:bg-gray-700 rounded-xl"></div>
                    <div class="h-64 bg-gray-200 dark:bg-gray-700 rounded-xl"></div>
                </div>
            </div>
        </div>
    </div>

    <!-- Vibration Tab -->
    <div id="vibration-tab" class="tab-panel hidden">
        <!-- Vibration sub-nav -->
        <div class="flex gap-0 mb-5 border-b border-gray-200 dark:border-gray-700">
            <button id="vib-sub-locations-btn" onclick="switchVibSubTab('locations')"
                    class="vib-sub-tab px-4 py-2 text-sm font-medium border-b-2 border-seismo-orange text-seismo-orange -mb-px transition-colors whitespace-nowrap">
                Locations
            </button>
            <!-- Future sub-tabs: Sessions, Data Files -->
        </div>

        <!-- Vibration Locations sub-panel -->
        <div id="vib-sub-locations" class="vib-sub-panel">
            <!-- Project-wide vibration events roll-up -->
            <div id="vibration-summary"
                 hx-get="/api/projects/{{ project_id }}/vibration_summary"
                 hx-trigger="load"
                 hx-swap="innerHTML"
                 class="mb-5">
                <div class="bg-white dark:bg-slate-800 rounded-xl shadow-lg p-4">
                    <div class="text-sm text-gray-500 dark:text-gray-400">Loading project summary…</div>
                </div>
            </div>

            <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
                <div class="bg-white dark:bg-slate-800 rounded-xl shadow-lg p-6 lg:col-span-2">
                    <div class="flex items-center justify-between mb-6">
                        <h2 class="text-xl font-semibold text-gray-900 dark:text-white">Monitoring Locations</h2>
                        <button onclick="openLocationModal('vibration')"
                                class="px-4 py-2 bg-seismo-orange text-white rounded-lg hover:bg-seismo-navy transition-colors">
                            <svg class="w-5 h-5 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
                            </svg>
                            Add Location
                        </button>
                    </div>
                    <div id="vibration-locations"
                         hx-get="/api/projects/{{ project_id }}/locations?location_type=vibration"
                         hx-trigger="load"
                         hx-swap="innerHTML">
                        <div class="text-center py-8 text-gray-500">Loading locations...</div>
                    </div>
                </div>

                {# Reusable location map — fetches from /locations-json
                   on its own.  Hovering any of the location cards on the
                   left highlights the matching pin on this map. #}
                <div>
                    {% with project_id=project_id, location_type='vibration', map_height='450px' %}
                        {% include 'partials/projects/location_map.html' %}
                    {% endwith %}
                </div>
            </div>
        </div>
    </div>

    <!-- Sound Tab -->
    <div id="sound-tab" class="tab-panel hidden">
        <!-- Sound sub-nav -->
        <div class="flex gap-0 mb-5 border-b border-gray-200 dark:border-gray-700">
            <button id="sound-sub-locations-btn" onclick="switchSoundSubTab('locations')"
                    class="sound-sub-tab px-4 py-2 text-sm font-medium border-b-2 border-seismo-orange text-seismo-orange -mb-px transition-colors whitespace-nowrap">
                NRLs
            </button>
            <button id="sound-sub-sessions-btn" onclick="switchSoundSubTab('sessions')"
                    class="sound-sub-tab px-4 py-2 text-sm font-medium border-b-2 border-transparent text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 -mb-px transition-colors whitespace-nowrap">
                Sessions
            </button>
            <button id="sound-sub-data-btn" onclick="switchSoundSubTab('data')"
                    class="sound-sub-tab px-4 py-2 text-sm font-medium border-b-2 border-transparent text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 -mb-px transition-colors whitespace-nowrap">
                Data Files
            </button>
            <button id="sound-sub-units-btn" onclick="switchSoundSubTab('units')"
                    class="sound-sub-tab hidden px-4 py-2 text-sm font-medium border-b-2 border-transparent text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 -mb-px transition-colors whitespace-nowrap">
                Assigned Units
            </button>
            <button id="sound-sub-schedules-btn" onclick="switchSoundSubTab('schedules')"
                    class="sound-sub-tab hidden px-4 py-2 text-sm font-medium border-b-2 border-transparent text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 -mb-px transition-colors whitespace-nowrap">
                Schedules
            </button>
        </div>

        <!-- NRLs sub-panel -->
        <div id="sound-sub-locations" class="sound-sub-panel">
            <div class="bg-white dark:bg-slate-800 rounded-xl shadow-lg p-6">
                <div class="flex items-center justify-between mb-6">
                    <h2 class="text-xl font-semibold text-gray-900 dark:text-white">Noise Recording Locations</h2>
                    <button onclick="openLocationModal('sound')"
                            class="px-4 py-2 bg-seismo-orange text-white rounded-lg hover:bg-seismo-navy transition-colors">
                        <svg class="w-5 h-5 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
                        </svg>
                        Add NRL
                    </button>
                </div>
                <div id="sound-locations"
                     hx-get="/api/projects/{{ project_id }}/locations?location_type=sound"
                     hx-trigger="load"
                     hx-swap="innerHTML">
                    <div class="text-center py-8 text-gray-500">Loading locations...</div>
                </div>
            </div>
        </div>

        <!-- Sessions sub-panel -->
        <div id="sound-sub-sessions" class="sound-sub-panel hidden">
            <div class="bg-white dark:bg-slate-800 rounded-xl shadow-lg p-6">
                <div class="flex items-center justify-between mb-6">
                    <h2 class="text-xl font-semibold text-gray-900 dark:text-white">Monitoring Sessions</h2>
                    <select id="sessions-filter" onchange="filterSessions()"
                            class="px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white text-sm">
                        <option value="all">All Sessions</option>
                        <option value="recording">Recording</option>
                        <option value="completed">Completed</option>
                        <option value="failed">Failed</option>
                    </select>
                </div>
                <div id="project-sessions"
                     hx-get="/api/projects/{{ project_id }}/sessions"
                     hx-trigger="load, every 30s"
                     hx-swap="innerHTML">
                    <div class="text-center py-8 text-gray-500">Loading sessions...</div>
                </div>
            </div>
            <!-- Monthly Calendar -->
            <div class="mt-6 bg-white dark:bg-slate-800 rounded-xl shadow-lg p-6">
                <h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Calendar View</h3>
                <div id="sessions-calendar"
                     hx-get="/api/projects/{{ project_id }}/sessions-calendar"
                     hx-trigger="load"
                     hx-swap="innerHTML">
                    <div class="text-center py-6 text-gray-400 text-sm">Loading calendar…</div>
                </div>
            </div>
        </div>

        <!-- Data Files sub-panel -->
        <div id="sound-sub-data" class="sound-sub-panel hidden">
            <!-- FTP File Browser (remote projects only) -->
            <div id="ftp-browser" class="mb-6"
                 hx-get="/api/projects/{{ project_id }}/ftp-browser"
                 hx-trigger="load"
                 hx-swap="innerHTML">
                <div class="bg-white dark:bg-slate-800 rounded-xl shadow-lg p-6">
                    <div class="text-center py-8 text-gray-500">Loading FTP browser...</div>
                </div>
            </div>
            <!-- Unified Files View -->
            <div class="bg-white dark:bg-slate-800 rounded-xl shadow-lg">
                <div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
                    <div class="flex items-center justify-between">
                        <h2 class="text-xl font-semibold text-gray-900 dark:text-white">Project Files</h2>
                        <div class="flex items-center gap-3">
                            <button onclick="toggleUploadAll()"
                                    class="px-3 py-2 text-sm bg-seismo-orange text-white rounded-lg hover:bg-seismo-navy transition-colors flex items-center gap-1.5">
                                <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="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"></path>
                                </svg>
                                Upload Data
                            </button>
                            <button onclick="htmx.trigger('#unified-files', 'refresh')"
                                    class="px-3 py-2 text-sm bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors">
                                <svg class="w-4 h-4 inline mr-1" 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>
                                Refresh
                            </button>
                        </div>
                    </div>
                </div>
                <!-- Upload Data Panel -->
                <div id="upload-all-panel" class="hidden border-b border-gray-200 dark:border-gray-700">
                    <div class="px-6 py-4 bg-gray-50 dark:bg-gray-800/50">
                        <p class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Bulk Import — Select Folder</p>
                        <p class="text-xs text-gray-500 dark:text-gray-400 mb-3">
                            Select your data folder directly — no zipping needed. Expected structure:
                            <code class="bg-gray-200 dark:bg-gray-700 px-1 rounded">[date]/[NRL name]/[Auto_####]/</code>.
                            NRL folders are matched to locations by name. MP3s are stored; Excel exports are skipped.
                        </p>
                        <div class="flex flex-wrap items-center gap-3">
                            <input type="file" id="upload-all-input"
                                   webkitdirectory directory multiple
                                   class="block text-sm text-gray-500 dark:text-gray-400
                                          file:mr-3 file:py-1.5 file:px-3 file:rounded-lg file:border-0
                                          file:text-sm file:font-medium file:bg-seismo-orange file:text-white
                                          hover:file:bg-seismo-navy file:cursor-pointer" />
                            <span id="upload-all-file-count" class="text-xs text-gray-500 dark:text-gray-400 hidden"></span>
                            <button id="upload-all-btn" onclick="submitUploadAll()"
                                    class="px-4 py-1.5 text-sm bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors">
                                Import
                            </button>
                            <button id="upload-all-cancel-btn" onclick="toggleUploadAll()"
                                    class="px-4 py-1.5 text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white transition-colors">
                                Cancel
                            </button>
                            <span id="upload-all-status" class="text-sm hidden"></span>
                        </div>
                        <div id="upload-all-progress-wrap" class="hidden mt-3">
                            <div class="flex justify-between text-xs text-gray-500 dark:text-gray-400 mb-1">
                                <span id="upload-all-progress-label">Uploading…</span>
                            </div>
                            <div class="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
                                <div id="upload-all-progress-bar"
                                     class="bg-green-500 h-2 rounded-full transition-all duration-300"
                                     style="width: 0%"></div>
                            </div>
                        </div>
                        <div id="upload-all-results" class="hidden mt-3 text-sm space-y-1"></div>
                    </div>
                </div>
                <div id="unified-files"
                     hx-get="/api/projects/{{ project_id }}/files-unified"
                     hx-trigger="load, refresh from:#unified-files"
                     hx-swap="innerHTML">
                    <div class="px-6 py-12 text-center text-gray-500">Loading files...</div>
                </div>
            </div>
        </div>

        <!-- Assigned Units sub-panel (remote only) -->
        <div id="sound-sub-units" class="sound-sub-panel hidden">
            <div class="bg-white dark:bg-slate-800 rounded-xl shadow-lg p-6">
                <div class="flex items-center justify-between mb-6">
                    <h2 class="text-xl font-semibold text-gray-900 dark:text-white">Assigned Units</h2>
                    <div class="text-sm text-gray-500">Units currently assigned to this project's NRLs</div>
                </div>
                <div id="project-units"
                     hx-get="/api/projects/{{ project_id }}/units"
                     hx-trigger="load, every 30s"
                     hx-swap="innerHTML">
                    <div class="text-center py-8 text-gray-500">Loading units...</div>
                </div>
            </div>
        </div>

        <!-- Schedules sub-panel (remote only) -->
        <div id="sound-sub-schedules" class="sound-sub-panel hidden">
            <div class="bg-white dark:bg-slate-800 rounded-xl shadow-lg p-6 mb-6">
                <div class="flex items-center justify-between mb-6">
                    <div>
                        <h2 class="text-xl font-semibold text-gray-900 dark:text-white">Recurring Schedules</h2>
                        <p class="text-sm text-gray-500 dark:text-gray-400 mt-1">Automated patterns that generate scheduled actions</p>
                    </div>
                    <button onclick="openScheduleModal()"
                            class="px-4 py-2 bg-seismo-orange text-white rounded-lg hover:bg-seismo-navy transition-colors">
                        <svg class="w-5 h-5 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
                        </svg>
                        Create Schedule
                    </button>
                </div>
                <div id="recurring-schedule-list"
                     hx-get="/api/projects/{{ project_id }}/recurring-schedules/partials/list"
                     hx-trigger="load, refresh from:#recurring-schedule-list"
                     hx-swap="innerHTML">
                    <div class="text-center py-8 text-gray-500">Loading recurring schedules...</div>
                </div>
            </div>
            <div class="bg-white dark:bg-slate-800 rounded-xl shadow-lg p-6">
                <div class="flex items-center justify-between mb-6">
                    <div>
                        <h2 id="schedules-title" class="text-xl font-semibold text-gray-900 dark:text-white">Upcoming Actions</h2>
                        <p id="schedules-subtitle" class="text-sm text-gray-500 dark:text-gray-400 mt-1">Scheduled start/stop/download actions</p>
                    </div>
                    <select id="schedules-filter" onchange="filterScheduledActions()"
                            class="px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white text-sm">
                        <option value="pending">Pending</option>
                        <option value="all">All</option>
                        <option value="completed">Completed</option>
                        <option value="failed">Failed</option>
                    </select>
                </div>
                <div id="project-schedules"
                     hx-get="/api/projects/{{ project_id }}/schedules?status=pending"
                     hx-trigger="load, every 30s, refresh from:#project-schedules"
                     hx-swap="innerHTML">
                    <div class="text-center py-8 text-gray-500">Loading scheduled actions...</div>
                </div>
            </div>
        </div>
    </div>

    <!-- Settings Tab -->
    <div id="settings-tab" class="tab-panel hidden">
        <div class="bg-white dark:bg-slate-800 rounded-xl shadow-lg p-6">
            <h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-6">Project Settings</h2>

            <form id="project-settings-form" class="space-y-6">
                <div>
                    <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Project Name</label>
                    <input type="text" name="name" id="settings-name"
                           class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white" required>
                </div>

                <div>
                    <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Description</label>
                    <textarea name="description" id="settings-description" rows="3"
                              class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white"></textarea>
                </div>

                <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
                    <div>
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Client Name</label>
                        <input type="text" name="client_name" id="settings-client-name"
                               class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white">
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Status</label>
                        <select name="status" id="settings-status"
                                class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white">
                            <option value="upcoming">Upcoming</option>
                            <option value="active">Active</option>
                            <option value="on_hold">On Hold</option>
                            <option value="completed">Completed</option>
                            <option value="archived">Archived</option>
                        </select>
                    </div>
                </div>

                <div>
                    <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Site Address</label>
                    <input type="text" name="site_address" id="settings-site-address"
                           class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white">
                </div>

                <div>
                    <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Site Coordinates</label>
                    <input type="text" name="site_coordinates" id="settings-site-coordinates" placeholder="40.7128,-74.0060"
                           class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white">
                    <p class="text-xs text-gray-500 mt-1">Format: latitude,longitude</p>
                </div>

                <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
                    <div>
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Start Date</label>
                        <input type="date" name="start_date" id="settings-start-date"
                               class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white">
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">End Date</label>
                        <input type="date" name="end_date" id="settings-end-date"
                               class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white">
                    </div>
                </div>

                <div id="sound-settings-section" class="hidden">
                    <div class="border-t border-gray-200 dark:border-gray-700 pt-5 mb-4">
                        <h3 class="text-base font-semibold text-gray-900 dark:text-white flex items-center gap-2">
                            <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="M15.536 8.464a5 5 0 010 7.072M12 6a7 7 0 010 14M8.464 8.464a5 5 0 000 7.072"/>
                            </svg>
                            Sound Monitoring
                        </h3>
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Data Collection</label>
                        <div class="grid grid-cols-2 gap-3">
                            <label class="flex items-start gap-3 p-3 border-2 rounded-lg cursor-pointer" id="settings-mode-manual-label">
                                <input type="radio" name="data_collection_mode" id="settings-mode-manual" value="manual"
                                       onchange="settingsUpdateModeStyles()"
                                       class="mt-0.5 accent-seismo-orange shrink-0">
                                <div>
                                    <p class="text-sm font-medium text-gray-900 dark:text-white">Manual</p>
                                    <p class="text-xs text-gray-500 dark:text-gray-400">SD card retrieved daily</p>
                                </div>
                            </label>
                            <label class="flex items-start gap-3 p-3 border-2 rounded-lg cursor-pointer" id="settings-mode-remote-label">
                                <input type="radio" name="data_collection_mode" id="settings-mode-remote" value="remote"
                                       onchange="settingsUpdateModeStyles()"
                                       class="mt-0.5 accent-seismo-orange shrink-0">
                                <div>
                                    <p class="text-sm font-medium text-gray-900 dark:text-white">Remote</p>
                                    <p class="text-xs text-gray-500 dark:text-gray-400">Modem, data pulled via FTP</p>
                                </div>
                            </label>
                        </div>
                    </div>
                </div>

                <div id="settings-success" class="hidden text-sm text-green-600 dark:text-green-400"></div>
                <div id="settings-error" class="hidden text-sm text-red-600"></div>

                <div class="flex justify-end gap-3 pt-2">
                    <button type="button" onclick="loadProjectDetails()"
                            class="px-6 py-2 border border-gray-300 dark:border-gray-600 rounded-lg text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700">
                        Reset
                    </button>
                    <button type="submit"
                            class="px-6 py-2 bg-seismo-orange hover:bg-seismo-navy text-white rounded-lg font-medium">
                        Save Changes
                    </button>
                </div>
            </form>

            <!-- Danger Zone -->
            <div class="mt-8 pt-8 border-t border-gray-200 dark:border-gray-700">
                <h3 class="text-lg font-semibold text-red-600 dark:text-red-400 mb-4">Danger Zone</h3>
                <div class="space-y-3">
                    <!-- On Hold -->
                    <div class="bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg p-4 flex items-center justify-between gap-4">
                        <div>
                            <p class="text-sm font-medium text-gray-900 dark:text-white">Put Project On Hold</p>
                            <p class="text-sm text-gray-600 dark:text-gray-400">Pause this project without archiving. Assignments and schedules remain in place.</p>
                        </div>
                        <div id="hold-btn-container" class="shrink-0">
                            <!-- Rendered by updateDangerZone() based on current status -->
                        </div>
                    </div>
                    <!-- Archive -->
                    <div class="bg-gray-50 dark:bg-gray-700/40 border border-gray-200 dark:border-gray-600 rounded-lg p-4 flex items-center justify-between gap-4">
                        <div>
                            <p class="text-sm font-medium text-gray-900 dark:text-white">Archive Project</p>
                            <p class="text-sm text-gray-600 dark:text-gray-400">Remove from active listings. All data is preserved and can be restored.</p>
                        </div>
                        <button onclick="archiveProject()"
                                class="shrink-0 px-4 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700 transition-colors text-sm">
                            Archive
                        </button>
                    </div>
                    <!-- Delete -->
                    <div class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4 flex items-center justify-between gap-4">
                        <div>
                            <p class="text-sm font-medium text-gray-900 dark:text-white">Delete Project</p>
                            <p class="text-sm text-gray-600 dark:text-gray-400">Permanently removes all project data after a 60-day grace period. This action is difficult to undo.</p>
                        </div>
                        <button onclick="openDeleteModal()"
                                class="shrink-0 px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors text-sm">
                            Delete
                        </button>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<!-- Location Modal -->
<div id="location-modal" class="hidden fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center">
    <div class="bg-white dark:bg-slate-800 rounded-xl shadow-2xl w-full max-w-3xl max-h-[90vh] overflow-y-auto m-4">
        <div class="p-6 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
            <div>
                <h2 id="location-modal-title" class="text-2xl font-bold text-gray-900 dark:text-white">Add Location</h2>
                <p class="text-gray-600 dark:text-gray-400 mt-1">Create or update a monitoring location</p>
            </div>
            <button onclick="closeLocationModal()" 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="location-form" class="p-6 space-y-4">
            <input type="hidden" id="location-id">

            <div>
                <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Name</label>
                <input type="text" name="name" id="location-name"
                       class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white" required>
            </div>

            <div>
                <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Description</label>
                <textarea name="description" id="location-description" rows="3"
                          class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white"></textarea>
            </div>

            <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
                <div>
                    <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Type</label>
                    <select name="location_type" id="location-type" onchange="updateConnectionModeVisibility()"
                            class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white">
                        <option value="sound">Sound</option>
                        <option value="vibration">Vibration</option>
                    </select>
                </div>
                <div>
                    <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Coordinates</label>
                    <input type="text" name="coordinates" id="location-coordinates" placeholder="40.7128,-74.0060"
                           class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white">
                </div>
            </div>

            <!-- Connection Mode — sound locations only -->
            <div id="connection-mode-field">
                <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Connection Mode</label>
                <div class="grid grid-cols-2 gap-3">
                    <label class="flex items-start gap-3 p-3 border-2 border-seismo-orange rounded-lg cursor-pointer bg-orange-50 dark:bg-orange-900/10" id="mode-connected-label">
                        <input type="radio" name="connection_mode" value="connected" checked
                               class="mt-0.5 text-seismo-orange" onchange="updateModeLabels()">
                        <div>
                            <div class="font-medium text-gray-900 dark:text-white text-sm">Connected</div>
                            <div class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">Remote unit accessible via modem. Supports live control and FTP download.</div>
                        </div>
                    </label>
                    <label class="flex items-start gap-3 p-3 border-2 border-gray-300 dark:border-gray-600 rounded-lg cursor-pointer" id="mode-offline-label">
                        <input type="radio" name="connection_mode" value="offline"
                               class="mt-0.5 text-seismo-orange" onchange="updateModeLabels()">
                        <div>
                            <div class="font-medium text-gray-900 dark:text-white text-sm">Offline / Manual Upload</div>
                            <div class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">No network access. Data collected from SD card and uploaded manually.</div>
                        </div>
                    </label>
                </div>
            </div>

            <div>
                <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Address</label>
                <input type="text" name="address" id="location-address"
                       class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white">
            </div>

            <div id="location-error" class="hidden text-sm text-red-600"></div>

            <div class="flex justify-end gap-3 pt-2">
                <button type="button" onclick="closeLocationModal()"
                        class="px-6 py-2 border border-gray-300 dark:border-gray-600 rounded-lg text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700">
                    Cancel
                </button>
                <button type="submit"
                        class="px-6 py-2 bg-seismo-orange hover:bg-seismo-navy text-white rounded-lg font-medium">
                    Save Location
                </button>
            </div>
        </form>
    </div>
</div>

<!-- Schedule Modal -->
<div id="schedule-modal" class="hidden fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center">
    <div class="bg-white dark:bg-slate-800 rounded-xl shadow-2xl w-full max-w-3xl max-h-[90vh] overflow-y-auto m-4">
        <div class="p-6 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
            <div>
                <h2 class="text-2xl font-bold text-gray-900 dark:text-white">Create Recurring Schedule</h2>
                <p class="text-gray-600 dark:text-gray-400 mt-1">Set up automated monitoring schedules</p>
            </div>
            <button onclick="closeScheduleModal()" 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="schedule-form" class="p-6 space-y-6">
            <!-- Schedule Name -->
            <div>
                <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Schedule Name</label>
                <input type="text" name="schedule_name" id="schedule-name"
                       placeholder="e.g., Weeknight Monitoring"
                       class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white" required>
            </div>

            <!-- Location Selection (Multiple) -->
            <div>
                <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
                    Locations
                    <span class="text-xs font-normal text-gray-500 ml-2">(select one or more)</span>
                </label>
                <div id="schedule-locations-container"
                     class="max-h-48 overflow-y-auto border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 p-2 space-y-1">
                    <div class="text-gray-500 text-sm py-2 text-center">Loading locations...</div>
                </div>
                <p id="schedule-location-empty" class="hidden text-xs text-gray-500 mt-2">
                    No locations available. Create a location first.
                </p>
                <p id="schedule-location-error" class="hidden text-xs text-red-500 mt-2">
                    Please select at least one location.
                </p>
            </div>

            <!-- Schedule Type Selection -->
            <div>
                <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">Schedule Type</label>
                <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
                    <label class="relative cursor-pointer">
                        <input type="radio" name="schedule_type" value="weekly_calendar" class="peer sr-only" checked onchange="toggleScheduleType('weekly_calendar')">
                        <div class="p-4 border-2 border-gray-200 dark:border-gray-600 rounded-lg peer-checked:border-seismo-orange peer-checked:bg-orange-50 dark:peer-checked:bg-orange-900/20 transition-colors">
                            <div class="flex items-center gap-3 mb-2">
                                <svg class="w-6 h-6 text-gray-500 peer-checked:text-seismo-orange" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
                                </svg>
                                <span class="font-medium text-gray-900 dark:text-white">Weekly Calendar</span>
                            </div>
                            <p class="text-xs text-gray-500 dark:text-gray-400">
                                Select specific days with start/end times. Ideal for weeknight monitoring (Mon-Fri 7pm-7am).
                            </p>
                        </div>
                    </label>
                    <label class="relative cursor-pointer">
                        <input type="radio" name="schedule_type" value="simple_interval" class="peer sr-only" onchange="toggleScheduleType('simple_interval')">
                        <div class="p-4 border-2 border-gray-200 dark:border-gray-600 rounded-lg peer-checked:border-seismo-orange peer-checked:bg-orange-50 dark:peer-checked:bg-orange-900/20 transition-colors">
                            <div class="flex items-center gap-3 mb-2">
                                <svg class="w-6 h-6 text-gray-500 peer-checked:text-seismo-orange" 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"/>
                                </svg>
                                <span class="font-medium text-gray-900 dark:text-white">24/7 Continuous</span>
                            </div>
                            <p class="text-xs text-gray-500 dark:text-gray-400">
                                Continuous monitoring with daily stop/download/restart cycle at a set time.
                            </p>
                        </div>
                    </label>
                    <label class="relative cursor-pointer">
                        <input type="radio" name="schedule_type" value="one_off" class="peer sr-only" onchange="toggleScheduleType('one_off')">
                        <div class="p-4 border-2 border-gray-200 dark:border-gray-600 rounded-lg peer-checked:border-seismo-orange peer-checked:bg-orange-50 dark:peer-checked:bg-orange-900/20 transition-colors">
                            <div class="flex items-center gap-3 mb-2">
                                <svg class="w-6 h-6 text-gray-500 peer-checked:text-seismo-orange" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
                                </svg>
                                <span class="font-medium text-gray-900 dark:text-white">One-Off Recording</span>
                            </div>
                            <p class="text-xs text-gray-500 dark:text-gray-400">
                                Single monitoring session with a specific start and end date/time (15 min - 24 hrs).
                            </p>
                        </div>
                    </label>
                </div>
            </div>

            <!-- Weekly Calendar Editor -->
            <div id="schedule-weekly-wrapper">
                {% include "partials/projects/schedule_calendar.html" %}
            </div>

            <!-- Simple Interval Editor -->
            <div id="schedule-interval-wrapper" class="hidden">
                {% include "partials/projects/schedule_interval.html" %}
            </div>

            <!-- One-Off Editor -->
            <div id="schedule-oneoff-wrapper" class="hidden">
                {% include "partials/projects/schedule_oneoff.html" %}
            </div>

            <!-- Timezone -->
            <div>
                <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Timezone</label>
                <select name="timezone" id="schedule-timezone"
                        class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white">
                    <option value="America/New_York">Eastern (America/New_York)</option>
                    <option value="America/Chicago">Central (America/Chicago)</option>
                    <option value="America/Denver">Mountain (America/Denver)</option>
                    <option value="America/Los_Angeles">Pacific (America/Los_Angeles)</option>
                    <option value="UTC">UTC</option>
                </select>
            </div>

            <div id="schedule-error" class="hidden text-sm text-red-600"></div>

            <div class="flex justify-end gap-3 pt-2">
                <button type="button" onclick="closeScheduleModal()"
                        class="px-6 py-2 border border-gray-300 dark:border-gray-600 rounded-lg text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700">
                    Cancel
                </button>
                <button type="submit"
                        class="px-6 py-2 bg-seismo-orange hover:bg-seismo-navy text-white rounded-lg font-medium">
                    Create Schedule
                </button>
            </div>
        </form>
    </div>
</div>

<!-- Assign Unit Modal -->
<div id="assign-modal" class="hidden fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center">
    <div class="bg-white dark:bg-slate-800 rounded-xl shadow-2xl w-full max-w-2xl max-h-[90vh] overflow-y-auto m-4">
        <div class="p-6 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
            <div>
                <h2 class="text-2xl font-bold text-gray-900 dark:text-white">Assign Unit</h2>
                <p class="text-gray-600 dark:text-gray-400 mt-1">Attach a device to this location</p>
            </div>
            <button onclick="closeAssignModal()" 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="assign-form" class="p-6 space-y-4">
            <input type="hidden" id="assign-location-id">
            <input type="hidden" id="assign-location-type">

            <div>
                <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Available Units</label>
                <select id="assign-unit-id" name="unit_id"
                        class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white" required>
                    <option value="">Select a unit</option>
                </select>
                <p id="assign-empty" class="hidden text-xs text-gray-500 mt-2">No available units match this location type.</p>
            </div>

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

            <div id="assign-error" class="hidden text-sm text-red-600"></div>

            <div class="flex justify-end gap-3 pt-2">
                <button type="button" onclick="closeAssignModal()"
                        class="px-6 py-2 border border-gray-300 dark:border-gray-600 rounded-lg text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700">
                    Cancel
                </button>
                <button type="submit"
                        class="px-6 py-2 bg-seismo-orange hover:bg-seismo-navy text-white rounded-lg font-medium">
                    Assign Unit
                </button>
            </div>
        </form>
    </div>
</div>

<!-- Remove Location Confirmation Modal —
     Soft-removal: preserves historical events, closes active assignments,
     cancels pending scheduled actions.  Distinct from Delete (which is
     permanent and only allowed when there's no history). -->
<div id="remove-location-modal" class="hidden fixed inset-0 bg-black bg-opacity-60 z-50 flex items-center justify-center">
    <div class="bg-white dark:bg-slate-800 rounded-xl shadow-2xl w-full max-w-md m-4 p-6">
        <div class="flex items-center gap-3 mb-4">
            <div class="p-2 bg-amber-100 dark:bg-amber-900/40 rounded-lg">
                <svg class="w-6 h-6 text-amber-600 dark:text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 17v-2a4 4 0 014-4h6m0 0l-3-3m3 3l-3 3M5 7h8a2 2 0 012 2v10a2 2 0 01-2 2H5a2 2 0 01-2-2V9a2 2 0 012-2z"/>
                </svg>
            </div>
            <h3 class="text-lg font-semibold text-gray-900 dark:text-white">Remove location</h3>
        </div>
        <p class="text-sm text-gray-600 dark:text-gray-400 mb-4">
            Mark <span id="remove-location-name" class="font-semibold text-gray-900 dark:text-white">…</span> as no longer actively monitored.
        </p>
        <ul class="text-xs text-gray-500 dark:text-gray-400 mb-4 space-y-1 ml-4 list-disc">
            <li>Closes any active unit assignment at this location</li>
            <li>Cancels pending scheduled actions at this location</li>
            <li>Historical events stay attributed (visible in reports + event lists)</li>
            <li>Can be restored later if needed</li>
        </ul>

        <input type="hidden" id="remove-location-id">

        <div class="mb-3">
            <label class="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1">Effective date</label>
            <input type="datetime-local" id="remove-location-effective"
                   class="w-full px-3 py-1.5 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white text-sm">
            <p class="text-[10px] text-gray-500 dark:text-gray-400 mt-1">Defaults to now. Backdate if the location was physically removed earlier.</p>
        </div>

        <div class="mb-4">
            <label class="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1">Reason (optional)</label>
            <input type="text" id="remove-location-reason" maxlength="200"
                   placeholder="e.g. client dropped from scope"
                   class="w-full px-3 py-1.5 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white text-sm">
        </div>

        <div id="remove-location-error" class="hidden text-sm text-red-600 mb-3"></div>

        <div class="flex justify-end gap-2">
            <button onclick="closeRemoveLocationModal()"
                    class="px-4 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-lg text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700">
                Cancel
            </button>
            <button onclick="confirmRemoveLocation()"
                    class="px-4 py-1.5 text-sm bg-amber-600 hover:bg-amber-700 text-white rounded-lg font-medium">
                Remove
            </button>
        </div>
    </div>
</div>

<!-- Delete Project Confirmation Modal -->
<div id="delete-project-modal" class="hidden fixed inset-0 bg-black bg-opacity-60 z-50 flex items-center justify-center">
    <div class="bg-white dark:bg-slate-800 rounded-xl shadow-2xl w-full max-w-md m-4 p-6">
        <div class="flex items-center gap-3 mb-4">
            <div class="p-2 bg-red-100 dark:bg-red-900/40 rounded-lg">
                <svg class="w-6 h-6 text-red-600 dark:text-red-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"></path>
                </svg>
            </div>
            <h3 class="text-lg font-bold text-gray-900 dark:text-white">Delete Project</h3>
        </div>
        <p class="text-sm text-gray-600 dark:text-gray-400 mb-2">
            This project will be soft-deleted and <strong class="text-gray-900 dark:text-white">permanently removed after 60 days</strong>. All associated locations, assignments, and sessions will be lost.
        </p>
        <p class="text-sm text-gray-700 dark:text-gray-300 mb-4">
            Type <span class="font-mono font-bold text-red-600 dark:text-red-400">delete</span> to confirm:
        </p>
        <input type="text" id="delete-confirm-input"
               placeholder="type delete"
               class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white mb-4 focus:outline-none focus:ring-2 focus:ring-red-500"
               autocomplete="off">
        <div class="flex gap-3 justify-end">
            <button onclick="closeDeleteModal()"
                    class="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 text-sm">
                Cancel
            </button>
            <button id="confirm-delete-btn" disabled onclick="executeDeleteProject()"
                    class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors text-sm disabled:opacity-40 disabled:cursor-not-allowed">
                Delete Project
            </button>
        </div>
    </div>
</div>

<script>
const projectId = "{{ project_id }}";
let editingLocationId = null;
let projectModules = [];  // list of enabled module_type strings, e.g. ['sound_monitoring']

async function quickUpdateStatus(newStatus) {
    try {
        const response = await fetch(`/api/projects/${projectId}`, {
            method: 'PUT',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({ status: newStatus })
        });
        if (response.ok) {
            // Reload the page to reflect new badge color and any side effects
            window.location.reload();
        } else {
            alert('Failed to update status');
        }
    } catch (e) {
        alert('Error updating status');
    }
}

// Tab switching
function switchTab(tabName) {
    document.querySelectorAll('.tab-panel').forEach(panel => panel.classList.add('hidden'));
    document.querySelectorAll('.tab-button').forEach(button => {
        button.classList.remove('border-seismo-orange', 'text-seismo-orange');
        button.classList.add('border-transparent', 'text-gray-600', 'dark:text-gray-400');
    });

    const panel = document.getElementById(`${tabName}-tab`);
    if (panel) panel.classList.remove('hidden');

    const button = document.querySelector(`[data-tab="${tabName}"]`);
    if (button) {
        button.classList.remove('border-transparent', 'text-gray-600', 'dark:text-gray-400');
        button.classList.add('border-seismo-orange', 'text-seismo-orange');
    }

    history.replaceState(null, '', `#${tabName}`);
}

function switchVibSubTab(name) {
    document.querySelectorAll('.vib-sub-panel').forEach(p => p.classList.add('hidden'));
    document.querySelectorAll('.vib-sub-tab').forEach(b => {
        b.classList.remove('border-seismo-orange', 'text-seismo-orange');
        b.classList.add('border-transparent', 'text-gray-500', 'dark:text-gray-400');
    });
    document.getElementById(`vib-sub-${name}`)?.classList.remove('hidden');
    const btn = document.getElementById(`vib-sub-${name}-btn`);
    if (btn) {
        btn.classList.remove('border-transparent', 'text-gray-500', 'dark:text-gray-400');
        btn.classList.add('border-seismo-orange', 'text-seismo-orange');
    }
}

function switchSoundSubTab(name) {
    document.querySelectorAll('.sound-sub-panel').forEach(p => p.classList.add('hidden'));
    document.querySelectorAll('.sound-sub-tab').forEach(b => {
        b.classList.remove('border-seismo-orange', 'text-seismo-orange');
        b.classList.add('border-transparent', 'text-gray-500', 'dark:text-gray-400');
    });
    document.getElementById(`sound-sub-${name}`)?.classList.remove('hidden');
    const btn = document.getElementById(`sound-sub-${name}-btn`);
    if (btn) {
        btn.classList.remove('border-transparent', 'text-gray-500', 'dark:text-gray-400');
        btn.classList.add('border-seismo-orange', 'text-seismo-orange');
    }
}

// Load project details
async function loadProjectDetails() {
    try {
        const response = await fetch(`/api/projects/${projectId}`);
        if (!response.ok) {
            throw new Error('Failed to load project details');
        }
        const data = await response.json();
        projectModules = data.modules || [];

        // Update breadcrumb
        document.getElementById('project-name-breadcrumb').textContent = data.name || 'Project';

        // Update settings form
        document.getElementById('settings-name').value = data.name || '';
        document.getElementById('settings-description').value = data.description || '';
        document.getElementById('settings-client-name').value = data.client_name || '';
        document.getElementById('settings-status').value = data.status || 'active';
        document.getElementById('settings-site-address').value = data.site_address || '';
        document.getElementById('settings-site-coordinates').value = data.site_coordinates || '';
        document.getElementById('settings-start-date').value = formatDate(data.start_date);
        document.getElementById('settings-end-date').value = formatDate(data.end_date);

        // Update data collection mode radio
        const mode = data.data_collection_mode || 'manual';
        const modeRadio = document.getElementById('settings-mode-' + mode);
        if (modeRadio) modeRadio.checked = true;
        settingsUpdateModeStyles();

        // Show/hide module tabs based on active modules
        const hasSoundModule = projectModules.includes('sound_monitoring');
        const hasVibrationModule = projectModules.includes('vibration_monitoring');
        const isRemote = mode === 'remote';

        document.getElementById('vibration-tab-btn').classList.toggle('hidden', !hasVibrationModule);
        document.getElementById('sound-tab-btn').classList.toggle('hidden', !hasSoundModule);
        document.getElementById('sound-settings-section')?.classList.toggle('hidden', !hasSoundModule);

        // Live monitoring section: only for sound projects (idempotent).
        if (hasSoundModule) startLiveStats();
        else document.getElementById('live-stats-section')?.classList.add('hidden');

        // Within Sound: show Assigned Units + Schedules sub-tabs only for remote projects
        document.getElementById('sound-sub-units-btn')?.classList.toggle('hidden', !isRemote);
        document.getElementById('sound-sub-schedules-btn')?.classList.toggle('hidden', !isRemote);

        // FTP browser: only show for remote projects
        document.getElementById('ftp-browser')?.classList.toggle('hidden', !isRemote);

        document.getElementById('settings-error').classList.add('hidden');
        updateDangerZone();
    } catch (err) {
        console.error('Failed to load project details:', err);
    }
}

function formatDate(value) {
    if (!value) return '';
    const date = new Date(value);
    if (Number.isNaN(date.getTime())) return '';
    return date.toISOString().slice(0, 10);
}

function settingsUpdateModeStyles() {
    const manualChecked = document.getElementById('settings-mode-manual')?.checked;
    const manualLabel = document.getElementById('settings-mode-manual-label');
    const remoteLabel = document.getElementById('settings-mode-remote-label');
    if (!manualLabel || !remoteLabel) return;
    if (manualChecked) {
        manualLabel.classList.add('border-seismo-orange', 'bg-orange-50', 'dark:bg-orange-900/20');
        manualLabel.classList.remove('border-gray-300', 'dark:border-gray-600');
        remoteLabel.classList.remove('border-seismo-orange', 'bg-orange-50', 'dark:bg-orange-900/20');
        remoteLabel.classList.add('border-gray-300', 'dark:border-gray-600');
    } else {
        remoteLabel.classList.add('border-seismo-orange', 'bg-orange-50', 'dark:bg-orange-900/20');
        remoteLabel.classList.remove('border-gray-300', 'dark:border-gray-600');
        manualLabel.classList.remove('border-seismo-orange', 'bg-orange-50', 'dark:bg-orange-900/20');
        manualLabel.classList.add('border-gray-300', 'dark:border-gray-600');
    }
}

// Project settings form submission
document.getElementById('project-settings-form').addEventListener('submit', async function(e) {
    e.preventDefault();

    const payload = {
        name: document.getElementById('settings-name').value.trim(),
        description: document.getElementById('settings-description').value.trim() || null,
        client_name: document.getElementById('settings-client-name').value.trim() || null,
        status: document.getElementById('settings-status').value,
        site_address: document.getElementById('settings-site-address').value.trim() || null,
        site_coordinates: document.getElementById('settings-site-coordinates').value.trim() || null,
        start_date: document.getElementById('settings-start-date').value || null,
        end_date: document.getElementById('settings-end-date').value || null,
        data_collection_mode: document.querySelector('input[name="data_collection_mode"]:checked')?.value || 'manual'
    };

    try {
        const response = await fetch(`/api/projects/${projectId}`, {
            method: 'PUT',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify(payload)
        });

        if (!response.ok) {
            throw new Error('Failed to update project');
        }

        // Refresh header and dashboard without full page reload
        refreshProjectDashboard();

        // Show success feedback
        const successEl = document.getElementById('settings-success');
        successEl.textContent = 'Settings saved.';
        successEl.classList.remove('hidden');
        document.getElementById('settings-error').classList.add('hidden');
        setTimeout(() => successEl.classList.add('hidden'), 3000);
    } catch (err) {
        const errorEl = document.getElementById('settings-error');
        errorEl.textContent = err.message || 'Failed to update project.';
        errorEl.classList.remove('hidden');
        document.getElementById('settings-success').classList.add('hidden');
    }
});

function refreshProjectDashboard() {
    htmx.ajax('GET', `/api/projects/${projectId}/dashboard`, {
        target: '#project-dashboard',
        swap: 'innerHTML'
    });
    htmx.ajax('GET', `/api/projects/${projectId}/header`, {
        target: '#project-header',
        swap: 'innerHTML'
    });
}

// Location modal functions
function updateConnectionModeVisibility() {
    const locType = document.getElementById('location-type').value;
    const field = document.getElementById('connection-mode-field');
    if (field) field.classList.toggle('hidden', locType !== 'sound');
}

function updateModeLabels() {
    const connected = document.querySelector('input[name="connection_mode"][value="connected"]');
    const offline = document.querySelector('input[name="connection_mode"][value="offline"]');
    const connLabel = document.getElementById('mode-connected-label');
    const offLabel = document.getElementById('mode-offline-label');
    if (!connected || !connLabel || !offLabel) return;
    const activeClasses = ['border-seismo-orange', 'bg-orange-50', 'dark:bg-orange-900/10'];
    const inactiveClasses = ['border-gray-300', 'dark:border-gray-600'];
    if (connected.checked) {
        connLabel.classList.add(...activeClasses);
        connLabel.classList.remove(...inactiveClasses);
        offLabel.classList.remove(...activeClasses);
        offLabel.classList.add(...inactiveClasses);
    } else {
        offLabel.classList.add(...activeClasses);
        offLabel.classList.remove(...inactiveClasses);
        connLabel.classList.remove(...activeClasses);
        connLabel.classList.add(...inactiveClasses);
    }
}

// Tracks the active location type tab so "Add Location" opens with the right type
let _activeLocationType = null;
function setActiveLocationType(type) {
    _activeLocationType = type;
}

function openLocationModal(defaultType) {
    defaultType = defaultType || _activeLocationType || defaultType;
    editingLocationId = null;
    document.getElementById('location-modal-title').textContent = 'Add Location';
    document.getElementById('location-id').value = '';
    document.getElementById('location-name').value = '';
    document.getElementById('location-description').value = '';
    document.getElementById('location-address').value = '';
    document.getElementById('location-coordinates').value = '';
    // Reset connection mode to connected
    const connectedRadio = document.querySelector('input[name="connection_mode"][value="connected"]');
    if (connectedRadio) { connectedRadio.checked = true; updateModeLabels(); }
    const locationTypeSelect = document.getElementById('location-type');
    const locationTypeWrapper = locationTypeSelect.closest('div');
    const hasSoundMod = projectModules.includes('sound_monitoring');
    const hasVibMod = projectModules.includes('vibration_monitoring');
    if (hasSoundMod && !hasVibMod) {
        locationTypeSelect.value = 'sound';
        locationTypeSelect.disabled = true;
        if (locationTypeWrapper) locationTypeWrapper.classList.add('hidden');
    } else if (hasVibMod && !hasSoundMod) {
        locationTypeSelect.value = 'vibration';
        locationTypeSelect.disabled = true;
        if (locationTypeWrapper) locationTypeWrapper.classList.add('hidden');
    } else {
        locationTypeSelect.disabled = false;
        if (locationTypeWrapper) locationTypeWrapper.classList.remove('hidden');
        locationTypeSelect.value = defaultType || 'sound';
    }
    updateConnectionModeVisibility();
    document.getElementById('location-error').classList.add('hidden');
    document.getElementById('location-modal').classList.remove('hidden');
}

function openEditLocationModal(button) {
    const data = JSON.parse(button.dataset.location);
    editingLocationId = data.id;
    document.getElementById('location-modal-title').textContent = 'Edit Location';
    document.getElementById('location-id').value = data.id;
    document.getElementById('location-name').value = data.name || '';
    document.getElementById('location-description').value = data.description || '';
    document.getElementById('location-address').value = data.address || '';
    document.getElementById('location-coordinates').value = data.coordinates || '';
    // Restore connection mode from metadata
    let savedMode = 'connected';
    try { savedMode = JSON.parse(data.location_metadata || '{}').connection_mode || 'connected'; } catch(e) {}
    const modeRadio = document.querySelector(`input[name="connection_mode"][value="${savedMode}"]`);
    if (modeRadio) { modeRadio.checked = true; updateModeLabels(); }
    const locationTypeSelect = document.getElementById('location-type');
    const locationTypeWrapper = locationTypeSelect.closest('div');
    const hasSoundModE = projectModules.includes('sound_monitoring');
    const hasVibModE = projectModules.includes('vibration_monitoring');
    if (hasSoundModE && !hasVibModE) {
        locationTypeSelect.value = 'sound';
        locationTypeSelect.disabled = true;
        if (locationTypeWrapper) locationTypeWrapper.classList.add('hidden');
    } else if (hasVibModE && !hasSoundModE) {
        locationTypeSelect.value = 'vibration';
        locationTypeSelect.disabled = true;
        if (locationTypeWrapper) locationTypeWrapper.classList.add('hidden');
    } else {
        locationTypeSelect.disabled = false;
        if (locationTypeWrapper) locationTypeWrapper.classList.remove('hidden');
        locationTypeSelect.value = data.location_type || 'sound';
    }
    updateConnectionModeVisibility();
    document.getElementById('location-error').classList.add('hidden');
    document.getElementById('location-modal').classList.remove('hidden');
}

function closeLocationModal() {
    document.getElementById('location-modal').classList.add('hidden');
}

document.getElementById('location-form').addEventListener('submit', async function(e) {
    e.preventDefault();

    const name = document.getElementById('location-name').value.trim();
    const description = document.getElementById('location-description').value.trim();
    const address = document.getElementById('location-address').value.trim();
    const coordinates = document.getElementById('location-coordinates').value.trim();
    let locationType = document.getElementById('location-type').value;
    if (projectModules.includes('sound_monitoring') && !projectModules.includes('vibration_monitoring')) {
        locationType = 'sound';
    } else if (projectModules.includes('vibration_monitoring') && !projectModules.includes('sound_monitoring')) {
        locationType = 'vibration';
    }

    const connectionMode = document.querySelector('input[name="connection_mode"]:checked')?.value || 'connected';

    try {
        if (editingLocationId) {
            const payload = {
                name,
                description: description || null,
                address: address || null,
                coordinates: coordinates || null,
                location_type: locationType,
                location_metadata: JSON.stringify({ connection_mode: connectionMode }),
            };
            const response = await fetch(`/api/projects/${projectId}/locations/${editingLocationId}`, {
                method: 'PUT',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify(payload)
            });
            if (!response.ok) {
                const data = await response.json().catch(() => ({}));
                throw new Error(data.detail || 'Failed to update location');
            }
        } else {
            const formData = new FormData();
            formData.append('name', name);
            formData.append('description', description);
            formData.append('address', address);
            formData.append('coordinates', coordinates);
            formData.append('location_type', locationType);
            formData.append('location_metadata', JSON.stringify({ connection_mode: connectionMode }));
            const response = await fetch(`/api/projects/${projectId}/locations/create`, {
                method: 'POST',
                body: formData
            });
            if (!response.ok) {
                const data = await response.json().catch(() => ({}));
                throw new Error(data.detail || 'Failed to create location');
            }
        }

        closeLocationModal();
        refreshLocationLists();
        refreshProjectDashboard();
    } catch (err) {
        const errorEl = document.getElementById('location-error');
        errorEl.textContent = err.message || 'Failed to save location.';
        errorEl.classList.remove('hidden');
    }
});

function refreshLocationLists() {
    htmx.ajax('GET', `/api/projects/${projectId}/locations?location_type=sound`, {
        target: '#sound-locations', swap: 'innerHTML'
    });
    htmx.ajax('GET', `/api/projects/${projectId}/locations?location_type=vibration`, {
        target: '#vibration-locations', swap: 'innerHTML'
    });
}

async function deleteLocation(locationId) {
    if (!confirm('Delete this location?')) return;

    try {
        const response = await fetch(`/api/projects/${projectId}/locations/${locationId}`, {
            method: 'DELETE'
        });
        if (!response.ok) {
            const data = await response.json().catch(() => ({}));
            throw new Error(data.detail || 'Failed to delete location');
        }
        refreshLocationLists();
        refreshProjectDashboard();
    } catch (err) {
        alert(err.message || 'Failed to delete location.');
    }
}

// ── Remove / Restore location ────────────────────────────────────────
// Soft-removal: marks a location as no longer actively monitored without
// destroying it.  Historical events stay attributed; active assignments
// are auto-closed and pending scheduled actions are auto-cancelled.

function openRemoveLocationModal(locationId, locationName) {
    document.getElementById('remove-location-id').value = locationId;
    document.getElementById('remove-location-name').textContent = locationName;
    document.getElementById('remove-location-reason').value = '';
    // Default effective_date to "now" in local datetime-input format.
    const now = new Date();
    const tzOffsetMin = now.getTimezoneOffset();
    const local = new Date(now.getTime() - tzOffsetMin * 60000);
    document.getElementById('remove-location-effective').value =
        local.toISOString().slice(0, 16);
    document.getElementById('remove-location-error').classList.add('hidden');
    document.getElementById('remove-location-modal').classList.remove('hidden');
}

function closeRemoveLocationModal() {
    document.getElementById('remove-location-modal').classList.add('hidden');
}

async function confirmRemoveLocation() {
    const locationId = document.getElementById('remove-location-id').value;
    const reason     = document.getElementById('remove-location-reason').value.trim();
    const effective  = document.getElementById('remove-location-effective').value;
    const errBox     = document.getElementById('remove-location-error');
    errBox.classList.add('hidden');

    try {
        const response = await fetch(
            `/api/projects/${projectId}/locations/${locationId}/remove`,
            {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    reason: reason || null,
                    effective_date: effective || null,
                }),
            }
        );
        if (!response.ok) {
            const data = await response.json().catch(() => ({}));
            throw new Error(data.detail || 'Failed to remove location');
        }
        const result = await response.json();
        closeRemoveLocationModal();
        refreshLocationLists();
        refreshProjectDashboard();
        // Lightweight feedback — the UI refresh already shows the location
        // moving to the Removed section, but a toast confirms the cascade.
        if (typeof showToast === 'function') {
            const bits = [];
            if (result.assignments_closed) bits.push(`${result.assignments_closed} assignment(s) closed`);
            if (result.actions_cancelled)  bits.push(`${result.actions_cancelled} action(s) cancelled`);
            const tail = bits.length ? ` (${bits.join(', ')})` : '';
            showToast(`Location removed${tail}`, 'success');
        }
    } catch (err) {
        errBox.textContent = err.message || 'Failed to remove location.';
        errBox.classList.remove('hidden');
    }
}

async function restoreLocation(locationId, locationName) {
    if (!confirm(`Restore "${locationName}" to active monitoring?\n\nNote: previously-closed assignments are NOT automatically re-opened — you'll need to re-assign units if you want to resume monitoring.`)) {
        return;
    }
    try {
        const response = await fetch(
            `/api/projects/${projectId}/locations/${locationId}/restore`,
            { method: 'POST' }
        );
        if (!response.ok) {
            const data = await response.json().catch(() => ({}));
            throw new Error(data.detail || 'Failed to restore location');
        }
        refreshLocationLists();
        refreshProjectDashboard();
        if (typeof showToast === 'function') {
            showToast(`"${locationName}" restored to active`, 'success');
        }
    } catch (err) {
        alert(err.message || 'Failed to restore location.');
    }
}

// Assign modal functions
function openAssignModal(locationId, locationType) {
    const safeType = locationType || 'sound';
    document.getElementById('assign-location-id').value = locationId;
    document.getElementById('assign-location-type').value = safeType;
    document.getElementById('assign-unit-id').innerHTML = '<option value="">Loading units...</option>';
    document.getElementById('assign-empty').classList.add('hidden');
    document.getElementById('assign-error').classList.add('hidden');
    document.getElementById('assign-modal').classList.remove('hidden');
    loadAvailableUnits(safeType);
}

function closeAssignModal() {
    document.getElementById('assign-modal').classList.add('hidden');
}

async function loadAvailableUnits(locationType) {
    try {
        const response = await fetch(`/api/projects/${projectId}/available-units?location_type=${locationType}`);
        if (!response.ok) {
            throw new Error('Failed to load available units');
        }
        const data = await response.json();
        const select = document.getElementById('assign-unit-id');
        select.innerHTML = '<option value="">Select a unit</option>';
        if (!data.length) {
            document.getElementById('assign-empty').classList.remove('hidden');
            return;
        }
        data.forEach(unit => {
            const option = document.createElement('option');
            option.value = unit.id;
            option.textContent = `${unit.id} • ${unit.model || unit.device_type}`;
            select.appendChild(option);
        });
    } catch (err) {
        const errorEl = document.getElementById('assign-error');
        errorEl.textContent = err.message || 'Failed to load units.';
        errorEl.classList.remove('hidden');
    }
}

document.getElementById('assign-form').addEventListener('submit', async function(e) {
    e.preventDefault();

    const locationId = document.getElementById('assign-location-id').value;
    const unitId = document.getElementById('assign-unit-id').value;
    const notes = document.getElementById('assign-notes').value.trim();

    if (!unitId) {
        document.getElementById('assign-error').textContent = 'Select a unit to assign.';
        document.getElementById('assign-error').classList.remove('hidden');
        return;
    }

    try {
        const formData = new FormData();
        formData.append('unit_id', unitId);
        formData.append('notes', notes);
        const response = await fetch(`/api/projects/${projectId}/locations/${locationId}/assign`, {
            method: 'POST',
            body: formData
        });
        if (!response.ok) {
            const data = await response.json().catch(() => ({}));
            throw new Error(data.detail || 'Failed to assign unit');
        }
        closeAssignModal();
        refreshLocationLists();
        refreshProjectDashboard();
    } catch (err) {
        const errorEl = document.getElementById('assign-error');
        errorEl.textContent = err.message || 'Failed to assign unit.';
        errorEl.classList.remove('hidden');
    }
});

async function unassignUnit(assignmentId) {
    if (!confirm('Unassign this unit?')) return;

    try {
        const response = await fetch(`/api/projects/${projectId}/assignments/${assignmentId}/unassign`, {
            method: 'POST'
        });
        if (!response.ok) {
            const data = await response.json().catch(() => ({}));
            throw new Error(data.detail || 'Failed to unassign unit');
        }
        refreshLocationLists();
        refreshProjectDashboard();
    } catch (err) {
        alert(err.message || 'Failed to unassign unit.');
    }
}

// Filter functions
function filterSessions() {
    const filter = document.getElementById('sessions-filter').value;
    const url = filter === 'all'
        ? `/api/projects/${projectId}/sessions`
        : `/api/projects/${projectId}/sessions?status=${filter}`;

    htmx.ajax('GET', url, {
        target: '#project-sessions',
        swap: 'innerHTML'
    });
}

function filterFiles() {
    const filter = document.getElementById('files-filter').value;
    const url = filter === 'all'
        ? `/api/projects/${projectId}/files`
        : `/api/projects/${projectId}/files?type=${filter}`;

    htmx.ajax('GET', url, {
        target: '#project-files',
        swap: 'innerHTML'
    });
}

function filterScheduledActions() {
    const filter = document.getElementById('schedules-filter').value;
    const url = filter === 'all'
        ? `/api/projects/${projectId}/schedules`
        : `/api/projects/${projectId}/schedules?status=${filter}`;

    // Update section title based on filter
    const titleEl = document.getElementById('schedules-title');
    const subtitleEl = document.getElementById('schedules-subtitle');

    const titles = {
        'pending': { title: 'Upcoming Actions', subtitle: 'Scheduled start/stop/download actions' },
        'completed': { title: 'Completed Actions', subtitle: 'Successfully executed actions' },
        'failed': { title: 'Failed Actions', subtitle: 'Actions that encountered errors' },
        'all': { title: 'All Actions', subtitle: 'Complete action history' }
    };

    const config = titles[filter] || titles['all'];
    titleEl.textContent = config.title;
    subtitleEl.textContent = config.subtitle;

    htmx.ajax('GET', url, {
        target: '#project-schedules',
        swap: 'innerHTML'
    });
}

// Utility functions
function exportProjectData() {
    window.location.href = `/api/projects/${projectId}/export`;
}

function archiveProject() {
    if (!confirm('Archive this project? You can restore it later from the archived projects list.')) return;

    document.getElementById('settings-status').value = 'archived';
    document.getElementById('project-settings-form').dispatchEvent(new Event('submit'));
}

async function holdProject() {
    try {
        const response = await fetch(`/api/projects/${projectId}/hold`, { method: 'POST' });
        if (!response.ok) throw new Error('Failed to put project on hold');
        await loadProjectDetails();
        updateDangerZone();
        htmx.trigger('#project-header', 'load');
    } catch (err) {
        alert('Failed to put project on hold: ' + err.message);
    }
}

async function unholdProject() {
    try {
        const response = await fetch(`/api/projects/${projectId}/unhold`, { method: 'POST' });
        if (!response.ok) throw new Error('Failed to resume project');
        await loadProjectDetails();
        updateDangerZone();
        htmx.trigger('#project-header', 'load');
    } catch (err) {
        alert('Failed to resume project: ' + err.message);
    }
}

function updateDangerZone() {
    const status = document.getElementById('settings-status').value;
    const container = document.getElementById('hold-btn-container');
    if (!container) return;
    if (status === 'on_hold') {
        container.innerHTML = `<button onclick="unholdProject()"
            class="px-4 py-2 bg-amber-500 text-white rounded-lg hover:bg-amber-600 transition-colors text-sm">
            Resume Project
        </button>`;
    } else {
        container.innerHTML = `<button onclick="holdProject()"
            class="px-4 py-2 border border-amber-500 text-amber-600 dark:text-amber-400 rounded-lg hover:bg-amber-50 dark:hover:bg-amber-900/20 transition-colors text-sm">
            Put On Hold
        </button>`;
    }
}

function openDeleteModal() {
    document.getElementById('delete-confirm-input').value = '';
    document.getElementById('confirm-delete-btn').disabled = true;
    document.getElementById('delete-project-modal').classList.remove('hidden');
    document.getElementById('delete-confirm-input').focus();
}

function closeDeleteModal() {
    document.getElementById('delete-project-modal').classList.add('hidden');
}

async function executeDeleteProject() {
    try {
        const response = await fetch(`/api/projects/${projectId}`, { method: 'DELETE' });
        if (!response.ok) throw new Error('Failed to delete project');
        closeDeleteModal();
        window.location.href = '/projects';
    } catch (err) {
        alert('Failed to delete project: ' + err.message);
    }
}

document.addEventListener('DOMContentLoaded', function() {
    const input = document.getElementById('delete-confirm-input');
    if (input) {
        input.addEventListener('input', function() {
            document.getElementById('confirm-delete-btn').disabled = this.value !== 'delete';
        });
    }
});

// ============================================================================
// Schedule Modal Functions
// ============================================================================

async function openScheduleModal() {
    // Reset form
    document.getElementById('schedule-name').value = '';
    document.getElementById('schedule-locations-container').innerHTML = '<div class="text-gray-500 text-sm py-2 text-center">Loading locations...</div>';
    document.getElementById('schedule-location-empty').classList.add('hidden');
    document.getElementById('schedule-location-error').classList.add('hidden');
    document.getElementById('schedule-error').classList.add('hidden');

    // Reset to weekly calendar type
    document.querySelector('input[name="schedule_type"][value="weekly_calendar"]').checked = true;
    toggleScheduleType('weekly_calendar');

    // Reset calendar checkboxes
    if (typeof clearAllDays === 'function') {
        clearAllDays();
    }

    // Show modal
    document.getElementById('schedule-modal').classList.remove('hidden');

    // Load locations
    await loadScheduleLocations();
}

function closeScheduleModal() {
    document.getElementById('schedule-modal').classList.add('hidden');
}

async function loadScheduleLocations() {
    const container = document.getElementById('schedule-locations-container');
    const emptyMsg = document.getElementById('schedule-location-empty');
    const errorMsg = document.getElementById('schedule-location-error');

    // Reset state
    emptyMsg.classList.add('hidden');
    errorMsg.classList.add('hidden');

    try {
        const response = await fetch(`/api/projects/${projectId}/locations-json`);
        if (!response.ok) {
            throw new Error('Failed to load locations');
        }
        const locations = await response.json();

        if (!locations.length) {
            container.innerHTML = '<div class="text-gray-500 text-sm py-2 text-center">No locations available</div>';
            emptyMsg.classList.remove('hidden');
            return;
        }

        // Build checkboxes for each location
        container.innerHTML = locations.map(loc => `
            <label class="flex items-center gap-3 p-2 rounded hover:bg-gray-50 dark:hover:bg-gray-600 cursor-pointer">
                <input type="checkbox"
                       name="schedule_locations"
                       value="${loc.id}"
                       data-name="${loc.name}"
                       data-type="${loc.location_type}"
                       class="rounded text-seismo-orange focus:ring-seismo-orange">
                <span class="text-sm text-gray-900 dark:text-white">${loc.name}</span>
                <span class="text-xs text-gray-500 dark:text-gray-400">(${loc.location_type})</span>
            </label>
        `).join('');

        // Add select all / clear all buttons if more than one location
        if (locations.length > 1) {
            container.insertAdjacentHTML('afterbegin', `
                <div class="flex gap-2 pb-2 mb-2 border-b border-gray-200 dark:border-gray-600">
                    <button type="button" onclick="selectAllLocations()" class="text-xs text-seismo-orange hover:underline">Select All</button>
                    <span class="text-gray-400">|</span>
                    <button type="button" onclick="clearAllLocations()" class="text-xs text-gray-500 hover:underline">Clear All</button>
                </div>
            `);
        }
    } catch (err) {
        console.error('Failed to load locations:', err);
        container.innerHTML = '<div class="text-red-500 text-sm py-2 text-center">Error loading locations</div>';
    }
}

function selectAllLocations() {
    document.querySelectorAll('input[name="schedule_locations"]').forEach(cb => cb.checked = true);
}

function clearAllLocations() {
    document.querySelectorAll('input[name="schedule_locations"]').forEach(cb => cb.checked = false);
}

function getSelectedLocationIds() {
    const checkboxes = document.querySelectorAll('input[name="schedule_locations"]:checked');
    return Array.from(checkboxes).map(cb => cb.value);
}

function toggleScheduleType(type) {
    const weeklyEditor = document.getElementById('schedule-weekly-wrapper');
    const intervalEditor = document.getElementById('schedule-interval-wrapper');
    const oneoffEditor = document.getElementById('schedule-oneoff-wrapper');

    weeklyEditor.classList.add('hidden');
    intervalEditor.classList.add('hidden');
    oneoffEditor.classList.add('hidden');

    if (type === 'weekly_calendar') {
        weeklyEditor.classList.remove('hidden');
    } else if (type === 'simple_interval') {
        intervalEditor.classList.remove('hidden');
    } else if (type === 'one_off') {
        oneoffEditor.classList.remove('hidden');
    }
}

// Schedule form submission
document.getElementById('schedule-form').addEventListener('submit', async function(e) {
    e.preventDefault();

    const name = document.getElementById('schedule-name').value.trim();
    const locationIds = getSelectedLocationIds();
    const scheduleType = document.querySelector('input[name="schedule_type"]:checked').value;
    const timezone = document.getElementById('schedule-timezone').value;

    // Hide previous errors
    document.getElementById('schedule-location-error').classList.add('hidden');

    if (!name) {
        showScheduleError('Please enter a schedule name.');
        return;
    }

    if (!locationIds.length) {
        document.getElementById('schedule-location-error').classList.remove('hidden');
        showScheduleError('Please select at least one location.');
        return;
    }

    // Build payload based on schedule type
    const payload = {
        name: name,
        location_ids: locationIds,  // Array of location IDs
        schedule_type: scheduleType,
        timezone: timezone,
    };

    if (scheduleType === 'weekly_calendar') {
        // Get weekly pattern from the calendar editor
        if (typeof getWeeklyPatternData === 'function') {
            payload.weekly_pattern = getWeeklyPatternData();
        } else {
            showScheduleError('Calendar editor not loaded properly.');
            return;
        }

        // Validate at least one day is selected
        const hasEnabledDay = Object.values(payload.weekly_pattern).some(day => day.enabled);
        if (!hasEnabledDay) {
            showScheduleError('Please select at least one day for monitoring.');
            return;
        }

        // Get auto-increment setting for calendar mode
        if (typeof getCalendarAutoIncrement === 'function') {
            payload.auto_increment_index = getCalendarAutoIncrement();
        } else {
            payload.auto_increment_index = true;
        }

        // Get include_download setting for calendar mode
        if (typeof getCalendarIncludeDownload === 'function') {
            payload.include_download = getCalendarIncludeDownload();
        } else {
            payload.include_download = true;
        }
    } else if (scheduleType === 'simple_interval') {
        // Get interval data
        if (typeof getIntervalData === 'function') {
            const intervalData = getIntervalData();
            payload.interval_type = intervalData.interval_type;
            payload.cycle_time = intervalData.cycle_time;
            payload.include_download = intervalData.include_download;
            payload.auto_increment_index = intervalData.auto_increment_index;
        } else {
            showScheduleError('Interval editor not loaded properly.');
            return;
        }
    } else if (scheduleType === 'one_off') {
        // Get one-off data
        if (typeof getOneOffData === 'function') {
            const oneOffData = getOneOffData();

            if (!oneOffData.start_datetime || !oneOffData.end_datetime) {
                showScheduleError('Please select both start and end date/time.');
                return;
            }

            const start = new Date(oneOffData.start_datetime);
            const end = new Date(oneOffData.end_datetime);
            const diffMinutes = (end - start) / (1000 * 60);

            if (diffMinutes <= 0) {
                showScheduleError('End time must be after start time.');
                return;
            }
            if (diffMinutes < 15) {
                showScheduleError('Duration must be at least 15 minutes.');
                return;
            }
            if (diffMinutes > 1440) {
                showScheduleError('Duration cannot exceed 24 hours.');
                return;
            }
            if (start <= new Date()) {
                showScheduleError('Start time must be in the future.');
                return;
            }

            payload.start_datetime = oneOffData.start_datetime;
            payload.end_datetime = oneOffData.end_datetime;
            payload.include_download = oneOffData.include_download;
            payload.auto_increment_index = oneOffData.auto_increment_index;
        } else {
            showScheduleError('One-off editor not loaded properly.');
            return;
        }
    }

    try {
        const response = await fetch(`/api/projects/${projectId}/recurring-schedules/`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(payload)
        });

        if (!response.ok) {
            const data = await response.json().catch(() => ({}));
            throw new Error(data.detail || 'Failed to create schedule');
        }

        const result = await response.json();

        // Close modal and refresh schedules list
        closeScheduleModal();

        // Refresh both the recurring schedules list and scheduled actions
        htmx.ajax('GET', `/api/projects/${projectId}/recurring-schedules/partials/list`, {
            target: '#recurring-schedule-list',
            swap: 'innerHTML'
        });
        htmx.ajax('GET', `/api/projects/${projectId}/schedules?status=pending`, {
            target: '#project-schedules',
            swap: 'innerHTML'
        });

        // Show success message
        console.log('Schedule(s) created:', result.message);

    } catch (err) {
        showScheduleError(err.message || 'Failed to create schedule.');
    }
});

function showScheduleError(message) {
    const errorEl = document.getElementById('schedule-error');
    errorEl.textContent = message;
    errorEl.classList.remove('hidden');
}

// ============================================================================
// Keyboard shortcuts
// ============================================================================
document.addEventListener('keydown', function(e) {
    if (e.key === 'Escape') {
        closeLocationModal();
        closeAssignModal();
        closeScheduleModal();
    }
});

// Click outside to close modals
document.getElementById('location-modal')?.addEventListener('click', function(e) {
    if (e.target === this) {
        closeLocationModal();
    }
});

document.getElementById('assign-modal')?.addEventListener('click', function(e) {
    if (e.target === this) {
        closeAssignModal();
    }
});

document.getElementById('schedule-modal')?.addEventListener('click', function(e) {
    if (e.target === this) {
        closeScheduleModal();
    }
});

// ── Upload Data ───────────────────────────────────────────────────────────────

function toggleUploadAll() {
    const panel = document.getElementById('upload-all-panel');
    panel.classList.toggle('hidden');
    if (!panel.classList.contains('hidden')) {
        document.getElementById('upload-all-status').textContent = '';
        document.getElementById('upload-all-status').className = 'text-sm hidden';
        document.getElementById('upload-all-results').classList.add('hidden');
        document.getElementById('upload-all-results').innerHTML = '';
        document.getElementById('upload-all-input').value = '';
        document.getElementById('upload-all-file-count').classList.add('hidden');
        document.getElementById('upload-all-progress-wrap').classList.add('hidden');
        document.getElementById('upload-all-progress-bar').style.width = '0%';
    }
}

// Show file count and filter info when folder is selected
document.getElementById('upload-all-input').addEventListener('change', function() {
    const countEl = document.getElementById('upload-all-file-count');
    const total = this.files.length;
    if (!total) { countEl.classList.add('hidden'); return; }
    const wanted = Array.from(this.files).filter(_isWantedFile).length;
    countEl.textContent = `${wanted} of ${total} files will be uploaded (Leq + .rnh only)`;
    countEl.classList.remove('hidden');
});

function _isWantedFile(f) {
    const n = (f.webkitRelativePath || f.name).toLowerCase();
    const base = n.split('/').pop();
    if (base.endsWith('.rnh')) return true;
    if (base.endsWith('.rnd')) {
        if (base.includes('_leq_')) return true;   // NL-43 Leq
        if (base.startsWith('au2_')) return true;   // AU2/NL-23 format
        if (!base.includes('_lp')) return true;     // unknown format — keep
    }
    return false;
}

function submitUploadAll() {
    const input = document.getElementById('upload-all-input');
    const status = document.getElementById('upload-all-status');
    const resultsEl = document.getElementById('upload-all-results');
    const btn = document.getElementById('upload-all-btn');
    const cancelBtn = document.getElementById('upload-all-cancel-btn');
    const progressWrap = document.getElementById('upload-all-progress-wrap');
    const progressBar = document.getElementById('upload-all-progress-bar');
    const progressLabel = document.getElementById('upload-all-progress-label');

    if (!input.files.length) {
        alert('Please select a folder to upload.');
        return;
    }

    // Filter client-side — only send Leq .rnd and .rnh files
    const filesToSend = Array.from(input.files).filter(_isWantedFile);
    if (!filesToSend.length) {
        alert('No Leq .rnd or .rnh files found in selected folder.');
        return;
    }

    const formData = new FormData();
    for (const f of filesToSend) {
        formData.append('files', f);
        formData.append('paths', f.webkitRelativePath || f.name);
    }

    // Disable controls and show progress
    btn.disabled = true;
    btn.textContent = 'Uploading\u2026';
    btn.classList.add('opacity-60', 'cursor-not-allowed');
    cancelBtn.disabled = true;
    cancelBtn.classList.add('opacity-40', 'cursor-not-allowed');
    status.className = 'text-sm hidden';
    resultsEl.classList.add('hidden');
    progressWrap.classList.remove('hidden');
    progressBar.style.width = '0%';
    progressLabel.textContent = `Uploading ${filesToSend.length} files\u2026`;

    const xhr = new XMLHttpRequest();

    xhr.upload.addEventListener('progress', (e) => {
        if (e.lengthComputable) {
            const pct = Math.round((e.loaded / e.total) * 100);
            progressBar.style.width = pct + '%';
            progressLabel.textContent = `Uploading ${filesToSend.length} files\u2026 ${pct}%`;
        }
    });

    xhr.upload.addEventListener('load', () => {
        progressBar.style.width = '100%';
        progressLabel.textContent = 'Processing files on server\u2026';
    });

    function _resetControls() {
        progressWrap.classList.add('hidden');
        btn.disabled = false;
        btn.textContent = 'Import';
        btn.classList.remove('opacity-60', 'cursor-not-allowed');
        cancelBtn.disabled = false;
        cancelBtn.classList.remove('opacity-40', 'cursor-not-allowed');
    }

    xhr.addEventListener('load', () => {
        _resetControls();
        try {
            const data = JSON.parse(xhr.responseText);
            if (xhr.status >= 200 && xhr.status < 300) {
                const s = data.sessions_created;
                const f = data.files_imported;
                status.textContent = `\u2713 Imported ${f} file${f !== 1 ? 's' : ''} across ${s} session${s !== 1 ? 's' : ''}`;
                status.className = 'text-sm text-green-600 dark:text-green-400';
                input.value = '';
                document.getElementById('upload-all-file-count').classList.add('hidden');

                let html = '';
                if (data.sessions && data.sessions.length) {
                    html += '<div class="font-medium text-gray-700 dark:text-gray-300 mb-1">Sessions created:</div>';
                    html += '<ul class="space-y-0.5 ml-2">';
                    for (const sess of data.sessions) {
                        html += `<li class="text-xs text-gray-600 dark:text-gray-400">\u2022 <span class="font-medium">${sess.location_name}</span> &mdash; ${sess.files} files`;
                        if (sess.leq_files || sess.lp_files) html += ` (${sess.leq_files} Leq, ${sess.lp_files} Lp)`;
                        if (sess.store_name) html += ` &mdash; ${sess.store_name}`;
                        html += '</li>';
                    }
                    html += '</ul>';
                }
                if (data.unmatched_folders && data.unmatched_folders.length) {
                    html += `<div class="mt-2 text-xs text-amber-600 dark:text-amber-400">\u26a0 Unmatched folders (no NRL location found): ${data.unmatched_folders.join(', ')}</div>`;
                }
                if (html) {
                    resultsEl.innerHTML = html;
                    resultsEl.classList.remove('hidden');
                }
                htmx.trigger(document.getElementById('unified-files'), 'refresh');
            } else {
                status.textContent = `Error: ${data.detail || 'Upload failed'}`;
                status.className = 'text-sm text-red-600 dark:text-red-400';
            }
        } catch {
            status.textContent = 'Error: Unexpected server response';
            status.className = 'text-sm text-red-600 dark:text-red-400';
        }
    });

    xhr.addEventListener('error', () => {
        _resetControls();
        status.textContent = 'Error: Network error during upload';
        status.className = 'text-sm text-red-600 dark:text-red-400';
    });

    xhr.open('POST', `/api/projects/{{ project_id }}/upload-all`);
    xhr.send(formData);
}

// Load project details on page load and restore active tab from URL hash
// ── Live monitoring section (sound) ──────────────────────────────────────
// Self-contained: fetches /live-stats every 15s and renders a rollup + a
// live tile per NRL. Started from loadProjectDetails() once we know the
// project has the sound module, so vibration-only projects don't poll.
let liveStatsTimer = null;
const LS_LEVEL_AMBER = 55, LS_LEVEL_RED = 70;

function lsEsc(s) {
    return String(s == null ? '' : s).replace(/[&<>"']/g, c => (
        {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
}
function lsNum(v) { const f = parseFloat(v); return isNaN(f) ? null : f; }
function lsFmtAgo(iso) {
    if (!iso) return '';
    const t = new Date(iso.endsWith('Z') ? iso : iso + 'Z');
    const s = Math.max(0, Math.round((Date.now() - t.getTime()) / 1000));
    if (s < 60) return s + 's ago';
    if (s < 3600) return Math.round(s / 60) + 'm ago';
    if (s < 86400) return Math.round(s / 3600) + 'h ago';
    return Math.round(s / 86400) + 'd ago';
}
// Headline Leq color, matched to the portal thresholds.
function lsLeqColor(leq, measuring) {
    if (!measuring || leq == null) return 'text-gray-400 dark:text-gray-500';
    if (leq >= LS_LEVEL_RED) return 'text-red-500';
    if (leq >= LS_LEVEL_AMBER) return 'text-amber-500';
    return 'text-green-500';
}
// Friendly labels for NL-43 battery / power-source codes (fall back to raw).
function lsBattery(code) {
    return ({F:'Full', M:'Mid', L:'Low', D:'Dead', E:'Empty'})[code] || (code || '');
}
function lsPower(code) {
    return ({I:'Battery', E:'External', U:'USB'})[code] || (code || '');
}

function lsRenderTile(loc) {
    const measuring = loc.measurement_state === 'Start' || loc.measurement_state === 'Measure';
    const wedged = loc.connection_state === 'wedged';
    const reachable = loc.is_reachable !== false;     // null/absent → assume ok
    const hasData = loc.measurement_state != null || loc.leq != null;

    // Status badge
    let badge;
    if (!loc.unit_id) {
        badge = '<span class="inline-flex items-center px-2 py-0.5 rounded-full text-[11px] bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400">No unit</span>';
    } else if (wedged) {
        badge = '<span class="inline-flex items-center px-2 py-0.5 rounded-full text-[11px] bg-red-100 dark:bg-red-900/40 text-red-600 dark:text-red-300">Wedged</span>';
    } else if (!reachable || !hasData) {
        badge = '<span class="inline-flex items-center px-2 py-0.5 rounded-full text-[11px] bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400">Offline</span>';
    } else if (measuring) {
        badge = '<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[11px] bg-orange-100 dark:bg-orange-900/30 text-seismo-orange"><span class="relative flex h-1.5 w-1.5"><span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-seismo-orange opacity-75"></span><span class="relative inline-flex rounded-full h-1.5 w-1.5 bg-seismo-orange"></span></span>Live</span>';
    } else {
        badge = '<span class="inline-flex items-center px-2 py-0.5 rounded-full text-[11px] bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400">Stopped</span>';
    }

    const leqNum = lsNum(loc.leq);
    const leqStr = (loc.leq == null || loc.leq === '') ? '--' : loc.leq;
    const leqColor = lsLeqColor(leqNum, measuring);

    // Health line: unit · last-seen · battery/power
    const bits = [];
    if (loc.unit_id) bits.push(`<span class="font-mono text-seismo-orange">${lsEsc(loc.unit_id)}</span>`);
    if (hasData && loc.last_seen) bits.push(lsEsc(lsFmtAgo(loc.last_seen)));
    if (hasData && (loc.battery_level || loc.power_source)) {
        const b = lsBattery(loc.battery_level), p = lsPower(loc.power_source);
        const low = loc.battery_level === 'L' || loc.battery_level === 'D' || loc.battery_level === 'E';
        bits.push(`<span class="${low ? 'text-red-500' : ''}">${lsEsc([p, b].filter(Boolean).join(' · '))}</span>`);
    }

    const levels = (hasData)
        ? `<div class="mt-1.5 text-xs text-gray-500 dark:text-gray-400 tabular-nums">
               Lp ${lsEsc(loc.lp ?? '--')} &nbsp; Lmax ${lsEsc(loc.lmax ?? '--')}
           </div>`
        : '';

    return `
        <a href="/projects/${projectId}/nrl/${encodeURIComponent(loc.id)}"
           class="block bg-white dark:bg-slate-800 rounded-xl shadow-lg p-4 border border-transparent hover:border-seismo-orange hover:shadow-xl transition-all">
            <div class="flex items-start justify-between gap-2">
                <div class="font-semibold text-gray-900 dark:text-white truncate">${lsEsc(loc.name)}</div>
                ${badge}
            </div>
            <div class="mt-3 flex items-baseline gap-1.5">
                <span class="text-4xl leading-none font-semibold tabular-nums ${leqColor}">${lsEsc(leqStr)}</span>
                <span class="text-xs text-gray-400 dark:text-gray-500 font-mono">dB Leq</span>
            </div>
            ${levels}
            <div class="mt-2 text-[11px] text-gray-400 dark:text-gray-500 flex flex-wrap gap-x-2 gap-y-0.5">
                ${bits.join('<span class="opacity-40">·</span>')}
            </div>
        </a>`;
}

function lsRender(locations) {
    const section = document.getElementById('live-stats-section');
    if (!section) return;
    if (!locations.length) { section.classList.add('hidden'); return; }
    section.classList.remove('hidden');

    // Rollup
    let live = 0, off = 0, peak = null, peakStr = null, peakLoc = null;
    for (const l of locations) {
        const measuring = l.measurement_state === 'Start' || l.measurement_state === 'Measure';
        const hasData = l.measurement_state != null || l.leq != null;
        if (measuring) {
            live++;
            const n = lsNum(l.leq);
            if (n != null && (peak == null || n > peak)) { peak = n; peakStr = l.leq; peakLoc = l.name; }
        } else if (!l.unit_id || !hasData || l.is_reachable === false) {
            off++;
        }
    }
    document.getElementById('ls-live').textContent = live;
    document.getElementById('ls-offline').textContent = off;
    const pw = document.getElementById('ls-loudest-wrap');
    if (peak != null) {
        pw.classList.remove('hidden');
        document.getElementById('ls-loudest').textContent = peakStr;
        document.getElementById('ls-loudest-loc').textContent = peakLoc;
    } else { pw.classList.add('hidden'); }

    document.getElementById('ls-tiles').innerHTML = locations.map(lsRenderTile).join('');
}

// Compact level-tinted pill classes for the inline NRL-card chips.
function lsInlineLevelPill(leq) {
    if (leq == null) return 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300';
    if (leq >= LS_LEVEL_RED) return 'bg-red-100 dark:bg-red-900/30 text-red-600 dark:text-red-300';
    if (leq >= LS_LEVEL_AMBER) return 'bg-amber-100 dark:bg-amber-900/30 text-amber-700 dark:text-amber-300';
    return 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300';
}
function lsInlineChipHtml(loc) {
    if (!loc.unit_id) return '';     // no unit assigned → no chip
    const base = 'inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[11px] font-medium ';
    const measuring = loc.measurement_state === 'Start' || loc.measurement_state === 'Measure';
    const hasData = loc.measurement_state != null || loc.leq != null;
    const reachable = loc.is_reachable !== false;
    if (loc.connection_state === 'wedged')
        return `<span class="${base}bg-red-100 dark:bg-red-900/40 text-red-600 dark:text-red-300">Wedged</span>`;
    if (!reachable || !hasData)
        return `<span class="${base}bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400">Offline</span>`;
    if (measuring) {
        const leqStr = (loc.leq == null || loc.leq === '') ? '--' : loc.leq;
        return `<span class="${base}${lsInlineLevelPill(lsNum(loc.leq))}">`
             + `<span class="w-1.5 h-1.5 rounded-full bg-current"></span>${lsEsc(leqStr)} dB</span>`;
    }
    return `<span class="${base}bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400">Stopped</span>`;
}
// Paint the inline chips on the NRL list cards (Overview + Sound tab).
function lsPaintInline(locations) {
    const byId = {};
    for (const l of locations) byId[l.id] = l;
    document.querySelectorAll('[data-loc-live]').forEach(el => {
        const loc = byId[el.getAttribute('data-loc-live')];
        const html = loc ? lsInlineChipHtml(loc) : '';
        el.innerHTML = html;
        el.classList.toggle('hidden', !html);
    });
}

let lsLastData = [];
async function loadLiveStats() {
    // Skip work while the tab is hidden in the background.
    if (document.hidden) return;
    try {
        const r = await fetch(`/api/projects/${projectId}/live-stats`);
        if (!r.ok) return;
        const j = await r.json();
        lsLastData = j.locations || [];
        lsRender(lsLastData);
        lsPaintInline(lsLastData);
    } catch (e) { /* keep last render */ }
}

// The NRL list partial reloads via htmx (e.g. the 30s dashboard swap), which
// wipes the painted chips — repaint from the last poll as soon as it settles.
document.body.addEventListener('htmx:afterSwap', (e) => {
    const id = e.target && e.target.id;
    if (id === 'project-locations' || id === 'sound-locations') lsPaintInline(lsLastData);
});

function startLiveStats() {
    if (liveStatsTimer) return;       // already running
    loadLiveStats();
    liveStatsTimer = setInterval(loadLiveStats, 15000);
}

document.addEventListener('DOMContentLoaded', function() {
    loadProjectDetails();

    // Restore tab from URL hash
    const hash = window.location.hash.replace('#', '');
    const validTabs = ['overview', 'vibration', 'sound', 'settings'];
    // Backwards compat: map old tab names to new ones
    const hashMap = { locations: 'sound', units: 'sound', schedules: 'sound', sessions: 'sound', data: 'sound' };
    const subTabMap = { units: 'units', schedules: 'schedules', sessions: 'sessions', data: 'data' };
    if (hash) {
        const mappedTab = hashMap[hash] || hash;
        if (validTabs.includes(mappedTab)) {
            switchTab(mappedTab);
            // Open the relevant sub-tab for backwards compat
            if (subTabMap[hash]) {
                switchSoundSubTab(subTabMap[hash]);
            }
        }
    }
});

</script>

<!-- Portal access modal -->
<div id="portal-access-modal" class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4"
     onclick="if(event.target===this)closePortalAccess()">
    <div class="bg-white dark:bg-slate-800 rounded-xl shadow-xl w-full max-w-lg p-6">
        <div class="flex items-center justify-between mb-1">
            <h3 class="text-lg font-semibold text-gray-900 dark:text-white">Client portal access</h3>
            <button onclick="closePortalAccess()" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-200">
                <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="M6 18L18 6M6 6l12 12"/></svg>
            </button>
        </div>
        <p class="text-sm text-gray-500 dark:text-gray-400 mb-4">
            Send the client the link <em>and</em> the password. Read-only. Disabling rotates the link.
        </p>

        <div class="flex items-center justify-between mb-4">
            <span class="text-sm font-medium text-gray-700 dark:text-gray-200">Portal enabled</span>
            <button id="pa-toggle" onclick="togglePortalEnabled()"
                    class="px-3 py-1.5 text-sm rounded-lg border border-slate-300 dark:border-slate-600">…</button>
        </div>

        <div id="pa-details" class="hidden space-y-4">
            <div>
                <label class="block text-xs text-gray-500 dark:text-gray-400 mb-1">Portal link</label>
                <div class="flex gap-2">
                    <input id="pa-link" readonly class="flex-1 px-3 py-2 text-sm rounded-lg border border-slate-300 dark:border-slate-600 bg-gray-50 dark:bg-slate-900 text-gray-800 dark:text-gray-200" />
                    <button onclick="copyField('pa-link', this)" class="px-3 py-2 text-sm rounded-lg bg-seismo-orange text-white hover:bg-orange-600">Copy</button>
                </div>
            </div>
            <div>
                <label class="block text-xs text-gray-500 dark:text-gray-400 mb-1">Password</label>
                <div class="flex gap-2">
                    <input id="pa-pass" readonly placeholder="•••••••• (set one below)"
                           class="flex-1 px-3 py-2 text-sm rounded-lg border border-slate-300 dark:border-slate-600 bg-gray-50 dark:bg-slate-900 text-gray-800 dark:text-gray-200" />
                    <button onclick="copyField('pa-pass', this)" class="px-3 py-2 text-sm rounded-lg bg-seismo-orange text-white hover:bg-orange-600">Copy</button>
                </div>
                <button onclick="regeneratePassword()" class="mt-2 text-sm text-seismo-orange hover:text-seismo-navy font-medium">↻ Generate new password</button>
                <p class="text-xs text-gray-400 mt-1">Shown once — copy it now. Regenerating invalidates the old one.</p>
            </div>
        </div>
    </div>
</div>

<script>
const PA_PROJECT_ID = "{{ project_id }}";
let paEnabled = false;
function paToast(msg) { if (window.showToast) showToast(msg, 'error'); else alert(msg); }
function openPortalAccess() { document.getElementById('portal-access-modal').classList.remove('hidden'); loadPortalAccess(); }
function closePortalAccess() { document.getElementById('portal-access-modal').classList.add('hidden'); }

function copyField(id, btn) {
    const inp = document.getElementById(id); inp.select();
    const done = () => { btn.textContent = 'Copied!'; setTimeout(() => btn.textContent = 'Copy', 1500); };
    if (navigator.clipboard) navigator.clipboard.writeText(inp.value).then(done).catch(() => { document.execCommand('copy'); done(); });
    else { document.execCommand('copy'); done(); }
}

async function loadPortalAccess() {
    try {
        const r = await fetch(`/projects/${PA_PROJECT_ID}/portal-access`);
        if (!r.ok) throw new Error('load failed');
        renderPortalAccess(await r.json());
    } catch (e) { paToast('Could not load portal access.'); }
}
function renderPortalAccess(j) {
    paEnabled = !!j.enabled;
    const toggle = document.getElementById('pa-toggle');
    const details = document.getElementById('pa-details');
    toggle.textContent = paEnabled ? 'On — click to disable' : 'Off — click to enable';
    toggle.className = 'px-3 py-1.5 text-sm rounded-lg border ' +
        (paEnabled ? 'border-green-500 text-green-600 dark:text-green-400' : 'border-slate-300 dark:border-slate-600');
    details.classList.toggle('hidden', !paEnabled);
    document.getElementById('pa-link').value = (paEnabled && j.link_url) ? j.link_url : '';
}
async function togglePortalEnabled() {
    const action = paEnabled ? 'disable' : 'enable';
    try {
        const r = await fetch(`/projects/${PA_PROJECT_ID}/portal-access/${action}`, { method: 'POST' });
        if (!r.ok) throw new Error('toggle failed');
        const j = await r.json();
        renderPortalAccess(action === 'disable' ? { enabled: false, link_url: null } : j);
    } catch (e) { paToast(`Could not ${action} the portal.`); }
}
async function regeneratePassword() {
    try {
        const r = await fetch(`/projects/${PA_PROJECT_ID}/portal-access/password`, { method: 'POST' });
        if (!r.ok) throw new Error('password failed');
        const j = await r.json();
        if (j.password) { const f = document.getElementById('pa-pass'); f.value = j.password; f.placeholder = ''; }
    } catch (e) { paToast('Could not generate a password.'); }
}
</script>
{% endblock %}
