{% extends "base.html" %}

{% block title %}Job Planner - Terra-View{% endblock %}

{% block extra_head %}
<style>
/* Calendar grid layout */
.calendar-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 1rem;
}

@media (max-width: 1280px) {
    .calendar-grid {
        grid-template-columns: repeat(3, 1fr);
    }
}

@media (max-width: 768px) {
    .calendar-grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (max-width: 480px) {
    .calendar-grid {
        grid-template-columns: 1fr;
    }
}

/* Month card */
.month-card {
    background: white;
    border-radius: 0.5rem;
    padding: 0.75rem;
    box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}

.dark .month-card {
    background: rgb(30 41 59);
}

/* Day grid */
.day-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 2px;
    font-size: 0.75rem;
}

.day-header {
    text-align: center;
    font-weight: 600;
    color: #6b7280;
    padding: 0.25rem 0;
}

.dark .day-header {
    color: #9ca3af;
}

/* Day cell */
.day-cell {
    aspect-ratio: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 0.25rem;
    cursor: pointer;
    position: relative;
    transition: all 0.15s ease;
    font-size: 0.7rem;
}

.day-cell:hover {
    transform: scale(1.1);
    z-index: 10;
}

.day-cell.today {
    ring: 2px;
    ring-color: #f48b1c;
    font-weight: bold;
}

/* Default neutral day style */
.day-cell.neutral {
    background-color: #f3f4f6;
    color: #374151;
}

.dark .day-cell.neutral {
    background-color: rgba(55, 65, 81, 0.5);
    color: #d1d5db;
}

/* Status indicator dot */
.day-cell .status-dot {
    position: absolute;
    top: 2px;
    right: 2px;
    width: 5px;
    height: 5px;
    border-radius: 50%;
}

.day-cell .status-dot.expired {
    background-color: #ef4444;
}

/* Reservation mode colors - applied dynamically */
.day-cell.reservation-available {
    background-color: #dcfce7;
    color: #166534;
}

.dark .day-cell.reservation-available {
    background-color: rgba(34, 197, 94, 0.2);
    color: #86efac;
}

.day-cell.reservation-partial {
    background-color: #fef3c7;
    color: #92400e;
}

.dark .day-cell.reservation-partial {
    background-color: rgba(245, 158, 11, 0.2);
    color: #fcd34d;
}

.day-cell.reservation-unavailable {
    background-color: #fee2e2;
    color: #991b1b;
}

.dark .day-cell.reservation-unavailable {
    background-color: rgba(239, 68, 68, 0.2);
    color: #fca5a5;
}

/* Legacy status colors (kept for day detail) */
.day-cell.some-reserved {
    background-color: #dbeafe;
    color: #1e40af;
}

.dark .day-cell.some-reserved {
    background-color: rgba(59, 130, 246, 0.2);
    color: #93c5fd;
}

.day-cell.empty {
    background: transparent;
    cursor: default;
}

.day-cell.empty:hover {
    transform: none;
}

/* Reservation bar */
.reservation-bar {
    display: flex;
    align-items: center;
    padding: 0.5rem 0.75rem;
    border-radius: 0.375rem;
    margin-bottom: 0.5rem;
    cursor: pointer;
    transition: opacity 0.15s ease;
}

.reservation-bar:hover {
    opacity: 0.8;
}

/* Slide-over panel */
.slide-panel {
    position: fixed;
    top: 0;
    right: 0;
    width: 100%;
    max-width: 28rem;
    height: 100vh;
    background: white;
    box-shadow: -4px 0 15px rgba(0,0,0,0.1);
    transform: translateX(100%);
    transition: transform 0.3s ease;
    z-index: 50;
    overflow-y: auto;
}

.dark .slide-panel {
    background: rgb(30 41 59);
}

.slide-panel.open {
    transform: translateX(0);
}

.panel-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.3);
    opacity: 0;
    visibility: hidden;
    transition: all 0.3s ease;
    z-index: 40;
}

.panel-backdrop.open {
    opacity: 1;
    visibility: visible;
}

/* Cal expire dots hidden by default; shown when toggle is active */
.cal-expire-dots-hidden .status-dot.expired {
    display: none;
}

/* Job bar visibility toggles */
.hide-confirmed .job-bar-confirmed { display: none; }
.hide-planned   .job-bar-planned   { display: none; }

/* Planned = dashed appearance via repeating-linear-gradient */
.job-bar-planned {
    background-image: repeating-linear-gradient(
        90deg,
        var(--bar-color) 0px,
        var(--bar-color) 4px,
        transparent 4px,
        transparent 7px
    ) !important;
    background-color: transparent !important;
    opacity: 0.75;
}
</style>
{% endblock %}

{% block content %}
<div class="mb-6">
    <h1 class="text-3xl font-bold text-gray-900 dark:text-white">Job Planner</h1>
    <p class="text-gray-600 dark:text-gray-400 mt-1">Plan and manage field deployments</p>
</div>

<!-- Main tab bar -->
<div class="flex rounded-lg bg-gray-100 dark:bg-gray-700 p-1 mb-6 w-fit">
    <button id="tab-btn-planner" onclick="switchTab('planner')"
            class="px-4 py-2 rounded-md text-sm font-medium transition-colors bg-white dark:bg-slate-600 text-gray-900 dark:text-white shadow">
        Planner
    </button>
    <button id="tab-btn-calendar" onclick="switchTab('calendar')"
            class="px-4 py-2 rounded-md text-sm font-medium transition-colors text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">
        Calendar
    </button>
</div>

<div id="view-calendar" class="hidden">

<!-- Stats + Legend row -->
<div class="flex flex-wrap items-center gap-4 mb-6">
    <!-- Compact stat pills -->
    <div class="flex items-center gap-3 text-sm bg-white dark:bg-slate-800 rounded-lg px-4 py-2 shadow flex-shrink-0">
        <span class="text-gray-500 dark:text-gray-400">{{ calendar_data.total_units }} total</span>
        <span class="text-gray-300 dark:text-gray-600">|</span>
        <span class="text-green-600 dark:text-green-400 font-medium"><span id="available-today">--</span> avail</span>
        <span class="text-gray-300 dark:text-gray-600">|</span>
        <span class="text-blue-600 dark:text-blue-400 font-medium"><span id="reserved-today">--</span> reserved</span>
        <span class="text-gray-300 dark:text-gray-600">|</span>
        <span class="text-yellow-600 dark:text-yellow-400 font-medium"><span id="expiring-today">--</span> expiring</span>
        <span class="text-gray-300 dark:text-gray-600">|</span>
        <span class="text-red-600 dark:text-red-400 font-medium"><span id="expired-today">--</span> cal expired</span>
    </div>

    <!-- Job legend -->
    {% if calendar_projects %}
    <div class="flex flex-wrap items-center gap-3">
        {% for item in calendar_projects %}
        <span class="flex items-center gap-1.5 text-sm text-gray-700 dark:text-gray-300">
            <span class="w-3 h-1.5 rounded-full flex-shrink-0" style="background-color: {{ item.color }};"></span>
            {{ item.name }}
        </span>
        {% endfor %}
    </div>
    {% endif %}
</div>

<!-- Action Bar -->
<div class="flex flex-wrap items-center justify-between gap-4 mb-6">
    <div class="flex items-center gap-4">
        <!-- Device Type Toggle -->
        <div class="flex rounded-lg bg-gray-100 dark:bg-gray-700 p-1">
            <button onclick="switchDeviceType('seismograph')"
               class="px-4 py-2 rounded-md text-sm font-medium transition-colors {% if device_type == 'seismograph' %}bg-white dark:bg-slate-600 text-gray-900 dark:text-white shadow{% else %}text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white{% endif %}">
                Seismographs
            </button>
            <button onclick="switchDeviceType('slm')"
               class="px-4 py-2 rounded-md text-sm font-medium transition-colors {% if device_type == 'slm' %}bg-white dark:bg-slate-600 text-gray-900 dark:text-white shadow{% else %}text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white{% endif %}">
                SLMs
            </button>
        </div>

        <!-- Legend / toggles -->
        <div class="hidden md:flex items-center gap-2 text-sm" id="main-legend">
            <!-- Confirmed jobs toggle -->
            <button onclick="toggleJobLayer('confirmed')" id="toggle-confirmed"
                    class="flex items-center gap-1.5 px-2 py-1 rounded transition-colors text-gray-700 dark:text-gray-200 bg-gray-100 dark:bg-gray-700"
                    title="Show/hide confirmed jobs">
                <span class="w-4 h-1.5 rounded-full bg-gray-500 dark:bg-gray-300 flex-shrink-0"></span>
                Confirmed
            </button>
            <!-- Planned jobs toggle -->
            <button onclick="toggleJobLayer('planned')" id="toggle-planned"
                    class="flex items-center gap-1.5 px-2 py-1 rounded transition-colors text-gray-700 dark:text-gray-200 bg-gray-100 dark:bg-gray-700"
                    title="Show/hide planned jobs">
                <span class="w-4 h-1.5 flex-shrink-0" style="background-image: repeating-linear-gradient(90deg, #6b7280 0px, #6b7280 4px, transparent 4px, transparent 7px);"></span>
                Planned
            </button>
            <!-- Cal expire dots toggle -->
            <button onclick="toggleCalExpireDots()" id="cal-expire-toggle"
                    class="flex items-center gap-1.5 px-2 py-1 rounded text-gray-400 dark:text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
                    title="Show/hide calibration expiry dots">
                <span class="w-3 h-3 rounded bg-gray-200 dark:bg-gray-600 relative flex-shrink-0">
                    <span class="absolute top-0 right-0 w-1.5 h-1.5 rounded-full bg-red-500"></span>
                </span>
                Cal expires
            </button>
        </div>
        <!-- Reservation mode legend (hidden by default) -->
        <div class="hidden md:hidden items-center gap-4 text-sm" id="reservation-legend">
            <span class="flex items-center gap-1">
                <span class="w-3 h-3 rounded bg-green-200 dark:bg-green-700"></span>
                Available
            </span>
            <span class="flex items-center gap-1">
                <span class="w-3 h-3 rounded bg-yellow-200 dark:bg-yellow-700"></span>
                Partial
            </span>
            <span class="flex items-center gap-1">
                <span class="w-3 h-3 rounded bg-red-200 dark:bg-red-700"></span>
                Unavailable
            </span>
        </div>
    </div>

    <button onclick="openReservationModal()"
            class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 flex items-center gap-2">
        <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
        </svg>
        New Reservation
    </button>
</div>

<!-- Calendar Grid -->
<div class="calendar-grid mb-8">
    {% for month_data in calendar_data.months %}
    <div class="month-card">
        <h3 class="font-semibold text-gray-900 dark:text-white mb-2 text-center">
            {{ month_data.short_name }} '{{ month_data.year_short }}
        </h3>
        <div class="day-grid">
            <!-- Day headers -->
            <div class="day-header">S</div>
            <div class="day-header">M</div>
            <div class="day-header">T</div>
            <div class="day-header">W</div>
            <div class="day-header">T</div>
            <div class="day-header">F</div>
            <div class="day-header">S</div>

            <!-- Empty cells for alignment (first_weekday is 0=Mon, we need 0=Sun) -->
            {% set first_day_offset = (month_data.first_weekday + 1) % 7 %}
            {% for i in range(first_day_offset) %}
            <div class="day-cell empty"></div>
            {% endfor %}

            <!-- Day cells -->
            {% for day_num in range(1, month_data.num_days + 1) %}
            {% set day_data = month_data.days[day_num] %}
            {% set date_str = '%04d-%02d-%02d'|format(month_data.year, month_data.month, day_num) %}
            {% set is_today = date_str == today %}
            {% set has_cal_expiring = day_data.cal_expiring_on_day is defined and day_data.cal_expiring_on_day > 0 %}
            {% set day_projects = [] %}
            {% for p in calendar_projects %}
                {% if p.start_date <= date_str and (p.end_date is none or p.end_date >= date_str) %}
                    {% set _ = day_projects.append(p) %}
                {% endif %}
            {% endfor %}
            <div class="day-cell neutral {% if is_today %}today ring-2 ring-seismo-orange{% endif %}"
                 data-date="{{ date_str }}"
                 data-available="{{ day_data.available }}"
                 onclick="openDayPanel('{{ date_str }}')"
                 title="Available: {{ day_data.available }}, Reserved: {{ day_data.reserved }}{% if has_cal_expiring %}, Cal expires: {{ day_data.cal_expiring_on_day }}{% endif %}{% for p in day_projects %} | {{ p.name }}{% endfor %}">
                {{ day_num }}
                {% if has_cal_expiring %}
                <span class="status-dot expired"></span>
                {% endif %}
                {% if day_projects %}
                <span class="absolute bottom-0.5 left-0.5 right-0.5 flex flex-col gap-px">
                    {% for p in day_projects %}
                    <span class="job-bar-wrap group/bar relative block">
                        <span class="block h-1 rounded-full {% if p.confirmed %}job-bar-confirmed{% else %}job-bar-planned{% endif %}"
                              style="--bar-color: {{ p.color }}; background-color: {{ p.color }};"></span>
                        <span class="job-bar-tip pointer-events-none absolute bottom-full left-1/2 -translate-x-1/2 mb-1 px-2 py-1 rounded bg-gray-900 text-white text-xs whitespace-nowrap opacity-0 group-hover/bar:opacity-100 transition-opacity z-50 shadow-lg">
                            {{ p.name }}{% if p.start_date and p.end_date %} · {{ p.start_date }} – {{ p.end_date }}{% endif %}
                        </span>
                    </span>
                    {% endfor %}
                </span>
                {% endif %}
            </div>
            {% endfor %}
        </div>
    </div>
    {% endfor %}
