/* Kvarken Forecast. Tokens first; light is the base, dark redefines tokens only. */

:root{
  --ground:#f2f4f7; --surface:#ffffff; --sunken:#e8ecf0;
  --ink:#0f1419; --ink-2:#4a5562; --ink-3:#7b8794;
  --rule:#dde3e9; --rule-soft:#eaeef2;
  --accent:#0b7fa8; --accent-ink:#ffffff; --accent-soft:rgba(11,127,168,.12);
  --ww3:#2563d9; --ww3-soft:rgba(37,99,217,.14); --wam:#e0642f;
  /* Magenta, not the violet it was: violet sits 30 degrees from the wave blue
     and the two lanes of a session chart read as one colour at a glance. Orange
     would separate better still but belongs to WAM. Shape carries the identity
     as well as hue -- the wave lane is a filled area, wind a bare line, and each
     peak label states its unit -- so the pairing survives colour blindness. */
  --wind:#c2186b; --wind-soft:rgba(194,24,107,.16);
  /* The station pins. Their bodies are white in both themes, so one value each
     serves both; these are the icon set's own colours. */
  --mk-buoy:#f27600; --mk-wind:#07964b; --mk-level:#1677e8; --mk-spot:#0a9491;
  --good:#1f8a52; --warn:#c48a10; --bad:#c43d2f;
  --night:rgba(15,20,25,.06);
  --sea:rgb(148,166,180);
  --scrim:rgba(15,20,25,.28);
  --shadow:0 1px 2px rgba(16,24,40,.05),0 10px 28px -14px rgba(16,24,40,.22);
  --shadow-lg:-8px 0 40px -12px rgba(16,24,40,.35);
  --f-ui:"Instrument Sans",-apple-system,"SF Pro Text","Helvetica Neue",Arial,sans-serif;
  --f-mono:"JetBrains Mono",ui-monospace,SFMono-Regular,Consolas,monospace;
  --r:16px; --r-s:10px;
  --ease:cubic-bezier(.32,.72,0,1);
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --ground:#0b0e13; --surface:#161a21; --sunken:#10141a;
    --ink:#e9edf2; --ink-2:#a4aeba; --ink-3:#6e7986;
    --rule:#262c36; --rule-soft:#1e242d;
    --accent:#3ab0dc; --accent-ink:#06202b; --accent-soft:rgba(58,176,220,.16);
    --ww3:#6394f2; --ww3-soft:rgba(99,148,242,.16); --wam:#f0834f;
    --wind:#f472a8; --wind-soft:rgba(244,114,168,.2); --tide:#e8a13c;
    --good:#4dc17f; --warn:#e0ad3a; --bad:#ef6a5b;
    --night:rgba(255,255,255,.05);
    --sea:rgb(104,118,130);
    --scrim:rgba(0,0,0,.5);
    --shadow:0 1px 2px rgba(0,0,0,.5),0 10px 28px -14px rgba(0,0,0,.7);
    --shadow-lg:-8px 0 40px -12px rgba(0,0,0,.8);
  }
  :root:not([data-theme="light"]) .basemap{filter:brightness(.72) saturate(.8)}
}
:root[data-theme="dark"]{
  --ground:#0b0e13; --surface:#161a21; --sunken:#10141a;
  --ink:#e9edf2; --ink-2:#a4aeba; --ink-3:#6e7986;
  --rule:#262c36; --rule-soft:#1e242d;
  --accent:#3ab0dc; --accent-ink:#06202b; --accent-soft:rgba(58,176,220,.16);
  --ww3:#6394f2; --ww3-soft:rgba(99,148,242,.16); --wam:#f0834f;
  --wind:#f472a8; --wind-soft:rgba(244,114,168,.2); --tide:#e8a13c;
  --good:#4dc17f; --warn:#e0ad3a; --bad:#ef6a5b;
  --night:rgba(255,255,255,.05);
  --sea:rgb(104,118,130);
  --scrim:rgba(0,0,0,.5);
  --shadow:0 1px 2px rgba(0,0,0,.5),0 10px 28px -14px rgba(0,0,0,.7);
  --shadow-lg:-8px 0 40px -12px rgba(0,0,0,.8);
}
:root[data-theme="dark"] .basemap{filter:brightness(.72) saturate(.8)}

/* ---------- base ---------- */
*{box-sizing:border-box}
/* `hidden` is a statement about state, so it beats any layout display. Author
   styles outrank the browser's own [hidden] rule whatever their specificity, so
   every element given both `display:` and `hidden` stays on screen without it.
   That has now cost two bugs -- the click card and the replay's loading panel. */
