/* Self-hosted type (AESTHETICS §2 "Warm Conversational"): Nunito for
   display/UI text, JetBrains Mono for small-caps kickers/labels/units.
   Both files are variable fonts (Google ships one static-latin-subset
   binary across the declared weight range; the browser's own variable-
   font axis resolution picks the right instance) — see vendor/fonts/. No
   external font request ever leaves the page (CSP: no external fonts). */
@font-face {
  font-family: "Nunito"; font-style: normal; font-weight: 400 800; font-display: swap;
  src: url("/vendor/fonts/nunito-latin-variable.woff2") format("woff2-variations"), url("/vendor/fonts/nunito-latin-variable.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono"; font-style: normal; font-weight: 400 600; font-display: swap;
  src: url("/vendor/fonts/jbm-latin-variable.woff2") format("woff2-variations"), url("/vendor/fonts/jbm-latin-variable.woff2") format("woff2");
}
:root {
  --font-display: "Nunito", -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  --bg: #f4f6fa;
  --panel: #ffffff;
  --card: #ffffff;
  --line: #d9dee6;
  --text: #1b2330;
  --muted: #5c6776;
  --accent: #0a7ea4;
  --accent-ink: #ffffff;
  --ok: #0f7a37;
  --warn: #b45309;
  --err: #d23035;
  --shadow: 0 6px 24px rgba(20, 30, 45, 0.14);
}
/* "Warm Conversational" dark base (DESIGN-LANGUAGE.md) — Windfield's own
   sibling-not-clone accent is a sky/airflow teal-cyan, with warm amber as
   the counterpoint (warn/attention), not terracotta. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0f1419;
    --panel: #1f2430;
    --card: #262c3a;
    --line: #2a3140;
    --text: #eef1f6;
    --muted: #9aa6b4;
    --accent: #3ad4c6;
    --accent-ink: #0b1117;
    --ok: #35c76a;
    --warn: #e8a33d;
    --err: #ef6166;
    --shadow: 0 10px 32px rgba(0, 0, 0, 0.55), 0 2px 8px rgba(232, 163, 61, 0.06);
  }
}
:root[data-theme="dark"] {
  --bg: #0f1419;
  --panel: #1f2430;
  --card: #262c3a;
  --line: #2a3140;
  --text: #eef1f6;
  --muted: #9aa6b4;
  --accent: #3ad4c6;
  --accent-ink: #0b1117;
  --ok: #35c76a;
  --warn: #e8a33d;
  --err: #ef6166;
  --shadow: 0 10px 32px rgba(0, 0, 0, 0.55), 0 2px 8px rgba(232, 163, 61, 0.06);
}
:root { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { color-scheme: dark; } }
/* Scrollbars follow the theme too (native controls + scrollbars, per the
   design mandate — never a light scrollbar on a dark page or vice versa). */
* { scrollbar-color: var(--muted) var(--panel); }
*::-webkit-scrollbar { width: 11px; height: 11px; }
*::-webkit-scrollbar-track { background: var(--panel); }
*::-webkit-scrollbar-thumb { background: var(--line); border-radius: 999px; border: 2px solid var(--panel); }
*::-webkit-scrollbar-thumb:hover { background: var(--muted); }
* { box-sizing: border-box; margin: 0; padding: 0; }
/* Theme-audit baseline (owner mandate): no native browser chrome on any
   control, anywhere — every button/input/select/textarea takes the theme
   tokens by default; component classes (.btn, .chip, .seg-opt, etc.) layer
   on top of this, never instead of it. */
button, input, select, textarea {
  font: inherit; color: inherit; background: var(--card); border: 1px solid var(--line);
  border-radius: 8px; padding: 6px 9px; accent-color: var(--accent);
}
button { cursor: pointer; }
button:disabled { cursor: not-allowed; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Root-cause fix for "empty styled box" artifacts (owner-reported stray
   round/rectangular blobs): several components set an unconditional
   `display` (e.g. .stats-card{display:grid}, .downloads{display:flex},
   .height-selector{display:flex}) which — at equal specificity — wins
   over the UA stylesheet's `[hidden]{display:none}`, so the `hidden`
   attribute stopped actually hiding them once they had their own display
   rule. This one rule, with !important, makes `hidden` always win,
   covering every current and future component without auditing each one
   by hand. */
[hidden] { display: none !important; }
html, body { height: 100%; overflow-x: hidden; }
body {
  font-family: var(--font-display);
  background: var(--bg);
  color: var(--text);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
/* Two faint radial glows (teal top-right, amber bottom-left) — dark theme
   only, per AESTHETICS §2.2. Decorative, behind all map/UI content. */
body.map-page { background-color: var(--bg); }
:root[data-theme="dark"] body.map-page { background-image: radial-gradient(ellipse 60% 50% at 100% 0%, rgba(58,212,198,0.07), transparent 60%), radial-gradient(ellipse 60% 50% at 0% 100%, rgba(232,163,61,0.06), transparent 60%); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) body.map-page { background-image: radial-gradient(ellipse 60% 50% at 100% 0%, rgba(58,212,198,0.07), transparent 60%), radial-gradient(ellipse 60% 50% at 0% 100%, rgba(232,163,61,0.06), transparent 60%); }
}
/* The map page needs the header (auto height — it can wrap to 2+ lines
   with the full nav) and the map to exactly fill the rest of 100vh, with
   no page scroll. CSS Grid's `1fr` row does this unconditionally (it is
   never ambiguous about "remaining space" the way flex-grow can be when
   the container's own size is set only via min-height); switching only
   body.map-page away from the base flex-column layout. Content pages
   (About/Privacy/404) keep body's natural min-height:100vh + flex, so
   long content still scrolls the document normally. */
body.map-page {
  height: 100vh; overflow: hidden;
  display: grid; grid-template-rows: auto 1fr; grid-template-columns: 1fr;
}
body.map-page > header { grid-row: 1; min-width: 0; }
body.map-page > main#view-map { grid-row: 2; min-height: 0; min-width: 0; }
a { color: var(--accent); }
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 2000;
  background: var(--accent); color: var(--accent-ink); padding: 8px 14px;
  border-radius: 0 0 6px 0; text-decoration: none; font-weight: 600;
}
.skip-link:focus { left: 0; }

header {
  padding: 12px 20px; border-bottom: 1px solid var(--line);
  background: var(--panel); display: flex; align-items: center;
  gap: 16px; flex-wrap: wrap;
}
.brand { display: flex; align-items: center; gap: 10px; text-decoration: none; color: var(--text); }
.brand img, .brand svg { width: 28px; height: 28px; flex: none; }
.brand h1 { font-size: 19px; letter-spacing: -0.3px; font-weight: 700; }
.tag { color: var(--muted); font-size: 12.5px; margin-top: 2px; }
.brand-col { display: flex; flex-direction: column; }

nav.views { display: flex; gap: 6px; margin-left: auto; align-items: center; flex-wrap: wrap; }
.view-btn, .view-link {
  background: var(--card); color: var(--muted); border: 1px solid var(--line);
  border-radius: 6px; padding: 6px 12px; font-size: 13.5px; cursor: pointer;
  text-decoration: none; display: inline-flex; align-items: center; gap: 6px;
  white-space: nowrap; font-family: inherit;
}
.view-btn:hover, .view-link:hover { color: var(--text); border-color: var(--accent); }
.view-btn.active { background: var(--accent); color: var(--accent-ink); font-weight: 600; border-color: var(--accent); }
.view-btn:focus-visible, .view-link:focus-visible, button:focus-visible, a:focus-visible, input:focus-visible, select:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
.theme-toggle { font-size: 16px; line-height: 1; padding: 6px 10px; }

main { flex: 1; min-height: 0; display: flex; flex-direction: column; }

/* ── static content pages (about / privacy / 404) ── */
.page { max-width: 820px; margin: 0 auto; padding: 32px 20px 60px; line-height: 1.6; }
.page h1 { font-size: 28px; margin-bottom: 6px; }
.page h2 { font-size: 20px; margin: 28px 0 10px; }
.page h3 { font-size: 16px; margin: 18px 0 6px; }
.page p { margin: 10px 0; color: var(--text); }
.page ul { margin: 10px 0 10px 22px; }
.page li { margin: 4px 0; }
.page .lede { color: var(--muted); font-size: 15px; margin-bottom: 18px; }
.page .callout {
  border: 1px solid var(--line); background: var(--card); border-radius: 10px;
  padding: 14px 16px; margin: 16px 0; font-size: 14px;
}
.page .callout.warn { border-color: var(--warn); }
.page table { width: 100%; border-collapse: collapse; margin: 12px 0; font-size: 14px; }
.page th, .page td { text-align: left; padding: 6px 10px; border-bottom: 1px solid var(--line); }

.not-found { display: grid; min-height: calc(100vh - 56px); place-items: center; }
.not-found .inner { max-width: 560px; padding: 32px; text-align: center; }

footer {
  padding: 10px 20px; border-top: 1px solid var(--line); background: var(--panel);
  color: var(--muted); font-size: 12.5px; text-align: center;
}
footer nav { display: inline; }
footer a { color: var(--muted); text-decoration: underline; }
footer a:hover { color: var(--accent); }

/* ── map view ── */
/* Column flex: #map and the permanent .mode-strip share the column's
   height; every other overlay here (.top-bar, .status-strip, .mode-strip's
   siblings aside) is position:absolute and does not take part in this flow. */
#view-map { position: relative; flex: 1; min-height: 0; display: flex; flex-direction: column; }
#map { flex: 1; min-height: 0; }
.leaflet-container { background: var(--bg); font-family: inherit; }
#view3d-root { position: absolute; inset: 0; z-index: 500; background: var(--bg); }

/* Side panel ("Model a wind field") — this positioning is a no-JS/
   winmgr-failed-to-load FALLBACK only. Once winmgr.js successfully wraps
   #panel it adds the `wn-win-body` class and winmgr.css's `.wn-win-body`
   (flex:1, overflow:auto, no position) governs sizing instead — guarded
   with :not(.wn-win-body) so the two never fight. Letting both apply at
   once was exactly the bug behind the "blank dark slab": #panel's id
   selector (0,1,0,0) outranks the class selector (0,0,1,0), so this
   fixed-pinned box always won and ignored the window's actual content
   height. */
#panel:not(.wn-win-body) {
  position: absolute; top: 12px; left: 12px; bottom: calc(12px + var(--mode-strip-h)); width: 340px;
  max-width: calc(100vw - 24px);
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: var(--shadow); display: flex; flex-direction: column;
  overflow: hidden; box-sizing: border-box;
  /* Leaflet's own corner controls (zoom/layers/attribution) use z-index:1000
     in leaflet.css; the panel must sit above them everywhere (incl. the
     mobile bottom sheet, which can span under the bottom-right attribution
     control) or those controls visually paint over it. */
  z-index: 1200;
}
#panel * { max-width: 100%; box-sizing: border-box; }
#panel .panel-head {
  padding: 12px 14px; border-bottom: 1px solid var(--line); display: flex;
  align-items: center; justify-content: space-between; gap: 8px;
}
#panel .panel-head h2 { font-size: 15px; font-weight: 700; }
#panel .panel-body { padding: 12px 14px; overflow-y: auto; flex: 1; }
#panel .panel-collapse { background: none; border: none; color: var(--muted); font-size: 18px; cursor: pointer; line-height: 1; }
#panel.collapsed .panel-body { display: none; }