</div>

<!-- Month Navigation (centered below calendar) -->
<div class="flex items-center justify-center gap-3 mb-8">
    <a href="/fleet-calendar?year={{ prev_year }}&month={{ prev_month }}&device_type={{ device_type }}"
       class="px-3 py-2 rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300"
       title="Previous month">
        <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="M15 19l-7-7 7-7"/>
        </svg>
    </a>
    <span class="text-lg font-bold text-gray-900 dark:text-white px-3">
        {{ calendar_data.months[0].name }} {{ calendar_data.months[0].year }} - {{ calendar_data.months[11].name }} {{ calendar_data.months[11].year }}
    </span>
    <a href="/fleet-calendar?year={{ next_year }}&month={{ next_month }}&device_type={{ device_type }}"
       class="px-3 py-2 rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300"
       title="Next month">
        <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="M9 5l7 7-7 7"/>
        </svg>
    </a>
    <a href="/fleet-calendar?device_type={{ device_type }}"
       class="ml-2 px-4 py-2 rounded-lg bg-seismo-orange text-white hover:bg-orange-600">
        Today
    </a>
</div>

<!-- Active Reservations -->
<div class="bg-white dark:bg-slate-800 rounded-xl shadow-lg p-6 mb-8">
    <h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">Active Reservations</h2>
    <div id="reservations-list"
         hx-get="/api/fleet-calendar/reservations-list?year={{ start_year }}&month={{ start_month }}&device_type={{ device_type }}"
         hx-trigger="calendar-reservations-refresh from:body"
         hx-swap="innerHTML">
        <p class="text-gray-500">Loading reservations...</p>
    </div>
</div>

</div><!-- end #view-calendar -->

<!-- Job Planner View -->
<div id="view-planner">
    <div class="flex flex-col lg:flex-row gap-6 min-h-[70vh]">

        <!-- LEFT PANEL -->
        <div class="lg:w-2/5 flex flex-col gap-4">

            <!-- Reservations list panel -->
            <div id="ptab-list" class="bg-white dark:bg-slate-800 rounded-xl shadow-lg p-6 flex flex-col gap-4">
                <div class="flex items-center justify-between">
                    <h2 class="text-xl font-semibold text-gray-900 dark:text-white">Jobs</h2>
                    <button onclick="plannerReset(); switchPlannerTab('assign')"
                            class="px-3 py-1.5 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm 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="M12 4v16m8-8H4"/>
                        </svg>
                        New Job
                    </button>
                </div>
                <div id="planner-reservations-list" class="overflow-y-visible"
                     hx-get="/api/fleet-calendar/reservations-list?year={{ start_year }}&month={{ start_month }}&device_type={{ device_type }}"
                     hx-trigger="load"
                     hx-swap="innerHTML">
                    <p class="text-gray-500">Loading reservations...</p>
                </div>
            </div>

            <!-- Job form panel -->
            <div id="ptab-assign" class="hidden bg-white dark:bg-slate-800 rounded-xl shadow-lg p-6 flex flex-col gap-4 flex-1">
                <div class="flex items-center justify-between gap-2 flex-wrap">
                    <h2 class="text-xl font-semibold text-gray-900 dark:text-white" id="planner-form-title">New Job</h2>
                    <div class="flex items-center gap-2">
                        <!-- Edit-mode actions (shown when editing an existing job) -->
                        <div id="planner-edit-actions" class="hidden flex items-center gap-2">
                            <button onclick="plannerPromote()"
                                    class="text-xs px-3 py-1.5 bg-green-600 hover:bg-green-700 text-white rounded-lg font-medium">
                                Promote to Project
                            </button>
                            <button onclick="plannerDeleteCurrent()"
                                    class="text-xs px-3 py-1.5 bg-red-600 hover:bg-red-700 text-white rounded-lg font-medium">
                                Delete
                            </button>
                        </div>
                        <button onclick="switchPlannerTab('list')"
                                class="text-sm text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 flex items-center gap-1">
                            <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="M6 18L18 6M6 6l12 12"/>
                            </svg>
                            Cancel
                        </button>
                    </div>
                </div>

            <!-- Job details collapsible card -->
            <div id="planner-meta-fields" class="rounded-lg border border-gray-200 dark:border-gray-700">

                <!-- Collapsed header (summary row) -->
                <button type="button" onclick="togglePlannerMeta()"
                        class="w-full flex items-center justify-between px-4 py-3 text-left hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors rounded-lg">
                    <div class="flex items-center gap-2 min-w-0">
                        <span id="planner-meta-color-dot" class="w-3 h-3 rounded-full flex-shrink-0" style="background:#3B82F6"></span>
                        <span id="planner-meta-summary" class="text-sm font-medium text-gray-700 dark:text-gray-300 truncate">New Job</span>
                    </div>
                    <svg id="planner-meta-chevron" class="w-4 h-4 text-gray-400 flex-shrink-0 transition-transform"
                         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"/>
                    </svg>
                </button>

                <!-- Expanded fields -->
                <div id="planner-meta-body" class="hidden px-4 pb-4 flex flex-col gap-4 border-t border-gray-200 dark:border-gray-700">

                <!-- Name -->
                <div class="pt-4">
                    <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Job Name *</label>
                    <input type="text" id="planner-name" placeholder="e.g., Pine Street – May Deployment"
                           oninput="plannerUpdateMetaSummary()"
                           class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500">
                </div>

                <!-- Device Type -->
                <div>
                    <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Device Type *</label>
                    <div class="flex rounded-lg border border-gray-300 dark:border-gray-600 overflow-hidden">
                        <label class="flex-1 cursor-pointer">
                            <input type="radio" name="planner_device_type" value="seismograph" checked class="sr-only peer" onchange="plannerDeviceTypeChanged()">
                            <span class="block text-center px-4 py-2 text-sm font-medium bg-white dark:bg-slate-700 peer-checked:bg-blue-600 peer-checked:text-white text-gray-700 dark:text-gray-300 transition-colors">
                                Seismograph
                            </span>
                        </label>
                        <label class="flex-1 cursor-pointer border-l border-gray-300 dark:border-gray-600">
                            <input type="radio" name="planner_device_type" value="slm" class="sr-only peer" onchange="plannerDeviceTypeChanged()">
                            <span class="block text-center px-4 py-2 text-sm font-medium bg-white dark:bg-slate-700 peer-checked:bg-blue-600 peer-checked:text-white text-gray-700 dark:text-gray-300 transition-colors">
                                Sound Level Meter
                            </span>
                        </label>
                    </div>
                </div>

                <!-- Project -->
                <div>
                    <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Link to Project (optional)</label>
                    <select id="planner-project"
                            class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500">
                        <option value="">-- No project --</option>
                        {% for project in projects %}
                        <option value="{{ project.id }}">{{ project.name }}</option>
                        {% endfor %}
                    </select>
                </div>

                <!-- Dates -->
                <div class="grid grid-cols-2 gap-3">
                    <div>
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Start Date *</label>
                        <input type="date" id="planner-start"
                               onchange="plannerDatesChanged(); plannerUpdateMetaSummary()"
                               class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500">
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">End Date *</label>
                        <input type="date" id="planner-end"
                               onchange="plannerDatesChanged(); plannerUpdateMetaSummary()"
                               class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500">
                    </div>
                </div>

                <!-- Color -->
                <div>
                    <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Color</label>
                    <input type="hidden" id="planner-color-value" value="#3B82F6">
                    <div class="flex flex-wrap gap-1.5 items-center" id="planner-color-swatches"></div>
                    <div class="flex items-center gap-2 mt-2">
                        <label class="text-xs text-gray-500 dark:text-gray-400">Custom:</label>
                        <input type="color" id="planner-color-wheel" value="#3B82F6"
                               oninput="plannerSetColor(this.value, false)"
                               class="w-8 h-8 rounded cursor-pointer border border-gray-300 dark:border-gray-600 p-0.5 bg-white dark:bg-slate-700">
                    </div>
                </div>

                <!-- Estimated Units -->
                <div>
                    <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Estimated Units</label>
                    <p class="text-xs text-gray-400 dark:text-gray-500 mb-1">Rough planning number — how many units you think this job will need</p>
                    <input type="number" id="planner-est-units" min="1" placeholder="e.g. 5"
                           class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500">
                </div>

                </div><!-- end #planner-meta-body -->
            </div><!-- end #planner-meta-fields -->

            <!-- Monitoring Locations -->
            <div class="flex items-center justify-between mt-2">
                <h3 class="text-sm font-semibold text-gray-700 dark:text-gray-300">Monitoring Locations</h3>
                <button onclick="plannerAddSlot()"
                        class="text-xs px-2 py-1 bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 rounded text-gray-700 dark:text-gray-300">
                    + Add Location
                </button>
            </div>

            <div id="planner-slots" class="flex flex-col gap-2">
                <!-- Locations rendered by JS -->
                <p class="text-sm text-gray-400 dark:text-gray-500 text-center py-4" id="planner-slots-empty">
                    Set dates and click "+ Add Location" to start adding units
                </p>
            </div>

            <!-- Notes -->
            <div>
                <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Notes (optional)</label>
                <textarea id="planner-notes" rows="2" placeholder="Optional notes"
                          class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500"></textarea>
            </div>

            <!-- Save -->
            <div class="flex gap-3 pt-2 border-t border-gray-200 dark:border-gray-700 mt-auto">
                <button onclick="plannerReset()"
                        class="px-4 py-2 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg text-sm">
                    Clear
                </button>
                <button onclick="plannerSave()"
                        class="flex-1 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm font-medium" id="planner-save-btn">
                    Save Job
                </button>
            </div>
            </div><!-- end ptab-assign -->

        </div><!-- end left panel -->

        <!-- RIGHT PANEL -->
        <div class="lg:w-3/5 flex flex-col gap-4">

            <!-- Fleet Summary (shown on jobs list) -->
            <div id="right-fleet-summary" class="bg-white dark:bg-slate-800 rounded-xl shadow-lg p-6 flex flex-col gap-4">
                <h2 class="text-xl font-semibold text-gray-900 dark:text-white">Fleet Summary</h2>
                <div id="fleet-summary-stats" class="flex flex-col gap-0">
                    <!-- Populated by JS -->
                </div>
                <input type="text" id="summary-search" placeholder="Search by unit ID..."
                       oninput="summaryFilterUnits()"
                       class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500">
                <div id="fleet-summary-list" class="flex flex-col gap-1 overflow-y-auto" style="max-height: 55vh;">
                    <p class="text-sm text-gray-400 dark:text-gray-500 text-center py-8">Loading fleet...</p>
                </div>
            </div>

            <!-- Available Units (shown when assigning) -->
            <div id="right-available-units" class="hidden bg-white dark:bg-slate-800 rounded-xl shadow-lg p-6 flex flex-col gap-4">
                <div class="flex items-center justify-between flex-wrap gap-2">
                    <h2 class="text-xl font-semibold text-gray-900 dark:text-white">Available Units
                        <span id="planner-avail-count" class="ml-2 text-sm font-normal text-gray-500 dark:text-gray-400"></span>
                    </h2>
                    <div class="flex items-center gap-3 text-sm text-gray-600 dark:text-gray-400">
                        <label class="flex items-center gap-1.5 cursor-pointer">
                            <input type="checkbox" id="planner-deployed-only" onchange="plannerFilterUnits()"
                                   class="rounded border-gray-300 dark:border-gray-600 text-blue-600 focus:ring-blue-500">
                            Deployed only
                        </label>
                        <label class="flex items-center gap-1.5 cursor-pointer">
                            <input type="checkbox" id="planner-benched-only" onchange="plannerFilterUnits()"
                                   class="rounded border-gray-300 dark:border-gray-600 text-blue-600 focus:ring-blue-500">
                            Benched only
                        </label>
                    </div>
                </div>
                <input type="text" id="planner-search" placeholder="Search by unit ID..."
                       oninput="plannerFilterUnits()"
                       class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500">
                <div id="planner-units-list" class="flex flex-col gap-1 overflow-y-auto flex-1" style="max-height: 55vh;">
                    <p class="text-sm text-gray-400 dark:text-gray-500 text-center py-8" id="planner-units-placeholder">
                        Set start and end dates to see available units
                    </p>
                </div>
            </div>

        </div><!-- end right panel -->

    </div><!-- end flex row -->