[hidden]{display:none!important}
html,body{height:100%;margin:0}
body{
  background:var(--ground);color:var(--ink);
  font:15px/1.45 var(--f-ui);-webkit-font-smoothing:antialiased;
  display:grid;grid-template-rows:auto 1fr;
}
h1,h2,h3,p{margin:0}
button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:pointer}
button:focus-visible,input:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:6px}
svg{display:block}
.num{font-variant-numeric:tabular-nums}
.bad{color:var(--bad)}
.arr{width:14px;height:14px;display:inline-block;vertical-align:-2px;fill:currentColor}
.dot{display:inline-block;width:7px;height:7px;border-radius:50%;vertical-align:1px;margin-right:2px}
.dot.close{background:var(--good)} .dot.wide{background:var(--warn)} .dot.bad{background:var(--bad)}
.rel{font-weight:500}
.rel.offshore{color:var(--good)} .rel.onshore{color:var(--bad)} .rel.cross-shore{color:var(--warn)}

.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:6px;
  min-height:34px;padding:0 14px;border-radius:9px;font-weight:500;font-size:14px;
  background:var(--surface);color:var(--ink);border:1px solid var(--rule);
  transition:border-color .15s,transform .1s;
}
.btn:hover{border-color:var(--ink-3)}
.btn:active{transform:scale(.98)}
.btn:disabled{opacity:.6;cursor:default}
.btn.primary{background:var(--accent);color:var(--accent-ink);border-color:transparent}
.btn.quiet{border-color:transparent;background:transparent;color:var(--accent)}
.btn.danger{color:var(--bad)}
.icon{width:34px;height:34px;padding:0;border-radius:9px;display:inline-flex;align-items:center;justify-content:center}
.icon svg{width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}

/* ---------- top bar ---------- */
.bar{
  position:sticky;top:0;z-index:40;
  display:flex;align-items:center;gap:14px;
  padding:10px 20px;padding-top:calc(10px + env(safe-area-inset-top));
  background:color-mix(in srgb,var(--ground) 78%,transparent);
  backdrop-filter:saturate(180%) blur(20px);-webkit-backdrop-filter:saturate(180%) blur(20px);
  border-bottom:1px solid var(--rule-soft);
}
.brand{display:flex;align-items:center;gap:10px;font-weight:600;font-size:16px;letter-spacing:-.01em;white-space:nowrap}
.brand svg{width:22px;height:22px}
.seg{display:inline-flex;padding:3px;border-radius:10px;background:var(--sunken);margin:0 auto}
.seg button{
  padding:6px 16px;border-radius:8px;font-weight:500;font-size:14px;color:var(--ink-2);min-height:32px;
  transition:background .2s var(--ease),color .2s,box-shadow .2s;
}
.seg button[aria-selected="true"]{background:var(--surface);color:var(--ink);box-shadow:0 1px 3px rgba(0,0,0,.12)}
.bar .right{display:flex;align-items:center;gap:8px;white-space:nowrap}
.stamp{font-size:12.5px;color:var(--ink-3)}
@media (max-width:640px){
  .bar{gap:10px;padding-left:14px;padding-right:14px}
  .brand span,.stamp{display:none}
  .seg button{padding:6px 12px}
}

/* ---------- overview ---------- */
main{min-height:0}
.view{display:none;height:100%}
.view.on{display:block}
.wrap{max-width:1380px;margin:0 auto;padding:22px 20px 40px}
@media (max-width:640px){.wrap{padding:16px 14px 32px}}
.lead{display:flex;align-items:baseline;gap:14px;flex-wrap:wrap;margin-bottom:16px}
.lead h2{font-size:22px;font-weight:600;letter-spacing:-.02em}
.lead p{font-size:13px;color:var(--ink-3)}
.lead .scale{margin-left:auto;font-size:12.5px;color:var(--ink-3)}

