/* SeaState design system — data-journalism clean.
   The discipline of NYT, the block formatting of Our World in Data, the spartan
   clarity of Climate Action Tracker. Navy ink on cool ivory. COLOR LIVES IN THE
   DATA, never the chrome — the only saturated hues are the model lines and the
   go/marginal/no-go status. Whitespace + fine hairlines do the separating. Every
   data block cites its source. Light theme only (see docs/CONVENTIONS.md). */

:root {
  /* surfaces & ink */
  --paper: #f3f4f2;
  --surface: #ffffff;
  --surface-2: #f7f8f7;
  --icon-halo: #ffffff;   /* map-glyph halo — white reads on the daytime blue sea */
  --ink: #16232e;         /* deep navy — reads near-black, on-brand */
  --ink-2: #48575f;
  --muted: #8a969b;
  --line: rgba(22, 35, 46, 0.11);
  --line-2: rgba(22, 35, 46, 0.20);
  --navy: #16232e;        /* brand structural color */
  --accent: #0f6b74;      /* interactive/links only, used sparingly */

  /* DATA ONLY — categorical model slots (fixed order = CVD mechanism; never reassign) */
  --m1: #2f6f9f;
  --m2: #2f9e78;
  --m3: #d98a2b;
  --m4: #7b5ea7;
  /* status ramp */
  --good: #2e8b57;
  --warn: #b9791a;
  --serious: #d9772b;
  --crit: #b23b3b;
  --now: #b23b3b;         /* current-time line */
  --obs: #48575f;         /* buoy observations */
  /* chart chrome */
  --grid: rgba(22, 35, 46, 0.09);
  --axis: rgba(22, 35, 46, 0.28);

  --sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --serif: Georgia, "Times New Roman", "Iowan Old Style", serif;
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  --r: 3px;
  color-scheme: light; /* native checkboxes / selects / scrollbars follow the theme */
}

/* ECDIS-style Dusk / Night color tables (after IHO S-52). Same token names, only
   luminance/hue re-cast: hue is kept constant so nothing shifts meaning between
   modes. Night is dim, blue-suppressed warm-neutral on near-black; red (--crit)
   stays the one reserved alarm token. :root above is the Day table. */
:root[data-theme="dusk"] {
  --paper: #22282a;
  --surface: #2b3234;
  --surface-2: #262c2e;
  --icon-halo: #dfe4e4;   /* light halo, reads on the dusk slate without glare */
  --ink: #b9c2c2;
  --ink-2: #9aa4a4;
  --muted: #7e8789;
  --line: rgba(220, 225, 225, 0.12);
  --line-2: rgba(220, 225, 225, 0.22);
  --navy: #cdd6d6;
  --accent: #5fbcc4;
  --m1: #4f93c8; --m2: #5aa06e; --m3: #c68a2e; --m4: #9a6fc0;
  --good: #4f9d5a; --warn: #c9a52e; --serious: #d97a1e; --crit: #d64a3a;
  --now: #9aa4a4; --obs: #9aa4a4;
  --grid: rgba(220, 225, 225, 0.10);
  --axis: rgba(220, 225, 225, 0.30);
  color-scheme: dark;
}
:root[data-theme="night"] {
  --paper: #070707;
  --surface: #100e0a;
  --surface-2: #16130d;
  --icon-halo: #221f18;   /* near-ground dark halo — no white glare; the warm glyph carries contrast */
  --ink: #b7a488;
  --ink-2: #97896f;
  --muted: #6f6656;
  --line: rgba(183, 164, 136, 0.14);
  --line-2: rgba(183, 164, 136, 0.24);
  --navy: #b7a488;
  --accent: #c9923f;
  --m1: #c9923f; --m2: #8a9b6b; --m3: #b98d6a; --m4: #7f938c;
  --good: #6f9f5b; --warn: #c9a13a; --serious: #d1741f; --crit: #e0503a;
  --now: #6f6656; --obs: #97896f;   /* now-line dim, NOT the reserved alarm red */
  --grid: rgba(183, 164, 136, 0.09);
  --axis: rgba(183, 164, 136, 0.28);
  color-scheme: dark;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  min-height: 100vh;
  font-family: var(--sans);
  background: var(--paper);
  color: var(--ink);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}