.field { margin-bottom: 14px; }
.field label, .field .flabel { display: block; font-size: 12px; font-weight: 600; color: var(--muted); margin-bottom: 5px; text-transform: uppercase; letter-spacing: .03em; }
.field input[type="text"], .field input[type="number"], .field select, .field input[type="datetime-local"] {
  width: 100%; padding: 7px 9px; border: 1px solid var(--line); border-radius: 7px;
  background: var(--card); color: var(--text); font-size: 13.5px; font-family: inherit;
}
.row { display: flex; gap: 8px; }
.row > * { flex: 1; min-width: 0; }
.row3 > * { flex: 1; min-width: 0; }
.chipset { display: flex; gap: 6px; flex-wrap: wrap; }
.chip {
  border: 1px solid var(--line); background: var(--card); color: var(--muted);
  border-radius: 999px; padding: 5px 12px; font-size: 12.5px; cursor: pointer; font-family: inherit;
}
.chip.active { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); font-weight: 600; }

/* Segmented choice (owner redesign: wind source, "what to show") — a row
   (or stacked column) of equal-weight pill options, one active. */
.segmented { display: flex; gap: 6px; flex-wrap: wrap; }
.segmented-col { flex-direction: column; }
.seg-opt {
  flex: 1 1 0; min-width: 0; border: 1px solid var(--line); background: var(--card); color: var(--text);
  border-radius: 10px; padding: 8px 10px; font-size: 12.5px; font-family: inherit; text-align: left;
  cursor: pointer; overflow-wrap: anywhere;
}
.segmented:not(.segmented-col) .seg-opt { text-align: center; border-radius: 999px; }
.seg-opt .hint { display: block; font-size: 11px; color: var(--muted); margin-top: 2px; }
.seg-opt.active { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); font-weight: 600; }
.seg-opt:disabled { opacity: .45; cursor: not-allowed; }

