*{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)); } }