::selection { background: var(--navy); color: #fff; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }

.mono { font-family: var(--mono); }
.source { font-family: var(--mono); font-size: 11px; color: var(--muted); letter-spacing: 0.02em; }

/* ---- app layout ---- */
#app { min-height: 100vh; display: flex; flex-direction: column; }

.apphead {
  position: sticky; top: 0; z-index: 10;
  display: flex; align-items: center; gap: 20px;
  height: 52px; padding: 0 20px;
  background: var(--surface); border-bottom: 1px solid var(--line);
}
.brand { display: flex; align-items: center; gap: 10px; font-size: 15px; font-weight: 600; letter-spacing: 0.01em; }
.brand .mark { display: inline-flex; color: var(--navy); }
.apphead nav { display: flex; gap: 2px; margin-left: 10px; }
.apphead nav a { font-size: 13px; font-weight: 500; color: var(--ink-2); padding: 6px 11px; border-radius: 2px; text-decoration: none; }
.apphead nav a.on { background: var(--surface-2); color: var(--ink); box-shadow: inset 0 -2px 0 var(--navy); }
.apphead nav a:hover { color: var(--ink); }
.coordbox { margin-left: auto; font-family: var(--mono); font-size: 11.5px; color: var(--ink-2); display: flex; gap: 16px; align-items: center; }
.coordbox .k { color: var(--muted); }
.coordbox .health { color: var(--muted); }

/* Day / Dusk / Night / Auto segmented toggle on the top bar */
.themeseg { display: inline-flex; border: 1px solid var(--line-2); border-radius: 4px; overflow: hidden; flex: none; }
.themeseg-btn { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.06em; padding: 4px 8px; background: transparent; color: var(--muted); border: 0; border-left: 1px solid var(--line); cursor: pointer; line-height: 1.4; }
.themeseg-btn:first-child { border-left: 0; }
.themeseg-btn.on { background: var(--navy); color: var(--surface); }
.themeseg-btn:hover:not(.on) { color: var(--ink); }
.themeseg-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.avatar { width: 26px; height: 26px; border-radius: 50%; background: var(--navy); color: #fff; display: grid; place-items: center; font-family: var(--mono); font-size: 11px; }

/* map-dominant console: map fills the left, a scrolling column of blocks on the
   right. The rail grows with the viewport so meteograms stay wide-and-short
   (a squarish time series is unreadable); charts keep a ~2.8:1 aspect. */
/* rail · workspace panel · splitter · map · splitter · track column */
.appbody { position: relative; display: grid; grid-template-columns: var(--rail-w, 74px) var(--wspanel-w, 300px) 7px 1fr 7px var(--col2-w, clamp(380px, 34vw, 560px)); }
/* A hidden panel leaves the grid short of items, so the template must lose the
   panel AND its splitter — otherwise auto-placement slides the map into the
   panel's slot and the map, not the panel, is what collapses. The left gutter
   element is display:none'd alongside. */
.appbody.wsclosed { grid-template-columns: var(--rail-w, 74px) 1fr 7px var(--col2-w, clamp(380px, 34vw, 560px)); }
.appbody.wsclosed .lgutter { display: none; }
.map { height: calc(100vh - 52px); background: var(--paper); }
.map .maplibregl-ctrl-attrib { font-size: 10px; }
/* MapLibre ships white controls that ignore our tokens — theme them, and flip
   their dark glyphs to read on the dusk/night grounds. */
.map .maplibregl-ctrl-group { background: var(--surface); }
.map .maplibregl-ctrl-group button + button { border-top-color: var(--line); }
.map .maplibregl-ctrl-group button:hover { background: var(--surface-2); }
.map .maplibregl-ctrl-attrib { background: var(--surface); color: var(--muted); }
.map .maplibregl-ctrl-attrib a { color: var(--ink-2); }
:root[data-theme="dusk"] .map .maplibregl-ctrl-icon,
:root[data-theme="night"] .map .maplibregl-ctrl-icon { filter: invert(0.82); }
/* on-brand checked state for the layer-control / vessel checkboxes */
input[type="checkbox"] { accent-color: var(--accent); }

/* tropical callout in the location primer / window briefing */
.trop-note { font-size: 13px; line-height: 1.45; margin: 0 0 10px; padding: 7px 10px; border-radius: 3px; border: 1px solid; }
.trop-note.warn { color: var(--warn); border-color: var(--warn); background: color-mix(in srgb, var(--warn) 8%, transparent); }
.trop-note.crit { color: var(--crit); border-color: var(--crit); background: color-mix(in srgb, var(--crit) 9%, transparent); }
.trop-note strong { color: inherit; }

/* sound-speed structure block in the location primer (survey acoustics) */
.svp-note { font-size: 13px; line-height: 1.5; margin: 10px 0 0; color: var(--ink-2); }
.svp-body .svp-note { margin-top: 2px; } /* first child of its own panel body — no big gap */
.svp-note .svp-k { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--m1); margin-right: 6px; }
.svp-note .svp-src { display: block; font-size: 11px; color: var(--muted); margin-top: 3px; }
/* Sound-speed equation selector */
.svp-ctl { display: flex; align-items: center; gap: 8px; margin: 6px 0 2px; }
.svp-ctl-lab { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }
.svp-eq { font-size: 12px; padding: 3px 8px; }
/* SVP profile: three depth-down panels — temperature / salinity / sound velocity */
.svp-svg { display: block; width: 100%; height: auto; margin: 8px 0 2px; }
.svp-svg .svp-band { fill: var(--m3); opacity: 0.13; }
.svp-svg .svp-grid { stroke: var(--grid); stroke-width: 1; }
.svp-svg .svp-mld { stroke: var(--axis); stroke-width: 1; stroke-dasharray: 3 3; }
.svp-svg .svp-axisln { stroke: var(--serious); stroke-width: 1; stroke-dasharray: 2 2; }
.svp-svg .svp-sound { fill: none; stroke: var(--m1); stroke-width: 1.7; stroke-linejoin: round; stroke-linecap: round; }
.svp-svg .svp-temp { fill: none; stroke: var(--m3); stroke-width: 1.5; stroke-linejoin: round; stroke-linecap: round; }
.svp-svg .svp-sal { fill: none; stroke: var(--m2); stroke-width: 1.5; stroke-linejoin: round; stroke-linecap: round; }
/* Observed (Argo float) curve — the reserved observations colour, dashed so it
 *  reads as measured-and-separate against the solid modelled curves it sits beside. */
.svp-svg .svp-obs { fill: none; stroke: var(--obs); stroke-width: 1.6; stroke-dasharray: 4 3; stroke-linejoin: round; stroke-linecap: round; }
/* Climatology (WOA23) curve — the "usual" reference. Muted and thin, dashed finely so
 *  it reads as quieter than both the solid model and the heavier dashed observed cast. */
.svp-svg .svp-clim { fill: none; stroke: var(--muted); stroke-width: 1.2; stroke-dasharray: 2 3; stroke-linejoin: round; stroke-linecap: round; opacity: 0.85; }
.svp-svg .svp-ax { fill: var(--muted); font-family: var(--mono); font-size: 9px; }
.svp-svg .svp-lab { fill: var(--muted); font-family: var(--mono); font-size: 9px; }
.svp-svg .svp-ptitle { fill: var(--ink-2); font-family: var(--mono); font-size: 10px; letter-spacing: 0.02em; }
.svp-svg .svp-unit { fill: var(--muted); font-family: var(--mono); font-size: 8.5px; }
.svp-legend { display: flex; flex-wrap: wrap; gap: 4px 12px; margin: 0; font-size: 11px; color: var(--muted); }
.svp-legend .svp-lg { display: inline-flex; align-items: center; }
.svp-legend .svp-lg::before { content: ""; width: 12px; height: 0; margin-right: 5px; border-top: 2px solid currentColor; }
.svp-legend .svp-lg-sound { color: var(--m1); }
.svp-legend .svp-lg-temp { color: var(--m3); }
.svp-legend .svp-lg-sal { color: var(--m2); }
.svp-legend .svp-lg-band::before { height: 10px; border: 0; background: var(--m3); opacity: 0.28; margin-top: -1px; }
.svp-legend .svp-lg-mld { color: var(--muted); }
.svp-legend .svp-lg-mld::before { border-top-style: dashed; border-top-color: var(--axis); }
.svp-legend .svp-lg-band { color: var(--muted); }
.svp-legend .svp-lg-obs { color: var(--obs); }
.svp-legend .svp-lg-obs::before { border-top-style: dashed; border-top-color: var(--obs); }
.svp-legend .svp-lg-clim { color: var(--muted); }
.svp-legend .svp-lg-clim::before { border-top-style: dashed; border-top-color: var(--muted); }
/* Provenance for the observed overlay — float, age, distance, reach — in the
 *  observations colour so the line and the curve read as one statement. */
.svp-obs-src { font-size: 11px; color: var(--obs); margin: 4px 0 0; }
/* Actions row: ship's-surface-SV input (Task 2) + the CSV download (Task 1). Wraps on
 *  narrow rails; the download sits at the end, pushed right when there is room. */
.svp-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin: 6px 0 2px; }
.svp-ship-ctl { display: flex; align-items: center; gap: 8px; }
.svp-ship { font-size: 12px; padding: 3px 8px; width: 8.5em; flex: 0 0 auto; }
.svp-dl { margin-left: auto; padding: 5px 12px; font-size: 12px; }
/* Δ chips ride on the legend entries: the closest-match one keyed to --good, the rest
 *  muted. Small, monospace, tight — a read at a glance, not a callout. */
