{% extends "base.html" %}

{% block title %}Dashboard - Seismo Fleet Manager{% endblock %}

{% block content %}
{% if environment == 'development' %}
<div class="mb-4 p-4 bg-yellow-100 dark:bg-yellow-900 border-l-4 border-yellow-500 text-yellow-700 dark:text-yellow-200 rounded">
    <p class="font-bold">Development Environment</p>
    <p class="text-sm">You are currently viewing the development version of Seismo Fleet Manager.</p>
</div>
{% endif %}

<div class="mb-8 flex justify-between items-center gap-4 flex-wrap">
    <div>
        <h1 class="text-3xl font-bold text-gray-900 dark:text-white">Dashboard</h1>
        <p class="text-gray-600 dark:text-gray-400 mt-1">Fleet overview and recent activity</p>
    </div>
    <div class="flex items-center gap-3">
        <a href="/deploy"
           class="hidden md:inline-flex items-center gap-2 px-4 py-2 bg-seismo-orange hover:bg-orange-600 text-white rounded-lg font-medium text-sm shadow"
           title="Capture a field install — pick unit, snap photo, leave">
            <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="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z"/>
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 13a3 3 0 11-6 0 3 3 0 016 0z"/>
            </svg>
            Field Deploy
        </a>
        <div class="text-right">
            <p class="text-xs text-gray-500 dark:text-gray-400">Last updated</p>
            <p id="last-refresh" class="text-sm text-gray-700 dark:text-gray-300 font-mono">--</p>
        </div>
    </div>
</div>

<!-- Pending-deployments banner — auto-shows when there are field captures
     awaiting classification.  Hides itself when count is 0.  Polled
     alongside the rest of the dashboard's 10-second refresh. -->
<a id="pending-deploy-banner" href="/tools/pending-deployments"
   class="hidden mb-6 flex items-center justify-between gap-3 p-3 rounded-lg bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 hover:bg-amber-100 dark:hover:bg-amber-900/30 transition-colors">
    <div class="flex items-center gap-3">
        <div class="w-9 h-9 rounded-lg bg-amber-100 dark:bg-amber-900/40 text-amber-600 dark:text-amber-400 flex items-center justify-center shrink-0">
            <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
            </svg>
        </div>
        <div>
            <div class="text-sm font-medium text-amber-900 dark:text-amber-200">
                <span id="pending-deploy-count">0</span> field deployment<span id="pending-deploy-plural">s</span> awaiting classification
            </div>
            <div class="text-xs text-amber-700 dark:text-amber-300">Click to pick project / location for these captures</div>
        </div>
    </div>
    <svg class="w-5 h-5 text-amber-600 dark:text-amber-400 shrink-0" 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"/>
    </svg>
</a>

<script>
async function _refreshPendingDeployBanner() {
    try {
        const r = await fetch('/api/deployments/pending?status=awaiting');
        if (!r.ok) return;
        const data = await r.json();
        const banner = document.getElementById('pending-deploy-banner');
        const countEl = document.getElementById('pending-deploy-count');
        const pluralEl = document.getElementById('pending-deploy-plural');
        if (data.count > 0) {
            countEl.textContent = data.count;
            pluralEl.textContent = data.count === 1 ? '' : 's';
            banner.classList.remove('hidden');
        } else {
            banner.classList.add('hidden');
        }
    } catch (e) {
        /* silent — banner just stays hidden */
    }
}
_refreshPendingDeployBanner();
setInterval(_refreshPendingDeployBanner, 30000);
</script>