</div><!-- end view-planner -->

<!-- Unit Detail Modal (planner) -->
<div id="unit-detail-modal" class="fixed inset-0 z-50 hidden">
    <div class="fixed inset-0 bg-black/50" onclick="closeUnitDetailModal()"></div>
    <div class="fixed inset-0 flex items-center justify-center p-4">
        <div class="bg-white dark:bg-slate-800 rounded-xl shadow-2xl w-full max-w-4xl max-h-[90vh] flex flex-col" onclick="event.stopPropagation()">
            <div class="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700">
                <h3 class="font-semibold text-gray-900 dark:text-white" id="unit-detail-modal-title">Unit Detail</h3>
                <button onclick="closeUnitDetailModal()" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
                    <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
                    </svg>
                </button>
            </div>
            <iframe id="unit-detail-iframe" src="" class="flex-1 rounded-b-xl" style="min-height: 70vh; border: none;"></iframe>
        </div>
    </div>
</div>

<!-- Unit Quick-Info Modal -->
<div id="unit-quick-modal" class="fixed inset-0 z-50 hidden">
    <div class="fixed inset-0 bg-black/50" onclick="closeUnitQuickModal()"></div>
    <div class="fixed inset-0 flex items-center justify-center p-4 pointer-events-none">
        <div id="unit-quick-modal-inner" class="bg-white dark:bg-slate-800 rounded-xl shadow-2xl w-full max-w-md pointer-events-auto" onclick="event.stopPropagation()">
            <!-- Header -->
            <div class="flex items-center justify-between px-5 py-4 border-b border-gray-200 dark:border-gray-700">
                <div class="flex items-center gap-3">
                    <h3 id="uqm-title" class="text-lg font-bold text-gray-900 dark:text-white"></h3>
                    <span id="uqm-deployed-badge"></span>
                    <span id="uqm-outforcal-badge"></span>
                </div>
                <button onclick="closeUnitQuickModal()" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
                    <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>
            <!-- Body -->
            <div class="px-5 py-4 flex flex-col gap-4">
                <!-- Cal row -->
                <div class="flex gap-6">
                    <div>
                        <p class="text-xs text-gray-500 dark:text-gray-400 mb-0.5">Last Calibration</p>
                        <p id="uqm-cal-date" class="text-sm font-medium text-gray-900 dark:text-white"></p>
                    </div>
                    <div>
                        <p class="text-xs text-gray-500 dark:text-gray-400 mb-0.5">Cal Due</p>
                        <p id="uqm-cal-due" class="text-sm font-medium"></p>
                    </div>
                </div>
                <!-- Location / address -->
                <div id="uqm-address-row" class="hidden">
                    <p class="text-xs text-gray-500 dark:text-gray-400 mb-0.5">Address / Location</p>
                    <p id="uqm-address" class="text-sm text-gray-800 dark:text-gray-200"></p>
                </div>
                <!-- Project -->
                <div id="uqm-project-row" class="hidden">
                    <p class="text-xs text-gray-500 dark:text-gray-400 mb-0.5">Project</p>
                    <p id="uqm-project" class="text-sm text-gray-800 dark:text-gray-200"></p>
                </div>
                <!-- Modem -->
                <div id="uqm-modem-row" class="hidden">
                    <p class="text-xs text-gray-500 dark:text-gray-400 mb-0.5">Deployed With Modem</p>
                    <p id="uqm-modem" class="text-sm text-gray-800 dark:text-gray-200"></p>
                </div>
                <!-- Last seen -->
                <div id="uqm-lastseen-row" class="hidden">
                    <p class="text-xs text-gray-500 dark:text-gray-400 mb-0.5">Last Seen</p>
                    <p id="uqm-lastseen" class="text-sm text-gray-800 dark:text-gray-200"></p>
                </div>
                <!-- Note -->
                <div id="uqm-note-row" class="hidden">
                    <p class="text-xs text-gray-500 dark:text-gray-400 mb-0.5">Note</p>
                    <p id="uqm-note" class="text-sm text-gray-800 dark:text-gray-200 italic"></p>
                </div>
                <!-- Reservations -->
                <div id="uqm-reservations-row" class="hidden">
                    <p class="text-xs text-gray-500 dark:text-gray-400 mb-1">Upcoming Jobs</p>
                    <div id="uqm-reservations" class="flex flex-col gap-1"></div>
                </div>
            </div>
        </div>
    </div>
</div>

<!-- Day Detail Slide Panel -->
<div id="panel-backdrop" class="panel-backdrop" onclick="closeDayPanel()"></div>
<div id="day-panel" class="slide-panel">
    <div class="p-6" id="day-panel-content">
        <!-- Content loaded via HTMX -->
    </div>
</div>

<!-- Reservation Modal -->
<!-- Promote to Project Modal -->
<div id="promote-modal" class="fixed inset-0 z-50 hidden">
    <div class="fixed inset-0 bg-black/50" onclick="closePromoteModal()"></div>
    <div class="fixed inset-0 flex items-center justify-center p-4">
        <div class="bg-white dark:bg-slate-800 rounded-xl shadow-2xl max-w-md w-full" onclick="event.stopPropagation()">
            <div class="p-6">
                <h2 class="text-lg font-semibold text-gray-900 dark:text-white mb-1">Promote to Project</h2>
                <p id="promote-modal-subtitle" class="text-sm text-gray-500 dark:text-gray-400 mb-4"></p>
                <div class="flex flex-col gap-3">
                    <div>
                        <label class="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Project Number <span class="text-gray-400">(optional)</span></label>
                        <input id="promote-project-number" type="text" placeholder="e.g. 2567-26"
                               class="w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white text-sm focus:ring-2 focus:ring-blue-500">
                    </div>
                    <div>
                        <label class="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Client Name <span class="text-gray-400">(optional)</span></label>
                        <input id="promote-client-name" type="text" placeholder="e.g. PJ Dick"
                               class="w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white text-sm focus:ring-2 focus:ring-blue-500">
                    </div>
                </div>
                <p class="text-xs text-gray-400 dark:text-gray-500 mt-3">This will create a new project with monitoring locations and unit assignments matching this reservation.</p>
                <div id="promote-error" class="hidden mt-3 p-2 bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 text-sm rounded"></div>
                <div class="flex gap-3 mt-5">
                    <button onclick="closePromoteModal()" class="flex-1 px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 text-sm text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-slate-700">Cancel</button>
                    <button onclick="confirmPromote()" class="flex-1 px-4 py-2 rounded-lg bg-emerald-600 hover:bg-emerald-700 text-white text-sm font-medium">Create Project</button>
                </div>
            </div>
        </div>
    </div>
</div>

<div id="reservation-modal" class="fixed inset-0 z-50 hidden">
    <div class="fixed inset-0 bg-black/50" onclick="closeReservationModal()"></div>
    <div class="fixed inset-0 flex items-center justify-center p-4">
        <div class="bg-white dark:bg-slate-800 rounded-xl shadow-2xl max-w-lg w-full max-h-[90vh] overflow-y-auto" onclick="event.stopPropagation()">
            <div class="p-6">
                <div class="flex items-center justify-between mb-6">
                    <h2 class="text-xl font-semibold text-gray-900 dark:text-white" id="modal-title">New Reservation</h2>
                    <button onclick="closeReservationModal()" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
                        <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
                        </svg>
                    </button>
                </div>
                <input type="hidden" id="editing-reservation-id" value="">

                <form id="reservation-form" onsubmit="submitReservation(event)">
                    <!-- Name -->
                    <div class="mb-4">
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Reservation Name *</label>
                        <input type="text" name="name" required
                               placeholder="e.g., Job A - March Deployment"
                               class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500">
                    </div>

                    <!-- Project (optional) -->
                    <div class="mb-4">
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Link to Project (optional)</label>
                        <select name="project_id"
                                class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500">
                            <option value="">-- No project --</option>
                            {% for project in projects %}
                            <option value="{{ project.id }}">{{ project.name }}</option>
                            {% endfor %}
                        </select>
                    </div>

                    <!-- Date Range -->
                    <div class="mb-4">
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Start Date *</label>
                        <input type="date" name="start_date" required
                               class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500">
                    </div>

                    <div class="mb-4">
                        <div class="flex items-center justify-between mb-1">
                            <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">End Date</label>
                            <label class="flex items-center gap-2 cursor-pointer text-sm">
                                <input type="checkbox" name="end_date_tbd" id="end_date_tbd"
                                       onchange="toggleEndDateTBD()"
                                       class="w-4 h-4 text-blue-600 focus:ring-blue-500 rounded border-gray-300 dark:border-gray-600">
                                <span class="text-gray-600 dark:text-gray-400">TBD / Ongoing</span>
                            </label>
                        </div>
                        <input type="date" name="end_date" id="end_date_input"
                               class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500">
                    </div>

                    <!-- Estimated End Date (shown when TBD is checked) -->
                    <div id="estimated-end-section" class="mb-4 hidden">
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
                            Estimated End Date <span class="text-gray-400 font-normal">(for planning)</span>
                        </label>
                        <input type="date" name="estimated_end_date"
                               class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500">
                        <p class="text-xs text-gray-500 mt-1">Used for calendar visualization only. Can be updated later.</p>
                    </div>

                    <!-- Assignment Type -->
                    <div class="mb-4">
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Assignment Type *</label>
                        <div class="flex gap-4">
                            <label class="flex items-center gap-2 cursor-pointer">
                                <input type="radio" name="assignment_type" value="quantity" checked
                                       onchange="toggleAssignmentType(this.value)"
                                       class="text-blue-600 focus:ring-blue-500">
                                <span class="text-gray-700 dark:text-gray-300">Reserve quantity</span>
                            </label>
                            <label class="flex items-center gap-2 cursor-pointer">
                                <input type="radio" name="assignment_type" value="specific"
                                       onchange="toggleAssignmentType(this.value)"
                                       class="text-blue-600 focus:ring-blue-500">
                                <span class="text-gray-700 dark:text-gray-300">Pick specific units</span>
                            </label>
                        </div>
                    </div>

                    <!-- Quantity (shown for quantity type) -->
                    <div id="quantity-section" class="mb-4">
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Units Needed</label>
                        <input type="number" name="quantity_needed" min="1" value="1"
                               onchange="updateCalendarAvailability()"
                               oninput="updateCalendarAvailability()"
                               class="w-32 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500">
                        <p class="text-xs text-gray-500 mt-1">Calendar will highlight availability based on quantity</p>
                    </div>

                    <!-- Specific Units (shown for specific type) -->
                    <div id="specific-section" class="mb-4 hidden">
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Select Units</label>
                        <div id="available-units-list" class="max-h-48 overflow-y-auto border border-gray-300 dark:border-gray-600 rounded-lg p-2">
                            <p class="text-gray-500 text-sm">Select dates first to see available units</p>
                        </div>
                    </div>

                    <!-- Color -->
                    <div class="mb-4">
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Color</label>
                        <div class="flex gap-2">
                            {% for color in ['#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6', '#EC4899'] %}
                            <label class="cursor-pointer">
                                <input type="radio" name="color" value="{{ color }}" {% if loop.first %}checked{% endif %} class="sr-only peer">
                                <span class="block w-8 h-8 rounded-full peer-checked:ring-2 peer-checked:ring-offset-2 peer-checked:ring-gray-900 dark:peer-checked:ring-white"
                                      style="background-color: {{ color }}"></span>
                            </label>
                            {% endfor %}
                        </div>
                    </div>

                    <!-- Notes -->
                    <div class="mb-6">
                        <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Notes</label>
                        <textarea name="notes" rows="2"
                                  placeholder="Optional notes about this reservation"
                                  class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500"></textarea>
                    </div>

                    <!-- Actions -->
                    <div class="flex justify-end gap-3">
                        <button type="button" onclick="closeReservationModal()"
                                class="px-4 py-2 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg">
                            Cancel
                        </button>
                        <button type="submit" id="modal-submit-btn"
                                class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
                            Create Reservation
                        </button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

<script>
const deviceType = '{{ device_type }}';
const startYear = {{ start_year }};
const startMonth = {{ start_month }};

// Cal expire dot toggle — off by default
let calExpireDotsVisible = false;
function toggleCalExpireDots() {
    calExpireDotsVisible = !calExpireDotsVisible;
    document.getElementById('view-calendar').classList.toggle('cal-expire-dots-hidden', !calExpireDotsVisible);
    const btn = document.getElementById('cal-expire-toggle');
    if (calExpireDotsVisible) {
        btn.classList.remove('text-gray-400', 'dark:text-gray-500');
        btn.classList.add('text-gray-700', 'dark:text-gray-200', 'bg-gray-100', 'dark:bg-gray-700');
    } else {
        btn.classList.add('text-gray-400', 'dark:text-gray-500');
        btn.classList.remove('text-gray-700', 'dark:text-gray-200', 'bg-gray-100', 'dark:bg-gray-700');
    }
}

