/* Brand typeface — self-hosted (applet-design rung 3, 2026-09-12).
   Overpass is bundled rather than pulled from Google Fonts: a built applet is
   routinely opened with no network (a client's file share, a laptop in a meeting
   room), and a CDN font fails there silently — the page just renders in whatever
   UI face the machine has. Bundling also makes the applet render identically on
   macOS, Windows and Linux for the first time, which is what lets a gate
   screenshot stand for what a client actually sees.
   Three weights, ~49 KB, copied by communication/_font_assets.py into fonts/
   beside this sheet. `font-weight: 500` (six sites) resolves down to 400 by CSS
   weight matching; 900 exists in the brand assets but nothing here uses it. */
@font-face {
  font-family: "Overpass";
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url("fonts/Overpass-400.woff2") format("woff2");
}
@font-face {
  font-family: "Overpass";
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  src: url("fonts/Overpass-600.woff2") format("woff2");
}
@font-face {
  font-family: "Overpass";
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  src: url("fonts/Overpass-700.woff2") format("woff2");
}

/* Road-viz applet — light mode only (basemap is a light raster). Palette tokens
   from the dataviz reference instance. */
:root {
  --n-ff: #fff;
  --surface-hover: var(--gold-wash);
  --ok-bg: #eaf4ec;
  --ok-text: #1e4a34;
  --ok-border: #2f6f4f;
  --warn-bg: #fff6e5;
  --warn-text: #7a4d06;
  --warn-border: #d08a1e;
  --bad-bg: #fdecea;
  --bad-text: #7d1f1f;
  --bad: #d03b3b;
  --shade-08: rgba(0, 0, 0, 0.08);
  --shade-10: rgba(0, 0, 0, 0.10);
  --shade-12: rgba(0, 0, 0, 0.12);
  --shade-16: rgba(0, 0, 0, 0.16);
  --surface-veil-94: var(--veil-ground-94);
  --surface-veil-96: var(--veil-ground-96);
  --on-accent: var(--brand-ink);
  --surface-1: var(--parchment-raised);
  --page: var(--parchment);
  --ink: #0b0b0b;
  --ink-2: #52514e;
  --muted: #898781;
  --hair: #e1e0d9;
  --border: rgba(11, 11, 11, 0.10);
  /* Selected state (applet-design rung 4, 2026-09-12). Gold is the brand's one
     accent and it is a shape with mass, never text: a selected surface is a
     gold fill with brand ink on it (7.3:1), because gold as text or as a
     hairline on a light ground measures 1.7:1. The family is hue 85 with
     chroma held at gold's own ~0.94 share of the sRGB ceiling at each
     lightness -- R5's ground family keeps parchment/plate's ~0.60 share, and
     that gap is what stops a gold wash reading as one more pane (derivation:
     scratchpad/brand/gold_stops.py). Anything that has to be *read* rather
     than filled -- headings, emphasised labels, links, the phone menu's
     checkmark, focus rings, the spinner, the scatter's guide bands -- goes to
     brand slate or brand ink instead. Blue survives only as a calibration
     data series; brand hues are chrome, the dataviz palette is data. */
  --brand-ink: #22303b;
  --brand-slate: #3d5467;
  --gold: #edb62b;
  --gold-deep: #c79603;
  --gold-edge: #fec849;
  --gold-soft: #ffe1a4;
  --gold-soft-hover: #ffdd97;
  --gold-wash: #ffedc7;
  --gold-wash-2: #ffe5b0;
  --sel: var(--gold);
  --sel-border: var(--gold-deep);
  --sel-soft: var(--gold-soft);
  --sel-soft-hover: var(--gold-soft-hover);
  --hover: var(--gold-wash);
  --hover-2: var(--gold-wash-2);
  --label-em: var(--brand-slate);
  --focus: var(--brand-ink);
  --pop-shade-rgb: 237 182 43;
  /* Warm grounds (applet-design rung 3, 2026-09-12). The brand's parchment /
     plate family on OKLCH hue 85, derived by scratchpad/brand/warm_stops.py:
     --parchment and --plate are the brand hexes exactly; -raised, -band and
     -hover are stops on the same curve (C tapering to 0 at white) for a
     surface that must read above the page, for banded chrome, and for a hover
     on a band. Grounds only -- text, borders, controls and the sheet's drag
     handle stay on the cool --n-* slate scale, which is what keeps the grab
     affordance legible against a warm sheet (Taylor, 2026-09-12). */
  --parchment: #fbf3e1;
  --parchment-raised: #fdfaf2;
  --parchment-band: #f8eace;
  --parchment-hover: #f6e5c4;
  --plate: #f4e1ba;
  --ground: var(--parchment);
  --ground-raised: var(--parchment-raised);
  --ground-band: var(--parchment-band);
  --ground-band-hover: var(--parchment-hover);
  --ground-trim: var(--plate);
  --veil-ground-96: rgba(251, 243, 225, 0.96);
  --veil-ground-94: rgba(253, 250, 242, 0.94);
  --panel-w: 300px;
  /* Typography (rung 3) — this bundle stands alone, so it declares its own face
     and its own copy of fonts/ rather than reaching up to the analyst applet's. */
  --font-ui: "Overpass", system-ui, sans-serif;
  --lh-ui: normal;
  font-family: var(--font-ui);
  line-height: var(--lh-ui);
}

* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body { color: var(--ink); background: var(--page); font-size: 14px; }

#app {
  display: grid;
  grid-template-columns: var(--panel-w) 1fr;
  height: 100vh;
}

.muted { color: var(--muted); font-size: 12px; }

/* ---- Left control panel ---- */
#controls {
  overflow-y: auto;
  padding: 16px 16px 24px;
  background: var(--surface-1);
  border-right: 1px solid var(--hair);
  display: flex;
  flex-direction: column;
  gap: 14px;
}
#title-block h1 { font-size: 18px; margin: 0 0 2px; }
#scenario-name { font-size: 12px; }

/* Applet nav strip (session 4 — integration into the analyst applet).
   Same idiom/markup as the analyst pages' .applet-nav (scenario_results.css)
   but themed with this bundle's own tokens — road-viz is a separate CSS
   file and ships light-mode only, so it doesn't share rules with the
   analyst applet's stylesheet. Absent (no element at all) on a standalone
   build-road-viz bundle — see index.html's {{ nav_strip }} placeholder. */
.applet-nav {
  display: flex; border: 1px solid var(--hair); border-radius: 7px;
  overflow: hidden;
  /* #controls is a flex column whose content usually exceeds the viewport
     (overflow-y: auto handles that) — under that shrink pressure, a flex
     item with overflow != visible gets an automatic min-height of 0 (CSS
     Flexbox §4.5) instead of a content-based one, so without this the strip
     was crushed to its 2px border with no visible links. Caught visually,
     not by the Playwright text-content checks, which don't see height. */
  flex-shrink: 0;
}
.applet-nav a {
  flex: 1; padding: 5px 8px; text-align: center; text-decoration: none;
  background: var(--n-ff); font-size: 11px; color: var(--ink-2); font-weight: 600;
}
.applet-nav a:hover { background: var(--page); }
.applet-nav a.active { background: var(--sel); color: var(--on-accent); }

.ctl { display: flex; flex-direction: column; gap: 5px; }
.ctl-label { font-weight: 600; font-size: 12px; color: var(--ink-2); }
select, input[type="range"] { width: 100%; }
select {
  font-size: 14px; padding: 7px 8px; border: 1px solid var(--border);
  border-radius: 7px; background: var(--n-ff); color: var(--ink);
}
/* Controls do not inherit font-family; see the note in scenario_results.css. */
button, input, select, textarea, optgroup { font-family: inherit; }

/* MapLibre's own sheet sets `font: 12px/20px Helvetica Neue, Arial, ...` on
   .maplibregl-map, and its attribution line, scale bar and popups inherit it —
   which is why those 16 elements survived every font change in this sheet. It is
   third-party CSS, but the attribution is chrome the reader sees inside our
   applet, so it takes the brand face. Only the family is overridden; MapLibre's
   own 12px/20px sizing is left alone. Delete this rule to revert. */
.maplibregl-map { font-family: var(--font-ui); }