<!-- Dashboard cards with auto-refresh -->
<div hx-get="/api/status-snapshot"
     hx-trigger="load, every 10s"
     hx-swap="none"
     hx-on::after-request="updateDashboard(event)">

    <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">

        <!-- Recent Alerts Card (col 1) -->
        <div class="rounded-xl shadow-lg bg-white dark:bg-slate-700 p-6" id="recent-alerts-card">
            <div class="flex items-center justify-between mb-4 cursor-pointer md:cursor-default" onclick="toggleCard('recent-alerts')">
                <h2 class="text-lg font-semibold text-gray-900 dark:text-white">Recent Alerts</h2>
                <div class="flex items-center gap-2">
                    <svg class="w-6 h-6 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                              d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z">
                        </path>
                    </svg>
                    <svg class="w-5 h-5 text-gray-500 transition-transform md:hidden chevron" id="recent-alerts-chevron" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
                    </svg>
                </div>
            </div>
            <div id="alerts-list" class="space-y-3 card-content" id-content="recent-alerts-content">
                <p class="text-sm text-gray-500 dark:text-gray-400">Loading alerts...</p>
            </div>
        </div>

        <!-- Recent Call-Ins Card (cols 2-3, double-wide) -->
        <div class="rounded-xl shadow-lg bg-white dark:bg-slate-700 p-6 md:col-span-2 lg:col-span-2" id="recent-callins-card">
            <div class="flex items-center justify-between mb-4 cursor-pointer md:cursor-default" onclick="toggleCard('recent-callins')">
                <div class="flex items-center gap-2">
                    <h2 class="text-lg font-semibold text-gray-900 dark:text-white">Recent Call-Ins</h2>
                    <span class="text-xs text-gray-500 dark:text-gray-400 hidden sm:inline">from SFM event forwards</span>
                </div>
                <div class="flex items-center gap-2">
                    <svg class="w-6 h-6 text-seismo-burgundy" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                              d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z">
                        </path>
                    </svg>
                    <svg class="w-5 h-5 text-gray-500 transition-transform md:hidden chevron" id="recent-callins-chevron" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
                    </svg>
                </div>
            </div>
            <div class="card-content" id="recent-callins-content">
                <div id="recent-callins-list" class="space-y-2">
                    <p class="text-sm text-gray-500 dark:text-gray-400">Loading recent call-ins...</p>
                </div>
                <a href="/sfm" class="block mt-3 text-center text-sm text-seismo-orange hover:text-seismo-burgundy font-medium">
                    View all events →
                </a>
            </div>
        </div>

        <!-- Fleet Summary Card (col 4) -->
        <div class="rounded-xl shadow-lg bg-white dark:bg-slate-700 p-6" id="fleet-summary-card">
            <div class="flex items-center justify-between mb-4 cursor-pointer md:cursor-default" onclick="toggleCard('fleet-summary')">
                <h2 class="text-lg font-semibold text-gray-900 dark:text-white">Fleet Summary</h2>
                <div class="flex items-center gap-2">
                    <svg class="w-6 h-6 text-seismo-orange" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                              d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z">
                        </path>
                    </svg>
                    <svg class="w-5 h-5 text-gray-500 transition-transform md:hidden chevron" id="fleet-summary-chevron" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
                    </svg>
                </div>
            </div>
            <div class="space-y-4 card-content" id="fleet-summary-content">
                <!-- Seismographs -->
                <div>
                    <div class="flex justify-between items-center mb-1.5">
                        <div class="flex items-center">
                            <svg class="w-4 h-4 mr-1.5 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
                            </svg>
                            <a href="/seismographs" class="text-sm font-semibold text-gray-800 dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400">Seismographs</a>
                        </div>
                        <span id="seismo-count" class="text-lg font-bold text-blue-600 dark:text-blue-400">--</span>
                    </div>
                    <div class="pl-6 flex flex-col gap-0.5 text-sm">
                        <div class="flex justify-between">
                            <span class="text-gray-500 dark:text-gray-400">Deployed</span>
                            <span id="seismo-deployed" class="font-medium text-gray-800 dark:text-gray-200">--</span>
                        </div>
                        <div class="flex justify-between">
                            <span class="text-gray-500 dark:text-gray-400">Benched</span>
                            <span id="seismo-benched" class="font-medium text-gray-800 dark:text-gray-200">--</span>
                        </div>
                    </div>
                </div>

                <!-- Sound Level Meters -->
                <div>
                    <div class="flex justify-between items-center mb-1.5">
                        <div class="flex items-center">
                            <svg class="w-4 h-4 mr-1.5 text-purple-500" 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.072m2.828-9.9a9 9 0 010 12.728M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z"></path>
                            </svg>
                            <a href="/sound-level-meters" class="text-sm font-semibold text-gray-800 dark:text-gray-200 hover:text-purple-600 dark:hover:text-purple-400">Sound Level Meters</a>
                        </div>
                        <span id="slm-count" class="text-lg font-bold text-purple-600 dark:text-purple-400">--</span>
                    </div>
                    <div class="pl-6 flex flex-col gap-0.5 text-sm">
                        <div class="flex justify-between">
                            <span class="text-gray-500 dark:text-gray-400">Deployed</span>
                            <span id="slm-deployed" class="font-medium text-gray-800 dark:text-gray-200">--</span>
                        </div>
                        <div class="flex justify-between">
                            <span class="text-gray-500 dark:text-gray-400">Benched</span>
                            <span id="slm-benched" class="font-medium text-gray-800 dark:text-gray-200">--</span>
                        </div>
                    </div>
                </div>

                <div class="border-t border-gray-200 dark:border-gray-700 pt-3 mt-3">
                    <p class="text-xs text-gray-500 dark:text-gray-500 mb-2 italic">Call-in Status:</p>
                    <div class="flex justify-between items-center mb-2" title="Units reporting normally (last seen < 12 hours)">
                        <div class="flex items-center">
                            <span class="w-3 h-3 rounded-full bg-green-500 mr-2 flex items-center justify-center">
                                <svg class="w-2 h-2 text-white" fill="currentColor" viewBox="0 0 20 20">
                                    <path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
                                </svg>
                            </span>
                            <span class="text-sm text-gray-600 dark:text-gray-400">OK</span>
                        </div>
                        <span id="status-ok" class="font-semibold text-green-600 dark:text-green-400">--</span>
                    </div>
                    <div class="flex justify-between items-center mb-2" title="Units with delayed reports (12-24 hours)">
                        <div class="flex items-center">
                            <span class="w-3 h-3 rounded-full bg-yellow-500 mr-2 flex items-center justify-center">
                                <svg class="w-2 h-2 text-white" fill="currentColor" viewBox="0 0 20 20">
                                    <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"/>
                                </svg>
                            </span>
                            <span class="text-sm text-gray-600 dark:text-gray-400">Pending</span>
                        </div>
                        <span id="status-pending" class="font-semibold text-yellow-600 dark:text-yellow-400">--</span>
                    </div>
                    <div class="flex justify-between items-center" title="Units not reporting (> 24 hours)">
                        <div class="flex items-center">
                            <span class="w-3 h-3 rounded-full bg-red-500 mr-2 flex items-center justify-center">
                                <svg class="w-2 h-2 text-white" fill="currentColor" viewBox="0 0 20 20">
                                    <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/>
                                </svg>
                            </span>
                            <span class="text-sm text-gray-600 dark:text-gray-400">Missing</span>
                        </div>
                        <span id="status-missing" class="font-semibold text-red-600 dark:text-red-400">--</span>
                    </div>
                </div>
            </div>
        </div>

    </div>

    <!-- Dashboard Filters -->
    <div class="rounded-xl shadow-lg bg-white dark:bg-slate-700 p-4 mb-4" id="dashboard-filters-card">
        <div class="flex items-center justify-between mb-3">
            <h3 class="text-sm font-semibold text-gray-700 dark:text-gray-300">Filter Dashboard</h3>
            <button onclick="resetFilters()" class="text-xs text-gray-500 hover:text-seismo-orange dark:hover:text-seismo-orange transition-colors">
                Reset Filters
            </button>
        </div>

        <div class="flex flex-wrap gap-6">
            <!-- Device Type Filters -->
            <div class="flex flex-col gap-1">
                <span class="text-xs text-gray-500 dark:text-gray-400 font-medium uppercase tracking-wide">Device Type</span>
                <div class="flex gap-4">
                    <label class="flex items-center gap-1.5 cursor-pointer">
                        <input type="checkbox" id="filter-seismograph" checked
                               class="rounded border-gray-300 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-slate-800"
                               onchange="applyFilters()">
                        <span class="text-sm text-gray-700 dark:text-gray-300">Seismographs</span>
                    </label>
                    <label class="flex items-center gap-1.5 cursor-pointer">
                        <input type="checkbox" id="filter-slm" checked
                               class="rounded border-gray-300 text-purple-600 focus:ring-purple-500 dark:border-gray-600 dark:bg-slate-800"
                               onchange="applyFilters()">
                        <span class="text-sm text-gray-700 dark:text-gray-300">SLMs</span>
                    </label>
                    <label class="flex items-center gap-1.5 cursor-pointer">
                        <input type="checkbox" id="filter-modem" checked
                               class="rounded border-gray-300 text-cyan-600 focus:ring-cyan-500 dark:border-gray-600 dark:bg-slate-800"
                               onchange="applyFilters()">
                        <span class="text-sm text-gray-700 dark:text-gray-300">Modems</span>
                    </label>
                </div>
            </div>

            <!-- Status Filters -->
            <div class="flex flex-col gap-1">
                <span class="text-xs text-gray-500 dark:text-gray-400 font-medium uppercase tracking-wide">Status</span>
                <div class="flex gap-4">
                    <label class="flex items-center gap-1.5 cursor-pointer">
                        <input type="checkbox" id="filter-ok" checked
                               class="rounded border-gray-300 text-green-600 focus:ring-green-500 dark:border-gray-600 dark:bg-slate-800"
                               onchange="applyFilters()">
                        <span class="text-sm text-green-600 dark:text-green-400">OK</span>
                    </label>
                    <label class="flex items-center gap-1.5 cursor-pointer">
                        <input type="checkbox" id="filter-pending" checked
                               class="rounded border-gray-300 text-yellow-600 focus:ring-yellow-500 dark:border-gray-600 dark:bg-slate-800"
                               onchange="applyFilters()">
                        <span class="text-sm text-yellow-600 dark:text-yellow-400">Pending</span>
                    </label>
                    <label class="flex items-center gap-1.5 cursor-pointer">
                        <input type="checkbox" id="filter-missing" checked
                               class="rounded border-gray-300 text-red-600 focus:ring-red-500 dark:border-gray-600 dark:bg-slate-800"
                               onchange="applyFilters()">
                        <span class="text-sm text-red-600 dark:text-red-400">Missing</span>
                    </label>
                </div>
            </div>
        </div>
    </div>

    <!-- Fleet Map -->
    <div class="rounded-xl shadow-lg bg-white dark:bg-slate-700 p-6 mb-8" id="fleet-map-card">
        <div class="flex items-center justify-between mb-4 cursor-pointer md:cursor-default" onclick="toggleCard('fleet-map')">
            <h2 class="text-xl font-semibold text-gray-900 dark:text-white">Fleet Map</h2>
            <div class="flex items-center gap-2">
                <span class="text-sm text-gray-500 dark:text-gray-400">Deployed units</span>
                <svg class="w-5 h-5 text-gray-500 transition-transform md:hidden chevron" id="fleet-map-chevron" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
                </svg>
            </div>
        </div>
        <div class="card-content" id="fleet-map-content">
            <div id="fleet-map" class="w-full h-64 md:h-96 rounded-lg"></div>
        </div>
    </div>

    <!-- Today's Schedule — horizontal collapsible card.
         Default collapsed; auto-expands when an upcoming action is detected
         (pending + scheduled within the next 4h).  JS reads
         data-has-upcoming on the inner partial after htmx swap. -->
    <div class="rounded-xl shadow-lg bg-white dark:bg-slate-700 p-4 mb-8" id="todays-actions-card">
        <div class="flex items-center justify-between cursor-pointer" onclick="toggleTodaysSchedule()">
            <div class="flex items-center gap-3">
                <svg class="w-5 h-5 text-seismo-orange" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                          d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
                </svg>
                <h2 class="text-base font-semibold text-gray-900 dark:text-white">Today's Schedule</h2>
                <span id="todays-actions-badge"
                      class="hidden text-xs font-medium px-2 py-0.5 rounded-full bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-200">
                </span>
            </div>
            <svg class="w-5 h-5 text-gray-500 transition-transform collapsed" id="todays-actions-chevron"
                 fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
            </svg>
        </div>
        <div class="card-content collapsed mt-4" id="todays-actions-content"
             hx-get="/dashboard/todays-actions"
             hx-trigger="load, every 30s"
             hx-swap="innerHTML"
             hx-on::after-swap="onTodaysActionsSwap(this)">
            <p class="text-sm text-gray-500 dark:text-gray-400">Loading scheduled actions...</p>
        </div>
    </div>

    <!-- Recent Photos Section -->
    <div class="rounded-xl shadow-lg bg-white dark:bg-slate-700 p-6 mb-8" id="recent-photos-card">
        <div class="flex items-center justify-between mb-4 cursor-pointer md:cursor-default" onclick="toggleCard('recent-photos')">
            <h2 class="text-xl font-semibold text-gray-900 dark:text-white">Recent Photos</h2>
            <div class="flex items-center gap-2">
                <svg class="w-6 h-6 text-seismo-orange" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
                </svg>
                <svg class="w-5 h-5 text-gray-500 transition-transform md:hidden chevron" id="recent-photos-chevron" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
                </svg>
            </div>
        </div>
        <div class="card-content" id="recent-photos-content">
            <div id="recentPhotosGallery" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4">
                <p class="text-sm text-gray-500 dark:text-gray-400 col-span-full">Loading recent photos...</p>
            </div>
        </div>
    </div>

    <!-- Fleet Status Section with Tabs -->
    <div class="rounded-xl shadow-lg bg-white dark:bg-slate-700 p-6" id="fleet-status-card">

        <div class="flex items-center justify-between mb-4 cursor-pointer md:cursor-default" onclick="toggleCard('fleet-status')">
            <h2 class="text-xl font-semibold text-gray-900 dark:text-white">Fleet Status</h2>

            <div class="flex items-center gap-2">
                <a href="/roster" class="text-seismo-orange hover:text-seismo-burgundy font-medium flex items-center" onclick="event.stopPropagation()">
                    Full Roster
                    <svg class="w-4 h-4 ml-1" 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>
                </a>
                <svg class="w-5 h-5 text-gray-500 transition-transform md:hidden chevron" id="fleet-status-chevron" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
                </svg>
            </div>
        </div>

        <div class="card-content" id="fleet-status-content">
            <!-- Tab Bar -->
            <div class="flex border-b border-gray-200 dark:border-gray-700 mb-4">
                <button
                    class="px-4 py-2 text-sm font-medium tab-button active-tab"
                    hx-get="/dashboard/active"
                    hx-target="#fleet-table"
                    hx-swap="innerHTML">
                    Active
                </button>

                <button
                    class="px-4 py-2 text-sm font-medium tab-button"
                    hx-get="/dashboard/benched"
                    hx-target="#fleet-table"
                    hx-swap="innerHTML">
                    Benched
                </button>
            </div>

            <!-- Tab Content Target -->
            <div id="fleet-table" class="space-y-2"
                 hx-get="/dashboard/active"
                 hx-trigger="load"
                 hx-swap="innerHTML">
                <p class="text-gray-500 dark:text-gray-400">Loading fleet data...</p>
            </div>
        </div>

    </div>