.svp-dchip { font-family: var(--mono); font-size: 10px; letter-spacing: 0.02em; margin-left: 6px; white-space: nowrap; }
.svp-dchip.ok { color: var(--good); }
.svp-dchip.muted { color: var(--muted); }
.svp-match-note { font-size: 11px; line-height: 1.45; color: var(--ink-2); margin: 4px 0 0; }
/* Draggable splitter between map and sidebar. Its width is the CSS var above. */
.gutter { height: calc(100vh - 52px); background: var(--line); cursor: col-resize; position: relative; touch-action: none; user-select: none; }
.gutter::after { content: ""; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 2px; height: 28px; border-radius: 2px; background: var(--muted); opacity: 0.5; }
.gutter:hover, .gutter.dragging { background: var(--accent); }
.gutter:hover::after, .gutter.dragging::after { background: #fff; opacity: 0.9; }
.col2 { height: calc(100vh - 52px); overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 16px; }
@media (max-width: 880px) {
  .appbody { grid-template-columns: 1fr; }
  .map { height: 52vh; border-bottom: 1px solid var(--line); }
  .gutter { display: none; }
  .col2 { height: auto; }
}

/* ---- data block (OWID-style: title · subtitle · content · source) ---- */
.block { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); padding: 14px 16px 12px; }
.block .bh { display: flex; align-items: baseline; gap: 10px; padding-bottom: 10px; }
/* collapsible: the chevron header folds the block to its title (bridge screens are small) */
.block .bh.clickable { cursor: pointer; user-select: none; }
.block .bh .chev { margin-left: auto; color: var(--muted); font-family: var(--mono); font-size: 13px; display: inline-block; transform: rotate(90deg); transition: transform 0.15s; }
.block.collapsed .bh .chev { transform: rotate(0deg); }
.block.collapsed > *:not(.bh) { display: none; }
.block.collapsed .bh { padding-bottom: 0; }
.block .bt { font-size: 14px; font-weight: 600; letter-spacing: -0.01em; }
.block .bsub { font-family: var(--mono); font-size: 11px; color: var(--muted); letter-spacing: 0.02em; }
.block .bsrc { margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--line); }
.block .note { font-size: 13px; color: var(--muted); }

.chart-wrap { width: 100%; height: 175px; }
.chart-wrap canvas { width: 100%; height: 100%; display: block; }

/* ---- forms (bottom-border field) — for the settings/vessels views ---- */
label.fld { display: block; }
label.fld .lab { display: block; font-family: var(--mono); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); margin-bottom: 7px; }
label.fld input, label.fld select {
  width: 100%; font: inherit; font-size: 14px; padding: 8px 2px;
  background: transparent; color: var(--ink); border: 0; border-bottom: 1.5px solid var(--line-2);
}
label.fld input:focus, label.fld select:focus { outline: none; border-bottom-color: var(--accent); }

.btn { font: inherit; font-size: 14px; font-weight: 600; cursor: pointer; padding: 9px 16px; border-radius: var(--r); border: 1px solid var(--line-2); background: var(--surface); color: var(--ink); }
.btn.primary { background: var(--navy); color: #fff; border-color: var(--navy); }
.btn.primary:hover { background: #0f1a23; }
.btn:disabled { opacity: 0.5; cursor: default; }

/* ---- header extras ---- */
.coordbox .disputed { color: var(--crit); font-weight: 600; }
.site-chip { display: inline-block; padding: 1px 7px; border: 1px solid var(--line-2); border-radius: 2px; letter-spacing: 0.04em; }
.site-chip.land { color: var(--warn); border-color: var(--warn); }
.site-chip.near { color: var(--accent); border-color: var(--accent); }
.site-chip.water { color: var(--ink-2); }
.landnote { border-left: 3px solid var(--warn); }
.landnote .note { color: var(--ink-2); font-size: 13.5px; }
.loginlink { font-size: 13px; font-weight: 500; color: var(--ink-2); text-decoration: none; }
.loginlink:hover { color: var(--ink); }

/* ---- interior pages (vessels, login) ---- */
.page { flex: 1; width: 100%; max-width: 64rem; margin: 0 auto; padding: 22px 20px 60px; display: flex; flex-direction: column; gap: 16px; overflow-y: auto; }
.eyebrow { font-family: var(--mono); font-size: 11px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--accent); }
.login-wrap { flex: 1; display: grid; place-items: center; padding: 40px 20px; }
.login-card { width: 100%; max-width: 340px; }
.field-err { font-family: var(--mono); font-size: 11.5px; color: var(--crit); margin-top: 8px; }

/* ---- vessels page ---- */
.lim-table { width: 100%; border-collapse: collapse; font-size: 13px; margin-top: 6px; }
.lim-table th { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); text-align: left; font-weight: 500; padding: 6px 8px 6px 0; border-bottom: 1px solid var(--line); }
.lim-table td { padding: 7px 8px 7px 0; border-bottom: 1px solid var(--line); vertical-align: middle; }
.lim-table input[type="number"] { font: inherit; font-size: 13px; padding: 4px 6px; border: 0; border-bottom: 1.5px solid var(--line-2); background: transparent; color: var(--ink); }
.lim-table input[type="number"]:focus { outline: none; border-bottom-color: var(--accent); }
.lim-table .btn { padding: 4px 10px; font-size: 12px; }
.vrow-dim { opacity: 0.45; }
.chip { display: inline-block; font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.03em; padding: 2px 8px; border: 1px solid var(--line-2); border-radius: 2px; white-space: nowrap; }
.chip.warn { color: var(--warn); border-color: var(--warn); }
.chip.ok { color: var(--good); border-color: var(--good); }
.catalog-form { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; padding: 8px 0; }
.catalog-form input[type="text"], .catalog-form select { font: inherit; font-size: 13px; padding: 6px 8px; border: 1px solid var(--line-2); border-radius: 2px; background: var(--surface); color: var(--ink); }

/* header action buttons (Edit / Delete on a vessel) */
.bh-actions { margin-left: auto; display: flex; gap: 8px; }
.bh-actions .btn { padding: 4px 12px; font-size: 12px; }
.vessel-gear { margin-bottom: 4px; }

/* vessel particulars form — a responsive field grid shared by add + edit */
.vessel-edit { margin: 4px 0 2px; }
.vform { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 14px 16px; align-items: end; }
.vform .vform-wide { grid-column: 1 / -1; }
.vform label.fld input, .vform label.fld textarea { font: inherit; font-size: 13px; width: 100%; padding: 6px 2px; border: 0; border-bottom: 1.5px solid var(--line-2); background: transparent; color: var(--ink); resize: vertical; }
.vform label.fld input:focus, .vform label.fld textarea:focus { outline: none; border-bottom-color: var(--accent); }
.vform-checks { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 8px 20px; }
.vform-check { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; cursor: pointer; }
.vform-check input { margin: 0; }
.vform-actions { display: flex; gap: 10px; margin-top: 14px; }

/* ---- workability / briefing panels ---- */
.fieldrow { display: flex; gap: 10px; padding: 2px 0 8px; }
.sel { font: inherit; font-size: 13px; padding: 5px 8px; border: 1px solid var(--line-2); border-radius: 2px; background: var(--surface); color: var(--ink); max-width: 100%; flex: 1; min-width: 0; }
/* The workability ribbon itself is a canvas in the track stack (chart/ribbon.ts),
   drawn on the shared axis; it has no styles here. Its day labels went with it:
   the stack's ruler already draws them once for every track. Only the colour key
   is still CSS, and it is shared with the sea-character legend. */
.ribbon-key { display: flex; gap: 12px; flex-wrap: wrap; font-family: var(--mono); font-size: 10.5px; color: var(--ink-2); margin-top: 8px; }
.ribbon-key i { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 5px; vertical-align: -1px; }
.windows { display: flex; gap: 6px; flex-wrap: wrap; min-height: 22px; }
.warnrow { font-family: var(--mono); font-size: 11px; color: var(--warn); margin-top: 9px; letter-spacing: 0.02em; }
/* Fleet roster: one row per hull, click to arm. The armed row carries the same
   accent stripe the map-fed track carries — one visual grammar for "this is the
   one everything is keyed to". */