// Load today's stats
document.addEventListener('DOMContentLoaded', function() {
    loadTodayStats();
    // Apply hidden class immediately (dots off by default)
    document.getElementById('view-calendar').classList.add('cal-expire-dots-hidden');
    // Load fleet summary for right panel
    loadFleetSummary();
    // Restore active tab from hash
    if (window.location.hash === '#cal') {
        switchTab('calendar');
        // Scroll today's month into view
        const todayCell = document.querySelector('.day-cell.today');
        if (todayCell) {
            const monthCard = todayCell.closest('.month-card');
            if (monthCard) monthCard.scrollIntoView({ behavior: 'smooth', block: 'start' });
        }
    }
});

async function loadTodayStats() {
    try {
        const today = new Date().toISOString().split('T')[0];
        const response = await fetch(`/api/fleet-calendar/day/${today}?device_type=${deviceType}`);
        // Just update the stats from the day data response headers or a separate call
        // For now, calculate from calendar data
        const todayData = findTodayInCalendar();
        if (todayData) {
            document.getElementById('available-today').textContent = todayData.available;
            document.getElementById('reserved-today').textContent = todayData.reserved;
            document.getElementById('expiring-today').textContent = todayData.expiring_soon;
            document.getElementById('expired-today').textContent = todayData.expired;
        }
    } catch (error) {
        console.error('Error loading today stats:', error);
    }
}

function findTodayInCalendar() {
    const today = new Date();
    const year = today.getFullYear();
    const month = today.getMonth() + 1;
    const day = today.getDate();
    const calendarData = {{ calendar_data | tojson }};
    // months is now an array, find the matching month
    const monthData = calendarData.months.find(m => m.year === year && m.month === month);
    if (monthData && monthData.days[day]) {
        return monthData.days[day];
    }
    return null;
}

function openDayPanel(dateStr) {
    const panel = document.getElementById('day-panel');
    const backdrop = document.getElementById('panel-backdrop');
    const content = document.getElementById('day-panel-content');

    // Load content via HTMX
    htmx.ajax('GET', `/api/fleet-calendar/day/${dateStr}?device_type=${deviceType}`, {
        target: '#day-panel-content',
        swap: 'innerHTML'
    });

    panel.classList.add('open');
    backdrop.classList.add('open');
}

function closeDayPanel() {
    const panel = document.getElementById('day-panel');
    const backdrop = document.getElementById('panel-backdrop');
    panel.classList.remove('open');
    backdrop.classList.remove('open');
}

let reservationModeActive = false;

function openReservationModal() {
    // Reset to "create" mode
    document.getElementById('modal-title').textContent = 'New Reservation';
    document.getElementById('modal-submit-btn').textContent = 'Create Reservation';
    document.getElementById('editing-reservation-id').value = '';
    document.getElementById('reservation-form').reset();

    document.getElementById('reservation-modal').classList.remove('hidden');
    reservationModeActive = true;
    document.getElementById('main-legend').classList.add('md:hidden');
    document.getElementById('main-legend').classList.remove('md:flex');
    document.getElementById('reservation-legend').classList.remove('md:hidden');
    document.getElementById('reservation-legend').classList.add('md:flex');
    updateCalendarAvailability();
}

function toggleResMenu(id) {
    const menu = document.getElementById('res-menu-' + id);
    if (!menu) return;
    const isHidden = menu.classList.contains('hidden');
    // Close all other open menus first
    document.querySelectorAll('[id^="res-menu-"]').forEach(m => m.classList.add('hidden'));
    if (isHidden) menu.classList.remove('hidden');
}

// Close any open res menu when clicking elsewhere
document.addEventListener('click', () => {
    document.querySelectorAll('[id^="res-menu-"]').forEach(m => m.classList.add('hidden'));
});

function toggleResCard(id) {
    const detail = document.getElementById('res-detail-' + id);
    const chevron = document.getElementById('chevron-' + id);
    if (!detail) return;
    const isHidden = detail.classList.contains('hidden');
    if (isHidden) {
        detail.classList.remove('hidden');
        detail.style.display = 'block';
    } else {
        detail.classList.add('hidden');
        detail.style.display = 'none';
    }
    if (chevron) chevron.style.transform = isHidden ? 'rotate(180deg)' : '';
}


async function deleteReservation(id, name) {
    if (!confirm(`Delete reservation "${name}"?\n\nThis will remove all unit assignments.`)) return;
    try {
        const response = await fetch(`/api/fleet-calendar/reservations/${id}`, { method: 'DELETE' });
        if (response.ok) {
            htmx.trigger('#planner-reservations-list', 'load');
        } else {
            const data = await response.json();
            showPlannerToast('Error: ' + (data.detail || 'Failed to delete'), true);
        }
    } catch (error) {
        console.error('Error:', error);
        showPlannerToast('Error deleting reservation', true);
    }
}

async function editReservation(id) {
    try {
        const response = await fetch(`/api/fleet-calendar/reservations/${id}`);
        if (!response.ok) { showPlannerToast('Failed to load reservation', true); return; }
        const res = await response.json();

        // Switch modal to "edit" mode
        document.getElementById('modal-title').textContent = 'Edit Reservation';
        document.getElementById('modal-submit-btn').textContent = 'Save Changes';
        document.getElementById('editing-reservation-id').value = id;

        // Populate fields
        const form = document.getElementById('reservation-form');
        form.querySelector('input[name="name"]').value = res.name;
        form.querySelector('select[name="project_id"]').value = res.project_id || '';
        form.querySelector('input[name="start_date"]').value = res.start_date;
        form.querySelector('textarea[name="notes"]').value = res.notes || '';

        // Color radio
        const colorRadio = form.querySelector(`input[name="color"][value="${res.color}"]`);
        if (colorRadio) colorRadio.checked = true;

        // Assignment type
        const atRadio = form.querySelector(`input[name="assignment_type"][value="${res.assignment_type}"]`);
        if (atRadio) { atRadio.checked = true; toggleAssignmentType(res.assignment_type); }
        if (res.assignment_type === 'quantity') {
            form.querySelector('input[name="quantity_needed"]').value = res.quantity_needed || 1;
        }

        // End date / TBD
        const tbdCheckbox = document.getElementById('end_date_tbd');
        if (res.end_date_tbd) {
            tbdCheckbox.checked = true;
            form.querySelector('input[name="estimated_end_date"]').value = res.estimated_end_date || '';
        } else {
            tbdCheckbox.checked = false;
            document.getElementById('end_date_input').value = res.end_date || '';
        }
        toggleEndDateTBD();

        document.getElementById('reservation-modal').classList.remove('hidden');
        reservationModeActive = true;
        document.getElementById('main-legend').classList.add('md:hidden');
        document.getElementById('main-legend').classList.remove('md:flex');
        document.getElementById('reservation-legend').classList.remove('md:hidden');
        document.getElementById('reservation-legend').classList.add('md:flex');
        updateCalendarAvailability();
    } catch (error) {
        console.error('Error loading reservation:', error);
        showPlannerToast('Error loading reservation', true);
    }
}

// ---- Promote to Project ----
let promoteReservationId = null;

function openPromoteModal(id, name) {
    promoteReservationId = id;
    document.getElementById('promote-modal-subtitle').textContent = `"${name}" will become a tracked project.`;
    document.getElementById('promote-project-number').value = '';
    document.getElementById('promote-client-name').value = '';
    document.getElementById('promote-error').classList.add('hidden');
    document.getElementById('promote-modal').classList.remove('hidden');
}

function closePromoteModal() {
    document.getElementById('promote-modal').classList.add('hidden');
    promoteReservationId = null;
}

async function confirmPromote() {
    if (!promoteReservationId) return;
    const btn = document.querySelector('#promote-modal button[onclick="confirmPromote()"]');
    const errEl = document.getElementById('promote-error');
    btn.textContent = 'Creating…';
    btn.disabled = true;
    errEl.classList.add('hidden');
    try {
        const resp = await fetch(`/api/fleet-calendar/reservations/${promoteReservationId}/promote-to-project`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                project_number: document.getElementById('promote-project-number').value.trim() || null,
                client_name: document.getElementById('promote-client-name').value.trim() || null,
            })
        });
        const result = await resp.json();
        if (!resp.ok) throw new Error(result.detail || 'Failed to promote');
        closePromoteModal();
        showPlannerToast('Project created! Redirecting…');
        const listUrl = document.getElementById('planner-reservations-list')?.getAttribute('hx-get');
        if (listUrl) htmx.ajax('GET', listUrl, {target: '#planner-reservations-list', swap: 'innerHTML'});
        setTimeout(() => { window.location.href = `/projects/${result.project_id}`; }, 1200);
    } catch (e) {
        errEl.textContent = e.message;
        errEl.classList.remove('hidden');
        btn.textContent = 'Create Project';
        btn.disabled = false;
    }
}

function closeReservationModal() {
    document.getElementById('reservation-modal').classList.add('hidden');
    document.getElementById('reservation-form').reset();
    document.getElementById('editing-reservation-id').value = '';
    reservationModeActive = false;
    document.getElementById('main-legend').classList.remove('md:hidden');
    document.getElementById('main-legend').classList.add('md:flex');
    document.getElementById('reservation-legend').classList.add('md:hidden');
    document.getElementById('reservation-legend').classList.remove('md:flex');
    resetCalendarColors();
}

function resetCalendarColors() {
    document.querySelectorAll('.day-cell:not(.empty)').forEach(cell => {
        cell.classList.remove('reservation-available', 'reservation-partial', 'reservation-unavailable');
        cell.classList.add('neutral');
    });
}

function updateCalendarAvailability() {
    if (!reservationModeActive) return;

    const quantityInput = document.querySelector('input[name="quantity_needed"]');
    const quantity = parseInt(quantityInput?.value) || 1;

    // Update each day cell based on available units vs quantity needed
    document.querySelectorAll('.day-cell:not(.empty)').forEach(cell => {
        const available = parseInt(cell.dataset.available) || 0;

        // Remove all status classes
        cell.classList.remove('neutral', 'reservation-available', 'reservation-partial', 'reservation-unavailable');

        if (available >= quantity) {
            cell.classList.add('reservation-available');
        } else if (available > 0) {
            cell.classList.add('reservation-partial');
        } else {
            cell.classList.add('reservation-unavailable');
        }
    });
}

function toggleEndDateTBD() {
    const checkbox = document.getElementById('end_date_tbd');
    const endDateInput = document.getElementById('end_date_input');
    const estimatedSection = document.getElementById('estimated-end-section');

    if (checkbox.checked) {
        endDateInput.disabled = true;
        endDateInput.value = '';
        endDateInput.classList.add('opacity-50', 'cursor-not-allowed');
        estimatedSection.classList.remove('hidden');
    } else {
        endDateInput.disabled = false;
        endDateInput.classList.remove('opacity-50', 'cursor-not-allowed');
        estimatedSection.classList.add('hidden');
    }
}

function toggleAssignmentType(type) {
    const quantitySection = document.getElementById('quantity-section');
    const specificSection = document.getElementById('specific-section');

    if (type === 'quantity') {
        quantitySection.classList.remove('hidden');
        specificSection.classList.add('hidden');
    } else {
        quantitySection.classList.add('hidden');
        specificSection.classList.remove('hidden');
        // Load available units based on selected dates
        loadAvailableUnits();
    }
}

async function loadAvailableUnits() {
    const startDate = document.querySelector('input[name="start_date"]').value;
    const endDate = document.querySelector('input[name="end_date"]').value;

    if (!startDate || !endDate) {
        document.getElementById('available-units-list').innerHTML =
            '<p class="text-gray-500 text-sm">Select dates first to see available units</p>';
        return;
    }

    try {
        const response = await fetch(
            `/api/fleet-calendar/availability?start_date=${startDate}&end_date=${endDate}&device_type=${deviceType}`
        );
        const data = await response.json();

        if (data.available_units.length === 0) {
            document.getElementById('available-units-list').innerHTML =
                '<p class="text-gray-500 text-sm">No units available for this period</p>';
            return;
        }

        let html = '';
        for (const unit of data.available_units) {
            html += `
                <label class="flex items-center gap-2 p-2 hover:bg-gray-50 dark:hover:bg-gray-700 rounded cursor-pointer">
                    <input type="checkbox" name="unit_ids" value="${unit.id}"
                           class="text-blue-600 focus:ring-blue-500 rounded">
                    <span class="text-gray-900 dark:text-white font-medium">${unit.id}</span>
                    <span class="text-gray-500 text-sm">Cal: ${unit.last_calibrated || 'N/A'}</span>
                    ${unit.calibration_status === 'expiring_soon' ?
                        '<span class="text-yellow-600 text-xs">Expiring soon</span>' : ''}
                </label>
            `;
        }
        document.getElementById('available-units-list').innerHTML = html;
    } catch (error) {
        console.error('Error loading available units:', error);
    }
}