.height-list { list-style: none; display: flex; flex-direction: column; gap: 6px; margin: 6px 0; }
.height-list li { display: flex; align-items: center; gap: 6px; }
.height-list input[type="number"] { width: 5.5em; }

/* Inline field errors (400 responses with a `field`) — shown next to the
   offending control in addition to the toast. */
.field-invalid { border-color: var(--err) !important; }
.field-error { color: var(--err); font-size: 12px; margin-top: 4px; overflow-wrap: anywhere; }

/* "Clear…" menu (owner safety redesign — replaces the one-click "Clear map"). */
.clear-menu {
  position: absolute; top: calc(100% + 4px); left: 0; z-index: 1300; min-width: 260px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 20px; box-shadow: var(--shadow);
  padding: 4px;
}
.clear-menu-item {
  display: block; width: 100%; text-align: left; padding: 8px 10px; border-radius: 7px;
  background: transparent; border: none; font-size: 13px; color: var(--text);
}
.clear-menu-item:hover { background: var(--card); }
.clear-menu-confirm {
  display: flex; align-items: center; gap: 8px; padding: 8px 10px; font-size: 12.5px; color: var(--text);
  overflow-wrap: anywhere;
}
.clear-menu-confirm .cm-text { flex: 1; min-width: 0; }
.clear-menu-confirm .btn-sm { flex: none; }
#settings-menu { left: auto; right: 0; }
#panel.drag-over { outline: 2px dashed var(--accent, #3AD4C6); outline-offset: -4px; }
#settings-save-inline input[type="text"] { min-width: 0; }
.toggle-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.switch { position: relative; width: 38px; height: 21px; flex: none; }
.switch input { opacity: 0; width: 0; height: 0; }
.switch .slider {
  position: absolute; inset: 0; background: var(--line); border-radius: 999px; cursor: pointer; transition: background .15s;
}
.switch .slider::before {
  content: ""; position: absolute; width: 15px; height: 15px; left: 3px; top: 3px;
  background: #fff; border-radius: 50%; transition: transform .15s;
}
.switch input:checked + .slider { background: var(--accent); }
.switch input:checked + .slider::before { transform: translateX(17px); }