.fleetlist { display: flex; flex-direction: column; gap: 4px; margin: 8px 0; }
.fleetrow {
  display: grid; grid-template-columns: 1fr auto; gap: 1px 8px; align-items: baseline;
  text-align: left; padding: 7px 9px; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface); cursor: pointer; font: inherit; color: var(--ink);
}
.fleetrow:hover { background: var(--surface-2); }
.fleetrow.armed { box-shadow: inset 3px 0 0 var(--accent); background: var(--surface-2); }
.fleetname { font-weight: 600; font-size: 13px; }
.fleetspecs { grid-row: 2; font-family: var(--mono); font-size: 10.5px; color: var(--muted); }
.fleetchip { grid-row: 1 / span 2; align-self: center; font-family: var(--mono); font-size: 10px; padding: 2px 6px; border-radius: 3px; }
.fleetchip.warn { color: var(--warn); border: 1px solid var(--warn); }
.fleetchip.ok { color: var(--good); border: 1px solid var(--good); }

/* Systems board: one row per upstream. Dot = derived status; the detail line is
   the reason, already worded. */
.syslist { display: flex; flex-direction: column; gap: 2px; margin-top: 6px; }
.sysrow {
  display: grid; grid-template-columns: 12px 1fr; gap: 0 8px; align-items: baseline;
  padding: 6px 4px; border-top: 1px solid var(--line);
}
.sysdot { width: 9px; height: 9px; border-radius: 50%; align-self: center; }
.sysdot.ok { background: var(--good); }
.sysdot.warn { background: var(--warn); }
.sysdot.down { background: var(--crit); }
.sysdot.idle { background: transparent; border: 1px solid var(--muted); }
.sysname { font-weight: 600; font-size: 12.5px; }
.sysdetail { grid-column: 2; font-family: var(--mono); font-size: 10.5px; color: var(--ink-2); }
.sysfeeds { grid-column: 2; font-family: var(--mono); font-size: 10px; color: var(--muted); }

/* Tropical workspace rows: one line per NHC system/outlook, dot = severity. */
.troprow { font-size: 12.5px; margin: 6px 0; }
.tropdot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; vertical-align: -1px; }
.tropdot.crit { background: var(--crit); }
.tropdot.warn { background: var(--warn); }
/* One-line hazard flag inside a track header. Never wraps: the header column is
   150 px, and prose allowed to wrap there stacks into a tall skinny block that
   breaks the track rhythm — the full sentence lives in the title attribute and
   the chart tooltip instead. */