/* Tabular figures where digits align or update in place (rung 3). */
table, #legend, #link-tip, .tip-delta, .tip-val {
  font-variant-numeric: tabular-nums;
}

input[type="checkbox"], input[type="radio"] { accent-color: var(--sel); }
input[type="range"] { accent-color: var(--brand-slate); }  /* a handle stays legible; gold is 1.7:1 on a light ground (rung 4) */

.ctl-group { border: 1px solid var(--hair); border-radius: 8px; padding: 10px 12px; }
.ctl-group > summary { font-weight: 600; cursor: pointer; font-size: 13px; }
.ctl-group > .ctl { margin-top: 12px; }

.ctl-overlays.hidden { display: none; }
#fc-checks { display: flex; flex-direction: column; gap: 4px; }
.fc-check { font-size: 13px; display: flex; align-items: center; gap: 6px; text-transform: capitalize; }

#drawer-toggle, #btn-3d-toggle {
  text-align: left; padding: 9px 12px; border: 1px solid var(--border);
  border-radius: 7px; background: var(--n-ff); font-size: 13px; font-weight: 600; cursor: pointer;
}
#drawer-toggle:hover, #btn-3d-toggle:hover { background: var(--surface-hover); }
#btn-3d-toggle.active { background: var(--sel); border-color: var(--sel-border); color: var(--on-accent); }

/* ---- Scenario Explore/Compare (P4) ---- */
#scenario-block.hidden { display: none; }
#scenario-block > .ctl { margin-top: 10px; }
.mode-toggle-strip { display: flex; gap: 6px; }
.mode-btn {
  flex: 1; text-align: center; padding: 7px 10px; border: 1px solid var(--border);
  border-radius: 7px; background: var(--n-ff); font-size: 13px; font-weight: 600; cursor: pointer;
}
.mode-btn:hover { background: var(--surface-hover); }
.mode-btn.active { background: var(--sel); border-color: var(--sel-border); color: var(--on-accent); }
.compare-ab-label {
  display: flex; align-items: center; gap: 6px; font-weight: 600; font-size: 12px; color: var(--ink-2);
}
#btn-swap-ab {
  border: 1px solid var(--border); border-radius: 5px; background: var(--n-ff);
  font-size: 12px; line-height: 1; padding: 3px 6px; cursor: pointer;
}
#btn-swap-ab:hover { background: var(--surface-hover); }
.compare-ab-row { display: flex; gap: 6px; }
.compare-ab-row select { flex: 1; min-width: 0; }
#compare-scenario-ctl.hidden { display: none; }

/* ---- Map ---- */
#map-wrap { position: relative; }
#map { position: absolute; inset: 0; }

/* ---- Legend (bottom-left) ---- */
#legend {
  position: absolute; left: 14px; bottom: 22px; z-index: 2;
  background: var(--surface-veil-94); border: 1px solid var(--border);
  border-radius: 9px; padding: 10px 12px; max-width: 280px;
  box-shadow: 0 2px 10px var(--shade-08); font-size: 12px;
}
.lg-title { font-weight: 600; margin-bottom: 7px; }
.lg-ramp-wrap { position: relative; height: 12px; }
.lg-ramp { height: 12px; border-radius: 3px; border: 1px solid var(--border); }
.lg-pivot {
  position: absolute; top: -2px; width: 2px; height: 16px; background: var(--ink);
  transform: translateX(-1px);
}
.lg-pivot::after {
  content: attr(data-pivot); position: absolute; top: -13px; left: 50%; transform: translateX(-50%);
  font-size: 9px; color: var(--ink-2); white-space: nowrap;
}
.lg-ends { display: flex; justify-content: space-between; margin-top: 3px; color: var(--ink-2); }
.lg-clamp { margin-top: 3px; font-size: 10px; }
.lg-cats { display: flex; flex-wrap: wrap; gap: 5px 10px; }
.lg-chip { display: inline-flex; align-items: center; gap: 5px; text-transform: capitalize; }
.lg-chip i { width: 12px; height: 12px; border-radius: 3px; display: inline-block; flex: none; }