</div>


<!-- TAB STYLE -->
<style>
.tab-button {
    color: #6b7280; /* gray-500 */
    border-bottom: 2px solid transparent;
}
.tab-button:hover {
    color: #374151; /* gray-700 */
}
.active-tab {
    color: #b84a12 !important; /* seismo orange */
    border-bottom: 2px solid #b84a12 !important;
}

/* Collapsible cards (mobile only) */
@media (max-width: 767px) {
    .card-content.collapsed {
        display: none;
    }
    .chevron.collapsed {
        transform: rotate(-90deg);
    }
}

/* Today's Schedule — horizontal collapsible at all breakpoints. */
#todays-actions-content.collapsed {
    display: none;
}
#todays-actions-chevron.collapsed {
    transform: rotate(-90deg);
}
#todays-actions-chevron {
    transition: transform 0.2s ease-in-out;
}
</style>


<script>
// ===== Dashboard Filtering System =====
let currentSnapshotData = null;  // Store latest snapshot data for re-filtering

// Filter state - tracks which device types and statuses to show
const filters = {
    deviceTypes: {
        seismograph: true,
        sound_level_meter: true,
        modem: true
    },
    statuses: {
        OK: true,
        Pending: true,
        Missing: true
    }
};

// Load saved filter preferences from localStorage
function loadFilterPreferences() {
    const saved = localStorage.getItem('dashboardFilters');
    if (saved) {
        try {
            const parsed = JSON.parse(saved);
            if (parsed.deviceTypes) Object.assign(filters.deviceTypes, parsed.deviceTypes);
            if (parsed.statuses) Object.assign(filters.statuses, parsed.statuses);
        } catch (e) {
            console.error('Error loading filter preferences:', e);
        }
    }

    // Sync checkboxes with loaded state
    const seismoCheck = document.getElementById('filter-seismograph');
    const slmCheck = document.getElementById('filter-slm');
    const modemCheck = document.getElementById('filter-modem');
    const okCheck = document.getElementById('filter-ok');
    const pendingCheck = document.getElementById('filter-pending');
    const missingCheck = document.getElementById('filter-missing');

    if (seismoCheck) seismoCheck.checked = filters.deviceTypes.seismograph;
    if (slmCheck) slmCheck.checked = filters.deviceTypes.sound_level_meter;
    if (modemCheck) modemCheck.checked = filters.deviceTypes.modem;
    if (okCheck) okCheck.checked = filters.statuses.OK;
    if (pendingCheck) pendingCheck.checked = filters.statuses.Pending;
    if (missingCheck) missingCheck.checked = filters.statuses.Missing;
}