.trackflag {
  font-family: var(--mono); font-size: 10px; color: var(--warn);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  min-width: 0; cursor: help;
}
.sliders { display: flex; gap: 18px; flex-wrap: wrap; padding-bottom: 8px; }
.sliders .sld { display: flex; align-items: center; gap: 8px; font-family: var(--mono); font-size: 11.5px; color: var(--ink-2); flex: 1; min-width: 180px; }
.sliders input[type="range"] { flex: 1; accent-color: var(--navy); min-width: 80px; }
.sliders output { min-width: 44px; color: var(--ink); }
.brief { font-size: 14px; line-height: 1.6; }
.brief p { margin: 8px 0 0; color: var(--ink-2); }
.brief .lede { font-weight: 600; color: var(--ink); }
.brief .verdict { display: inline-block; color: #fff; font-size: 11.5px; font-weight: 600; padding: 1px 8px; border-radius: 2px; margin-left: 8px; vertical-align: 1px; }
.brief .b-note { font-family: var(--mono); font-size: 11px; color: var(--muted); }
.brief .quiet { color: var(--muted); font-size: 12px; }

/* ---- public site ---- */
.bare { position: relative; min-height: 100vh; display: flex; flex-direction: column; overflow: hidden; }
.contour-bg { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0.45; pointer-events: none; }
.bare-top { position: relative; display: flex; justify-content: flex-end; padding: 26px 40px 0; }
.bare-menu { display: flex; gap: 28px; font-family: var(--mono); font-size: 12.5px; letter-spacing: 0.05em; }
.bare-menu a { color: var(--muted); text-decoration: none; }
.bare-menu a:hover { color: var(--ink); }
.bare-menu a.login { color: var(--ink-2); }
.bare-center { position: relative; flex: 1; display: grid; place-items: center; padding: 40px 40px 16vh; }

.waves-bg { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.lockup { position: relative; display: flex; flex-direction: column; align-items: center; }
.lockup-mark { width: clamp(130px, 17vw, 200px); height: auto; display: block; }
.lockup-word { margin-top: 30px; font-size: clamp(28px, 4.6vw, 44px); font-weight: 300; letter-spacing: 0.3em; text-indent: 0.3em; color: var(--navy); }
.lockup-tag { margin-top: 12px; font-family: var(--mono); font-size: 11.5px; letter-spacing: 0.3em; text-indent: 0.3em; text-transform: uppercase; color: var(--muted); }

.site-page { max-width: 1040px; margin: 0 auto; padding: 0 40px; width: 100%; }
.pubnav { display: flex; align-items: center; padding: 26px 0; gap: 40px; border-bottom: 1px solid var(--line); }
.pubnav .brand { text-decoration: none; color: var(--ink); }
.pubnav .brand .mark { color: var(--navy); }
.pubnav-links { margin-left: auto; display: flex; align-items: center; gap: 30px; font-size: 14.5px; }
.pubnav-links a { color: var(--ink-2); text-decoration: none; }
.pubnav-links a:hover { color: var(--ink); }
.pubnav-links a.loginlink { color: var(--ink); font-weight: 500; }

.lead { padding: 56px 0 26px; max-width: 62ch; }
.lead h2 { font-family: var(--serif); font-size: clamp(28px, 4.4vw, 42px); font-weight: 400; letter-spacing: -0.015em; line-height: 1.12; margin: 14px 0 0; text-wrap: balance; }
.lead p { color: var(--ink-2); font-size: 17.5px; margin: 20px 0 0; line-height: 1.62; }

.doc { display: grid; grid-template-columns: 188px 1fr; gap: 56px; padding: 26px 0 80px; border-top: 1px solid var(--line); }
.doc aside { position: sticky; top: 24px; align-self: start; font-family: var(--mono); font-size: 12px; display: flex; flex-direction: column; }
.doc aside a { color: var(--muted); padding: 6px 0 6px 12px; letter-spacing: 0.03em; border-left: 1px solid var(--line-2); text-decoration: none; }
.doc aside a:hover { color: var(--ink); border-color: var(--accent); }
.doc article { max-width: 64ch; }
.doc article h3 { font-size: 13px; font-family: var(--mono); font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--ink); margin: 42px 0 0; padding-bottom: 8px; border-bottom: 1px solid var(--line); }
.doc article h3:first-child { margin-top: 0; }
.doc article p { color: var(--ink-2); margin: 14px 0 0; font-size: 15.5px; line-height: 1.7; }
.doc article .callout { border-left: 2px solid var(--navy); padding: 2px 0 2px 18px; margin-top: 22px; color: var(--ink); font-size: 15.5px; }

.sitefoot { border-top: 1px solid var(--line); padding: 24px 0 56px; display: flex; gap: 18px; flex-wrap: wrap; color: var(--muted); font-family: var(--mono); font-size: 11.5px; letter-spacing: 0.03em; }
.sitefoot .right { margin-left: auto; }

@media (max-width: 760px) {
  .doc { grid-template-columns: 1fr; gap: 22px; }
  .doc aside { position: static; flex-direction: row; flex-wrap: wrap; gap: 4px 16px; }
  .doc aside a { border-left: 0; padding-left: 0; }
  .site-page, .bare-top { padding-left: 22px; padding-right: 22px; }
}

/* ---- map overlay chrome (layer control + tropical badge) ---- */
.trop-badge { position: absolute; top: 10px; right: 10px; z-index: 5; background: var(--surface); border: 1px solid var(--line); border-radius: 2px; padding: 4px 10px; font-family: var(--mono); font-size: 11px; color: var(--ink-2); pointer-events: none; letter-spacing: 0.02em; }
.layerctl { position: absolute; left: 10px; bottom: 24px; z-index: 6; background: var(--surface); border: 1px solid var(--line); border-radius: 2px; font-size: 12px; color: var(--ink-2); max-width: 250px; overflow: hidden; box-shadow: 0 1px 4px rgba(22,35,46,0.10); }
.layerctl > summary { cursor: pointer; padding: 6px 11px; font-weight: 600; list-style: none; user-select: none; display: flex; align-items: center; gap: 6px; }
.layerctl > summary::-webkit-details-marker { display: none; }
.layerctl > summary::before { content: "▸"; font-size: 10px; color: var(--muted); }
.layerctl[open] > summary::before { content: "▾"; }
.layerctl .lc-body { padding: 4px 11px 9px; display: flex; flex-direction: column; gap: 7px; border-top: 1px solid var(--line); }
.layerctl label { display: flex; gap: 7px; align-items: flex-start; cursor: pointer; line-height: 1.25; }
.layerctl label small { display: block; color: var(--muted); font-size: 10.5px; }
.layerctl input { margin: 2px 0 0; flex: none; }
.layerctl .lc-swatch { width: 10px; height: 10px; border-radius: 2px; margin: 2px 0 0; flex: none; }
.layerctl .lc-range { border-top: 1px solid var(--line); padding-top: 8px; margin-top: 1px; }
.layerctl .lc-swatch-range { background: linear-gradient(90deg, #12405f, #2f6f9f 55%, #8fb8d4); }
.layerctl .lc-range-legend { display: none; flex-direction: column; gap: 5px; padding: 1px 0 0 17px; }
.layerctl .lc-rl-row { display: flex; gap: 7px; align-items: center; font-size: 11px; color: var(--ink-2); }
.layerctl .lc-rl-row .lc-swatch { margin: 0; }

/* full port-record popup */
.portcard .maplibregl-popup-content { font-family: var(--sans); font-size: 12.5px; line-height: 1.5; color: var(--ink-2); padding: 12px 14px; max-height: 320px; overflow-y: auto; }
.portcard .port-head { font-size: 14px; color: var(--ink); }
.portcard .port-sec { font-family: var(--mono); font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); margin-top: 8px; border-bottom: 1px solid var(--line); padding-bottom: 2px; }
.portcard .port-fit { font-family: var(--mono); font-size: 11px; margin-top: 6px; }
.portcard .port-fit.ok { color: var(--good); }
.portcard .port-fit.bad { color: var(--crit); }
.portcard .quiet { color: var(--muted); font-size: 11px; }

/* --- Help surface: inline ⓘ affordance, popover, deep-link, doc pages --------- */
/* All token-driven, so it re-casts across the Day/Dusk/Night ECDIS palettes. */

/* the ⓘ glyph: a small circled italic i beside a title/label/legend row */
.help-i { display: inline-grid; place-items: center; width: 14px; height: 14px; margin-left: 5px; padding: 0; border: 1px solid var(--line-2); border-radius: 50%; background: transparent; color: var(--muted); font-family: var(--serif); font-style: italic; font-size: 10px; line-height: 1; cursor: help; vertical-align: middle; flex: none; }
.help-i:hover, .help-i:focus-visible, .help-i[aria-expanded="true"] { color: var(--accent); border-color: var(--accent); }

/* the single reused popover (fixed, rides above the map canvas) */
.help-pop { position: fixed; z-index: 60; max-width: 300px; background: var(--surface); border: 1px solid var(--line-2); border-radius: 4px; box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18); padding: 11px 13px; font-family: var(--sans); }
.help-pop[hidden] { display: none; }
.help-pop-term { margin: 0; font-size: 12.5px; font-weight: 600; color: var(--ink); }
.help-pop-short { margin: 5px 0 0; font-size: 12.5px; line-height: 1.45; color: var(--ink-2); }
.help-pop-meta { margin: 6px 0 0; font-family: var(--mono); font-size: 10.5px; color: var(--muted); }
.help-pop-limit { margin: 8px 0 0; padding-left: 8px; border-left: 2px solid var(--warn); font-size: 11.5px; line-height: 1.4; color: var(--ink-2); }
.help-pop-more { display: inline-block; margin-top: 9px; font-size: 11.5px; color: var(--accent); text-decoration: none; }
.help-pop-more:hover { text-decoration: underline; }

/* inline text deep-link into the Methods page */
.help-link { color: var(--accent); text-decoration: none; border-bottom: 1px dotted var(--accent); }
.help-link:hover { border-bottom-style: solid; }

/* Link out to a buoy's own NDBC station page — sits to the right in the panel header. */
.ndbc-link { margin-left: auto; font-family: var(--mono); font-size: 11px; color: var(--accent); text-decoration: none; border-bottom: 1px dotted var(--accent); white-space: nowrap; }
.ndbc-link:hover { border-bottom-style: solid; }

/* Wind-field control + hourly scrubber — floats over the map, bottom-centre. */
.windctl { position: absolute; left: 50%; bottom: 16px; transform: translateX(-50%); z-index: 5;
  display: flex; flex-direction: column; align-items: center; gap: 6px; max-width: 90%;
  background: var(--surface); border: 1px solid var(--line); border-radius: 8px; padding: 8px 12px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.18); }
.windctl-toggles { display: flex; align-items: center; gap: 12px; }
.windctl-title { font-family: var(--mono); font-size: 11px; letter-spacing: 0.03em; text-transform: uppercase; color: var(--ink-2); }
.windctl-toggle { display: flex; align-items: center; gap: 6px; cursor: pointer; user-select: none;
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.03em; text-transform: uppercase; color: var(--ink); }
.windctl-legend { display: flex; align-items: center; gap: 8px; font-family: var(--mono); font-size: 10.5px; color: var(--muted); }
.windctl-ramp { width: 150px; height: 9px; border-radius: 2px; border: 1px solid var(--line);
  background: linear-gradient(90deg, rgb(26,120,60) 0%, rgb(46,160,90) 34%, rgb(166,217,106) 55%,
    rgb(253,206,80) 75%, rgb(244,140,60) 88%, rgb(214,47,39) 100%); }
.windctl-leg-hi { color: var(--crit); }
/* The active layer's name, large, on the map itself — the colours mean nothing
   without knowing which quantity they encode, and a checkbox in a side panel is
   not an answer at a glance. Display-only; the layer list stays the one control. */