.section-title { font-family: var(--font-mono); font-size: 11.5px; font-weight: 600; font-variant-caps: all-small-caps; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); margin: 16px 0 8px; }
.section-title:first-child { margin-top: 0; }
.hint { font-size: 11.5px; color: var(--muted); margin-top: 4px; line-height: 1.4; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  border: 1px solid var(--line); background: var(--card); color: var(--text);
  border-radius: 8px; padding: 8px 14px; font-size: 13.5px; cursor: pointer; font-family: inherit;
}
.btn:hover { border-color: var(--accent); }
.btn-primary { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); font-weight: 700; }
.btn-primary:hover { opacity: .92; }
.btn-primary:disabled { opacity: .5; cursor: not-allowed; }
.btn-block { width: 100%; }
.btn-ghost { background: transparent; }
.btn-sm { padding: 4px 9px; font-size: 12px; border-radius: 6px; }

/* domain readout */
.domain-readout {
  border: 1px solid var(--line); border-radius: 8px; padding: 10px; background: var(--card);
  font-size: 12.5px; display: grid; grid-template-columns: 1fr 1fr; gap: 4px 10px;
}
.domain-readout.over { border-color: var(--err); background: rgba(210,48,53,0.08); }
.domain-readout dt { color: var(--muted); }
.domain-readout dd { text-align: right; font-weight: 600; }
.domain-readout .over-msg { grid-column: 1 / -1; color: var(--err); font-weight: 600; margin-top: 2px; }

/* compass dial */
.compass-wrap { display: flex; align-items: center; gap: 12px; }
.compass {
  width: 76px; height: 76px; border-radius: 50%; border: 2px solid var(--line);
  background: var(--card); position: relative; flex: none; cursor: pointer; touch-action: none;
}
.compass .needle {
  position: absolute; left: 50%; top: 50%; width: 2.5px; height: 32px;
  background: var(--accent); transform-origin: bottom center; border-radius: 2px;
  transform: translate(-50%, -100%) rotate(0deg);
}
.compass .cN, .compass .cE, .compass .cS, .compass .cW {
  position: absolute; font-size: 9px; font-weight: 700; color: var(--muted);
}
.compass .cN { top: 3px; left: 50%; transform: translateX(-50%); }
.compass .cS { bottom: 3px; left: 50%; transform: translateX(-50%); }
.compass .cE { right: 4px; top: 50%; transform: translateY(-50%); }
.compass .cW { left: 4px; top: 50%; transform: translateY(-50%); }