// Save filter preferences to localStorage
function saveFilterPreferences() {
    localStorage.setItem('dashboardFilters', JSON.stringify(filters));
}

// Apply filters - called when any checkbox changes
function applyFilters() {
    // Update filter state from checkboxes
    const seismoCheck = document.getElementById('filter-seismograph');
    const slmCheck = document.getElementById('filter-slm');
    const modemCheck = document.getElementById('filter-modem');
    const okCheck = document.getElementById('filter-ok');
    const pendingCheck = document.getElementById('filter-pending');
    const missingCheck = document.getElementById('filter-missing');

    if (seismoCheck) filters.deviceTypes.seismograph = seismoCheck.checked;
    if (slmCheck) filters.deviceTypes.sound_level_meter = slmCheck.checked;
    if (modemCheck) filters.deviceTypes.modem = modemCheck.checked;
    if (okCheck) filters.statuses.OK = okCheck.checked;
    if (pendingCheck) filters.statuses.Pending = pendingCheck.checked;
    if (missingCheck) filters.statuses.Missing = missingCheck.checked;

    saveFilterPreferences();

    // Re-render with current data and filters
    if (currentSnapshotData) {
        renderFilteredDashboard(currentSnapshotData);
    }
}

// Reset all filters to show everything
function resetFilters() {
    filters.deviceTypes = { seismograph: true, sound_level_meter: true, modem: true };
    filters.statuses = { OK: true, Pending: true, Missing: true };

    // Update all checkboxes
    const checkboxes = [
        'filter-seismograph', 'filter-slm', 'filter-modem',
        'filter-ok', 'filter-pending', 'filter-missing'
    ];
    checkboxes.forEach(id => {
        const el = document.getElementById(id);
        if (el) el.checked = true;
    });

    saveFilterPreferences();

    if (currentSnapshotData) {
        renderFilteredDashboard(currentSnapshotData);
    }
}