.windctl-layerbig {
  font-family: var(--mono); font-size: 17px; font-weight: 700;
  letter-spacing: 0.14em; color: var(--ink); line-height: 1.1;
  cursor: help;
}
.windctl-layerbig:empty { display: none; }
/* The map's provenance line: model + cycle, muted — a source citation, not a headline. */
.windctl-src { font-family: var(--mono); font-size: 10px; color: var(--muted); letter-spacing: 0.02em; cursor: help; }
.windctl-src:empty { display: none; }
.windctl-bar { display: flex; align-items: center; gap: 6px; }
.windctl-btn { border: 1px solid var(--line); background: var(--surface-2); color: var(--ink);
  border-radius: 5px; padding: 2px 9px; font-size: 12px; line-height: 1.4; cursor: pointer; }
.windctl-btn:hover, .windctl-btn.windctl-playing { background: var(--accent); color: #fff; border-color: var(--accent); }
.windctl-fwd { transform: none; }
.windctl-now { font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em; }
.windctl-time { font-family: var(--mono); font-size: 12px; min-width: 172px; text-align: center; color: var(--ink); }
.windctl-status { font-size: 11px; color: var(--muted); }
.windctl-status:empty { display: none; }

/* persistent header disclaimer — the ethical anchor, always one glance away */
.apphead .disclaimer { font-family: var(--mono); font-size: 10px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); text-decoration: none; border: 1px solid var(--line); border-radius: 3px; padding: 2px 6px; white-space: nowrap; }
.apphead .disclaimer:hover { color: var(--warn); border-color: var(--warn); }

/* Guide / Methods reference pages — scoped under .helppage so the single-column
   reading layout doesn't collide with the site pages' own two-column .doc grid. */
.helppage .doc { display: block; max-width: 760px; margin: 0 auto; padding: 20px 24px 96px; font-family: var(--sans); color: var(--ink-2); line-height: 1.6; }
.helppage .doc h1 { font-size: 26px; color: var(--ink); margin: 0 0 6px; letter-spacing: -0.01em; }
.helppage .doc-lede { font-size: 15px; color: var(--ink-2); margin: 0 0 18px; }
.helppage .doc-lede h2 { border: 0; padding: 0; margin: 0 0 8px; font-size: 25px; letter-spacing: -0.01em; }
.helppage .doc h2 { font-size: 18px; color: var(--ink); margin: 36px 0 10px; padding-bottom: 6px; border-bottom: 1px solid var(--line); scroll-margin-top: 64px; }
.helppage .doc h3 { font-size: 14.5px; color: var(--ink); margin: 22px 0 6px; scroll-margin-top: 64px; }
.helppage .doc p { margin: 8px 0; font-size: 13.5px; }
.helppage .doc a { color: var(--accent); }
.helppage .doc-meta { font-family: var(--mono); font-size: 11px; color: var(--muted); margin: 2px 0 6px; }
.helppage .doc-limit { border-left: 3px solid var(--warn); background: var(--surface-2); border-radius: 0 3px 3px 0; padding: 8px 12px; margin: 10px 0; font-size: 13px; color: var(--ink-2); }
.helppage .doc-warn { border: 1px solid var(--warn); background: var(--surface-2); border-radius: 4px; padding: 12px 14px; margin: 14px 0 8px; font-size: 13.5px; color: var(--ink); }
.helppage .doc-warn strong { color: var(--warn); }
.helppage .doc-toc { margin: 8px 0 26px; padding: 12px 14px; border: 1px solid var(--line); border-radius: 4px; background: var(--surface-2); display: flex; flex-wrap: wrap; gap: 6px 16px; }
.helppage .doc-toc a { font-size: 12.5px; color: var(--accent); text-decoration: none; }
.helppage .doc-toc a:hover { text-decoration: underline; }
.helppage .doc-seealso { font-size: 12px; color: var(--muted); margin: 8px 0 0; }
.helppage .doc dl { margin: 8px 0; }
.helppage .doc dt { margin-top: 12px; font-weight: 600; font-size: 13.5px; color: var(--ink); }
.helppage .doc dd { margin: 2px 0 0; font-size: 13px; }

/* forecast freshness stamp — how stale the currently-loaded view is */
.freshness { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.02em; color: var(--muted); padding: 1px 2px 7px; }
.freshness[hidden] { display: none; }

/* --- track stack --------------------------------------------------------
   The forecast charts, contiguous, under one shared time ruler. They are
   grouped so the ruler heads them all; the briefing, primer, validation and
   SVP panels sit BELOW the stack rather than between the charts, which is
   what lets a single x-axis serve every track. */
/* One column width for the whole stack. The ruler, the confidence strip and every
   track derive their plot origin from this single number — when the track header
   column was introduced, the ruler kept its own older inset and its ticks sat
   150px off the data they label. A shared axis that does not share the axis is
   worse than no shared axis, so there is now exactly one place to change it. */
.trackstack { --track-hdr: 150px; display: flex; flex-direction: column; }

.trackstack .rulerrow,
.trackstack .confrow {
  display: grid;
  grid-template-columns: var(--track-hdr) minmax(0, 1fr);
  align-items: stretch;
  padding: 0;
  border-left: 1px solid var(--line);
  border-right: 1px solid var(--line);
  background: var(--surface);
}
.trackstack .rulerrow {
  position: sticky; top: 0; z-index: 3;
  height: 30px;
  /* A DEFINITE row track. Left auto, the track sizes to the canvas's intrinsic
     (device-pixel) height, the canvas's own height:100% has nothing to resolve
     against, and the ruler spills 150px down over the first two charts. */
  grid-template-rows: 30px;
  border-top: 1px solid var(--line);
  border-radius: var(--r) var(--r) 0 0;
}
.trackstack .rulerrow canvas { grid-column: 2; display: block; width: 100%; height: 100%; min-height: 0; }
/* The header cell of the ruler and the confidence strip: same width as a track's,
   so the three left edges are one line. */
.trackstack .stubnm {
  grid-column: 1;
  display: flex; align-items: center;
  padding: 0 10px;
  border-right: 1px solid var(--line);
  font-family: var(--mono); font-size: 9.5px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--muted);
}

/* Tracks butt against each other so the playhead reads as one line down the
   stack rather than as a crosshair repeated in several separate cards. */
.trackstack > .block { margin-bottom: 0; border-radius: 0; border-top: 0; }
.trackstack > .block:last-child { border-radius: 0 0 var(--r) var(--r); }

/* --- tracks -------------------------------------------------------------
   A track is a header column plus a plot, not a card. Reading it is "what
   shape, and is it over the line"; the exact number lives in the header, which
   is why a track can be a third the height of a full chart block. */