/* ---- Link hover tooltip ---- */
#link-tip {
  position: absolute; z-index: 4; pointer-events: none;
  background: var(--surface-veil-96); border: 1px solid var(--border);
  border-radius: 8px; padding: 8px 11px; max-width: 240px;
  box-shadow: 0 2px 10px var(--shade-12); font-size: 12px;
}
#link-tip.hidden { display: none; }
/* Compare mode needs real table width for 3 numeric columns beside a long
   label ("Volume — total (demand + preload)") without any cell wrapping
   past the box edge — 240px (Explore's single-value width) isn't enough. */
#link-tip.tip-compare { max-width: 380px; }
.tip-name { font-weight: 600; margin-bottom: 4px; text-transform: capitalize; }
.tip-note { color: var(--ink-2); margin-bottom: 4px; }
.tip-row { display: flex; justify-content: space-between; gap: 12px; }
.tip-row .tip-label { color: var(--ink-2); flex: none; }
.tip-row .tip-val { font-variant-numeric: tabular-nums; text-align: right; }

/* Compare-mode tooltip table (A | B | Δ) — a real <table>, mirroring
   .ins-table.ins-compare, so columns stay aligned across rows regardless of
   label/unit text length (independent flex rows can't guarantee that). The
   label column wraps normally; the 3 value columns stay single-line. */
.tip-table { width: 100%; border-collapse: collapse; margin-top: 2px; }
.tip-table td { padding: 2px 0 2px 4px; vertical-align: top; }
.tip-table td:first-child { padding-left: 0; }
.tip-table td.tip-label { color: var(--ink-2); }
.tip-table td:not(.tip-label) {
  text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap;
}
.tip-delta { font-weight: 600; }
.tip-compare-head td {
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.03em; color: var(--muted); padding-bottom: 3px;
}

/* ---- Mobile chrome (hidden on desktop) ---- */
#mobile-bar { display: none; }
.sheet-handle { display: none; }
#controls-close { display: none; }

/* ---- Link inspector (right) ---- */
#inspector {
  position: absolute; top: 0; right: 0; height: 100vh; width: 320px; z-index: 5;
  background: var(--surface-1); border-left: 1px solid var(--hair);
  box-shadow: -3px 0 14px var(--shade-10); overflow-y: auto; padding: 16px 18px 28px;
}
#inspector.hidden, #drawer.hidden { display: none; }
#inspector-close, #drawer-close {
  position: absolute; top: 10px; right: 12px; border: none; background: none;
  font-size: 22px; line-height: 1; cursor: pointer; color: var(--muted);
}
.ins-head { margin-bottom: 12px; padding-right: 20px; }
.ins-name { font-size: 16px; font-weight: 600; }
.ins-id { margin-top: 2px; }
.ins-group { margin-top: 14px; }
.ins-group-title {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--brand-slate); border-bottom: 1px solid var(--hair); padding-bottom: 3px; margin-bottom: 5px;
}
.ins-table { width: 100%; border-collapse: collapse; }
.ins-table td { padding: 2px 0; vertical-align: top; font-size: 13px; }
.ins-table td:first-child { color: var(--ink-2); padding-right: 10px; }
.ins-table td:last-child { text-align: right; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }

/* Compare-mode inspector rows (A | B | Δ) — DESIGN-road-compare.md §7. */
.ins-table.ins-compare td { text-align: right; font-variant-numeric: tabular-nums; padding: 2px 4px; }
.ins-table.ins-compare td:first-child { text-align: left; color: var(--ink-2); padding-left: 0; }
.ins-compare-head td { font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.03em; color: var(--muted); padding-bottom: 3px; }
.ins-delta { font-weight: 600; }
.ins-vot-compare { display: flex; flex-direction: column; gap: 8px; }
.ins-jump {
  margin-top: 8px; padding: 6px 10px; border: 1px solid var(--border); border-radius: 6px;
  background: var(--n-ff); font-size: 12px; cursor: pointer; width: 100%;
}
.ins-jump:hover { background: var(--surface-hover); }

.ins-vot { margin-bottom: 6px; }
.vot-bar {
  display: flex; height: 14px; border-radius: 4px; overflow: hidden; margin: 4px 0;
  border: 1px solid var(--border);
}
.vot-seg { display: block; height: 100%; }
.vot-key { display: flex; gap: 12px; }
.vot-key span { display: inline-flex; align-items: center; gap: 4px; }
.vot-key i { width: 9px; height: 9px; border-radius: 2px; display: inline-block; }