/* status strip */
.status-strip {
  position: absolute; top: 12px; left: 50%; transform: translateX(-50%);
  background: var(--panel); border: 1px solid var(--line); border-radius: 20px;
  box-shadow: var(--shadow); padding: 10px 16px; z-index: 1100; min-width: 280px;
  display: none; font-size: 13px;
}
.status-strip.show { display: block; }
.status-strip .ss-row { display: flex; align-items: center; gap: 10px; }
.status-strip .ss-bar { flex: 1; height: 6px; border-radius: 4px; background: var(--line); overflow: hidden; }
.status-strip .ss-fill { height: 100%; background: var(--accent); width: 0%; transition: width .3s; }
.status-strip .ss-msg { color: var(--muted); margin-top: 4px; font-size: 12px; }
.status-strip .ss-pct { font-weight: 700; min-width: 2.6em; text-align: right; }

/* stats card */
.stats-card { border: 1px solid var(--line); border-radius: 20px; padding: 10px; background: var(--card); font-size: 12.5px; display: grid; grid-template-columns: 1fr 1fr; gap: 6px 10px; }
.stats-card dt { color: var(--muted); } .stats-card dd { text-align: right; font-weight: 600; }

.downloads { list-style: none; display: flex; flex-direction: column; gap: 4px; }
.downloads a { display: flex; justify-content: space-between; padding: 6px 8px; border-radius: 6px; border: 1px solid var(--line); background: var(--card); text-decoration: none; color: var(--text); font-size: 12.5px; }
.downloads a:hover { border-color: var(--accent); }
.downloads a .dl-size { color: var(--muted); }

.history-list { list-style: none; display: flex; flex-direction: column; gap: 6px; }
.history-item {
  display: flex; align-items: center; gap: 8px; border: 1px solid var(--line); border-radius: 16px;
  padding: 6px 8px; cursor: pointer; background: var(--card); font-size: 12px;
}
.history-item:hover { border-color: var(--accent); }
.history-chip { width: 16px; height: 16px; border-radius: 4px; flex: none; }
.history-meta { flex: 1; min-width: 0; overflow: hidden; }
.history-meta .h1 { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.history-meta .h2 { color: var(--muted); }

/* legend / layers controls (Leaflet corner controls) */
.wn-ctl {
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: var(--shadow); color: var(--text); font-size: 12.5px;
}
.wn-layerctl .lc-toggle {
  border: none; background: none; cursor: pointer; padding: 8px 10px; color: var(--text);
  display: flex; align-items: center; gap: 6px; font-family: inherit; font-size: 12.5px;
}
.wn-layerctl .lc-body { padding: 4px 12px 10px; min-width: 190px; }
.wn-layerctl .lc-body[hidden] { display: none; }
.lc-sec { font-size: 10.5px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); font-weight: 700; margin: 8px 0 4px; }
.lc-row { display: flex; align-items: center; gap: 7px; padding: 3px 0; cursor: pointer; }
.lc-row input { flex: none; }
.lc-row.radio-row { gap: 7px; }
.lc-opacity { display: flex; align-items: center; gap: 6px; padding: 2px 0 2px 22px; }
.lc-opacity input[type=range] { flex: 1; }

/* legend rows render inside the Layers panel (.lc-body), not a separate
   corner control — see app.js buildLayersPanel() / wind-layers.js legendRows(). */
.legend-row { display: flex; align-items: center; gap: 7px; padding: 2px 0; font-size: 12px; }
.legend-swatch { width: 18px; height: 10px; border-radius: 2px; flex: none; }

.wn-attrib { font-size: 10.5px; }

/* toasts */
#toasts { position: fixed; bottom: calc(16px + var(--mode-strip-h)); right: 16px; z-index: 3000; display: flex; flex-direction: column; gap: 8px; max-width: 320px; }
/* "New here?" prompt: a single dedicated, dismissable card — deliberately
   NOT part of the #toasts stack (bottom-right) so it can never visually
   stack with error/success toasts. Lives bottom-center; at most one ever
   exists (the JS guards against a second show). */
.get-started-card {
  position: fixed; left: 50%; bottom: calc(16px + var(--mode-strip-h)); transform: translateX(-50%);
  z-index: 2900; display: flex; align-items: center; gap: 10px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 20px; box-shadow: var(--shadow);
  padding: 10px 10px 10px 16px; font-size: 13px; color: var(--text); max-width: calc(100vw - 32px);
  animation: toast-in .18s ease-out;
}
.get-started-card .gs-text { flex: 1 1 auto; }
/* Mobile: winmgr's bottom sheet-tab strip (.wn-sheet-tabs, position:fixed,
   bottom:0, ~52px with its padding + safe-area inset) sits below the
   mode-strip and isn't accounted for in --mode-strip-h — without this the
   card overlapped it. Raise the card clear of both. */