.cards{display:grid;gap:16px;grid-template-columns:repeat(auto-fill,minmax(330px,1fr))}
@media (max-width:400px){.cards{grid-template-columns:1fr}}
.card{
  position:relative;text-align:left;
  background:var(--surface);border-radius:var(--r);box-shadow:var(--shadow);
  padding:16px 18px 14px;cursor:pointer;border:1px solid transparent;
  transition:transform .2s var(--ease),box-shadow .2s,border-color .2s;
}
.card:hover{transform:translateY(-1px);box-shadow:0 1px 2px rgba(16,24,40,.05),0 16px 32px -14px rgba(16,24,40,.3)}
.card:active{transform:scale(.995)}
.card[aria-current="true"]{border-color:var(--accent)}
.card.busy .hero,.card.busy .mini{opacity:.45}
.card header{display:flex;align-items:baseline;gap:10px;margin-bottom:10px}
.card h3{font-size:17px;font-weight:600;letter-spacing:-.01em;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.card .coords{font-size:11.5px;color:var(--ink-3);font-family:var(--f-mono)}
.card .chev{color:var(--ink-3);width:16px;height:16px;flex:none;align-self:center}
.card .chev svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.card .nodata{margin:8px 0 12px;font-size:12.5px;color:var(--bad)}

.hero{display:grid;grid-template-columns:1.35fr 1fr 1fr 1.15fr;gap:8px;align-items:end;margin-bottom:6px}
.stat .v{display:flex;align-items:baseline;gap:3px;font-size:22px;font-weight:600;letter-spacing:-.025em;line-height:1.05;font-variant-numeric:tabular-nums}
.stat.big .v{font-size:38px;letter-spacing:-.035em}
.stat .u{font-size:12px;font-weight:500;color:var(--ink-3);letter-spacing:0}
.stat .l{font-size:11.5px;color:var(--ink-3);margin-top:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.stat .l b{font-weight:500;color:var(--ink-2)}
.peak{display:flex;align-items:baseline;gap:6px;font-size:12.5px;color:var(--ink-3);margin:6px 0 8px}
.peak b{color:var(--ink);font-weight:600;font-variant-numeric:tabular-nums}
.peak time{color:var(--ink-2)}

.mini{position:relative;height:74px;margin-top:2px}
.mini svg{width:100%;height:56px}
.mini .axis{position:absolute;left:0;right:0;top:58px;height:16px;font-size:10.5px;color:var(--ink-3);font-variant-numeric:tabular-nums}
.mini .axis span{position:absolute;white-space:nowrap}
.mini .axis span.day{font-weight:500;color:var(--ink-2);padding-left:3px;border-left:1px solid var(--rule)}
.mini .ymax{position:absolute;left:0;top:-2px;font-size:10px;color:var(--ink-3);font-family:var(--f-mono)}
.mini .f24{position:absolute;top:-2px;font-size:10px;color:var(--ink-3);transform:translateX(-50%)}

.empty{grid-column:1/-1;text-align:center;padding:56px 20px;color:var(--ink-3);background:var(--surface);border-radius:var(--r);border:1px dashed var(--rule)}
.empty h3{color:var(--ink);font-weight:600;margin-bottom:6px}
.legend{display:flex;gap:16px;flex-wrap:wrap;font-size:12.5px;color:var(--ink-3);margin-top:18px}
.legend span{display:inline-flex;align-items:center;gap:6px}
.legend i{width:14px;height:3px;border-radius:2px;display:inline-block}
.legend i.night{width:12px;height:12px;border-radius:3px;background:var(--night);border:1px solid var(--rule)}

/* ---------- detail panel ---------- */
.scrim{position:fixed;inset:0;background:var(--scrim);opacity:0;pointer-events:none;transition:opacity .3s;z-index:50}
.scrim.open{opacity:1;pointer-events:auto}
.panel{
  position:fixed;top:0;right:0;bottom:0;width:min(640px,100%);z-index:60;
  background:var(--ground);box-shadow:var(--shadow-lg);
  transform:translateX(102%);transition:transform .38s var(--ease);
  overflow-y:auto;overscroll-behavior:contain;
}
.panel.open{transform:none}
.panel .head{
  position:sticky;top:0;z-index:2;display:flex;align-items:center;gap:10px;
  padding:14px 18px;padding-top:calc(14px + env(safe-area-inset-top));
  background:color-mix(in srgb,var(--ground) 82%,transparent);
  backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px);
  border-bottom:1px solid var(--rule-soft);
}
.panel .head h2{font-size:20px;font-weight:600;letter-spacing:-.02em;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.panel .head .sub{display:block;font-size:12px;color:var(--ink-3);font-weight:400;font-family:var(--f-mono);letter-spacing:0}
.panel .body{padding:16px 18px 32px;display:grid;gap:16px}
.block{background:var(--surface);border-radius:var(--r);box-shadow:var(--shadow);padding:14px 16px}
.readout{display:grid;grid-template-columns:repeat(4,1fr);gap:10px}
.readout .t{grid-column:1/-1;font-size:13px;color:var(--ink-2);display:flex;gap:8px;align-items:baseline}
.readout .t b{font-weight:600;color:var(--ink);font-size:15px}
.readout .t .tag{font-size:11px;padding:2px 7px;border-radius:99px;background:var(--accent-soft);color:var(--accent);font-weight:500}
.readout .t .hint{margin-left:auto;font-size:11.5px;color:var(--ink-3)}
.readout .k{font-size:11px;color:var(--ink-3);text-transform:uppercase;letter-spacing:.05em}
.readout .v{font-size:20px;font-weight:600;letter-spacing:-.02em;font-variant-numeric:tabular-nums;line-height:1.1;margin-top:2px}
.readout .v small{font-size:12px;color:var(--ink-3);font-weight:500;margin-left:2px}
.readout .s{font-size:11.5px;color:var(--ink-3);margin-top:2px}
@media (max-width:420px){.readout{grid-template-columns:repeat(2,1fr)}}
.charts{display:grid;gap:14px;cursor:crosshair;touch-action:pan-y}
.charts svg{width:100%;height:auto}
.charts text{font-family:var(--f-mono);font-size:10px;fill:var(--ink-3)}
.charts .lbl{font-family:var(--f-ui);font-size:11px;font-weight:600}
.chart-title{font-size:13px;font-weight:600;color:var(--ink-2);margin:0 0 4px}
.chart-title small{font-weight:400;color:var(--ink-3);font-size:12px}
/* z-index:0 keeps Leaflet's panes, which run to 1000, inside this box. Without
   it the map and the rose ride over the panel's sticky header when you scroll. */
.orient{position:relative;z-index:0;aspect-ratio:1;max-width:340px;margin:4px auto 0;border-radius:12px;overflow:hidden;background:var(--sunken)}
.orient .lmap{position:absolute;inset:0}
.orient .lmap .leaflet-control-attribution{font-size:9px}
.orient svg{position:absolute;inset:0;width:100%;height:100%;cursor:crosshair;z-index:800}   /* above Leaflet panes (<=700), below its controls (1000) */
.orient text{font:600 12px var(--f-ui);fill:#0f1419;paint-order:stroke;stroke:rgba(255,255,255,.85);stroke-width:3px;text-anchor:middle}
.orient-block .hint{font-size:11.5px;color:var(--ink-3);margin-top:8px;text-align:center}
/* Base and overlay pickers under the inset. Same shape as the header's view
   tabs, at chip size. */
.orientctl{display:flex;gap:8px;justify-content:center;flex-wrap:wrap;align-items:center;margin-top:8px}
.btn.compact{padding:4px 10px;font-size:11.5px}
/* Position picker in the spot dialog. Fixed height: a dialog that grows with
   its map pushes the buttons off a short screen. */
.pickmap{position:relative;height:210px;border-radius:10px;overflow:hidden;background:var(--sunken);border:1px solid var(--rule)}
.pickmap .lmap{position:absolute;inset:0}
.coord{margin-top:6px;font-size:11.5px;color:var(--ink-3);text-align:center}
/* Panel scrubber: reads the numbers above it across the forecast. */
.ptime{display:flex;gap:10px;align-items:center;margin-top:10px}
.ptime input[type=range]{flex:1}
.pick{display:flex;background:var(--sunken);border:1px solid var(--rule);border-radius:8px;padding:2px;gap:2px}
.pick button{font:500 11.5px var(--f-ui);color:var(--ink-2);background:none;border:0;border-radius:6px;padding:3px 9px;cursor:pointer}
.pick button:hover:not([disabled]){color:var(--ink)}
.pick button[aria-pressed="true"]{background:var(--card);color:var(--ink);box-shadow:0 1px 2px rgba(0,0,0,.12)}
.pick button[disabled]{opacity:.4;cursor:default}
/* Depth key and profile. The bar rules are shared with the map legend above;
   only the width differs, so the key sits over the profile it explains. */
.depthkey{max-width:340px;margin:0 auto 6px}
.maplegend .depthkey{max-width:none;margin:6px 0 0}
.profile svg{display:block;width:100%;height:auto}
.profile text{font:9.5px var(--f-mono);fill:var(--ink-2)}
.profile text.lbl{font:600 10px var(--f-ui)}
#depthNote{font-size:11.5px;color:var(--ink-3);margin-top:6px;line-height:1.45}
#depthNote b{color:var(--ink);font-weight:600;font-variant-numeric:tabular-nums}
.actions{display:flex;gap:8px}
.actions .btn{flex:1}
/* ---------- session log ---------- */
.session{padding:8px 0;border-top:1px solid var(--rule-soft)}
.session:first-child{border-top:0;padding-top:2px}
.session header{display:flex;align-items:baseline;gap:8px;font-size:13px}
.session header b{font-weight:600}
.session .when{color:var(--ink-3);font-size:11.5px;font-variant-numeric:tabular-nums}
.session .open{margin-left:auto;font:500 11px var(--f-ui);color:var(--accent);padding:1px 6px;border-radius:6px}
.session .open:hover{background:var(--accent-soft)}
.session .del{color:var(--ink-3);font:1rem/1 var(--f-ui);padding:0 2px}
.session .del:hover{color:var(--bad)}
.session .del.arm{font:600 11px var(--f-ui);color:var(--bad)}
.session .cond{font-size:12.5px;color:var(--ink-2);margin-top:3px;display:flex;flex-wrap:wrap;gap:0 6px;align-items:center}
.session .cond b{color:var(--ink);font-weight:600;font-variant-numeric:tabular-nums}
.session .snote{font-size:12.5px;color:var(--ink);margin-top:4px;white-space:pre-wrap}
/* Naming a variable in its lane's colour, so a sentence doubles as the legend. */
/* Marks a card whose numbers were read offshore of the spot, because no model
   has a cell there. On the coordinates, since it qualifies the whole card; the
   panel carries the explanation and hovering gives it too. */
.card .coords .off{font-style:normal;color:var(--ink-3);vertical-align:2px;margin-left:1px}

.k-wave{color:var(--ww3);font-weight:600}
.k-wind{color:var(--wind);font-weight:600}
.sesschart{width:100%;height:auto;margin:5px 0 2px;display:block}
.sesschart .cur{stroke-width:1.4;opacity:.8}
.sesschart text{font-family:var(--f-mono);font-size:8.5px;fill:var(--ink-3)}
.sesschart .peak{font:600 9.5px var(--f-ui)}
#permanentNote{margin:0;align-self:center}
#sessionMsg:empty{display:none}
#sessionList{margin-bottom:10px}
.field textarea{
  font:14px var(--f-ui);padding:10px 12px;border-radius:var(--r-s);resize:vertical;
  background:var(--ground);color:var(--ink);border:1px solid var(--rule);
}

.note{font-size:12px;color:var(--ink-3);line-height:1.5}
.note b{color:var(--ink-2);font-weight:500}

/* ---------- map ---------- */
#viewMap{display:none;grid-template-rows:auto 1fr;height:100%}
#viewMap.on{display:grid}
.tools{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:10px 20px;background:var(--surface);border-bottom:1px solid var(--rule-soft)}
.chips{display:flex;gap:6px;flex-wrap:wrap}
.chip{padding:5px 12px;border-radius:99px;font-size:13px;font-weight:500;min-height:30px;background:var(--sunken);color:var(--ink-2);border:1px solid transparent;transition:all .15s}
.chip[aria-pressed="true"]{background:var(--accent-soft);color:var(--accent);border-color:color-mix(in srgb,var(--accent) 35%,transparent)}
.time{display:flex;align-items:center;gap:10px;flex:1 1 400px;min-width:0}
.time input[type=range]{flex:1;accent-color:var(--accent);min-width:80px}
.clock{font:500 13px var(--f-mono);font-variant-numeric:tabular-nums;white-space:nowrap;min-width:17ch}
.status{display:flex;align-items:center;gap:10px;margin-left:auto}
.status span{font-size:11.5px;color:var(--ink-3);font-family:var(--f-mono);white-space:nowrap}
/* z-index:0 makes the map its own stacking context. Without it Leaflet's panes,
   which run to 1000, compete with the page and paint over the detail panel. */
#map{min-height:0;position:relative;z-index:0;background:var(--sea)}
.leaflet-container{font-family:var(--f-ui)}
.leaflet-container .leaflet-control-attribution{font-size:10px;background:color-mix(in srgb,var(--surface) 80%,transparent);color:var(--ink-3)}
.leaflet-tooltip{font:500 12.5px var(--f-ui);border-radius:8px;border:0;box-shadow:var(--shadow)}
@media (max-width:640px){.tools{padding:8px 12px}.time{min-width:100%}}

.maplegend{
  width:372px;display:flex;flex-direction:column;gap:3px;
  background:color-mix(in srgb,var(--surface) 88%,transparent);backdrop-filter:blur(10px);
  padding:8px 10px;border-radius:10px;box-shadow:var(--shadow);font-size:11.5px;color:var(--ink-2);
}
.maplegend .fbar,.depthkey .fbar{display:flex;height:12px;border-radius:3px;overflow:hidden}
.maplegend .fbar.wind{margin-top:6px;border:1px solid var(--rule)}
.maplegend .fbar span,.depthkey .fbar span{flex:1}
.maplegend .ticks,.depthkey .ticks{position:relative;height:12px;font:9.5px var(--f-mono);color:var(--ink-2)}
.maplegend .ticks span,.depthkey .ticks span{position:absolute;transform:translateX(-50%)}
.maplegend p{font-size:11px;color:var(--ink-3);margin-top:2px;line-height:1.4}

/* Every map label hangs in a zero-size box sitting exactly on its coordinate.
   Leaflet owns the box -- its transform and its margins -- and nothing of ours
   is written there; the label inside centres itself, and a nudge from the
   spreading pass is one more term in the same transform.

   The separation is the point. The nudge used to be the icon's own margin-top,
   which is where Leaflet keeps the icon anchor: two writers on one property,
   and labels that slid off their stations whenever the map repositioned a
   marker while a nudge was in place. */
.pinbox{width:0;height:0}
.pinbox > *{position:absolute;transform:translate(-50%,calc(-50% + var(--lift,0px)))}
/* A spot's name hangs beside the pin's bulb -- right of the point, and up by
   about the height the bulb sits above the tip -- so it clears both the pin and
   the coordinate the pin is marking. */
.pinbox > .spotname{transform:translate(17px,calc(-50% - 18px + var(--lift,0px)))}

/* One frame for every live reading. Opaque, because these sit out on the wave
   field where a translucent box takes the colour underneath it. */
.pill{
  display:flex;align-items:center;justify-content:center;gap:3px;
  height:22px;padding:0 8px;white-space:nowrap;
  background:var(--surface);color:var(--ink);
  border:1px solid var(--ink-3);border-radius:99px;
  font:600 12px var(--f-ui);font-variant-numeric:tabular-nums;
  box-shadow:0 1px 4px rgba(0,0,0,.28);
}
.pill small{font-size:9.5px;font-weight:500;color:var(--ink-3)}
.pill svg{width:11px;height:11px;flex:none}
/* What tells them apart: a live dot, an arrow, or a signed number. */
.buoy .live-dot{width:7px;height:7px;border-radius:50%;background:#e0335c;flex:none;animation:pulse 1.6s ease-in-out infinite}
@keyframes pulse{50%{opacity:.35}}
@media (prefers-reduced-motion:reduce){.buoy i{animation:none}}
.leaflet-tooltip .muted{color:var(--ink-3);font-weight:400;font-size:11px}
.live{display:inline-block;padding:0 5px;border-radius:99px;background:#e0335c;color:#fff;font-size:10px;font-weight:600;vertical-align:1px}
.ice{display:inline-block;padding:1px 7px;border-radius:99px;background:var(--accent-soft);color:var(--accent);font-size:11px;font-weight:600;white-space:nowrap}
.readout .s .ice{margin-top:2px}
.maplegend #iceLegend img{display:block;width:100%;height:14px;margin-top:6px;border-radius:3px}
/* Opaque, unlike the gauge pills: a wind reading sits out on the wave field,
   where a translucent box takes the colour underneath it and the arrow -- pale
   at the low speeds this coast mostly gets -- disappears into the green. */
/* Your own spots, over everything. The dot marks the coordinate; the name is
   attached to it so the two can never drift apart. */
/* Zoomed out, a reading becomes a pin: a station is here, and this is what it
   measures. No value in it at all -- not the digits, which are gone, and not the
   colour, which is fixed per kind. At this scale a value is either unreadable or
   in the wrong place, and a mark that pretends otherwise is worse than one that
   says less.

   A pin is anchored by its tip, so it overrides the centring the other labels
   use. 25 x 28 keeps the four-unit strokes of a 64-wide drawing at about 1.5 px,
   which is the smallest that still reads as a picture rather than a smudge.

   Not called `.mini`: that is the card strip-chart class, and reusing the name
   collapsed every strip on the overview to fourteen pixels tall. */
.mark{width:25px;height:28px}
.mark svg{width:100%;height:100%;display:block;filter:drop-shadow(0 1px 2px rgba(0,0,0,.45))}
.pinbox > .mark{transform:translate(-50%,calc(-100% + var(--lift,0px)))}
.mark-buoy{color:var(--mk-buoy)}
.mark-wind{color:var(--mk-wind)}
.mark-level{color:var(--mk-level)}

/* The same three symbols, keyed in the legend. */
.markkey{display:flex;flex-wrap:wrap;gap:3px 12px;align-items:center;margin-top:9px}
.markkey span{display:flex;align-items:center;gap:4px;font-size:11px;color:var(--ink-2)}
/* `.markkey span svg` rather than `.markkey svg`: the map pin's own
   `.spotpin svg{width:100%}` is the more specific of the two otherwise, and the
   legend's copy of the wave pin grew to fill the legend. */
.markkey span.spotpin{width:auto;height:auto;cursor:default}
.markkey span svg{width:15px;height:17px;flex:none;filter:none}
.markkey p{flex-basis:100%;margin-top:1px}

/* Your own spots, over everything and at every zoom: a breaking wave, drawn a
   little larger than a station pin because it is the thing the map is for. The
   pin is the coordinate -- anchored by its tip, like the station pins -- and the
   name is a separate label so it can be nudged clear of a neighbour without
   taking the coordinate with it. */
.spotpin{width:29px;height:33px;cursor:pointer;color:var(--mk-spot)}
.spotpin svg{width:100%;height:100%;display:block;filter:drop-shadow(0 1px 3px rgba(0,0,0,.5))}
.pinbox > .spotpin{transform:translate(-50%,calc(-100% + var(--lift,0px)))}
.spotname{
  white-space:nowrap;cursor:pointer;
  font:700 12px var(--f-ui);letter-spacing:-.01em;
  color:var(--ink);background:var(--surface);
  border:1px solid var(--ink);border-radius:99px;padding:2px 8px;
  box-shadow:0 1px 5px rgba(0,0,0,.35);
}
.spotpin.on{color:var(--accent)}                  /* the spot whose panel is open */
.spotname.on{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}
.pickhint{
  position:absolute;top:12px;left:50%;transform:translateX(-50%);z-index:400;
  padding:8px 14px;border-radius:99px;background:var(--ink);color:var(--ground);font-size:13px;font-weight:500;box-shadow:var(--shadow);
}

/* click card: corner pointer follows data-q, the quadrant it was placed in */
.mpop{
  position:absolute;z-index:650;min-width:168px;padding:9px 12px;
  background:var(--surface);color:var(--ink);border-radius:11px;font:13px/1.3 var(--f-ui);
  box-shadow:var(--shadow),0 0 0 1px var(--rule-soft);
}
.mpop::before{content:"";position:absolute;width:10px;height:10px;background:var(--surface);transform:rotate(45deg);box-shadow:-1px -1px 0 var(--rule-soft)}
.mpop[data-q="se"]::before{left:-5px;top:-5px}
.mpop[data-q="sw"]::before{right:-5px;top:-5px;transform:rotate(135deg)}
.mpop[data-q="ne"]::before{left:-5px;bottom:-5px;transform:rotate(-45deg)}
.mpop[data-q="nw"]::before{right:-5px;bottom:-5px;transform:rotate(-135deg)}
.mpop .x{position:absolute;top:2px;right:6px;color:var(--ink-3);font-size:16px;line-height:1;padding:2px 3px}
.mpop .body{display:grid;gap:5px}
.mpop .row{display:grid;grid-template-columns:44px 1fr auto;align-items:baseline;gap:8px;padding-right:22px}
.mpop .k{font-size:10.5px;color:var(--ink-3);text-transform:uppercase;letter-spacing:.05em}
.mpop .v{font-size:19px;font-weight:600;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.mpop .v small{font-size:11px;color:var(--ink-3);font-weight:500;margin-left:1px}
.mpop .d{font-size:12.5px;color:var(--ink-2);font-variant-numeric:tabular-nums;display:inline-flex;align-items:center;gap:3px}
.mpop.wide{min-width:364px}
.mpop .bname{font-weight:600;font-size:13px;padding-right:22px}
.mpop .brow{font-size:12px;color:var(--ink-2);margin:1px 0 4px}
.mpop svg.verify{width:100%;height:auto;margin:2px 0}
.mpop svg.verify text{font-family:var(--f-mono);font-size:9px;fill:var(--ink-3)}
.mpop .keys{display:flex;gap:12px;font-size:11px;color:var(--ink-3);margin-top:2px}
.mpop .keys span{display:inline-flex;align-items:center;gap:4px}
.mpop .keys i{width:12px;height:2.5px;border-radius:2px}
.mpop .bias{font-size:11.5px;color:var(--ink-2);margin-top:5px;padding-top:5px;border-top:1px solid var(--rule-soft)}
.mpop .muted{font-size:11.5px;color:var(--ink-3)}
.mpop .add{font:500 12px var(--f-ui);color:var(--accent);text-align:left;padding:2px 0 0;border-top:1px solid var(--rule-soft);margin-top:2px}

/* ---------- session replay ---------- */

/* Wider than the form dialogs, and its own grid: header, then the map beside a
   rail, then the timeline across the bottom. */
#replayDlg{width:min(1180px,calc(100vw - 20px));border-radius:18px}
/* minmax(0,...) throughout: a grid track sized to its content would take its
   width from the Leaflet tile container inside the map, which is deliberately
   larger than the viewport, and the dialog would grow past the screen. */
.replay{display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto;height:min(86vh,800px)}
.replay .head{display:flex;align-items:center;gap:10px;padding:12px 12px 10px 18px;border-bottom:1px solid var(--rule)}
.replay .who{flex:1;min-width:0}
.replay .who h2{font-size:18px;font-weight:600;letter-spacing:-.02em;margin:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.replay .who .when{display:block;font-size:11.5px;color:var(--ink-3);font-family:var(--f-mono);font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.replay .stage{position:relative;display:grid;grid-template-columns:minmax(0,1fr) 260px;min-height:0;min-width:0}

/* Over the map, where the eye already is: the archive read takes seconds, and
   until it lands both the map and the timeline are empty. */
.rload{
  position:absolute;inset:0;z-index:5;
  display:grid;place-content:center;justify-items:center;gap:9px;padding:20px;text-align:center;
  background:var(--surface);
  background:color-mix(in srgb,var(--surface) 84%,transparent);
  backdrop-filter:blur(2px);
}
.rload b{font-size:14px;font-weight:600}
.rload small{font-size:11.5px;color:var(--ink-3);max-width:38ch;line-height:1.45}
.rload .spin{
  width:22px;height:22px;border-radius:50%;
  border:2.5px solid var(--rule);border-top-color:var(--accent);
  animation:spin .8s linear infinite;
}
.rload[data-state="error"] .spin{display:none}
.rload[data-state="error"] b{color:var(--bad)}
@keyframes spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.rload .spin{animation-duration:2.4s}}
/* z-index:0 keeps Leaflet's panes, which run to 1000, out of the page's way. */
.replay .stagemap{position:relative;z-index:0;min-height:0;min-width:0;overflow:hidden;background:var(--sea)}
.replay .rail{border-left:1px solid var(--rule);padding:12px;overflow:auto;display:grid;gap:10px;align-content:start}
.replay .rail .orient{max-width:none;margin:0}
.replay .foot{border-top:1px solid var(--rule);padding:6px 14px 10px;min-width:0}
.replay .tl{touch-action:pan-y;cursor:col-resize}
.replay .tlbar{display:flex;align-items:center;gap:8px;margin-top:2px}
.replay .tlbar .hint{margin:0}
.replay .tlbar #rStatus{margin-left:auto;text-align:right}

.rclock{display:flex;align-items:baseline;gap:6px;font-family:var(--f-mono);font-variant-numeric:tabular-nums}
.rclock b{font-size:14px}
.rclock span,.rclock .in{font-size:11px;color:var(--ink-3);font-weight:500}
.rclock .in{color:var(--accent)}
.rvals{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.rvals .k{font-size:10.5px;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-3)}
.rvals .v{font:600 22px/1.1 var(--f-ui);font-variant-numeric:tabular-nums}
.rvals .v small{font-size:11px;font-weight:500;color:var(--ink-3);margin-left:2px}
.rvals .s{font-size:11.5px;color:var(--ink-2);display:flex;align-items:center;gap:3px;flex-wrap:wrap}

/* Narrow: the map above, then the rail as one short band with the top-down view
   beside the numbers. Both stage rows are given a fraction, because a row sized
   `auto` lets the square inset grow to the container's width and push the
   timeline off the screen. */
@media (max-width:860px){
  .replay{height:94vh}
  .replay .stage{grid-template-columns:1fr;grid-template-rows:minmax(200px,1.3fr) minmax(0,1fr)}
  .replay .rail{
    border-left:0;border-top:1px solid var(--rule);
    grid-template-columns:132px minmax(0,1fr);column-gap:12px;align-content:start;
  }
  .replay .rail .orient{grid-row:span 2;width:132px}
  .replay .rail .rvals .v{font-size:18px}
  .replay .rail .snote{grid-column:1/-1}
  .replay .tlbar{flex-wrap:wrap}
  .replay .tlbar #rScales{display:none}          /* the peak labels already carry the units */
  .replay .tlbar #rStatus{font-size:10.5px}
}

/* ---------- dialog ---------- */
dialog{border:0;padding:0;border-radius:18px;background:var(--surface);color:var(--ink);box-shadow:0 30px 80px -20px rgba(0,0,0,.45);width:min(440px,calc(100% - 32px));max-width:none}
dialog::backdrop{background:var(--scrim);backdrop-filter:blur(6px)}
dialog form{padding:22px 22px 18px;display:grid;gap:14px}
dialog h2{font-size:19px;font-weight:600;letter-spacing:-.02em}
.field{display:grid;gap:5px}
.field label{font-size:12.5px;color:var(--ink-2);font-weight:500}
.field input{font:15px var(--f-ui);padding:10px 12px;border-radius:var(--r-s);background:var(--ground);color:var(--ink);border:1px solid var(--rule);min-height:42px}
.field input.mono{font-family:var(--f-mono);font-size:14px}
.field small{font-size:11.5px;color:var(--ink-3)}
.two{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.err{font-size:12.5px;color:var(--bad);min-height:1.2em}
dialog .row{display:flex;gap:8px;justify-content:flex-end;align-items:center}
dialog .row .btn.quiet{margin-right:auto}

@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}