// Watch for date changes to reload available units
document.addEventListener('DOMContentLoaded', function() {
    const startInput = document.querySelector('input[name="start_date"]');
    const endInput = document.querySelector('input[name="end_date"]');

    if (startInput && endInput) {
        startInput.addEventListener('change', function() {
            if (document.querySelector('input[name="assignment_type"]:checked').value === 'specific') {
                loadAvailableUnits();
            }
        });
        endInput.addEventListener('change', function() {
            if (document.querySelector('input[name="assignment_type"]:checked').value === 'specific') {
                loadAvailableUnits();
            }
        });
    }
});

async function submitReservation(event) {
    event.preventDefault();

    const form = event.target;
    const formData = new FormData(form);
    const endDateTbd = formData.get('end_date_tbd') === 'on';
    const editingId = document.getElementById('editing-reservation-id').value;

    const data = {
        name: formData.get('name'),
        project_id: formData.get('project_id') || null,
        start_date: formData.get('start_date'),
        end_date: endDateTbd ? null : formData.get('end_date'),
        end_date_tbd: endDateTbd,
        estimated_end_date: endDateTbd ? (formData.get('estimated_end_date') || null) : null,
        assignment_type: formData.get('assignment_type'),
        device_type: deviceType,
        color: formData.get('color'),
        notes: formData.get('notes') || null
    };

    if (!data.end_date && !data.end_date_tbd) {
        showPlannerToast('Please enter an end date or check "TBD / Ongoing"');
        return;
    }

    if (data.assignment_type === 'quantity') {
        data.quantity_needed = parseInt(formData.get('quantity_needed'));
    } else {
        data.unit_ids = formData.getAll('unit_ids');
    }

    const isEdit = editingId !== '';
    const url = isEdit
        ? `/api/fleet-calendar/reservations/${editingId}`
        : '/api/fleet-calendar/reservations';
    const method = isEdit ? 'PUT' : 'POST';

    try {
        const response = await fetch(url, {
            method,
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(data)
        });

        const result = await response.json();

        if (result.success) {
            closeReservationModal();
            window.location.reload();
        } else {
            showPlannerToast(`Error ${isEdit ? 'saving' : 'creating'} reservation: ` + (result.detail || 'Unknown error'));
        }
    } catch (error) {
        console.error('Error:', error);
        showPlannerToast(`Error ${isEdit ? 'saving' : 'creating'} reservation`);
    }
}

// Close panels on escape key
document.addEventListener('keydown', function(e) {
    if (e.key === 'Escape') {
        closeDayPanel();
        closeReservationModal();
    }
});

// ============================================================
// Panel switching
// ============================================================
function switchPlannerTab(tab) {
    const isAssign = tab === 'assign';
    document.getElementById('ptab-list').classList.toggle('hidden', isAssign);
    document.getElementById('ptab-assign').classList.toggle('hidden', !isAssign);
    showRightPanel(isAssign ? 'available' : 'summary');
}

function switchDeviceType(dt) {
    const hash = document.getElementById('view-calendar').classList.contains('hidden') ? '' : '#cal';
    window.location.href = `/fleet-calendar?year={{ start_year }}&month={{ start_month }}&device_type=${dt}${hash}`;
}

function switchTab(tab) {
    const isCalendar = tab === 'calendar';
    document.getElementById('view-calendar').classList.toggle('hidden', !isCalendar);
    document.getElementById('view-planner').classList.toggle('hidden', isCalendar);

    ['planner', 'calendar'].forEach(t => {
        const btn = document.getElementById(`tab-btn-${t}`);
        const active = t === tab;
        btn.classList.toggle('bg-white', active);
        btn.classList.toggle('dark:bg-slate-600', active);
        btn.classList.toggle('text-gray-900', active);
        btn.classList.toggle('dark:text-white', active);
        btn.classList.toggle('shadow', active);
        btn.classList.toggle('text-gray-600', !active);
        btn.classList.toggle('dark:text-gray-300', !active);
        btn.classList.toggle('hover:text-gray-900', !active);
        btn.classList.toggle('dark:hover:text-white', !active);
    });

    if (isCalendar) {
        htmx.trigger(document.body, 'calendar-reservations-refresh');
        // Scroll today into view
        requestAnimationFrame(() => {
            const todayCell = document.querySelector('.day-cell.today');
            if (todayCell) {
                const monthCard = todayCell.closest('.month-card');
                if (monthCard) monthCard.scrollIntoView({ behavior: 'smooth', block: 'start' });
            }
        });
    }
    history.replaceState(null, '', isCalendar ? '#cal' : window.location.pathname + window.location.search);
}

// Job layer toggles
let confirmedVisible = true;
let plannedVisible = true;

function toggleJobLayer(layer) {
    if (layer === 'confirmed') {
        confirmedVisible = !confirmedVisible;
        document.getElementById('view-calendar').classList.toggle('hide-confirmed', !confirmedVisible);
        const btn = document.getElementById('toggle-confirmed');
        btn.classList.toggle('text-gray-700', confirmedVisible);
        btn.classList.toggle('dark:text-gray-200', confirmedVisible);
        btn.classList.toggle('bg-gray-100', confirmedVisible);
        btn.classList.toggle('dark:bg-gray-700', confirmedVisible);
        btn.classList.toggle('text-gray-400', !confirmedVisible);
        btn.classList.toggle('dark:text-gray-500', !confirmedVisible);
    } else {
        plannedVisible = !plannedVisible;
        document.getElementById('view-calendar').classList.toggle('hide-planned', !plannedVisible);
        const btn = document.getElementById('toggle-planned');
        btn.classList.toggle('text-gray-700', plannedVisible);
        btn.classList.toggle('dark:text-gray-200', plannedVisible);
        btn.classList.toggle('bg-gray-100', plannedVisible);
        btn.classList.toggle('dark:bg-gray-700', plannedVisible);
        btn.classList.toggle('text-gray-400', !plannedVisible);
        btn.classList.toggle('dark:text-gray-500', !plannedVisible);
    }
}

// ============================================================
// Reservation Planner
// ============================================================
let plannerSelectedSlotIdx = null;

let plannerState = {
    reservation_id: null,   // null = creating new
    slots: [],              // array of {unit_id: string|null, power_type: string|null, notes: string|null, location_name: string|null}
    allUnits: []            // full list from server
};
let dragSrcIdx = null;

// ---- Color picker ----
const PLANNER_PALETTE = [
    '#3B82F6','#2563EB','#06B6D4','#0D9488',
    '#10B981','#84CC16','#EAB308','#F97316',
    '#EF4444','#DC2626','#EC4899','#A855F7',
    '#8B5CF6','#6366F1','#78716C','#94A3B8',
    '#F59E0B','#14B8A6'
];

// Colors in use by existing reservations (populated from Jinja at page load)
const EXISTING_JOB_COLORS = {{ calendar_projects | map(attribute='color') | list | tojson }};

function plannerPickSmartColor() {
    // Parse hex to HSL, pick the palette color that maximizes minimum hue distance from existing colors
    function hexToHsl(hex) {
        const r = parseInt(hex.slice(1,3),16)/255, g = parseInt(hex.slice(3,5),16)/255, b = parseInt(hex.slice(5,7),16)/255;
        const max = Math.max(r,g,b), min = Math.min(r,g,b), l = (max+min)/2;
        if (max === min) return [0, 0, l];
        const d = max - min, s = l > 0.5 ? d/(2-max-min) : d/(max+min);
        let h;
        if (max === r) h = ((g-b)/d + (g<b?6:0))/6;
        else if (max === g) h = ((b-r)/d + 2)/6;
        else h = ((r-g)/d + 4)/6;
        return [h*360, s, l];
    }
    function hueDist(a, b) { const d = Math.abs(a-b); return Math.min(d, 360-d); }

    const usedHues = EXISTING_JOB_COLORS
        .filter(c => c && c.startsWith('#'))
        .map(c => hexToHsl(c)[0]);

    // Also exclude the current planner color if editing
    const cur = document.getElementById('planner-color-value')?.value;
    if (cur && plannerState.reservation_id) usedHues.push(hexToHsl(cur)[0]);

    let best = PLANNER_PALETTE[0], bestDist = -1;
    for (const c of PLANNER_PALETTE) {
        const h = hexToHsl(c)[0];
        const minDist = usedHues.length ? Math.min(...usedHues.map(uh => hueDist(h, uh))) : 999;
        if (minDist > bestDist) { bestDist = minDist; best = c; }
    }
    return best;
}

function plannerRenderColorSwatches(selected) {
    const container = document.getElementById('planner-color-swatches');
    if (!container) return;
    container.innerHTML = PLANNER_PALETTE.map(c => {
        const active = c.toLowerCase() === (selected||'').toLowerCase();
        return `<button type="button" onclick="plannerSetColor('${c}', true)"
                    title="${c}"
                    class="w-6 h-6 rounded-full border-2 transition-all ${active ? 'border-gray-900 dark:border-white scale-125' : 'border-transparent hover:scale-110'}"
                    style="background-color:${c}"></button>`;
    }).join('');
}

function plannerSetColor(hex, fromSwatch) {
    document.getElementById('planner-color-value').value = hex;
    document.getElementById('planner-color-wheel').value = hex.length === 7 ? hex : '#3B82F6';
    plannerRenderColorSwatches(hex);
    const dot = document.getElementById('planner-meta-color-dot');
    if (dot) dot.style.background = hex;
}

function togglePlannerMeta() {
    const body = document.getElementById('planner-meta-body');
    const chevron = document.getElementById('planner-meta-chevron');
    const open = !body.classList.contains('hidden');
    body.classList.toggle('hidden', open);
    chevron.style.transform = open ? '' : 'rotate(180deg)';
}

function plannerSetMetaOpen(open) {
    const body = document.getElementById('planner-meta-body');
    const chevron = document.getElementById('planner-meta-chevron');
    body.classList.toggle('hidden', !open);
    chevron.style.transform = open ? 'rotate(180deg)' : '';
}

function plannerUpdateMetaSummary() {
    const name = document.getElementById('planner-name')?.value.trim();
    const start = document.getElementById('planner-start')?.value;
    const end = document.getElementById('planner-end')?.value;
    const summary = document.getElementById('planner-meta-summary');
    if (!summary) return;
    let text = name || 'New Job';
    if (start) {
        const s = new Date(start + 'T00:00:00').toLocaleDateString('en-US', {month:'short', day:'numeric'});
        const e = end ? new Date(end + 'T00:00:00').toLocaleDateString('en-US', {month:'short', day:'numeric'}) : '…';
        text += `  ·  ${s} – ${e}`;
    }
    summary.textContent = text;
}

// Initialize swatches and meta summary on page load
document.addEventListener('DOMContentLoaded', () => {
    plannerRenderColorSwatches('#3B82F6');
    plannerUpdateMetaSummary();
});

function plannerDatesChanged() {
    plannerLoadUnits();
}

function plannerDeviceTypeChanged() {
    plannerDatesChanged();
    loadFleetSummary();
}

async function plannerLoadUnits() {
    const start = document.getElementById('planner-start').value;
    const end = document.getElementById('planner-end').value;
    const excludeId = plannerState.reservation_id || '';
    const plannerDeviceType = document.querySelector('input[name="planner_device_type"]:checked')?.value || 'seismograph';

    let url = `/api/fleet-calendar/planner-availability?device_type=${plannerDeviceType}`;
    if (start && end && end >= start) {
        url += `&start_date=${start}&end_date=${end}`;
    }
    if (excludeId) url += `&exclude_reservation_id=${excludeId}`;

    // Show loading state
    const placeholder = document.getElementById('planner-units-placeholder');
    if (placeholder) { placeholder.classList.remove('hidden'); placeholder.textContent = 'Loading units...'; }
    document.getElementById('planner-units-list')?.querySelectorAll('.planner-unit-row').forEach(el => el.remove());

    try {
        const resp = await fetch(url);
        const data = await resp.json();
        plannerState.allUnits = data.units || [];
        const hasDates = start && end;
        document.getElementById('planner-avail-count').textContent =
            hasDates ? `(${plannerState.allUnits.length} available for period)` : `(${plannerState.allUnits.length} total)`;
        plannerRenderUnits();
    } catch (e) {
        console.error('Planner load error', e);
    }
}

function plannerFilterUnits() {
    // Mutually exclusive checkboxes
    const deployedOnly = document.getElementById('planner-deployed-only');
    const benchedOnly = document.getElementById('planner-benched-only');
    if (event && event.target === deployedOnly && deployedOnly.checked) benchedOnly.checked = false;
    if (event && event.target === benchedOnly && benchedOnly.checked) deployedOnly.checked = false;
    plannerRenderUnits();
}

