<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AIRCRAFT RADAR // INTERCEPT - See the Invisible</title>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/responsive.css') }}">
    <link rel="stylesheet" href="{{ url_for('static', filename='css/adsb_dashboard.css') }}">
</head>
<body>
    <div class="radar-bg"></div>
    <div class="scanline"></div>

    <header class="header">
        <div class="logo">
            AIRCRAFT RADAR
            <span>// INTERCEPT - See the Invisible</span>
        </div>
        <div class="status-bar">
            <a href="/?mode=aircraft" class="back-link">Main Dashboard</a>
        </div>
    </header>

    <!-- Slim Statistics Bar -->
    <div class="stats-strip">
        <div class="stats-strip-inner">
            <div class="strip-stat">
                <span class="strip-value" id="stripAircraftNow">0</span>
                <span class="strip-label">NOW</span>
            </div>
            <div class="strip-stat">
                <span class="strip-value" id="stripTotalSeen">0</span>
                <span class="strip-label">SEEN</span>
            </div>
            <div class="strip-stat">
                <span class="strip-value" id="stripMaxRange">0</span>
                <span class="strip-label">MAX NM</span>
            </div>
            <div class="strip-stat">
                <span class="strip-value" id="stripHighest">-</span>
                <span class="strip-label">HIGH FL</span>
            </div>
            <div class="strip-stat">
                <span class="strip-value" id="stripFastest">-</span>
                <span class="strip-label">FAST KT</span>
            </div>
            <div class="strip-stat">
                <span class="strip-value" id="stripClosest">-</span>
                <span class="strip-label">NEAR NM</span>
            </div>
            <div class="strip-stat">
                <span class="strip-value" id="stripCountries">0</span>
                <span class="strip-label">COUNTRIES</span>
            </div>
            <div class="strip-stat">
                <span class="strip-value" id="stripAcars">0</span>
                <span class="strip-label">ACARS</span>
            </div>
            <div class="strip-stat signal-stat" title="Signal quality (messages/errors)">
                <span class="strip-value" id="stripSignal">--</span>
                <span class="strip-label">SIGNAL</span>
            </div>
            <div class="strip-stat session-stat">
                <span class="strip-value" id="stripSession">00:00:00</span>
                <span class="strip-label">SESSION</span>
            </div>
            <div class="strip-divider"></div>
            <button type="button" class="strip-btn" onclick="showSquawkInfo(null)" title="Squawk Code Reference">
                📟 Squawk
            </button>
            <button type="button" class="strip-btn" onclick="lookupSelectedFlight()" title="Lookup selected aircraft on FlightAware" id="flightLookupBtn" disabled>
                🔗 Lookup
            </button>
            <button type="button" class="strip-btn primary" onclick="generateReport()" title="Generate Session Report">
                📊 Report
            </button>
            <div class="strip-divider"></div>
            <div class="strip-status">
                <div class="status-dot inactive" id="trackingDot"></div>
                <span id="trackingStatus">STANDBY</span>
            </div>
            <div class="strip-time" id="utcTime">--:--:-- UTC</div>
        </div>
    </div>

    <main class="dashboard">
        <!-- ACARS Panel (left of map) - Collapsible -->
        <div class="acars-sidebar" id="acarsSidebar">
            <div class="acars-sidebar-content" id="acarsSidebarContent">
                <div class="panel acars-panel">
                    <div class="panel-header">
                        <span>ACARS MESSAGES</span>
                        <div style="display: flex; align-items: center; gap: 8px;">
                            <span id="acarsCount" style="font-size: 10px; color: var(--accent-cyan);">0</span>
                            <div class="panel-indicator" id="acarsPanelIndicator"></div>
                        </div>
                    </div>
                    <div id="acarsPanelContent">
                        <div class="acars-info" style="font-size: 9px; color: var(--text-muted); padding: 5px 8px; border-bottom: 1px solid var(--border-color);">
                            <span style="color: var(--accent-yellow);">⚠</span> Requires separate SDR (VHF ~131 MHz)
                        </div>
                        <div class="acars-controls" style="padding: 8px; border-bottom: 1px solid var(--border-color);">
                            <div style="display: flex; gap: 5px; margin-bottom: 5px;">
                                <select id="acarsDeviceSelect" style="flex: 1; font-size: 10px;">
                                    <option value="0">SDR 0</option>
                                    <option value="1">SDR 1</option>
                                </select>
                                <select id="acarsRegionSelect" onchange="updateAcarsFreqCheckboxes()" style="flex: 1; font-size: 10px;">
                                    <option value="na">N. America</option>
                                    <option value="eu">Europe</option>
                                    <option value="ap">Asia-Pac</option>
                                </select>
                            </div>
                            <div id="acarsFreqSelector" style="display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 8px; font-size: 9px;">
                                <!-- Frequency checkboxes populated by JS -->
                            </div>
                            <button class="acars-btn" id="acarsToggleBtn" onclick="toggleAcars()" style="width: 100%;">
                                ▶ START ACARS
                            </button>
                        </div>
                        <div class="acars-messages" id="acarsMessages">
                            <div class="no-aircraft" style="padding: 20px; text-align: center;">
                                <div style="font-size: 10px; color: var(--text-muted);">No ACARS messages</div>
                                <div style="font-size: 9px; color: var(--text-dim); margin-top: 5px;">Start ACARS to receive aircraft datalink messages</div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <button class="acars-collapse-btn" id="acarsCollapseBtn" onclick="toggleAcarsSidebar()" title="Toggle ACARS Panel">
                <span id="acarsCollapseIcon">◀</span>
                <span class="acars-collapse-label">ACARS</span>
            </button>
        </div>

        <!-- Main Display (Map or Radar Scope) -->
        <div class="main-display">
            <div class="display-container">
                <div id="radarMap">
                    <canvas id="radarOverlayCanvas"></canvas>
                </div>
                <div id="radarScope">
                    <canvas id="radarCanvas"></canvas>
                </div>
            </div>
        </div>

        <!-- Sidebar -->
        <div class="sidebar">
            <!-- View Toggle -->
            <div class="view-toggle">
                <button class="view-btn active" id="mapViewBtn" onclick="setView('map')">MAP</button>
                <button class="view-btn" id="radarViewBtn" onclick="setView('radar')">RADAR</button>
            </div>

            <!-- Selected Aircraft -->
            <div class="panel selected-aircraft">
                <div class="panel-header">
                    <span>SELECTED TARGET</span>
                    <div class="panel-indicator"></div>
                </div>
                <div class="selected-info" id="selectedInfo">
                    <div class="no-aircraft">
                        <div class="no-aircraft-icon">&#9992;</div>
                        <div>Select an aircraft</div>
                    </div>
                </div>
            </div>

            <!-- Aircraft List -->
            <div class="panel aircraft-list">
                <div class="panel-header">
                    <span>TRACKED AIRCRAFT</span>
                    <div class="panel-indicator"></div>
                </div>
                <div class="aircraft-list-content" id="aircraftList">
                    <div class="no-aircraft">
                        <div>No aircraft detected</div>
                        <div style="font-size: 10px; margin-top: 5px;">Start tracking to begin</div>
                    </div>
                </div>
            </div>
        </div>

        <!-- Controls Bar - Reorganized -->
        <div class="controls-bar">
            <!-- Display Options Group -->
            <div class="control-group">
                <span class="control-group-label">DISPLAY</span>
                <div class="control-group-items">
                    <label title="Show aircraft trails"><input type="checkbox" id="showTrails" onchange="toggleTrails()"> Trails</label>
                    <label title="Show range rings"><input type="checkbox" id="showRangeRings" checked onchange="drawRangeRings()"> Rings</label>
                    <label title="Radar sweep overlay"><input type="checkbox" id="radarOverlay" onchange="toggleRadarOverlay()"> Radar</label>
                    <label title="Audio alerts"><input type="checkbox" id="alertToggle" checked onchange="toggleAlerts()"> Alerts</label>
                    <select id="aircraftFilter" onchange="applyFilter()" title="Filter aircraft">
                        <option value="all">All Aircraft</option>
                        <option value="military">Military</option>
                        <option value="civil">Civil</option>
                        <option value="emergency">Emergency</option>
                        <option value="watchlist">Watchlist</option>
                    </select>
                    <button class="watchlist-btn" onclick="showWatchlistModal()" title="Manage Watchlist">★</button>
                    <select id="rangeSelect" onchange="updateRange()" title="Range rings distance">
                        <option value="50">50nm</option>
                        <option value="100">100nm</option>
                        <option value="200" selected>200nm</option>
                        <option value="300">300nm</option>
                    </select>
                </div>
            </div>

            <!-- Location Group -->
            <div class="control-group">
                <span class="control-group-label">LOCATION</span>
                <div class="control-group-items">
                    <input type="text" id="obsLat" value="51.5074" onchange="updateObserverLoc()" style="width: 70px;" title="Latitude" placeholder="Lat">
                    <input type="text" id="obsLon" value="-0.1278" onchange="updateObserverLoc()" style="width: 70px;" title="Longitude" placeholder="Lon">
                    <span id="gpsIndicator" class="gps-indicator" style="display: none;" title="GPS connected via gpsd"><span class="gps-dot"></span> GPS</span>
                </div>
            </div>

            <!-- ADS-B Tracking Group -->
            <div class="control-group tracking-group">
                <span class="control-group-label">ADS-B TRACKING</span>
                <div class="control-group-items">
                    <label title="Use remote dump1090"><input type="checkbox" id="useRemoteDump1090" onchange="toggleRemoteDump1090()"> Remote</label>
                    <span class="remote-dump1090-controls" style="display: none;">
                        <input type="text" id="remoteSbsHost" placeholder="Host" style="width: 70px;">
                        <input type="number" id="remoteSbsPort" value="30003" style="width: 55px;">
                    </span>
                    <select id="adsbDeviceSelect" title="SDR device for ADS-B (1090 MHz)">
                        <option value="0">SDR 0</option>
                    </select>
                    <button class="start-btn" id="startBtn" onclick="toggleTracking()">START</button>
                </div>
            </div>

            <!-- Airband Listening Group -->
            <div class="control-group airband-group">
                <span class="control-group-label">AIRBAND</span>
                <div class="control-group-items">
                    <select id="airbandFreqSelect" onchange="updateAirbandFreq()" class="airband-controls" title="Airband frequency">
                        <option value="121.5">121.5 Guard</option>
                        <option value="118.0">118.0</option>
                        <option value="119.1">119.1</option>
                        <option value="120.5">120.5</option>
                        <option value="123.45">123.45 Air-Air</option>
                        <option value="127.85">127.85</option>
                        <option value="128.825">128.825</option>
                        <option value="132.0">132.0</option>
                        <option value="134.725">134.725</option>
                        <option value="custom">Custom...</option>
                    </select>
                    <input type="number" id="airbandCustomFreq" step="0.025" placeholder="MHz" class="airband-controls" style="width: 65px; display: none;">
                    <select id="airbandSpacing" class="airband-controls" title="Channel spacing" style="width: 55px; display: none;" onchange="updateAirbandSpacing()">
                        <option value="25">25 kHz</option>
                        <option value="8.33">8.33 kHz</option>
                    </select>
                    <select id="airbandDeviceSelect" class="airband-controls" title="SDR for airband">
                        <option value="0">SDR 0</option>
                    </select>
                    <div class="airband-sliders">
                        <span title="Squelch">SQ</span>
                        <input type="range" id="airbandSquelch" min="0" max="100" value="0" class="airband-controls" title="Squelch" onchange="updateAirbandSquelch()">
                        <span title="Volume">VOL</span>
                        <input type="range" id="airbandVolume" min="0" max="100" value="80" class="airband-controls" oninput="updateAirbandVolume()" title="Volume">
                    </div>
                    <button class="airband-btn" id="airbandBtn" onclick="toggleAirband()">▶ LISTEN</button>
                    <span id="airbandStatus" class="airband-status">OFF</span>
                    <div class="airband-visualizer" id="airbandVisualizerContainer" style="display: none;">
                        <div class="signal-meter">
                            <div class="meter-bar">
                                <div class="meter-fill" id="airbandSignalMeter"></div>
                                <div class="meter-peak" id="airbandSignalPeak"></div>
                            </div>
                        </div>
                        <canvas id="airbandSpectrumCanvas" width="80" height="20"></canvas>
                    </div>
                </div>
            </div>
            <audio id="airbandPlayer" style="display: none;"></audio>
        </div>
    </main>

    <script>
        // ============================================
        // STATE
        // ============================================
        let radarMap = null;
        let aircraft = {};
        let markers = {};
        let selectedIcao = null;
        let eventSource = null;
        let isTracking = false;
        let currentFilter = 'all';
        let alertedAircraft = {};
        let alertsEnabled = true;
        let currentView = 'map';  // 'map' or 'radar'

        // Watchlist - persisted to localStorage
        let watchlist = JSON.parse(localStorage.getItem('adsb_watchlist') || '[]');

        // Aircraft trails
        let aircraftTrails = {};  // ICAO -> [{lat, lon, alt, time}, ...]
        let trailLines = {};      // ICAO -> L.polyline (array of segments)
        let showTrails = false;
        const MAX_TRAIL_POINTS = 100;

        // Radar scope
        let radarScope = null;
        let radarAnimationId = null;
        let maxRange = 200;  // nautical miles

        // Statistics
        let stats = {
            totalAircraftSeen: new Set(),
            maxRange: 0,
            messagesPerSecond: 0,
            messageTimestamps: [],
            countriesSeen: new Set(),
            highestAltitude: 0,
            fastestSpeed: 0,
            closestDistance: Infinity,
            sessionStart: null,
            acarsMessages: 0
        };

        // Session log for report generation
        let sessionLog = {
            startTime: null,
            endTime: null,
            aircraftLog: [],      // Array of all aircraft seen with details
            highlights: [],       // Notable events (military, emergency, etc)
            maxConcurrent: 0,
            peakMsgRate: 0
        };

        // ICAO Country Allocations (first hex digit ranges)
        const ICAO_COUNTRY_RANGES = [
            { start: 0x000000, end: 0x003FFF, country: 'Zimbabwe' },
            { start: 0x004000, end: 0x0043FF, country: 'Mozambique' },
            { start: 0x006000, end: 0x006FFF, country: 'South Africa' },
            { start: 0x008000, end: 0x00FFFF, country: 'South Africa' },
            { start: 0x010000, end: 0x017FFF, country: 'Egypt' },
            { start: 0x018000, end: 0x01FFFF, country: 'Libya' },
            { start: 0x020000, end: 0x027FFF, country: 'Morocco' },
            { start: 0x028000, end: 0x02FFFF, country: 'Tunisia' },
            { start: 0x030000, end: 0x0303FF, country: 'Botswana' },
            { start: 0x032000, end: 0x032FFF, country: 'Burundi' },
            { start: 0x034000, end: 0x034FFF, country: 'Cameroon' },
            { start: 0x038000, end: 0x038FFF, country: 'Congo' },
            { start: 0x03E000, end: 0x03EFFF, country: 'Gabon' },
            { start: 0x040000, end: 0x040FFF, country: 'Ethiopia' },
            { start: 0x042000, end: 0x042FFF, country: 'Equatorial Guinea' },
            { start: 0x044000, end: 0x044FFF, country: 'Ghana' },
            { start: 0x048000, end: 0x0483FF, country: 'Tanzania' },
            { start: 0x050000, end: 0x050FFF, country: 'Kenya' },
            { start: 0x054000, end: 0x054FFF, country: 'Zambia' },
            { start: 0x058000, end: 0x058FFF, country: 'Seychelles' },
            { start: 0x060000, end: 0x061FFF, country: 'Algeria' },
            { start: 0x068000, end: 0x068FFF, country: 'Angola' },
            { start: 0x070000, end: 0x070FFF, country: 'Ivory Coast' },
            { start: 0x078000, end: 0x078FFF, country: 'Mauritius' },
            { start: 0x080000, end: 0x080FFF, country: 'Nigeria' },
            { start: 0x088000, end: 0x088FFF, country: 'Uganda' },
            { start: 0x090000, end: 0x090FFF, country: 'Qatar' },
            { start: 0x0A0000, end: 0x0A7FFF, country: 'India' },
            { start: 0x0C0000, end: 0x0C4FFF, country: 'Australia' },
            { start: 0x100000, end: 0x1FFFFF, country: 'Russia' },
            { start: 0x200000, end: 0x27FFFF, country: 'USA' },
            { start: 0x280000, end: 0x28FFFF, country: 'USA' },
            { start: 0x300000, end: 0x33FFFF, country: 'Italy' },
            { start: 0x340000, end: 0x37FFFF, country: 'Spain' },
            { start: 0x380000, end: 0x3BFFFF, country: 'France' },
            { start: 0x3C0000, end: 0x3FFFFF, country: 'Germany' },
            { start: 0x400000, end: 0x43FFFF, country: 'UK' },
            { start: 0x440000, end: 0x447FFF, country: 'Austria' },
            { start: 0x448000, end: 0x44FFFF, country: 'Belgium' },
            { start: 0x450000, end: 0x457FFF, country: 'Bulgaria' },
            { start: 0x458000, end: 0x45FFFF, country: 'Denmark' },
            { start: 0x460000, end: 0x467FFF, country: 'Finland' },
            { start: 0x468000, end: 0x46FFFF, country: 'Greece' },
            { start: 0x470000, end: 0x477FFF, country: 'Hungary' },
            { start: 0x478000, end: 0x47FFFF, country: 'Norway' },
            { start: 0x480000, end: 0x487FFF, country: 'Netherlands' },
            { start: 0x488000, end: 0x48FFFF, country: 'Poland' },
            { start: 0x490000, end: 0x497FFF, country: 'Portugal' },
            { start: 0x498000, end: 0x49FFFF, country: 'Czech Republic' },
            { start: 0x4A0000, end: 0x4A7FFF, country: 'Romania' },
            { start: 0x4A8000, end: 0x4AFFFF, country: 'Sweden' },
            { start: 0x4B0000, end: 0x4B7FFF, country: 'Switzerland' },
            { start: 0x4B8000, end: 0x4BFFFF, country: 'Turkey' },
            { start: 0x4C0000, end: 0x4C7FFF, country: 'Serbia' },
            { start: 0x4CA000, end: 0x4CAFFF, country: 'Ireland' },
            { start: 0x4D0000, end: 0x4D03FF, country: 'Iceland' },
            { start: 0x500000, end: 0x5003FF, country: 'Luxembourg' },
            { start: 0x501000, end: 0x5013FF, country: 'Monaco' },
            { start: 0x502000, end: 0x502FFF, country: 'Malta' },
            { start: 0x503000, end: 0x5033FF, country: 'San Marino' },
            { start: 0x505000, end: 0x5057FF, country: 'Latvia' },
            { start: 0x506000, end: 0x5067FF, country: 'Lithuania' },
            { start: 0x507000, end: 0x5077FF, country: 'Moldova' },
            { start: 0x508000, end: 0x50FFFF, country: 'Slovakia' },
            { start: 0x510000, end: 0x5107FF, country: 'Slovenia' },
            { start: 0x511000, end: 0x5117FF, country: 'Uzbekistan' },
            { start: 0x512000, end: 0x5127FF, country: 'Ukraine' },
            { start: 0x513000, end: 0x5137FF, country: 'Belarus' },
            { start: 0x514000, end: 0x5147FF, country: 'Estonia' },
            { start: 0x515000, end: 0x5157FF, country: 'Macedonia' },
            { start: 0x516000, end: 0x5167FF, country: 'Bosnia' },
            { start: 0x517000, end: 0x5177FF, country: 'Georgia' },
            { start: 0x518000, end: 0x5187FF, country: 'Tajikistan' },
            { start: 0x600000, end: 0x6003FF, country: 'Armenia' },
            { start: 0x680000, end: 0x6803FF, country: 'Kyrgyzstan' },
            { start: 0x681000, end: 0x6813FF, country: 'Turkmenistan' },
            { start: 0x682000, end: 0x6823FF, country: 'Azerbaijan' },
            { start: 0x683000, end: 0x6833FF, country: 'Kazakhstan' },
            { start: 0x700000, end: 0x700FFF, country: 'Afghanistan' },
            { start: 0x702000, end: 0x702FFF, country: 'Bangladesh' },
            { start: 0x704000, end: 0x704FFF, country: 'Maldives' },
            { start: 0x706000, end: 0x706FFF, country: 'Nepal' },
            { start: 0x708000, end: 0x708FFF, country: 'Pakistan' },
            { start: 0x70A000, end: 0x70AFFF, country: 'Sri Lanka' },
            { start: 0x70C000, end: 0x70C3FF, country: 'Myanmar' },
            { start: 0x710000, end: 0x717FFF, country: 'Japan' },
            { start: 0x718000, end: 0x71FFFF, country: 'Japan' },
            { start: 0x720000, end: 0x727FFF, country: 'Laos' },
            { start: 0x728000, end: 0x72FFFF, country: 'Mongolia' },
            { start: 0x730000, end: 0x737FFF, country: 'Nepal' },
            { start: 0x738000, end: 0x73FFFF, country: 'South Korea' },
            { start: 0x740000, end: 0x747FFF, country: 'Indonesia' },
            { start: 0x748000, end: 0x74FFFF, country: 'Malaysia' },
            { start: 0x750000, end: 0x757FFF, country: 'Philippines' },
            { start: 0x758000, end: 0x75FFFF, country: 'Singapore' },
            { start: 0x760000, end: 0x767FFF, country: 'Thailand' },
            { start: 0x768000, end: 0x76FFFF, country: 'Vietnam' },
            { start: 0x780000, end: 0x7BFFFF, country: 'China' },
            { start: 0x7C0000, end: 0x7FFFFF, country: 'Australia' },
            { start: 0x800000, end: 0x83FFFF, country: 'India' },
            { start: 0x840000, end: 0x87FFFF, country: 'Japan' },
            { start: 0x880000, end: 0x887FFF, country: 'Pakistan' },
            { start: 0x890000, end: 0x890FFF, country: 'Hong Kong' },
            { start: 0x894000, end: 0x894FFF, country: 'Taiwan' },
            { start: 0x895000, end: 0x8953FF, country: 'North Korea' },
            { start: 0x896000, end: 0x896FFF, country: 'Jordan' },
            { start: 0x897000, end: 0x897FFF, country: 'Lebanon' },
            { start: 0x898000, end: 0x898FFF, country: 'Kuwait' },
            { start: 0x899000, end: 0x8993FF, country: 'Saudi Arabia' },
            { start: 0x8A0000, end: 0x8A7FFF, country: 'Saudi Arabia' },
            { start: 0x900000, end: 0x9003FF, country: 'Kuwait' },
            { start: 0x901000, end: 0x9013FF, country: 'Bahrain' },
            { start: 0x902000, end: 0x9023FF, country: 'Yemen' },
            { start: 0x903000, end: 0x9033FF, country: 'Syria' },
            { start: 0xA00000, end: 0xAFFFFF, country: 'USA' },
            { start: 0xC00000, end: 0xC3FFFF, country: 'Canada' },
            { start: 0xC80000, end: 0xC87FFF, country: 'New Zealand' },
            { start: 0xE00000, end: 0xE3FFFF, country: 'Argentina' },
            { start: 0xE40000, end: 0xE7FFFF, country: 'Brazil' },
            { start: 0xE80000, end: 0xE80FFF, country: 'Chile' },
            { start: 0xE84000, end: 0xE84FFF, country: 'Colombia' },
            { start: 0xE88000, end: 0xE88FFF, country: 'Peru' },
            { start: 0xE8C000, end: 0xE8CFFF, country: 'Venezuela' },
            { start: 0xF00000, end: 0xF07FFF, country: 'ICAO (special)' }
        ];

        function getCountryFromIcao(icao) {
            const icaoNum = parseInt(icao, 16);
            for (const range of ICAO_COUNTRY_RANGES) {
                if (icaoNum >= range.start && icaoNum <= range.end) {
                    return range.country;
                }
            }
            return 'Unknown';
        }

        // Observer location and range rings (load from localStorage or default to London)
        let observerLocation = (function() {
            const saved = localStorage.getItem('observerLocation');
            if (saved) {
                try {
                    const parsed = JSON.parse(saved);
                    if (parsed.lat && parsed.lon) return parsed;
                } catch (e) {}
            }
            return { lat: 51.5074, lon: -0.1278 };
        })();
        let rangeRingsLayer = null;
        let observerMarker = null;

        // GPS state
        let gpsConnected = false;
        let gpsEventSource = null;

        // ============================================
        // AUDIO ALERTS
        // ============================================
        let audioContext = null;
        function getAudioContext() {
            if (!audioContext) {
                audioContext = new (window.AudioContext || window.webkitAudioContext)();
            }
            return audioContext;
        }

        function playAlertSound(type) {
            if (!alertsEnabled) return;
            try {
                const ctx = getAudioContext();
                const oscillator = ctx.createOscillator();
                const gainNode = ctx.createGain();
                oscillator.connect(gainNode);
                gainNode.connect(ctx.destination);

                if (type === 'emergency') {
                    oscillator.frequency.setValueAtTime(880, ctx.currentTime);
                    oscillator.frequency.setValueAtTime(660, ctx.currentTime + 0.15);
                    oscillator.frequency.setValueAtTime(880, ctx.currentTime + 0.3);
                    gainNode.gain.setValueAtTime(0.3, ctx.currentTime);
                    gainNode.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.5);
                    oscillator.start(ctx.currentTime);
                    oscillator.stop(ctx.currentTime + 0.5);
                } else if (type === 'military') {
                    oscillator.frequency.setValueAtTime(523, ctx.currentTime);
                    gainNode.gain.setValueAtTime(0.2, ctx.currentTime);
                    gainNode.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.3);
                    oscillator.start(ctx.currentTime);
                    oscillator.stop(ctx.currentTime + 0.3);
                }
            } catch (e) {
                console.warn('Audio alert failed:', e);
            }
        }

        function checkAndAlertAircraft(icao, ac) {
            if (alertedAircraft[icao]) return;
            const militaryInfo = isMilitaryAircraft(icao, ac.callsign);
            const squawkInfo = checkSquawkCode(ac);
            const onWatchlist = isOnWatchlist(ac);

            if (squawkInfo && squawkInfo.type === 'emergency') {
                alertedAircraft[icao] = 'emergency';
                playAlertSound('emergency');
                showAlertBanner(`EMERGENCY: ${squawkInfo.name} - ${ac.callsign || icao}`, '#ff0000');
            } else if (onWatchlist) {
                alertedAircraft[icao] = 'watchlist';
                playAlertSound('military');  // Use military sound for watchlist
                showAlertBanner(`WATCHLIST: ${ac.callsign || ac.registration || icao} detected!`, '#00d4ff');
            } else if (militaryInfo.military) {
                alertedAircraft[icao] = 'military';
                playAlertSound('military');
                showAlertBanner(`MILITARY: ${ac.callsign || icao}${militaryInfo.country ? ' (' + militaryInfo.country + ')' : ''}`, '#556b2f');
            }
        }

        function showAlertBanner(message, color) {
            const banner = document.createElement('div');
            banner.style.cssText = `
                position: fixed; top: 70px; left: 50%; transform: translateX(-50%);
                background: ${color}; color: white; padding: 10px 20px; border-radius: 6px;
                font-weight: bold; font-size: 13px; z-index: 10000;
                box-shadow: 0 4px 20px rgba(0,0,0,0.5); animation: slideDown 0.3s ease-out;
            `;
            banner.textContent = message;
            document.body.appendChild(banner);
            setTimeout(() => {
                banner.style.opacity = '0';
                banner.style.transition = 'opacity 0.3s';
                setTimeout(() => banner.remove(), 300);
            }, 5000);
        }

        function toggleAlerts() {
            alertsEnabled = document.getElementById('alertToggle').checked;
        }

        // ============================================
        // WATCHLIST FUNCTIONS
        // ============================================
        function saveWatchlist() {
            localStorage.setItem('adsb_watchlist', JSON.stringify(watchlist));
        }

        function isOnWatchlist(aircraft) {
            const icao = aircraft.icao?.toUpperCase();
            const callsign = aircraft.callsign?.toUpperCase()?.trim();
            const registration = aircraft.registration?.toUpperCase()?.trim();

            return watchlist.some(entry => {
                const val = entry.value.toUpperCase();
                if (entry.type === 'icao' && icao === val) return true;
                if (entry.type === 'callsign' && callsign && callsign.includes(val)) return true;
                if (entry.type === 'registration' && registration === val) return true;
                if (entry.type === 'any') {
                    if (icao === val) return true;
                    if (callsign && callsign.includes(val)) return true;
                    if (registration === val) return true;
                }
                return false;
            });
        }

        function addToWatchlist(value, type = 'any', note = '') {
            value = value.trim().toUpperCase();
            if (!value) return false;

            // Check for duplicates
            const exists = watchlist.some(e => e.value.toUpperCase() === value && e.type === type);
            if (exists) return false;

            watchlist.push({ value, type, note, added: Date.now() });
            saveWatchlist();
            renderWatchlist();
            return true;
        }

        function removeFromWatchlist(index) {
            watchlist.splice(index, 1);
            saveWatchlist();
            renderWatchlist();
        }

        function showWatchlistModal() {
            renderWatchlist();
            document.getElementById('watchlistModal').classList.add('active');
            document.getElementById('watchlistInput').focus();
        }

        function closeWatchlistModal() {
            document.getElementById('watchlistModal').classList.remove('active');
        }

        function renderWatchlist() {
            const container = document.getElementById('watchlistEntries');
            document.getElementById('watchlistCount').textContent = watchlist.length;

            if (watchlist.length === 0) {
                container.innerHTML = '<div class="watchlist-empty">No entries. Add callsigns, registrations, or ICAO codes to watch.</div>';
                return;
            }

            container.innerHTML = watchlist.map((entry, i) => `
                <div class="watchlist-entry">
                    <div class="watchlist-entry-info">
                        <span class="watchlist-value">${entry.value}</span>
                        <span class="watchlist-type">${entry.type}</span>
                        ${entry.note ? `<span class="watchlist-note">${entry.note}</span>` : ''}
                    </div>
                    <button class="watchlist-remove" onclick="removeFromWatchlist(${i})" title="Remove">&times;</button>
                </div>
            `).join('');
        }

        // Close watchlist modal on overlay click or Escape key
        document.getElementById('watchlistModal')?.addEventListener('click', (e) => {
            if (e.target.id === 'watchlistModal') closeWatchlistModal();
        });
        document.addEventListener('keydown', (e) => {
            if (e.key === 'Escape') {
                closeWatchlistModal();
                closeSquawkModal();
            }
        });

        function handleWatchlistAdd() {
            const input = document.getElementById('watchlistInput');
            const type = document.getElementById('watchlistType').value;
            const note = document.getElementById('watchlistNote').value.trim();

            if (addToWatchlist(input.value, type, note)) {
                input.value = '';
                document.getElementById('watchlistNote').value = '';
            }
        }

        function addCurrentAircraftToWatchlist() {
            if (!selectedIcao || !aircraft[selectedIcao]) return;
            const ac = aircraft[selectedIcao];
            const value = ac.callsign || ac.registration || ac.icao;
            const type = ac.callsign ? 'callsign' : (ac.registration ? 'registration' : 'icao');
            addToWatchlist(value, type);
            showAlertBanner(`Added ${value} to watchlist`, '#00d4ff');
        }

        // ============================================
        // MILITARY/EMERGENCY DETECTION
        // ============================================
        const MILITARY_RANGES = [
            { start: 0xADF7C0, end: 0xADFFFF, country: 'US' },
            { start: 0xAE0000, end: 0xAEFFFF, country: 'US' },
            { start: 0x3F4000, end: 0x3F7FFF, country: 'FR' },
            { start: 0x43C000, end: 0x43CFFF, country: 'UK' },
            { start: 0x3D0000, end: 0x3DFFFF, country: 'DE' },
            { start: 0x501C00, end: 0x501FFF, country: 'NATO' },
        ];

        const MILITARY_PREFIXES = [
            'REACH', 'JAKE', 'DOOM', 'IRON', 'HAWK', 'VIPER', 'COBRA', 'THUNDER',
            'SHADOW', 'NIGHT', 'STEEL', 'GRIM', 'REAPER', 'BLADE', 'STRIKE',
            'RCH', 'CNV', 'MCH', 'EVAC', 'TOPCAT', 'ASCOT', 'RRR', 'HRK',
            'NAVY', 'ARMY', 'USAF', 'RAF', 'RCAF', 'RAAF', 'IAF', 'PAF'
        ];

        const SQUAWK_CODES = {
            // Emergency codes
            '7500': { type: 'emergency', name: 'HIJACK', desc: 'Aircraft is being hijacked', color: '#ff0000' },
            '7600': { type: 'emergency', name: 'RADIO FAILURE', desc: 'Lost communication with ATC', color: '#ff6600' },
            '7700': { type: 'emergency', name: 'EMERGENCY', desc: 'General emergency (mayday)', color: '#ff0000' },
            // Special codes
            '7777': { type: 'special', name: 'MILITARY INTERCEPT', desc: 'Military interceptor operations', color: '#ff00ff' },
            '7000': { type: 'vfr', name: 'VFR (EU)', desc: 'Visual Flight Rules - Europe', color: '#00d4ff' },
            '1200': { type: 'vfr', name: 'VFR (US/CA)', desc: 'Visual Flight Rules - North America', color: '#00d4ff' },
            '2000': { type: 'standard', name: 'UNASSIGNED', desc: 'No assigned code / entering controlled airspace', color: '#888888' },
            '1000': { type: 'standard', name: 'IFR (EU)', desc: 'Instrument Flight Rules with no assigned code', color: '#00ff88' },
            '0000': { type: 'special', name: 'DISCRETE', desc: 'Military/special operations', color: '#ff00ff' },
            '4000': { type: 'special', name: 'FERRY/DELIVERY', desc: 'Aircraft ferry/delivery flight', color: '#ffaa00' },
            '5000': { type: 'special', name: 'MILITARY (UK)', desc: 'UK military operations', color: '#556b2f' },
            '0033': { type: 'special', name: 'PARACHUTE OPS', desc: 'Parachute dropping in progress', color: '#ffaa00' },
            '7001': { type: 'special', name: 'VFR INTRUSION', desc: 'VFR aircraft entering controlled airspace', color: '#ffaa00' },
            '7004': { type: 'special', name: 'AEROBATIC', desc: 'Aerobatic flight display', color: '#00d4ff' },
            '7010': { type: 'special', name: 'RADIO EQUIPPED', desc: 'IFR flight (UK zones)', color: '#00ff88' }
        };

        const SQUAWK_REFERENCE = [
            { code: '7500', name: 'Hijack', desc: 'Aircraft is being hijacked - do not acknowledge' },
            { code: '7600', name: 'Radio Failure', desc: 'Two-way radio communication failure' },
            { code: '7700', name: 'Emergency', desc: 'General emergency (mayday/pan-pan)' },
            { code: '7777', name: 'Military Intercept', desc: 'Active military intercept operations' },
            { code: '---', name: '---', desc: '---' },
            { code: '1200', name: 'VFR (US/Canada)', desc: 'Visual flight rules - North America' },
            { code: '7000', name: 'VFR (Europe)', desc: 'Visual flight rules - ICAO/Europe' },
            { code: '2000', name: 'Conspicuity', desc: 'Entering airspace, no code assigned' },
            { code: '1000', name: 'IFR (Europe)', desc: 'Instrument flight rules, no code assigned' },
            { code: '---', name: '---', desc: '---' },
            { code: '0000', name: 'Discrete', desc: 'Military/special operations' },
            { code: '0033', name: 'Parachute Ops', desc: 'Parachute dropping operations' },
            { code: '4000', name: 'Ferry Flight', desc: 'Aircraft delivery/repositioning' },
            { code: '5000', name: 'Military (UK)', desc: 'UK military low-level operations' },
            { code: '7001', name: 'VFR Intrusion', desc: 'VFR aircraft entering controlled space' },
            { code: '7004', name: 'Aerobatic', desc: 'Aerobatic display flight' }
        ];

        function isMilitaryAircraft(icao, callsign) {
            const icaoNum = parseInt(icao, 16);
            for (const range of MILITARY_RANGES) {
                if (icaoNum >= range.start && icaoNum <= range.end) {
                    return { military: true, country: range.country };
                }
            }
            if (callsign) {
                const upper = callsign.toUpperCase();
                for (const prefix of MILITARY_PREFIXES) {
                    if (upper.startsWith(prefix)) {
                        return { military: true, type: 'callsign' };
                    }
                }
            }
            return { military: false };
        }

        function checkSquawkCode(aircraft) {
            if (aircraft.squawk && SQUAWK_CODES[aircraft.squawk]) {
                return SQUAWK_CODES[aircraft.squawk];
            }
            return null;
        }

        // ============================================
        // DISTANCE/BEARING CALCULATIONS
        // ============================================
        function calculateDistanceNm(lat1, lon1, lat2, lon2) {
            const R = 3440.065;
            const dLat = (lat2 - lat1) * Math.PI / 180;
            const dLon = (lon2 - lon1) * Math.PI / 180;
            const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
                      Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
                      Math.sin(dLon/2) * Math.sin(dLon/2);
            const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
            return R * c;
        }

        function calculateBearing(lat1, lon1, lat2, lon2) {
            const dLon = (lon2 - lon1) * Math.PI / 180;
            const lat1Rad = lat1 * Math.PI / 180;
            const lat2Rad = lat2 * Math.PI / 180;
            const y = Math.sin(dLon) * Math.cos(lat2Rad);
            const x = Math.cos(lat1Rad) * Math.sin(lat2Rad) -
                      Math.sin(lat1Rad) * Math.cos(lat2Rad) * Math.cos(dLon);
            let bearing = Math.atan2(y, x) * 180 / Math.PI;
            return (bearing + 360) % 360;
        }

        // ============================================
        // STATISTICS
        // ============================================
        function updateStatistics(icao, ac) {
            const isNew = !stats.totalAircraftSeen.has(icao);
            stats.totalAircraftSeen.add(icao);

            // Track country
            const country = getCountryFromIcao(icao);
            if (country !== 'Unknown') {
                stats.countriesSeen.add(country);
            }

            // Log new aircraft
            if (isNew) {
                const militaryInfo = isMilitaryAircraft(icao, ac.callsign);
                const squawkInfo = checkSquawkCode(ac);
                sessionLog.aircraftLog.push({
                    icao,
                    callsign: ac.callsign || '',
                    registration: ac.registration || '',
                    country,
                    military: militaryInfo.military,
                    firstSeen: new Date().toISOString(),
                    altitude: ac.altitude,
                    speed: ac.speed
                });

                // Log highlights
                if (militaryInfo.military) {
                    sessionLog.highlights.push({
                        time: new Date().toISOString(),
                        type: 'military',
                        icao,
                        callsign: ac.callsign || icao,
                        country: militaryInfo.country || country
                    });
                }
                if (squawkInfo && squawkInfo.type === 'emergency') {
                    sessionLog.highlights.push({
                        time: new Date().toISOString(),
                        type: 'emergency',
                        icao,
                        callsign: ac.callsign || icao,
                        squawk: ac.squawk,
                        name: squawkInfo.name
                    });
                }
            }

            // Distance calculation
            if (ac.lat && ac.lon) {
                const distance = calculateDistanceNm(
                    observerLocation.lat, observerLocation.lon,
                    ac.lat, ac.lon
                );

                if (distance > stats.maxRange) {
                    stats.maxRange = distance;
                }
            }

            // Message rate
            const now = Date.now();
            stats.messageTimestamps.push(now);
            stats.messageTimestamps = stats.messageTimestamps.filter(t => now - t < 5000);
            stats.messagesPerSecond = stats.messageTimestamps.length / 5;

            // Track peak message rate
            if (stats.messagesPerSecond > sessionLog.peakMsgRate) {
                sessionLog.peakMsgRate = stats.messagesPerSecond;
            }

            updateStatsDisplay();
        }

        // Signal quality tracking
        let signalStats = {
            goodMessages: 0,
            errorMessages: 0
        };

        function updateStatsDisplay() {
            const aircraftCount = Object.keys(aircraft).length;

            // Track max concurrent
            if (aircraftCount > sessionLog.maxConcurrent) {
                sessionLog.maxConcurrent = aircraftCount;
            }

            // Calculate live stats from current aircraft
            let highest = 0, fastest = 0, closest = Infinity;
            let highestIcao = '', fastestIcao = '', closestIcao = '';

            Object.entries(aircraft).forEach(([icao, ac]) => {
                if (ac.altitude && ac.altitude > highest) {
                    highest = ac.altitude;
                    highestIcao = icao;
                }
                if (ac.speed && ac.speed > fastest) {
                    fastest = ac.speed;
                    fastestIcao = icao;
                }
                if (ac.lat && ac.lon) {
                    const dist = calculateDistanceNm(
                        observerLocation.lat, observerLocation.lon,
                        ac.lat, ac.lon
                    );
                    if (dist < closest) {
                        closest = dist;
                        closestIcao = icao;
                    }
                }
            });

            // Update strip stats
            document.getElementById('stripAircraftNow').textContent = aircraftCount;
            document.getElementById('stripTotalSeen').textContent = stats.totalAircraftSeen.size;
            document.getElementById('stripMaxRange').textContent = stats.maxRange.toFixed(0);
            document.getElementById('stripHighest').textContent = highest > 0 ? Math.round(highest / 100) : '-';
            document.getElementById('stripFastest').textContent = fastest > 0 ? Math.round(fastest) : '-';
            document.getElementById('stripClosest').textContent = closest < Infinity ? closest.toFixed(1) : '-';
            document.getElementById('stripCountries').textContent = stats.countriesSeen.size;
            document.getElementById('stripAcars').textContent = stats.acarsMessages;

            // Update signal quality
            updateSignalQuality();
        }

        // Session timer
        let sessionTimerInterval = null;
        function startSessionTimer() {
            if (!stats.sessionStart) {
                stats.sessionStart = Date.now();
                sessionLog.startTime = new Date().toISOString();
            }
            if (sessionTimerInterval) clearInterval(sessionTimerInterval);
            sessionTimerInterval = setInterval(updateSessionTimer, 1000);
        }

        function stopSessionTimer() {
            sessionLog.endTime = new Date().toISOString();
        }

        function updateSessionTimer() {
            if (!stats.sessionStart) return;
            const elapsed = Date.now() - stats.sessionStart;
            const hours = Math.floor(elapsed / 3600000);
            const mins = Math.floor((elapsed % 3600000) / 60000);
            const secs = Math.floor((elapsed % 60000) / 1000);
            document.getElementById('stripSession').textContent =
                `${hours.toString().padStart(2,'0')}:${mins.toString().padStart(2,'0')}:${secs.toString().padStart(2,'0')}`;
        }

        // Report generation
        function generateReport() {
            stopSessionTimer();

            const report = {
                title: 'ADS-B Session Report',
                generated: new Date().toISOString(),
                session: {
                    start: sessionLog.startTime,
                    end: sessionLog.endTime || new Date().toISOString(),
                    duration: stats.sessionStart ? formatDuration(Date.now() - stats.sessionStart) : 'N/A'
                },
                location: {
                    lat: observerLocation.lat,
                    lon: observerLocation.lon
                },
                statistics: {
                    totalAircraftSeen: stats.totalAircraftSeen.size,
                    maxConcurrent: sessionLog.maxConcurrent,
                    maxRange: stats.maxRange.toFixed(1) + ' nm',
                    peakMessageRate: sessionLog.peakMsgRate.toFixed(1) + ' msg/s',
                    countriesSeen: Array.from(stats.countriesSeen).sort(),
                    acarsMessages: stats.acarsMessages
                },
                highlights: sessionLog.highlights,
                aircraftLog: sessionLog.aircraftLog
            };

            // Show report modal
            showReportModal(report);
        }

        function formatDuration(ms) {
            const hours = Math.floor(ms / 3600000);
            const mins = Math.floor((ms % 3600000) / 60000);
            const secs = Math.floor((ms % 60000) / 1000);
            return `${hours}h ${mins}m ${secs}s`;
        }

        function showReportModal(report) {
            const modal = document.createElement('div');
            modal.className = 'report-modal';
            modal.innerHTML = `
                <div class="report-content">
                    <div class="report-header">
                        <h2>📊 Session Report</h2>
                        <button class="report-close" onclick="this.closest('.report-modal').remove()">×</button>
                    </div>
                    <div class="report-body">
                        <div class="report-section">
                            <h3>Session Info</h3>
                            <div class="report-grid">
                                <span>Duration:</span><span>${report.session.duration}</span>
                                <span>Location:</span><span>${report.location.lat.toFixed(4)}, ${report.location.lon.toFixed(4)}</span>
                            </div>
                        </div>
                        <div class="report-section">
                            <h3>Statistics</h3>
                            <div class="report-grid">
                                <span>Total Aircraft:</span><span>${report.statistics.totalAircraftSeen}</span>
                                <span>Max Concurrent:</span><span>${report.statistics.maxConcurrent}</span>
                                <span>Max Range:</span><span>${report.statistics.maxRange}</span>
                                <span>Peak Msg Rate:</span><span>${report.statistics.peakMessageRate}</span>
                                <span>Countries:</span><span>${report.statistics.countriesSeen.length} (${report.statistics.countriesSeen.slice(0,5).join(', ')}${report.statistics.countriesSeen.length > 5 ? '...' : ''})</span>
                                <span>ACARS Messages:</span><span>${report.statistics.acarsMessages}</span>
                            </div>
                        </div>
                        ${report.highlights.length > 0 ? `
                        <div class="report-section">
                            <h3>Highlights</h3>
                            <div class="report-highlights">
                                ${report.highlights.slice(0, 10).map(h => `
                                    <div class="highlight-item ${h.type}">
                                        <span class="highlight-type">${h.type.toUpperCase()}</span>
                                        <span class="highlight-detail">${h.callsign}${h.country ? ' (' + h.country + ')' : ''}${h.name ? ' - ' + h.name : ''}</span>
                                    </div>
                                `).join('')}
                                ${report.highlights.length > 10 ? `<div class="highlight-more">+${report.highlights.length - 10} more...</div>` : ''}
                            </div>
                        </div>
                        ` : ''}
                        <div class="report-section">
                            <h3>Aircraft Log (${report.aircraftLog.length})</h3>
                            <div class="report-table-wrap">
                                <table class="report-table">
                                    <thead>
                                        <tr><th>ICAO</th><th>Callsign</th><th>Country</th><th>Type</th></tr>
                                    </thead>
                                    <tbody>
                                        ${report.aircraftLog.slice(0, 50).map(ac => `
                                            <tr class="${ac.military ? 'military' : ''}">
                                                <td>${ac.icao}</td>
                                                <td>${ac.callsign || '-'}</td>
                                                <td>${ac.country}</td>
                                                <td>${ac.military ? '🎖️ MIL' : 'CIV'}</td>
                                            </tr>
                                        `).join('')}
                                    </tbody>
                                </table>
                                ${report.aircraftLog.length > 50 ? `<div class="report-more">Showing 50 of ${report.aircraftLog.length} aircraft</div>` : ''}
                            </div>
                        </div>
                    </div>
                    <div class="report-footer">
                        <button class="report-btn" onclick="downloadReport()">💾 Download JSON</button>
                        <button class="report-btn" onclick="copyReportToClipboard()">📋 Copy Summary</button>
                    </div>
                </div>
            `;
            document.body.appendChild(modal);

            // Store report for download
            window._currentReport = report;
        }

        function downloadReport() {
            if (!window._currentReport) return;
            const blob = new Blob([JSON.stringify(window._currentReport, null, 2)], { type: 'application/json' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = `adsb-report-${new Date().toISOString().slice(0,10)}.json`;
            a.click();
            URL.revokeObjectURL(url);
        }

        function copyReportToClipboard() {
            if (!window._currentReport) return;
            const r = window._currentReport;
            const summary = `ADS-B Session Report
Duration: ${r.session.duration}
Aircraft Seen: ${r.statistics.totalAircraftSeen}
Max Concurrent: ${r.statistics.maxConcurrent}
Max Range: ${r.statistics.maxRange}
Countries: ${r.statistics.countriesSeen.join(', ')}
Highlights: ${r.highlights.length} events
ACARS: ${r.statistics.acarsMessages} messages`;
            navigator.clipboard.writeText(summary).then(() => {
                alert('Summary copied to clipboard');
            });
        }

        // ============================================
        // SIGNAL QUALITY
        // ============================================
        function updateSignalQuality() {
            const msgRate = stats.messagesPerSecond;
            const el = document.getElementById('stripSignal');
            const stat = el.closest('.strip-stat');

            if (!isTracking || msgRate === 0) {
                el.textContent = '--';
                stat.classList.remove('good', 'warning', 'poor');
                return;
            }

            // Signal quality based on message rate
            // Good: >10 msg/s, Warning: 2-10, Poor: <2
            if (msgRate >= 10) {
                el.textContent = '●●●';
                stat.classList.remove('warning', 'poor');
                stat.classList.add('good');
            } else if (msgRate >= 2) {
                el.textContent = '●●○';
                stat.classList.remove('good', 'poor');
                stat.classList.add('warning');
            } else {
                el.textContent = '●○○';
                stat.classList.remove('good', 'warning');
                stat.classList.add('poor');
            }
        }

        // ============================================
        // FLIGHT LOOKUP
        // ============================================
        function lookupSelectedFlight() {
            if (!selectedIcao || !aircraft[selectedIcao]) return;
            const ac = aircraft[selectedIcao];
            const callsign = ac.callsign?.trim();
            const reg = ac.registration?.trim();

            // Prefer callsign, then registration, then ICAO
            let searchTerm = callsign || reg || selectedIcao;

            // Open FlightAware search
            const url = `https://flightaware.com/live/flight/${searchTerm}`;
            window.open(url, '_blank');
        }

        function updateFlightLookupBtn() {
            const btn = document.getElementById('flightLookupBtn');
            if (selectedIcao && aircraft[selectedIcao]) {
                btn.disabled = false;
                const ac = aircraft[selectedIcao];
                const label = ac.callsign || ac.registration || selectedIcao;
                btn.title = `Lookup ${label} on FlightAware`;
            } else {
                btn.disabled = true;
                btn.title = 'Select an aircraft first';
            }
        }

        // ============================================
        // AIRCRAFT TRAILS
        // ============================================
        function toggleTrails() {
            showTrails = document.getElementById('showTrails').checked;
            if (!showTrails) {
                // Remove all trail lines from map
                Object.keys(trailLines).forEach(icao => {
                    if (trailLines[icao]) {
                        trailLines[icao].forEach(line => radarMap.removeLayer(line));
                        delete trailLines[icao];
                    }
                });
            } else {
                // Draw existing trails
                Object.keys(aircraftTrails).forEach(icao => {
                    updateTrailLine(icao);
                });
            }
        }

        function recordTrailPoint(icao, lat, lon, alt) {
            if (!aircraftTrails[icao]) aircraftTrails[icao] = [];
            const trail = aircraftTrails[icao];

            // Only add if moved significantly
            if (trail.length === 0 ||
                Math.abs(trail[trail.length-1].lat - lat) > 0.0005 ||
                Math.abs(trail[trail.length-1].lon - lon) > 0.0005) {
                trail.push({ lat, lon, alt: alt || 0, time: Date.now() });
                if (trail.length > MAX_TRAIL_POINTS) trail.shift();
            }
        }

        function getAltitudeColor(alt) {
            if (!alt || alt <= 0) return '#888888';
            if (alt < 10000) return '#00ff88';   // Green - low
            if (alt < 25000) return '#00d4ff';   // Cyan - medium
            if (alt < 35000) return '#ffcc00';   // Yellow - high
            return '#ff9500';                    // Orange - very high
        }

        function updateTrailLine(icao) {
            if (!showTrails || !radarMap) return;

            const trail = aircraftTrails[icao];
            if (!trail || trail.length < 2) return;

            // Remove old trail lines
            if (trailLines[icao]) {
                trailLines[icao].forEach(line => radarMap.removeLayer(line));
            }
            trailLines[icao] = [];

            // Create gradient segments
            const now = Date.now();
            for (let i = 1; i < trail.length; i++) {
                const p1 = trail[i-1];
                const p2 = trail[i];
                const age = (now - p2.time) / 1000;  // seconds
                const opacity = Math.max(0.2, 1 - (age / 120));  // Fade over 2 minutes

                const color = getAltitudeColor(p2.alt);
                const line = L.polyline([[p1.lat, p1.lon], [p2.lat, p2.lon]], {
                    color: color,
                    weight: 2,
                    opacity: opacity
                }).addTo(radarMap);
                trailLines[icao].push(line);
            }
        }

        function cleanupTrail(icao) {
            if (trailLines[icao]) {
                trailLines[icao].forEach(line => radarMap.removeLayer(line));
                delete trailLines[icao];
            }
            delete aircraftTrails[icao];
        }

        // ============================================
        // RADAR SCOPE (PPI)
        // ============================================
        class RadarScope {
            constructor(canvasId) {
                this.canvas = document.getElementById(canvasId);
                this.ctx = this.canvas.getContext('2d');
                this.sweepAngle = 0;
                this.blips = [];  // Aircraft blips with afterglow
                this.resize();
                window.addEventListener('resize', () => this.resize());
            }

            resize() {
                const container = this.canvas.parentElement;
                const size = Math.min(container.clientWidth, container.clientHeight) - 40;
                this.canvas.width = size;
                this.canvas.height = size;
                this.centerX = size / 2;
                this.centerY = size / 2;
                this.radius = (size / 2) - 30;
            }

            draw() {
                const ctx = this.ctx;
                const w = this.canvas.width;
                const h = this.canvas.height;

                // Clear
                ctx.fillStyle = '#1a1a2e';
                ctx.fillRect(0, 0, w, h);

                // Draw range rings
                this.drawRangeRings();

                // Draw compass rose
                this.drawCompassRose();

                // Draw aircraft blips
                this.drawBlips();

                // Draw sweep line
                this.drawSweep();

                // Draw center point (observer)
                ctx.beginPath();
                ctx.arc(this.centerX, this.centerY, 4, 0, Math.PI * 2);
                ctx.fillStyle = '#ffff00';
                ctx.fill();
            }

            drawRangeRings() {
                const ctx = this.ctx;
                const rings = [0.25, 0.5, 0.75, 1.0];

                ctx.strokeStyle = 'rgba(0, 255, 255, 0.2)';
                ctx.lineWidth = 1;

                rings.forEach((ratio, i) => {
                    const r = this.radius * ratio;
                    ctx.beginPath();
                    ctx.arc(this.centerX, this.centerY, r, 0, Math.PI * 2);
                    ctx.stroke();

                    // Range label
                    const rangeNm = Math.round(maxRange * ratio);
                    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
                    ctx.font = '10px JetBrains Mono';
                    ctx.fillText(`${rangeNm}`, this.centerX + r + 5, this.centerY + 4);
                });
            }

            drawCompassRose() {
                const ctx = this.ctx;
                const directions = [
                    { angle: 0, label: 'N' },
                    { angle: 90, label: 'E' },
                    { angle: 180, label: 'S' },
                    { angle: 270, label: 'W' }
                ];

                ctx.fillStyle = '#00ffff';
                ctx.font = 'bold 12px Orbitron';
                ctx.textAlign = 'center';
                ctx.textBaseline = 'middle';

                directions.forEach(d => {
                    const rad = (d.angle - 90) * Math.PI / 180;
                    const x = this.centerX + (this.radius + 15) * Math.cos(rad);
                    const y = this.centerY + (this.radius + 15) * Math.sin(rad);
                    ctx.fillText(d.label, x, y);
                });

                // Draw tick marks every 30 degrees
                ctx.strokeStyle = 'rgba(0, 255, 255, 0.3)';
                ctx.lineWidth = 1;
                for (let angle = 0; angle < 360; angle += 30) {
                    const rad = (angle - 90) * Math.PI / 180;
                    const inner = this.radius - 5;
                    const outer = this.radius + 2;
                    ctx.beginPath();
                    ctx.moveTo(
                        this.centerX + inner * Math.cos(rad),
                        this.centerY + inner * Math.sin(rad)
                    );
                    ctx.lineTo(
                        this.centerX + outer * Math.cos(rad),
                        this.centerY + outer * Math.sin(rad)
                    );
                    ctx.stroke();
                }
            }

            drawSweep() {
                const ctx = this.ctx;
                const rad = (this.sweepAngle - 90) * Math.PI / 180;

                // Sweep line with gradient
                const gradient = ctx.createLinearGradient(
                    this.centerX, this.centerY,
                    this.centerX + this.radius * Math.cos(rad),
                    this.centerY + this.radius * Math.sin(rad)
                );
                gradient.addColorStop(0, 'rgba(0, 255, 255, 0.8)');
                gradient.addColorStop(1, 'rgba(0, 255, 255, 0.1)');

                ctx.beginPath();
                ctx.moveTo(this.centerX, this.centerY);
                ctx.lineTo(
                    this.centerX + this.radius * Math.cos(rad),
                    this.centerY + this.radius * Math.sin(rad)
                );
                ctx.strokeStyle = gradient;
                ctx.lineWidth = 2;
                ctx.stroke();

                // Sweep arc (afterglow)
                const startAngle = (this.sweepAngle - 90 - 30) * Math.PI / 180;
                const endAngle = (this.sweepAngle - 90) * Math.PI / 180;
                const arcGradient = ctx.createConicGradient(startAngle, this.centerX, this.centerY);
                arcGradient.addColorStop(0, 'rgba(0, 255, 255, 0)');
                arcGradient.addColorStop(1, 'rgba(0, 255, 255, 0.15)');

                ctx.beginPath();
                ctx.moveTo(this.centerX, this.centerY);
                ctx.arc(this.centerX, this.centerY, this.radius, startAngle, endAngle);
                ctx.closePath();
                ctx.fillStyle = arcGradient;
                ctx.fill();

                // Update sweep angle
                this.sweepAngle = (this.sweepAngle + 2) % 360;
            }

            drawBlips() {
                const ctx = this.ctx;
                const now = Date.now();

                // Update blips from aircraft data
                this.blips = [];
                Object.entries(aircraft).forEach(([icao, ac]) => {
                    if (!ac.lat || !ac.lon) return;
                    if (!passesFilter(icao, ac)) return;

                    const distance = calculateDistanceNm(
                        observerLocation.lat, observerLocation.lon,
                        ac.lat, ac.lon
                    );

                    if (distance > maxRange) return;

                    const bearing = calculateBearing(
                        observerLocation.lat, observerLocation.lon,
                        ac.lat, ac.lon
                    );

                    const ratio = distance / maxRange;
                    const rad = (bearing - 90) * Math.PI / 180;
                    const x = this.centerX + (this.radius * ratio) * Math.cos(rad);
                    const y = this.centerY + (this.radius * ratio) * Math.sin(rad);

                    this.blips.push({
                        x, y,
                        icao,
                        callsign: ac.callsign,
                        altitude: ac.altitude,
                        selected: icao === selectedIcao
                    });
                });

                // Draw blips
                this.blips.forEach(blip => {
                    // Blip glow
                    const gradient = ctx.createRadialGradient(blip.x, blip.y, 0, blip.x, blip.y, 12);
                    gradient.addColorStop(0, blip.selected ? 'rgba(255, 255, 0, 0.8)' : 'rgba(0, 255, 255, 0.8)');
                    gradient.addColorStop(1, 'rgba(0, 255, 255, 0)');
                    ctx.fillStyle = gradient;
                    ctx.fillRect(blip.x - 12, blip.y - 12, 24, 24);

                    // Blip dot
                    ctx.beginPath();
                    ctx.arc(blip.x, blip.y, blip.selected ? 5 : 3, 0, Math.PI * 2);
                    ctx.fillStyle = blip.selected ? '#ffff00' : '#00ffff';
                    ctx.fill();

                    // Label
                    if (blip.callsign || blip.selected) {
                        ctx.fillStyle = '#00ffff';
                        ctx.font = '9px JetBrains Mono';
                        ctx.textAlign = 'left';
                        ctx.fillText(blip.callsign || blip.icao, blip.x + 8, blip.y - 5);
                        if (blip.altitude) {
                            ctx.fillText(`${Math.round(blip.altitude/100)}`, blip.x + 8, blip.y + 7);
                        }
                    }
                });
            }

            handleClick(event) {
                const rect = this.canvas.getBoundingClientRect();
                const x = event.clientX - rect.left;
                const y = event.clientY - rect.top;

                // Find clicked blip
                for (const blip of this.blips) {
                    const dx = x - blip.x;
                    const dy = y - blip.y;
                    if (dx * dx + dy * dy < 100) {  // 10px radius
                        selectAircraft(blip.icao);
                        return;
                    }
                }
            }
        }

        function startRadarAnimation() {
            if (radarAnimationId) return;
            function animate() {
                if (currentView === 'radar' && radarScope) {
                    radarScope.draw();
                }
                radarAnimationId = requestAnimationFrame(animate);
            }
            animate();
        }

        function stopRadarAnimation() {
            if (radarAnimationId) {
                cancelAnimationFrame(radarAnimationId);
                radarAnimationId = null;
            }
        }

        // ============================================
        // RADAR OVERLAY ON MAP
        // ============================================
        let radarOverlayEnabled = false;
        let radarOverlayAnimationId = null;
        let radarOverlaySweepAngle = 0;

        function toggleRadarOverlay() {
            radarOverlayEnabled = document.getElementById('radarOverlay').checked;
            const canvas = document.getElementById('radarOverlayCanvas');

            if (radarOverlayEnabled) {
                canvas.classList.add('active');
                startRadarOverlayAnimation();
            } else {
                canvas.classList.remove('active');
                stopRadarOverlayAnimation();
            }
        }

        function startRadarOverlayAnimation() {
            if (radarOverlayAnimationId) return;

            const canvas = document.getElementById('radarOverlayCanvas');
            const ctx = canvas.getContext('2d');

            function animate() {
                if (radarOverlayEnabled && currentView === 'map') {
                    drawRadarOverlay(canvas, ctx);
                }
                radarOverlayAnimationId = requestAnimationFrame(animate);
            }
            animate();
        }

        function stopRadarOverlayAnimation() {
            if (radarOverlayAnimationId) {
                cancelAnimationFrame(radarOverlayAnimationId);
                radarOverlayAnimationId = null;
            }
        }

        function drawRadarOverlay(canvas, ctx) {
            const container = canvas.parentElement;
            const w = container.clientWidth;
            const h = container.clientHeight;

            // Resize canvas if needed
            if (canvas.width !== w || canvas.height !== h) {
                canvas.width = w;
                canvas.height = h;
            }

            const centerX = w / 2;
            const centerY = h / 2;
            const radius = Math.min(w, h) / 2 - 20;

            // Clear with transparency
            ctx.clearRect(0, 0, w, h);

            // Draw range rings
            ctx.strokeStyle = 'rgba(0, 255, 255, 0.15)';
            ctx.lineWidth = 1;
            const rings = [0.25, 0.5, 0.75, 1.0];
            rings.forEach((ratio, i) => {
                const r = radius * ratio;
                ctx.beginPath();
                ctx.arc(centerX, centerY, r, 0, Math.PI * 2);
                ctx.stroke();

                // Range label
                const rangeNm = Math.round(maxRange * ratio);
                ctx.fillStyle = 'rgba(0, 255, 255, 0.4)';
                ctx.font = '10px JetBrains Mono';
                ctx.fillText(`${rangeNm}nm`, centerX + r + 5, centerY);
            });

            // Draw compass directions
            ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
            ctx.font = 'bold 14px Orbitron';
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';

            const directions = [
                { angle: 0, label: 'N', x: centerX, y: centerY - radius - 12 },
                { angle: 90, label: 'E', x: centerX + radius + 12, y: centerY },
                { angle: 180, label: 'S', x: centerX, y: centerY + radius + 12 },
                { angle: 270, label: 'W', x: centerX - radius - 12, y: centerY }
            ];
            directions.forEach(d => ctx.fillText(d.label, d.x, d.y));

            // Draw tick marks every 30 degrees
            ctx.strokeStyle = 'rgba(0, 255, 255, 0.2)';
            for (let angle = 0; angle < 360; angle += 30) {
                const rad = (angle - 90) * Math.PI / 180;
                const inner = radius - 8;
                const outer = radius + 3;
                ctx.beginPath();
                ctx.moveTo(centerX + inner * Math.cos(rad), centerY + inner * Math.sin(rad));
                ctx.lineTo(centerX + outer * Math.cos(rad), centerY + outer * Math.sin(rad));
                ctx.stroke();
            }

            // Draw sweep line
            const sweepRad = (radarOverlaySweepAngle - 90) * Math.PI / 180;
            const gradient = ctx.createLinearGradient(
                centerX, centerY,
                centerX + radius * Math.cos(sweepRad),
                centerY + radius * Math.sin(sweepRad)
            );
            gradient.addColorStop(0, 'rgba(0, 255, 255, 0.6)');
            gradient.addColorStop(1, 'rgba(0, 255, 255, 0.1)');

            ctx.beginPath();
            ctx.moveTo(centerX, centerY);
            ctx.lineTo(
                centerX + radius * Math.cos(sweepRad),
                centerY + radius * Math.sin(sweepRad)
            );
            ctx.strokeStyle = gradient;
            ctx.lineWidth = 2;
            ctx.stroke();

            // Draw sweep arc (afterglow)
            const startAngle = (radarOverlaySweepAngle - 90 - 40) * Math.PI / 180;
            const endAngle = (radarOverlaySweepAngle - 90) * Math.PI / 180;

            ctx.beginPath();
            ctx.moveTo(centerX, centerY);
            ctx.arc(centerX, centerY, radius, startAngle, endAngle);
            ctx.closePath();

            const arcGradient = ctx.createConicGradient(startAngle, centerX, centerY);
            arcGradient.addColorStop(0, 'rgba(0, 255, 255, 0)');
            arcGradient.addColorStop(1, 'rgba(0, 255, 255, 0.1)');
            ctx.fillStyle = arcGradient;
            ctx.fill();

            // Draw center point
            ctx.beginPath();
            ctx.arc(centerX, centerY, 4, 0, Math.PI * 2);
            ctx.fillStyle = 'rgba(255, 255, 0, 0.8)';
            ctx.fill();
            ctx.beginPath();
            ctx.arc(centerX, centerY, 6, 0, Math.PI * 2);
            ctx.strokeStyle = 'rgba(255, 255, 0, 0.4)';
            ctx.lineWidth = 1;
            ctx.stroke();

            // Update sweep angle
            radarOverlaySweepAngle = (radarOverlaySweepAngle + 1.5) % 360;
        }

        // ============================================
        // VIEW TOGGLE
        // ============================================
        function setView(view) {
            currentView = view;
            const mapEl = document.getElementById('radarMap');
            const scopeEl = document.getElementById('radarScope');
            const mapBtn = document.getElementById('mapViewBtn');
            const radarBtn = document.getElementById('radarViewBtn');

            if (view === 'map') {
                mapEl.style.display = 'block';
                scopeEl.classList.remove('active');
                mapBtn.classList.add('active');
                radarBtn.classList.remove('active');
                stopRadarAnimation();
                // Invalidate map size after showing
                setTimeout(() => radarMap && radarMap.invalidateSize(), 100);
            } else {
                mapEl.style.display = 'none';
                scopeEl.classList.add('active');
                mapBtn.classList.remove('active');
                radarBtn.classList.add('active');
                if (!radarScope) {
                    radarScope = new RadarScope('radarCanvas');
                    document.getElementById('radarCanvas').addEventListener('click', (e) => radarScope.handleClick(e));
                }
                radarScope.resize();
                startRadarAnimation();
            }
        }

        function updateRange() {
            maxRange = parseInt(document.getElementById('rangeSelect').value);
            drawRangeRings();
        }

        // ============================================
        // RANGE RINGS (MAP)
        // ============================================
        function drawRangeRings() {
            if (!radarMap) return;

            if (rangeRingsLayer) {
                radarMap.removeLayer(rangeRingsLayer);
                rangeRingsLayer = null;
            }

            const showRings = document.getElementById('showRangeRings')?.checked;
            if (!showRings) return;

            rangeRingsLayer = L.layerGroup();

            const distances = [maxRange * 0.25, maxRange * 0.5, maxRange * 0.75, maxRange];
            distances.forEach(nm => {
                const meters = nm * 1852;
                const circle = L.circle([observerLocation.lat, observerLocation.lon], {
                    radius: meters,
                    color: '#00ff88',
                    fillColor: 'transparent',
                    fillOpacity: 0,
                    weight: 1,
                    opacity: 0.4,
                    dashArray: '5, 5'
                });

                const labelLat = observerLocation.lat + (nm * 0.0166);
                const label = L.marker([labelLat, observerLocation.lon], {
                    icon: L.divIcon({
                        className: 'range-label',
                        html: `<span style="color: #00ff88; font-size: 10px; background: rgba(0,0,0,0.7); padding: 1px 4px; border-radius: 2px;">${Math.round(nm)} nm</span>`,
                        iconSize: [40, 12],
                        iconAnchor: [20, 6]
                    })
                });

                rangeRingsLayer.addLayer(circle);
                rangeRingsLayer.addLayer(label);
            });

            // Observer marker
            if (observerMarker) radarMap.removeLayer(observerMarker);
            observerMarker = L.marker([observerLocation.lat, observerLocation.lon], {
                icon: L.divIcon({
                    className: 'observer-marker',
                    html: '<div style="width: 12px; height: 12px; background: #ff0; border: 2px solid #000; border-radius: 50%; box-shadow: 0 0 10px #ff0;"></div>',
                    iconSize: [12, 12],
                    iconAnchor: [6, 6]
                })
            }).bindPopup('Your Location').addTo(radarMap);

            rangeRingsLayer.addTo(radarMap);
        }

        function updateObserverLoc() {
            const lat = parseFloat(document.getElementById('obsLat').value);
            const lon = parseFloat(document.getElementById('obsLon').value);

            if (!isNaN(lat) && !isNaN(lon) && lat >= -90 && lat <= 90 && lon >= -180 && lon <= 180) {
                observerLocation.lat = lat;
                observerLocation.lon = lon;

                // Save to localStorage for persistence
                localStorage.setItem('observerLocation', JSON.stringify(observerLocation));

                if (radarMap) {
                    radarMap.setView([lat, lon], radarMap.getZoom());
                }
                drawRangeRings();
            }
        }

        function getGeolocation() {
            if (!navigator.geolocation) {
                alert('Geolocation not supported');
                return;
            }
            if (!window.isSecureContext) {
                alert('GPS requires HTTPS. Enter coordinates manually.');
                return;
            }

            const btn = document.getElementById('geolocateBtn');
            btn.textContent = '...';

            navigator.geolocation.getCurrentPosition(
                (position) => {
                    observerLocation.lat = position.coords.latitude;
                    observerLocation.lon = position.coords.longitude;

                    // Save to localStorage for persistence
                    localStorage.setItem('observerLocation', JSON.stringify(observerLocation));

                    document.getElementById('obsLat').value = observerLocation.lat.toFixed(4);
                    document.getElementById('obsLon').value = observerLocation.lon.toFixed(4);
                    if (radarMap) {
                        radarMap.setView([observerLocation.lat, observerLocation.lon], 8);
                    }
                    drawRangeRings();
                    btn.textContent = 'Locate';
                },
                (error) => {
                    alert('Location error: ' + error.message);
                    btn.textContent = 'Locate';
                },
                { enableHighAccuracy: true, timeout: 10000 }
            );
        }

        // ============================================
        // GPS FUNCTIONS (gpsd auto-connect)
        // ============================================
        async function autoConnectGps() {
            try {
                const response = await fetch('/gps/auto-connect', { method: 'POST' });
                const data = await response.json();

                if (data.status === 'connected') {
                    gpsConnected = true;
                    startGpsStream();
                    showGpsIndicator(true);
                    console.log('GPS: Auto-connected to gpsd');
                    if (data.position) {
                        updateLocationFromGps(data.position);
                    }
                } else {
                    console.log('GPS: gpsd not available -', data.message);
                }
            } catch (e) {
                console.log('GPS: Auto-connect failed -', e.message);
            }
        }

        let gpsReconnectTimeout = null;

        function startGpsStream() {
            if (gpsEventSource) {
                gpsEventSource.close();
            }
            if (gpsReconnectTimeout) {
                clearTimeout(gpsReconnectTimeout);
                gpsReconnectTimeout = null;
            }

            gpsEventSource = new EventSource('/gps/stream');
            gpsEventSource.onmessage = (event) => {
                try {
                    const data = JSON.parse(event.data);
                    if (data.type === 'position' && data.latitude && data.longitude) {
                        updateLocationFromGps(data);
                    }
                } catch (e) {
                    console.error('GPS parse error:', e);
                }
            };
            gpsEventSource.onerror = (e) => {
                // Don't log every error - connection suspends are normal
                if (gpsEventSource) {
                    gpsEventSource.close();
                    gpsEventSource = null;
                }
                // Auto-reconnect after 5 seconds if still connected
                if (gpsConnected && !gpsReconnectTimeout) {
                    gpsReconnectTimeout = setTimeout(() => {
                        gpsReconnectTimeout = null;
                        if (gpsConnected) {
                            startGpsStream();
                        }
                    }, 5000);
                }
            };
        }

        // Reconnect GPS stream when tab becomes visible
        document.addEventListener('visibilitychange', () => {
            if (!document.hidden && gpsConnected && !gpsEventSource) {
                startGpsStream();
            }
        });

        function updateLocationFromGps(position) {
            observerLocation.lat = position.latitude;
            observerLocation.lon = position.longitude;
            document.getElementById('obsLat').value = position.latitude.toFixed(4);
            document.getElementById('obsLon').value = position.longitude.toFixed(4);

            // Center map on GPS location (on first fix)
            if (radarMap && !radarMap._gpsInitialized) {
                radarMap.setView([position.latitude, position.longitude], radarMap.getZoom());
                radarMap._gpsInitialized = true;
                // Draw range rings immediately after centering
                drawRangeRings();
            } else {
                drawRangeRings();
            }
        }

        function showGpsIndicator(show) {
            const indicator = document.getElementById('gpsIndicator');
            if (indicator) {
                indicator.style.display = show ? 'inline-flex' : 'none';
            }
        }

        // ============================================
        // FILTERING
        // ============================================
        function applyFilter() {
            currentFilter = document.getElementById('aircraftFilter').value;
            // Clear markers and redraw
            Object.keys(markers).forEach(icao => {
                radarMap.removeLayer(markers[icao]);
                delete markers[icao];
            });
            Object.keys(markerState).forEach(icao => delete markerState[icao]);
            pendingMarkerUpdates.clear();
            Object.keys(aircraft).forEach(icao => {
                if (aircraft[icao].lat && aircraft[icao].lon) {
                    pendingMarkerUpdates.add(icao);
                }
            });
            scheduleUIUpdate();
        }

        function passesFilter(icao, ac) {
            if (currentFilter === 'all') return true;
            const militaryInfo = isMilitaryAircraft(icao, ac.callsign);
            const squawkInfo = checkSquawkCode(ac);
            if (currentFilter === 'military') return militaryInfo.military;
            if (currentFilter === 'civil') return !militaryInfo.military;
            if (currentFilter === 'emergency') return squawkInfo && squawkInfo.type === 'emergency';
            if (currentFilter === 'watchlist') return isOnWatchlist(ac);
            return true;
        }

        // ============================================
        // INITIALIZATION
        // ============================================
        document.addEventListener('DOMContentLoaded', () => {
            // Initialize observer location input fields from saved location
            const obsLatInput = document.getElementById('obsLat');
            const obsLonInput = document.getElementById('obsLon');
            if (obsLatInput) obsLatInput.value = observerLocation.lat.toFixed(4);
            if (obsLonInput) obsLonInput.value = observerLocation.lon.toFixed(4);

            initMap();
            initDeviceSelectors();
            updateClock();
            setInterval(updateClock, 1000);
            setInterval(cleanupOldAircraft, 10000);
            checkAdsbTools();
            checkAircraftDatabase();
            checkDvbDriverConflict();

            // Auto-connect to gpsd if available
            autoConnectGps();
        });

        // Track which device is being used for ADS-B tracking
        let adsbActiveDevice = null;

        function initDeviceSelectors() {
            // Populate both ADS-B and airband device selectors
            fetch('/devices')
                .then(r => r.json())
                .then(devices => {
                    const adsbSelect = document.getElementById('adsbDeviceSelect');
                    const airbandSelect = document.getElementById('airbandDeviceSelect');

                    // Clear loading state
                    adsbSelect.innerHTML = '';
                    airbandSelect.innerHTML = '';

                    if (devices.length === 0) {
                        adsbSelect.innerHTML = '<option value="0">No SDR found</option>';
                        airbandSelect.innerHTML = '<option value="0">No SDR found</option>';
                        airbandSelect.disabled = true;
                    } else {
                        devices.forEach((dev, i) => {
                            const idx = dev.index !== undefined ? dev.index : i;

                            // Build descriptive label
                            const type = dev.sdr_type || dev.driver || 'RTL-SDR';
                            const typeName = type.toUpperCase().replace('RTLSDR', 'RTL-SDR');
                            const shortSerial = dev.serial ? ` (${dev.serial.slice(-4)})` : '';
                            const displayName = `${typeName} #${idx}${shortSerial}`;
                            const fullName = dev.name || `${typeName} Device ${idx}`;
                            const tooltip = `${fullName}${dev.serial ? ' - Serial: ' + dev.serial : ''}`;

                            // Add to ADS-B selector
                            const adsbOpt = document.createElement('option');
                            adsbOpt.value = idx;
                            adsbOpt.textContent = displayName;
                            adsbOpt.title = tooltip;
                            adsbSelect.appendChild(adsbOpt);

                            // Add to Airband selector
                            const airbandOpt = document.createElement('option');
                            airbandOpt.value = idx;
                            airbandOpt.textContent = displayName;
                            airbandOpt.title = tooltip;
                            airbandSelect.appendChild(airbandOpt);
                        });

                        // Default: ADS-B uses first device, Airband uses second (if available)
                        adsbSelect.value = devices[0].index !== undefined ? devices[0].index : 0;
                        if (devices.length > 1) {
                            airbandSelect.value = devices[1].index !== undefined ? devices[1].index : 1;
                        }

                        // Show warning if only one device
                        if (devices.length === 1) {
                            document.getElementById('airbandStatus').textContent = '1 SDR only';
                            document.getElementById('airbandStatus').style.color = 'var(--accent-orange)';
                        }
                    }
                })
                .catch(() => {
                    document.getElementById('adsbDeviceSelect').innerHTML = '<option value="0">Error</option>';
                    document.getElementById('airbandDeviceSelect').innerHTML = '<option value="0">Error</option>';
                });
        }

        function checkDvbDriverConflict() {
            fetch('/settings/rtlsdr/driver-status')
                .then(r => r.json())
                .then(data => {
                    if (data.issue_detected) {
                        showDvbDriverWarning(data.loaded_modules);
                    }
                })
                .catch(() => {});
        }

        function showDvbDriverWarning(loadedModules) {
            // Don't show if already dismissed this session
            if (sessionStorage.getItem('dvb_warning_dismissed')) return;

            const warning = document.createElement('div');
            warning.id = 'dvbDriverWarning';
            warning.style.cssText = `
                position: fixed;
                top: 70px;
                left: 50%;
                transform: translateX(-50%);
                background: rgba(239, 68, 68, 0.95);
                color: white;
                padding: 15px 20px;
                border-radius: 8px;
                font-size: 13px;
                z-index: 10000;
                box-shadow: 0 4px 20px rgba(0,0,0,0.5);
                max-width: 500px;
                text-align: center;
            `;

            warning.innerHTML = `
                <div style="font-weight: bold; margin-bottom: 8px;">⚠️ DVB Driver Conflict Detected</div>
                <div style="margin-bottom: 10px;">
                    Kernel DVB drivers are claiming your RTL-SDR devices, preventing them from working properly.
                    <br><small>Loaded modules: ${loadedModules.join(', ')}</small>
                </div>
                <div style="display: flex; gap: 10px; justify-content: center;">
                    <button onclick="fixDvbDrivers()" style="background: white; color: #dc2626; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; font-weight: bold;">
                        Fix Now
                    </button>
                    <button onclick="dismissDvbWarning()" style="background: rgba(255,255,255,0.2); color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer;">
                        Dismiss
                    </button>
                </div>
            `;
            document.body.appendChild(warning);
        }

        function fixDvbDrivers() {
            const warning = document.getElementById('dvbDriverWarning');
            if (warning) {
                warning.innerHTML = '<div>Applying fix...</div>';
            }

            fetch('/settings/rtlsdr/blacklist-drivers', { method: 'POST' })
                .then(r => r.json())
                .then(data => {
                    if (data.status === 'success' || data.status === 'partial') {
                        if (warning) {
                            warning.style.background = 'rgba(34, 197, 94, 0.95)';
                            warning.innerHTML = `
                                <div style="font-weight: bold;">✓ DVB Drivers Disabled</div>
                                <div style="margin-top: 8px;">${data.message}</div>
                                <button onclick="this.parentElement.remove()" style="margin-top: 10px; background: white; color: #16a34a; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer;">
                                    OK
                                </button>
                            `;
                        }
                    } else {
                        if (warning) {
                            warning.innerHTML = `
                                <div style="font-weight: bold;">Manual Fix Required</div>
                                <div style="margin-top: 8px;">${data.message}</div>
                                <button onclick="this.parentElement.remove()" style="margin-top: 10px; background: rgba(255,255,255,0.2); color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer;">
                                    Close
                                </button>
                            `;
                        }
                    }
                })
                .catch(err => {
                    if (warning) {
                        warning.innerHTML = `
                            <div>Error: ${err.message}</div>
                            <button onclick="this.parentElement.remove()" style="margin-top: 10px; background: rgba(255,255,255,0.2); color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer;">
                                Close
                            </button>
                        `;
                    }
                });
        }

        function dismissDvbWarning() {
            sessionStorage.setItem('dvb_warning_dismissed', 'true');
            const warning = document.getElementById('dvbDriverWarning');
            if (warning) warning.remove();
        }

        function checkAdsbTools() {
            fetch('/adsb/tools')
                .then(r => r.json())
                .then(data => {
                    if (data.needs_readsb) {
                        showReadsbWarning(data.soapy_types);
                    }
                })
                .catch(() => {});
        }

        // ============================================
        // AIRCRAFT DATABASE
        // ============================================
        let aircraftDbStatus = { installed: false };

        function checkAircraftDatabase() {
            fetch('/adsb/aircraft-db/status')
                .then(r => r.json())
                .then(status => {
                    aircraftDbStatus = status;
                    if (!status.installed) {
                        showAircraftDbBanner('not_installed');
                    } else {
                        // Check for updates in background
                        fetch('/adsb/aircraft-db/check-updates')
                            .then(r => r.json())
                            .then(data => {
                                if (data.update_available) {
                                    showAircraftDbBanner('update_available', data.latest_version);
                                }
                            })
                            .catch(() => {});
                    }
                })
                .catch(() => {});
        }

        function showAircraftDbBanner(type, version) {
            // Remove any existing banner
            const existing = document.getElementById('aircraftDbBanner');
            if (existing) existing.remove();

            const banner = document.createElement('div');
            banner.id = 'aircraftDbBanner';
            banner.style.cssText = `
                position: fixed;
                top: 70px;
                right: 20px;
                background: ${type === 'not_installed' ? 'rgba(59, 130, 246, 0.95)' : 'rgba(34, 197, 94, 0.95)'};
                color: white;
                padding: 12px 16px;
                border-radius: 8px;
                font-size: 12px;
                z-index: 10000;
                box-shadow: 0 4px 20px rgba(0,0,0,0.3);
                max-width: 320px;
                font-family: 'Inter', sans-serif;
            `;

            if (type === 'not_installed') {
                banner.innerHTML = `
                    <div style="font-weight: bold; margin-bottom: 6px;">Aircraft Database Not Installed</div>
                    <div style="margin-bottom: 10px; font-size: 11px; opacity: 0.9;">Download to see aircraft types, registrations, and model info.</div>
                    <button onclick="downloadAircraftDb()" style="background: white; color: #3b82f6; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-weight: 500; font-size: 11px;">Download Database</button>
                    <button onclick="this.parentElement.remove()" style="position: absolute; top: 6px; right: 8px; background: none; border: none; color: white; cursor: pointer; font-size: 14px;">×</button>
                `;
            } else {
                banner.innerHTML = `
                    <div style="font-weight: bold; margin-bottom: 6px;">Database Update Available</div>
                    <div style="margin-bottom: 10px; font-size: 11px; opacity: 0.9;">New version: ${version || 'latest'}</div>
                    <button onclick="downloadAircraftDb()" style="background: white; color: #22c55e; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-weight: 500; font-size: 11px;">Update Now</button>
                    <button onclick="this.parentElement.remove()" style="position: absolute; top: 6px; right: 8px; background: none; border: none; color: white; cursor: pointer; font-size: 14px;">×</button>
                `;
            }

            document.body.appendChild(banner);
        }

        function downloadAircraftDb() {
            const banner = document.getElementById('aircraftDbBanner');
            if (banner) {
                banner.innerHTML = `
                    <div style="font-weight: bold;">Downloading...</div>
                    <div style="font-size: 11px; opacity: 0.9;">This may take a moment</div>
                `;
            }

            fetch('/adsb/aircraft-db/download', { method: 'POST' })
                .then(r => r.json())
                .then(data => {
                    if (data.success) {
                        if (banner) {
                            banner.style.background = 'rgba(34, 197, 94, 0.95)';
                            banner.innerHTML = `
                                <div style="font-weight: bold;">Database Installed</div>
                                <div style="font-size: 11px; opacity: 0.9;">${data.message}</div>
                            `;
                            setTimeout(() => banner.remove(), 3000);
                        }
                        aircraftDbStatus.installed = true;
                    } else {
                        if (banner) {
                            banner.style.background = 'rgba(239, 68, 68, 0.95)';
                            banner.innerHTML = `
                                <div style="font-weight: bold;">Download Failed</div>
                                <div style="font-size: 11px;">${data.error || 'Unknown error'}</div>
                                <button onclick="this.parentElement.remove()" style="position: absolute; top: 6px; right: 8px; background: none; border: none; color: white; cursor: pointer; font-size: 14px;">×</button>
                            `;
                        }
                    }
                })
                .catch(err => {
                    if (banner) {
                        banner.style.background = 'rgba(239, 68, 68, 0.95)';
                        banner.innerHTML = `
                            <div style="font-weight: bold;">Download Failed</div>
                            <div style="font-size: 11px;">${err.message}</div>
                            <button onclick="this.parentElement.remove()" style="position: absolute; top: 6px; right: 8px; background: none; border: none; color: white; cursor: pointer; font-size: 14px;">×</button>
                        `;
                    }
                });
        }

        function showReadsbWarning(sdrTypes) {
            const typeList = sdrTypes.join(', ') || 'SoapySDR device';
            const warning = document.createElement('div');
            warning.id = 'readsbWarning';
            warning.style.cssText = `
                position: fixed;
                bottom: 80px;
                left: 50%;
                transform: translateX(-50%);
                background: rgba(245, 158, 11, 0.95);
                color: #000;
                padding: 15px 25px;
                border-radius: 8px;
                font-size: 12px;
                z-index: 10000;
                box-shadow: 0 4px 20px rgba(0,0,0,0.5);
                max-width: 500px;
                text-align: left;
                font-family: 'Inter', sans-serif;
            `;
            warning.innerHTML = `
                <div style="font-weight: bold; margin-bottom: 8px;">⚠️ ${typeList} Detected - readsb Required</div>
                <div style="margin-bottom: 10px;">ADS-B tracking with ${typeList} requires <strong>readsb</strong> compiled with SoapySDR support.</div>
                <details style="font-size: 11px;">
                    <summary style="cursor: pointer; margin-bottom: 8px;">Installation Instructions</summary>
                    <div style="background: rgba(0,0,0,0.1); padding: 10px; border-radius: 4px; margin-top: 5px;">
                        <code style="display: block; white-space: pre-wrap; font-family: 'JetBrains Mono', monospace; font-size: 10px;">sudo apt install build-essential libsoapysdr-dev librtlsdr-dev
git clone https://github.com/wiedehopf/readsb.git
cd readsb
make HAVE_SOAPYSDR=1
sudo make install</code>
                    </div>
                </details>
                <button onclick="this.parentElement.remove()" style="position: absolute; top: 8px; right: 12px; background: none; border: none; color: #000; cursor: pointer; font-size: 16px; font-weight: bold;">×</button>
            `;
            document.body.appendChild(warning);
        }

        function updateClock() {
            const now = new Date();
            document.getElementById('utcTime').textContent =
                now.toISOString().substring(11, 19) + ' UTC';
        }

        function initMap() {
            radarMap = L.map('radarMap', {
                center: [observerLocation.lat, observerLocation.lon],
                zoom: 7,
                minZoom: 3,
                maxZoom: 15
            });

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

            // Draw range rings after map is ready
            setTimeout(() => drawRangeRings(), 100);

            // Fix map size on mobile after initialization
            setTimeout(() => {
                if (radarMap) radarMap.invalidateSize();
            }, 200);

            // Additional invalidateSize to ensure all tiles load
            setTimeout(() => {
                if (radarMap) radarMap.invalidateSize();
            }, 500);
        }

        // Handle window resize for map (especially important on mobile)
        window.addEventListener('resize', function() {
            if (radarMap) radarMap.invalidateSize();
        });

        // Handle orientation changes for mobile devices
        window.addEventListener('orientationchange', function() {
            setTimeout(() => {
                if (radarMap) radarMap.invalidateSize();
            }, 200);
        });

        // ============================================
        // TRACKING CONTROL
        // ============================================

        function toggleRemoteDump1090() {
            const useRemote = document.getElementById('useRemoteDump1090').checked;
            const controls = document.querySelector('.remote-dump1090-controls');
            controls.style.display = useRemote ? 'flex' : 'none';
        }

        function getRemoteDump1090Config() {
            const useRemote = document.getElementById('useRemoteDump1090').checked;
            if (!useRemote) return null;

            const host = document.getElementById('remoteSbsHost').value.trim();
            const port = parseInt(document.getElementById('remoteSbsPort').value) || 30003;

            if (!host) {
                alert('Please enter remote dump1090 host address');
                return false;
            }

            return { host, port };
        }

        async function toggleTracking() {
            const btn = document.getElementById('startBtn');

            if (!isTracking) {
                // Check for remote dump1090 config
                const remoteConfig = getRemoteDump1090Config();
                if (remoteConfig === false) return;

                // Get selected ADS-B device
                const adsbDevice = parseInt(document.getElementById('adsbDeviceSelect').value) || 0;

                const requestBody = {
                    device: adsbDevice
                };
                if (remoteConfig) {
                    requestBody.remote_sbs_host = remoteConfig.host;
                    requestBody.remote_sbs_port = remoteConfig.port;
                }

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

                    const text = await response.text();
                    let data;
                    try {
                        data = JSON.parse(text);
                    } catch (e) {
                        alert('Invalid response: ' + text);
                        return;
                    }

                    if (data.status === 'success' || data.status === 'started' || data.status === 'already_running') {
                        startEventStream();
                        drawRangeRings();
                        startSessionTimer();
                        isTracking = true;
                        adsbActiveDevice = adsbDevice;  // Track which device is being used
                        btn.textContent = 'STOP';
                        btn.classList.add('active');
                        document.getElementById('trackingDot').classList.remove('inactive');
                        document.getElementById('trackingStatus').textContent = 'TRACKING';
                        // Disable ADS-B device selector while tracking
                        document.getElementById('adsbDeviceSelect').disabled = true;
                    } else {
                        alert('Failed to start: ' + (data.message || JSON.stringify(data)));
                    }
                } catch (err) {
                    alert('Error: ' + err.message);
                }
            } else {
                try {
                    await fetch('/adsb/stop', { method: 'POST' });
                } catch (err) {}

                stopEventStream();
                isTracking = false;
                adsbActiveDevice = null;
                btn.textContent = 'START';
                btn.classList.remove('active');
                document.getElementById('trackingDot').classList.add('inactive');
                document.getElementById('trackingStatus').textContent = 'STANDBY';
                // Re-enable ADS-B device selector
                document.getElementById('adsbDeviceSelect').disabled = false;
            }
        }

        function startEventStream() {
            if (eventSource) eventSource.close();

            console.log('Starting ADS-B event stream...');
            eventSource = new EventSource('/adsb/stream');

            eventSource.onopen = () => {
                console.log('ADS-B stream connected');
            };

            eventSource.onmessage = (event) => {
                try {
                    const data = JSON.parse(event.data);
                    if (data.type === 'aircraft') {
                        updateAircraft(data);
                    } else if (data.type === 'status') {
                        console.log('ADS-B status:', data.message);
                    } else if (data.type === 'keepalive') {
                        // Keepalive received
                    } else {
                        console.log('ADS-B data:', data);
                    }
                } catch (err) {
                    console.error('ADS-B parse error:', err, event.data);
                }
            };

            eventSource.onerror = (e) => {
                console.error('ADS-B stream error:', e);
                if (eventSource.readyState === EventSource.CLOSED) {
                    console.log('ADS-B stream closed, will not auto-reconnect');
                }
            };
        }

        function stopEventStream() {
            if (eventSource) {
                eventSource.close();
                eventSource = null;
            }
        }

        // ============================================
        // AIRCRAFT UPDATES
        // ============================================
        let pendingUIUpdate = false;
        let pendingMarkerUpdates = new Set();
        const MAX_MARKER_UPDATES_PER_FRAME = 20;

        function scheduleUIUpdate() {
            if (pendingUIUpdate) return;
            pendingUIUpdate = true;
            requestAnimationFrame(() => {
                updateStatsDisplay();
                renderAircraftList();

                let updateCount = 0;
                const toProcess = [];
                for (const icao of pendingMarkerUpdates) {
                    if (updateCount < MAX_MARKER_UPDATES_PER_FRAME) {
                        updateMarkerImmediate(icao);
                        toProcess.push(icao);
                        updateCount++;
                    }
                }
                toProcess.forEach(icao => pendingMarkerUpdates.delete(icao));

                if (pendingMarkerUpdates.size > 0) {
                    pendingUIUpdate = false;
                    scheduleUIUpdate();
                    return;
                }

                if (selectedIcao && aircraft[selectedIcao]) {
                    showAircraftDetails(selectedIcao);
                }

                pendingUIUpdate = false;
            });
        }

        function updateAircraft(data) {
            const icao = data.icao;
            if (!icao) return;

            aircraft[icao] = {
                ...aircraft[icao],
                ...data,
                lastSeen: Date.now()
            };

            checkAndAlertAircraft(icao, aircraft[icao]);
            updateStatistics(icao, aircraft[icao]);

            // Record trail point
            if (data.lat && data.lon) {
                recordTrailPoint(icao, data.lat, data.lon, data.altitude);
                if (showTrails) {
                    updateTrailLine(icao);
                }
                pendingMarkerUpdates.add(icao);
            }

            scheduleUIUpdate();
        }

        const markerState = {};

        function updateMarkerImmediate(icao) {
            const ac = aircraft[icao];
            if (!ac || !ac.lat || !ac.lon) return;

            if (!passesFilter(icao, ac)) {
                if (markers[icao]) {
                    radarMap.removeLayer(markers[icao]);
                    delete markers[icao];
                    delete markerState[icao];
                }
                return;
            }

            const militaryInfo = isMilitaryAircraft(icao, ac.callsign);
            const rotation = Math.round((ac.heading || 0) / 5) * 5;
            const color = militaryInfo.military ? '#556b2f' : getAltitudeColor(ac.altitude);
            const callsign = ac.callsign || icao;
            const alt = ac.altitude ? ac.altitude + ' ft' : 'N/A';
            const iconType = getAircraftIconType(ac.type_code, militaryInfo.military);

            const prevState = markerState[icao] || {};
            const iconChanged = prevState.rotation !== rotation || prevState.color !== color || prevState.iconType !== iconType;
            const tooltipChanged = prevState.callsign !== callsign || prevState.alt !== alt;

            if (markers[icao]) {
                markers[icao].setLatLng([ac.lat, ac.lon]);
                if (iconChanged) {
                    markers[icao].setIcon(createMarkerIcon(rotation, color, iconType));
                }
                if (tooltipChanged) {
                    markers[icao].unbindTooltip();
                    markers[icao].bindTooltip(`${callsign}<br>${alt}`, {
                        permanent: false, direction: 'top', className: 'aircraft-tooltip'
                    });
                }
            } else {
                markers[icao] = L.marker([ac.lat, ac.lon], { icon: createMarkerIcon(rotation, color, iconType) })
                    .addTo(radarMap)
                    .on('click', () => selectAircraft(icao));
                markers[icao].bindTooltip(`${callsign}<br>${alt}`, {
                    permanent: false, direction: 'top', className: 'aircraft-tooltip'
                });
            }

            markerState[icao] = { rotation, color, callsign, alt, iconType };
        }

        // Aircraft type icon SVG paths
        const AIRCRAFT_ICONS = {
            jet: 'M12 2L8 10H4v2l8 4 8-4v-2h-4L12 2zm0 14l-6 3v1h12v-1l-6-3z',
            helicopter: 'M12 4L10 6H8V8h1l3 8 3-8h1V6h-2L12 4zm-1 14v2H9v1h6v-1h-2v-2h-2zm7-7h-2v2h2v-2zM4 11h2v2H4v-2z',
            prop: 'M12 3L9 8H5v2l7 6 7-6v-2h-4L12 3zm0 12l-4 2v1h8v-1l-4-2z',
            military: 'M12 2L7 9H3l1 3 8 6 8-6 1-3h-4L12 2zm0 14l-5 2.5V20h10v-1.5L12 16z',
            glider: 'M12 4L10 8H4v1.5l8 4 8-4V8h-6L12 4zm0 10l-6 2v1h12v-1l-6-2z'
        };

        // Determine aircraft type from type_code
        function getAircraftIconType(typeCode, isMilitary) {
            if (isMilitary) return 'military';
            if (!typeCode) return 'jet';

            const code = typeCode.toUpperCase();

            // Helicopters
            if (code.startsWith('H') || code.includes('HELI') ||
                ['R22', 'R44', 'R66', 'EC35', 'EC45', 'AS50', 'AS55', 'AS65', 'B06', 'B212', 'B412', 'S76', 'A109', 'AW139', 'AW169'].some(h => code.includes(h))) {
                return 'helicopter';
            }

            // Gliders
            if (code.startsWith('G') || code.includes('GLID')) {
                return 'glider';
            }

            // Light props (common GA aircraft)
            if (['C150', 'C152', 'C172', 'C182', 'C206', 'C208', 'C210', 'PA28', 'PA32', 'PA34', 'PA44', 'PA46', 'SR20', 'SR22', 'DA40', 'DA42', 'TB20', 'M20', 'BE35', 'BE36', 'BE58'].some(p => code.includes(p))) {
                return 'prop';
            }

            // Turboprops
            if (['ATR', 'DH8', 'DHC', 'SF34', 'J328', 'B190', 'PC12', 'TBM'].some(t => code.includes(t))) {
                return 'prop';
            }

            return 'jet';
        }

        function createMarkerIcon(rotation, color, iconType = 'jet') {
            const path = AIRCRAFT_ICONS[iconType] || AIRCRAFT_ICONS.jet;
            const size = iconType === 'helicopter' ? 22 : 24;
            return L.divIcon({
                className: `aircraft-marker aircraft-${iconType}`,
                html: `<svg width="${size}" height="${size}" viewBox="0 0 24 24" style="transform: rotate(${rotation}deg); color: ${color}; filter: drop-shadow(0 0 5px ${color});">
                    <path fill="currentColor" d="${path}"/>
                </svg>`,
                iconSize: [size, size],
                iconAnchor: [size/2, size/2]
            });
        }

        // ============================================
        // AIRCRAFT LIST
        // ============================================
        let renderedAircraftOrder = [];
        let lastFullRebuild = 0;
        const MAX_AIRCRAFT_DISPLAY = 50;
        const MIN_REBUILD_INTERVAL = 2000;

        function renderAircraftList() {
            const container = document.getElementById('aircraftList');
            const sortedAircraft = Object.entries(aircraft)
                .filter(([icao, ac]) => passesFilter(icao, ac))
                .map(([icao, ac]) => ({ ...ac, icao }))
                .sort((a, b) => (b.altitude || 0) - (a.altitude || 0))
                .slice(0, MAX_AIRCRAFT_DISPLAY);

            if (sortedAircraft.length === 0) {
                if (container.querySelector('.no-aircraft')) return;
                container.innerHTML = `<div class="no-aircraft"><div>No aircraft detected</div></div>`;
                renderedAircraftOrder = [];
                return;
            }

            const newOrder = sortedAircraft.map(ac => ac.icao);
            const orderChanged = newOrder.length !== renderedAircraftOrder.length ||
                newOrder.some((icao, i) => icao !== renderedAircraftOrder[i]);

            const now = Date.now();
            const canRebuild = now - lastFullRebuild > MIN_REBUILD_INTERVAL;

            if (orderChanged && canRebuild) {
                lastFullRebuild = now;
                const fragment = document.createDocumentFragment();

                sortedAircraft.forEach(ac => {
                    const div = document.createElement('div');
                    div.className = `aircraft-item ${selectedIcao === ac.icao ? 'selected' : ''} ${isOnWatchlist(ac) ? 'watched' : ''}`;
                    div.setAttribute('data-icao', ac.icao);
                    div.onclick = () => selectAircraft(ac.icao);
                    div.innerHTML = buildAircraftItemHTML(ac);
                    fragment.appendChild(div);
                });

                container.innerHTML = '';
                container.appendChild(fragment);
                renderedAircraftOrder = newOrder;
            } else {
                const existingItems = {};
                container.querySelectorAll('[data-icao]').forEach(el => {
                    existingItems[el.getAttribute('data-icao')] = el;
                });

                sortedAircraft.forEach(ac => {
                    const existingItem = existingItems[ac.icao];
                    if (existingItem) {
                        existingItem.className = `aircraft-item ${selectedIcao === ac.icao ? 'selected' : ''} ${isOnWatchlist(ac) ? 'watched' : ''}`;
                        existingItem.innerHTML = buildAircraftItemHTML(ac);
                    }
                });
            }
        }

        function buildAircraftItemHTML(ac) {
            const callsign = ac.callsign || '------';
            const alt = ac.altitude ? ac.altitude.toLocaleString() : '---';
            const speed = ac.speed || '---';
            const heading = ac.heading ? ac.heading + '°' : '---';
            const typeCode = ac.type_code || '';
            const militaryInfo = isMilitaryAircraft(ac.icao, ac.callsign);
            const badge = militaryInfo.military ?
                `<span style="background:#556b2f;color:#fff;padding:1px 4px;border-radius:2px;font-size:8px;margin-left:4px;">MIL</span>` : '';
            // Vertical rate indicator: arrow up (climbing), arrow down (descending), or dash (level)
            let vsIndicator = '-';
            let vsColor = '';
            if (ac.vertical_rate !== undefined) {
                if (ac.vertical_rate > 300) { vsIndicator = '↑'; vsColor = 'color:#00ff88;'; }
                else if (ac.vertical_rate < -300) { vsIndicator = '↓'; vsColor = 'color:#ff6b6b;'; }
            }

            return `
                <div class="aircraft-header">
                    <span class="aircraft-callsign">${callsign}${badge}</span>
                    <span class="aircraft-icao">${typeCode ? typeCode + ' • ' : ''}${ac.icao}</span>
                </div>
                <div class="aircraft-details">
                    <div class="aircraft-detail">
                        <div class="aircraft-detail-value">${alt}</div>
                        <div class="aircraft-detail-label">ALT</div>
                    </div>
                    <div class="aircraft-detail">
                        <div class="aircraft-detail-value">${speed}</div>
                        <div class="aircraft-detail-label">SPD</div>
                    </div>
                    <div class="aircraft-detail">
                        <div class="aircraft-detail-value">${heading}</div>
                        <div class="aircraft-detail-label">HDG</div>
                    </div>
                    <div class="aircraft-detail">
                        <div class="aircraft-detail-value" style="${vsColor}">${vsIndicator}</div>
                        <div class="aircraft-detail-label">V/S</div>
                    </div>
                </div>
            `;
        }

        function selectAircraft(icao) {
            selectedIcao = icao;
            renderAircraftList();
            showAircraftDetails(icao);
            updateFlightLookupBtn();

            const ac = aircraft[icao];
            if (ac && ac.lat && ac.lon && currentView === 'map') {
                radarMap.setView([ac.lat, ac.lon], 10);
            }
        }

        function showAircraftDetails(icao) {
            const ac = aircraft[icao];
            const container = document.getElementById('selectedInfo');

            if (!ac) {
                container.innerHTML = `
                    <div class="no-aircraft">
                        <div class="no-aircraft-icon">&#9992;</div>
                        <div>Select an aircraft</div>
                    </div>`;
                return;
            }

            const callsign = ac.callsign || ac.icao;
            const lat = ac.lat ? ac.lat.toFixed(4) + '°' : 'N/A';
            const lon = ac.lon ? ac.lon.toFixed(4) + '°' : 'N/A';
            const alt = ac.altitude ? ac.altitude.toLocaleString() + ' ft' : 'N/A';
            const speed = ac.speed ? ac.speed + ' kts' : 'N/A';
            const heading = ac.heading ? ac.heading + '°' : 'N/A';
            const squawk = ac.squawk || 'N/A';
            const vrate = ac.vertical_rate !== undefined ? (ac.vertical_rate >= 0 ? '+' : '') + ac.vertical_rate.toLocaleString() + ' ft/min' : 'N/A';
            const registration = ac.registration || '';
            const typeCode = ac.type_code || '';
            const typeDesc = ac.type_desc || '';
            const militaryInfo = isMilitaryAircraft(ac.icao, ac.callsign);
            const badge = militaryInfo.military ?
                `<div style="background:#556b2f;color:#fff;padding:3px 8px;border-radius:4px;font-size:10px;text-align:center;margin-bottom:8px;">MILITARY${militaryInfo.country ? ' (' + militaryInfo.country + ')' : ''}</div>` : '';
            // Aircraft type info line (shown if available from database)
            const typeInfo = (typeCode || typeDesc) ?
                `<div style="color:#00d4ff;font-size:11px;margin-bottom:6px;">${typeDesc || typeCode}${registration ? ' • ' + registration : ''}</div>` : '';

            container.innerHTML = `
                <div id="aircraftPhotoContainer" style="display:none;margin-bottom:10px;">
                    <a id="aircraftPhotoLink" href="#" target="_blank" rel="noopener">
                        <img id="aircraftPhoto" src="" alt="Aircraft photo" style="width:100%;border-radius:6px;border:1px solid #333;">
                    </a>
                    <div id="aircraftPhotoCredit" style="font-size:9px;color:#666;margin-top:2px;text-align:right;"></div>
                </div>
                <div class="selected-callsign">${callsign}</div>
                ${typeInfo}
                ${badge}
                <div class="telemetry-grid">
                    <div class="telemetry-item">
                        <div class="telemetry-label">ICAO</div>
                        <div class="telemetry-value">${ac.icao}</div>
                    </div>
                    <div class="telemetry-item">
                        <div class="telemetry-label">Squawk</div>
                        <div class="telemetry-value squawk-clickable" onclick="showSquawkInfo('${squawk}')" title="Click for squawk info">${squawk}</div>
                    </div>
                    <div class="telemetry-item">
                        <div class="telemetry-label">Lat</div>
                        <div class="telemetry-value">${lat}</div>
                    </div>
                    <div class="telemetry-item">
                        <div class="telemetry-label">Lon</div>
                        <div class="telemetry-value">${lon}</div>
                    </div>
                    <div class="telemetry-item">
                        <div class="telemetry-label">Altitude</div>
                        <div class="telemetry-value">${alt}</div>
                    </div>
                    <div class="telemetry-item">
                        <div class="telemetry-label">Speed</div>
                        <div class="telemetry-value">${speed}</div>
                    </div>
                    <div class="telemetry-item">
                        <div class="telemetry-label">Heading</div>
                        <div class="telemetry-value">${heading}</div>
                    </div>
                    <div class="telemetry-item">
                        <div class="telemetry-label">V/S</div>
                        <div class="telemetry-value" style="${ac.vertical_rate > 0 ? 'color: #00ff88;' : ac.vertical_rate < 0 ? 'color: #ff6b6b;' : ''}">${vrate}</div>
                    </div>
                    <div class="telemetry-item">
                        <div class="telemetry-label">Range</div>
                        <div class="telemetry-value">${ac.lat ? calculateDistanceNm(observerLocation.lat, observerLocation.lon, ac.lat, ac.lon).toFixed(1) + ' nm' : 'N/A'}</div>
                    </div>
                </div>`;

            // Fetch aircraft photo if registration is available
            if (registration) {
                fetchAircraftPhoto(registration);
            }
        }

        // Cache for aircraft photos to avoid repeated API calls
        const photoCache = {};

        async function fetchAircraftPhoto(registration) {
            const container = document.getElementById('aircraftPhotoContainer');
            const img = document.getElementById('aircraftPhoto');
            const link = document.getElementById('aircraftPhotoLink');
            const credit = document.getElementById('aircraftPhotoCredit');

            if (!container || !img) return;

            // Check cache first
            if (photoCache[registration]) {
                const cached = photoCache[registration];
                if (cached.thumbnail) {
                    img.src = cached.thumbnail;
                    link.href = cached.link || '#';
                    credit.textContent = cached.photographer ? `Photo: ${cached.photographer}` : '';
                    container.style.display = 'block';
                }
                return;
            }

            try {
                const response = await fetch(`/adsb/aircraft-photo/${encodeURIComponent(registration)}`);
                const data = await response.json();

                // Cache the result
                photoCache[registration] = data;

                if (data.success && data.thumbnail) {
                    img.src = data.thumbnail;
                    link.href = data.link || '#';
                    credit.textContent = data.photographer ? `Photo: ${data.photographer}` : '';
                    container.style.display = 'block';
                } else {
                    container.style.display = 'none';
                }
            } catch (err) {
                console.debug('Failed to fetch aircraft photo:', err);
                container.style.display = 'none';
            }
        }

        function cleanupOldAircraft() {
            const now = Date.now();
            const timeout = 60000;
            let needsUpdate = false;

            Object.keys(aircraft).forEach(icao => {
                if (now - aircraft[icao].lastSeen > timeout) {
                    if (markers[icao]) {
                        radarMap.removeLayer(markers[icao]);
                        delete markers[icao];
                    }
                    cleanupTrail(icao);
                    delete aircraft[icao];
                    delete alertedAircraft[icao];
                    needsUpdate = true;

                    if (selectedIcao === icao) {
                        selectedIcao = null;
                        showAircraftDetails(null);
                        updateFlightLookupBtn();
                    }
                }
            });

            if (needsUpdate) {
                scheduleUIUpdate();
            }
        }

        // ============================================
        // AIRBAND AUDIO
        // ============================================
        let isAirbandPlaying = false;

        // Web Audio API for airband visualization
        let airbandAudioContext = null;
        let airbandAnalyser = null;
        let airbandSource = null;
        let airbandVisualizerId = null;
        let airbandPeakLevel = 0;

        function initAirbandVisualizer() {
            const audioPlayer = document.getElementById('airbandPlayer');

            if (!airbandAudioContext) {
                airbandAudioContext = new (window.AudioContext || window.webkitAudioContext)();
            }

            if (airbandAudioContext.state === 'suspended') {
                airbandAudioContext.resume();
            }

            if (!airbandSource) {
                try {
                    airbandSource = airbandAudioContext.createMediaElementSource(audioPlayer);
                    airbandAnalyser = airbandAudioContext.createAnalyser();
                    airbandAnalyser.fftSize = 128;
                    airbandAnalyser.smoothingTimeConstant = 0.7;

                    airbandSource.connect(airbandAnalyser);
                    airbandAnalyser.connect(airbandAudioContext.destination);
                } catch (e) {
                    console.warn('Could not create airband audio source:', e);
                    return;
                }
            }

            document.getElementById('airbandVisualizerContainer').style.display = 'flex';
            drawAirbandVisualizer();
        }

        function drawAirbandVisualizer() {
            if (!airbandAnalyser) return;

            const canvas = document.getElementById('airbandSpectrumCanvas');
            const ctx = canvas.getContext('2d');
            const bufferLength = airbandAnalyser.frequencyBinCount;
            const dataArray = new Uint8Array(bufferLength);

            function draw() {
                airbandVisualizerId = requestAnimationFrame(draw);
                airbandAnalyser.getByteFrequencyData(dataArray);

                // Signal meter
                let sum = 0;
                for (let i = 0; i < bufferLength; i++) sum += dataArray[i];
                const average = sum / bufferLength;
                const levelPercent = (average / 255) * 100;

                if (levelPercent > airbandPeakLevel) {
                    airbandPeakLevel = levelPercent;
                } else {
                    airbandPeakLevel *= 0.95;
                }

                const meterFill = document.getElementById('airbandSignalMeter');
                const meterPeak = document.getElementById('airbandSignalPeak');
                if (meterFill) meterFill.style.width = levelPercent + '%';
                if (meterPeak) meterPeak.style.left = Math.min(airbandPeakLevel, 100) + '%';

                // Draw spectrum
                ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
                ctx.fillRect(0, 0, canvas.width, canvas.height);

                const barWidth = canvas.width / bufferLength * 2;
                let x = 0;

                for (let i = 0; i < bufferLength; i++) {
                    const barHeight = (dataArray[i] / 255) * canvas.height;
                    const hue = 200 - (i / bufferLength) * 60;
                    ctx.fillStyle = `hsl(${hue}, 80%, ${40 + (dataArray[i] / 255) * 30}%)`;
                    ctx.fillRect(x, canvas.height - barHeight, barWidth - 1, barHeight);
                    x += barWidth;
                }
            }
            draw();
        }

        function stopAirbandVisualizer() {
            if (airbandVisualizerId) {
                cancelAnimationFrame(airbandVisualizerId);
                airbandVisualizerId = null;
            }

            const meterFill = document.getElementById('airbandSignalMeter');
            const meterPeak = document.getElementById('airbandSignalPeak');
            if (meterFill) meterFill.style.width = '0%';
            if (meterPeak) meterPeak.style.left = '0%';
            airbandPeakLevel = 0;

            const container = document.getElementById('airbandVisualizerContainer');
            if (container) container.style.display = 'none';
        }

        function initAirband() {
            // Check if audio tools are available
            fetch('/listening/tools')
                .then(r => r.json())
                .then(data => {
                    const missingTools = [];
                    if (!data.rtl_fm) missingTools.push('rtl_fm');
                    if (!data.ffmpeg) missingTools.push('ffmpeg (audio encoder)');

                    if (missingTools.length > 0) {
                        document.getElementById('airbandBtn').disabled = true;
                        document.getElementById('airbandBtn').style.opacity = '0.5';
                        document.getElementById('airbandStatus').textContent = 'UNAVAILABLE';
                        document.getElementById('airbandStatus').style.color = 'var(--accent-red)';

                        // Show warning banner
                        showAirbandWarning(missingTools);
                    }
                })
                .catch(() => {
                    // Endpoint not available, disable airband
                    document.getElementById('airbandBtn').disabled = true;
                    document.getElementById('airbandBtn').style.opacity = '0.5';
                    document.getElementById('airbandStatus').textContent = 'UNAVAILABLE';
                    document.getElementById('airbandStatus').style.color = 'var(--accent-red)';
                });
        }

        function showAirbandWarning(missingTools) {
            const warning = document.createElement('div');
            warning.id = 'airbandWarning';
            warning.style.cssText = `
                position: fixed;
                bottom: 70px;
                left: 50%;
                transform: translateX(-50%);
                background: rgba(239, 68, 68, 0.95);
                color: white;
                padding: 12px 20px;
                border-radius: 8px;
                font-size: 12px;
                z-index: 10000;
                box-shadow: 0 4px 20px rgba(0,0,0,0.5);
                max-width: 400px;
                text-align: center;
            `;

            const toolList = missingTools.join(', ');
            warning.innerHTML = `
                <div style="font-weight: bold; margin-bottom: 6px;">⚠️ Airband Listen Unavailable</div>
                <div>Missing required tools: <strong>${toolList}</strong></div>
                <div style="margin-top: 8px; font-size: 10px; opacity: 0.9;">
                    Install with: <code style="background: rgba(0,0,0,0.3); padding: 2px 6px; border-radius: 3px;">sudo apt install rtl-sdr ffmpeg</code> (Debian) or <code style="background: rgba(0,0,0,0.3); padding: 2px 6px; border-radius: 3px;">brew install librtlsdr ffmpeg</code> (macOS)
                </div>
                <button onclick="this.parentElement.remove()" style="position: absolute; top: 5px; right: 8px; background: none; border: none; color: white; cursor: pointer; font-size: 14px;">×</button>
            `;
            document.body.appendChild(warning);

            // Auto-dismiss after 15 seconds
            setTimeout(() => {
                if (warning.parentElement) {
                    warning.style.opacity = '0';
                    warning.style.transition = 'opacity 0.3s';
                    setTimeout(() => warning.remove(), 300);
                }
            }, 15000);
        }

        function updateAirbandFreq() {
            const select = document.getElementById('airbandFreqSelect');
            const customInput = document.getElementById('airbandCustomFreq');
            const spacingSelect = document.getElementById('airbandSpacing');
            if (select.value === 'custom') {
                customInput.style.display = 'inline-block';
                spacingSelect.style.display = 'inline-block';
            } else {
                customInput.style.display = 'none';
                spacingSelect.style.display = 'none';
                // If audio is playing, restart on new frequency
                if (isAirbandPlaying) {
                    stopAirband();
                    setTimeout(() => startAirband(), 300);
                }
            }
        }

        function updateAirbandSpacing() {
            const spacing = document.getElementById('airbandSpacing').value;
            const customInput = document.getElementById('airbandCustomFreq');
            if (spacing === '8.33') {
                // 8.33 kHz = 0.00833 MHz
                customInput.step = '0.00833';
            } else {
                // 25 kHz = 0.025 MHz
                customInput.step = '0.025';
            }
        }

        // Handle custom frequency input changes
        document.addEventListener('DOMContentLoaded', () => {
            const customInput = document.getElementById('airbandCustomFreq');
            if (customInput) {
                customInput.addEventListener('change', () => {
                    // If audio is playing, restart on new custom frequency
                    if (isAirbandPlaying) {
                        stopAirband();
                        setTimeout(() => startAirband(), 300);
                    }
                });
            }
        });

        function getAirbandFrequency() {
            const select = document.getElementById('airbandFreqSelect');
            if (select.value === 'custom') {
                return parseFloat(document.getElementById('airbandCustomFreq').value) || 121.5;
            }
            return parseFloat(select.value);
        }

        function toggleAirband() {
            if (isAirbandPlaying) {
                stopAirband();
            } else {
                startAirband();
            }
        }

        async function startAirband() {
            const frequency = getAirbandFrequency();
            const device = parseInt(document.getElementById('airbandDeviceSelect').value);
            const squelch = parseInt(document.getElementById('airbandSquelch').value);

            console.log('[AIRBAND] Starting with device:', device, 'freq:', frequency, 'squelch:', squelch);

            // Check if ADS-B tracking is using this device
            if (isTracking && adsbActiveDevice !== null && device === adsbActiveDevice) {
                const useAnyway = confirm(
                    `Warning: ADS-B tracking is using SDR ${adsbActiveDevice}.\n\n` +
                    'Using the same device for airband will stop ADS-B tracking.\n\n' +
                    'Select a different SDR device for airband listening, or click OK to stop tracking and listen.'
                );
                if (!useAnyway) {
                    return;
                }
            }

            document.getElementById('airbandStatus').textContent = 'STARTING...';
            document.getElementById('airbandStatus').style.color = 'var(--accent-orange)';

            try {
                // Start audio on backend
                const response = await fetch('/listening/audio/start', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        frequency: frequency,
                        modulation: 'am',  // Airband uses AM
                        squelch: squelch,
                        gain: 40,
                        device: device
                    })
                });

                const data = await response.json();
                console.log('[AIRBAND] Backend response:', data.status);

                if (data.status !== 'started') {
                    document.getElementById('airbandStatus').textContent = 'ERROR';
                    document.getElementById('airbandStatus').style.color = 'var(--accent-red)';
                    alert('Airband Error: ' + (data.message || 'Failed to start'));
                    return;
                }

                isAirbandPlaying = true;

                // Wait for stream to be ready (backend needs time to start rtl_fm)
                await new Promise(r => setTimeout(r, 300));

                // Setup audio player
                const audioPlayer = document.getElementById('airbandPlayer');

                // Fully reset audio element
                audioPlayer.oncanplay = null;
                try { audioPlayer.pause(); } catch (e) {}
                audioPlayer.removeAttribute('src');
                audioPlayer.load();

                // Connect to stream
                const streamUrl = `/listening/audio/stream?t=${Date.now()}`;
                console.log('[AIRBAND] Connecting to stream:', streamUrl);
                audioPlayer.src = streamUrl;

                // Apply current volume setting
                const volume = parseInt(document.getElementById('airbandVolume').value) || 80;
                audioPlayer.volume = volume / 100;

                // Initialize visualizer
                initAirbandVisualizer();

                // Wait for audio to be ready then play
                audioPlayer.oncanplay = () => {
                    console.log('[AIRBAND] Audio can play');
                    audioPlayer.play().catch(e => console.warn('[AIRBAND] Autoplay blocked:', e));
                };

                // Also try to play immediately (some browsers need this)
                audioPlayer.play().catch(e => {
                    console.log('[AIRBAND] Initial play blocked, waiting for canplay');
                });

                document.getElementById('airbandBtn').innerHTML = '<span class="airband-icon">⏹</span> STOP';
                document.getElementById('airbandBtn').classList.add('active');
                document.getElementById('airbandStatus').textContent = frequency.toFixed(3) + ' MHz';
                document.getElementById('airbandStatus').style.color = 'var(--accent-green)';
                document.getElementById('airbandVisualizerContainer').style.display = 'flex';

            } catch (err) {
                console.error('[AIRBAND] Error:', err);
                document.getElementById('airbandStatus').textContent = 'ERROR';
                document.getElementById('airbandStatus').style.color = 'var(--accent-red)';
            }
        }

        function stopAirband() {
            // Stop visualizer
            stopAirbandVisualizer();

            // Stop browser audio
            const audioPlayer = document.getElementById('airbandPlayer');
            audioPlayer.pause();
            audioPlayer.src = '';

            fetch('/listening/audio/stop', { method: 'POST' })
                .then(r => r.json())
                .then(() => {
                    isAirbandPlaying = false;
                    document.getElementById('airbandBtn').innerHTML = '<span class="airband-icon">▶</span> LISTEN';
                    document.getElementById('airbandBtn').classList.remove('active');
                    document.getElementById('airbandStatus').textContent = 'OFF';
                    document.getElementById('airbandStatus').style.color = 'var(--text-muted)';
                    document.getElementById('airbandVisualizerContainer').style.display = 'none';
                })
                .catch(() => {});
        }

        function updateAirbandVolume() {
            const audioPlayer = document.getElementById('airbandPlayer');
            const volume = parseInt(document.getElementById('airbandVolume').value) || 80;
            if (audioPlayer) {
                audioPlayer.volume = volume / 100;
            }
        }

        function updateAirbandSquelch() {
            // If airband is playing, restart with new squelch value
            if (isAirbandPlaying) {
                stopAirband();
                setTimeout(() => startAirband(), 300);
            }
        }

        // Initialize airband on page load
        document.addEventListener('DOMContentLoaded', initAirband);

        // ============================================
        // ACARS Functions
        // ============================================
        let acarsEventSource = null;
        let isAcarsRunning = false;
        let acarsMessageCount = 0;
        let acarsSidebarCollapsed = localStorage.getItem('acarsSidebarCollapsed') === 'true';
        let acarsFrequencies = {
            'na': ['129.125', '130.025', '130.450', '131.550'],
            'eu': ['131.525', '131.725', '131.550'],
            'ap': ['131.550', '131.450']
        };

        function toggleAcarsSidebar() {
            const sidebar = document.getElementById('acarsSidebar');
            acarsSidebarCollapsed = !acarsSidebarCollapsed;
            sidebar.classList.toggle('collapsed', acarsSidebarCollapsed);
            localStorage.setItem('acarsSidebarCollapsed', acarsSidebarCollapsed);
        }

        // Initialize ACARS sidebar state and frequency checkboxes
        document.addEventListener('DOMContentLoaded', () => {
            const sidebar = document.getElementById('acarsSidebar');
            if (sidebar && acarsSidebarCollapsed) {
                sidebar.classList.add('collapsed');
            }
            updateAcarsFreqCheckboxes();
        });

        function updateAcarsFreqCheckboxes() {
            const region = document.getElementById('acarsRegionSelect').value;
            const freqs = acarsFrequencies[region] || acarsFrequencies['na'];
            const container = document.getElementById('acarsFreqSelector');

            // Save currently checked frequencies
            const previouslyChecked = new Set();
            container.querySelectorAll('input:checked').forEach(cb => {
                previouslyChecked.add(cb.value);
            });

            container.innerHTML = freqs.map(freq => {
                // Check by default if it was previously checked or if this is initial load
                const checked = previouslyChecked.size === 0 || previouslyChecked.has(freq) ? 'checked' : '';
                return `
                    <label style="display: flex; align-items: center; gap: 3px; padding: 2px 6px; background: var(--bg-secondary); border-radius: 3px; cursor: pointer;">
                        <input type="checkbox" class="acars-freq-cb" value="${freq}" ${checked} style="margin: 0; cursor: pointer;">
                        <span>${freq}</span>
                    </label>
                `;
            }).join('');
        }

        function getAcarsRegionFreqs() {
            const checkboxes = document.querySelectorAll('.acars-freq-cb:checked');
            const selectedFreqs = Array.from(checkboxes).map(cb => cb.value);

            // If none selected, return all for the region as fallback
            if (selectedFreqs.length === 0) {
                const region = document.getElementById('acarsRegionSelect').value;
                return acarsFrequencies[region] || acarsFrequencies['na'];
            }

            return selectedFreqs;
        }

        function toggleAcars() {
            if (isAcarsRunning) {
                stopAcars();
            } else {
                startAcars();
            }
        }

        function startAcars() {
            const device = document.getElementById('acarsDeviceSelect').value;
            const frequencies = getAcarsRegionFreqs();

            // Warn if using same device as ADS-B
            if (isTracking && device === '0') {
                const useAnyway = confirm(
                    'Warning: ADS-B tracking may be using SDR device 0.\n\n' +
                    'ACARS uses VHF frequencies (129-131 MHz) while ADS-B uses 1090 MHz.\n' +
                    'You need TWO separate SDR devices to receive both simultaneously.\n\n' +
                    'Click OK to start ACARS on device ' + device + ' anyway.'
                );
                if (!useAnyway) return;
            }

            fetch('/acars/start', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ device, frequencies, gain: '40' })
            })
            .then(r => r.json())
            .then(data => {
                if (data.status === 'started') {
                    isAcarsRunning = true;
                    acarsMessageCount = 0;
                    document.getElementById('acarsToggleBtn').textContent = '■ STOP ACARS';
                    document.getElementById('acarsToggleBtn').classList.add('active');
                    document.getElementById('acarsPanelIndicator').classList.add('active');
                    startAcarsStream();
                } else {
                    alert('ACARS Error: ' + data.message);
                }
            })
            .catch(err => alert('ACARS Error: ' + err));
        }

        function stopAcars() {
            fetch('/acars/stop', { method: 'POST' })
                .then(r => r.json())
                .then(() => {
                    isAcarsRunning = false;
                    document.getElementById('acarsToggleBtn').textContent = '▶ START ACARS';
                    document.getElementById('acarsToggleBtn').classList.remove('active');
                    document.getElementById('acarsPanelIndicator').classList.remove('active');
                    if (acarsEventSource) {
                        acarsEventSource.close();
                        acarsEventSource = null;
                    }
                });
        }

        function startAcarsStream() {
            if (acarsEventSource) acarsEventSource.close();
            acarsEventSource = new EventSource('/acars/stream');

            acarsEventSource.onmessage = function(e) {
                const data = JSON.parse(e.data);
                if (data.type === 'acars') {
                    acarsMessageCount++;
                    stats.acarsMessages++;
                    document.getElementById('acarsCount').textContent = acarsMessageCount;
                    document.getElementById('stripAcars').textContent = stats.acarsMessages;
                    addAcarsMessage(data);
                }
            };

            acarsEventSource.onerror = function() {
                console.error('ACARS stream error');
            };
        }

        function addAcarsMessage(data) {
            const container = document.getElementById('acarsMessages');

            // Remove "no messages" placeholder if present
            const placeholder = container.querySelector('.no-aircraft');
            if (placeholder) placeholder.remove();

            const msg = document.createElement('div');
            msg.className = 'acars-message-item';
            msg.style.cssText = 'padding: 6px 8px; border-bottom: 1px solid var(--border-color); font-size: 10px;';

            const flight = data.flight || 'UNKNOWN';
            const reg = data.reg || '';
            const label = data.label || '';
            const text = data.text || data.msg || '';
            const time = new Date().toLocaleTimeString();

            msg.innerHTML = `
                <div style="display: flex; justify-content: space-between; margin-bottom: 2px;">
                    <span style="color: var(--accent-cyan); font-weight: bold;">${flight}</span>
                    <span style="color: var(--text-muted);">${time}</span>
                </div>
                ${reg ? `<div style="color: var(--text-muted); font-size: 9px;">Reg: ${reg}</div>` : ''}
                ${label ? `<div style="color: var(--accent-green);">Label: ${label}</div>` : ''}
                ${text ? `<div style="color: var(--text-primary); margin-top: 3px; word-break: break-word;">${text}</div>` : ''}
            `;

            container.insertBefore(msg, container.firstChild);

            // Keep max 50 messages
            while (container.children.length > 50) {
                container.removeChild(container.lastChild);
            }
        }

        // Populate ACARS device selector
        document.addEventListener('DOMContentLoaded', () => {
            fetch('/devices')
                .then(r => r.json())
                .then(devices => {
                    const select = document.getElementById('acarsDeviceSelect');
                    select.innerHTML = '';
                    if (devices.length === 0) {
                        select.innerHTML = '<option value="0">No SDR detected</option>';
                    } else {
                        devices.forEach((d, i) => {
                            const opt = document.createElement('option');
                            opt.value = d.index || i;
                            opt.textContent = `Device ${d.index || i}: ${d.name || d.type || 'SDR'}`;
                            select.appendChild(opt);
                        });
                        // Default to device 1 if available (device 0 likely used for ADS-B)
                        if (devices.length > 1) {
                            select.value = '1';
                        }
                    }
                });
        });

        // ============================================
        // SQUAWK CODE REFERENCE
        // ============================================
        function showSquawkInfo(code) {
            const modal = document.getElementById('squawkModal');
            const codeInfo = document.getElementById('squawkCodeInfo');
            const refTable = document.getElementById('squawkRefTable');

            // Show info for the clicked code
            let infoHtml = '';
            if (code && code !== 'N/A' && SQUAWK_CODES[code]) {
                const info = SQUAWK_CODES[code];
                infoHtml = `
                    <div class="squawk-current">
                        <div class="squawk-current-code" style="color: ${info.color}">${code}</div>
                        <div class="squawk-current-name">${info.name}</div>
                        <div class="squawk-current-desc">${info.desc}</div>
                    </div>
                `;
            } else if (code && code !== 'N/A') {
                infoHtml = `
                    <div class="squawk-current">
                        <div class="squawk-current-code">${code}</div>
                        <div class="squawk-current-name">ASSIGNED CODE</div>
                        <div class="squawk-current-desc">Discrete code assigned by ATC for identification</div>
                    </div>
                `;
            }
            codeInfo.innerHTML = infoHtml;

            // Build reference table
            let tableHtml = '';
            SQUAWK_REFERENCE.forEach(item => {
                if (item.code === '---') {
                    tableHtml += `<tr class="squawk-divider"><td colspan="3"></td></tr>`;
                } else {
                    const isEmergency = ['7500', '7600', '7700'].includes(item.code);
                    tableHtml += `
                        <tr class="${isEmergency ? 'emergency' : ''}">
                            <td class="squawk-code">${item.code}</td>
                            <td class="squawk-name">${item.name}</td>
                            <td class="squawk-desc">${item.desc}</td>
                        </tr>
                    `;
                }
            });
            refTable.innerHTML = tableHtml;

            modal.classList.add('active');
        }

        function closeSquawkModal() {
            document.getElementById('squawkModal').classList.remove('active');
        }

        // Close modal on overlay click
        document.getElementById('squawkModal')?.addEventListener('click', (e) => {
            if (e.target.id === 'squawkModal') closeSquawkModal();
        });
    </script>

    <!-- Squawk Code Reference Modal -->
    <div id="squawkModal" class="squawk-modal">
        <div class="squawk-modal-content">
            <div class="squawk-modal-header">
                <span>SQUAWK CODE REFERENCE</span>
                <button class="squawk-modal-close" onclick="closeSquawkModal()">&times;</button>
            </div>
            <div id="squawkCodeInfo"></div>
            <div class="squawk-ref-container">
                <table class="squawk-ref-table">
                    <thead>
                        <tr>
                            <th>Code</th>
                            <th>Name</th>
                            <th>Description</th>
                        </tr>
                    </thead>
                    <tbody id="squawkRefTable"></tbody>
                </table>
            </div>
        </div>
    </div>

    <!-- Watchlist Modal -->
    <div id="watchlistModal" class="watchlist-modal">
        <div class="watchlist-modal-content">
            <div class="watchlist-modal-header">
                <span>★ WATCHLIST</span>
                <button class="watchlist-modal-close" onclick="closeWatchlistModal()">&times;</button>
            </div>
            <div class="watchlist-add-form">
                <input type="text" id="watchlistInput" placeholder="Callsign, registration, or ICAO..." onkeypress="if(event.key==='Enter')handleWatchlistAdd()">
                <select id="watchlistType">
                    <option value="any">Any match</option>
                    <option value="callsign">Callsign</option>
                    <option value="registration">Registration</option>
                    <option value="icao">ICAO Hex</option>
                </select>
                <input type="text" id="watchlistNote" placeholder="Note (optional)" style="flex:1;">
                <button onclick="handleWatchlistAdd()">ADD</button>
            </div>
            <div class="watchlist-entries" id="watchlistEntries">
                <div class="watchlist-empty">No entries. Add callsigns, registrations, or ICAO codes to watch.</div>
            </div>
            <div class="watchlist-footer">
                <span class="watchlist-count"><span id="watchlistCount">0</span> entries</span>
                <span class="watchlist-hint">Alerts trigger when watched aircraft appear</span>
            </div>
        </div>
    </div>

    <style>
        .squawk-clickable {
            cursor: pointer;
            text-decoration: underline;
            text-decoration-style: dotted;
            text-underline-offset: 2px;
        }
        .squawk-clickable:hover {
            color: var(--accent-cyan);
        }
        .squawk-modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.8);
            z-index: 1000;
            justify-content: center;
            align-items: center;
        }
        .squawk-modal.active {
            display: flex;
        }
        .squawk-modal-content {
            background: var(--bg-secondary, #1a1a1a);
            border: 1px solid var(--border-color, #333);
            border-radius: 8px;
            width: 90%;
            max-width: 500px;
            max-height: 80vh;
            overflow: hidden;
            display: flex;
            flex-direction: column;
        }
        .squawk-modal-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 12px 16px;
            background: var(--bg-tertiary, #252525);
            border-bottom: 1px solid var(--border-color, #333);
            font-weight: 600;
            font-size: 12px;
            letter-spacing: 0.1em;
        }
        .squawk-modal-close {
            background: none;
            border: none;
            color: var(--text-secondary, #888);
            font-size: 20px;
            cursor: pointer;
            padding: 0 4px;
        }
        .squawk-modal-close:hover {
            color: #fff;
        }
        .squawk-current {
            padding: 16px;
            text-align: center;
            border-bottom: 1px solid var(--border-color, #333);
            background: var(--bg-tertiary, #252525);
        }
        .squawk-current-code {
            font-family: 'JetBrains Mono', monospace;
            font-size: 32px;
            font-weight: bold;
        }
        .squawk-current-name {
            font-size: 14px;
            font-weight: 600;
            margin-top: 4px;
            color: var(--text-primary, #fff);
        }
        .squawk-current-desc {
            font-size: 11px;
            color: var(--text-secondary, #888);
            margin-top: 4px;
        }
        .squawk-ref-container {
            overflow-y: auto;
            flex: 1;
        }
        .squawk-ref-table {
            width: 100%;
            border-collapse: collapse;
            font-size: 11px;
        }
        .squawk-ref-table th {
            background: var(--bg-tertiary, #252525);
            padding: 8px 12px;
            text-align: left;
            font-weight: 600;
            color: var(--text-secondary, #888);
            position: sticky;
            top: 0;
        }
        .squawk-ref-table td {
            padding: 8px 12px;
            border-bottom: 1px solid var(--border-color, #222);
        }
        .squawk-ref-table tr:hover {
            background: var(--bg-tertiary, #252525);
        }
        .squawk-ref-table tr.emergency {
            background: rgba(255, 0, 0, 0.1);
        }
        .squawk-ref-table tr.emergency:hover {
            background: rgba(255, 0, 0, 0.2);
        }
        .squawk-ref-table .squawk-code {
            font-family: 'JetBrains Mono', monospace;
            font-weight: bold;
            color: var(--accent-cyan, #00d4ff);
        }
        .squawk-ref-table tr.emergency .squawk-code {
            color: #ff4444;
        }
        .squawk-ref-table .squawk-name {
            font-weight: 500;
            color: var(--text-primary, #fff);
        }
        .squawk-ref-table .squawk-desc {
            color: var(--text-secondary, #888);
        }
        .squawk-ref-table tr.squawk-divider td {
            padding: 4px;
            border-bottom: 1px solid var(--border-color, #444);
        }

        /* Watchlist Button */
        .watchlist-btn {
            background: transparent;
            border: 1px solid var(--border-color, #444);
            color: var(--accent-cyan, #00d4ff);
            padding: 4px 8px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 12px;
            transition: all 0.2s;
        }
        .watchlist-btn:hover {
            background: var(--accent-cyan, #00d4ff);
            color: #000;
        }

        /* Watchlist Modal */
        .watchlist-modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.8);
            z-index: 1000;
            justify-content: center;
            align-items: center;
        }
        .watchlist-modal.active {
            display: flex;
        }
        .watchlist-modal-content {
            background: var(--bg-secondary, #1a1a1a);
            border: 1px solid var(--border-color, #333);
            border-radius: 8px;
            width: 90%;
            max-width: 500px;
            max-height: 70vh;
            overflow: hidden;
            display: flex;
            flex-direction: column;
        }
        .watchlist-modal-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 12px 16px;
            background: var(--bg-tertiary, #252525);
            border-bottom: 1px solid var(--border-color, #333);
            font-weight: 600;
            font-size: 12px;
            letter-spacing: 0.1em;
            color: var(--accent-cyan, #00d4ff);
        }
        .watchlist-modal-close {
            background: none;
            border: none;
            color: var(--text-secondary, #888);
            font-size: 20px;
            cursor: pointer;
        }
        .watchlist-modal-close:hover {
            color: #fff;
        }
        .watchlist-add-form {
            display: flex;
            gap: 8px;
            padding: 12px;
            background: var(--bg-tertiary, #252525);
            border-bottom: 1px solid var(--border-color, #333);
            flex-wrap: wrap;
        }
        .watchlist-add-form input,
        .watchlist-add-form select {
            padding: 6px 10px;
            border: 1px solid var(--border-color, #444);
            border-radius: 4px;
            background: var(--bg-primary, #0d0d0d);
            color: var(--text-primary, #fff);
            font-size: 12px;
        }
        .watchlist-add-form input:first-child {
            flex: 2;
            min-width: 150px;
        }
        .watchlist-add-form button {
            padding: 6px 16px;
            background: var(--accent-cyan, #00d4ff);
            color: #000;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-weight: 600;
            font-size: 11px;
        }
        .watchlist-add-form button:hover {
            opacity: 0.9;
        }
        .watchlist-entries {
            flex: 1;
            overflow-y: auto;
            padding: 8px;
        }
        .watchlist-empty {
            text-align: center;
            color: var(--text-secondary, #666);
            padding: 30px;
            font-size: 12px;
        }
        .watchlist-entry {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 10px 12px;
            background: var(--bg-tertiary, #252525);
            border-radius: 4px;
            margin-bottom: 6px;
        }
        .watchlist-entry:hover {
            background: var(--bg-primary, #1a1a1a);
        }
        .watchlist-entry-info {
            display: flex;
            align-items: center;
            gap: 10px;
            flex-wrap: wrap;
        }
        .watchlist-value {
            font-family: 'JetBrains Mono', monospace;
            font-weight: bold;
            color: var(--accent-cyan, #00d4ff);
            font-size: 13px;
        }
        .watchlist-type {
            font-size: 9px;
            padding: 2px 6px;
            background: var(--bg-primary, #0d0d0d);
            border-radius: 3px;
            color: var(--text-secondary, #888);
            text-transform: uppercase;
        }
        .watchlist-note {
            font-size: 11px;
            color: var(--text-secondary, #888);
        }
        .watchlist-remove {
            background: none;
            border: none;
            color: var(--text-secondary, #666);
            font-size: 18px;
            cursor: pointer;
            padding: 0 4px;
        }
        .watchlist-remove:hover {
            color: #ff4444;
        }
        .watchlist-footer {
            display: flex;
            justify-content: space-between;
            padding: 10px 16px;
            background: var(--bg-tertiary, #252525);
            border-top: 1px solid var(--border-color, #333);
            font-size: 10px;
            color: var(--text-secondary, #888);
        }
        .watchlist-hint {
            opacity: 0.7;
        }

        /* Watched aircraft highlight in list */
        .aircraft-item.watched {
            border-left: 3px solid var(--accent-cyan, #00d4ff);
        }
        .aircraft-item.watched::before {
            content: '★';
            position: absolute;
            right: 8px;
            top: 8px;
            color: var(--accent-cyan, #00d4ff);
            font-size: 10px;
        }
    </style>
</body>
</html>