/* ---- Diagnostics drawer ---- */
#drawer {
  position: absolute; top: 0; right: 0; height: 100vh; width: 360px; z-index: 6;
  background: var(--surface-1); border-left: 1px solid var(--hair);
  box-shadow: -3px 0 14px var(--shade-12); overflow-y: auto; padding: 16px 20px 30px;
}
#drawer h2 { font-size: 17px; margin: 0 0 10px; }
.dr-sec { margin-top: 16px; }
.dr-sec h3 { font-size: 13px; margin: 0 0 6px; color: var(--brand-slate); }
.dr-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.dr-table td { padding: 3px 6px 3px 0; border-bottom: 1px solid var(--hair); }
.dr-table td:not(:first-child) { text-align: right; font-variant-numeric: tabular-nums; }
.dr-th td { font-weight: 600; color: var(--ink-2); }
.dr-flag { color: var(--bad); font-weight: 700; }
.dr-note { margin: -2px 0 4px; }

/* Gateway / s71 inspector flags. These call out a defect (a gateway that seeds
   nothing, a fired symmetry guard, a gateway sitting on the analysis outline
   rather than at the extract cut), so they are deliberately louder than a table
   row — the whole point is that these were previously invisible. */
.gw-flag { padding: 6px 8px; margin-bottom: 6px; border-radius: 4px;
           font-size: 12px; line-height: 1.35; border-left: 3px solid; }
.gw-bad  { background: var(--bad-bg); border-color: var(--bad); color: var(--bad-text); }
.gw-warn { background: var(--warn-bg); border-color: var(--warn-border); color: var(--warn-text); }
.gw-ok   { background: var(--ok-bg); border-color: var(--ok-border); color: var(--ok-text); }
.gw-flag code { font-size: 11px; }
.gw-caveat { font-size: 11px; color: var(--muted); font-style: italic;
             margin-top: 4px; }
.ins-table td.sub { padding-left: 10px; color: var(--muted); }
.s71-warn { font-size: 11px; font-style: italic; margin-top: 4px; }

/* modeled-vs-observed scatter */
.sc-svg { width: 100%; height: auto; margin-top: 8px; }
.sc-grid { stroke: var(--hair); stroke-width: 1; }
.sc-frame { fill: none; stroke: var(--border); stroke-width: 1; }
.sc-band30 { fill: var(--brand-slate); opacity: 0.08; }
.sc-band10 { fill: var(--brand-slate); opacity: 0.12; }
.sc-diag { stroke: var(--ink-2); stroke-width: 1.2; stroke-dasharray: 4 3; }
.sc-tick { fill: var(--muted); font-size: 9px; }
.sc-axis { fill: var(--ink-2); font-size: 10px; }
.sc-pt { cursor: pointer; }
.sc-pt:hover { stroke-width: 1.8; }
.sc-pt:focus { outline: 2px solid var(--focus); outline-offset: 1px; }

/* ==== Mobile (≤720px): map-first + bottom sheets ==========================
   The map owns the viewport; a fixed bottom bar toggles the control sheet and
   the diagnostics sheet. Tapping a link raises the inspector as a half-height
   sheet so the map stays visible above it; dragging a sheet's handle resizes
   it (sheet_drag.js). Desktop layout above is untouched. */