// Check if a unit passes the current filters
function unitPassesFilter(unit) {
    const deviceType = unit.device_type || 'seismograph';
    const status = unit.status || 'Missing';

    // Check device type filter
    if (!filters.deviceTypes[deviceType]) {
        return false;
    }

    // Check status filter
    if (!filters.statuses[status]) {
        return false;
    }

    return true;
}

// Get display label for device type
function getDeviceTypeLabel(deviceType) {
    switch(deviceType) {
        case 'sound_level_meter': return 'SLM';
        case 'modem': return 'Modem';
        default: return 'Seismograph';
    }
}

// Render dashboard with filtered data
function renderFilteredDashboard(data) {
    // Filter active units for alerts
    const filteredActive = {};
    Object.entries(data.active || {}).forEach(([id, unit]) => {
        if (unitPassesFilter(unit)) {
            filteredActive[id] = unit;
        }
    });

    // Update alerts with filtered data
    updateAlertsFiltered(filteredActive);

    // Update map with filtered data
    updateFleetMapFiltered(data.units);
}

// Update the Recent Alerts section with filtering
function updateAlertsFiltered(filteredActive) {
    const alertsList = document.getElementById('alerts-list');
    const missingUnits = Object.entries(filteredActive).filter(([_, u]) => u.status === 'Missing' && u.device_type !== 'modem');

    if (!missingUnits.length) {
        // Check if this is because of filters or genuinely no alerts
        const anyMissing = currentSnapshotData && Object.values(currentSnapshotData.active || {}).some(u => u.status === 'Missing');
        if (anyMissing) {
            alertsList.innerHTML = '<p class="text-sm text-gray-500 dark:text-gray-400">No alerts match current filters</p>';
        } else {
            alertsList.innerHTML = '<p class="text-sm text-green-600 dark:text-green-400">All units reporting normally</p>';
        }
    } else {
        let alertsHtml = '';
        missingUnits.forEach(([id, unit]) => {
            const deviceLabel = getDeviceTypeLabel(unit.device_type);
            alertsHtml += `
            <div class="flex items-start space-x-2 text-sm">
                <span class="w-2 h-2 rounded-full bg-red-500 mt-1.5"></span>
                <div>
                    <a href="/unit/${id}" class="font-medium text-red-600 dark:text-red-400 hover:underline">${id}</a>
                    <span class="text-xs text-gray-500 ml-1">(${deviceLabel})</span>
                    <p class="text-gray-600 dark:text-gray-400">Missing for ${unit.age}</p>
                </div>
            </div>`;
        });
        alertsList.innerHTML = alertsHtml;
    }
}

