Files
2026-05-04 08:06:34 +03:00

1360 lines
56 KiB
CSS

*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%;overflow-x:hidden;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#1a1a2e;color:#e0e0e0;overscroll-behavior-y:contain}
/* ===== Navbar ===== */
#navbar{display:flex;align-items:center;height:44px;background:#16213e;border-bottom:1px solid #0f3460;padding:0 12px;z-index:2000;position:relative}
.nav-brand{font-size:15px;font-weight:700;color:#d2ff1a;margin-right:20px;white-space:nowrap}
.nav-tabs{display:flex;gap:2px}
.nav-tab{padding:8px 16px;color:#8899aa;font-size:13px;cursor:pointer;border-radius:4px 4px 0 0;transition:background .15s,color .15s;text-decoration:none;user-select:none}
.nav-tab:hover{color:#ccc;background:rgba(255,255,255,.05)}
.nav-tab.active{color:#d2ff1a;background:#1a1a2e;font-weight:600}
.hamburger{display:none;background:none;border:none;color:#d2ff1a;font-size:22px;cursor:pointer;padding:4px 8px}
/* Connection banner (top-right) */
.conn-banner{
/* Make it global, centered, high-contrast (works on light maps too) */
position:fixed;
left:50%;
top:54px; /* below navbar */
transform:translateX(-50%);
z-index:2500;
padding:7px 12px;
border-radius:999px;
font-size:12px;
font-weight:800;
white-space:nowrap;
max-width:80vw;
overflow:hidden;
text-overflow:ellipsis;
border:1px solid transparent;
box-shadow:0 6px 18px rgba(0,0,0,.45);
backdrop-filter: blur(6px);
}
.conn-banner--offline{background:rgba(248,81,73,.18);border-color:rgba(248,81,73,.55);color:#ffb4b0}
.conn-banner--online{background:rgba(63,185,80,.14);border-color:rgba(63,185,80,.5);color:#7ee787}
@media(max-width:600px){
.nav-tabs{display:none;position:absolute;top:44px;left:0;right:0;background:#16213e;flex-direction:column;border-bottom:1px solid #0f3460;z-index:2001}
.nav-tabs.open{display:flex}
.nav-tab{padding:12px 20px;border-radius:0}
.hamburger{display:block}
}
/* ===== Tab pages ===== */
.tab-page{display:none;height:calc(100vh - 44px);overflow:hidden;position:relative}
@supports (height: 100dvh){
.tab-page{height:calc(100dvh - 44px)}
}
.tab-page.active{display:block}
/* ===== Map page ===== */
#map{width:100%;height:100%;background:#0e1a2b}
#status{position:absolute;z-index:1000;top:8px;left:8px;background:rgba(22,33,62,.92);padding:6px 12px;border-radius:4px;font-size:13px;color:#d2ff1a;box-shadow:0 2px 6px rgba(0,0,0,.4)}
#sidebar{position:absolute;z-index:1000;top:8px;right:8px;width:280px;max-height:calc(100% - 16px);background:rgba(22,33,62,.95);padding:10px;border-radius:6px;box-shadow:0 2px 8px rgba(0,0,0,.4);font-size:12px;display:flex;flex-direction:column;overflow:hidden}
#sidebar h3{margin:0 0 8px;font-size:13px;border-bottom:1px solid #0f3460;padding-bottom:5px;color:#8899aa;flex-shrink:0}
.vessel-sidebar-heading{font-size:15px;margin:0 0 8px}
.vessel-count-paren{font-weight:600;color:#c9d1d9}
#vessel-count-suffix{color:#8b949e;font-weight:400}
.vessel-list-toolbar{margin-bottom:8px;flex-shrink:0}
.vessel-search-input{width:100%;box-sizing:border-box;padding:6px 8px;margin-bottom:6px;border:1px solid #30363d;border-radius:4px;background:#0d1117;color:#c9d1d9;font-size:12px}
.vessel-search-input::placeholder{color:#6e7681}
.vessel-search-input:focus{outline:none;border-color:#4fc3f7}
.vessel-toolbar-row{display:flex;flex-wrap:wrap;gap:6px 10px;align-items:flex-end}
.vessel-toolbar-label{font-size:10px;color:#8b949e;display:flex;flex-direction:column;gap:2px;min-width:0}
.vessel-toolbar-select{max-width:140px;padding:4px 6px;border-radius:4px;border:1px solid #30363d;background:#161b22;color:#c9d1d9;font-size:11px}
#vessel-list{flex:1;overflow-y:auto;min-height:0}
#range-filter{flex-shrink:0}
.leaflet-control-zoom{display:none}
.leaflet-control-attribution{display:none}
.leaflet-control-layers{background:rgba(22,33,62,.95)!important;border:1px solid #0f3460!important;border-radius:6px!important;color:#e0e0e0!important;box-shadow:0 2px 8px rgba(0,0,0,.4)!important}
.leaflet-control-layers label{color:#ccd}
.leaflet-control-layers-toggle{width:30px!important;height:30px!important;background-color:rgba(22,33,62,.92)!important;border:1px solid #0f3460!important;border-radius:4px!important}
.leaflet-control-layers-expanded{padding:8px 12px 6px!important}
.leaflet-control-layers-base label{margin-bottom:2px;display:flex;align-items:center;gap:6px;font-size:13px;cursor:pointer}
/* Compass UI (inside OwnShip panel) */
.os-compass-row{display:flex;align-items:center;justify-content:space-between;gap:10px}
.os-compass{display:inline-flex;align-items:center;gap:8px}
.compass-toggle{
width:28px;height:28px;
border-radius:8px;
border:1px solid #0f3460;
background:#16213e;
cursor:pointer;
display:flex;
align-items:center;
justify-content:center;
}
.compass-toggle:hover{border-color:#4fc3f7}
.compass-toggle-dot{
width:10px;height:10px;border-radius:999px;
background:#6e7681;
box-shadow:0 0 0 2px rgba(0,0,0,.25) inset;
}
.compass--rotate-on .compass-toggle-dot{background:#d2ff1a}
.compass-dial{
position:relative;
width:34px;height:34px;
border-radius:999px;
border:1px solid rgba(210,255,26,.25);
background:rgba(13,17,23,.55);
display:flex;
align-items:center;
justify-content:center;
}
.compass-arrow{
width:28px;height:28px;
transform-origin:50% 50%;
will-change:transform;
}
.compass-n{
position:absolute;
top:-7px;
left:50%;
transform:translateX(-50%);
font-size:10px;
font-weight:900;
color:#d2ff1a;
text-shadow:0 1px 0 rgba(0,0,0,.65);
user-select:none;
pointer-events:none;
}
.compass--no-heading{
opacity:.55;
border-color:rgba(139,148,158,.35);
}
.vessel-item{padding:7px;margin:4px 0;border:1px solid #0f3460;border-radius:4px;cursor:pointer;transition:background .15s}
.vessel-item:hover{background:rgba(255,255,255,.05)}
.vessel-item.selected{background:rgba(210,255,26,.1);border-color:#d2ff1a}
.vessel-item.vessel-item--no-pos{opacity:.55;cursor:default}
.vessel-item.vessel-item--no-pos:hover{background:transparent}
.vessel-item.vessel-item--no-pos .mmsi{color:#8b949e}
.vessel-item .coords.coords--no-pos{color:#8b949e;font-style:italic}
.vessel-item .vessel-mmsi-row{display:flex;align-items:center;justify-content:space-between;gap:8px}
.vessel-item .mmsi{font-weight:700;color:#4fc3f7;flex:1;min-width:0;word-break:break-all}
.vessel-item .vessel-flag{font-size:18px;line-height:1;flex-shrink:0;opacity:.95}
.vessel-item .callsign-row{font-size:11px;color:#8b949e}
.vessel-item .name{color:#8899aa;font-size:11px}
.vessel-item .coords{color:#667;font-size:10px}
#range-filter{border-top:1px solid #0f3460;margin-top:8px;padding-top:6px}
#range-label{font-size:11px;color:#8899aa;margin-bottom:4px}
#range-slider{width:100%;height:4px;-webkit-appearance:none;appearance:none;background:#0f3460;border-radius:2px;outline:none;cursor:pointer}
#range-slider::-webkit-slider-thumb{-webkit-appearance:none;width:14px;height:14px;border-radius:50%;background:#d2ff1a;cursor:pointer;border:none}
#range-slider::-moz-range-thumb{width:14px;height:14px;border-radius:50%;background:#d2ff1a;cursor:pointer;border:none}
/* Generic sliders (danger radii) */
input[type="range"]#set-warn-radius-slider,
input[type="range"]#set-near-radius-slider{
height:4px;
-webkit-appearance:none;
appearance:none;
background:#0f3460;
border-radius:2px;
outline:none;
cursor:pointer;
}
input[type="range"]#set-warn-radius-slider::-webkit-slider-thumb{
-webkit-appearance:none;
width:14px;height:14px;border-radius:50%;
background:#f85149;
border:none;
}
input[type="range"]#set-warn-radius-slider::-moz-range-thumb{
width:14px;height:14px;border-radius:50%;
background:#f85149;border:none;
}
input[type="range"]#set-near-radius-slider::-webkit-slider-thumb{
-webkit-appearance:none;
width:14px;height:14px;border-radius:50%;
background:#f0883e;
border:none;
}
input[type="range"]#set-near-radius-slider::-moz-range-thumb{
width:14px;height:14px;border-radius:50%;
background:#f0883e;border:none;
}
.vessel-item .dist{color:#d2ff1a;font-size:10px;font-weight:600;display:inline-flex;gap:6px;align-items:baseline}
.vessel-item .dist .dist-val{color:#d2ff1a;font-weight:700}
.vessel-item .dist .brg-val{color:#8b949e;font-weight:600}
.vessel-item .dist .brg-val::before{content:"\2192 ";opacity:.6}
/* Unified SOG/COG/HDG stats row (desktop). Mobile variants override this via
.sidebar--compact and .targets-list selectors later in the file. */
.vessel-item .compact-stats{
display:flex;flex-wrap:wrap;gap:4px 8px;
margin-top:4px;
font-family:ui-monospace,Menlo,Consolas,monospace;
font-size:11px;
}
.vessel-item .compact-stats > span{
display:inline-flex;gap:4px;align-items:baseline;
padding:1px 5px;
background:rgba(15,52,96,.4);
border:1px solid rgba(79,195,247,.16);
border-radius:3px;
cursor:help;
white-space:nowrap;
}
.vessel-item .compact-stats > span:hover{border-color:rgba(79,195,247,.45);background:rgba(15,52,96,.7)}
.vessel-item .compact-stats .k{color:#8b949e;font-weight:600;font-size:9px;letter-spacing:.4px;text-transform:uppercase}
.vessel-item .compact-stats b{color:#e0e0e0;font-weight:700}
#cursor-coords{position:absolute;z-index:1000;bottom:8px;left:8px;background:rgba(22,33,62,.9);padding:5px 10px;border-radius:4px;font-family:monospace;font-size:11px;color:#8899aa}
/* Danger banner (ownship proximity) */
.global-banners{
position:fixed;
left:50%;
top:92px; /* stacked under conn-banner */
transform:translateX(-50%);
z-index:2490;
display:flex;
flex-direction:column;
align-items:center;
gap:8px;
pointer-events:none;
}
.danger-banner{
padding:9px 16px;
border-radius:999px;
background:rgba(248,81,73,.92);
border:2px solid rgba(0,0,0,.35);
color:#ffffff;
font-size:14px;
font-weight:900;
letter-spacing:.8px;
text-transform:uppercase;
box-shadow:0 10px 26px rgba(0,0,0,.55);
user-select:none;
pointer-events:none;
}
/* Highlight nearby targets */
.leaflet-marker-icon.vessel-nearby{
filter: drop-shadow(0 0 5px rgba(210,255,26,.9)) drop-shadow(0 0 10px rgba(210,255,26,.35));
}
.vessel-icon{will-change:transform}
.vessel-icon--svg{line-height:0;display:flex;align-items:flex-end;justify-content:center}
.vessel-icon--svg svg{vertical-align:bottom}
.leaflet-marker-icon{transition:none!important}
.vessel-overlay-icon{z-index:1000!important;pointer-events:none}
.vessel-overlay-lost{
background:transparent!important;
border:0!important;
}
.vessel-lost-dot{
display:block;
width:16px;
height:16px;
border-radius:50%;
background:#8b949e;
border:1px solid #111;
box-shadow:0 1px 3px rgba(0,0,0,.75);
}
/* AIS aids (base station / buoy) */
.ais-base-station-icon{
/* base_station.svg is black strokes only; make it visible on dark map */
filter: invert(1) brightness(1.25) drop-shadow(0 0 2px rgba(0,0,0,.8));
}
.ais-buoy-icon{
filter: drop-shadow(0 0 2px rgba(0,0,0,.75));
}
.ais-bs-divicon,.ais-aton-divicon{
background:transparent!important;
border:0!important;
}
.ais-bs-symbol,.ais-aton-symbol{
position:relative;
width:30px;
height:30px;
box-sizing:border-box;
display:flex;
align-items:center;
justify-content:center;
color:#f0f6fc;
text-shadow:0 1px 2px rgba(0,0,0,.9);
filter:drop-shadow(0 1px 3px rgba(0,0,0,.8));
}
.ais-bs-symbol{
border:2px solid #4fc3f7;
border-radius:50%;
background:#0d1117;
}
.ais-bs-mast{
position:absolute;
left:14px;
top:7px;
width:2px;
height:17px;
background:#c9d1d9;
border-radius:2px;
}
.ais-bs-mast::before,.ais-bs-mast::after{
content:"";
position:absolute;
left:-6px;
width:14px;
height:2px;
background:#c9d1d9;
border-radius:2px;
}
.ais-bs-mast::before{top:5px}
.ais-bs-mast::after{top:17px}
.ais-bs-waves::before,.ais-bs-waves::after{
content:"";
position:absolute;
left:50%;
transform:translateX(-50%);
border:2px solid #4fc3f7;
border-bottom:0;
border-radius:18px 18px 0 0;
}
.ais-bs-waves::before{top:4px;width:16px;height:8px}
.ais-bs-waves::after{top:1px;width:24px;height:12px;opacity:.75}
.ais-aton-symbol{
border:2px solid #0d1117;
border-radius:50%;
background:#667;
}
.ais-aton-symbol--fixed{border-radius:6px}
.ais-aton-symbol--floating{border-radius:50% 50% 48% 48%}
.ais-aton-label{
z-index:1;
font-size:10px;
font-weight:900;
line-height:1;
letter-spacing:0;
color:#fff;
}
.ais-aton-symbol--cardinal-n{background:linear-gradient(180deg,#111 0 50%,#ffd84a 50% 100%)}
.ais-aton-symbol--cardinal-e{background:linear-gradient(180deg,#111 0 33%,#ffd84a 33% 66%,#111 66% 100%)}
.ais-aton-symbol--cardinal-s{background:linear-gradient(180deg,#ffd84a 0 50%,#111 50% 100%)}
.ais-aton-symbol--cardinal-w{background:linear-gradient(180deg,#ffd84a 0 33%,#111 33% 66%,#ffd84a 66% 100%)}
.ais-aton-symbol--lateral-port{background:#d72525}
.ais-aton-symbol--lateral-starboard{background:#168a45}
.ais-aton-symbol--preferred-port{background:linear-gradient(180deg,#d72525 0 35%,#168a45 35% 65%,#d72525 65% 100%)}
.ais-aton-symbol--preferred-starboard{background:linear-gradient(180deg,#168a45 0 35%,#d72525 35% 65%,#168a45 65% 100%)}
.ais-aton-symbol--isolated-danger{background:linear-gradient(180deg,#111 0 30%,#d72525 30% 70%,#111 70% 100%)}
.ais-aton-symbol--safe-water{background:linear-gradient(90deg,#d72525 0 32%,#fff 32% 68%,#d72525 68% 100%)}
.ais-aton-symbol--safe-water .ais-aton-label{color:#111;text-shadow:0 1px 2px rgba(255,255,255,.75)}
.ais-aton-symbol--special{background:#ffd84a}
.ais-aton-symbol--special .ais-aton-label{color:#111;text-shadow:none}
.ais-aton-symbol--wreck{background:linear-gradient(90deg,#1976d2 0 50%,#ffd84a 50% 100%)}
.ais-aton-symbol--light{background:#f7f7f7}
.ais-aton-symbol--light .ais-aton-label{color:#111;text-shadow:none}
.ais-aton-symbol--leading{background:#8b5cf6}
.ais-aton-symbol--racon{background:#c678dd}
.ais-aton-symbol--structure{background:#6e7681}
.ais-aton-symbol--light-vessel{background:#f0883e}
.ais-aton-symbol--reference,.ais-aton-symbol--generic{background:#30363d}
.ais-aid--virtual .ais-bs-symbol::after,.ais-aid--virtual .ais-aton-symbol::after,
.ais-aid--synthetic .ais-bs-symbol::after,.ais-aid--synthetic .ais-aton-symbol::after{
content:"";
position:absolute;
inset:-6px;
border-radius:50%;
pointer-events:none;
}
.ais-aid--virtual .ais-bs-symbol::after,.ais-aid--virtual .ais-aton-symbol::after{
border:2px dashed #4fc3f7;
}
.ais-aid--synthetic .ais-bs-symbol::after,.ais-aid--synthetic .ais-aton-symbol::after{
border:2px dotted #d2ff1a;
}
.ais-aid--offposition .ais-aton-symbol{
box-shadow:0 0 0 3px rgba(248,81,73,.45),0 0 12px rgba(248,81,73,.9);
}
.transponder-hint{font-size:11px;color:#889;margin:0 0 12px;line-height:1.4}
.transponder-preview{margin:0;padding:10px;background:#0d1117;border:1px solid #30363d;border-radius:6px;font-size:10px;line-height:1.35;white-space:pre-wrap;word-break:break-all;max-height:55vh;overflow:auto;color:#c9d1d9}
.tp-dim{width:64px}
.transponder-raw-hex{width:100%;max-width:720px;box-sizing:border-box;font-family:ui-monospace,monospace;font-size:11px;padding:8px;background:#0d1117;border:1px solid #30363d;border-radius:6px;color:#c9d1d9;resize:vertical}
.ship-type-select{max-width:100%;width:min(560px,100%)}
.ship-type-legend{font-size:10px;color:#889;margin:-4px 0 10px;line-height:1.4}
.ship-type-legend abbr{text-decoration:underline dotted;cursor:help}
.ship-editor-block{margin:14px 0;padding:12px;background:#0d1117;border:1px solid #30363d;border-radius:8px}
.ship-editor-wrap{display:flex;flex-wrap:wrap;gap:16px;align-items:flex-start}
.ship-editor-svg{flex-shrink:0;width:min(340px,100%);max-width:100%;height:auto;touch-action:none;cursor:default}
.ship-dim-main{stroke:#8b949e;stroke-width:1.25;fill:none}
.ship-dim-ext{stroke:#484f58;stroke-width:1;stroke-dasharray:3 4;stroke-linecap:square}
.ship-dim-txt{fill:#c9d1d9;font-size:10px;font-family:ui-sans-serif,system-ui,sans-serif}
.ship-dim-group{pointer-events:none}
.ship-axis-lbl--dyn{fill:#667;font-size:10px}
.ship-axis-lbl{fill:#667;font-size:11px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif}
.ship-hull{fill:rgba(79,195,247,.12);stroke:#4fc3f7;stroke-width:1.5}
.ship-grid{stroke:#30363d;stroke-width:0.6;stroke-dasharray:4 3}
.ship-gps-group{cursor:grab}
.ship-gps-group:active,body.ship-editor-dragging .ship-gps-group{cursor:grabbing}
.ship-gps-hit{fill:transparent;stroke:none;pointer-events:all}
.ship-gps-dot{fill:#58a6ff;stroke:#1f6feb;stroke-width:1.5;pointer-events:none}
.ship-gps-lbl{fill:#e0e0e0;font-size:7px;font-weight:700;pointer-events:none;user-select:none;font-family:system-ui,sans-serif}
.ship-gps-group--template .ship-gps-dot{fill:#8b949e;stroke:#484f58;stroke-dasharray:2 2}
.ship-editor-legend{margin:0;padding-left:18px;max-width:320px;font-size:11px;color:#889;line-height:1.45}
.ship-editor-legend li{margin:4px 0}
.ship-editor-legend strong{color:#c9d1d9}
.ship-edge-handles{pointer-events:all}
.ship-handle circle{fill:rgba(210,255,26,.4);stroke:#c5e636;stroke-width:1.5}
.ship-handle--stern circle{cursor:ns-resize}
.ship-handle--starboard circle{cursor:ew-resize}
.ship-handle:active circle{fill:rgba(210,255,26,.65)}
#ownship-panel{position:absolute;z-index:1000;bottom:8px;right:8px;width:280px;background:rgba(22,33,62,.95);padding:10px 14px;border-radius:6px;font-size:12px;box-shadow:0 2px 8px rgba(0,0,0,.4)}
#ownship-panel h4{margin:0 0 5px;font-size:12px;border-bottom:1px solid #0f3460;padding-bottom:4px;color:#8899aa}
#ownship-panel .row{margin:2px 0}
#ownship-panel .label{color:#667}
.ownship-btn{display:inline-block;padding:4px 10px;margin:3px 3px 0 0;border:1px solid #0f3460;border-radius:4px;background:#16213e;cursor:pointer;font-size:11px;color:#8899aa;transition:all .15s}
.ownship-btn:hover{border-color:#4fc3f7;color:#ccc}
.ownship-btn.active{background:#d2ff1a;border-color:#a8cc14;color:#1a1a2e;font-weight:700}
.ownship-icon{will-change:transform}
/* Mobile panel bar (hidden on desktop) */
#mob-panel-bar{display:none}
/* ===== Mobile map panels =====
Also enable for phone landscape (small height). */
@media(max-width:600px), (max-height:520px) and (pointer:coarse){
:root{
/* mob-panel-bar is disabled, but the vars are kept at 0 so positioning
calculations (sidebar/ownship-panel/HUD) still evaluate cleanly. */
--mob-panel-bar-height:0px;
--mob-panel-bar-width:0px;
/* JS may set --vv-bottom for WebView/browser UI occlusions */
--vv-bottom: 0px;
--vv-top: 0px;
--vv-right: 0px;
--vv-left: 0px;
--safe-bottom: env(safe-area-inset-bottom, 0px);
--safe-right: env(safe-area-inset-right, 0px);
}
@supports (padding: max(0px, 0px)){
:root{
--safe-bottom: max(env(safe-area-inset-bottom, 0px), var(--vv-bottom, 0px));
--safe-right: max(env(safe-area-inset-right, 0px), var(--vv-right, 0px));
}
}
#mob-panel-bar{
display:flex;
position:fixed;
left:0;right:0;
bottom:0;
z-index:1100;
background:#16213e;
border-top:1px solid #0f3460;
padding-bottom:var(--safe-bottom);
touch-action:none;
overscroll-behavior:contain;
}
/* Visible drag-handle pill so users don't intuitively pull-to-refresh the page */
#mob-panel-bar::before{
content:"";
position:absolute;
left:50%;top:4px;transform:translateX(-50%);
width:44px;height:4px;border-radius:3px;
background:rgba(255,255,255,.28);
pointer-events:none;
}
#mob-panel-bar:active::before{background:rgba(210,255,26,.55)}
.mob-panel-tab{flex:1;padding:15px 0 9px;background:none;border:none;color:#8899aa;font-size:13px;font-weight:600;cursor:pointer;transition:color .15s,background .15s;border-top:2px solid transparent;touch-action:none}
.mob-panel-tab:active{background:rgba(255,255,255,.04)}
.mob-panel-tab.active{color:#d2ff1a;border-top-color:#d2ff1a}
#sidebar{position:absolute;top:auto;bottom:calc(var(--mob-panel-bar-height) + var(--safe-bottom));left:0;right:0;width:auto;max-height:60vh;border-radius:10px 10px 0 0;z-index:1100;display:none;overscroll-behavior:contain;padding-top:14px}
#sidebar.mob-open{display:flex}
#ownship-panel{position:absolute;top:auto;bottom:calc(var(--mob-panel-bar-height) + var(--safe-bottom));left:0;right:0;width:auto;max-height:60vh;overflow-y:auto;border-radius:10px 10px 0 0;z-index:1100;min-width:0;display:none;overscroll-behavior:contain;padding-top:14px}
#ownship-panel.mob-open{display:block}
/* Drag-pill grip on top of the mobile bottom-sheet panels (sidebar & ownship-panel)
— gives the user an intuitive affordance to swipe them down to close, and avoids
accidentally triggering browser pull-to-refresh while interacting with the sheet. */
#sidebar.mob-open::before,
#ownship-panel.mob-open::before{
content:"";
position:absolute;
left:50%;top:5px;transform:translateX(-50%);
width:44px;height:4px;border-radius:3px;
background:rgba(255,255,255,.28);
pointer-events:none;
}
#sidebar.mob-swiping,
#ownship-panel.mob-swiping{
transition:none;
will-change:transform;
}
#sidebar.mob-swiping::before,
#ownship-panel.mob-swiping::before{background:rgba(210,255,26,.55)}
#cursor-coords{bottom:calc(var(--mob-panel-bar-height) + var(--safe-bottom) + 8px);transition:bottom .2s}
}
/* Landscape phone: move mobile buttons + panels to the right */
@media (max-height:520px) and (pointer:coarse){
#mob-panel-bar{
left:auto;
right:0;
top:44px; /* below navbar */
bottom:0;
width:calc(var(--mob-panel-bar-width) + var(--safe-right));
flex-direction:column;
border-top:none;
border-left:1px solid #0f3460;
padding-bottom:0;
padding-right:var(--safe-right);
}
/* In landscape the bar is on the right — rotate the drag pill 90° */
#mob-panel-bar::before{
left:4px;top:50%;transform:translateY(-50%);
width:4px;height:44px;border-radius:3px;
}
.mob-panel-tab{
padding:12px 10px 12px 14px;
border-top:none;
border-left:2px solid transparent;
}
.mob-panel-tab.active{
border-top-color:transparent;
border-left-color:#d2ff1a;
}
#sidebar,
#ownship-panel{
top:0;
bottom:var(--safe-bottom);
left:auto;
right:calc(var(--mob-panel-bar-width) + var(--safe-right) + 8px);
width:min(420px, 40vw);
max-height:calc(100vh - 44px - var(--safe-bottom));
border-radius:10px;
}
@supports (height: 100dvh){
#sidebar,
#ownship-panel{
max-height:calc(100dvh - 44px - var(--safe-bottom));
}
}
#cursor-coords{
bottom:8px;
}
}
/* ===== Stats page ===== */
#page-stats{padding:20px;overflow-y:auto}
.stats-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:14px;margin-bottom:20px}
.stat-card{background:#16213e;border:1px solid #0f3460;border-radius:8px;padding:16px}
.stat-card .val{font-size:28px;font-weight:700;color:#d2ff1a}
.stat-card .lbl{font-size:12px;color:#8899aa;margin-top:2px}
.stat-card-test{border-color:#d29922}.stat-card-test .val{color:#f0883e}
.test-ch-detail{font-size:11px;color:#8899aa;margin-top:4px;display:flex;gap:10px}.test-ch-detail b{color:#e0e0e0}
.stat-section{margin-top:20px}
.stat-section h3{font-size:14px;color:#8899aa;margin-bottom:10px;border-bottom:1px solid #0f3460;padding-bottom:6px}
.stat-table{width:100%;border-collapse:collapse;font-size:13px}
.stat-table th,.stat-table td{padding:6px 10px;text-align:left;border-bottom:1px solid #0f3460}
.stat-table th{color:#667;font-weight:400}
.stat-table td{color:#e0e0e0}
.stat-details{background:#16213e;border:1px solid #0f3460;border-radius:8px;padding:10px 12px}
.stat-details>summary{cursor:pointer;user-select:none;color:#4fc3f7;font-size:13px;font-weight:700;outline:none}
.stat-details>summary::-webkit-details-marker{display:none}
.stat-details>summary::before{content:"▸";display:inline-block;margin-right:8px;color:#667;transition:transform .15s}
.stat-details[open]>summary::before{transform:rotate(90deg)}
.stat-adv-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:12px;margin-top:10px}
.stat-adv-card{background:#0d1117;border:1px solid #0f3460;border-radius:8px;padding:10px 12px}
.stat-adv-title{font-size:12px;color:#8899aa;margin-bottom:6px;font-weight:700}
.stat-table-compact th,.stat-table-compact td{padding:4px 8px;font-size:12px}
.stat-k{color:#667;font-family:monospace}
.stat-v{color:#e0e0e0;font-family:monospace;text-align:right}
.stat-v.err{color:#f85149;font-weight:700}
/* ===== Settings page ===== */
#page-settings{padding:20px;overflow-y:auto}
#page-transponder{padding:20px;overflow-y:auto;box-sizing:border-box}
.settings-card{background:#16213e;border:1px solid #0f3460;border-radius:8px;padding:16px;margin-bottom:14px}
.settings-card h3{font-size:14px;color:#8899aa;margin-bottom:10px}
.settings-row{display:flex;justify-content:space-between;align-items:center;padding:8px 0;border-bottom:1px solid rgba(15,52,96,.5)}
.settings-row:last-child{border-bottom:none}
.settings-label{color:#ccc;font-size:13px}
.settings-value{color:#4fc3f7;font-size:13px;font-family:monospace}
/* Network settings */
.net-mode-btns{display:flex;gap:8px;margin:10px 0}
.net-mode-btn{flex:1;padding:10px 0;text-align:center;border:2px solid #0f3460;border-radius:6px;background:#1a1a2e;cursor:pointer;font-size:13px;font-weight:600;color:#8899aa;transition:all .2s}
.net-mode-btn:hover{border-color:#4fc3f7;color:#ccc}
.net-mode-btn.active{border-color:#d2ff1a;background:rgba(210,255,26,.1);color:#d2ff1a}
.net-input{background:#1a1a2e;border:1px solid #0f3460;color:#e0e0e0;padding:6px 10px;border-radius:4px;font-size:13px;font-family:monospace;width:200px}
.net-input:focus{outline:none;border-color:#4fc3f7}
.net-btn{padding:8px 20px;border:none;border-radius:4px;font-size:13px;font-weight:600;cursor:pointer;transition:all .2s}
.net-btn-primary{background:#d2ff1a;color:#1a1a2e}
.net-btn-primary:hover{background:#b8e016}
.net-btn-danger{background:#f85149;color:#fff}
.net-btn-danger:hover{background:#d63a32}
.net-btn:disabled{opacity:.4;cursor:not-allowed}
.net-status{display:inline-block;padding:3px 10px;border-radius:12px;font-size:11px;font-weight:700}
.net-status.ap{background:rgba(79,195,247,.15);color:#4fc3f7}
.net-status.wifi{background:rgba(210,255,26,.15);color:#d2ff1a}
.net-status.unknown{background:rgba(136,153,170,.15);color:#8899aa}
.net-scan-list{max-height:180px;overflow-y:auto;margin:8px 0}
.net-scan-item{display:flex;justify-content:space-between;align-items:center;padding:6px 10px;border-radius:4px;cursor:pointer;font-size:12px;color:#ccc;transition:background .15s}
.net-scan-item:hover{background:rgba(255,255,255,.05)}
.net-scan-item.selected{background:rgba(210,255,26,.1);color:#d2ff1a}
.net-signal{color:#667;font-size:11px}
.net-msg{padding:8px 12px;border-radius:4px;font-size:12px;margin-top:8px;display:none}
.net-msg.ok{display:block;background:rgba(35,134,54,.15);color:#3fb950}
.net-msg.err{display:block;background:rgba(248,81,73,.15);color:#f85149}
.net-msg.info{display:block;background:rgba(79,195,247,.15);color:#4fc3f7}
.net-toggle-adv{color:#4fc3f7;font-size:12px;cursor:pointer;margin-top:6px;display:inline-block}
.net-toggle-adv:hover{text-decoration:underline}
.net-advanced{display:none;margin-top:10px}
.net-advanced.open{display:block}
/* ===== Logs page ===== */
#page-logs.active{display:flex;flex-direction:column}
.logs-toolbar{display:flex;gap:8px;padding:8px 12px;background:#16213e;border-bottom:1px solid #0f3460;align-items:center;flex-shrink:0}
.logs-toolbar label{font-size:12px;color:#8899aa}
.logs-toolbar select,.logs-toolbar input{background:#1a1a2e;border:1px solid #0f3460;color:#e0e0e0;padding:4px 8px;border-radius:4px;font-size:12px}
.log-btn{padding:4px 12px;border:1px solid #0f3460;border-radius:4px;background:#16213e;color:#8899aa;cursor:pointer;font-size:12px;transition:all .15s}
.log-btn:hover{border-color:#4fc3f7;color:#ccc}
.log-btn.active{background:#d2ff1a;color:#1a1a2e;border-color:#a8cc14;font-weight:600}
#log-output{flex:1;overflow-y:auto;overflow-x:auto;padding:8px 12px;font-family:"Cascadia Mono","Fira Code",monospace;font-size:12px;line-height:1.6;white-space:pre;background:#0d1117}
.log-line{color:#8b949e}
.log-line .ts{color:#484f58}
.log-line.ais{color:#4fc3f7}
.log-line.gps{color:#d2ff1a}
.log-line.unknown{color:#f85149}
/* ===== Config page ===== */
#page-config.active{display:flex;flex-direction:column;background:#0d1117}
.config-toolbar{display:flex;align-items:center;gap:10px;padding:8px 12px;background:#161b22;border-bottom:1px solid #0f3460;flex-shrink:0;font-size:13px;color:#8899aa}
.config-svc-label{font-size:12px;color:#667}
.config-svc-badge{display:inline-block;padding:2px 10px;border-radius:12px;font-size:11px;font-weight:700}
.config-svc-badge.active{background:rgba(63,185,80,.15);color:#3fb950}
.config-svc-badge.inactive{background:rgba(248,81,73,.15);color:#f85149}
.config-svc-badge.unknown{background:rgba(136,153,170,.15);color:#8899aa}
.config-body{flex:1;display:flex;flex-direction:column;padding:12px;min-height:0}
.config-tabs{display:flex;align-items:center;gap:8px;margin-bottom:8px;flex-wrap:wrap}
.config-tab{padding:6px 10px;border:1px solid #0f3460;border-radius:6px;background:#1a1a2e;color:#8899aa;font-size:12px;font-weight:700;cursor:pointer}
.config-tab.active{border-color:#d2ff1a;color:#d2ff1a;background:rgba(210,255,26,.08)}
.config-file-hint{font-size:11px;color:#667;font-family:monospace}
.config-editor{flex:1;min-height:200px;background:#0d1117;color:#e0e0e0;border:1px solid #30363d;border-radius:6px;padding:12px;font-family:"Cascadia Mono","Fira Code","JetBrains Mono",monospace;font-size:13px;line-height:1.6;resize:none;tab-size:4;white-space:pre;overflow:auto}
.config-editor:focus{outline:none;border-color:#58a6ff}
.config-actions{display:flex;align-items:center;gap:8px;margin-top:10px;flex-wrap:wrap}
.config-msg{font-size:12px;white-space:nowrap}
.config-msg.ok{color:#3fb950}
.config-msg.err{color:#f85149}
.config-msg.info{color:#4fc3f7}
.config-hint{margin-top:8px;font-size:11px;color:#484f58;font-family:monospace}
/* ===== Console (xterm.js) ===== */
#page-console.active{display:flex;flex-direction:column;background:#0d1117}
.console-toolbar{display:flex;align-items:center;gap:10px;padding:8px 12px;background:#161b22;border-bottom:1px solid #0f3460;flex-shrink:0;font-size:12px;color:#8899aa}
.console-toolbar code{color:#4fc3f7;font-size:11px}
#terminal-wrap{flex:1;min-height:0;padding:0 8px 8px;box-sizing:border-box;display:none}
#terminal-wrap .xterm{height:100%;padding:6px 0}
#terminal-unavailable{display:none;padding:24px;color:#8899aa;font-size:13px;line-height:1.5;max-width:520px}
/* ===== Slots TDMA section ===== */
.slots-section{margin-top:20px}
.slots-header{display:flex;align-items:center;gap:8px;padding:12px 16px;background:#16213e;border:1px solid #0f3460;border-radius:8px;cursor:pointer;user-select:none;font-size:14px;color:#8899aa;transition:background .15s}
.slots-header:hover{background:#1a2744}
.slots-arrow{font-size:10px;color:#667;display:inline-block;transition:transform .2s}
.slots-arrow.open{transform:rotate(90deg)}
.slots-content{display:none;margin-top:8px}
.slots-content.open{display:block}
.slots-channels{display:grid;grid-template-columns:1fr 1fr;gap:14px}
@media(max-width:960px){.slots-channels{grid-template-columns:1fr}}
.slots-channel{background:#16213e;border:1px solid #0f3460;border-radius:8px;padding:14px}
.slots-ch-title{font-size:13px;font-weight:700;color:#4fc3f7;margin-bottom:6px}
.slots-info{font-size:12px;color:#8899aa;margin-bottom:8px;font-family:monospace}
.slots-no-data{font-size:12px;color:#484f58;font-style:italic}
.slots-channel canvas{display:block;border-radius:4px;background:#0d1117;max-width:100%}
.slots-canvas-wrap{display:block;overflow:auto;border-radius:4px;background:#0d1117}
.slots-canvas-wrap canvas{max-width:none}
.slots-legend{margin-top:12px;display:flex;gap:18px;font-size:12px;color:#8899aa}
.slots-legend-item{display:flex;align-items:center;gap:5px}
.slots-legend-box{display:inline-block;width:12px;height:12px;border-radius:2px}
.slots-legend-box.free{background:#238636}
.slots-legend-box.occ{background:#f85149}
.slots-test-send{margin-top:14px;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.slots-test-label{font-size:12px;color:#8899aa;white-space:nowrap}
.slots-test-input{background:#0d1117;border:1px solid #30363d;border-radius:4px;color:#e0e0e0;padding:5px 8px;font-size:12px;font-family:inherit}
.slots-test-input:focus{outline:none;border-color:#58a6ff}
.slots-test-btn{background:#238636;color:#fff;border:none;border-radius:4px;padding:5px 14px;font-size:12px;cursor:pointer;white-space:nowrap;transition:background .15s}
.slots-test-btn:hover{background:#2ea043}
.slots-test-btn:active{background:#196c2e}
.slots-test-btn:disabled{background:#21262d;color:#484f58;cursor:not-allowed}
.slots-test-status{font-size:11px;white-space:nowrap}
.slots-test-status.ok{color:#3fb950}
.slots-test-status.err{color:#f85149}
.slots-test-status.wait{color:#d29922}
.slots-selected-info{margin-top:8px;font-size:12px;color:#8899aa;font-family:monospace;min-height:16px}
.slots-bar{height:6px;border-radius:3px;background:#0d1117;margin-bottom:10px;overflow:hidden}
.slots-bar-fill{height:100%;border-radius:3px;transition:width .3s}
.slots-tooltip{position:fixed;z-index:9999;pointer-events:none;background:rgba(22,33,62,.95);border:1px solid #0f3460;border-radius:4px;padding:4px 8px;font-size:11px;color:#e0e0e0;font-family:monospace;white-space:nowrap;display:none;box-shadow:0 2px 8px rgba(0,0,0,.5)}
.slots-rssi-label{font-size:11px;color:#667;margin-bottom:4px}
.slots-channel canvas.rssi-chart{width:100%;height:90px;margin-bottom:8px}
.slots-rssi-legend{display:flex;gap:14px;font-size:11px;color:#8899aa;margin-top:4px;margin-bottom:6px}
.slots-rssi-legend span::before{content:'';display:inline-block;width:16px;height:2px;margin-right:4px;vertical-align:middle}
/* ===================================================================
Vessel InfoWindow (MarineTraffic-style)
- Desktop: floating, draggable card anchored inside map area
- Mobile : full-width bottom-sheet above the mob-panel-bar
=================================================================== */
.vinf{
position:absolute;
z-index:1200;
top:64px; left:8px;
width:360px;
max-width:calc(100vw - 16px);
background:#16213e;
border:1px solid #0f3460;
border-radius:10px;
box-shadow:0 10px 28px rgba(0,0,0,.55);
color:#e0e0e0;
font-size:12px;
display:flex;
flex-direction:column;
overflow:hidden;
user-select:none;
touch-action:none;
}
.vinf[hidden]{display:none!important}
/* Drag/grip strip at the top (mobile pill + desktop subtle handle) */
.vinf-grip{
position:relative;
height:14px;
flex-shrink:0;
background:#0f3460;
cursor:grab;
display:none;
}
.vinf-grip::before{
content:"";
position:absolute;left:50%;top:50%;
transform:translate(-50%,-50%);
width:40px;height:4px;border-radius:3px;
background:rgba(255,255,255,.22);
}
.vinf-grip:hover::before{background:rgba(255,255,255,.36)}
.vinf-grip:active{cursor:grabbing}
.vinf-header{
display:flex;
align-items:center;
gap:8px;
padding:8px 10px;
background:#0f3460;
border-bottom:1px solid #0f3460;
cursor:grab;
}
.vinf-header:active{cursor:grabbing}
/* Collapsed-mode mini stats (hidden in the expanded view) */
.vinf-mini{
display:none;
gap:10px;
font-size:11px;color:#8b949e;
font-family:ui-monospace,monospace;
}
.vinf-mini b{color:#c9d1d9;font-weight:700}
/* Collapsed state: hide body/footer via clip; keep header + mini stats visible */
.vinf-body,.vinf-footer{
transition:max-height .22s ease, opacity .18s ease, padding .22s ease, border-color .18s ease;
will-change:max-height,opacity;
overflow:hidden;
}
.vinf--collapsed .vinf-sub{display:none}
.vinf--collapsed .vinf-mini{display:flex}
.vinf--collapsed .vinf-header{border-bottom-color:transparent}
.vinf--collapsed .vinf-body{max-height:0!important;opacity:0;padding-top:0;padding-bottom:0;pointer-events:none;border-color:transparent}
.vinf--collapsed .vinf-footer{max-height:0!important;opacity:0;padding-top:0;padding-bottom:0;border-top-color:transparent;pointer-events:none}
/* Collapse button icon swap (chevron down when expanded → becomes up when collapsed) */
.vinf-ic-expand{display:block}
.vinf-ic-collapse{display:none}
.vinf--collapsed .vinf-ic-expand{display:none}
.vinf--collapsed .vinf-ic-collapse{display:block}
.vinf-icon{
width:34px; height:34px;
flex-shrink:0;
display:flex;
align-items:center;
justify-content:center;
background:#0d1117;
border-radius:6px;
border:1px solid rgba(255,255,255,.08);
}
.vinf-icon svg{display:block;width:26px;height:26px}
.vinf-title{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;line-height:1.2}
.vinf-name{
font-size:14px;font-weight:700;color:#e0e0e0;
white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.vinf-sub{
font-size:10px;color:#8b949e;
white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
display:flex;align-items:center;gap:6px;
}
.vinf-flag{
display:inline-block;
font-size:14px;line-height:1;
}
.vinf-btn{
background:transparent;border:none;color:#8b949e;
cursor:pointer;padding:4px 6px;border-radius:4px;
display:flex;align-items:center;justify-content:center;
}
.vinf-btn:hover{color:#e0e0e0;background:rgba(255,255,255,.06)}
.vinf-btn svg{display:block;width:18px;height:18px}
.vinf-drag-handle{
color:#667;padding:4px 2px;cursor:grab;
display:inline-flex;align-items:center;
}
/* Body */
.vinf-body{
padding:10px;
display:flex;
flex-direction:column;
gap:10px;
max-height:60vh;
overflow-y:auto;
user-select:text;
touch-action:auto;
}
.vinf-voyage{
display:grid;
grid-template-columns:1fr auto 1fr;
gap:6px 10px;
align-items:center;
background:#0d1117;
border:1px solid rgba(255,255,255,.05);
border-radius:6px;
padding:8px 10px;
}
.vinf-port{
font-size:13px;font-weight:700;color:#c9d1d9;
min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.vinf-port--right{text-align:right}
.vinf-port-country{color:#667;font-weight:500;margin-right:4px;font-size:11px}
.vinf-voyage-arrow{
color:#4fc3f7;font-size:14px;display:flex;align-items:center;justify-content:center;
padding:0 2px;
}
.vinf-eta{
grid-column:1/-1;
display:flex;justify-content:space-between;gap:10px;
font-size:11px;color:#8b949e;
border-top:1px dashed rgba(255,255,255,.08);
padding-top:6px;margin-top:2px;
}
.vinf-eta b{color:#c9d1d9}
.vinf-progress{
height:4px;background:#0d1117;border-radius:2px;overflow:hidden;
margin:6px 0 2px;
grid-column:1/-1;
}
.vinf-progress-fill{height:100%;background:linear-gradient(90deg,#4fc3f7,#d2ff1a);border-radius:2px}
/* 2x2 grid: nav / speed-course / draught / heading */
.vinf-grid{
display:grid;
grid-template-columns:1fr 1fr;
gap:1px;
background:rgba(255,255,255,.05);
border-radius:6px;
overflow:hidden;
}
.vinf-cell{
background:#0d1117;
padding:7px 10px;
display:flex;flex-direction:column;gap:3px;
min-width:0;
}
.vinf-cell-lbl{font-size:10px;color:#667;text-transform:uppercase;letter-spacing:.4px}
.vinf-cell-val{font-size:13px;font-weight:700;color:#c9d1d9;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.vinf-cell-val--wrap{white-space:normal;line-height:1.25;overflow:visible;text-overflow:clip}
.vinf-cell-val.dim{color:#667;font-weight:500;font-style:italic}
/* Identifiers (MMSI/IMO/Callsign) */
.vinf-ids{
display:flex;flex-wrap:wrap;gap:6px 14px;
font-size:11px;color:#8b949e;
padding:4px 2px;
}
.vinf-ids b{color:#c9d1d9;font-family:ui-monospace,monospace}
.vinf-ids a{color:#4fc3f7;text-decoration:none}
.vinf-ids a:hover{text-decoration:underline}
/* Signal block */
.vinf-signal{
display:flex;align-items:center;gap:10px;
padding:6px 10px;
background:#0d1117;
border-radius:6px;
font-size:11px;color:#8b949e;
}
.vinf-signal-bars{
display:inline-flex;align-items:flex-end;gap:2px;
height:14px;
}
.vinf-signal-bar{
width:3px;background:#30363d;border-radius:1px;
}
.vinf-signal-bar.on{background:#3fb950}
.vinf-signal-bar.warn{background:#d29922}
.vinf-signal-bar.bad{background:#f85149}
.vinf-signal b{color:#c9d1d9}
.vinf-signal .sig-db{font-family:ui-monospace,monospace;color:#c9d1d9}
/* Actions */
.vinf-actions{
display:flex;flex-wrap:wrap;gap:6px;
padding:2px 0;
}
.vinf-act{
display:inline-flex;align-items:center;gap:5px;
padding:6px 10px;
background:#16213e;
border:1px solid #0f3460;
border-radius:6px;
color:#c9d1d9;
font-size:11px;font-weight:600;
cursor:pointer;
transition:border-color .15s,color .15s,background .15s;
}
.vinf-act:hover{border-color:#4fc3f7;color:#e0e0e0}
.vinf-act.primary{background:rgba(79,195,247,.12);border-color:#4fc3f7;color:#4fc3f7}
.vinf-act.primary:hover{background:rgba(79,195,247,.22)}
.vinf-act svg{width:14px;height:14px}
/* Footer */
.vinf-footer{
padding:6px 10px;
background:#0d1117;
font-size:10px;color:#667;
display:flex;justify-content:space-between;gap:8px;flex-wrap:wrap;
border-top:1px solid rgba(255,255,255,.04);
}
.vinf-footer b{color:#c9d1d9}
/* Mobile: bottom-sheet — fixed above the mob-panel-bar */
@media(max-width:600px), (max-height:520px) and (pointer:coarse){
.vinf{
position:absolute;
top:auto;
left:0;right:0;
bottom:calc(var(--mob-panel-bar-height, 42px) + var(--safe-bottom, 0px));
width:auto;
max-width:none;
border-radius:10px 10px 0 0;
max-height:70vh;
transition:max-height .22s ease;
}
.vinf-grip{display:block}
.vinf-header{cursor:default}
.vinf-body{max-height:50vh}
/* Collapsed on mobile: shrink the whole sheet to just the grip + header */
.vinf.vinf--collapsed{max-height:72px}
}
@media (max-height:520px) and (pointer:coarse){
/* landscape: panel on the right side like sidebar */
.vinf{
top:52px;
bottom:var(--safe-bottom,0px);
left:auto;right:calc(var(--mob-panel-bar-width,72px) + var(--safe-right,0px) + 8px);
width:min(420px,44vw);
max-width:none;
border-radius:10px;
}
/* Landscape collapsed: shrink width instead of height for a cleaner look */
.vinf.vinf--collapsed{
width:min(260px,38vw);
max-height:none;
}
}
/* ===================================================================
Map controls (centre / north-up / ruler / one-hand / zoom)
=================================================================== */
.map-controls{
position:absolute;
z-index:1000;
top:8px; left:50%;
transform:translateX(-50%);
display:flex;gap:4px;
background:rgba(22,33,62,.92);
border:1px solid #0f3460;
border-radius:8px;
padding:3px;
box-shadow:0 2px 8px rgba(0,0,0,.4);
}
.map-ctrl{
width:32px;height:32px;
border:none;border-radius:6px;
background:transparent;color:#c9d1d9;
cursor:pointer;
display:flex;align-items:center;justify-content:center;
font-size:16px;font-weight:700;line-height:1;
transition:background .15s,color .15s;
}
.map-ctrl:hover{background:rgba(255,255,255,.08);color:#d2ff1a}
.map-ctrl:active{background:rgba(255,255,255,.14)}
.map-ctrl.active{background:rgba(210,255,26,.14);color:#d2ff1a}
.map-ctrl svg{display:block}
/* On mobile, put the control strip to the top-right corner to leave more
horizontal space for the conn-banner, and make it vertical. */
@media(max-width:600px){
.map-controls{
top:52px;
left:auto;right:8px;
transform:none;
flex-direction:column;
}
}
/* Leaflet scale bar (enabled via L.control.scale) */
.leaflet-control-scale{
margin:0 !important;
}
.leaflet-control-scale-line{
background:rgba(22,33,62,.85)!important;
color:#d2ff1a!important;
border:1px solid rgba(210,255,26,.35)!important;
border-top:none!important;
padding:1px 6px!important;
font-size:10px!important;
line-height:1.3!important;
font-weight:600;
}
/* ===================================================================
One-hand zoom pad (big thumb-reach buttons, toggled on)
=================================================================== */
.onehand-pad{
position:absolute;
right:8px; bottom:60px;
z-index:1150;
display:flex;flex-direction:column;gap:8px;
}
.onehand-pad[hidden]{display:none!important}
.onehand-btn{
width:54px;height:54px;
border-radius:50%;
border:1px solid #0f3460;
background:rgba(22,33,62,.92);
color:#d2ff1a;
font-size:26px;font-weight:800;
cursor:pointer;
display:flex;align-items:center;justify-content:center;
box-shadow:0 4px 12px rgba(0,0,0,.45);
touch-action:manipulation;
}
.onehand-btn:active{background:rgba(22,33,62,1);transform:scale(.96)}
.onehand-btn#oh-center{color:#4fc3f7}
/* On landscape-compact, push it next to the mob-panel-bar on the right side */
@media (max-height:520px) and (pointer:coarse){
.onehand-pad{right:calc(var(--mob-panel-bar-width,72px) + var(--safe-right,0px) + 8px); bottom:16px}
}
/* ===================================================================
Ruler tool
=================================================================== */
.ruler-hud{
position:absolute;z-index:1250;
top:58px; left:50%;transform:translateX(-50%);
background:rgba(22,33,62,.94);
border:1px solid #4fc3f7;
border-radius:999px;
padding:6px 14px;
color:#c9d1d9;
font-size:12px;font-weight:600;
box-shadow:0 4px 14px rgba(0,0,0,.5);
pointer-events:auto;
display:flex;align-items:center;gap:10px;
}
.ruler-hud[hidden]{display:none!important}
.ruler-hud .dist{color:#d2ff1a;font-weight:700;font-family:ui-monospace,monospace}
.ruler-hud .hint{color:#8b949e;font-weight:500}
.ruler-hud button{
background:transparent;border:1px solid transparent;color:#8b949e;
font-size:12px;cursor:pointer;border-radius:4px;padding:2px 6px;
}
.ruler-hud button:hover{color:#f85149;border-color:#f85149}
body.ruler-active .leaflet-container{cursor:crosshair !important}
/* ===================================================================
Targets tab page (full list; desktop has same data in #sidebar)
=================================================================== */
#page-targets.active{display:flex;flex-direction:column;background:#1a1a2e}
.targets-page-inner{
flex:1;min-height:0;
display:flex;flex-direction:column;
padding:14px 16px 0;
max-width:860px;
margin:0 auto;
width:100%;
box-sizing:border-box;
}
.targets-page-inner h3{
font-size:17px;margin:0 0 10px;color:#c9d1d9;
border-bottom:1px solid #0f3460;padding-bottom:8px;
}
.targets-list{
flex:1;min-height:0;overflow-y:auto;padding-bottom:16px;
display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));
gap:8px;align-content:start;
}
.targets-list .vessel-item{margin:0;background:#16213e}
.targets-list .vessel-item:hover{background:rgba(255,255,255,.05)}
/* Only show the page-targets tab on mobile; on desktop we keep the sidebar */
.nav-tab--targets{display:none}
@media(max-width:600px){
.nav-tab--targets{display:inline-block}
}
/* ===================================================================
Mobile compact "nearby" sidebar: MMSI / COG / SOG / HDG / BRG / DIST
=================================================================== */
.sidebar--compact .vessel-item{
display:grid;
grid-template-columns:auto 1fr auto;
grid-template-areas:
"mmsi name dist"
"stats stats stats";
gap:2px 8px;
padding:6px 8px;
align-items:center;
}
.sidebar--compact .vessel-item .vessel-mmsi-row{grid-area:mmsi;display:flex;gap:6px;align-items:center}
.sidebar--compact .vessel-item .mmsi{font-size:12px}
.sidebar--compact .vessel-item .name{grid-area:name;font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sidebar--compact .vessel-item .dist{grid-area:dist;display:flex;flex-direction:column;align-items:flex-end;font-size:11px;line-height:1.15;gap:1px}
.sidebar--compact .vessel-item .dist .dist-val{color:#d2ff1a;font-weight:700}
.sidebar--compact .vessel-item .dist .brg-val{color:#8b949e;font-size:10px}
.sidebar--compact .vessel-item .dist .brg-val::before{content:"\2192 ";opacity:.6}
.sidebar--compact .vessel-item .compact-stats{
grid-area:stats;
display:grid;
grid-template-columns:repeat(auto-fit, minmax(58px, 1fr));
gap:3px 6px;
font-size:10px;color:#8b949e;font-family:ui-monospace,monospace;
}
.sidebar--compact .vessel-item .compact-stats > span{
display:flex;align-items:baseline;gap:4px;
white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
cursor:help;
}
.sidebar--compact .vessel-item .compact-stats .k{color:#667;font-weight:500}
.sidebar--compact .vessel-item .compact-stats b{color:#c9d1d9;font-weight:600}
.sidebar--compact .vessel-item .callsign-row,
.sidebar--compact .vessel-item .coords,
.sidebar--compact .vessel-item > div:not(.vessel-mmsi-row):not(.name):not(.dist):not(.compact-stats){
display:none;
}
/* ===================================================================
Targets tab full list: readable stats grid with labels
=================================================================== */
.targets-list .vessel-item{
display:grid;
grid-template-columns:auto 1fr auto;
grid-template-areas:
"mmsi name dist"
"stats stats stats";
gap:4px 10px;
padding:10px 12px;
}
.targets-list .vessel-item .vessel-mmsi-row{grid-area:mmsi;display:flex;gap:6px;align-items:center}
.targets-list .vessel-item .name{grid-area:name;font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.targets-list .vessel-item .dist{grid-area:dist;display:flex;flex-direction:column;align-items:flex-end;gap:2px;font-size:12px;line-height:1.15}
.targets-list .vessel-item .dist .dist-val{color:#d2ff1a;font-weight:700}
.targets-list .vessel-item .dist .brg-val{color:#8b949e;font-size:11px;font-weight:600}
.targets-list .vessel-item .dist .brg-val::before{content:"\2192 ";opacity:.6}
.targets-list .vessel-item > div:not(.vessel-mmsi-row):not(.name):not(.dist):not(.compact-stats){
display:none;
}
.targets-list .compact-stats{
grid-area:stats;
display:grid;
grid-template-columns:repeat(auto-fill, minmax(84px, 1fr));
gap:4px 6px;
margin-top:2px;
font-family:ui-monospace,monospace;
}
.targets-list .compact-stats > span{
display:flex;flex-direction:column;
align-items:flex-start;
padding:4px 6px;
background:rgba(15,52,96,.55);
border:1px solid rgba(79,195,247,.18);
border-radius:5px;
cursor:help;
line-height:1.1;
min-width:0;
}
.targets-list .compact-stats > span:hover{
border-color:rgba(79,195,247,.5);
background:rgba(15,52,96,.85);
}
.targets-list .compact-stats .k{
font-size:9px;font-weight:700;
letter-spacing:.6px;
color:#8b949e;
text-transform:uppercase;
}
.targets-list .compact-stats b{
font-size:12px;color:#e0e0e0;font-weight:700;
max-width:100%;
white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* ===================================================================
Nearby HUD — semi-transparent corner overlay that shows own ship
(speed / coords / compass) plus up to 5 nearest targets (or a single
currently selected target with a clear-X). Replaces the mobile
bottom tab-bar as the primary in-field readout.
=================================================================== */
.nearby-hud{
position:absolute;
z-index:1000;
left:8px;
bottom:8px;
width:min(290px, 42vw);
max-height:min(50vh, 540px);
background:rgba(13,17,23,.72);
backdrop-filter:blur(6px);
-webkit-backdrop-filter:blur(6px);
border:1px solid rgba(210,255,26,.22);
border-radius:10px;
color:#c9d1d9;
font-family:ui-monospace,Menlo,Consolas,monospace;
font-size:11px;
box-shadow:0 4px 14px rgba(0,0,0,.45);
display:flex;flex-direction:column;
overflow:hidden;
transition:max-height .2s ease, opacity .2s ease;
}
.nearby-hud.is-empty{opacity:.6}
.nearby-hud.is-collapsed{
max-height:54px;
}
.nearby-hud.is-collapsed .nearby-hud__list,
.nearby-hud.is-collapsed .nhud-own__coords,
.nearby-hud.is-collapsed .nhud-own__src{display:none}
.nearby-hud.is-collapsed .nhud-compass{transform:scale(.82);margin-right:4px}
.nearby-hud__own{
padding:6px 8px 5px 8px;
border-bottom:1px solid rgba(79,195,247,.14);
}
.nhud-own__row{display:flex;gap:8px;align-items:center}
.nhud-own__info{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.nhud-own__line{display:flex;gap:6px;align-items:baseline}
.nhud-k{color:#8b949e;font-weight:600;font-size:10px;text-transform:uppercase;letter-spacing:.4px}
.nhud-own__line b{color:#d2ff1a;font-weight:700;font-size:13px}
.nhud-own__coords{color:#c9d1d9;font-size:10px;opacity:.85;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.nhud-own__src{color:#667;font-size:9px;text-transform:uppercase;letter-spacing:.5px}
.nhud-own__src.nhud-own__src--err{color:#f85149;text-transform:none;letter-spacing:0}
.nhud-compass{
position:relative;
display:inline-flex;align-items:center;justify-content:center;
flex:0 0 auto;
}
.nhud-compass svg{display:block}
.nhud-compass-needle{
transition:transform .25s ease;
}
.nhud-compass.no-data .nhud-compass-needle{opacity:.3}
.nhud-compass-val{
position:absolute;left:50%;top:55%;transform:translate(-50%, -50%);
pointer-events:none;
font-size:9px;font-weight:700;color:#d2ff1a;
background:rgba(13,17,23,.6);padding:0 3px;border-radius:3px;
letter-spacing:.3px;
display:none;
}
.nhud-compass.has-val .nhud-compass-val{display:inline-block}
.nhud-compass.from-phone{box-shadow:0 0 0 2px rgba(79,195,247,.28) inset;border-radius:50%}
.nhud-compass.is-rotating-map{box-shadow:0 0 0 2px rgba(210,255,26,.55) inset;border-radius:50%}
.nhud-compass:hover{filter:brightness(1.1)}
.nearby-hud__list{
flex:1;min-height:0;
overflow-y:auto;
overscroll-behavior:contain;
}
.nearby-hud__list::-webkit-scrollbar{width:4px}
.nearby-hud__list::-webkit-scrollbar-thumb{background:rgba(210,255,26,.25);border-radius:2px}
.nhud-item{
position:relative;
padding:5px 8px 5px 8px;
border-bottom:1px solid rgba(79,195,247,.08);
cursor:pointer;
transition:background .12s;
}
.nhud-item:last-child{border-bottom:none}
.nhud-item:hover{background:rgba(210,255,26,.07)}
.nhud-item.is-selected{background:rgba(210,255,26,.12);border-left:3px solid #d2ff1a;padding-left:5px}
.nhud-item__top{display:flex;gap:6px;align-items:baseline;justify-content:space-between}
.nhud-item__id{
display:flex;gap:4px;align-items:baseline;min-width:0;flex:1;
white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.nhud-item__name{color:#c9d1d9;font-weight:600;font-size:11px;overflow:hidden;text-overflow:ellipsis}
.nhud-item__callsign{color:#8b949e;font-size:9px}
.nhud-item__mmsi{color:#667;font-size:9px;font-weight:500}
.nhud-item__dist{color:#d2ff1a;font-weight:700;font-size:11px;white-space:nowrap;flex:0 0 auto}
.nhud-item__bot{display:flex;gap:8px;align-items:baseline;margin-top:1px;color:#8b949e;font-size:10px}
.nhud-item__bot .k{color:#667;margin-right:2px;font-size:9px}
.nhud-item__bot b{color:#c9d1d9;font-weight:600}
.nhud-item__brg::before{content:"\2192 ";opacity:.6}
.nhud-item__coords{color:#667;font-size:9px;margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.nhud-item__clear{
position:absolute;right:4px;top:4px;
width:20px;height:20px;border-radius:50%;
background:rgba(248,81,73,.15);color:#f85149;
border:1px solid rgba(248,81,73,.35);
display:flex;align-items:center;justify-content:center;
cursor:pointer;font-size:12px;font-weight:700;line-height:1;
padding:0;
}
.nhud-item__clear:hover{background:rgba(248,81,73,.3)}
.nearby-hud__toggle{
position:absolute;top:2px;right:2px;
width:22px;height:22px;
background:transparent;border:none;border-radius:4px;
color:#8b949e;cursor:pointer;
display:flex;align-items:center;justify-content:center;
z-index:2;
transition:transform .2s ease, color .15s;
}
.nearby-hud__toggle:hover{color:#d2ff1a}
.nearby-hud.is-collapsed .nearby-hud__toggle{transform:rotate(180deg)}
.nhud-empty{
padding:10px 8px;color:#667;text-align:center;font-size:10px;
}
/* Mobile layout: wider, anchored above the mob-panel-bar. */
@media(max-width:600px), (max-height:520px) and (pointer:coarse){
.nearby-hud{
left:8px;right:8px;
width:auto;
bottom:calc(var(--mob-panel-bar-height, 42px) + var(--safe-bottom, 0px) + 8px);
max-height:38vh;
}
/* When the bottom sheets (sidebar / ownship-panel) are open, hide the HUD to avoid stacking. */
#sidebar.mob-open ~ .nearby-hud,
#ownship-panel.mob-open ~ .nearby-hud{display:none}
}
@media (max-height:520px) and (pointer:coarse){
.nearby-hud{
left:8px;
bottom:calc(8px + var(--safe-bottom, 0px));
right:calc(var(--mob-panel-bar-width, 72px) + var(--safe-right, 0px) + 8px);
max-height:calc(100vh - 52px - var(--safe-bottom, 0px));
}
}