@media (max-width: 720px) {
  /* No safe-area term: the viewport meta has no `viewport-fit=cover` (dropped
     2026-09-12 with the scenario-results bar, same reason -- on Android Chrome
     edge-to-edge the inset drew an empty grey band under the buttons). */
  :root { --bar-h: 52px; }

  #app { display: block; height: 100vh; height: 100dvh; }
  #map-wrap { position: absolute; inset: 0 0 var(--bar-h) 0; }

  #mobile-bar {
    display: flex; position: fixed; left: 0; right: 0; bottom: 0;
    height: var(--bar-h); z-index: 10; gap: 10px;
    padding: 7px 10px;
    background: var(--surface-1); border-top: 1px solid var(--hair);
  }
  #mobile-bar button {
    flex: 1; font-size: 14px; font-weight: 600; border: 1px solid var(--border);
    border-radius: 8px; background: var(--n-ff); color: var(--ink); cursor: pointer;
  }
  #mobile-bar button[aria-expanded="true"] { background: var(--sel); border-color: var(--sel-border); color: var(--on-accent); }

  /* The inspector and drawer become bottom sheets docked above the bar;
     #controls overrides the edge below. */
  #controls, #inspector, #drawer {
    position: fixed; left: 0; right: 0; bottom: var(--bar-h); top: auto;
    width: auto; height: auto; border: none; border-top: 1px solid var(--hair);
    border-radius: 14px 14px 0 0; box-shadow: 0 -4px 18px var(--shade-16);
    padding-top: 4px; overflow-y: auto;
  }
  /* Style & filters slides down from the top, like the analyst applet's
     Controls sheet (Taylor, 2026-09-12): full height, so it and the two
     bottom sheets never fight over the same edge. */
  #controls {
    top: 0; bottom: var(--bar-h); z-index: 8; max-height: none;
    border-top: none; border-radius: 0; box-shadow: none;
    transform: translateY(-105%); transition: transform 0.18s ease-out;
  }
  #controls.open { transform: none; }
  /* Opening heights; from there the handle drags the sheet anywhere between
     120px and 24px short of the top (sheet_drag.js writes an inline height).
     The sheets scroll as a whole, so the handle is sticky: it stays under the
     thumb however far the body has scrolled. `touch-action: none` keeps the
     browser from turning the drag into a scroll first. */
  #inspector { z-index: 5; height: 48vh; padding-top: 0; }
  #drawer { z-index: 6; height: 56vh; padding-top: 0; }

  .sheet-handle {
    display: flex; justify-content: center; padding: 8px 0 0; height: 32px;
    box-sizing: border-box; cursor: grab; touch-action: none;
    position: sticky; top: 0; z-index: 1; background: var(--surface-1);
  }
  /* Drag hitbox reaches 24px past the row into the body (invisible). */
  .sheet-handle::after { content: ""; position: absolute; left: 0; right: 0; top: 100%; height: 24px; }
  .sheet-handle span { width: 38px; height: 4px; border-radius: 2px; background: var(--hair); }

  /* The bar's buttons replace the in-panel diagnostics toggle. Compare stays
     desktop-only (§8) — a phone opening a compare-capable bundle sees exactly
     what it would see opening a single-scenario one: no picker, no toggle. */
  #drawer-toggle { display: none; }
  #scenario-block { display: none !important; }
  /* The nav strip is #controls's first child and the ✕ is absolute at its
     top-right; a sheet from the top shows the overlap the 70vh bottom sheet
     used to hide below the fold. */
  #controls > .applet-nav { margin-right: 44px; }
  #controls-close {
    display: block; position: absolute; top: 6px; right: 10px; border: none;
    background: none; font-size: 26px; line-height: 1; padding: 6px; color: var(--muted); cursor: pointer;
  }

  /* Legend moves top-left so sheets don't cover it, and collapses to its
     title chip (tap toggles — wired in app.js). */
  #legend { top: 10px; left: 10px; bottom: auto; max-width: min(62vw, 240px); padding: 8px 10px; cursor: pointer; }
  #legend.collapsed .lg-sec:not(:first-child) { display: none; }
  #legend.collapsed .lg-sec > *:not(.lg-title) { display: none; }
  #legend.collapsed .lg-title { margin-bottom: 0; }
  #legend.collapsed .lg-title::after { content: " ▸"; color: var(--muted); }

  /* Touch targets. 16px select font also stops iOS zoom-on-focus. */
  select { font-size: 16px; padding: 10px; }
  .fc-check { font-size: 15px; padding: 3px 0; }
  .fc-check input, #toggle-counts { width: 18px; height: 18px; }
  input[type="range"] { height: 28px; }
  .ins-jump { padding: 11px 10px; font-size: 13px; }
  #inspector-close, #drawer-close { font-size: 26px; padding: 6px; top: 6px; }
}