// Update map with filtered data
function updateFleetMapFiltered(allUnits) {
    if (!fleetMap) return;

    // Clear existing markers
    fleetMarkers.forEach(marker => fleetMap.removeLayer(marker));
    fleetMarkers = [];

    // Get deployed units with coordinates that pass the filter.
    // Modems are not plotted — they inherit the paired device's location,
    // which would just stack a duplicate marker on the same pin.
    const deployedUnits = Object.entries(allUnits || {})
        .filter(([_, u]) => u.deployed
            && u.coordinates
            && (u.device_type || 'seismograph') !== 'modem'
            && unitPassesFilter(u));

    if (deployedUnits.length === 0) {
        return;
    }

    const bounds = [];

    deployedUnits.forEach(([id, unit]) => {
        const coords = parseLocation(unit.coordinates);
        if (coords) {
            const [lat, lon] = coords;

            // Color based on status
            const markerColor = unit.status === 'OK' ? 'green' :
                               unit.status === 'Pending' ? 'orange' : 'red';

            // Different marker style per device type
            const deviceType = unit.device_type || 'seismograph';
            let radius = 8;
            let weight = 2;

            if (deviceType === 'modem') {
                radius = 6;
                weight = 2;
            } else if (deviceType === 'sound_level_meter') {
                radius = 8;
                weight = 3;
            }

            const marker = L.circleMarker([lat, lon], {
                radius: radius,
                fillColor: markerColor,
                color: '#fff',
                weight: weight,
                opacity: 1,
                fillOpacity: 0.8
            }).addTo(fleetMap);

            // Popup with device type
            const deviceLabel = getDeviceTypeLabel(deviceType);

            const locName = unit.location_name || '';
            marker.bindPopup(`
                <div class="p-2">
                    <h3 class="font-bold text-lg">${id}</h3>
                    <p class="text-sm text-gray-600">${deviceLabel}</p>
                    ${locName ? `<p class="text-sm text-gray-700">📍 ${locName}</p>` : ''}
                    <p class="text-sm">Status: <span style="color: ${markerColor}">${unit.status}</span></p>
                    ${unit.note ? `<p class="text-sm text-gray-600">${unit.note}</p>` : ''}
                    <a href="/unit/${id}" class="text-blue-600 hover:underline text-sm">View Details</a>
                </div>
            `);

            fleetMarkers.push(marker);
            bounds.push([lat, lon]);
        }
    });

    // Only fit bounds on initial load, not on subsequent updates
    // This preserves the user's current map view when auto-refreshing
    if (bounds.length > 0 && !fleetMapInitialized) {
        const padding = window.innerWidth < 768 ? [20, 20] : [50, 50];
        fleetMap.fitBounds(bounds, { padding: padding });
        fleetMapInitialized = true;
    }
}

// Toggle card collapse/expand (mobile only)
function toggleCard(cardName) {
    // Only work on mobile
    if (window.innerWidth >= 768) return;

    const content = document.getElementById(`${cardName}-content`);
    const chevron = document.getElementById(`${cardName}-chevron`);

    if (!content || !chevron) return;

    // Toggle collapsed state
    const isCollapsed = content.classList.contains('collapsed');

    if (isCollapsed) {
        content.classList.remove('collapsed');
        chevron.classList.remove('collapsed');

        // If expanding the fleet map, invalidate size after animation
        if (cardName === 'fleet-map' && window.fleetMap) {
            setTimeout(() => {
                window.fleetMap.invalidateSize();
            }, 300);
        }
    } else {
        content.classList.add('collapsed');
        chevron.classList.add('collapsed');
    }

    // Save state to localStorage
    const cardStates = JSON.parse(localStorage.getItem('dashboardCardStates') || '{}');
    cardStates[cardName] = !isCollapsed;
    localStorage.setItem('dashboardCardStates', JSON.stringify(cardStates));
}

// Restore card states from localStorage on page load
function restoreCardStates() {
    const cardStates = JSON.parse(localStorage.getItem('dashboardCardStates') || '{}');
    // Note: todays-actions has its own collapse handling (see toggleTodaysSchedule / onTodaysActionsSwap)
    const cardNames = ['fleet-summary', 'recent-alerts', 'recent-callins', 'fleet-map', 'fleet-status'];

    cardNames.forEach(cardName => {
        const content = document.getElementById(`${cardName}-content`);
        const chevron = document.getElementById(`${cardName}-chevron`);

        if (!content || !chevron) return;

        // Default to expanded (true) if no saved state
        const isCollapsed = cardStates[cardName] === false;

        if (isCollapsed) {
            content.classList.add('collapsed');
            chevron.classList.add('collapsed');
        }
    });
}

// Restore states when DOM is ready
if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', restoreCardStates);
} else {
    restoreCardStates();
}