@media (max-width: 639px) {
  .get-started-card, #toasts { bottom: calc(52px + env(safe-area-inset-bottom, 0px) + 8px + var(--mode-strip-h)); }
  .get-started-card { left: 8px; right: 8px; transform: none; max-width: none; }
}
#gs-dismiss-btn { font-size: 16px; line-height: 1; padding: 2px 8px; }
.toast {
  background: var(--panel); border: 1px solid var(--line); border-left: 4px solid var(--accent);
  border-radius: 16px; box-shadow: var(--shadow); padding: 10px 14px; font-size: 13px; color: var(--text);
  animation: toast-in .18s ease-out;
}
.toast.error { border-left-color: var(--err); }
.toast.success { border-left-color: var(--ok); }
@keyframes toast-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }

/* domain draw handles */
.domain-handle {
  width: 12px; height: 12px; border-radius: 3px; background: var(--accent); border: 2px solid #fff;
  box-shadow: 0 0 0 1px rgba(0,0,0,.3);
}
.domain-handle.edge { border-radius: 50%; }

/* result popup */
.probe-pop table { font-size: 12.5px; }
.probe-pop td { padding: 2px 6px; }
.probe-pop td.k { color: var(--muted); }

/* mobile: bottom sheet */
@media (max-width: 760px) {
  header { padding: 10px 14px; }
  /* Two rows max, so the map starts higher: row 1 is the plain nav links,
     horizontally scrollable rather than wrapping one-per-line; row 2 is
     forced onto its own line (flex-basis:100% breaks the flex row before
     it) and holds Light/Dark + Get started together. */
  nav.views { width: 100%; justify-content: flex-start; flex-wrap: wrap; overflow-x: visible; row-gap: 6px; }
  .nav-links {
    display: flex; gap: 6px; flex-wrap: nowrap; overflow-x: auto; width: 100%;
    -webkit-overflow-scrolling: touch; scrollbar-width: thin;
  }
  .nav-links .view-link, .nav-links .view-btn { flex: 0 0 auto; }
  nav.views .appearance-control { flex: 0 0 auto; }
  nav.views #get-started-btn { flex: 0 0 auto; margin-left: auto; }
  #panel:not(.wn-win-body) {
    left: 0; right: 0; bottom: 0; top: auto; width: 100vw; max-width: 100vw;
    /* Leave room so the sheet never covers the whole viewport and so its own
       content (not the page footer, which is hidden below) is the only
       thing below the header. */
    max-height: min(60vh, calc(100vh - 64px));
    border-radius: 14px 14px 0 0; border-left: none; border-right: none; border-bottom: none;
  }
  .status-strip { left: 10px; right: 10px; transform: none; top: 10px; }
  /* The footer duplicates info already in the attribution control and would
     otherwise sit under/behind the bottom sheet; drop it on the map page
     only (About/Privacy/404 keep theirs — this selector only matches the
     footer that follows main#view-map). */
  main#view-map ~ footer { display: none; }
  /* Two-column field rows get tight at phone width; collapse under 480px. */
}
@media (max-width: 480px) {
  .row, .field.row, .row3 { flex-direction: column; }
  .row > *, .row3 > * { width: 100%; }
}

.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* terrain profile panel */
.profile-panel {
  position: absolute; left: 12px; right: 12px; bottom: calc(12px + var(--mode-strip-h)); max-width: 760px; margin: 0 auto;
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px; box-shadow: var(--shadow);
  z-index: 970; padding: 10px 14px;
}
.profile-panel canvas { width: 100%; height: 120px; display: block; }
.profile-close { float: right; background: none; border: none; font-size: 16px; cursor: pointer; color: var(--muted); }

.measure-readout {
  background: var(--panel); border: 1px solid var(--line); border-radius: 8px; padding: 6px 10px; font-size: 12.5px; box-shadow: var(--shadow);
}

/* ════════════════ Phase 3 additions ════════════════ */

/* header: appearance segmented control + help/get-started buttons */
.appearance-control { display: flex; border: 1px solid var(--line); border-radius: 7px; overflow: hidden; }
.app-opt {
  display: inline-flex; align-items: center; gap: 5px; background: var(--card); color: var(--muted);
  border: none; padding: 6px 10px; font-size: 12.5px; cursor: pointer; font-family: inherit;
  border-right: 1px solid var(--line);
}
.app-opt:last-child { border-right: none; }
.app-opt.active { background: var(--accent); color: var(--accent-ink); font-weight: 600; }
.app-opt:hover:not(.active) { color: var(--text); }
@media (max-width: 640px) { .app-opt { padding: 6px 7px; font-size: 11.5px; } }