function plannerRenderUnits() {
    const search = document.getElementById('planner-search').value.toLowerCase();
    const deployedOnly = document.getElementById('planner-deployed-only').checked;
    const benchedOnly = document.getElementById('planner-benched-only').checked;
    const slottedIds = new Set(plannerState.slots.map(s => s.unit_id).filter(Boolean));
    const start = document.getElementById('planner-start').value;
    const end = document.getElementById('planner-end').value;

    let units = plannerState.allUnits.filter(u => {
        if (deployedOnly && !u.deployed) return false;
        if (benchedOnly && u.deployed) return false;
        if (search && !u.id.toLowerCase().includes(search)) return false;
        return true;
    });

    const placeholder = document.getElementById('planner-units-placeholder');
    const list = document.getElementById('planner-units-list');

    // Show/hide slot-selection hint banner
    let slotHint = document.getElementById('planner-slot-hint');
    if (!slotHint) {
        slotHint = document.createElement('div');
        slotHint.id = 'planner-slot-hint';
        list.parentNode.insertBefore(slotHint, list);
    }
    if (plannerSelectedSlotIdx !== null) {
        const slotNum = plannerSelectedSlotIdx + 1;
        slotHint.className = 'mb-2 px-3 py-2 rounded-lg bg-blue-50 dark:bg-blue-900/30 border border-blue-300 dark:border-blue-700 text-sm text-blue-700 dark:text-blue-300';
        slotHint.textContent = `Assigning to Loc. ${slotNum} — click a unit below`;
    } else {
        slotHint.className = 'hidden';
        slotHint.textContent = '';
    }

    if (plannerState.allUnits.length === 0) {
        placeholder.classList.remove('hidden');
        const start = document.getElementById('planner-start').value;
        const end = document.getElementById('planner-end').value;
        placeholder.textContent = (start && end) ? 'No units available for this period' : 'Set start and end dates to see available units';
        list.querySelectorAll('.planner-unit-row').forEach(el => el.remove());
        return;
    }
    placeholder.classList.add('hidden');
    list.querySelectorAll('.planner-unit-row').forEach(el => el.remove());

    if (units.length === 0) {
        const empty = document.createElement('p');
        empty.className = 'planner-unit-row text-sm text-gray-400 dark:text-gray-500 text-center py-8';
        empty.textContent = 'No units match your filter';
        list.appendChild(empty);
        return;
    }

    for (const unit of units) {
        const isSlotted = slottedIds.has(unit.id);
        const row = document.createElement('div');
        row.className = `planner-unit-row flex items-center justify-between px-3 py-2 rounded-lg border transition-colors ${
            isSlotted
                ? 'bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800 opacity-60 cursor-default'
                : 'border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700/50 cursor-pointer'
        }`;
        row.dataset.unitId = unit.id;
        if (!isSlotted) row.onclick = () => plannerAssignUnit(unit.id);

        const calDate = unit.last_calibrated
            ? new Date(unit.last_calibrated + 'T00:00:00').toLocaleDateString('en-US', {month:'short', day:'numeric', year:'numeric'})
            : 'No cal date';

        // Calibration expiry warning during deployment
        let expiryWarning = '';
        if (start && end && unit.expiry_date) {
            const expiry = new Date(unit.expiry_date + 'T00:00:00');
            const jobStart = new Date(start + 'T00:00:00');
            const jobEnd = new Date(end + 'T00:00:00');
            if (expiry >= jobStart && expiry <= jobEnd) {
                const expiryStr = expiry.toLocaleDateString('en-US', {month:'short', day:'numeric', year:'numeric'});
                expiryWarning = `<span class="text-xs px-1.5 py-0.5 bg-amber-50 dark:bg-amber-900/20 text-amber-600 dark:text-amber-400 rounded border border-amber-200 dark:border-amber-800" title="Will need swap during job">cal expires ${expiryStr}</span>`;
            }
        }

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

        row.innerHTML = `
            <div class="flex flex-col gap-0.5 min-w-0">
                <div class="flex items-center gap-2 flex-wrap">
                    <button onclick="event.stopPropagation(); openUnitQuickModal('${unit.id}')"
                            class="font-medium text-blue-600 dark:text-blue-400 hover:underline text-sm">${unit.id}</button>
                    ${deployedBadge}
                    ${expiryWarning}
                </div>
                <span class="text-xs text-gray-400 dark:text-gray-500">Cal: ${calDate}</span>
            </div>
            <div class="flex-shrink-0 ml-2">
                ${isSlotted
                    ? '<span class="text-xs text-blue-600 dark:text-blue-400 font-medium">Assigned</span>'
                    : '<button class="text-xs px-2 py-1 bg-blue-600 text-white rounded hover:bg-blue-700 whitespace-nowrap">Assign →</button>'
                }
            </div>
        `;
        list.appendChild(row);
    }
}

function openUnitDetailModal(unitId) {
    document.getElementById('unit-detail-modal-title').textContent = unitId;
    document.getElementById('unit-detail-iframe').src = `/unit/${unitId}?embed=1`;
    document.getElementById('unit-detail-modal').classList.remove('hidden');
}

function closeUnitDetailModal() {
    document.getElementById('unit-detail-modal').classList.add('hidden');
    document.getElementById('unit-detail-iframe').src = '';
}

async function openUnitQuickModal(unitId) {
    document.getElementById('unit-quick-modal').classList.remove('hidden');
    // Reset while loading
    document.getElementById('uqm-title').textContent = unitId;
    document.getElementById('uqm-deployed-badge').innerHTML = '';
    document.getElementById('uqm-outforcal-badge').innerHTML = '';
    document.getElementById('uqm-cal-date').textContent = '…';
    document.getElementById('uqm-cal-due').textContent = '…';
    ['uqm-address-row','uqm-project-row','uqm-modem-row','uqm-lastseen-row','uqm-note-row','uqm-reservations-row']
        .forEach(id => document.getElementById(id).classList.add('hidden'));

    try {
        const resp = await fetch(`/api/fleet-calendar/unit-quick-info/${unitId}`);
        if (!resp.ok) throw new Error('Not found');
        const u = await resp.json();
        const today = new Date(); today.setHours(0,0,0,0);

        // Deployed badge
        document.getElementById('uqm-deployed-badge').innerHTML = u.deployed
            ? '<span class="text-xs px-2 py-0.5 bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400 rounded-full">Deployed</span>'
            : '<span class="text-xs px-2 py-0.5 bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 rounded-full">Benched</span>';

        // Out for cal badge
        if (u.out_for_calibration) {
            document.getElementById('uqm-outforcal-badge').innerHTML =
                '<span class="text-xs px-2 py-0.5 bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-400 rounded-full">Out for Cal</span>';
        }

        // Cal date
        const calDateEl = document.getElementById('uqm-cal-date');
        calDateEl.textContent = u.last_calibrated
            ? new Date(u.last_calibrated + 'T00:00:00').toLocaleDateString('en-US', {month:'short', day:'numeric', year:'numeric'})
            : 'No record';
        calDateEl.className = `text-sm font-medium ${!u.last_calibrated ? 'text-red-500 dark:text-red-400' : 'text-gray-900 dark:text-white'}`;

        // Cal due
        const calDueEl = document.getElementById('uqm-cal-due');
        if (u.next_calibration_due) {
            const due = new Date(u.next_calibration_due + 'T00:00:00');
            const expired = due < today;
            calDueEl.textContent = due.toLocaleDateString('en-US', {month:'short', day:'numeric', year:'numeric'}) + (expired ? ' (expired)' : '');
            calDueEl.className = `text-sm font-medium ${expired ? 'text-red-500 dark:text-red-400' : 'text-green-600 dark:text-green-400'}`;
        } else {
            calDueEl.textContent = '—';
            calDueEl.className = 'text-sm font-medium text-red-500 dark:text-red-400';
        }

        // Address
        if (u.address) {
            document.getElementById('uqm-address-row').classList.remove('hidden');
            document.getElementById('uqm-address').textContent = u.address;
        }

        // Project
        if (u.project_id) {
            document.getElementById('uqm-project-row').classList.remove('hidden');
            document.getElementById('uqm-project').textContent = u.project_id;
        }

        // Modem
        if (u.deployed_with_modem_id) {
            document.getElementById('uqm-modem-row').classList.remove('hidden');
            document.getElementById('uqm-modem').textContent = u.deployed_with_modem_id;
        }

        // Last seen
        if (u.last_seen) {
            document.getElementById('uqm-lastseen-row').classList.remove('hidden');
            document.getElementById('uqm-lastseen').textContent =
                new Date(u.last_seen).toLocaleString('en-US', {month:'short', day:'numeric', year:'numeric', hour:'numeric', minute:'2-digit'});
        }

        // Note
        if (u.note) {
            document.getElementById('uqm-note-row').classList.remove('hidden');
            document.getElementById('uqm-note').textContent = u.note;
        }

        // Reservations
        if (u.reservations && u.reservations.length > 0) {
            document.getElementById('uqm-reservations-row').classList.remove('hidden');
            document.getElementById('uqm-reservations').innerHTML = u.reservations.map(r => {
                const s = r.start_date ? new Date(r.start_date + 'T00:00:00').toLocaleDateString('en-US', {month:'short', day:'numeric'}) : '';
                const e = r.end_date_tbd ? 'TBD' : (r.end_date ? new Date(r.end_date + 'T00:00:00').toLocaleDateString('en-US', {month:'short', day:'numeric'}) : 'TBD');
                const loc = r.location_name ? ` · ${r.location_name}` : '';
                return `<div class="flex items-center gap-2 text-sm">
                    <span class="w-2.5 h-2.5 rounded-full flex-shrink-0" style="background:${r.color}"></span>
                    <span class="font-medium text-gray-800 dark:text-gray-200">${r.name}</span>
                    <span class="text-gray-400 dark:text-gray-500">${s}–${e}${loc}</span>
                </div>`;
            }).join('');
        }

    } catch(e) {
        document.getElementById('uqm-cal-date').textContent = 'Error loading';
    }
}

function closeUnitQuickModal() {
    document.getElementById('unit-quick-modal').classList.add('hidden');
}

function plannerAddSlot() {
    plannerState.slots.push({ unit_id: null, power_type: null, notes: null, location_name: null });
    plannerRenderSlots();
}

// ============================================================
// Fleet Summary (right panel on jobs list)
// ============================================================
let summaryAllUnits = [];
let summaryActiveFilters = new Set(); // multi-select: 'deployed' | 'benched' | 'cal_expired' | 'cal_good' | 'out_for_cal' | 'reserved'

async function loadFleetSummary() {
    const plannerDeviceType = document.querySelector('input[name="planner_device_type"]:checked')?.value || 'seismograph';
    try {
        const resp = await fetch(`/api/fleet-calendar/planner-availability?device_type=${plannerDeviceType}`);
        const data = await resp.json();
        summaryAllUnits = data.units || [];
        summaryActiveFilters = new Set();
        renderFleetSummary();
    } catch(e) { console.error('Fleet summary load error', e); }
}

function summaryFilterUnits() {
    renderFleetSummary();
}

// Stat cards: set exactly this one filter (or clear all if already the only active one)
function summarySetFilter(f) {
    if (f === null) {
        summaryActiveFilters = new Set();
    } else if (summaryActiveFilters.size === 1 && summaryActiveFilters.has(f)) {
        summaryActiveFilters = new Set();
    } else {
        summaryActiveFilters = new Set([f]);
    }
    renderFleetSummary();
}

// Pills: toggle independently (multi-select)
function summaryToggleFilter(f) {
    if (summaryActiveFilters.has(f)) summaryActiveFilters.delete(f);
    else summaryActiveFilters.add(f);
    renderFleetSummary();
}