function updateDashboard(event) {
    try {
        // Only process responses from /api/status-snapshot
        const requestUrl = event.detail.xhr.responseURL || event.detail.pathInfo?.requestPath;
        if (!requestUrl || !requestUrl.includes('/api/status-snapshot')) {
            return; // Ignore responses from other endpoints (like /dashboard/todays-actions)
        }

        const data = JSON.parse(event.detail.xhr.response);

        // Store data for filter re-application
        currentSnapshotData = data;

        // Update "Last updated" timestamp with timezone
        const now = new Date();
        const timezone = localStorage.getItem('timezone') || 'America/New_York';
        document.getElementById('last-refresh').textContent = now.toLocaleTimeString('en-US', {
            hour: '2-digit',
            minute: '2-digit',
            second: '2-digit',
            timeZone: timezone,
            timeZoneName: 'short'
        });

        // ===== Fleet Summary: per-device-type counts (always unfiltered) =====
        // Deployed = unit has an active UnitAssignment (location_id set by
        // the snapshot helper).  Benched = no active assignment.
        // Retired, out-for-calibration, and roster-unknown units (emitters
        // not in the roster) are excluded from totals.
        const counts = {
            seismograph:        { total: 0, deployed: 0, benched: 0 },
            sound_level_meter:  { total: 0, deployed: 0, benched: 0 },
        };
        let monitoredOk = 0, monitoredPending = 0, monitoredMissing = 0;
        const unknownIds = new Set(Object.keys(data.unknown || {}));

        Object.entries(data.units || {}).forEach(([uid, unit]) => {
            if (unit.retired || unit.out_for_calibration) return;
            if (unknownIds.has(uid)) return;
            const dt = unit.device_type || 'seismograph';
            const bucket = counts[dt];
            if (!bucket) return;          // skip modems and anything else

            bucket.total++;
            if (unit.location_id) {
                bucket.deployed++;
            } else {
                bucket.benched++;
            }

            // Status tally only for seismographs + SLMs that are actually
            // deployed (assigned).  Mirrors the per-device buckets so the
            // sum matches.
            if (unit.location_id) {
                if (unit.status === 'OK')      monitoredOk++;
                else if (unit.status === 'Pending') monitoredPending++;
                else if (unit.status === 'Missing') monitoredMissing++;
            }
        });

        document.getElementById('seismo-count').textContent     = counts.seismograph.total;
        document.getElementById('seismo-deployed').textContent  = counts.seismograph.deployed;
        document.getElementById('seismo-benched').textContent   = counts.seismograph.benched;
        document.getElementById('slm-count').textContent        = counts.sound_level_meter.total;
        document.getElementById('slm-deployed').textContent     = counts.sound_level_meter.deployed;
        document.getElementById('slm-benched').textContent      = counts.sound_level_meter.benched;
        document.getElementById('status-ok').textContent        = monitoredOk;
        document.getElementById('status-pending').textContent   = monitoredPending;
        document.getElementById('status-missing').textContent   = monitoredMissing;

        // ===== Apply filters and render map + alerts =====
        renderFilteredDashboard(data);

    } catch (err) {
        console.error("Dashboard update error:", err);
    }
}

// Handle tab switching and initialize components
document.addEventListener('DOMContentLoaded', function() {
    // Load filter preferences
    loadFilterPreferences();

    const tabButtons = document.querySelectorAll('.tab-button');

    tabButtons.forEach(button => {
        button.addEventListener('click', function() {
            // Remove active-tab class from all buttons
            tabButtons.forEach(btn => btn.classList.remove('active-tab'));
            // Add active-tab class to clicked button
            this.classList.add('active-tab');
        });
    });

    // Initialize fleet map
    initFleetMap();
});

let fleetMap = null;
let fleetMarkers = [];
let fleetMapInitialized = false;

// Make fleetMap accessible globally for toggleCard function
window.fleetMap = null;

function initFleetMap() {
    // Initialize the map centered on the US (can adjust based on your deployment area)
    fleetMap = L.map('fleet-map').setView([39.8283, -98.5795], 4);
    window.fleetMap = fleetMap;

    // Add OpenStreetMap tiles
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution: '© OpenStreetMap contributors',
        maxZoom: 18
    }).addTo(fleetMap);

    // Force map to recalculate size after a brief delay to ensure container is fully rendered
    setTimeout(() => {
        fleetMap.invalidateSize();
    }, 100);
}

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

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

    // TODO: Add geocoding support for address strings
    return null;
}

// Load and display recent photos
async function loadRecentPhotos() {
    try {
        const response = await fetch('/api/recent-photos?limit=12');
        if (!response.ok) {
            throw new Error('Failed to load recent photos');
        }

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

        if (data.photos && data.photos.length > 0) {
            gallery.innerHTML = '';
            data.photos.forEach(photo => {
                const photoDiv = document.createElement('div');
                photoDiv.className = 'relative group';
                photoDiv.innerHTML = `
                    <a href="/unit/${photo.unit_id}" class="block">
                        <img src="${photo.path}" alt="${photo.unit_id}"
                             class="w-full h-32 object-cover rounded-lg shadow hover:shadow-lg transition-shadow">
                        <div class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent p-2 rounded-b-lg">
                            <p class="text-white text-xs font-semibold">${photo.unit_id}</p>
                        </div>
                    </a>
                `;
                gallery.appendChild(photoDiv);
            });
        } else {
            gallery.innerHTML = '<p class="text-sm text-gray-500 dark:text-gray-400 col-span-full">No photos uploaded yet. Upload photos from unit detail pages.</p>';
        }
    } catch (error) {
        console.error('Error loading recent photos:', error);
        document.getElementById('recentPhotosGallery').innerHTML = '<p class="text-sm text-red-500 col-span-full">Failed to load recent photos</p>';
    }
}

// Load recent photos on page load and refresh every 30 seconds
loadRecentPhotos();
setInterval(loadRecentPhotos, 30000);

