Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>Coastal Surveillance Simulator</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Saira+Condensed:wght@400;500&display=swap" rel="stylesheet"> | |
| <link rel="stylesheet" href="style.css" /> | |
| </head> | |
| <body> | |
| <div id="app"> | |
| <div class="coastal-surveillance-sim"> | |
| <div id="header"> | |
| <span class="blink" aria-hidden="true"></span> | |
| <h1>Coastal Surveillance Sim</h1> | |
| <span class="sub">// COASTKEEPER - ONEBERRY</span> | |
| <div class="spacer"></div> | |
| <div class="theme-toggle" id="theme-toggle"> | |
| <button type="button" class="theme-btn" data-theme-val="light" title="Light theme" aria-label="Light theme"> | |
| <svg width="18" height="18" viewBox="0 0 16 16" fill="none" aria-hidden="true"> | |
| <circle cx="8" cy="8" r="3" stroke="currentColor" stroke-width="1.5"/> | |
| <line x1="8" y1="1" x2="8" y2="3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/> | |
| <line x1="8" y1="13" x2="8" y2="15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/> | |
| <line x1="1" y1="8" x2="3" y2="8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/> | |
| <line x1="13" y1="8" x2="15" y2="8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/> | |
| <line x1="3.05" y1="3.05" x2="4.46" y2="4.46" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/> | |
| <line x1="11.54" y1="11.54" x2="12.95" y2="12.95" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/> | |
| <line x1="12.95" y1="3.05" x2="11.54" y2="4.46" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/> | |
| <line x1="4.46" y1="11.54" x2="3.05" y2="12.95" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/> | |
| </svg> | |
| </button> | |
| <button type="button" class="theme-btn" data-theme-val="dark" title="Dark theme" aria-label="Dark theme"> | |
| <svg width="18" height="18" viewBox="0 0 16 16" fill="none" aria-hidden="true"> | |
| <path d="M13.5 10.5A6 6 0 0 1 5.5 2.5a6 6 0 1 0 8 8z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> | |
| </svg> | |
| </button> | |
| <button type="button" class="theme-btn" data-theme-val="system" title="System theme" aria-label="System theme"> | |
| <svg width="18" height="18" viewBox="0 0 16 16" fill="none" aria-hidden="true"> | |
| <rect x="1" y="2" width="14" height="10" rx="1.5" stroke="currentColor" stroke-width="1.5"/> | |
| <line x1="5" y1="14" x2="11" y2="14" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/> | |
| <line x1="8" y1="12" x2="8" y2="14" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/> | |
| </svg> | |
| </button> | |
| </div> | |
| </div> | |
| <div id="canvas-wrap"> | |
| <svg id="c"></svg> | |
| <div class="dir-bar" role="group" aria-label="Camera rotation direction"> | |
| <button type="button" class="dir-btn" id="dir-cw">↻ CW</button> | |
| <button type="button" class="dir-btn" id="dir-ccw">↺ CCW</button> | |
| </div> | |
| <div class="zoom-bar"> | |
| <button type="button" class="zoom-btn" id="zoom-out" aria-label="Zoom out">−</button> | |
| <span id="zoom-label" class="zoom-label">100%</span> | |
| <button type="button" class="zoom-btn" id="zoom-in" aria-label="Zoom in">+</button> | |
| </div> | |
| </div> | |
| <div id="panel"> | |
| <div class="section"> | |
| <div class="section-title">Status</div> | |
| <div class="stats-grid"> | |
| <div class="stat"> | |
| <div class="lbl">Scan time</div> | |
| <div id="st-scan" class="val">—</div> | |
| </div> | |
| <div class="stat"> | |
| <div class="lbl">Boat dist.</div> | |
| <div id="st-dist" class="val">—</div> | |
| </div> | |
| <div class="stat"> | |
| <div class="lbl">Detected</div> | |
| <div id="st-vis" class="val">—</div> | |
| </div> | |
| <div class="stat stat-risk" title="Blind metres ÷ (blind + detected metres) from your last completed in-FOV and out-of-FOV spells. The bar fills only after their combined length reaches a minimum (avoids noisy early readings)."> | |
| <div class="lbl">Blind share</div> | |
| <div class="risk-row"> | |
| <div id="st-risk" class="val st-risk-val">—</div> | |
| <div class="risk-track"> | |
| <div id="st-risk-bar" class="risk-fill"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="stats-extra"> | |
| <div class="stat"> | |
| <div class="lbl">Detected dist.</div> | |
| <div id="st-travel" class="val st-sm">—</div> | |
| <div id="st-travel-prev" class="stat-sub">prev: —</div> | |
| </div> | |
| <div class="stat"> | |
| <div class="lbl">Blind dist.</div> | |
| <div id="st-blind" class="val st-sm">—</div> | |
| <div id="st-blind-prev" class="stat-sub">prev: —</div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="section"> | |
| <div class="section-title">Camera</div> | |
| <div class="section-body"> | |
| <div class="ctrl"> | |
| <label for="camera-preset">Presets</label> | |
| <select id="camera-preset" class="preset-select" aria-label="Apply a camera preset"> | |
| <option value="" selected>—</option> | |
| <option value="vos1">VOS #1 (default)</option> | |
| <option value="prop1">Proposal #1</option> | |
| <option value="prop2">Proposal #2</option> | |
| </select> | |
| </div> | |
| <div class="ctrl"> | |
| <label for="input-fov">FOV <span id="val-fov">18</span>°</label> | |
| <input id="input-fov" type="range" min="5" max="90" step="1" value="18" /> | |
| </div> | |
| <div class="ctrl"> | |
| <label for="input-front-speed">Front speed <span id="val-front-speed">6</span>°/s</label> | |
| <input id="input-front-speed" type="range" min="1" max="30" step="1" value="6" /> | |
| </div> | |
| <div class="ctrl"> | |
| <label for="input-back-speed">Back speed <span id="val-back-speed">60</span>°/s</label> | |
| <input id="input-back-speed" type="range" min="5" max="180" step="5" value="60" /> | |
| </div> | |
| <div class="ctrl"> | |
| <label for="input-front-arc">Front arc <span id="val-front-arc">180</span>°</label> | |
| <input id="input-front-arc" type="range" min="10" max="350" step="5" value="180" /> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="section"> | |
| <div class="section-title">Boat</div> | |
| <div class="section-body"> | |
| <div class="ctrl"> | |
| <label for="input-boat-speed">Speed <span id="val-boat-speed">20</span> kt</label> | |
| <input id="input-boat-speed" type="range" min="1" max="60" step="1" value="20" /> | |
| </div> | |
| <div class="hint"> | |
| Drag <span class="hint-s">S</span> or <span class="hint-e">E</span> anchors on canvas to reposition | |
| trajectory. | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script src="app.js" defer></script> | |
| </body> | |
| </html> |