function renderFleetSummary() {
    const search = document.getElementById('summary-search')?.value.toLowerCase() || '';
    const today = new Date(); today.setHours(0,0,0,0);

    // Computed flags for each unit
    const withFlags = summaryAllUnits.map(u => {
        const expiry = u.expiry_date ? new Date(u.expiry_date + 'T00:00:00') : null;
        return {
            ...u,
            _calExpired: !u.last_calibrated || (expiry && expiry < today),
            _calGood:    u.last_calibrated && expiry && expiry >= today,
            _outForCal:  !!u.out_for_calibration,
            _allocated:  !!u.allocated,
            _reserved:   (u.reservations || []).length > 0,
        };
    });

    // Counts always against full list
    const counts = {
        total:       withFlags.length,
        deployed:    withFlags.filter(u => u.deployed).length,
        benched:     withFlags.filter(u => !u.deployed).length,
        cal_expired: withFlags.filter(u => u._calExpired).length,
        cal_good:    withFlags.filter(u => u._calGood).length,
        out_for_cal: withFlags.filter(u => u._outForCal).length,
        allocated:   withFlags.filter(u => u._allocated).length,
        reserved:    withFlags.filter(u => u._reserved).length,
    };

    const af = summaryActiveFilters;

    // Stat cards — single-shortcut behavior, highlighted when they're the sole active filter
    const cardActive = (f) => af.size === 1 && af.has(f);
    const card = (f, label, count, colorClass, ringColor) => {
        const isActive = f === null ? af.size === 0 : cardActive(f);
        return `<button onclick="summarySetFilter(${f === null ? 'null' : `'${f}'`})"
            class="rounded-lg p-3 text-left w-full cursor-pointer transition-all ring-2 ${isActive ? ringColor : 'ring-transparent'} ${colorClass}">
            <p class="text-2xl font-bold">${count}</p>
            <p class="text-xs opacity-80">${label}</p>
        </button>`;
    };

    document.getElementById('fleet-summary-stats').innerHTML = `
        <div class="grid grid-cols-2 sm:grid-cols-4 gap-2">
            ${card(null,        'Total',    counts.total,    'bg-gray-50 dark:bg-slate-700 text-gray-900 dark:text-white hover:bg-gray-100 dark:hover:bg-slate-600',              'ring-gray-400 dark:ring-gray-300')}
            ${card('deployed',  'Deployed', counts.deployed, 'bg-green-50 dark:bg-green-900/20 text-green-700 dark:text-green-400 hover:bg-green-100 dark:hover:bg-green-900/40', 'ring-green-500')}
            ${card('benched',   'Benched',  counts.benched,  'bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-400 hover:bg-blue-100 dark:hover:bg-blue-900/40',       'ring-blue-500')}
            ${card('cal_good',  'Cal Good', counts.cal_good, 'bg-teal-50 dark:bg-teal-900/20 text-teal-700 dark:text-teal-400 hover:bg-teal-100 dark:hover:bg-teal-900/40',       'ring-teal-500')}
        </div>
        <div class="flex flex-wrap gap-1.5 mt-2">
            ${summaryPill('cal_expired', 'Cal Expired', counts.cal_expired, af)}
            ${summaryPill('out_for_cal', 'Out for Cal', counts.out_for_cal, af)}
            ${summaryPill('allocated',   'Allocated',   counts.allocated,   af)}
            ${summaryPill('reserved',    'Reserved',    counts.reserved,    af)}
        </div>`;

    // Apply all active filters (AND logic) + search
    const filterFns = {
        deployed:    u => u.deployed,
        benched:     u => !u.deployed,
        cal_expired: u => u._calExpired,
        cal_good:    u => u._calGood,
        out_for_cal: u => u._outForCal,
        allocated:   u => u._allocated,
        reserved:    u => u._reserved,
    };
    let units = af.size === 0 ? withFlags : withFlags.filter(u => [...af].some(f => filterFns[f](u)));
    if (search) units = units.filter(u => u.id.toLowerCase().includes(search));

    const list = document.getElementById('fleet-summary-list');
    if (units.length === 0) {
        list.innerHTML = '<p class="text-sm text-gray-400 dark:text-gray-500 text-center py-8">No units match</p>';
        return;
    }

    list.innerHTML = units.map(u => {
        const calDate = u.last_calibrated
            ? new Date(u.last_calibrated + 'T00:00:00').toLocaleDateString('en-US', {month:'short', day:'numeric', year:'numeric'})
            : null;
        const expiryDate = u.expiry_date
            ? new Date(u.expiry_date + 'T00:00:00').toLocaleDateString('en-US', {month:'short', day:'numeric', year:'numeric'})
            : null;

        const deployedBadge = u.deployed
            ? '<span class="text-xs px-1.5 py-0.5 bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400 rounded">Deployed</span>'
            : '<span class="text-xs px-1.5 py-0.5 bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 rounded">Benched</span>';
        const outForCalBadge = u._outForCal
            ? '<span class="text-xs px-1.5 py-0.5 bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-400 rounded">Out for Cal</span>'
            : '';
        const allocatedBadge = u._allocated
            ? `<span class="text-xs px-1.5 py-0.5 bg-orange-100 dark:bg-orange-900/30 text-orange-700 dark:text-orange-400 rounded" title="${u.allocated_to_project_id ? 'For: ' + u.allocated_to_project_id : ''}">Allocated${u.allocated_to_project_id ? ': ' + u.allocated_to_project_id : ''}</span>`
            : '';
        let calBadge;
        if (!calDate) {
            calBadge = '<span class="text-xs px-1.5 py-0.5 bg-red-100 dark:bg-red-900/30 text-red-600 dark:text-red-400 rounded">Cal expired</span>';
        } else if (u._calExpired) {
            calBadge = `<span class="text-xs px-1.5 py-0.5 bg-red-100 dark:bg-red-900/30 text-red-600 dark:text-red-400 rounded">Cal expired ${expiryDate}</span>`;
        } else {
            calBadge = `<span class="text-xs text-gray-400 dark:text-gray-500">Cal: ${calDate} · exp. ${expiryDate}</span>`;
        }

        const resBadges = (u.reservations || []).map(r => {
            const s = r.start_date ? new Date(r.start_date + 'T00:00:00').toLocaleDateString('en-US', {month:'short', day:'numeric'}) : '';
            const e = r.end_date ? new Date(r.end_date + 'T00:00:00').toLocaleDateString('en-US', {month:'short', day:'numeric'}) : 'TBD';
            return `<span class="text-xs px-1.5 py-0.5 rounded font-medium" style="background-color:${r.color}22; color:${r.color}; border:1px solid ${r.color}66;">${r.reservation_name} ${s}–${e}</span>`;
        }).join('');

        return `
            <div class="flex flex-col gap-1 px-3 py-2 rounded-lg border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700/50">
                <div class="flex items-center gap-2 flex-wrap">
                    <button onclick="openUnitQuickModal('${u.id}')"
                            class="font-medium text-blue-600 dark:text-blue-400 hover:underline text-sm">${u.id}</button>
                    ${deployedBadge}${outForCalBadge}${allocatedBadge}${calBadge}
                </div>
                ${resBadges ? `<div class="flex flex-wrap gap-1">${resBadges}</div>` : ''}
            </div>`;
    }).join('');
}

function summaryPill(f, label, count, activeSet) {
    const isActive = activeSet.has(f);
    const pillColors = {
        cal_expired: isActive ? 'bg-red-600 text-white border-red-600'       : 'bg-transparent border-gray-300 dark:border-gray-600 text-gray-600 dark:text-gray-400 hover:border-red-500 hover:text-red-600 dark:hover:text-red-400',
        out_for_cal: isActive ? 'bg-purple-600 text-white border-purple-600' : 'bg-transparent border-gray-300 dark:border-gray-600 text-gray-600 dark:text-gray-400 hover:border-purple-500 hover:text-purple-600 dark:hover:text-purple-400',
        allocated:   isActive ? 'bg-orange-500 text-white border-orange-500' : 'bg-transparent border-gray-300 dark:border-gray-600 text-gray-600 dark:text-gray-400 hover:border-orange-500 hover:text-orange-600 dark:hover:text-orange-400',
        reserved:    isActive ? 'bg-indigo-600 text-white border-indigo-600' : 'bg-transparent border-gray-300 dark:border-gray-600 text-gray-600 dark:text-gray-400 hover:border-indigo-500 hover:text-indigo-600 dark:hover:text-indigo-400',
    };
    return `<button onclick="summaryToggleFilter('${f}')"
        class="text-xs px-2.5 py-1 rounded-full font-medium border transition-colors ${pillColors[f]}">
        ${label} <span class="${isActive ? 'opacity-80' : 'opacity-60'}">${count}</span>
    </button>`;
}

function showRightPanel(panel) {
    document.getElementById('right-fleet-summary').classList.toggle('hidden', panel !== 'summary');
    document.getElementById('right-available-units').classList.toggle('hidden', panel !== 'available');
}

function showPlannerToast(msg, isError = false) {
    let toast = document.getElementById('planner-toast');
    if (!toast) {
        toast = document.createElement('div');
        toast.id = 'planner-toast';
        toast.className = 'fixed bottom-6 left-1/2 -translate-x-1/2 z-50 px-5 py-2.5 rounded-lg shadow-lg text-white text-sm font-medium transition-opacity duration-300 pointer-events-none';
        document.body.appendChild(toast);
    }
    toast.style.background = isError ? '#ef4444' : '#22c55e';
    toast.textContent = (isError ? '✕ ' : '✓ ') + msg;
    toast.style.opacity = '1';
    clearTimeout(toast._hideTimer);
    toast._hideTimer = setTimeout(() => { toast.style.opacity = '0'; }, isError ? 4000 : 2500);
}

function plannerSyncSlotsToEstimate() {
    const target = parseInt(document.getElementById('planner-est-units').value);
    if (!target || target < 1) return;
    const current = plannerState.slots.length;
    if (target > current) {
        // Add empty slots up to target
        for (let i = current; i < target; i++) {
            plannerState.slots.push({ unit_id: null, power_type: null, notes: null, location_name: null });
        }
    } else if (target < current) {
        // Remove empty slots from the bottom, never remove filled ones
        for (let i = plannerState.slots.length - 1; i >= target; i--) {
            if (!plannerState.slots[i].unit_id) {
                plannerState.slots.splice(i, 1);
            }
            if (plannerState.slots.length <= target) break;
        }
    }
    plannerRenderSlots();
}

function plannerSelectSlot(idx) {
    plannerSelectedSlotIdx = (plannerSelectedSlotIdx === idx) ? null : idx;
    plannerRenderSlots();
    plannerRenderUnits();
}

function plannerAssignUnit(unitId) {
    if (plannerSelectedSlotIdx !== null && plannerSelectedSlotIdx < plannerState.slots.length && !plannerState.slots[plannerSelectedSlotIdx].unit_id) {
        plannerState.slots[plannerSelectedSlotIdx].unit_id = unitId;
        plannerSelectedSlotIdx = null;
    } else {
        const emptyIdx = plannerState.slots.findIndex(s => !s.unit_id);
        if (emptyIdx >= 0) {
            plannerState.slots[emptyIdx].unit_id = unitId;
        } else {
            plannerState.slots.push({ unit_id: unitId, power_type: null, notes: null, location_name: null });
        }
    }
    plannerRenderSlots();
    plannerRenderUnits();
}

function plannerRemoveSlot(idx) {
    plannerState.slots.splice(idx, 1);
    plannerRenderSlots();
    plannerRenderUnits();
}

function plannerSetPowerType(idx, value) {
    plannerState.slots[idx].power_type = value || null;
}

function plannerSetSlotNotes(idx, value) {
    plannerState.slots[idx].notes = value || null;
}

function plannerSetLocationName(idx, value) {
    plannerState.slots[idx].location_name = value || null;
}