/* search bar (geosearch.js mounts the real widget; this is the slot + fallback input) */
.top-bar { position: absolute; top: 12px; left: 50%; transform: translateX(-50%); z-index: 960; width: min(420px, calc(100vw - 24px)); }
.top-bar input[type="search"] {
  width: 100%; padding: 9px 12px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--panel); color: var(--text); box-shadow: var(--shadow); font-size: 13.5px;
}
/* geosearch.js may leave its results/suggestions container in the DOM
   empty between searches — never show it (and its rounded shadow box) as
   a mystery blank row under the search field. */
.top-bar ul:empty, .top-bar div:empty, .top-bar span:empty { display: none !important; } /* never inputs/buttons: void elements are always :empty */
.wn-3d-toggle {
  position: absolute; top: 12px; right: 12px; z-index: 960;
  background: var(--card); color: var(--text); border: 1px solid var(--line);
}
.wn-3d-toggle.active { border-color: var(--accent); color: var(--accent); }
.wn-3d-toggle:disabled { opacity: .5; cursor: not-allowed; }

/* Permanent mode/selection strip — part of the map chrome, fixed at the
   bottom of the map area (owner decision: no floating chip/popup for this).
   #view-map's flex column reserves exactly this height for it so #map
   shrinks instead of anything overlapping; it sits directly above the
   Leaflet attribution line. */
:root { --mode-strip-h: 30px; }
.mode-strip {
  flex: 0 0 var(--mode-strip-h); height: var(--mode-strip-h); z-index: 960;
  background: var(--panel); border-top: 1px solid var(--line);
  display: flex; align-items: center; gap: 10px; padding: 0 10px;
  font-family: "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: var(--muted);
  user-select: none;
}
.mode-strip .ms-mode { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); }
.mode-strip .ms-sel { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: center; }
.mode-strip .ms-actions { flex: 0 0 auto; display: flex; gap: 6px; }
.mode-strip.mode-active { border-top-color: var(--accent); }
.mode-strip.edit-active { border-top-color: var(--warn); }
.mode-strip .btn-sm { text-transform: none; letter-spacing: normal; font-family: inherit; }