// Load and display recent call-ins.
// Source: SFM events (forwarded by series3-watcher from Blastware ACH).
// Each event = one call-home.  Heartbeat-derived endpoint /api/recent-callins
// is being phased out but kept as a backup.
async function loadRecentCallins() {
    const callinsList = document.getElementById('recent-callins-list');
    try {
        const response = await fetch('/api/recent-event-callins?limit=10');
        if (!response.ok) {
            throw new Error('Failed to load recent call-ins');
        }

        const data = await response.json();

        if (!data.call_ins || data.call_ins.length === 0) {
            callinsList.innerHTML = '<p class="text-sm text-gray-500 dark:text-gray-400">No recent event call-ins from SFM</p>';
            return;
        }

        // Two-column dense grid on lg+, single column below.
        let html = '<div class="grid grid-cols-1 lg:grid-cols-2 gap-x-4 gap-y-1">';
        data.call_ins.forEach(c => {
            const isFalse = c.false_trigger;
            const pvs = c.peak_vector_sum;
            const pvsStr = (pvs !== null && pvs !== undefined)
                ? Number(pvs).toFixed(3) + ' in/s'
                : '—';

            // Subtitle: prefer sensor_location, fallback to project.
            const subtitle = c.sensor_location || c.project || '';

            // Status dot: amber for false trigger, green for real event,
            // gray if unit not in roster.
            const dotClass = !c.in_roster
                ? 'bg-gray-400'
                : (isFalse ? 'bg-amber-400' : 'bg-green-500');

            // Format event timestamp short (e.g. "05-13 05:00").
            let tsShort = '';
            if (c.event_timestamp) {
                const ts = c.event_timestamp.replace('T', ' ');
                // "2026-05-13 05:00:13" → "05-13 05:00"
                tsShort = ts.length >= 16 ? ts.slice(5, 16) : ts;
            }

            const unitLink = c.in_roster
                ? `<a href="/unit/${c.unit_id}" class="font-medium text-gray-900 dark:text-white hover:text-seismo-orange">${c.unit_id}</a>`
                : `<span class="font-medium text-gray-500 dark:text-gray-400" title="Not in roster">${c.unit_id}</span>`;

            html += `
            <div class="flex items-center justify-between py-1.5 border-b border-gray-100 dark:border-gray-700/50 last:border-0">
                <div class="flex items-center gap-2 min-w-0 flex-1">
                    <span class="w-2 h-2 rounded-full ${dotClass} flex-shrink-0" title="${isFalse ? 'False trigger' : 'Event'}"></span>
                    <div class="min-w-0 flex-1">
                        <div class="flex items-center gap-2">
                            ${unitLink}
                            ${isFalse ? '<span class="text-[10px] uppercase tracking-wide text-amber-600 dark:text-amber-400">false</span>' : ''}
                            <span class="text-xs text-gray-500 dark:text-gray-400">${pvsStr}</span>
                        </div>
                        ${subtitle ? `<p class="text-xs text-gray-500 dark:text-gray-400 truncate" title="${subtitle.replace(/"/g, '&quot;')}">${subtitle}</p>` : ''}
                    </div>
                </div>
                <div class="text-right ml-2 flex-shrink-0">
                    <span class="text-xs text-gray-600 dark:text-gray-400 block">${c.time_ago}</span>
                    ${tsShort ? `<span class="text-[10px] text-gray-400 dark:text-gray-500 block font-mono">${tsShort}</span>` : ''}
                </div>
            </div>`;
        });
        html += '</div>';
        callinsList.innerHTML = html;
    } catch (error) {
        console.error('Error loading recent call-ins:', error);
        callinsList.innerHTML = '<p class="text-sm text-red-500">Failed to load recent call-ins</p>';
    }
}

// Load recent call-ins on page load and refresh every 30 seconds.
loadRecentCallins();
setInterval(loadRecentCallins, 30000);

// ===== Today's Schedule horizontal card =====
function toggleTodaysSchedule() {
    const content = document.getElementById('todays-actions-content');
    const chevron = document.getElementById('todays-actions-chevron');
    if (!content || !chevron) return;
    const isCollapsed = content.classList.toggle('collapsed');
    chevron.classList.toggle('collapsed', isCollapsed);
    // Remember the user's explicit choice so we don't fight them on the next
    // 30s htmx refresh.
    localStorage.setItem('todaysScheduleUserToggled', '1');
    localStorage.setItem('todaysScheduleCollapsed', isCollapsed ? '1' : '0');
}

function onTodaysActionsSwap(el) {
    // Read pending/total counts from the rendered partial to drive
    // auto-expand + the header badge.
    const badge = document.getElementById('todays-actions-badge');
    const content = document.getElementById('todays-actions-content');
    const chevron = document.getElementById('todays-actions-chevron');
    if (!content || !chevron) return;

    // Count yellow status indicators in the rendered partial as a proxy for
    // "pending action present today".
    const pendingDots = el.querySelectorAll('.bg-yellow-400').length;
    const pendingTimes = el.querySelectorAll('.text-yellow-600').length;
    const hasPending = pendingDots > 0 || pendingTimes > 0;

    if (badge) {
        if (hasPending) {
            const n = Math.max(pendingDots, pendingTimes);
            badge.textContent = `${n} pending today`;
            badge.classList.remove('hidden');
        } else {
            badge.classList.add('hidden');
        }
    }

    // Auto-expand only if the user hasn't manually toggled this session AND
    // there's something pending.  Once the user collapses/expands manually,
    // their preference sticks.
    const userToggled = localStorage.getItem('todaysScheduleUserToggled') === '1';
    if (!userToggled && hasPending) {
        content.classList.remove('collapsed');
        chevron.classList.remove('collapsed');
    } else if (!userToggled && !hasPending) {
        content.classList.add('collapsed');
        chevron.classList.add('collapsed');
    } else if (userToggled) {
        const stored = localStorage.getItem('todaysScheduleCollapsed') === '1';
        content.classList.toggle('collapsed', stored);
        chevron.classList.toggle('collapsed', stored);
    }
}
</script>

{% endblock %}