function plannerRenderSlots() {
    const container = document.getElementById('planner-slots');
    const emptyMsg = document.getElementById('planner-slots-empty');
    container.querySelectorAll('.planner-slot-row').forEach(el => el.remove());

    if (plannerState.slots.length === 0) {
        emptyMsg.classList.remove('hidden');
        return;
    }
    emptyMsg.classList.add('hidden');

    plannerState.slots.forEach((slot, idx) => {
        const isSelected = !slot.unit_id && plannerSelectedSlotIdx === idx;
        const row = document.createElement('div');
        row.className = `planner-slot-row flex flex-col gap-1.5 px-3 py-2 rounded-lg border ${
            isSelected
                ? 'border-blue-500 dark:border-blue-400 bg-blue-50 dark:bg-blue-900/30 ring-2 ring-blue-400 dark:ring-blue-500'
                : 'border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-slate-700/50'
        }`;
        row.dataset.idx = idx;
        row.draggable = !!slot.unit_id;

        // Drag events
        if (slot.unit_id) {
            row.addEventListener('dragstart', e => {
                dragSrcIdx = idx;
                e.dataTransfer.effectAllowed = 'move';
                row.classList.add('opacity-50');
            });
            row.addEventListener('dragend', () => row.classList.remove('opacity-50'));
        }
        row.addEventListener('dragover', e => {
            e.preventDefault();
            e.dataTransfer.dropEffect = 'move';
            container.querySelectorAll('.planner-slot-row').forEach(r => r.classList.remove('ring-2', 'ring-blue-400'));
            row.classList.add('ring-2', 'ring-blue-400');
        });
        row.addEventListener('dragleave', () => row.classList.remove('ring-2', 'ring-blue-400'));
        row.addEventListener('drop', e => {
            e.preventDefault();
            row.classList.remove('ring-2', 'ring-blue-400');
            if (dragSrcIdx === null || dragSrcIdx === idx) return;
            // Swap unit_id only — power_type stays with the location slot
            const srcSlot = plannerState.slots[dragSrcIdx];
            const dstSlot = plannerState.slots[idx];
            [srcSlot.unit_id, dstSlot.unit_id] = [dstSlot.unit_id, srcSlot.unit_id];
            dragSrcIdx = null;
            plannerRenderSlots();
            plannerRenderUnits();
        });

        const powerSelect = `
            <select onchange="plannerSetPowerType(${idx}, this.value)"
                    class="text-xs px-2 py-1 border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-slate-700 text-gray-700 dark:text-gray-300 focus:ring-1 focus:ring-blue-500">
                <option value="" ${!slot.power_type ? 'selected' : ''}>— power —</option>
                <option value="ac" ${slot.power_type === 'ac' ? 'selected' : ''}>A/C Power</option>
                <option value="solar" ${slot.power_type === 'solar' ? 'selected' : ''}>Solar</option>
            </select>`;

        const dragHandle = slot.unit_id
            ? `<span class="text-gray-300 dark:text-gray-600 cursor-grab active:cursor-grabbing select-none" title="Drag to reorder">⠿</span>`
            : `<span class="w-4"></span>`;

        // Build inline cal text for filled slots
        let calInline = '';
        if (slot.unit_id) {
            const uData = plannerState.allUnits.find(u => u.id === slot.unit_id);
            if (uData) {
                const today = new Date(); today.setHours(0,0,0,0);
                const expiry = uData.expiry_date ? new Date(uData.expiry_date + 'T00:00:00') : null;
                const calExpired = !uData.last_calibrated || (expiry && expiry < today);
                const start = document.getElementById('planner-start').value;
                const end = document.getElementById('planner-end').value;
                const jobStart = start ? new Date(start + 'T00:00:00') : null;
                const jobEnd = end ? new Date(end + 'T00:00:00') : null;
                const expiresInJob = expiry && jobStart && jobEnd && expiry >= jobStart && expiry <= jobEnd;

                if (!uData.last_calibrated) {
                    calInline = `<span class="text-xs text-red-500 dark:text-red-400 font-medium">No cal</span>`;
                } else if (calExpired) {
                    const expiryStr = expiry.toLocaleDateString('en-US', {month:'short', day:'numeric', year:'numeric'});
                    calInline = `<span class="text-xs text-red-500 dark:text-red-400 font-medium">Cal exp. ${expiryStr}</span>`;
                } else if (expiresInJob) {
                    const expiryStr = expiry.toLocaleDateString('en-US', {month:'short', day:'numeric', year:'numeric'});
                    calInline = `<span class="text-xs text-amber-500 dark:text-amber-400 font-medium">Cal exp. ${expiryStr}</span>`;
                } else {
                    const expiryStr = expiry.toLocaleDateString('en-US', {month:'short', day:'numeric', year:'numeric'});
                    calInline = `<span class="text-xs text-gray-400 dark:text-gray-500">Cal exp. ${expiryStr}</span>`;
                }
            }
        }

        row.innerHTML = `
            <div class="flex items-center gap-2">
                ${dragHandle}
                <span class="text-sm text-gray-500 dark:text-gray-400 w-16 flex-shrink-0">Loc. ${idx + 1}</span>
                ${slot.unit_id
                    ? `<button onclick="openUnitQuickModal('${slot.unit_id}')" class="font-medium text-blue-600 dark:text-blue-400 hover:underline">${slot.unit_id}</button>
                       ${calInline ? `<span class="flex-1">${calInline}</span>` : '<span class="flex-1"></span>'}
                       ${powerSelect}
                       <button onclick="plannerClearSlot(${idx})" class="text-xs text-gray-400 hover:text-red-500 flex-shrink-0" title="Remove unit">✕</button>`
                    : `<button onclick="plannerSelectSlot(${idx})" class="flex-1 text-left text-sm italic ${plannerSelectedSlotIdx === idx ? 'text-blue-600 dark:text-blue-400 font-medium' : 'text-gray-400 dark:text-gray-500'}">${plannerSelectedSlotIdx === idx ? '← click a unit to assign here' : 'Empty — click to select'}</button>
                       ${powerSelect}
                       <button onclick="plannerRemoveSlot(${idx})" class="text-xs text-gray-400 hover:text-red-500 flex-shrink-0" title="Remove location">✕</button>`
                }
            </div>
            <div class="pl-8 flex flex-col gap-1 mt-1">
                <input type="text" value="${slot.location_name ? slot.location_name.replace(/"/g, '&quot;') : ''}"
                       oninput="plannerSetLocationName(${idx}, this.value)"
                       placeholder="Location name (e.g. North Gate)"
                       class="w-full text-xs px-2 py-1 border border-gray-200 dark:border-gray-600 rounded bg-white dark:bg-slate-700 text-gray-600 dark:text-gray-400 placeholder-gray-300 dark:placeholder-gray-600 focus:ring-1 focus:ring-blue-500">
                <input type="text" value="${slot.notes ? slot.notes.replace(/"/g, '&quot;') : ''}"
                       oninput="plannerSetSlotNotes(${idx}, this.value)"
                       placeholder="Location notes (optional)"
                       class="w-full text-xs px-2 py-1 border border-gray-200 dark:border-gray-600 rounded bg-white dark:bg-slate-700 text-gray-600 dark:text-gray-400 placeholder-gray-300 dark:placeholder-gray-600 focus:ring-1 focus:ring-blue-500">
            </div>
        `;
        container.appendChild(row);
    });
}

function plannerClearSlot(idx) {
    plannerState.slots[idx].unit_id = null;
    plannerSelectedSlotIdx = null;
    plannerRenderSlots();
    plannerRenderUnits();
}

function plannerReset() {
    plannerSelectedSlotIdx = null;
    plannerState = { reservation_id: null, slots: [], allUnits: [] };
    document.getElementById('planner-name').value = '';
    document.getElementById('planner-project').value = '';
    document.getElementById('planner-start').value = '';
    document.getElementById('planner-end').value = '';
    document.getElementById('planner-notes').value = '';
    document.getElementById('planner-est-units').value = '';
    document.getElementById('planner-search').value = '';
    const defaultDt = document.querySelector('input[name="planner_device_type"][value="seismograph"]');
    if (defaultDt) defaultDt.checked = true;
    document.getElementById('planner-deployed-only').checked = false;
    document.getElementById('planner-avail-count').textContent = '';
    const smartColor = plannerPickSmartColor();
    plannerSetColor(smartColor, true);
    const titleEl = document.getElementById('planner-form-title');
    if (titleEl) titleEl.textContent = 'New Job';
    document.getElementById('planner-save-btn').textContent = 'Save Job';
    document.getElementById('planner-meta-fields').style.display = '';
    document.getElementById('planner-edit-actions').classList.add('hidden');
    plannerSetMetaOpen(true);
    const summaryEl = document.getElementById('planner-meta-summary');
    if (summaryEl) summaryEl.textContent = 'New Job';
    plannerRenderSlots();
    plannerRenderUnits();
}

async function plannerSave() {
    const name = document.getElementById('planner-name').value.trim();
    const start = document.getElementById('planner-start').value;
    const end = document.getElementById('planner-end').value;
    const projectId = document.getElementById('planner-project').value;
    const notes = document.getElementById('planner-notes').value.trim();
    const color = document.getElementById('planner-color-value')?.value || '#3B82F6';
    const estUnits = parseInt(document.getElementById('planner-est-units').value) || null;
    const totalSlots = plannerState.slots.length || null;
    const filledSlots = plannerState.slots.filter(s => s.unit_id);

    if (!name) { showPlannerToast('Please enter a job name.', true); return; }
    if (!start || !end) { showPlannerToast('Please set start and end dates.', true); return; }
    if (end < start) { showPlannerToast('End date must be after start date.', true); return; }

    const btn = document.getElementById('planner-save-btn');
    btn.disabled = true;
    btn.textContent = 'Saving...';

    try {
        const isEdit = !!plannerState.reservation_id;
        const url = isEdit
            ? `/api/fleet-calendar/reservations/${plannerState.reservation_id}`
            : '/api/fleet-calendar/reservations';
        const method = isEdit ? 'PUT' : 'POST';

        const plannerDeviceType = document.querySelector('input[name="planner_device_type"]:checked')?.value || 'seismograph';
        // Save full slot list including empties so they survive round-trips
        const allSlots = plannerState.slots.map(s => ({
            unit_id: s.unit_id || null,
            location_name: s.location_name || null,
            power_type: s.power_type || null,
            notes: s.notes || null
        }));
        const payload = {
            name, start_date: start, end_date: end,
            project_id: projectId || null,
            assignment_type: 'specific',
            device_type: plannerDeviceType,
            color, notes: notes || null,
            estimated_units: estUnits,
            quantity_needed: totalSlots,
            location_slots: allSlots
        };

        const resp = await fetch(url, {
            method,
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(payload)
        });
        const result = await resp.json();
        if (!result.success) throw new Error(result.detail || 'Save failed');

        const reservationId = isEdit ? plannerState.reservation_id : result.reservation_id;

        // Always call assign-units (even with empty list) — endpoint does a full replace
        const unitIds = filledSlots.map(s => s.unit_id);
        const powerTypes = {};
        const locationNotes = {};
        const locationNames = {};
        const slotIndices = {};
        filledSlots.forEach((s, i) => {
            if (s.power_type) powerTypes[s.unit_id] = s.power_type;
            if (s.notes) locationNotes[s.unit_id] = s.notes;
            if (s.location_name) locationNames[s.unit_id] = s.location_name;
            slotIndices[s.unit_id] = i;
        });
        const assignResp = await fetch(
            `/api/fleet-calendar/reservations/${reservationId}/assign-units`,
            {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ unit_ids: unitIds, power_types: powerTypes, location_notes: locationNotes, location_names: locationNames, slot_indices: slotIndices })
            }
        );
        const assignResult = await assignResp.json();
        if (assignResult.conflicts && assignResult.conflicts.length > 0) {
            const conflictIds = assignResult.conflicts.map(c => c.unit_id).join(', ');
            showPlannerToast(`Saved! ${assignResult.conflicts.length} unit(s) had conflicts and were skipped: ${conflictIds}`);
        }

        plannerReset();
        switchPlannerTab('list');
        // Reload the reservations list partial
        const listUrl = document.getElementById('planner-reservations-list').getAttribute('hx-get');
        htmx.ajax('GET', listUrl, {target: '#planner-reservations-list', swap: 'innerHTML'});
        showPlannerToast(isEdit ? 'Job updated!' : 'Job created!');
    } catch (e) {
        console.error('Planner save error', e);
        showPlannerToast('Error saving job: ' + e.message, true);
    } finally {
        btn.disabled = false;
        btn.textContent = plannerState.reservation_id ? 'Save Changes' : 'Save Job';
    }
}

function plannerPromote() {
    if (!plannerState.reservation_id) return;
    const name = document.getElementById('planner-form-title').textContent;
    openPromoteModal(plannerState.reservation_id, name);
}

async function plannerDeleteCurrent() {
    const name = document.getElementById('planner-form-title').textContent;
    if (!confirm(`Delete job "${name}"?\n\nThis will remove all unit assignments.`)) return;
    try {
        const resp = await fetch(`/api/fleet-calendar/reservations/${plannerState.reservation_id}`, { method: 'DELETE' });
        if (resp.ok) {
            switchPlannerTab('list');
            plannerReset();
            const listUrl = document.getElementById('planner-reservations-list').getAttribute('hx-get');
            htmx.ajax('GET', listUrl, {target: '#planner-reservations-list', swap: 'innerHTML'});
            showPlannerToast('Job deleted');
        } else {
            const data = await resp.json();
            showPlannerToast('Error: ' + (data.detail || 'Failed to delete'), true);
        }
    } catch (e) {
        showPlannerToast('Error deleting job', true);
    }
}

async function openPlanner(reservationId) {
    plannerReset();
    if (reservationId) {
        try {
            const resp = await fetch(`/api/fleet-calendar/reservations/${reservationId}`);
            const res = await resp.json();
            plannerState.reservation_id = reservationId;
            document.getElementById('planner-name').value = res.name;
            document.getElementById('planner-project').value = res.project_id || '';
            document.getElementById('planner-start').value = res.start_date;
            document.getElementById('planner-end').value = res.end_date || '';
            document.getElementById('planner-notes').value = res.notes || '';
            document.getElementById('planner-est-units').value = res.estimated_units || '';
            plannerSetColor(res.color || '#3B82F6', true);
            const dtRadio = document.querySelector(`input[name="planner_device_type"][value="${res.device_type || 'seismograph'}"]`);
            if (dtRadio) dtRadio.checked = true;
            // Restore full slot list — use location_slots if available, else fall back to assigned_units only
            const assignedById = {};
            for (const u of (res.assigned_units || [])) assignedById[u.id] = u;
            if (res.location_slots && res.location_slots.length > 0) {
                for (const s of res.location_slots) {
                    const filled = s.unit_id ? (assignedById[s.unit_id] || {}) : {};
                    plannerState.slots.push({
                        unit_id: s.unit_id || null,
                        power_type: s.power_type || filled.power_type || null,
                        notes: s.notes || filled.notes || null,
                        location_name: s.location_name || filled.location_name || null
                    });
                }
            } else {
                // Legacy: no location_slots stored, just load filled ones
                for (const u of (res.assigned_units || [])) {
                    plannerState.slots.push({ unit_id: u.id, power_type: u.power_type || null, notes: u.notes || null, location_name: u.location_name || null });
                }
            }

            const titleEl = document.getElementById('planner-form-title');
            if (titleEl) titleEl.textContent = res.name;
            document.getElementById('planner-save-btn').textContent = 'Save Changes';
            document.getElementById('planner-meta-fields').style.display = '';
            document.getElementById('planner-edit-actions').classList.remove('hidden');
            plannerSetMetaOpen(false);
            plannerUpdateMetaSummary();
            plannerRenderSlots();
            plannerLoadUnits();
        } catch (e) {
            console.error('Error loading reservation for planner', e);
        }
    }
    switchTab('planner');
    switchPlannerTab('assign');
}
</script>
{% endblock %}