@media (max-width: 640px) {
  .mode-strip { font-size: 10px; gap: 6px; padding: 0 8px; }
  .mode-strip .ms-sel { text-align: left; }
  .top-bar { top: 52px; left: 8px; right: 8px; transform: none; width: auto; }
}
.btn-danger { background: var(--err); color: #fff; border-color: var(--err); }
.btn-danger:hover { opacity: .9; }

/* panel header actions (Clear map + collapse) */
.panel-head-actions { display: flex; align-items: center; gap: 6px; }

/* section numbering ("1 Pick an area" etc.) */
.section-num {
  display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px;
  border-radius: 50%; background: var(--accent); color: var(--accent-ink); font-size: 11px; font-weight: 700;
  margin-top: 16px;
}
.section-num + .section-title { display: inline-block; margin-top: 16px; margin-left: 6px; }

/* Explanations are hover/focus tooltips via native `title` (+ WN_TIP once
   loaded) only — no visible "(i)" icon anywhere (owner correction). */

/* start-mode cards */
.mode-cards { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px; margin-bottom: 4px; }
#solver-cards { grid-template-columns: 1fr 1fr; }
.mode-card:disabled, .mode-card.unavailable { opacity: .55; cursor: not-allowed; }
.mode-card .unavail-reason { color: var(--err); font-weight: 600; }
/* "Coloured dot + label + one-line description" selectable card
   (AESTHETICS §2.6): a small filled dot (the mode/solver's own accent
   colour — teal/mint/amber) sits beside the label, with its description
   underneath in muted text. */
.mode-card {
  display: flex; flex-direction: row; align-items: flex-start; gap: 8px; text-align: left;
  padding: 10px; border-radius: 20px;
  border: 1px solid var(--line); background: var(--card); color: var(--text); cursor: pointer; font-family: inherit;
}
.mode-dot {
  width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto; margin-top: 4px;
  background: var(--muted); box-shadow: 0 0 0 3px transparent; transition: box-shadow .15s;
}
.mode-dot-teal .mode-dot { background: var(--accent); }
.mode-dot-mint .mode-dot { background: var(--ok); }
.mode-dot-amber .mode-dot { background: var(--warn); }
.mode-card-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.mode-card strong { font-size: 13px; }
.mode-card span { font-size: 11px; color: var(--muted); }
.mode-card.active { border-color: var(--accent); background: rgba(10,126,164,0.08); }
.mode-card.active strong { color: var(--accent); }
.mode-card.active .mode-dot { box-shadow: 0 0 0 3px rgba(58,212,198,0.22); }
@media (max-width: 420px) { .mode-cards { grid-template-columns: 1fr; } }

.run-reason { color: var(--err); margin-top: 6px; }
.recreate-card {
  border: 1px solid var(--accent); background: rgba(10,126,164,0.08); border-radius: 10px;
  padding: 10px; margin-bottom: 10px; font-size: 12.5px;
}
.recreate-card p { margin-bottom: 8px; }

/* Layers panel: presets + drag-reorder rows */
.lc-presets { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 6px; }
.lc-layer-row { display: flex; align-items: center; gap: 6px; padding: 2px 0; cursor: grab; }
.lc-layer-row.dragging { opacity: .4; }
.lc-layer-row .lc-row { flex: 1; min-width: 0; padding: 0; }
.lc-drag { color: var(--muted); font-size: 11px; cursor: grab; flex: none; }
.lc-reason { flex: none; }

/* weather / forecast module slots (placeholders until web-modules lands) */
.weather-card-slot, .forecast-player-slot { min-height: 1px; }
.weather-card-slot:empty::before, .forecast-player-slot:empty::before {
  content: "Loading\2026"; color: var(--muted); font-size: 12px;
}

/* Help drawer */
.help-drawer {
  position: fixed; top: 0; right: 0; bottom: 0; width: min(420px, 100vw); z-index: 2500;
  background: var(--panel); box-shadow: var(--shadow); transform: translateX(100%); transition: transform .2s;
  overflow-y: auto; border-left: 1px solid var(--line);
}
.help-drawer.open { transform: translateX(0); }
.help-drawer-inner { padding: 20px; }
.help-drawer h2 { margin-bottom: 10px; }
.help-drawer h3 { margin: 16px 0 6px; font-size: 14px; }
.help-drawer p, .help-drawer li { font-size: 13.5px; line-height: 1.55; }
.help-drawer ul { margin: 0 0 0 18px; }
.help-drawer kbd {
  display: inline-block; padding: 1px 6px; border: 1px solid var(--line); border-radius: 5px;
  background: var(--card); font-family: monospace; font-size: 12px;
}

/* vertical height selector, right edge of the map */
.height-selector {
  position: absolute; right: 12px; top: 50%; transform: translateY(-50%); z-index: 950;
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px; box-shadow: var(--shadow);
  padding: 6px; display: flex; flex-direction: column; gap: 2px;
}
.height-selector .hs-label { font-size: 9.5px; text-transform: uppercase; color: var(--muted); text-align: center; padding: 2px 0 4px; }
.height-selector button {
  display: block; width: 100%; padding: 6px 10px; border: 1px solid transparent; border-radius: 6px;
  background: transparent; color: var(--muted); font-size: 12px; cursor: pointer; font-family: inherit; white-space: nowrap;
}
.height-selector button:hover { background: var(--card); color: var(--text); }
.height-selector button.active { background: var(--accent); color: var(--accent-ink); font-weight: 600; }
@media (max-width: 640px) { .height-selector { right: 6px; } .height-selector button { padding: 5px 7px; font-size: 11px; } }

.wn-particle-canvas, .wn-streamline-canvas { pointer-events: none; }

/* probe popup: multi-height table */
.probe-pop .probe-heights { margin-top: 6px; border-top: 1px solid var(--line); padding-top: 6px; }
.probe-pop .probe-heights table { width: 100%; }
.probe-pop .probe-heights th { color: var(--muted); font-weight: 600; text-align: left; padding: 1px 6px; }

/* Pan vs Select/Edit visual affordance for drag handles (opacity/pointer
   events are also toggled inline by each tool; this is the shared default). */
.domain-handle { opacity: .4; transition: opacity .15s; }
body.wn-edit-mode .domain-handle { opacity: 1; }

@media (prefers-reduced-motion: reduce) { * { animation-duration: 0.001ms !important; } }

/* Mode/selection row inside the Model window (replaces the bottom strip). */
.mode-strip.mode-strip-inline { position: static; height: auto; min-height: 0; flex: 0 0 auto; flex-wrap: wrap; gap: 6px 12px;
  padding: 6px 10px; margin: 0 0 8px; border: 1px solid var(--line); border-radius: 10px; background: var(--card); }
.mode-strip.mode-strip-inline:empty { display: none; }
