home readme diff tree note docs

0commit e54ab9296884ecf785ca3d0af4b0a04876e41bec
1Author: lakefox <mason@lakefox.net>
2Date:   Sat May 23 12:22:36 2026 -0600
3
4    Test
5
6diff --git a/.index.html.swp b/.index.html.swp
7new file mode 100644
8index 0000000..23ab6e2
9Binary files /dev/null and b/.index.html.swp differ
10diff --git a/index.html b/index.html
11new file mode 100644
12index 0000000..998472f
13--- /dev/null
14+++ b/index.html
15@@ -0,0 +1,2295 @@
16+<!DOCTYPE html>
17+<html lang="en">
18+<head>
19+    <meta charset="UTF-8">
20+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
21+    <title>NexStorm - Tactical Storm Intercept HUD & Radar Terminal</title>
22+    
23+    <!-- Leaflet Map CSS & JS -->
24+    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
25+    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
26+    
27+    <!-- Tailwind CSS -->
28+    <script src="https://cdn.tailwindcss.com"></script>
29+    
30+    <!-- Font Awesome Icons -->
31+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />
32+    
33+    <!-- Google Fonts (Inter & Orbitron for Tech Dashboard) -->
34+    <link rel="preconnect" href="https://fonts.googleapis.com">
35+    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
36+    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Orbitron:wght@500;700;900&display=swap" rel="stylesheet">
37+    
38+    <!-- Tone.js for client-side procedural alert sounds -->
39+    <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
40+
41+    <style>
42+        body {
43+            font-family: 'Inter', sans-serif;
44+            background-color: #030712;
45+            overflow-x: hidden;
46+            transition: filter 0.3s ease;
47+        }
48+        
49+        .tech-font {
50+            font-family: 'Orbitron', sans-serif;
51+        }
52+
53+        /* Tactical HUD Night Vision Mode (Grayscale + Monochromatic Red/Orange preserve night adaptation) */
54+        .night-vision-mode {
55+            filter: grayscale(100%) brightness(55%) sepia(100%) hue-rotate(-50deg) saturate(600%) !important;
56+        }
57+
58+        /* Custom Scrollbar */
59+        ::-webkit-scrollbar {
60+            width: 6px;
61+            height: 6px;
62+        }
63+        ::-webkit-scrollbar-track {
64+            background: #0b0f19;
65+        }
66+        ::-webkit-scrollbar-thumb {
67+            background: #374151;
68+            border-radius: 4px;
69+        }
70+        ::-webkit-scrollbar-thumb:hover {
71+            background: #4b5563;
72+        }
73+
74+        /* Leaflet Dark Popup Customization */
75+        .leaflet-popup-content-wrapper {
76+            background: rgba(17, 24, 39, 0.95) !important;
77+            color: #f3f4f6 !important;
78+            border: 1px solid rgba(75, 85, 99, 0.4) !important;
79+            backdrop-filter: blur(8px);
80+            border-radius: 8px !important;
81+            box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.5) !important;
82+        }
83+        .leaflet-popup-tip {
84+            background: rgba(17, 24, 39, 0.95) !important;
85+            border: 1px solid rgba(75, 85, 99, 0.4) !important;
86+        }
87+        .leaflet-container {
88+            background-color: #0c0f1d !important;
89+        }
90+
91+        /* Pulsing Alert Keyframes */
92+        @keyframes alert-pulse {
93+            0%, 100% { border-color: rgba(239, 68, 68, 0.4); box-shadow: 0 0 0 0px rgba(239, 68, 68, 0.2); }
94+            50% { border-color: rgba(239, 68, 68, 1); box-shadow: 0 0 12px 4px rgba(239, 68, 68, 0.4); }
95+        }
96+        .pulse-severe {
97+            animation: alert-pulse 1.8s infinite;
98+        }
99+
100+        @keyframes scanning {
101+            0% { transform: rotate(0deg); }
102+            100% { transform: rotate(360deg); }
103+        }
104+        .radar-scan {
105+            animation: scanning 4s linear infinite;
106+        }
107+
108+        .custom-gps-marker {
109+            background: none;
110+            border: none;
111+        }
112+
113+        .custom-metar-marker {
114+            background: none;
115+            border: none;
116+        }
117+    </style>
118+</head>
119+<body class="text-slate-100 flex flex-col h-screen overflow-hidden">
120+
121+    <!-- Toast Notifications Container -->
122+    <div id="toast-container" class="fixed top-20 right-4 z-[3000] flex flex-col gap-2 pointer-events-none"></div>
123+
124+    <header class="bg-slate-900/90 border-b border-slate-800 backdrop-blur-md px-6 py-3 flex items-center justify-between z-[1000] shrink-0">
125+        <div class="flex items-center space-x-3">
126+            <div class="relative w-10 h-10 flex items-center justify-center bg-sky-950/80 rounded-lg border border-sky-500/30">
127+                <i class="fa-solid fa-satellite-dish text-sky-400 text-lg z-10"></i>
128+                <div class="absolute inset-0 border-2 border-dashed border-sky-400/20 rounded-lg radar-scan"></div>
129+            </div>
130+            <div>
131+                <h1 class="text-md font-bold tracking-wider uppercase text-slate-100 flex items-center gap-2">
132+                    NEXSTORM <span class="text-[10px] bg-red-600/30 text-red-400 px-2 py-0.5 rounded border border-red-500/20 uppercase font-mono tracking-widest animate-pulse">Tactical HUD</span>
133+                </h1>
134+                <p class="text-[10px] text-slate-400 uppercase tracking-widest font-mono">Storm Intercept Operations Console</p>
135+            </div>
136+        </div>
137+
138+        <!-- System Stats Bar -->
139+        <div class="hidden lg:flex items-center space-x-6 text-xs font-mono border-l border-r border-slate-800 px-6 py-1">
140+            <div class="flex items-center gap-2">
141+                <span class="w-2.5 h-2.5 rounded-full bg-red-500 animate-ping"></span>
142+                <span>TORNADO: <span id="stat-tor" class="text-red-400 font-bold">0</span></span>
143+            </div>
144+            <div class="flex items-center gap-2">
145+                <span class="w-2.5 h-2.5 rounded-full bg-yellow-500"></span>
146+                <span>SVR TSTM: <span id="stat-svr" class="text-yellow-400 font-bold">0</span></span>
147+            </div>
148+            <div class="flex items-center gap-2">
149+                <span class="w-2.5 h-2.5 rounded-full bg-emerald-500"></span>
150+                <span>FLASH FLOOD: <span id="stat-ff" class="text-emerald-400 font-bold">0</span></span>
151+            </div>
152+        </div>
153+
154+        <!-- Search & Advanced Tactical Controls -->
155+        <div class="flex items-center space-x-3">
156+            <!-- Tactical Night Vision Mode Trigger -->
157+            <button id="btn-night-vision" title="Toggle Red Light Tactical HUD Mode" class="bg-slate-800 hover:bg-slate-700 active:scale-95 border border-slate-700/50 p-2 rounded-lg text-rose-500 hover:text-rose-400 transition-all">
158+                <i class="fa-solid fa-eye-low-vision text-sm"></i>
159+                <span class="hidden xl:inline ml-1 font-mono text-xs">Night Ops</span>
160+            </button>
161+
162+            <div class="relative flex items-center">
163+                <input id="search-input" type="text" placeholder="Search Intercept Target..." 
164+                    class="bg-slate-950 border border-slate-800 hover:border-slate-700 focus:border-sky-500 rounded-lg px-3 py-1.5 text-xs w-40 md:w-56 transition-all focus:outline-none text-slate-200 pl-8 font-mono">
165+                <i class="fa-solid fa-magnifying-glass absolute left-3 text-[10px] text-slate-500"></i>
166+                <button id="search-btn" class="absolute right-1 text-[10px] bg-slate-800 hover:bg-slate-700 px-2 py-1 rounded text-slate-300">GO</button>
167+            </div>
168+            <button id="btn-gps" title="Toggle GPS Tracking Marker" class="bg-slate-800 hover:bg-slate-700 active:scale-95 border border-slate-700/50 p-2 rounded-lg text-slate-300 hover:text-sky-400 transition-all">
169+                <i class="fa-solid fa-location-crosshairs"></i>
170+            </button>
171+            <button id="btn-telemetry-toggle" title="Toggle Dashboard Telemetry Panel" class="bg-slate-800 hover:bg-slate-700 active:scale-95 border border-slate-700/50 px-3 py-2 rounded-lg text-slate-300 hover:text-orange-400 transition-all text-xs flex items-center gap-1.5">
172+                <i class="fa-solid fa-gauge"></i>
173+                <span class="hidden md:inline font-mono">Dashboard</span>
174+            </button>
175+        </div>
176+    </header>
177+
178+    <main class="flex flex-1 overflow-hidden relative">
179+        
180+        <!-- Left Panel: Severe Alerts feed & Tactical Options -->
181+        <aside id="alert-sidebar" class="absolute md:relative inset-y-0 left-0 w-80 bg-slate-900/95 md:bg-slate-950/80 border-r border-slate-800 backdrop-blur-md flex flex-col z-[1000] transition-all duration-300 transform -translate-x-full md:translate-x-0">
182+            <!-- Sidebar Header -->
183+            <div class="p-4 border-b border-slate-800 flex items-center justify-between">
184+                <h2 class="text-xs font-bold tracking-widest text-slate-400 uppercase font-mono">Warning Stream</h2>
185+                <div class="flex items-center space-x-2">
186+                    <button id="sound-toggle" class="bg-slate-800 hover:bg-slate-700 border border-slate-700 text-slate-400 px-2 py-1 rounded text-[10px] flex items-center gap-1 transition-all" title="Toggle audio alarms">
187+                        <i id="sound-icon" class="fa-solid fa-volume-mute text-slate-500"></i>
188+                        <span id="sound-text">Mute Audio</span>
189+                    </button>
190+                    <!-- Close button for mobile -->
191+                    <button class="md:hidden text-slate-400 hover:text-slate-100" onclick="toggleSidebar()">
192+                        <i class="fa-solid fa-times"></i>
193+                    </button>
194+                </div>
195+            </div>
196+
197+            <!-- Warning Feed Stream -->
198+            <div id="alerts-container" class="flex-1 overflow-y-auto p-3 space-y-2">
199+                <div class="flex flex-col items-center justify-center h-48 text-center px-4">
200+                    <i class="fa-solid fa-triangle-exclamation text-slate-600 text-2xl mb-2 animate-pulse"></i>
201+                    <p class="text-xs text-slate-400 font-mono">Acquiring NWS alert grids...</p>
202+                </div>
203+            </div>
204+
205+            <!-- Footer: Global Radar Palette Info -->
206+            <div class="p-4 border-t border-slate-800 bg-slate-950/50">
207+                <div class="flex justify-between text-[10px] text-slate-400 uppercase font-mono mb-2">
208+                    <span>Quick Filter Streams</span>
209+                    <span id="warning-total-lbl">0 Total</span>
210+                </div>
211+                <div class="grid grid-cols-3 gap-1">
212+                    <button onclick="filterWarnings('Tornado')" class="p-1 rounded bg-red-950/40 border border-red-900/40 text-[9px] font-mono hover:bg-red-900/30 text-red-400 text-center">TOR</button>
213+                    <button onclick="filterWarnings('Severe')" class="p-1 rounded bg-yellow-950/40 border border-yellow-900/40 text-[9px] font-mono hover:bg-yellow-900/30 text-yellow-400 text-center">SVR</button>
214+                    <button onclick="filterWarnings('Flood')" class="p-1 rounded bg-emerald-950/40 border border-emerald-900/40 text-[9px] font-mono hover:bg-emerald-900/30 text-emerald-400 text-center">FFW</button>
215+                </div>
216+            </div>
217+        </aside>
218+
219+        <!-- Center Map & Overlays -->
220+        <section class="flex-1 flex flex-col relative h-full">
221+            
222+            <!-- Floating Mobile Sidebar Toggles -->
223+            <div class="absolute top-4 left-4 z-[999] flex gap-2">
224+                <button onclick="toggleSidebar()" class="md:hidden bg-slate-900/90 border border-slate-800 px-3 py-2 rounded-lg text-slate-300">
225+                    <i class="fa-solid fa-bars mr-1"></i> <span class="text-xs font-mono">Alerts</span>
226+                </button>
227+                <button onclick="toggleTelemetry()" class="md:hidden bg-slate-900/90 border border-slate-800 px-3 py-2 rounded-lg text-slate-300">
228+                    <i class="fa-solid fa-gauge mr-1"></i> <span class="text-xs font-mono">HUD</span>
229+                </button>
230+            </div>
231+
232+            <!-- Map Coordinate Intercept Planning Overlay Tools -->
233+            <div class="absolute top-16 left-4 z-[999] bg-slate-900/90 border border-slate-800 p-2 rounded-lg flex flex-col gap-2 shadow-xl pointer-events-auto">
234+                <button id="btn-draw-planner" title="Initiate Cell Intercept Vector Plotter" class="bg-slate-950 hover:bg-slate-800 active:scale-95 border border-slate-800 p-2 rounded text-slate-300 hover:text-red-400 transition-all flex items-center gap-1.5 text-xs font-mono">
235+                    <i class="fa-solid fa-drafting-compass"></i>
236+                    <span>Plot Intercept Vector</span>
237+                </button>
238+                <div id="planner-status-bar" class="hidden text-[8px] font-mono text-red-400 uppercase animate-pulse text-center">
239+                    [Drawing Path...]
240+                </div>
241+            </div>
242+
243+            <!-- Leaflet Map Element -->
244+            <div id="map" class="w-full flex-1 z-[1]"></div>
245+
246+            <!-- BOTTOM HUD INTERACTIVE COMMAND BLOCK -->
247+            <div class="absolute bottom-4 left-1/2 -translate-x-1/2 w-[92%] max-w-5xl bg-slate-900/95 border border-slate-800 backdrop-blur-md rounded-xl p-4 shadow-2xl z-[999] flex flex-col xl:flex-row gap-4 justify-between items-center">
248+                
249+                <!-- Playback controls -->
250+                <div class="flex items-center space-x-3 w-full xl:w-auto justify-between xl:justify-start">
251+                    <div class="flex items-center space-x-1">
252+                        <button id="btn-loop-prev" class="bg-slate-800 hover:bg-slate-700 active:scale-95 text-slate-300 w-8 h-8 rounded-lg flex items-center justify-center transition-all">
253+                            <i class="fa-solid fa-backward-step"></i>
254+                        </button>
255+                        <button id="btn-loop-play" class="bg-sky-600 hover:bg-sky-500 active:scale-95 text-white w-10 h-10 rounded-lg flex items-center justify-center shadow-lg shadow-sky-900/30 transition-all">
256+                            <i id="play-icon" class="fa-solid fa-play"></i>
257+                        </button>
258+                        <button id="btn-loop-next" class="bg-slate-800 hover:bg-slate-700 active:scale-95 text-slate-300 w-8 h-8 rounded-lg flex items-center justify-center transition-all">
259+                            <i class="fa-solid fa-forward-step"></i>
260+                        </button>
261+                    </div>
262+                    
263+                    <div class="flex flex-col">
264+                        <span id="timeline-time" class="text-xs font-mono font-bold tracking-widest text-sky-400">Loading Radar Loop...</span>
265+                        <span id="timeline-status" class="text-[9px] uppercase tracking-widest text-slate-500 font-mono">NEXRAD Real-time feed</span>
266+                    </div>
267+                </div>
268+
269+                <!-- Timeline Slider -->
270+                <div class="flex-1 w-full flex items-center space-x-3">
271+                    <span class="text-[9px] font-mono text-slate-500">PAST</span>
272+                    <input id="timeline-slider" type="range" min="0" max="7" value="7" class="w-full h-1 bg-slate-800 rounded-lg appearance-none cursor-pointer accent-sky-500 focus:outline-none">
273+                    <span class="text-[9px] font-mono text-sky-400">LIVE</span>
274+                </div>
275+
276+                <!-- Multi-Column Command Suite (Radar, Clouds, SPC Convective, METAR Winds) -->
277+                <div class="grid grid-cols-2 md:grid-cols-4 gap-3 w-full xl:w-auto border-t xl:border-t-0 border-slate-800 pt-3 xl:pt-0">
278+                    <!-- Column 1: Radar Layer -->
279+                    <div class="flex flex-col space-y-1">
280+                        <label class="text-[8px] uppercase font-mono text-slate-500">NEXRAD Layer</label>
281+                        <select id="layer-select" class="bg-slate-950 text-slate-300 text-[10px] rounded border border-slate-800 py-1 px-1.5 focus:outline-none focus:border-sky-500 w-full font-mono">
282+                            <option value="nexrad-n0r-900913" selected>Base Reflectivity (N0R)</option>
283+                            <option value="nexrad-n0q-900913">Hi-Res Reflectivity (N0Q)</option>
284+                            <option value="nexrad-n1p-900913">Storm Precip (N1P)</option>
285+                        </select>
286+                        <div class="flex justify-between text-[8px] uppercase font-mono text-slate-500">
287+                            <span>Opacity:</span>
288+                            <span id="opacity-val">80%</span>
289+                        </div>
290+                        <input id="radar-opacity" type="range" min="0" max="100" value="80" class="w-full h-1 bg-slate-800 rounded-lg appearance-none cursor-pointer accent-sky-500">
291+                    </div>
292+
293+                    <!-- Column 2: NASA Clouds -->
294+                    <div class="flex flex-col space-y-1">
295+                        <label class="text-[8px] uppercase font-mono text-slate-500">NASA Satellite</label>
296+                        <select id="cloud-select" class="bg-slate-950 text-slate-300 text-[10px] rounded border border-slate-800 py-1 px-1.5 focus:outline-none focus:border-emerald-500 w-full font-mono">
297+                            <option value="none">Disabled</option>
298+                            <option value="goes-east" selected>GOES-East IR</option>
299+                            <option value="goes-west">GOES-West IR</option>
300+                            <option value="both">Both (East/West)</option>
301+                        </select>
302+                        <div class="flex justify-between text-[8px] uppercase font-mono text-slate-500">
303+                            <span>Opacity:</span>
304+                            <span id="cloud-opacity-val">45%</span>
305+                        </div>
306+                        <input id="cloud-opacity" type="range" min="0" max="100" value="45" class="w-full h-1 bg-slate-800 rounded-lg appearance-none cursor-pointer accent-emerald-500">
307+                    </div>
308+
309+                    <!-- Column 3: SPC Convective Outlook -->
310+                    <div class="flex flex-col space-y-2 bg-slate-950/40 p-1.5 rounded border border-slate-800/40">
311+                        <span class="text-[8px] uppercase font-mono text-slate-500">SPC Outlook</span>
312+                        <div class="flex items-center gap-1.5">
313+                            <input type="checkbox" id="chk-spc" class="accent-orange-500 rounded cursor-pointer">
314+                            <label for="chk-spc" class="text-[10px] font-mono text-slate-300 cursor-pointer">Day 1 Convective</label>
315+                        </div>
316+                        <span id="spc-status-lbl" class="text-[7px] font-mono text-slate-500 uppercase">OFFLINE</span>
317+                    </div>
318+
319+                    <!-- Column 4: Local METAR Winds -->
320+                    <div class="flex flex-col space-y-2 bg-slate-950/40 p-1.5 rounded border border-slate-800/40">
321+                        <span class="text-[8px] uppercase font-mono text-slate-500">METAR winds</span>
322+                        <div class="flex items-center gap-1.5">
323+                            <input type="checkbox" id="chk-metar" class="accent-teal-500 rounded cursor-pointer">
324+                            <label for="chk-metar" class="text-[10px] font-mono text-slate-300 cursor-pointer">Surface Stations</label>
325+                        </div>
326+                        <span id="metar-status-lbl" class="text-[7px] font-mono text-slate-500 uppercase">OFFLINE</span>
327+                    </div>
328+                </div>
329+            </div>
330+
331+            <!-- Legend & Floating Overlays Container (Top Right) -->
332+            <div id="right-map-widgets-container" class="absolute top-4 right-4 z-[999] flex flex-col gap-3 w-80 max-h-[75vh] overflow-y-auto pointer-events-none">
333+                <!-- Legend Overlay -->
334+                <div class="bg-slate-900/90 border border-slate-800 backdrop-blur-md rounded-xl p-3 shadow-2xl w-full hidden sm:block pointer-events-auto">
335+                    <div class="flex justify-between items-center text-[10px] font-mono text-slate-400 mb-1.5 uppercase tracking-widest">
336+                        <span>Reflectivity Scale</span>
337+                        <span>dBZ</span>
338+                    </div>
339+                    <!-- dBZ Gradient bar representing standard weather color scales -->
340+                    <div class="h-3 w-full rounded flex overflow-hidden">
341+                        <div style="background-color: #00ffff;" class="flex-1" title="5 dBZ"></div>
342+                        <div style="background-color: #04bcfc;" class="flex-1" title="15 dBZ"></div>
343+                        <div style="background-color: #0100f8;" class="flex-1" title="25 dBZ"></div>
344+                        <div style="background-color: #00e000;" class="flex-1" title="30 dBZ"></div>
345+                        <div style="background-color: #00a000;" class="flex-1" title="35 dBZ"></div>
346+                        <div style="background-color: #006000;" class="flex-1" title="40 dBZ"></div>
347+                        <div style="background-color: #ffff00;" class="flex-1" title="45 dBZ"></div>
348+                        <div style="background-color: #e0a000;" class="flex-1" title="50 dBZ"></div>
349+                        <div style="background-color: #ff0000;" class="flex-1" title="55 dBZ"></div>
350+                        <div style="background-color: #c00000;" class="flex-1" title="60 dBZ"></div>
351+                        <div style="background-color: #800000;" class="flex-1" title="65 dBZ"></div>
352+                        <div style="background-color: #ff00ff;" class="flex-1" title="70 dBZ"></div>
353+                        <div style="background-color: #990099;" class="flex-1" title="75 dBZ"></div>
354+                    </div>
355+                    <div class="flex justify-between text-[8px] font-mono text-slate-500 mt-1 uppercase">
356+                        <span>Rain (5)</span>
357+                        <span>Storm (35)</span>
358+                        <span>Severe (50)</span>
359+                        <span>Hail (75)</span>
360+                    </div>
361+
362+                    <!-- SPC Color scale overlay details (Visible if checked) -->
363+                    <div id="spc-legend-hud" class="hidden border-t border-slate-800/60 mt-2.5 pt-2">
364+                        <div class="text-[9px] font-mono text-slate-400 uppercase tracking-widest mb-1">SPC Risk Levels</div>
365+                        <div class="grid grid-cols-6 gap-0.5 text-[8px] font-mono font-bold text-center text-slate-900">
366+                            <div style="background-color: #c0dec0;" class="rounded-sm" title="Thunderstorm">TSTM</div>
367+                            <div style="background-color: #79a579;" class="rounded-sm text-slate-100" title="Marginal">MRGL</div>
368+                            <div style="background-color: #f7f779;" class="rounded-sm" title="Slight">SLGT</div>
369+                            <div style="background-color: #f7b05b;" class="rounded-sm" title="Enhanced">ENH</div>
370+                            <div style="background-color: #e55b5b;" class="rounded-sm text-slate-100" title="Moderate">MDT</div>
371+                            <div style="background-color: #f75bf7;" class="rounded-sm" title="High">HIGH</div>
372+                        </div>
373+                    </div>
374+                </div>
375+
376+                <!-- Floating Telemetry Container (under Legend, visible when sidebar is collapsed) -->
377+                <div id="floating-telemetry-box" class="flex-col gap-3 hidden pointer-events-auto">
378+                    <!-- Dynamic floating layout nodes -->
379+                </div>
380+            </div>
381+        </section>
382+
383+        <!-- Right Panel: Telemetry Dashboard, Barometer Graph, and Live Dashcam Feed -->
384+        <aside id="telemetry-sidebar" class="absolute inset-y-0 right-0 w-80 bg-slate-900/95 md:bg-slate-950/80 border-l border-slate-800 backdrop-blur-md flex flex-col z-[1000] transition-all duration-300 transform translate-x-full md:translate-x-0">
385+            <div class="p-4 border-b border-slate-800 flex items-center justify-between">
386+                <div class="flex items-center gap-2">
387+                    <i class="fa-solid fa-gauge-high text-orange-400"></i>
388+                    <h2 class="text-xs font-bold tracking-widest text-slate-400 uppercase font-mono">Telemetry & Sensors</h2>
389+                </div>
390+                <div class="flex items-center gap-2">
391+                    <!-- Collapse Button for desktop/mobile -->
392+                    <button id="btn-close-telemetry" title="Collapse Panel" class="text-slate-400 hover:text-slate-100 p-1">
393+                        <i class="fa-solid fa-angles-right text-xs"></i>
394+                    </button>
395+                    <button class="md:hidden text-slate-400 hover:text-slate-100" onclick="toggleTelemetry()">
396+                        <i class="fa-solid fa-times"></i>
397+                    </button>
398+                </div>
399+            </div>
400+
401+            <!-- Dynamic widget placeholder elements -->
402+            <div id="telemetry-widgets-container" class="flex-1 overflow-y-auto p-4 space-y-4">
403+                
404+                <!-- Time & Clock Monitor -->
405+                <div id="clock-widget-wrapper" class="bg-slate-950/70 border border-slate-800 rounded-lg p-3">
406+                    <div class="flex justify-between items-center text-[10px] font-mono text-slate-500 uppercase mb-1">
407+                        <span>Chaser Terminal Time</span>
408+                        <span class="text-orange-400 animate-pulse font-bold">● Operations Active</span>
409+                    </div>
410+                    <div id="telemetry-clock" class="text-lg font-bold tech-font text-slate-100 tracking-wider">00:00:00 AM</div>
411+                    <div id="telemetry-date" class="text-[10px] text-slate-400 font-mono mt-0.5">--- --, ----</div>
412+                </div>
413+
414+                <!-- Barometer Device Widget (Atmospheric Pressure Trends) -->
415+                <div id="barometer-widget-wrapper" class="bg-slate-950/70 border border-slate-800 rounded-lg p-3 space-y-2">
416+                    <div class="flex justify-between items-center text-[10px] font-mono text-slate-500 uppercase border-b border-slate-800/60 pb-1.5">
417+                        <span>Atmospheric Barometer</span>
418+                        <div class="flex items-center gap-1">
419+                            <span id="baro-sensor-badge" class="w-1.5 h-1.5 rounded-full bg-slate-600"></span>
420+                            <span id="baro-sensor-lbl" class="text-[8px] font-mono text-slate-500 uppercase">Simulated</span>
421+                        </div>
422+                    </div>
423+
424+                    <div class="flex justify-between items-end">
425+                        <div class="flex flex-col">
426+                            <span class="text-[8px] uppercase font-mono text-slate-500">Current Pressure</span>
427+                            <span id="lbl-pressure-val" class="text-lg font-bold tech-font text-sky-400">29.25 <span class="text-[10px] text-slate-400">inHG</span></span>
428+                        </div>
429+                        <button id="btn-baro-drop" title="Simulate pressure drops within storm updrafts" class="bg-red-950/40 hover:bg-red-900/30 border border-red-900/40 text-[9px] font-mono text-red-400 px-2 py-1 rounded">
430+                            <i class="fa-solid fa-wind animate-pulse mr-1"></i>Meso Drop
431+                        </button>
432+                    </div>
433+
434+                    <!-- Live Trending Sparkline (Rendered using inline responsive SVGs) -->
435+                    <div class="h-12 w-full bg-slate-950 border border-slate-900 rounded p-1 flex items-center justify-center relative overflow-hidden">
436+                        <svg id="svg-baro-chart" class="absolute inset-0 w-full h-full" viewBox="0 0 100 40" preserveAspectRatio="none">
437+                            <path id="path-baro-line" d="M0,20 L10,20 L20,20" fill="none" stroke="#38bdf8" stroke-width="1.5" />
438+                        </svg>
439+                    </div>
440+                </div>
441+
442+                <!-- Storm Intercept planner corridor path results -->
443+                <div id="corridor-widget-wrapper" class="bg-slate-950/70 border border-slate-800 rounded-lg p-3 space-y-2 hidden">
444+                    <div class="flex justify-between items-center text-[10px] font-mono text-slate-500 uppercase border-b border-slate-800/60 pb-1.5">
445+                        <span>Intercept Path Log</span>
446+                        <button id="btn-clear-corridor" class="text-[8px] font-mono text-slate-500 hover:text-slate-100 uppercase underline">Clear</button>
447+                    </div>
448+                    <div class="grid grid-cols-2 gap-1 text-[9px] font-mono">
449+                        <span class="text-slate-500">CORRIDOR DIR:</span>
450+                        <span id="lbl-corridor-heading" class="text-slate-300 font-bold">---°</span>
451+                        <span class="text-slate-500">CELL SPEED:</span>
452+                        <div class="flex items-center gap-1">
453+                            <input id="input-cell-speed" type="number" min="5" max="100" value="40" class="bg-slate-950 border border-slate-800 w-12 text-center rounded text-orange-400">
454+                            <span class="text-slate-400">MPH</span>
455+                        </div>
456+                    </div>
457+                    <!-- Computed Intercept Intersections list -->
458+                    <div id="planner-eta-container" class="space-y-1 mt-2 max-h-32 overflow-y-auto border border-slate-900 bg-slate-950/40 p-1 rounded">
459+                        <p class="text-[8px] font-mono text-slate-500 italic text-center">Plot vector to evaluate ETA targets</p>
460+                    </div>
461+                </div>
462+
463+                <!-- Dashcam Viewport Wrapper -->
464+                <div id="dashcam-widget-wrapper" class="bg-slate-950/70 border border-slate-800 rounded-lg overflow-hidden flex flex-col">
465+                    <div class="p-2 border-b border-slate-800 flex justify-between items-center bg-slate-900/40">
466+                        <span class="text-[9px] uppercase font-mono text-slate-400 flex items-center gap-1">
467+                            <i class="fa-solid fa-video text-slate-400"></i> Dashcam Input
468+                        </span>
469+                        <div class="flex items-center gap-1.5">
470+                            <span id="dashcam-indicator" class="w-1.5 h-1.5 rounded-full bg-slate-600"></span>
471+                            <span id="dashcam-status-lbl" class="text-[8px] font-mono text-slate-500 uppercase">Disconnected</span>
472+                        </div>
473+                    </div>
474+                    
475+                    <div id="dashcam-container" class="relative bg-slate-950 flex items-center justify-center border-b border-slate-800 transition-all duration-200 mx-auto w-full overflow-hidden" style="height: 150px;">
476+                        <video id="dashcam-video" class="absolute inset-0 w-full h-full object-cover hidden" autoplay muted playsinline></video>
477+                        <div id="dashcam-placeholder" class="text-center p-4">
478+                            <i class="fa-solid fa-camera-rotate text-slate-700 text-2xl mb-1.5"></i>
479+                            <p class="text-[10px] text-slate-500 font-mono">No Active Feed</p>
480+                        </div>
481+                    </div>
482+
483+                    <!-- Web Cam Resize Controller Controls -->
484+                    <div class="p-2 border-b border-slate-800/60 bg-slate-950/40 space-y-2">
485+                        <div class="flex items-center justify-between text-[8px] uppercase font-mono text-slate-500">
486+                            <span>Dashcam Height</span>
487+                            <span id="lbl-cam-height" class="text-sky-400 font-bold">150px</span>
488+                        </div>
489+                        <input id="slider-cam-height" type="range" min="100" max="400" value="150" class="w-full h-1 bg-slate-800 rounded-lg appearance-none cursor-pointer accent-sky-500 focus:outline-none">
490+                        
491+                        <div class="flex items-center justify-between text-[8px] uppercase font-mono text-slate-500">
492+                            <span>Dashcam Width</span>
493+                            <span id="lbl-cam-width" class="text-sky-400 font-bold">100%</span>
494+                        </div>
495+                        <input id="slider-cam-width" type="range" min="150" max="320" value="320" class="w-full h-1 bg-slate-800 rounded-lg appearance-none cursor-pointer accent-sky-500 focus:outline-none">
496+
497+                        <div class="flex gap-1 justify-between">
498+                            <button id="btn-size-sm" class="flex-1 text-[8px] bg-slate-900 hover:bg-slate-800 border border-slate-800 py-1 rounded text-slate-400 hover:text-slate-100 font-mono">120px H</button>
499+                            <button id="btn-size-md" class="flex-1 text-[8px] bg-slate-900 hover:bg-slate-800 border border-slate-800 py-1 rounded text-slate-400 hover:text-slate-100 font-mono">180px H</button>
500+                            <button id="btn-size-lg" class="flex-1 text-[8px] bg-slate-900 hover:bg-slate-800 border border-slate-800 py-1 rounded text-slate-400 hover:text-slate-100 font-mono">300px H</button>
501+                            <button id="btn-pip" class="px-2 text-[8px] bg-slate-900 hover:bg-slate-800 border border-slate-800 py-1 rounded text-slate-400 hover:text-sky-400 font-mono" title="Native Picture-in-Picture mode"><i class="fa-solid fa-window-restore"></i> PiP</button>
502+                        </div>
503+                    </div>
504+
505+                    <div class="p-2 bg-slate-950/40 flex justify-between gap-2">
506+                        <button id="btn-camera-power" class="w-full text-[10px] font-semibold bg-slate-800 hover:bg-slate-700 active:scale-95 text-slate-200 py-1.5 px-2 rounded-md transition-all font-mono flex items-center justify-center gap-1">
507+                            <i class="fa-solid fa-power-off"></i> Connect Dashcam
508+                        </button>
509+                    </div>
510+                </div>
511+
512+                <!-- Live Computational Geolocation Details -->
513+                <div id="geolocation-widget-wrapper" class="bg-slate-950/70 border border-slate-800 rounded-lg p-3 space-y-3 flex flex-col">
514+                    <div class="flex justify-between items-center text-[10px] font-mono text-slate-500 uppercase border-b border-slate-800/60 pb-1.5">
515+                        <span>Tactical Intercept GPS</span>
516+                        <span id="gps-lock-badge" class="text-[9px] text-red-500 uppercase font-bold">Unbound</span>
517+                    </div>
518+
519+                    <div class="grid grid-cols-2 gap-3">
520+                        <div class="flex flex-col">
521+                            <span class="text-[8px] uppercase font-mono text-slate-500">Latitude</span>
522+                            <span id="tele-lat" class="text-xs font-bold tech-font text-slate-200">00.00000°</span>
523+                        </div>
524+                        <div class="flex flex-col">
525+                            <span class="text-[8px] uppercase font-mono text-slate-500">Longitude</span>
526+                            <span id="tele-lon" class="text-xs font-bold tech-font text-slate-200">00.00000°</span>
527+                        </div>
528+                    </div>
529+
530+                    <div class="grid grid-cols-2 gap-3">
531+                        <div class="flex flex-col">
532+                            <span class="text-[8px] uppercase font-mono text-slate-500">Velocity (Speed)</span>
533+                            <span id="tele-speed" class="text-xs font-bold tech-font text-sky-400">0.0 MPH</span>
534+                        </div>
535+                        <div class="flex flex-col">
536+                            <span class="text-[8px] uppercase font-mono text-slate-500">Chaser Heading</span>
537+                            <span id="tele-heading" class="text-xs font-bold tech-font text-emerald-400">---°</span>
538+                        </div>
539+                    </div>
540+
541+                    <div class="grid grid-cols-2 gap-3">
542+                        <div class="flex flex-col">
543+                            <span class="text-[8px] uppercase font-mono text-slate-500">Altitude</span>
544+                            <span id="tele-alt" class="text-xs font-bold tech-font text-purple-400">0 m</span>
545+                        </div>
546+                        <div class="flex flex-col flex-1">
547+                            <span class="text-[8px] uppercase font-mono text-slate-500">Auto-Center Map</span>
548+                            <div class="flex items-center mt-0.5">
549+                                <label class="relative inline-flex items-center cursor-pointer">
550+                                    <input type="checkbox" id="chk-autocenter" class="sr-only peer" checked>
551+                                    <div class="w-7 h-4 bg-slate-800 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-slate-300 after:border-slate-300 after:border after:rounded-full after:h-3 after:w-3 after:transition-all peer-checked:bg-orange-500"></div>
552+                                </label>
553+                            </div>
554+                        </div>
555+                    </div>
556+                </div>
557+
558+                <!-- Recorder & Export Suite -->
559+                <div id="recorder-widget-wrapper" class="bg-slate-950/70 border border-slate-800 rounded-lg p-3 space-y-3">
560+                    <div class="flex justify-between items-center text-[10px] font-mono text-slate-500 uppercase border-b border-slate-800/60 pb-1.5">
561+                        <span>Tactical Logging Suite</span>
562+                        <div class="flex items-center gap-1">
563+                            <span id="rec-dot" class="w-1.5 h-1.5 rounded-full bg-slate-600"></span>
564+                            <span id="rec-timer" class="text-[9px] font-mono text-slate-500 uppercase">OFFLINE</span>
565+                        </div>
566+                    </div>
567+
568+                    <div class="flex flex-col gap-2">
569+                        <button id="btn-rec-control" class="w-full text-xs font-bold bg-orange-600 hover:bg-orange-500 active:scale-95 text-white py-2 rounded-lg transition-all font-mono flex items-center justify-center gap-1.5 shadow-lg shadow-orange-900/20">
570+                            <i class="fa-solid fa-circle text-[9px] animate-pulse"></i> Start Recording Session
571+                        </button>
572+                        <button id="btn-export-session" disabled class="w-full text-xs font-bold bg-slate-800 hover:bg-slate-700 active:scale-95 disabled:opacity-40 disabled:pointer-events-none text-slate-300 py-2 rounded-lg transition-all font-mono flex items-center justify-center gap-1.5">
573+                            <i class="fa-solid fa-file-export"></i> Export Intercept logs
574+                        </button>
575+                    </div>
576+
577+                    <div class="text-[9px] text-slate-500 font-mono leading-relaxed mt-1">
578+                        Compiles and downloads synchronized high-resolution WebM dashcam feeds and structural CSV GPS/Barometric spatial data tables.
579+                    </div>
580+                </div>
581+
582+            </div>
583+        </aside>
584+
585+    </main>
586+
587+    <!-- Detailed Severe Alert Modal -->
588+    <div id="modal-alert" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-[2000] hidden flex items-center justify-center p-4">
589+        <div class="bg-slate-900 border border-slate-800 rounded-xl max-w-2xl w-full p-6 shadow-2xl flex flex-col max-h-[85vh]">
590+            <div class="flex justify-between items-start border-b border-slate-800 pb-4 mb-4">
591+                <div>
592+                    <span id="modal-badge" class="text-[10px] font-mono px-2 py-0.5 rounded border uppercase">Warning</span>
593+                    <h3 id="modal-title" class="text-lg font-bold text-slate-100 mt-1">Severe Alert Details</h3>
594+                    <p id="modal-sub" class="text-xs text-slate-400 font-mono mt-0.5">NWS Regional Warning Office</p>
595+                </div>
596+                <button onclick="closeModal()" class="text-slate-400 hover:text-slate-100 p-1">
597+                    <i class="fa-solid fa-times text-lg"></i>
598+                </button>
599+            </div>
600+            
601+            <div class="flex-1 overflow-y-auto space-y-4 pr-1 text-slate-300">
602+                <div>
603+                    <h4 class="text-xs uppercase font-mono text-slate-500 mb-1">Headline</h4>
604+                    <p id="modal-headline" class="text-sm font-semibold text-slate-200"></p>
605+                </div>
606+                <div>
607+                    <h4 class="text-xs uppercase font-mono text-slate-500 mb-1">Areas Affected</h4>
608+                    <p id="modal-areas" class="text-xs font-mono text-sky-400"></p>
609+                </div>
610+                <div class="grid grid-cols-2 gap-4 bg-slate-950/40 border border-slate-800/40 p-3 rounded-lg">
611+                    <div>
612+                        <h4 class="text-[10px] uppercase font-mono text-slate-500">Expires</h4>
613+                        <p id="modal-expires" class="text-xs font-mono text-slate-300"></p>
614+                    </div>
615+                    <div>
616+                        <h4 class="text-[10px] uppercase font-mono text-slate-500">Urgency & Severity</h4>
617+                        <p id="modal-severity" class="text-xs font-mono text-slate-300"></p>
618+                    </div>
619+                </div>
620+                <div>
621+                    <h4 class="text-xs uppercase font-mono text-slate-500 mb-1">Detailed Description</h4>
622+                    <p id="modal-desc" class="text-xs whitespace-pre-line bg-slate-950/50 p-3 rounded-lg border border-slate-800/50 leading-relaxed font-mono text-slate-300"></p>
623+                </div>
624+                <div>
625+                    <h4 class="text-xs uppercase font-mono text-slate-500 mb-1">Instructions</h4>
626+                    <p id="modal-instruction" class="text-xs bg-red-950/20 border border-red-900/30 text-red-300 p-3 rounded-lg leading-relaxed"></p>
627+                </div>
628+            </div>
629+        </div>
630+    </div>
631+
632+    <script>
633+        // --- State and Variables ---
634+        let map;
635+        let radarLayers = [];
636+        let activeRadarLayer = null;
637+        let warningGeoJsonLayer = null;
638+        
639+        // Dynamic Satellite Cloud Layers
640+        let cloudLayerEast = null;
641+        let cloudLayerWest = null;
642+
643+        // SPC Day 1 Convective Outlook Layer
644+        let spcOutlookLayer = null;
645+
646+        // METAR Observations Layer
647+        let metarObservationsLayer = null;
648+        let metarRefreshInterval = null;
649+
650+        // Storm Path Intercept Planner Variables
651+        let isPlanningCorridor = false;
652+        let corridorClickCount = 0;
653+        let corridorStartLatLng = null;
654+        let corridorEndLatLng = null;
655+        let corridorLineLayer = null;
656+        let corridorConeLayer = null;
657+        let corridorMarkerLayers = [];
658+        
659+        let playbackInterval = null;
660+        let playbackTimes = [];
661+        let timelineIndex = 7; // Start at latest
662+        let isPlaying = false;
663+        
664+        let allNwsAlerts = [];
665+        let filteredAlerts = [];
666+        let warningCounters = { Tornado: 0, Severe: 0, Flood: 0 };
667+        
668+        let isSoundEnabled = false;
669+        let synth = null;
670+
671+        // --- Live GPS and Telemetry System Variables ---
672+        let gpsWatchId = null;
673+        let mapUserMarker = null;
674+        let currentTelemetry = {
675+            latitude: null,
676+            longitude: null,
677+            speed: null,     
678+            heading: null,   
679+            altitude: null,  
680+            timestamp: null
681+        };
682+        let isTrackingGps = false;
683+
684+        // --- Barometric Pressure Variables ---
685+        let pressureHistory = Array(20).fill(1013.25); // Baseline std pressure
686+        let currentPressure = 1013.25;
687+        let barometerSensor = null;
688+
689+        // --- Dashcam and Session Recorder System Variables ---
690+        let cameraStream = null;
691+        let mediaRecorder = null;
692+        let recordedVideoChunks = [];
693+        let sessionTelemetryHistory = []; 
694+        let isRecordingSession = false;
695+        let sessionRecordStartTime = null;
696+        let sessionTimerInterval = null;
697+
698+        // Sidebar Collapse State tracking
699+        let isTelemetryCollapsed = false;
700+
701+        // --- Color mappings for NWS warning categories ---
702+        const WARNING_STYLES = {
703+            'Tornado Warning': { color: '#ef4444', fillColor: '#ef4444', weight: 2.5, opacity: 0.9, fillOpacity: 0.25 },
704+            'Severe Thunderstorm Warning': { color: '#eab308', fillColor: '#eab308', weight: 2, opacity: 0.85, fillOpacity: 0.18 },
705+            'Flash Flood Warning': { color: '#10b981', fillColor: '#10b981', weight: 2, opacity: 0.85, fillOpacity: 0.15 },
706+            'Special Marine Warning': { color: '#06b6d4', fillColor: '#06b6d4', weight: 2, opacity: 0.85, fillOpacity: 0.15 },
707+            'Winter Storm Warning': { color: '#a5f3fc', fillColor: '#a5f3fc', weight: 1.5, opacity: 0.8, fillOpacity: 0.12 },
708+            'default': { color: '#a855f7', fillColor: '#a855f7', weight: 1.5, opacity: 0.8, fillOpacity: 0.1 }
709+        };
710+
711+        // --- Main Initialization ---
712+        window.onload = function() {
713+            initMap();
714+            generateTimelineTimes();
715+            loadRadarLayers();
716+            updateCloudLayers(); 
717+            fetchSevereAlerts();
718+            setupEventListeners();
719+            startTelemetryClock();
720+            initBarometer(); // Connect physical sensor or fallback to simulated trends
721+            
722+            // Adjust default collapse view on load depending on screensize
723+            if (window.innerWidth < 768) {
724+                setTelemetrySidebarCollapsed(true);
725+            } else {
726+                setTelemetrySidebarCollapsed(false);
727+            }
728+
729+            // Periodically refresh active warning data every 60 seconds
730+            setInterval(fetchSevereAlerts, 60000);
731+        };
732+
733+        // --- Toast Message System (Replacing native Alert Windows) ---
734+        function showNotification(message, type = 'info') {
735+            const container = document.getElementById('toast-container');
736+            if (!container) return;
737+            const toast = document.createElement('div');
738+            
739+            let colorClasses = 'bg-slate-900 border-slate-800 text-slate-100';
740+            let iconClass = 'fa-info-circle text-sky-400';
741+            
742+            if (type === 'success') {
743+                colorClasses = 'bg-slate-900 border-emerald-900/50 text-slate-100';
744+                iconClass = 'fa-circle-check text-emerald-400';
745+            } else if (type === 'warning') {
746+                colorClasses = 'bg-slate-900 border-yellow-900/50 text-slate-100';
747+                iconClass = 'fa-triangle-exclamation text-yellow-400';
748+            } else if (type === 'error') {
749+                colorClasses = 'bg-slate-900 border-red-900/50 text-slate-100';
750+                iconClass = 'fa-circle-exclamation text-red-400';
751+            }
752+
753+            toast.className = `p-3 rounded-lg border ${colorClasses} shadow-xl backdrop-blur-md flex items-center gap-2.5 max-w-xs pointer-events-auto transition-all duration-300 opacity-0 translate-x-4`;
754+            toast.innerHTML = `
755+                <i class="fa-solid ${iconClass}"></i>
756+                <div class="text-[11px] font-mono leading-relaxed">${message}</div>
757+            `;
758+            
759+            container.appendChild(toast);
760+            
761+            // Force reflow for transitions
762+            setTimeout(() => {
763+                toast.classList.remove('opacity-0', 'translate-x-4');
764+            }, 50);
765+
766+            // Remove Toast after delay
767+            setTimeout(() => {
768+                toast.classList.add('opacity-0', 'translate-x-4');
769+                setTimeout(() => {
770+                    toast.remove();
771+                }, 300);
772+            }, 4500);
773+        }
774+
775+        // --- Live Time Clock ---
776+        function startTelemetryClock() {
777+            const updateClock = () => {
778+                const now = new Date();
779+                const clockEl = document.getElementById('telemetry-clock');
780+                const dateEl = document.getElementById('telemetry-date');
781+                
782+                if (clockEl) {
783+                    clockEl.innerText = now.toLocaleTimeString([], { hour12: true, hour: '2-digit', minute: '2-digit', second: '2-digit' });
784+                }
785+                if (dateEl) {
786+                    dateEl.innerText = now.toLocaleDateString([], { weekday: 'long', year: 'numeric', month: 'short', day: 'numeric' });
787+                }
788+            };
789+            updateClock();
790+            setInterval(updateClock, 1000);
791+        }
792+
793+        // --- Map Base Setup ---
794+        function initMap() {
795+            map = L.map('map', {
796+                zoomControl: false,
797+                minZoom: 3,
798+                maxZoom: 18
799+            }).setView([37.8, -96], 4);
800+
801+            L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
802+                attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> &copy; <a href="https://carto.com/attributions">CARTO</a>',
803+                subdomains: 'abcd',
804+                maxZoom: 20
805+            }).addTo(map);
806+
807+            L.control.zoom({
808+                position: 'topright'
809+            }).addTo(map);
810+
811+            // Register map click callbacks to intercept planning tools
812+            map.on('click', handleMapClickForCorridor);
813+        }
814+
815+        // --- STREAMING_CHUNK: Processing device atmospheric sensor inputs and graph drawings ---
816+        // --- Barometer and Pressure Sensor API Hook ---
817+        function initBarometer() {
818+            const badge = document.getElementById('baro-sensor-badge');
819+            const lbl = document.getElementById('baro-sensor-lbl');
820+
821+            // Attempt to bind standard Sensor API (supported on select hardware devices)
822+            if (typeof Barometer !== 'undefined') {
823+                try {
824+                    barometerSensor = new Barometer({ frequency: 0.2 }); // Poll once every 5 seconds
825+                    barometerSensor.addEventListener('reading', () => {
826+                        const pressureHpa = barometerSensor.pressure / 100; // Convert Pa to hPa
827+                        updatePressureValue(pressureHpa, true);
828+                    });
829+                    barometerSensor.addEventListener('error', (event) => {
830+                        console.warn("Hardware barometer connection error:", event.error.name);
831+                    });
832+                    barometerSensor.start();
833+                    badge.className = 'w-1.5 h-1.5 rounded-full bg-emerald-500 animate-pulse';
834+                    lbl.innerText = 'Device Sensor';
835+                } catch (e) {
836+                    console.warn("Hardware Barometer start failed. Reverting to simulator.");
837+                }
838+            }
839+
840+            // Start simulated micro barometric fluctuations (adds tactical visual depth)
841+            setInterval(() => {
842+                if (barometerSensor) return; // Keep standard readings
843+                // Normal ambient pressure fluctuating around sea level
844+                const drift = (Math.random() - 0.5) * 0.15;
845+                const newPressure = Math.max(900, Math.min(1050, currentPressure + drift));
846+                updatePressureValue(newPressure, false);
847+            }, 5000);
848+
849+            // Draw initial flat sparkline
850+            drawPressureChart();
851+        }
852+
853+        function updatePressureValue(value, isRealSensor) {
854+            currentPressure = value;
855+            document.getElementById('lbl-pressure-val').innerHTML = `${(currentPressure*0.02953).toFixed(2)} <span class="text-[10px] text-slate-400">inHG</span>`;
856+            
857+            pressureHistory.shift();
858+            pressureHistory.push(currentPressure);
859+            drawPressureChart();
860+
861+            // Sound tactical alarm chime if pressure crashes steeply
862+            if (currentPressure < 980) {
863+                document.getElementById('lbl-pressure-val').className = 'text-lg font-bold tech-font text-red-500 animate-pulse';
864+                if (isSoundEnabled) {
865+                    triggerSirens();
866+                }
867+            } else {
868+                document.getElementById('lbl-pressure-val').className = 'text-lg font-bold tech-font text-sky-400';
869+            }
870+        }
871+
872+        function triggerMesoPressureCollapse() {
873+            showNotification("Entering storm core. Initiating simulated meso-updraft pressure plunge...", "warning");
874+            let steps = 0;
875+            const collapseInterval = setInterval(() => {
876+                steps++;
877+                // Crash atmospheric pressure rapidly to simulate severe tornado proximity (965 hPa)
878+                const fall = 1013.25 - (steps * 2.5);
879+                updatePressureValue(fall, false);
880+                
881+                if (steps >= 20) {
882+                    clearInterval(collapseInterval);
883+                    showNotification("WARNING: Severe low pressure threshold reached (<970 hPa)!", "error");
884+                }
885+            }, 300);
886+        }
887+
888+        function drawPressureChart() {
889+            const svg = document.getElementById('svg-baro-chart');
890+            const path = document.getElementById('path-baro-line');
891+            if (!svg || !path) return;
892+
893+            const minP = Math.min(...pressureHistory) - 1;
894+            const maxP = Math.max(...pressureHistory) + 1;
895+            const pRange = maxP - minP || 1;
896+
897+            let points = [];
898+            for (let i = 0; i < pressureHistory.length; i++) {
899+                const x = (i / (pressureHistory.length - 1)) * 100;
900+                // Invert coordinates since Y=0 is top in SVG
901+                const y = 40 - ((pressureHistory[i] - minP) / pRange) * 32 - 4;
902+                points.push(`${x.toFixed(1)},${y.toFixed(1)}`);
903+            }
904+
905+            path.setAttribute('d', 'M' + points.join(' L'));
906+        }
907+
908+        // --- STREAMING_CHUNK: Fetching and parsing NOAA SPC Convective Outlook layers ---
909+        // --- Live SPC Day 1 Convective Outlook Layers ---
910+        async function toggleSpcOutlook(active) {
911+            const label = document.getElementById('spc-status-lbl');
912+            const legend = document.getElementById('spc-legend-hud');
913+
914+            if (!active) {
915+                if (spcOutlookLayer) {
916+                    map.removeLayer(spcOutlookLayer);
917+                    spcOutlookLayer = null;
918+                }
919+                label.innerText = 'OFFLINE';
920+                label.className = 'text-[7px] font-mono text-slate-500 uppercase';
921+                legend.classList.add('hidden');
922+                return;
923+            }
924+
925+            label.innerText = 'LOADING...';
926+            label.className = 'text-[7px] font-mono text-yellow-400 uppercase animate-pulse';
927+
928+            try {
929+                // Request live Day 1 Categorical Outlook boundary features directly from NOAA's Vector services
930+                const response = await fetch('https://mapservices.weather.noaa.gov/vector/rest/services/outlooks/SPC_wx_outlks/MapServer/1/query?where=1%3D1&outFields=*&f=geojson');
931+                if (!response.ok) throw new Error("SPC NOAA server unresponsive");
932+                
933+                const data = await response.json();
934+
935+                if (spcOutlookLayer) {
936+                    map.removeLayer(spcOutlookLayer);
937+                }
938+
939+                spcOutlookLayer = L.geoJSON(data, {
940+                    style: function(feature) {
941+                        return {
942+                            fillColor: feature.properties.fill || '#555555',
943+                            fillOpacity: 0.18,
944+                            color: feature.properties.stroke || '#888888',
945+                            weight: 1.5,
946+                            opacity: 0.75
947+                        };
948+                    },
949+                    onEachFeature: function(feature, layer) {
950+                        const p = feature.properties;
951+                        const labelText = p.label2 || 'Outlook';
952+                        layer.bindTooltip(`SPC DAY 1: ${labelText} Risk`, { sticky: true, className: 'bg-slate-950 border border-slate-800 text-[9px] font-mono text-slate-100 p-1 rounded' });
953+                    }
954+                }).addTo(map);
955+
956+                label.innerText = 'LIVE SYNCED';
957+                label.className = 'text-[7px] font-mono text-emerald-400 uppercase font-bold';
958+                legend.classList.remove('hidden');
959+                showNotification("Convective Risk boundaries rendered. Crosscheck radar cells with storm threat areas.", "success");
960+
961+            } catch (err) {
962+                console.error("SPC Outlook load failure:", err);
963+                label.innerText = 'ERR / LIMIT';
964+                label.className = 'text-[7px] font-mono text-red-500 uppercase font-bold';
965+                document.getElementById('chk-spc').checked = false;
966+                showNotification("Unable to parse SPC layers. Service limits might be choked.", "error");
967+            }
968+        }
969+
970+        // --- STREAMING_CHUNK: Gathering live airport surface observations from Aviation Weather ---
971+        // --- Live surface METAR Winds observations ---
972+        async function refreshSurfaceMetars() {
973+            const chk = document.getElementById('chk-metar');
974+            const label = document.getElementById('metar-status-lbl');
975+            if (!chk.checked) return;
976+
977+            label.innerText = 'SCANNING...';
978+            label.className = 'text-[7px] font-mono text-teal-400 uppercase animate-pulse';
979+
980+            const bounds = map.getBounds();
981+            const bbox = `${bounds.getWest().toFixed(4)},${bounds.getSouth().toFixed(4)},${bounds.getEast().toFixed(4)},${bounds.getNorth().toFixed(4)}`;
982+
983+            try {
984+                // Fetch live observations inside current map viewport using official AviationWeather GeoJSON endpoints
985+                const res = await fetch(`https://aviationweather.gov/api/data/metar?bbox=${bbox}&format=geojson`);
986+                if (!res.ok) throw new Error("AWC observations down");
987+                
988+                const data = await res.json();
989+
990+                if (metarObservationsLayer) {
991+                    map.removeLayer(metarObservationsLayer);
992+                }
993+
994+                metarObservationsLayer = L.geoJSON(data, {
995+                    pointToLayer: function(feature, latlng) {
996+                        const p = feature.properties;
997+                        const tempF = p.temp ? (p.temp * 1.8 + 32).toFixed(0) : 'N/A';
998+                        const dewF = p.dewp ? (p.dewp * 1.8 + 32).toFixed(0) : 'N/A';
999+                        const speedKt = p.wspd || 0;
1000+                        const dirDeg = p.wdir || 0;
1001+                        const stationId = p.id || 'STN';
1002+
1003+                        // Set custom colored markers to easily trace drylines and warm boundary positions
1004+                        let windColor = 'text-sky-400';
1005+                        if (speedKt > 25) windColor = 'text-red-500 animate-pulse';
1006+                        else if (speedKt > 15) windColor = 'text-orange-400';
1007+
1008+                        let markerHtml = `
1009+                            <div class="flex flex-col items-center justify-center font-mono pointer-events-auto">
1010+                                <div class="bg-slate-950/90 border border-slate-800 rounded-md px-1 py-0.5 text-[8px] flex items-center gap-1 shadow-lg leading-none">
1011+                                    <span class="text-orange-400 font-bold">${tempF}°</span>
1012+                                    <div style="transform: rotate(${dirDeg}deg);" class="${windColor} transition-all">
1013+                                        <i class="fa-solid fa-arrow-up text-[9px]"></i>
1014+                                    </div>
1015+                                    <span class="text-emerald-400 font-bold">${speedKt}k</span>
1016+                                </div>
1017+                                <span class="text-[7px] text-slate-500 mt-0.5 scale-90">${stationId}</span>
1018+                            </div>
1019+                        `;
1020+
1021+                        const metarIcon = L.divIcon({
1022+                            className: 'custom-metar-marker',
1023+                            html: markerHtml,
1024+                            iconSize: [45, 25]
1025+                        });
1026+
1027+                        const marker = L.marker(latlng, { icon: metarIcon });
1028+                        
1029+                        // Setup detailed popup containing raw aviation METAR logs
1030+                        const popupHtml = `
1031+                            <div class="p-1 font-mono text-[10px] w-64 leading-relaxed">
1032+                                <div class="flex justify-between border-b border-slate-800 pb-1 mb-1">
1033+                                    <span class="font-bold text-sky-400">METAR: ${stationId}</span>
1034+                                    <span class="text-slate-500">${p.obsTime || 'Recent'}</span>
1035+                                </div>
1036+                                <div class="grid grid-cols-2 gap-1 mb-1.5">
1037+                                    <span>Temp: <strong class="text-orange-400">${tempF}°F (${p.temp}°C)</strong></span>
1038+                                    <span>Dew Point: <strong class="text-emerald-400">${dewF}°F (${p.dewp}°C)</strong></span>
1039+                                    <span>Wind: <strong class="text-yellow-400">${p.wdir}° @ ${speedKt} kt</strong></span>
1040+                                    <span>Altimeter: <strong class="text-purple-400">${p.altim || '29.92'} inHg</strong></span>
1041+                                </div>
1042+                                <div class="bg-slate-950/80 p-1.5 border border-slate-800 rounded text-[9px] text-slate-400 whitespace-normal leading-normal select-all">
1043+                                    ${p.rawOb || 'No raw string records.'}
1044+                                </div>
1045+                            </div>
1046+                        `;
1047+                        marker.bindPopup(popupHtml);
1048+                        return marker;
1049+                    }
1050+                }).addTo(map);
1051+
1052+                label.innerText = 'CONNECTED';
1053+                label.className = 'text-[7px] font-mono text-teal-400 uppercase font-bold';
1054+
1055+            } catch (err) {
1056+                console.error("METAR Surface fetch error:", err);
1057+                label.innerText = 'ERR / SECURE';
1058+                label.className = 'text-[7px] font-mono text-red-500 uppercase font-bold';
1059+            }
1060+        }
1061+
1062+        function toggleMetarObservations(active) {
1063+            const chk = document.getElementById('chk-metar');
1064+            const label = document.getElementById('metar-status-lbl');
1065+
1066+            if (!active) {
1067+                clearInterval(metarRefreshInterval);
1068+                if (metarObservationsLayer) {
1069+                    map.removeLayer(metarObservationsLayer);
1070+                    metarObservationsLayer = null;
1071+                }
1072+                label.innerText = 'OFFLINE';
1073+                label.className = 'text-[7px] font-mono text-slate-500';
1074+                return;
1075+            }
1076+
1077+            refreshSurfaceMetars();
1078+            // Regularly refresh surface metrics every 3 minutes
1079+            metarRefreshInterval = setInterval(refreshSurfaceMetars, 180000);
1080+            
1081+            // Re-fetch instantly when user drags map focus around
1082+            map.on('moveend', refreshSurfaceMetars);
1083+        }
1084+
1085+        // --- STREAMING_CHUNK: Plotting cell motion vectors and drawing mathematical cones ---
1086+        // --- Storm Intercept Path Planner (Cone of Uncertainty Vectoring) ---
1087+        function toggleCorridorPlanner() {
1088+            const btn = document.getElementById('btn-draw-planner');
1089+            const status = document.getElementById('planner-status-bar');
1090+
1091+            if (isPlanningCorridor) {
1092+                cancelPlanning();
1093+            } else {
1094+                isPlanningCorridor = true;
1095+                corridorClickCount = 0;
1096+                btn.className = 'bg-red-600 hover:bg-red-500 text-white font-bold border-red-500 p-2 rounded text-xs font-mono transition-all flex items-center gap-1.5';
1097+                status.classList.remove('hidden');
1098+                document.getElementById('map').style.cursor = 'crosshair';
1099+                showNotification("Vectoring Active: Left-click target storm core on map to set start point.", "info");
1100+            }
1101+        }
1102+
1103+        function cancelPlanning() {
1104+            const btn = document.getElementById('btn-draw-planner');
1105+            const status = document.getElementById('planner-status-bar');
1106+            isPlanningCorridor = false;
1107+            corridorClickCount = 0;
1108+            btn.className = 'bg-slate-950 hover:bg-slate-800 border border-slate-800 p-2 rounded text-slate-300 hover:text-red-400 transition-all flex items-center gap-1.5 text-xs font-mono';
1109+            status.classList.add('hidden');
1110+            document.getElementById('map').style.cursor = '';
1111+        }
1112+
1113+        function handleMapClickForCorridor(e) {
1114+            if (!isPlanningCorridor) return;
1115+
1116+            corridorClickCount++;
1117+
1118+            if (corridorClickCount === 1) {
1119+                corridorStartLatLng = e.latlng;
1120+                showNotification("Base-point established. Click second point downstream to indicate storm trajectory.", "info");
1121+            } else if (corridorClickCount === 2) {
1122+                corridorEndLatLng = e.latlng;
1123+                calculateStormCorridor();
1124+                cancelPlanning();
1125+            }
1126+        }
1127+
1128+        function calculateStormCorridor() {
1129+            if (!corridorStartLatLng || !corridorEndLatLng) return;
1130+
1131+            // 1. Clear previous layers
1132+            clearCorridorLayer();
1133+
1134+            const p1 = corridorStartLatLng;
1135+            const p2 = corridorEndLatLng;
1136+
1137+            // 2. Compute true geographic bearing (direction degrees clockwise from North)
1138+            const lat1 = p1.lat * Math.PI / 180;
1139+            const lon1 = p1.lng * Math.PI / 180;
1140+            const lat2 = p2.lat * Math.PI / 180;
1141+            const lon2 = p2.lng * Math.PI / 180;
1142+            const dLon = lon2 - lon1;
1143+
1144+            const y = Math.sin(dLon) * Math.cos(lat2);
1145+            const x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
1146+            let bearing = Math.atan2(y, x) * 180 / Math.PI;
1147+            bearing = (bearing + 360) % 360;
1148+
1149+            document.getElementById('lbl-corridor-heading').innerText = `${bearing.toFixed(0)}° (${getCompassDirection(bearing).split(' ')[1] || '---'})`;
1150+
1151+            // Draw center vector line
1152+            corridorLineLayer = L.polyline([p1, p2], {
1153+                color: '#ef4444',
1154+                weight: 2,
1155+                dashArray: '5, 8',
1156+                opacity: 0.95
1157+            }).addTo(map);
1158+
1159+            // Project mathematical widening cone points (standard 20-degree sweep) out to 100km (62 miles)
1160+            const coneDistance = 100000; // Meters
1161+            const outerLeft = projectPointSpherical(p1, bearing - 20, coneDistance);
1162+            const outerRight = projectPointSpherical(p1, bearing + 20, coneDistance);
1163+
1164+            corridorConeLayer = L.polygon([p1, outerLeft, outerRight], {
1165+                color: '#f97316',
1166+                fillColor: '#f97316',
1167+                fillOpacity: 0.12,
1168+                weight: 1.5,
1169+                opacity: 0.6
1170+            }).addTo(map);
1171+
1172+            // Compute distance ETA intervals (15, 30, 45, 60 minutes) matching storm speed
1173+            const speedMph = parseFloat(document.getElementById('input-cell-speed').value) || 40;
1174+            const speedMps = speedMph * 0.44704; // Miles per hour to Meters per second
1175+            const intervals = [15, 30, 45, 60];
1176+            const etaListContainer = document.getElementById('planner-eta-container');
1177+            etaListContainer.innerHTML = '';
1178+
1179+            intervals.forEach(mins => {
1180+                const distMeters = speedMps * (mins * 60);
1181+                const projectedPoint = projectPointSpherical(p1, bearing, distMeters);
1182+
1183+                // Create checkpoint flag on map
1184+                const checkpointMarker = L.circleMarker(projectedPoint, {
1185+                    radius: 5,
1186+                    color: '#ef4444',
1187+                    fillColor: '#ffffff',
1188+                    fillOpacity: 0.9,
1189+                    weight: 2
1190+                }).addTo(map);
1191+
1192+                checkpointMarker.bindTooltip(`${mins}m ETA Core`, { permanent: true, direction: 'right', className: 'bg-slate-950 border border-slate-800 text-[8px] font-mono text-red-400 rounded px-1' });
1193+                corridorMarkerLayers.push(checkpointMarker);
1194+
1195+                // Add to HUD log interface, attempting to fetch local municipal names via Nominatim reverse API
1196+                appendEtaLogItem(mins, projectedPoint);
1197+            });
1198+
1199+            document.getElementById('corridor-widget-wrapper').classList.remove('hidden');
1200+            showNotification("Intercept corridors drawn. Safe retreat vector points mapped.", "success");
1201+        }
1202+
1203+        function projectPointSpherical(start, bearing, distance) {
1204+            const R = 6378137; // Earth's radius in meters
1205+            const d = distance / R;
1206+            const brng = bearing * Math.PI / 180;
1207+            const lat1 = start.lat * Math.PI / 180;
1208+            const lon1 = start.lng * Math.PI / 180;
1209+
1210+            const lat2 = Math.asin(Math.sin(lat1) * Math.cos(d) + Math.cos(lat1) * Math.sin(d) * Math.cos(brng));
1211+            const lon2 = lon1 + Math.atan2(Math.sin(brng) * Math.sin(d) * Math.cos(lat1), Math.cos(d) - Math.sin(lat1) * Math.sin(lat2));
1212+
1213+            return L.latLng(lat2 * 180 / Math.PI, lon2 * 180 / Math.PI);
1214+        }
1215+
1216+        async function appendEtaLogItem(mins, latlng) {
1217+            const container = document.getElementById('planner-eta-container');
1218+            const row = document.createElement('div');
1219+            row.className = 'flex justify-between border-b border-slate-900 pb-1 text-[9px] font-mono';
1220+            
1221+            // Default placeholder string
1222+            row.innerHTML = `
1223+                <span class="text-slate-400">+${mins} MINS:</span>
1224+                <span class="text-sky-400 font-bold">${latlng.lat.toFixed(3)}, ${latlng.lng.toFixed(3)}</span>
1225+            `;
1226+            container.appendChild(row);
1227+
1228+            try {
1229+                // Fetch Nominatim reverse geocode data for real-world municipal target naming
1230+                const res = await fetch(`https://nominatim.openstreetmap.org/reverse?lat=${latlng.lat}&lon=${latlng.lng}&format=json&zoom=10`);
1231+                if (res.ok) {
1232+                    const place = await res.json();
1233+                    const name = place.address.city || place.address.town || place.address.county || 'Unincorporated Region';
1234+                    row.innerHTML = `
1235+                        <span class="text-red-400 font-bold">+${mins} MIN: ${name.toUpperCase()}</span>
1236+                        <span class="text-slate-500">${latlng.lat.toFixed(2)}, ${latlng.lng.toFixed(2)}</span>
1237+                    `;
1238+                }
1239+            } catch (err) {
1240+                // Graceful fallback
1241+            }
1242+        }
1243+
1244+        function clearCorridorLayer() {
1245+            if (corridorLineLayer && map.hasLayer(corridorLineLayer)) map.removeLayer(corridorLineLayer);
1246+            if (corridorConeLayer && map.hasLayer(corridorConeLayer)) map.removeLayer(corridorConeLayer);
1247+            corridorMarkerLayers.forEach(m => {
1248+                if (map.hasLayer(m)) map.removeLayer(m);
1249+            });
1250+            corridorMarkerLayers = [];
1251+            document.getElementById('planner-eta-container').innerHTML = `<p class="text-[8px] font-mono text-slate-500 italic text-center">Plot vector to evaluate ETA targets</p>`;
1252+        }
1253+
1254+        // --- Live Time Clock & Date ---
1255+        function generateTimelineTimes() {
1256+            playbackTimes = [];
1257+            const now = new Date();
1258+            
1259+            const radarDelay = 8 * 60 * 1000; 
1260+            const startTime = new Date(now.getTime() - radarDelay);
1261+            
1262+            for (let i = 7; i >= 0; i--) {
1263+                const targetTime = new Date(startTime.getTime() - (i * 5 * 60 * 1000));
1264+                
1265+                const m = targetTime.getUTCMinutes();
1266+                targetTime.setUTCMinutes(Math.floor(m / 5) * 5);
1267+                targetTime.setUTCSeconds(0);
1268+                targetTime.setUTCMilliseconds(0);
1269+                
1270+                playbackTimes.push(targetTime);
1271+            }
1272+        }
1273+
1274+        // --- Load WMS Tile Radar Layers ---
1275+        function loadRadarLayers() {
1276+            radarLayers.forEach(l => {
1277+                if (map.hasLayer(l)) map.removeLayer(l);
1278+            });
1279+            radarLayers = [];
1280+
1281+            const opacity = parseFloat(document.getElementById('radar-opacity').value) / 100;
1282+
1283+            playbackTimes.forEach((time, index) => {
1284+                const formattedTime = time.toISOString().substring(0, 16) + 'Z';
1285+                
1286+                const layer = L.tileLayer.wms('https://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r-t.cgi', {
1287+                    layers: 'nexrad-n0r-wmst',
1288+                    time: formattedTime,
1289+                    format: 'image/png',
1290+                    transparent: true,
1291+                    opacity: opacity,
1292+                    zIndex: 10 + index,
1293+                    attribution: 'Iowa State IEM NEXRAD'
1294+                });
1295+                
1296+                radarLayers.push(layer);
1297+            });
1298+
1299+            activeRadarLayer = radarLayers[timelineIndex];
1300+            activeRadarLayer.addTo(map);
1301+            updateTimelineUI();
1302+        }
1303+
1304+        // --- Managing Cloud Overlay Layers ---
1305+        function updateCloudLayers() {
1306+            const cloudType = document.getElementById('cloud-select').value;
1307+            const opacity = parseFloat(document.getElementById('cloud-opacity').value) / 100;
1308+            const todayIso = new Date().toISOString().split('T')[0];
1309+
1310+            if (cloudLayerEast && map.hasLayer(cloudLayerEast)) {
1311+                map.removeLayer(cloudLayerEast);
1312+            }
1313+            if (cloudLayerWest && map.hasLayer(cloudLayerWest)) {
1314+                map.removeLayer(cloudLayerWest);
1315+            }
1316+
1317+            const opacityContainer = document.getElementById('cloud-opacity').parentElement;
1318+            if (cloudType === 'none') {
1319+                opacityContainer.style.opacity = '0.35';
1320+                opacityContainer.style.pointerEvents = 'none';
1321+                return; 
1322+            } else {
1323+                opacityContainer.style.opacity = '1';
1324+                opacityContainer.style.pointerEvents = 'auto';
1325+            }
1326+
1327+            if (!cloudLayerEast) {
1328+                cloudLayerEast = L.tileLayer.wms('https://gibs.earthdata.nasa.gov/wms/epsg3857/best/wms.cgi', {
1329+                    layers: 'GOES-East_ABI_Band13_Clean_Infrared',
1330+                    format: 'image/png',
1331+                    transparent: true,
1332+                    opacity: opacity,
1333+                    time: todayIso,
1334+                    zIndex: 5, 
1335+                    attribution: 'NASA GIBS'
1336+                });
1337+            } else {
1338+                cloudLayerEast.setOpacity(opacity);
1339+            }
1340+
1341+            if (!cloudLayerWest) {
1342+                cloudLayerWest = L.tileLayer.wms('https://gibs.earthdata.nasa.gov/wms/epsg3857/best/wms.cgi', {
1343+                    layers: 'GOES-West_ABI_Band13_Clean_Infrared',
1344+                    format: 'image/png',
1345+                    transparent: true,
1346+                    opacity: opacity,
1347+                    time: todayIso,
1348+                    zIndex: 5,
1349+                    attribution: 'NASA GIBS'
1350+                });
1351+            } else {
1352+                cloudLayerWest.setOpacity(opacity);
1353+            }
1354+
1355+            if (cloudType === 'goes-east' || cloudType === 'both') {
1356+                cloudLayerEast.addTo(map);
1357+            }
1358+            if (cloudType === 'goes-west' || cloudType === 'both') {
1359+                cloudLayerWest.addTo(map);
1360+            }
1361+        }
1362+
1363+        // --- Playback Animation Engine ---
1364+        function updateTimelineUI() {
1365+            const time = playbackTimes[timelineIndex];
1366+            if (!time) return;
1367+
1368+            const localString = time.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', timeZoneName: 'short' });
1369+            const utcString = time.toISOString().replace('T', ' ').substring(11, 16) + ' UTC';
1370+            
1371+            document.getElementById('timeline-time').innerText = `${localString} (${utcString})`;
1372+            document.getElementById('timeline-slider').value = timelineIndex;
1373+            
1374+            if (timelineIndex === 7) {
1375+                document.getElementById('timeline-status').innerText = "Live Radar Image";
1376+                document.getElementById('timeline-status').className = "text-[9px] uppercase tracking-widest text-emerald-400 font-bold font-mono";
1377+            } else {
1378+                const elapsedMin = (7 - timelineIndex) * 5;
1379+                document.getElementById('timeline-status').innerText = `${elapsedMin} minutes ago`;
1380+                document.getElementById('timeline-status').className = "text-[9px] uppercase tracking-widest text-sky-400 font-mono";
1381+            }
1382+        }
1383+
1384+        function switchFrame(newIndex) {
1385+            if (newIndex < 0 || newIndex >= radarLayers.length) return;
1386+            
1387+            const prevLayer = activeRadarLayer;
1388+            timelineIndex = newIndex;
1389+            activeRadarLayer = radarLayers[timelineIndex];
1390+            
1391+            activeRadarLayer.addTo(map);
1392+            
1393+            if (prevLayer && prevLayer !== activeRadarLayer) {
1394+                map.removeLayer(prevLayer);
1395+            }
1396+            
1397+            updateTimelineUI();
1398+        }
1399+
1400+        function togglePlayback() {
1401+            const playIcon = document.getElementById('play-icon');
1402+            if (isPlaying) {
1403+                clearInterval(playbackInterval);
1404+                playIcon.className = 'fa-solid fa-play';
1405+                isPlaying = false;
1406+            } else {
1407+                playIcon.className = 'fa-solid fa-pause';
1408+                isPlaying = true;
1409+                
1410+                playbackInterval = setInterval(() => {
1411+                    let nextIndex = timelineIndex + 1;
1412+                    if (nextIndex >= radarLayers.length) {
1413+                        nextIndex = 0; 
1414+                    }
1415+                    switchFrame(nextIndex);
1416+                }, 1000); 
1417+            }
1418+        }
1419+
1420+        // --- Fetch & Process Real-time Warning Polygons ---
1421+        async function fetchSevereAlerts() {
1422+            try {
1423+                const res = await fetch('https://api.weather.gov/alerts/active?status=actual');
1424+                if (!res.ok) throw new Error("API Limit exceeded");
1425+                
1426+                const data = await res.json();
1427+                
1428+                if (warningGeoJsonLayer) {
1429+                    map.removeLayer(warningGeoJsonLayer);
1430+                }
1431+
1432+                allNwsAlerts = data.features || [];
1433+                filteredAlerts = [...allNwsAlerts];
1434+                
1435+                processWarningStats();
1436+                renderSidebarAlerts();
1437+                drawWarningLayers();
1438+
1439+            } catch (err) {
1440+                console.error("Error drawing severe warnings:", err);
1441+                document.getElementById('alerts-container').innerHTML = `
1442+                    <div class="text-center p-4">
1443+                        <i class="fa-solid fa-circle-exclamation text-yellow-500 text-lg mb-1"></i>
1444+                        <p class="text-xs text-slate-400 font-mono">Unable to connect to NWS Servers. Retrying shortly.</p>
1445+                    </div>
1446+                `;
1447+            }
1448+        }
1449+
1450+        // --- Compute Warning Statistics & Update Header Badges ---
1451+        function processWarningStats() {
1452+            warningCounters = { Tornado: 0, Severe: 0, Flood: 0 };
1453+            
1454+            allNwsAlerts.forEach(alert => {
1455+                const event = alert.properties.event || '';
1456+                if (event.includes('Tornado')) warningCounters.Tornado++;
1457+                else if (event.includes('Severe Thunderstorm')) warningCounters.Severe++;
1458+                else if (event.includes('Flood')) warningCounters.Flood++;
1459+            });
1460+
1461+            document.getElementById('stat-tor').innerText = warningCounters.Tornado;
1462+            document.getElementById('stat-svr').innerText = warningCounters.Severe;
1463+            document.getElementById('stat-ff').innerText = warningCounters.Flood;
1464+            
1465+            const totalAlerts = allNwsAlerts.length;
1466+            document.getElementById('warning-total-lbl').innerText = `${totalAlerts} Total`;
1467+            
1468+            if (warningCounters.Tornado > 0 && isSoundEnabled) {
1469+                triggerSirens();
1470+            }
1471+        }
1472+
1473+        // --- Draw Warning Shapes Layer onto Leaflet ---
1474+        function drawWarningLayers() {
1475+            warningGeoJsonLayer = L.geoJSON(filteredAlerts, {
1476+                filter: function(feature) {
1477+                    return feature.geometry && feature.geometry.coordinates;
1478+                },
1479+                style: function(feature) {
1480+                    const event = feature.properties.event || '';
1481+                    let styleDef = WARNING_STYLES['default'];
1482+                    
1483+                    if (event.includes('Tornado')) styleDef = WARNING_STYLES['Tornado Warning'];
1484+                    else if (event.includes('Severe Thunderstorm')) styleDef = WARNING_STYLES['Severe Thunderstorm Warning'];
1485+                    else if (event.includes('Flash Flood')) styleDef = WARNING_STYLES['Flash Flood Warning'];
1486+                    else if (event.includes('Special Marine')) styleDef = WARNING_STYLES['Special Marine Warning'];
1487+                    else if (event.includes('Winter Storm')) styleDef = WARNING_STYLES['Winter Storm Warning'];
1488+
1489+                    return styleDef;
1490+                },
1491+                onEachFeature: function(feature, layer) {
1492+                    const p = feature.properties;
1493+                    
1494+                    let popupHtml = `
1495+                        <div class="p-1 font-sans text-xs w-64">
1496+                            <div class="flex items-center justify-between border-b border-slate-700/60 pb-1.5 mb-1.5">
1497+                                <span class="font-bold tracking-wider text-[10px] uppercase font-mono px-2 py-0.5 rounded bg-red-950/40 text-red-400 border border-red-900/30">${p.event}</span>
1498+                                <span class="text-[9px] font-mono text-slate-400">NWS Alert</span>
1499+                            </div>
1500+                            <p class="font-bold text-slate-100 text-xs mb-1">${p.headline || 'No Headline Provided'}</p>
1501+                            <p class="text-[10px] text-slate-400 line-clamp-2 mb-2">${p.description || ''}</p>
1502+                            <div class="flex justify-between items-center border-t border-slate-800 pt-1.5 mt-1">
1503+                                <button onclick="viewFullAlert('${p.id}')" class="bg-sky-600 hover:bg-sky-500 text-white font-semibold font-mono text-[9px] px-2.5 py-1 rounded transition-all">READ MORE</button>
1504+                                <span class="text-[9px] font-mono text-slate-500">Exp: ${new Date(p.expires).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</span>
1505+                            </div>
1506+                        </div>
1507+                    `;
1508+                    layer.bindPopup(popupHtml);
1509+                    layer.bindTooltip(p.event, { sticky: true, className: 'bg-slate-950 border border-slate-800 text-[9px] text-slate-200 rounded font-mono p-1' });
1510+                }
1511+            }).addTo(map);
1512+        }
1513+
1514+        // --- Sidebar Alerts Feed Renderer ---
1515+        function renderSidebarAlerts() {
1516+            const container = document.getElementById('alerts-container');
1517+            if (filteredAlerts.length === 0) {
1518+                container.innerHTML = `
1519+                    <div class="flex flex-col items-center justify-center h-48 text-center px-4">
1520+                        <i class="fa-solid fa-circle-check text-emerald-500 text-2xl mb-2 animate-bounce"></i>
1521+                        <p class="text-xs text-emerald-400 font-bold font-mono">No Active Warnings Found</p>
1522+                        <p class="text-[10px] text-slate-500 uppercase mt-0.5 font-mono">Atmospheric conditions nominal</p>
1523+                    </div>
1524+                `;
1525+                return;
1526+            }
1527+
1528+            container.innerHTML = '';
1529+            filteredAlerts.forEach(alert => {
1530+                const p = alert.properties;
1531+                const event = p.event || 'Special Alert';
1532+                
1533+                let accentColor = 'border-l-purple-500';
1534+                let iconClass = 'fa-triangle-exclamation';
1535+                let bgStyle = 'bg-slate-900/40';
1536+                let pulseClass = '';
1537+
1538+                if (event.includes('Tornado')) {
1539+                    accentColor = 'border-l-red-500';
1540+                    iconClass = 'fa-tornado text-red-500';
1541+                    bgStyle = 'bg-red-950/10 hover:bg-red-950/20';
1542+                    pulseClass = 'pulse-severe';
1543+                } else if (event.includes('Severe Thunderstorm')) {
1544+                    accentColor = 'border-l-yellow-500';
1545+                    iconClass = 'fa-cloud-bolt text-yellow-500';
1546+                    bgStyle = 'bg-yellow-950/10 hover:bg-yellow-950/20';
1547+                } else if (event.includes('Flood')) {
1548+                    accentColor = 'border-l-emerald-500';
1549+                    iconClass = 'fa-cloud-showers-water text-emerald-500';
1550+                    bgStyle = 'bg-emerald-950/10 hover:bg-emerald-950/20';
1551+                }
1552+
1553+                const card = document.createElement('div');
1554+                card.className = `p-3 rounded-lg border border-slate-800/80 ${accentColor} border-l-4 ${bgStyle} hover:border-slate-700 transition-all cursor-pointer ${pulseClass}`;
1555+                card.innerHTML = `
1556+                    <div class="flex items-start justify-between">
1557+                        <span class="text-[10px] font-bold tracking-wide font-mono text-slate-200 flex items-center gap-1.5">
1558+                            <i class="fa-solid ${iconClass}"></i>
1559+                            ${event}
1560+                        </span>
1561+                        <span class="text-[8px] font-mono text-slate-500">${new Date(p.sent).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</span>
1562+                    </div>
1563+                    <p class="text-[10px] text-slate-400 mt-1 line-clamp-2">${p.headline || 'Active NWS warning coverage'}</p>
1564+                    <div class="flex justify-between items-center mt-2 pt-2 border-t border-slate-800/60 text-[9px] font-mono">
1565+                        <span class="text-sky-400 hover:underline">LOCATE</span>
1566+                        <span class="text-slate-500">Exp: ${new Date(p.expires).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</span>
1567+                    </div>
1568+                `;
1569+                
1570+                card.addEventListener('click', () => {
1571+                    locateWarning(alert);
1572+                });
1573+                
1574+                container.appendChild(card);
1575+            });
1576+        }
1577+
1578+        // --- Zoom & Focus Warning Polygon on Map ---
1579+        function locateWarning(alertFeature) {
1580+            if (!alertFeature.geometry || !alertFeature.geometry.coordinates) {
1581+                showNotification(`No geographic bounding box available for: ${alertFeature.properties.areaDesc}`, 'warning');
1582+                return;
1583+            }
1584+
1585+            const tempLayer = L.geoJSON(alertFeature);
1586+            map.fitBounds(tempLayer.getBounds(), { maxZoom: 10, padding: [20, 20] });
1587+            
1588+            setTimeout(() => {
1589+                warningGeoJsonLayer.eachLayer(layer => {
1590+                    if (layer.feature.properties.id === alertFeature.properties.id) {
1591+                        layer.openPopup();
1592+                    }
1593+                });
1594+            }, 350);
1595+
1596+            if (isSoundEnabled) {
1597+                playWarningChime(alertFeature.properties.event);
1598+            }
1599+        }
1600+
1601+        // --- Filtering Warnings from Feed ---
1602+        function filterWarnings(type) {
1603+            if (type === 'Tornado') {
1604+                filteredAlerts = allNwsAlerts.filter(a => a.properties.event.includes('Tornado'));
1605+            } else if (type === 'Severe') {
1606+                filteredAlerts = allNwsAlerts.filter(a => a.properties.event.includes('Severe'));
1607+            } else if (type === 'Flood') {
1608+                filteredAlerts = allNwsAlerts.filter(a => a.properties.event.includes('Flood'));
1609+            } else {
1610+                filteredAlerts = [...allNwsAlerts];
1611+            }
1612+            renderSidebarAlerts();
1613+            drawWarningLayers();
1614+        }
1615+
1616+        // --- Search City / Location via OpenStreetMap Nominatim Geocoder ---
1617+        async function performSearch() {
1618+            const query = document.getElementById('search-input').value.trim();
1619+            if (!query) return;
1620+
1621+            try {
1622+                const res = await fetch(`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(query)}&limit=1`);
1623+                const data = await res.json();
1624+                
1625+                if (data && data.length > 0) {
1626+                    const place = data[0];
1627+                    const lat = parseFloat(place.lat);
1628+                    const lon = parseFloat(place.lon);
1629+                    
1630+                    map.setView([lat, lon], 8);
1631+                    
1632+                    const marker = L.circleMarker([lat, lon], {
1633+                        radius: 12,
1634+                        color: '#0ea5e9',
1635+                        fillColor: '#0ea5e9',
1636+                        fillOpacity: 0.3,
1637+                        weight: 2
1638+                    }).addTo(map);
1639+                    
1640+                    setTimeout(() => map.removeLayer(marker), 6000);
1641+                } else {
1642+                    showNotification("Target intercept city not found.", "warning");
1643+                }
1644+            } catch (err) {
1645+                console.error("Geocoding failed:", err);
1646+            }
1647+        }
1648+
1649+        // --- STREAMING_CHUNK: Gathering live telemetry and high accuracy chaser positions ---
1650+        // --- High-Precision GPS System and Computations ---
1651+        function toggleGpsTracking() {
1652+            if (!navigator.geolocation) {
1653+                showNotification("Geolocation API is not supported by your browser.", "error");
1654+                return;
1655+            }
1656+
1657+            const gpsButton = document.getElementById('btn-gps');
1658+
1659+            if (isTrackingGps) {
1660+                navigator.geolocation.clearWatch(gpsWatchId);
1661+                gpsWatchId = null;
1662+                isTrackingGps = false;
1663+                
1664+                gpsButton.classList.remove('bg-orange-600', 'text-white', 'border-orange-500');
1665+                gpsButton.classList.add('bg-slate-800', 'text-slate-300');
1666+                
1667+                if (mapUserMarker) {
1668+                    map.removeLayer(mapUserMarker);
1669+                    mapUserMarker = null;
1670+                }
1671+                
1672+                document.getElementById('gps-lock-badge').className = 'text-[9px] text-red-500 uppercase font-bold';
1673+                document.getElementById('gps-lock-badge').innerText = 'Unbound';
1674+                document.getElementById('tele-lat').innerText = '00.00000°';
1675+                document.getElementById('tele-lon').innerText = '00.00000°';
1676+                document.getElementById('tele-speed').innerText = '0.0 MPH';
1677+                document.getElementById('tele-heading').innerText = '---°';
1678+                document.getElementById('tele-alt').innerText = '0 m';
1679+
1680+                showNotification("Real-time GPS tracking deactivated.", "info");
1681+            } else {
1682+                showNotification("Requesting persistent geolocation tracking link...", "info");
1683+                
1684+                gpsWatchId = navigator.geolocation.watchPosition(
1685+                    updateLiveTelemetry,
1686+                    handleGpsError,
1687+                    { enableHighAccuracy: true, timeout: 15000, maximumAge: 0 }
1688+                );
1689+                
1690+                isTrackingGps = true;
1691+                gpsButton.classList.remove('bg-slate-800', 'text-slate-300');
1692+                gpsButton.classList.add('bg-orange-600', 'text-white', 'border-orange-500');
1693+            }
1694+        }
1695+
1696+        function handleGpsError(err) {
1697+            console.error("GPS connection failed:", err);
1698+            showNotification(`GPS Tracking Failure: ${err.message}`, "error");
1699+            
1700+            isTrackingGps = false;
1701+            const gpsButton = document.getElementById('btn-gps');
1702+            gpsButton.classList.remove('bg-orange-600', 'text-white', 'border-orange-500');
1703+            gpsButton.classList.add('bg-slate-800', 'text-slate-300');
1704+        }
1705+
1706+        function updateLiveTelemetry(position) {
1707+            const coords = position.coords;
1708+            
1709+            currentTelemetry.latitude = coords.latitude;
1710+            currentTelemetry.longitude = coords.longitude;
1711+            currentTelemetry.speed = coords.speed; 
1712+            currentTelemetry.heading = coords.heading; 
1713+            currentTelemetry.altitude = coords.altitude; 
1714+            currentTelemetry.timestamp = new Date(position.timestamp);
1715+
1716+            document.getElementById('gps-lock-badge').className = 'text-[9px] text-emerald-400 uppercase font-bold animate-pulse';
1717+            document.getElementById('gps-lock-badge').innerText = 'Locked';
1718+
1719+            document.getElementById('tele-lat').innerText = `${coords.latitude.toFixed(5)}°`;
1720+            document.getElementById('tele-lon').innerText = `${coords.longitude.toFixed(5)}°`;
1721+
1722+            const speedMph = coords.speed ? (coords.speed * 2.23694).toFixed(1) : "0.0";
1723+            document.getElementById('tele-speed').innerText = `${speedMph} MPH`;
1724+
1725+            const headingText = getCompassDirection(coords.heading);
1726+            document.getElementById('tele-heading').innerText = headingText;
1727+
1728+            const altText = coords.altitude ? `${coords.altitude.toFixed(0)} m` : "N/A";
1729+            document.getElementById('tele-alt').innerText = altText;
1730+
1731+            const latlng = [coords.latitude, coords.longitude];
1732+            
1733+            if (!mapUserMarker) {
1734+                const telemetryIcon = L.divIcon({
1735+                    className: 'custom-gps-marker',
1736+                    html: `
1737+                        <div class="relative flex items-center justify-center">
1738+                            <div class="absolute w-6 h-6 bg-orange-500/35 rounded-full animate-ping"></div>
1739+                            <div class="relative w-4 h-4 bg-orange-500 border-2 border-slate-100 rounded-full shadow-lg flex items-center justify-center">
1740+                                <div class="w-1.5 h-1.5 bg-white rounded-full"></div>
1741+                            </div>
1742+                        </div>
1743+                    `,
1744+                    iconSize: [24, 24]
1745+                });
1746+                
1747+                mapUserMarker = L.marker(latlng, { icon: telemetryIcon }).addTo(map);
1748+                mapUserMarker.bindTooltip("Your Tracking Station", { permanent: false, className: 'bg-slate-950 border border-slate-800 text-[10px] text-slate-200 rounded font-mono p-1' });
1749+            } else {
1750+                mapUserMarker.setLatLng(latlng);
1751+            }
1752+
1753+            const autoCenterChecked = document.getElementById('chk-autocenter').checked;
1754+            if (autoCenterChecked) {
1755+                map.setView(latlng, map.getZoom() < 8 ? 10 : map.getZoom());
1756+            }
1757+
1758+            if (isRecordingSession) {
1759+                logSessionTelemetry(coords, speedMph, headingText);
1760+            }
1761+        }
1762+
1763+        function getCompassDirection(bearing) {
1764+            if (bearing === null || bearing === undefined || isNaN(bearing)) return "---°";
1765+            const compassSectors = ["N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"];
1766+            const index = Math.round(bearing / 22.5) % 16;
1767+            return `${bearing.toFixed(0)}° ${compassSectors[index]}`;
1768+        }
1769+
1770+        // --- Dashcam Stream Integrations ---
1771+        async function toggleCameraPower() {
1772+            const videoEl = document.getElementById('dashcam-video');
1773+            const placeholderEl = document.getElementById('dashcam-placeholder');
1774+            const indicatorEl = document.getElementById('dashcam-indicator');
1775+            const labelEl = document.getElementById('dashcam-status-lbl');
1776+            const btnEl = document.getElementById('btn-camera-power');
1777+
1778+            if (cameraStream) {
1779+                stopCameraStream();
1780+                videoEl.classList.add('hidden');
1781+                placeholderEl.classList.remove('hidden');
1782+                
1783+                indicatorEl.className = 'w-1.5 h-1.5 rounded-full bg-slate-600';
1784+                labelEl.innerText = 'Disconnected';
1785+                labelEl.className = 'text-[8px] font-mono text-slate-500 uppercase';
1786+                
1787+                btnEl.innerHTML = `<i class="fa-solid fa-power-off"></i> Connect Dashcam`;
1788+                btnEl.className = 'w-full text-[10px] font-semibold bg-slate-800 hover:bg-slate-700 active:scale-95 text-slate-200 py-1.5 px-2 rounded-md transition-all font-mono flex items-center justify-center gap-1';
1789+                showNotification("Dashcam stream terminated.", "info");
1790+            } else {
1791+                showNotification("Connecting to system camera inputs...", "info");
1792+                try {
1793+                    cameraStream = await navigator.mediaDevices.getUserMedia({
1794+                        video: { facingMode: 'environment', width: { ideal: 1280 }, height: { ideal: 720 } },
1795+                        audio: true 
1796+                    });
1797+                    
1798+                    videoEl.srcObject = cameraStream;
1799+                    videoEl.classList.remove('hidden');
1800+                    placeholderEl.classList.add('hidden');
1801+                    
1802+                    indicatorEl.className = 'w-1.5 h-1.5 rounded-full bg-emerald-500 animate-pulse';
1803+                    labelEl.innerText = 'Connected';
1804+                    labelEl.className = 'text-[8px] font-mono text-emerald-400 uppercase font-bold';
1805+                    
1806+                    btnEl.innerHTML = `<i class="fa-solid fa-circle-stop text-red-500"></i> Disconnect Feed`;
1807+                    btnEl.className = 'w-full text-[10px] font-semibold bg-red-950/20 hover:bg-red-950/40 border border-red-900/40 text-red-300 py-1.5 px-2 rounded-md transition-all font-mono flex items-center justify-center gap-1';
1808+                    
1809+                    showNotification("Dashcam stream connected successfully.", "success");
1810+                } catch (err) {
1811+                    console.error("Camera connection failed:", err);
1812+                    showNotification(`Camera Hook Error: Secure HTTPS connection or localhost is required for browser device permissions.`, "error");
1813+                }
1814+            }
1815+        }
1816+
1817+        function stopCameraStream() {
1818+            if (cameraStream) {
1819+                cameraStream.getTracks().forEach(track => track.stop());
1820+                cameraStream = null;
1821+            }
1822+            document.getElementById('dashcam-video').srcObject = null;
1823+        }
1824+
1825+        // --- Session Recording & Synchronized Log Exporters ---
1826+        function toggleSessionRecording() {
1827+            const btn = document.getElementById('btn-rec-control');
1828+            const dot = document.getElementById('rec-dot');
1829+            const timerText = document.getElementById('rec-timer');
1830+            const exportBtn = document.getElementById('btn-export-session');
1831+
1832+            if (isRecordingSession) {
1833+                stopSessionRecording();
1834+            } else {
1835+                if (!cameraStream) {
1836+                    showNotification("Cannot record. Connect camera feed first.", "warning");
1837+                    return;
1838+                }
1839+                
1840+                try {
1841+                    recordedVideoChunks = [];
1842+                    sessionTelemetryHistory = [];
1843+                    
1844+                    let options = { mimeType: 'video/webm;codecs=vp9,opus' };
1845+                    if (!MediaRecorder.isTypeSupported(options.mimeType)) {
1846+                        options = { mimeType: 'video/webm;codecs=vp8,opus' };
1847+                        if (!MediaRecorder.isTypeSupported(options.mimeType)) {
1848+                            options = { mimeType: 'video/webm' };
1849+                        }
1850+                    }
1851+
1852+                    mediaRecorder = new MediaRecorder(cameraStream, options);
1853+                    
1854+                    mediaRecorder.ondataavailable = (event) => {
1855+                        if (event.data && event.data.size > 0) {
1856+                            recordedVideoChunks.push(event.data);
1857+                        }
1858+                    };
1859+
1860+                    mediaRecorder.onstop = () => {
1861+                        exportBtn.removeAttribute('disabled');
1862+                        exportBtn.className = 'w-full text-xs font-bold bg-orange-600 hover:bg-orange-500 active:scale-95 text-white py-2 rounded-lg transition-all font-mono flex items-center justify-center gap-1.5 shadow-lg shadow-orange-900/25';
1863+                        showNotification("Recording captured! Intercept suite package is ready for export.", "success");
1864+                    };
1865+
1866+                    mediaRecorder.start(1000); 
1867+                    isRecordingSession = true;
1868+                    sessionRecordStartTime = Date.now();
1869+                    
1870+                    btn.innerHTML = `<i class="fa-solid fa-circle-stop"></i> Stop & Save Session`;
1871+                    btn.className = 'w-full text-xs font-bold bg-red-600 hover:bg-red-500 active:scale-95 text-white py-2 rounded-lg transition-all font-mono flex items-center justify-center gap-1.5 shadow-lg shadow-red-900/20';
1872+                    dot.className = 'w-1.5 h-1.5 rounded-full bg-red-500 animate-ping';
1873+                    exportBtn.setAttribute('disabled', 'true');
1874+                    exportBtn.className = 'w-full text-xs font-bold bg-slate-800 hover:bg-slate-700 active:scale-95 disabled:opacity-40 disabled:pointer-events-none text-slate-300 py-2 rounded-lg transition-all font-mono flex items-center justify-center gap-1.5';
1875+
1876+                    sessionTimerInterval = setInterval(() => {
1877+                        const elapsedMs = Date.now() - sessionRecordStartTime;
1878+                        const totalSeconds = Math.floor(elapsedMs / 1000);
1879+                        const mins = Math.floor(totalSeconds / 60).toString().padStart(2, '0');
1880+                        const secs = (totalSeconds % 60).toString().padStart(2, '0');
1881+                        timerText.innerText = `REC: ${mins}:${secs}`;
1882+                        timerText.className = 'text-[9px] font-mono text-red-500 uppercase font-bold';
1883+
1884+                        if (!isTrackingGps) {
1885+                            logSessionTelemetry(null, "0.0", "---°");
1886+                        }
1887+                    }, 1000);
1888+
1889+                    showNotification("Tactical Session Recording initialized.", "success");
1890+
1891+                } catch (err) {
1892+                    console.error("Recording setup failed:", err);
1893+                    showNotification(`Recorder Setup Error: ${err.message}`, "error");
1894+                }
1895+            }
1896+        }
1897+
1898+        function stopSessionRecording() {
1899+            if (mediaRecorder && mediaRecorder.state !== 'inactive') {
1900+                mediaRecorder.stop();
1901+            }
1902+
1903+            clearInterval(sessionTimerInterval);
1904+            isRecordingSession = false;
1905+
1906+            const btn = document.getElementById('btn-rec-control');
1907+            const dot = document.getElementById('rec-dot');
1908+            const timerText = document.getElementById('rec-timer');
1909+
1910+            btn.innerHTML = `<i class="fa-solid fa-circle text-[9px] animate-pulse"></i> Start Recording Session`;
1911+            btn.className = 'w-full text-xs font-bold bg-orange-600 hover:bg-orange-500 active:scale-95 text-white py-2 rounded-lg transition-all font-mono flex items-center justify-center gap-1.5 shadow-lg shadow-orange-900/20';
1912+            dot.className = 'w-1.5 h-1.5 rounded-full bg-slate-600';
1913+            timerText.innerText = 'OFFLINE';
1914+            timerText.className = 'text-[9px] font-mono text-slate-500 uppercase';
1915+        }
1916+
1917+        function logSessionTelemetry(coords, speedVal, headingVal) {
1918+            const timestamp = new Date();
1919+            const elapsedSecs = sessionRecordStartTime ? ((timestamp.getTime() - sessionRecordStartTime) / 1000).toFixed(1) : 0;
1920+            
1921+            sessionTelemetryHistory.push({
1922+                elapsedSeconds: elapsedSecs,
1923+                utcTime: timestamp.toISOString(),
1924+                localTime: timestamp.toLocaleTimeString(),
1925+                latitude: coords ? coords.latitude : (currentTelemetry.latitude || "N/A"),
1926+                longitude: coords ? coords.longitude : (currentTelemetry.longitude || "N/A"),
1927+                pressureHpa: currentPressure.toFixed(2),
1928+                speedMph: speedVal,
1929+                heading: headingVal,
1930+                altitude: coords && coords.altitude ? coords.altitude.toFixed(1) : (currentTelemetry.altitude ? currentTelemetry.altitude.toFixed(1) : "N/A")
1931+            });
1932+        }
1933+
1934+        function exportRecordedSuite() {
1935+            if (recordedVideoChunks.length === 0) {
1936+                showNotification("No recorded files found to export.", "warning");
1937+                return;
1938+            }
1939+
1940+            const exportBtn = document.getElementById('btn-export-session');
1941+            exportBtn.setAttribute('disabled', 'true');
1942+            exportBtn.innerText = "Processing Suite...";
1943+
1944+            try {
1945+                const videoBlob = new Blob(recordedVideoChunks, { type: 'video/webm' });
1946+                const videoUrl = URL.createObjectURL(videoBlob);
1947+                
1948+                const videoAnchor = document.createElement('a');
1949+                videoAnchor.href = videoUrl;
1950+                videoAnchor.download = `nexstorm_dashcam_${Date.now()}.webm`;
1951+                document.body.appendChild(videoAnchor);
1952+                videoAnchor.click();
1953+                document.body.removeChild(videoAnchor);
1954+
1955+                let csvRows = [
1956+                    ["Elapsed Seconds", "Timestamp (UTC)", "Local Time", "Latitude", "Longitude", "Atmospheric Pressure (hPa)", "Velocity (MPH)", "Heading (Bearing)", "Altitude (Meters)"]
1957+                ];
1958+
1959+                sessionTelemetryHistory.forEach(log => {
1960+                    csvRows.push([
1961+                        log.elapsedSeconds,
1962+                        log.utcTime,
1963+                        `"${log.localTime}"`,
1964+                        log.latitude,
1965+                        log.longitude,
1966+                        log.pressureHpa,
1967+                        log.speedMph,
1968+                        `"${log.heading}"`,
1969+                        log.altitude
1970+                    ]);
1971+                });
1972+
1973+                const csvContent = csvRows.map(e => e.join(",")).join("\n");
1974+                const csvBlob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
1975+                const csvUrl = URL.createObjectURL(csvBlob);
1976+
1977+                const csvAnchor = document.createElement('a');
1978+                csvAnchor.href = csvUrl;
1979+                csvAnchor.download = `nexstorm_telemetry_${Date.now()}.csv`;
1980+                document.body.appendChild(csvAnchor);
1981+                csvAnchor.click();
1982+                document.body.removeChild(csvAnchor);
1983+
1984+                showNotification("Export suite downloaded successfully!", "success");
1985+
1986+            } catch (err) {
1987+                console.error("Export process collapsed:", err);
1988+                showNotification(`Export collapsed: ${err.message}`, "error");
1989+            } finally {
1990+                exportBtn.removeAttribute('disabled');
1991+                exportBtn.className = 'w-full text-xs font-bold bg-orange-600 hover:bg-orange-500 active:scale-95 text-white py-2 rounded-lg transition-all font-mono flex items-center justify-center gap-1.5 shadow-lg shadow-orange-900/25';
1992+                exportBtn.innerHTML = `<i class="fa-solid fa-file-export"></i> Export Intercept logs`;
1993+            }
1994+        }
1995+
1996+        // --- Synthesized Procedural Audio System (Tone.js) ---
1997+        function toggleSound() {
1998+            if (!isSoundEnabled) {
1999+                Tone.start();
2000+                synth = new Tone.PolySynth(Tone.Synth).toDestination();
2001+                synth.set({
2002+                    oscillator: { type: "triangle" },
2003+                    envelope: { attack: 0.05, decay: 0.1, sustain: 0.3, release: 0.8 }
2004+                });
2005+                
2006+                isSoundEnabled = true;
2007+                document.getElementById('sound-icon').className = 'fa-solid fa-volume-high text-emerald-400 animate-pulse';
2008+                document.getElementById('sound-text').innerText = 'Audio Enabled';
2009+                
2010+                synth.triggerAttackRelease(["C4", "E4", "G4"], "0.3");
2011+            } else {
2012+                isSoundEnabled = false;
2013+                document.getElementById('sound-icon').className = 'fa-solid fa-volume-mute text-slate-500';
2014+                document.getElementById('sound-text').innerText = 'Mute Audio';
2015+            }
2016+        }
2017+
2018+        function playWarningChime(eventType) {
2019+            if (!isSoundEnabled || !synth) return;
2020+            
2021+            if (eventType.includes('Tornado')) {
2022+                synth.triggerAttackRelease(["A5", "C6"], "0.2");
2023+                setTimeout(() => synth.triggerAttackRelease(["A5", "C6"], "0.2"), 300);
2024+            } else if (eventType.includes('Severe')) {
2025+                synth.triggerAttackRelease(["E5", "G5"], "0.15");
2026+            } else {
2027+                synth.triggerAttackRelease(["C4", "G4"], "0.3");
2028+            }
2029+        }
2030+
2031+        function triggerSirens() {
2032+            if (!isSoundEnabled || !synth) return;
2033+            const now = Tone.now();
2034+            synth.triggerAttackRelease("C5", "0.5", now);
2035+            synth.triggerAttackRelease("F5", "0.5", now + 0.6);
2036+        }
2037+
2038+        // --- Full Screen Detail Modals ---
2039+        function viewFullAlert(id) {
2040+            const alert = allNwsAlerts.find(a => a.properties.id === id);
2041+            if (!alert) return;
2042+
2043+            const p = alert.properties;
2044+            
2045+            document.getElementById('modal-title').innerText = p.event || 'Severe Warning';
2046+            document.getElementById('modal-badge').innerText = p.severity || 'Moderate';
2047+            
2048+            const badge = document.getElementById('modal-badge');
2049+            if (p.event.includes('Tornado')) {
2050+                badge.className = 'text-[10px] font-mono px-2 py-0.5 rounded border uppercase bg-red-950 text-red-400 border-red-800';
2051+            } else if (p.event.includes('Severe')) {
2052+                badge.className = 'text-[10px] font-mono px-2 py-0.5 rounded border uppercase bg-yellow-950 text-yellow-400 border-yellow-800';
2053+            } else {
2054+                badge.className = 'text-[10px] font-mono px-2 py-0.5 rounded border uppercase bg-slate-900 text-slate-300 border-slate-700';
2055+            }
2056+
2057+            document.getElementById('modal-sub').innerText = `ID: ${p.id || 'NWS'}`;
2058+            document.getElementById('modal-headline').innerText = p.headline || 'No specific headline provided.';
2059+            document.getElementById('modal-areas').innerText = p.areaDesc || 'US Regional Coordinates';
2060+            document.getElementById('modal-expires').innerText = new Date(p.expires).toLocaleString();
2061+            document.getElementById('modal-severity').innerText = `${p.severity || 'N/A'} Severity | ${p.urgency || 'N/A'} Urgency`;
2062+            document.getElementById('modal-desc').innerText = p.description || 'No extended description available.';
2063+            document.getElementById('modal-instruction').innerText = p.instruction || 'Monitor local weather broadcasts and instructions.';
2064+
2065+            document.getElementById('modal-alert').classList.remove('hidden');
2066+        }
2067+
2068+        function closeModal() {
2069+            document.getElementById('modal-alert').classList.add('hidden');
2070+        }
2071+
2072+        // --- Mobile Responsive Drawer Handler ---
2073+        function toggleSidebar() {
2074+            const sidebar = document.getElementById('alert-sidebar');
2075+            if (sidebar.classList.contains('-translate-x-full')) {
2076+                sidebar.classList.remove('-translate-x-full');
2077+            } else {
2078+                sidebar.classList.add('-translate-x-full');
2079+            }
2080+        }
2081+
2082+        // --- STREAMING_CHUNK: Enhancing camera size controls for both height and width ---
2083+        function setupWebcamSizer() {
2084+            const container = document.getElementById('dashcam-container');
2085+            const heightSlider = document.getElementById('slider-cam-height');
2086+            const heightLabel = document.getElementById('lbl-cam-height');
2087+            const widthSlider = document.getElementById('slider-cam-width');
2088+            const widthLabel = document.getElementById('lbl-cam-width');
2089+            const videoEl = document.getElementById('dashcam-video');
2090+            const btnPip = document.getElementById('btn-pip');
2091+
2092+            const setHeight = (val) => {
2093+                container.style.height = `${val}px`;
2094+                heightSlider.value = val;
2095+                heightLabel.innerText = `${val}px`;
2096+            };
2097+
2098+            const setWidth = (val) => {
2099+                if (val >= 320 && !isTelemetryCollapsed) {
2100+                    container.style.width = '100%';
2101+                    widthLabel.innerText = '100%';
2102+                } else {
2103+                    container.style.width = `${val}px`;
2104+                    widthLabel.innerText = `${val}px`;
2105+                }
2106+                widthSlider.value = val;
2107+            };
2108+
2109+            heightSlider.addEventListener('input', (e) => {
2110+                setHeight(e.target.value);
2111+            });
2112+
2113+            widthSlider.addEventListener('input', (e) => {
2114+                setWidth(e.target.value);
2115+            });
2116+
2117+            document.getElementById('btn-size-sm').addEventListener('click', () => setHeight(120));
2118+            document.getElementById('btn-size-md').addEventListener('click', () => setHeight(180));
2119+            document.getElementById('btn-size-lg').addEventListener('click', () => setHeight(300));
2120+
2121+            btnPip.addEventListener('click', async () => {
2122+                if (!document.pictureInPictureEnabled) {
2123+                    showNotification("Picture-in-Picture mode is not supported in this browser.", "warning");
2124+                    return;
2125+                }
2126+                if (!cameraStream) {
2127+                    showNotification("Connect your webcam feed first to use Picture-in-Picture.", "info");
2128+                    return;
2129+                }
2130+                try {
2131+                    if (document.pictureInPictureElement) {
2132+                        await document.exitPictureInPicture();
2133+                    } else {
2134+                        await videoEl.requestPictureInPicture();
2135+                    }
2136+                } catch (err) {
2137+                    console.error("Picture-in-Picture failure:", err);
2138+                }
2139+            });
2140+        }
2141+
2142+        // --- Handling DOM widget shifting on collapse ---
2143+        function setTelemetrySidebarCollapsed(collapsed) {
2144+            isTelemetryCollapsed = collapsed;
2145+            const sidebar = document.getElementById('telemetry-sidebar');
2146+            const icon = document.querySelector('#btn-close-telemetry i');
2147+            
2148+            if (isTelemetryCollapsed) {
2149+                sidebar.classList.add('translate-x-full', 'md:translate-x-full');
2150+                sidebar.classList.remove('translate-x-0', 'md:translate-x-0');
2151+                if (icon) {
2152+                    icon.className = 'fa-solid fa-angles-left text-xs';
2153+                }
2154+            } else {
2155+                sidebar.classList.remove('translate-x-full', 'md:translate-x-full');
2156+                sidebar.classList.add('translate-x-0', 'md:translate-x-0');
2157+                if (icon) {
2158+                    icon.className = 'fa-solid fa-angles-right text-xs';
2159+                }
2160+            }
2161+            
2162+            updateWidgetsPlacement();
2163+        }
2164+
2165+        function toggleTelemetry() {
2166+            setTelemetrySidebarCollapsed(!isTelemetryCollapsed);
2167+        }
2168+
2169+        // Move widgets between the Collapsible Sidebar and the Map's Floating Top-Right Box
2170+        function updateWidgetsPlacement() {
2171+            const camWidget = document.getElementById('dashcam-widget-wrapper');
2172+            const geoWidget = document.getElementById('geolocation-widget-wrapper');
2173+            const baroWidget = document.getElementById('barometer-widget-wrapper');
2174+            const corridorWidget = document.getElementById('corridor-widget-wrapper');
2175+
2176+            const floatingBox = document.getElementById('floating-telemetry-box');
2177+            const sidebarParent = document.getElementById('telemetry-widgets-container');
2178+            const recorderWidget = document.getElementById('recorder-widget-wrapper');
2179+
2180+            if (!camWidget || !geoWidget || !baroWidget || !corridorWidget || !floatingBox || !sidebarParent) return;
2181+
2182+            if (isTelemetryCollapsed) {
2183+                floatingBox.appendChild(camWidget);
2184+                floatingBox.appendChild(geoWidget);
2185+                floatingBox.appendChild(baroWidget);
2186+                floatingBox.appendChild(corridorWidget);
2187+                
2188+                camWidget.style.maxWidth = '320px';
2189+                geoWidget.style.maxWidth = '320px';
2190+                baroWidget.style.maxWidth = '320px';
2191+                corridorWidget.style.maxWidth = '320px';
2192+                
2193+                floatingBox.classList.remove('hidden');
2194+                floatingBox.classList.add('flex');
2195+            } else {
2196+                sidebarParent.insertBefore(baroWidget, recorderWidget);
2197+                sidebarParent.insertBefore(corridorWidget, recorderWidget);
2198+                sidebarParent.insertBefore(camWidget, recorderWidget);
2199+                sidebarParent.insertBefore(geoWidget, recorderWidget);
2200+                
2201+                camWidget.style.maxWidth = '';
2202+                geoWidget.style.maxWidth = '';
2203+                baroWidget.style.maxWidth = '';
2204+                corridorWidget.style.maxWidth = '';
2205+                
2206+                floatingBox.classList.add('hidden');
2207+                floatingBox.classList.remove('flex');
2208+            }
2209+        }
2210+
2211+        function toggleNightVision() {
2212+            const body = document.body;
2213+            const btn = document.getElementById('btn-night-vision');
2214+            if (body.classList.contains('night-vision-mode')) {
2215+                body.classList.remove('night-vision-mode');
2216+                btn.className = 'bg-slate-800 hover:bg-slate-700 active:scale-95 border border-slate-700/50 p-2 rounded-lg text-rose-500 hover:text-rose-400 transition-all';
2217+                showNotification("Tactical Night Vision deactivated.", "info");
2218+            } else {
2219+                body.classList.add('night-vision-mode');
2220+                btn.className = 'bg-rose-950/40 border border-rose-800 text-rose-400 p-2 rounded-lg transition-all animate-pulse';
2221+                showNotification("Tactical Night Vision activated. Monochromatic eyes protection adaptive matrix synced.", "success");
2222+            }
2223+        }
2224+
2225+        // --- Core App Interactions ---
2226+        function setupEventListeners() {
2227+            setupWebcamSizer();
2228+
2229+            // Opacity Handler
2230+            document.getElementById('radar-opacity').addEventListener('input', (e) => {
2231+                const val = e.target.value;
2232+                document.getElementById('opacity-val').innerText = `${val}%`;
2233+                
2234+                radarLayers.forEach(layer => {
2235+                    layer.setOpacity(parseFloat(val) / 100);
2236+                });
2237+            });
2238+
2239+            // Cloud Layer Action Listeners
2240+            document.getElementById('cloud-select').addEventListener('change', updateCloudLayers);
2241+            document.getElementById('cloud-opacity').addEventListener('input', (e) => {
2242+                const val = e.target.value;
2243+                document.getElementById('cloud-opacity-val').innerText = `${val}%`;
2244+                if (cloudLayerEast) cloudLayerEast.setOpacity(parseFloat(val) / 100);
2245+                if (cloudLayerWest) cloudLayerWest.setOpacity(parseFloat(val) / 100);
2246+            });
2247+
2248+            // Playback Slider Handler
2249+            document.getElementById('timeline-slider').addEventListener('input', (e) => {
2250+                const val = parseInt(e.target.value);
2251+                switchFrame(val);
2252+            });
2253+
2254+            // Loop Controls Buttons
2255+            document.getElementById('btn-loop-play').addEventListener('click', togglePlayback);
2256+            document.getElementById('btn-loop-prev').addEventListener('click', () => {
2257+                let prev = timelineIndex - 1;
2258+                if (prev < 0) prev = radarLayers.length - 1;
2259+                switchFrame(prev);
2260+            });
2261+            document.getElementById('btn-loop-next').addEventListener('click', () => {
2262+                let next = timelineIndex + 1;
2263+                if (next >= radarLayers.length) next = 0;
2264+                switchFrame(next);
2265+            });
2266+
2267+            // Switch Radar Layer type
2268+            document.getElementById('layer-select').addEventListener('change', loadRadarLayers);
2269+
2270+            // Sound Toggle Keybind
2271+            document.getElementById('sound-toggle').addEventListener('click', toggleSound);
2272+
2273+            // Night Vision Toggle
2274+            document.getElementById('btn-night-vision').addEventListener('click', toggleNightVision);
2275+
2276+            // Baro meso collapse trigger
2277+            document.getElementById('btn-baro-drop').addEventListener('click', triggerMesoPressureCollapse);
2278+
2279+            // Intercept Draw Planner controls
2280+            document.getElementById('btn-draw-planner').addEventListener('click', toggleCorridorPlanner);
2281+            document.getElementById('btn-clear-corridor').addEventListener('click', clearCorridorLayer);
2282+            document.getElementById('input-cell-speed').addEventListener('change', calculateStormCorridor);
2283+
2284+            // SPC Convective outlook checkbox
2285+            document.getElementById('chk-spc').addEventListener('change', (e) => {
2286+                toggleSpcOutlook(e.target.checked);
2287+            });
2288+
2289+            // METAR Weather checkbox
2290+            document.getElementById('chk-metar').addEventListener('change', (e) => {
2291+                toggleMetarObservations(e.target.checked);
2292+            });
2293+
2294+            // Geolocation and Telemetry Panel Controls
2295+            document.getElementById('btn-gps').addEventListener('click', toggleGpsTracking);
2296+            document.getElementById('btn-telemetry-toggle').addEventListener('click', toggleTelemetry);
2297+            document.getElementById('btn-close-telemetry').addEventListener('click', () => setTelemetrySidebarCollapsed(true));
2298+            document.getElementById('btn-camera-power').addEventListener('click', toggleCameraPower);
2299+            document.getElementById('btn-rec-control').addEventListener('click', toggleSessionRecording);
2300+            document.getElementById('btn-export-session').addEventListener('click', exportRecordedSuite);
2301+
2302+            // Search Handlers
2303+            document.getElementById('search-btn').addEventListener('click', performSearch);
2304+            document.getElementById('search-input').addEventListener('keypress', (e) => {
2305+                if (e.key === 'Enter') performSearch();
2306+            });
2307+        }
2308+    </script>
2309+</body>
2310+</html>