.block.track {
  display: grid;
  /* Same variable as .rulerrow/.confrow — the header column's width is the tracks'
     left edge, and three literals that happen to agree today are three chances to
     disagree tomorrow. The fallback only matters for a track outside a stack. */
  grid-template-columns: var(--track-hdr, 150px) minmax(0, 1fr);
  align-items: stretch;
  padding: 0;
  gap: 0;
}
.block.track .trackhdr {
  padding: 6px 10px;
  border-right: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1px;
  min-width: 0;
}
.block.track .bh { padding-bottom: 0; }
.block.track .bt {
  font-size: 12px; font-weight: 500; color: var(--ink-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* The meter: the value under the playhead. Tabular figures so it does not
   jitter as the playhead sweeps. */
.block.track .tval {
  font-family: var(--mono); font-size: 15px; font-weight: 600;
  font-variant-numeric: tabular-nums; letter-spacing: -0.01em;
}
.block.track .tval.over { color: var(--crit); }
/* Workability's meter runs the other way round: it is alarming when it is LOW, so
   it carries a band class rather than .over. Slightly smaller because it says a
   percentage AND a word ("38% · Poor") in the same 150 px column. */
.block.track .tval.wk-good { color: var(--good); }
.block.track .tval.wk-warn { color: var(--warn); }
.block.track .tval.wk-serious { color: var(--serious); }
.block.track .tval.wk-crit { color: var(--crit); }
.block.track.ribbontrack .tval { font-size: 13px; }
/* The ribbon is a band, not a plot: it needs the height of its cells and nothing
   more. The canvas insets the band itself (see chart/ribbon.ts). */
.block.track.ribbontrack .chart-wrap { height: 44px; }
/* The colour key rides in the footer where a track's source line would be — it is
   the same kind of thing, a note about how to read what is above it. */
.block.track .bsrc .ribbon-key { margin-top: 0; }
.block.track .bsub { font-size: 10px; }
.block.track .chart-wrap { height: 76px; }
/* Tracks are not all worth the same height, because they do not all carry the same
   amount of information. Wind speed and wave height each stack three or four models,
   an ensemble or envelope band, a limit line, no-go shading and now the map field's
   own trace -- at 76 px those lines land within a few pixels of each other on a calm
   day and read as one thick smudge, which is exactly when you most need to see that
   the models agree. Wave period, daylight, sea character and surface current carry
   one or two lines and say everything they have to say in 76.
   Applied via a modifier rather than to :nth-child, so reordering the stack cannot
   silently hand the extra height to the wrong track. */
.block.track.tall .chart-wrap { height: 118px; }
/* DAW-style channel stripe: the track whose quantity the MAP is currently
   painting. One accent edge, the way a mixer marks the armed channel — so the
   glance that connects "those colours on the water" to "this row of numbers"
   costs nothing. Accent, not a status colour: this is identity, not judgement. */
.block.track.mapfed { box-shadow: inset 3px 0 0 var(--accent); }
.block.track.mapfed .trackhdr { border-left: 0; }
/* The source line spans both columns, muted -- provenance matters but should not
   compete with the trace for attention. */
.block.track .bsrc {
  grid-column: 1 / -1;
  margin-top: 0; padding: 5px 10px 6px;
  font-size: 10px; line-height: 1.4;
}
.block.track.collapsed .chart-wrap,
.block.track.collapsed .bsrc { display: none; }

/* --- workspace rail + panel (ui/rail.ts) ---------------------------------------
   The rail is space and mode; the transport bar is time. Icon AND label at 74 px:
   this is read on a moving vessel by a weekly-at-best user, where an unlabelled
   glyph is a guess. Every control here clears a 30 px hit target for the same
   reason. The panel is exclusive — one workspace visible — while layer toggles,
   which are additive, live inside the Observations workspace rather than in the
   rail, so a rail click never means two different things. */
.rail {
  width: var(--rail-w, 74px); height: calc(100vh - 52px);
  background: var(--surface-2); border-right: 1px solid var(--line);
  display: flex; flex-direction: column; padding: 6px 0;
  /* overflow-x hidden deliberately: the flex column reports a scrollWidth a few
     px past its client box, which raises a phantom horizontal scrollbar and
     steals 15 px of the rail's height. */
  overflow: hidden auto;
}
.rail hr { border: 0; border-top: 1px solid var(--line); margin: 6px 10px; width: calc(100% - 20px); }
.rspring { flex: 1 1 auto; }
.rbtn {
  position: relative; width: 100%; min-height: 46px; background: none; border: 0;
  border-left: 2.5px solid transparent; cursor: pointer; color: var(--muted);
  font: inherit; font-family: var(--sans); padding: 8px 2px 7px;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
}
.rbtn svg { width: 21px; height: 21px; stroke: currentColor; fill: none; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.rbtn span { font-size: 10px; line-height: 1.15; text-align: center; }
.rbtn:hover { color: var(--ink); background: var(--surface); }
.rbtn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.rbtn[aria-pressed="true"] { color: var(--accent); border-left-color: var(--accent); background: var(--surface); }
/* Status, not decoration: a badge always carries a title explaining it. */
/* Anchored beside the icon, not in the button's corner: at 46 px row height a
   corner dot sits nearer the row above and reads as that row's badge. */
.rbadge { position: absolute; top: 9px; right: 19px; width: 8px; height: 8px; border-radius: 50%; border: 1.5px solid var(--surface-2); }
.rbadge.stop { background: var(--crit); }
.rbadge.warn { background: var(--warn); }

.wspanel {
  width: var(--wspanel-w, 300px); height: calc(100vh - 52px);
  border-right: 1px solid var(--line); background: var(--surface);
  display: flex; flex-direction: column; min-width: 0;
}
/* `display: flex` above would otherwise beat the UA's [hidden] rule. */
.wspanel[hidden] { display: none; }
.wshead {
  margin: 0; padding: 10px 14px 9px; border-bottom: 1px solid var(--line);
  background: var(--surface-2); color: var(--muted); font-weight: 500;
  font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.12em; text-transform: uppercase;
}
.wsbody { overflow-y: auto; flex: 1 1 auto; font-size: 13px; color: var(--ink-2); }

.rsec { border-bottom: 1px solid var(--line); }
.rsec > summary {
  cursor: pointer; list-style: none; padding: 9px 14px; min-height: 30px;
  display: flex; align-items: center; gap: 7px; user-select: none;
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted);
}
.rsec > summary::-webkit-details-marker { display: none; }
.rsec > summary::before { content: "▸"; font-size: 11px; }
.rsec[open] > summary::before { content: "▾"; }
.rsec > summary:hover { color: var(--ink); background: var(--surface-2); }
.rsec > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.rsbody { padding: 2px 14px 14px; }
.rsbody > p { margin: 0 0 0.7em; font-size: 12.5px; line-height: 1.55; }
.rsbody > p:last-child { margin-bottom: 0; }
/* Blocks moved out of the column drop their card chrome inside the panel: they
   already carry their own collapsible header, so wrapping them in a <details>
   section as well would give one panel two disclosure controls. The hairline
   between them does the separating instead. */
.wsbody > .block {
  background: none; border: 0; border-radius: 0;
  border-bottom: 1px solid var(--line); padding: 10px 14px 12px;
}
.wsbody > .block:last-child { border-bottom: 0; }
.wsbody > .block .bt { font-size: 12px; }
/* Narrower than the column these came from — let the controls wrap rather than
   overflow. */
.wsbody .fieldrow { flex-wrap: wrap; }
.wsbody .sel { max-width: 100%; }
.rsbody .block { background: none; border: 0; border-radius: 0; padding: 0; }

/* Narrow: the panel becomes a drawer over the map rather than squeezing it. */
@media (max-width: 1180px) {
  .appbody { grid-template-columns: var(--rail-w, 74px) 1fr 7px var(--col2-w, clamp(380px, 34vw, 560px)); }
  .wspanel { position: absolute; left: var(--rail-w, 74px); top: 0; z-index: 6; box-shadow: 0 2px 14px rgba(0, 0, 0, 0.18); }
}
@media (max-width: 880px) {
  .rail { width: auto; height: auto; flex-direction: row; overflow-x: auto; border-right: 0; border-bottom: 1px solid var(--line); }
  .rail hr { display: none; }
  .rspring { display: none; }
  .rbtn { width: auto; min-width: 62px; border-left: 0; border-bottom: 2.5px solid transparent; }
  .rbtn[aria-pressed="true"] { border-left-color: transparent; border-bottom-color: var(--accent); }
  .wspanel { position: static; width: auto; height: auto; max-height: 46vh; border-right: 0; border-bottom: 1px solid var(--line); box-shadow: none; }
}

/* Hosted layer surface: the overlay control, moved off the map into the
   Observations workspace. It loses the floating card chrome — the panel is the
   card now — and gains room, so the sub-labels stop being cramped. */
.layerctl-hosted {
  position: static; max-width: none; border: 0; border-radius: 0;
  border-bottom: 1px solid var(--line); box-shadow: none; background: none; font-size: 12.5px;
}
.layerctl-hosted > summary {
  padding: 9px 14px; min-height: 30px; font-weight: 500;
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted);
}
.layerctl-hosted > summary:hover { color: var(--ink); background: var(--surface-2); }
.layerctl-hosted .lc-body { padding: 4px 14px 12px; gap: 9px; }
/* The reach field's Wind/Waves toggles, hosted in that same list: they read as
   one more layer row rather than as the header of a separate control. */
.windctl-toggles-hosted { flex-wrap: wrap; gap: 6px 12px; padding-bottom: 8px; border-bottom: 1px solid var(--line); }
.windctl-toggles-hosted .windctl-title { flex-basis: 100%; font-size: 10px; letter-spacing: 0.1em; color: var(--muted); }

/* Zoom-ladder segmented control in the Theater workspace. Three rungs only —
   the ladder floors where GFS-Wave stops resolving, so there is nothing below
   "Operational" to offer. */
.rungseg { display: flex; border: 1px solid var(--line-2); border-radius: var(--r); overflow: hidden; margin: 2px 0 10px; }
.rungseg-btn {
  flex: 1 1 0; min-height: 30px; padding: 5px 8px; cursor: pointer;
  background: transparent; color: var(--ink-2); border: 0; border-left: 1px solid var(--line);
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.03em;
}
.rungseg-btn:first-child { border-left: 0; }
.rungseg-btn.on { background: var(--navy); color: var(--surface); }
.rungseg-btn:hover:not(.on) { color: var(--ink); background: var(--surface-2); }
.rungseg-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* Mono key/value facts inside a workspace panel. */
.wsbody .kv { display: grid; grid-template-columns: auto 1fr; gap: 3px 10px; margin: 0 0 10px; font-family: var(--mono); font-size: 11.5px; }
.wsbody .kv dt { color: var(--muted); }
.wsbody .kv dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.wscheck { display: flex; align-items: center; gap: 7px; min-height: 30px; font-size: 12.5px; cursor: pointer; }
/* One unbreakable token (the theater cache key) — give it the whole row. */
.wsbody .kv dt.kvwide { grid-column: 1 / -1; margin-top: 4px; }
.wsbody .kv dd.kvwide { grid-column: 1 / -1; text-align: left; font-size: 11px; color: var(--ink-2); }

/* Confidence strip under the shared ruler. It sits in the same grid column as the
   plots and carries the charts' own gutters, so a band's left edge sits over the
   hour it describes. Colour is muted on purpose: this is a caption for the axis,
   not another data series competing with it. */
.trackstack .confrow { grid-template-rows: 17px; border-bottom: 1px solid var(--line); }
.confbar {
  grid-column: 2; display: flex; height: 17px; align-items: stretch; font-family: var(--mono); font-size: 9px; letter-spacing: 0.04em; }
.confbar .cb {
  display: flex; align-items: center; justify-content: center; min-width: 0;
  overflow: hidden; white-space: nowrap; color: var(--surface);
  border-right: 1px solid var(--surface);
}
.confbar .cb:last-child { border-right: 0; }
.confbar .c0 { background: color-mix(in srgb, var(--obs) 55%, transparent); }
.confbar .c1 { background: color-mix(in srgb, var(--good) 78%, transparent); }
.confbar .c2 { background: color-mix(in srgb, var(--warn) 78%, transparent); }
/* Hatched, not a third solid colour: past 120 h the steps themselves are
   3-hourly, so the bar should read as "coarser data", not "worse weather". */
.confbar .c3 {
  color: var(--ink-2);
  background: repeating-linear-gradient(45deg, var(--line-2), var(--line-2) 4px, transparent 4px, transparent 8px);
}

/* Transport — the one place time is driven from. Heads the track stack, above
   the ruler, because it belongs to the whole timeline rather than to any track. */
.transport {
  display: flex; align-items: center; gap: 6px;
  padding: 5px 10px;
  border: 1px solid var(--line); border-bottom: 0;
  border-radius: var(--r) var(--r) 0 0;
  background: var(--surface-2);
}
.tp-btn {
  min-height: 26px; min-width: 28px; padding: 2px 8px; cursor: pointer;
  background: var(--surface); color: var(--ink-2);
  border: 1px solid var(--line-2); border-radius: var(--r);
  font-family: var(--mono); font-size: 11px; line-height: 1.2;
}
.tp-btn:hover:not(:disabled) { color: var(--ink); border-color: var(--accent); }
.tp-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.tp-btn:disabled { opacity: 0.4; cursor: default; }
.tp-btn.on { background: var(--navy); color: var(--surface); border-color: var(--navy); }
.tp-clear { margin-left: 0; }
.tp-time {
  flex: 1 1 auto; text-align: center;
  font-family: var(--mono); font-size: 11.5px; font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.tp-time.idle { color: var(--muted); }
/* The stack's first child now carries the rounded top, not the ruler. */
.trackstack .rulerrow { border-radius: 0; }
/* The field's valid-time line, when the field is muted for lead time. */
.windctl-time.windctl-faded { color: var(--muted); }
/* The field has run past the end of its grid — what is painted is its last step. */
.windctl-time.windctl-stale { color: var(--warn); }

/* Saved operating areas in the Theater workspace. */
.thsave { display: flex; gap: 6px; margin: 10px 0 8px; }
.thname {
  flex: 1 1 auto; min-width: 0; min-height: 30px; padding: 4px 8px;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--line-2); border-radius: var(--r);
  font-family: var(--mono); font-size: 11.5px;
}
.thname::placeholder { color: var(--muted); }
.thlist { display: flex; flex-direction: column; }
.throw { display: flex; align-items: stretch; gap: 4px; border-top: 1px solid var(--line); }
.throw:last-child { border-bottom: 1px solid var(--line); }
.throw.on { background: var(--surface-2); box-shadow: inset 2px 0 0 var(--accent); }
.thopen {
  flex: 1 1 auto; min-width: 0; min-height: 30px; padding: 5px 8px; cursor: pointer;
  background: none; border: 0; text-align: left; color: inherit; font: inherit;
  display: flex; flex-direction: column; gap: 1px;
}
.thopen:hover { background: var(--surface-2); }
.thopen:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.thnm { font-size: 12.5px; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.thsub { font-family: var(--mono); font-size: 9.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.thdel {
  flex: 0 0 auto; width: 30px; cursor: pointer; background: none; border: 0;
  color: var(--muted); font-size: 15px; line-height: 1;
}
.thdel:hover { color: var(--crit); }
.thdel:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
