/* The brand typeface (@font-face) and the colour / type tokens (:root) are in
   tokens.css, which every page links before this sheet. */

* { box-sizing: border-box; margin: 0; padding: 0; }

html { height: 100%; }
/* Column layout: #app (the map; until G7 the app bar over it) takes the whole
   height.
   #measurements (the dock) is fixed to the viewport's bottom and OVERLAYS the
   map (G1, Taylor 2026-10-20), as the phone sheet always has: dragging the
   dock changes nothing on the map. */
body {
  height: 100%; font-family: var(--font-ui); font-size: 14px; line-height: var(--lh-ui);
  display: flex; flex-direction: column;
  /* The dock body's height (S3-b): the old 180px dock less its ~32px header.
     Moved by the resize grip; the dock's own height is this plus its chrome. */
  --mp-body-h: 148px;
}
/* #app stacks #panel (empty but for an init-error banner, since G7) over the
   map (applet-ui-cleanup frame slice: the left sidebar is gone). */
#app { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }

/* ---- #panel: the init-error banner's slot ----
   It was the app bar (desktop) and the gear menu until G7 (Taylor,
   2026-10-20/21), which folded the bar into the legend's head row
   (.page-head, below) and Advanced / Units into the legend's Display fold.
   It holds only hidden furniture now, so it is an empty block over the map
   until app.js prepends an init-error banner to it. */
#panel { flex: 0 0 auto; position: relative; z-index: 20; }
/* The banner itself (app.js _showInitErrorBanner): a strip across the top of
   the page, over the map's top edge (the map runs below it), in the warning
   tokens. First seen on screen 2026-10-21, forced with a 404 / 500 on a boot
   file: readable and clear of the legend on a desktop as it was; its colours
   moved here from inline literals. The phone places it above the sheet
   (mobile.css). */
.init-error {
  display: flex; align-items: center; gap: 10px; padding: 8px 12px;
  background: var(--warn-bg); border-bottom: 1px solid var(--warn-border);
  color: var(--warn-text); font-size: 13px; line-height: 1.35;
}
.init-error-msg { flex: 1 1 auto; min-width: 0; }
.init-error button {
  flex: none; padding: 3px 12px; font: inherit; font-weight: 600; cursor: pointer;
  color: var(--warn-text); background: var(--n-ff); border: 1px solid var(--warn-border); border-radius: 4px;
}

/* ---- The page head (G7; _page_head.html) ----
   One row: lockup (22px, links to ives.st) | rule | "Connectome" | the page
   menu ("Map ▾"), at the head of the map page's legend card and of the
   Calibration page's head card. Sizes are D22 of shots-2026-10-20-g7 (Taylor,
   2026-10-21). The phone keeps the row and the sizes but for its 20px lockup
   (mobile.css). White-label: one ellipsized span replaces lockup, rule and
   title. */
.page-head {
  display: flex; align-items: center; gap: 7px; white-space: nowrap;
  margin: 0 0 7px; padding: 0 0 6px; border-bottom: 1px solid var(--n-dd);
}
.brand-lockup { display: flex; align-items: center; flex: 0 0 auto; }
.brand-lockup svg { display: block; height: 22px; width: auto; aspect-ratio: 1531 / 630; }
.brand-lockup:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 2px; }
.brand-rule { flex: 0 0 1px; width: 1px; height: 16px; background: var(--brand-slate); opacity: .5; }
.page-head-title {
  font-size: 15px; font-weight: 700; color: var(--brand-slate); line-height: 1;
  overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
.page-head-title.white-label { line-height: 1.15; }
/* The page menu (page_menu.js): the button names the current page; the list
   is placed `position: fixed` under it by script, so the legend's own scroll
   cannot clip it. The 4px negative right margin lines the caret up with the
   card's padding edge rather than the button's. */
.page-menu { position: relative; margin-left: auto; flex: 0 0 auto; }
#page-menu-btn {
  display: flex; align-items: center; gap: 4px; position: relative;
  height: 26px; margin: 0 -4px 0 0; padding: 0 6px;
  border: 0; border-radius: 5px; background: none; cursor: pointer;
  font-size: 13px; font-weight: 700; color: var(--brand-ink);
}
#page-menu-btn:hover, #page-menu-btn[aria-expanded="true"] { background: var(--sel-soft); }
#page-menu-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.page-menu-caret { font-size: 10px; color: var(--n-55); }
#page-menu-list {
  display: flex; flex-direction: column; position: fixed; z-index: 20;
  min-width: 170px; padding: 4px 0;
  background: var(--ground-raised); border: 1px solid var(--n-cc); border-radius: 8px;
  box-shadow: 0 4px 14px var(--shade-15);
}
#page-menu-list[hidden], .page-menu-backdrop[hidden] { display: none; }
/* Catches the closing click so it does not reach the map (page_menu.js). */
.page-menu-backdrop { position: fixed; inset: 0; z-index: 19; }
#page-menu-list a {
  display: flex; align-items: center; justify-content: space-between;
  min-height: 32px; padding: 0 14px;
  font-size: 13px; color: var(--n-33); text-decoration: none;
}
#page-menu-list a:hover { background: var(--hover); }
#page-menu-list a.active { font-weight: 700; color: var(--brand-ink); box-shadow: inset 3px 0 0 var(--gold); }
#page-menu-list a.active::after { content: "\2713"; font-size: 12px; margin-left: 12px; color: var(--brand-ink); }

/* Advanced and Units: the foot of the legend's Display fold since G7 (the
   gear menu's before). */
.adv-toggle {
  display: flex; align-items: center; gap: 5px;
  font-size: 12px; color: var(--n-44); cursor: pointer;
}
.adv-toggle input { margin: 0; cursor: pointer; }
/* Reuses .norm-toggle/.norm-btn wholesale, so it introduces no colour of its own. */
.title-units { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.title-units-label { font-size: 12px; color: var(--n-44); }
/* .norm-toggle's default `margin: 0 0 3px auto` right-aligns it inside a
   full-width block; here the flex row already places it. */
.title-units .norm-toggle { margin: 0; }
/* Advanced is a *user* gate (CSS class); the inline display:none data gates
   mean "this study cannot show this" and must still win when Advanced is on. */
body:not(.adv-on) .adv-only { display: none !important; }
/* Native controls (rung 4). A discrete on/off state is gold -- the browser
   paints a dark tick on it by itself, which is the brand's gold-shape-with-ink
   again -- while a continuous control's handle stays slate. Same reason the
   phone sheet's drag handle is slate on a parchment sheet: a grab affordance
   has to be legible, and gold on a light ground is 1.7:1. Left unstyled these
   render in the browser's own blue, which is the one blue R4 would otherwise
   miss: it is a default, not a declaration, so it survives a grep. */
/* Form controls do not inherit font-family — left alone they render in the UA's own
   form face, which is the same trap as rung 4's undeclared checkbox colour: a
   default carries no declaration, so no grep of this sheet finds it. Only the face
   is inherited; size and weight stay whatever each control's own rule set them to. */
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 wherever digits line up in a column or update in place (rung 3).
   Overpass's figures are PROPORTIONAL by default and its `1` is ~40% narrower than
   its other digits, so a column of numbers goes ragged and a value that re-renders
   on hover jitters sideways. Running text keeps proportional figures, where they
   read better. */
table,
#legend, .snap-tooltip, .tip,
.pn-val, .pn-dc-val, .dcp-band-val, .dcp-col-sum {
  font-variant-numeric: tabular-nums;
}

input[type="checkbox"], input[type="radio"] { accent-color: var(--sel); }
input[type="range"] { accent-color: var(--brand-slate); }

/* ---- Collapsible sidebar sections ---- */
.sidebar-section { border-bottom: 1px solid var(--n-dd); }
.section-header {
  display: flex; flex-direction: column;
  padding: 7px 12px; cursor: pointer;
  font-size: 12px; font-weight: 600; color: var(--n-55);
  user-select: none; background: var(--ground-band);
}
.section-header:hover { background: var(--ground-band-hover); }
.sec-label {
  padding: 7px 12px;
  font-size: 12px; font-weight: 600; color: var(--n-55);
  background: var(--ground-band);
  border-bottom: 1px solid var(--n-e0);
  display: flex; justify-content: space-between; align-items: center;
}
.sec-header-top {
  display: flex; justify-content: space-between; align-items: center;
}
.sec-arrow { font-size: 10px; color: var(--n-99); transition: transform 0.15s; }
.sidebar-section.expanded .sec-arrow { transform: rotate(90deg); }
.section-body { display: none; padding: 8px 10px; }
.sidebar-section.expanded .section-body { display: block; }
.sec-summary { font-size: 12px; color: var(--n-33); font-weight: 500; margin-top: 3px; white-space: normal; line-height: 1.3; }
.sidebar-section.expanded .sec-summary { display: none; }

/* ---- Dropdowns ---- */
.ctrl-group { display: flex; flex-direction: column; gap: 3px; margin-bottom: 6px; }
.ctrl-group:last-child { margin-bottom: 0; }
.ctrl-group label { font-size: 11px; font-weight: 600; color: var(--n-66); }
.ctrl-group select {
  width: 100%; padding: 4px 6px; border: 1px solid var(--n-cc);
  border-radius: 4px; background: var(--n-ff); font-size: 12px; cursor: pointer;
}
.ctrl-group select:focus { outline: 2px solid var(--focus); }
.ctrl-group select:disabled { background: var(--n-f0); color: var(--n-aa); cursor: not-allowed; }

/* ---- Tooltip-indicator icon ----
   Disabled <select>s and tightly-packed grid cells consume hover area and
   suppress the native title-attr tooltip from showing on their parents.
   `.tip` is a small `(?)` glyph placed next to a label that carries the
   title= itself: always discoverable, always hoverable, regardless of
   neighboring disabled controls. */
.tip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 13px; height: 13px;
  margin-left: 3px;
  border-radius: 50%;
  background: var(--nc-c2);
  color: var(--on-dark);
  font-size: 9px;
  font-weight: 700;
  cursor: help;
  vertical-align: middle;
  line-height: 1;
  user-select: none;
}
.tip:hover { background: var(--sel); color: var(--on-accent); }

/* ---- Traveler profile — dev-only since PLAN-dock-controls S4 (`?dev=1`
   sets html.dev, app.js): the dock's tree is the profile control and the
   dock's #param-profile chip its readout. The section survives, hidden, as
   the profile's second renderer for the sweeps. Deleted at S6. ---- */
#traveler-section {
  display: none;
  border-bottom: 1px solid var(--n-dd);
  padding: 7px 10px 8px;
  background: var(--ground-band);
}
html.dev #traveler-section { display: block; }
.traveler-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 6px;
}
.traveler-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--n-77);
}
.uc-toggle-strip {
  display: flex;
  border: 1px solid var(--n-cc);
  border-radius: 4px;
  overflow: hidden;
}
.uc-toggle-btn {
  padding: 3px 10px;
  border: none;
  background: var(--n-ff);
  font-size: 11px;
  cursor: pointer;
  color: var(--n-55);
  line-height: 1.4;
}
.uc-toggle-btn:hover { background: var(--hover); }
.uc-toggle-btn.active { background: var(--sel); color: var(--on-accent); font-weight: 600; }
/* PLAN-s37 S5 — the reset that replaced the All/Specific toggle. Shown only
   while the selection is narrowed (app.js `syncTravelerChrome`). */
.uc-reset-btn {
  padding: 3px 10px;
  border: 1px solid var(--n-cc);
  border-radius: 4px;
  background: var(--n-ff);
  font-size: 11px;
  cursor: pointer;
  color: var(--n-55);
  line-height: 1.4;
}
.uc-reset-btn:hover { background: var(--hover); border-color: var(--sel-border); color: var(--label-em); }
/* The subset's own status line: how many profiles it folds, and — while the
   slices are in flight — how many have landed and roughly what they cost. The
   honest alternative to a cap (PLAN-s37 S5, Taylor 2026-09-12). */
.uc-subset-note {
  font-size: 10.5px;
  line-height: 1.35;
  color: var(--n-66);
  background: var(--ground-raised);
  border: 1px solid var(--n-dd);
  border-radius: 4px;
  padding: 4px 7px;
  margin-bottom: 6px;
}
.uc-subset-note.loading { color: var(--label-em); border-color: var(--gold-edge); background: var(--gold-wash); }
.uc-subset-note .uc-subset-bar {
  display: block;
  height: 3px;
  margin-top: 4px;
  border-radius: 2px;
  background: var(--hover-2);
  overflow: hidden;
}
.uc-subset-note .uc-subset-bar > i {
  display: block;
  height: 100%;
  background: var(--sel);
  transition: width 120ms linear;
}
.traveler-grid {
  display: grid;
  grid-template-columns: 1.5fr 1fr 1fr;
  gap: 8px;
}
.traveler-grid .ctrl-group { min-width: 0; }
.traveler-extras {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 6px;
}
.traveler-extras:empty,
.traveler-extras:not(:has(> *:not([style*="display:none"]))) {
  margin-top: 0;
}
#uc-specific-controls { display: none; }
#uc-specific-controls.visible { display: block; }

/* ---- Indicator list ----
   `.ind-group` was an accordion; SPEC-s22 S2 flattens the panel into one
   ordered column of buttons. The four `[data-group]` wrapper divs survive as
   plain grouping containers only — grpSel() and state.activeGroup resolution
   (app.js, compare.js, indicators.js) key off `.ind-group[data-group="X"]` —
   so they carry no border/background/radius and no open/closed state; every
   row in every group is always rendered. `.ind-group-body` also survives as a
   nesting level but is always visible now. */
.ind-group { margin-bottom: 10px; }
.ind-group-body { padding: 0; }
/* The Indicators section is dev-only since PLAN-dock-controls S3 (`?dev=1`
   sets html.dev, app.js): the dock's picker and parameter strip are the
   reader's controls, and the section survives, hidden, as the registry's
   second renderer for the sweeps. Deleted at S6. */
#sec-indicators { display: none; }
html.dev #sec-indicators { display: block; }

.ind-btn {
  display: block; width: 100%; text-align: left;
  padding: 4px 7px; margin-bottom: 2px;
  border: 1px solid var(--n-dd); border-radius: 3px;
  background: var(--n-ff); cursor: pointer; font-size: 11px; color: var(--n-44);
}
.ind-btn:last-child { margin-bottom: 0; }
.ind-btn:hover { background: var(--hover); }
.ind-btn.active {
  background: var(--sel); color: var(--on-accent); border-color: var(--sel-border); font-weight: 600;
}

/* ---- Normalization strips (per km² / total / per person / %) ---- */
/* Compact pill row. width:fit-content + margin-left:auto keeps it small
   without needing a float (the legend's units and colour-scale strips still
   right-align this way; in the dock's parameter strip the margin is zeroed).
   SPEC-s22 §5.3 moved these out of the indicator groups: they are not one
   fixed toggle per group but elements whose buttons renderNormStrip()
   rebuilds for whichever indicator is mapped — in #mp-params since S3. */
.norm-toggle {
  display: flex;
  border: 1px solid var(--n-cc);
  border-radius: 3px;
  overflow: hidden;
  margin: 0 0 3px auto;
  width: fit-content;
}
.norm-btn {
  padding: 0 8px;
  border: none;
  background: var(--n-ff);
  font-size: 10px;
  color: var(--n-66);
  cursor: pointer;
  line-height: 1.6;
}
.norm-btn + .norm-btn { border-left: 1px solid var(--n-dd); }
.norm-btn:hover { background: var(--hover); }
.norm-btn.active { background: var(--sel); color: var(--on-accent); font-weight: 600; }

/* ---- Sub-control checkbox row (SPEC-s22 §5.5's log₁₀ switch) ---- */
/* Metrics match the normalization strips above so the strip's controls line
   up whichever combination is showing. */
.subctrl-check { display: flex; align-items: center; gap: 5px; margin: 0; font-size: 12px; }
.subctrl-check input { margin: 0; cursor: pointer; }
.subctrl-check label { font-size: 11px; font-weight: 600; color: var(--n-66); cursor: pointer; }

/* ---- Outbound indicator buttons ---- */
.od-btn {
  display: block; width: 100%; text-align: center;
  padding: 3px 1px; border: 1px solid var(--n-dd); border-radius: 3px;
  background: var(--n-ff); cursor: pointer; font-size: 11px; color: var(--n-aa);
  transition: background 0.1s;
}
.od-btn:hover:not(:disabled) { background: var(--hover); color: var(--n-44); }
.od-btn.active { background: var(--sel); color: var(--on-accent); border-color: var(--sel-border); }
.od-btn:disabled { background: var(--n-f4); color: var(--n-cc); cursor: not-allowed; border-color: var(--n-ee); }

/* ---- Composite indicator chips (Mode choice / Destination choice) ----
   Indicators with no live per-mode column read as text buttons in the
   .ind-btn idiom. They keep .od-btn so every selection/disable path still
   finds them; these rules only undo .od-btn's centred glyph and muted
   colour. The .active and
   :disabled rules are the same in both classes, so they need no override. */
.ind-row { margin-bottom: 4px; }
.ind-row:last-child { margin-bottom: 0; }
.od-chip {
  text-align: left; color: var(--n-44); padding: 4px 7px; margin-bottom: 2px;
}
.od-chip:last-child { margin-bottom: 0; }

/* ---- Per-mode generalized impedance (Mode choice family) ----
   One indicator per mode rather than an indicator x column cell, so these are
   .ind-btn (not .od-btn) laid out as a wrapping chip row. The swatch carries
   the mid stop of that mode's ramp, so the button reads as its map.
   SPEC-s22 S4 split the old single `#impedance-modes` block: `.imp-row-label`
   is now the static row (`#row-gtm`) that stays in the flat list, and the
   chips (`.imp-btns` / `.imp-btn`) are shared sub-control host content that
   moves — the CAR / CAR_NONHH pair also merges into one chip (§5.1); see
   app.js buildImpedanceButtons / updateImpedanceButtonState. */
.imp-row-label {
  font-size: 10px; color: var(--n-55); padding: 2px 3px 3px 0;
  display: flex; align-items: center; line-height: 1.2;
  cursor: pointer; border-radius: 3px;
}
/* Clickable (app.js wires a click handler): the row IS the family — mode is
   a parameter in the dock since PLAN-trips-by-mode S(a), so this label is the
   whole of the family's presence on the panel, and it is what carries
   `.active` when one of the family's indicators is mapped (R9). */
.imp-row-label:hover { background: var(--hover); }
/* No padding change with `.active`: the label's own padding is asymmetric
   (nothing on the left, so it lines up with the row text above it), and
   adding any here would shift the words sideways as the selection moves. */
.imp-row-label.active {
  background: var(--sel); color: var(--on-accent); font-weight: 600;
}
/* The chips are ONE strip now, in the dock's parameter block (#subctrl-mode),
   rebuilt for whichever family is mapped — S(a) replaced the four permanent
   per-family rows session 27 had put under each panel label. The bottom
   margin was for those rows' spacing inside the panel; inside #mp-params the
   strip is a flex item among the other parameters and takes none. */
.imp-btns { display: flex; flex-wrap: wrap; gap: 3px; margin: 0 0 6px; }
.mp-param .imp-btns { margin: 0; }
.imp-btn {
  display: inline-flex; align-items: center; gap: 5px;
  width: auto; margin-bottom: 0; padding: 3px 8px 3px 6px;
}
.imp-btn::before {
  content: ''; flex: none; width: 9px; height: 9px; border-radius: 2px;
  background: var(--imp-chip, var(--n-cc));
  box-shadow: inset 0 0 0 1px var(--shade-18);
}
.imp-btn:disabled {
  background: var(--n-f4); color: var(--n-cc); cursor: not-allowed; border-color: var(--n-ee);
}
/* The chip is the mode's identity, so it dims rather than disappearing. */
.imp-btn:disabled::before { opacity: 0.3; }
.imp-btn:disabled:hover { background: var(--n-f4); }
.imp-btn.active::before { box-shadow: inset 0 0 0 1px var(--veil-55); }

/* ---- Layer toggles ---- */
.layer-toggle-row {
  display: flex; align-items: center; gap: 6px;
  font-size: 11px; cursor: pointer; padding: 2px 0;
}
.layer-toggle-row input[type="checkbox"] { cursor: pointer; flex-shrink: 0; }
.layer-swatch {
  display: inline-block; width: 20px; height: 4px;
  border-radius: 2px; flex-shrink: 0;
}
.transit-freq-legend {
  margin: 2px 0 2px 22px; display: flex; flex-direction: column; gap: 2px;
}
.transit-freq-row {
  display: flex; align-items: center; gap: 5px; font-size: 10px; color: var(--n-77);
}
.transit-freq-swatch {
  display: inline-block; width: 14px; height: 3px; border-radius: 1px; flex-shrink: 0;
}

/* ---- Map area ---- */
/* The map runs from the top of the window (the app bar's foot until G7) to the
   viewport's bottom on both layouts; the
   dock lies over its foot (G1, Taylor 2026-10-20). Before that #map shrank
   with the dock, and since MapLibre keeps the camera's centre at the canvas
   middle and its distance in proportion to the canvas height, every dock
   drag slid the map and steepened the 3D perspective. Now the canvas never
   resizes on a drag. What the dock covers is published as --sheet-h on
   #map-area (scenario_picker.js publishSheetH, a ResizeObserver on the dock,
   both layouts), and three things follow it: MapLibre's bottom corners (the
   attribution), the OD hint and the Flip frame. The camera takes the dock's
   height at boot as a transform padding instead (basemap.js openingCamera
   `dockPadding`), so fits and pitch changes centre on the uncovered map. */
#map-area { flex: 1; min-height: 0; display: flex; flex-direction: column; position: relative; }
#map { flex: 1; }
#map .maplibregl-ctrl-bottom-left, #map .maplibregl-ctrl-bottom-right { bottom: var(--sheet-h, 0px); }

/* Loading overlay. Two jobs in one element:
   BOOT -- it ships in the markup already visible (no `hidden`), fixed to the
   viewport, so it is on screen at first paint rather than at map `load`. The
   sidebar behind it is chrome with empty dropdowns and a "Loading..." snapshot
   at that point, so the boot variant is OPAQUE ground; app.js drops `.boot`
   once init settles and the element reverts to the veil.
   IN FLIGHT -- the same element over the loaded app while a scenario's
   lazily-fetched data is on the wire (showSpinner/hideSpinner, ref-counted).
   Fixed rather than absolute-inside-#map-area since session 51: at boot there
   is no populated map area to cover, and covering the half-built sidebar is
   the point. */
#loading-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  gap: 20px;
  align-items: center;
  justify-content: center;
  background: var(--veil-45);
}
#loading-overlay.boot { background: var(--ground); }
#loading-overlay[hidden] { display: none; }
/* The Ives Street badge mark, spinning. Inlined as a data URI rather than
   fetched: an asset whose whole job is to be on screen before anything else
   must not itself be a request the reader waits on. 288px covers 4x DPI at the
   72px display size; the un-encoded source sits beside this sheet as
   ives-badge.png (source only -- the build does not copy it). Re-encode with
   `base64 -w0 ives-badge.png` if the mark ever changes.
   3.2s, four times the old ring's 0.8s (Taylor, 2026-09-14): a wordless ring
   reads as "busy" at any speed, but a logo has a right way up, and the slower
   it turns the more it reads as a mark rotating rather than a part spinning. */
#loading-overlay .spinner {
  width: 72px;
  height: 72px;
  background: url("data:image/png;base64,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") center / contain no-repeat;
  animation: spin 3.2s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
/* Readers who ask for less motion get the mark, held still. The overlay is
   still the wait signal -- it is present or it is not. */
/* The download bar under the mark (app.js _renderProgress). Hidden until the
   wait has a known size; it fills to 95 % on bytes and the overlay's own
   hiding is the last step. Over the veil (a scenario switch) it sits on the
   dimmed map, so the track is opaque ground, not a tint. */
#loading-progress {
  width: 160px;
  height: 4px;
  border-radius: 2px;
  background: var(--n-dd);
  overflow: hidden;
}
#loading-progress[hidden] { display: none; }
#loading-progress > div {
  width: 0;
  height: 100%;
  background: var(--gold-deep);
  transition: width 0.2s linear;
}
@media (prefers-reduced-motion: reduce) {
  #loading-overlay .spinner { animation: none; }
  #loading-progress > div { transition: none; }
}

/* ---- Measurements panel (bottom-docked, full width, fixed height) ----
   Replaces the floating #mp-body. The panel is always present; when no
   origin is pinned it shows #mp-placeholder instead of #mp-body.

   #mp-body uses CSS multi-column so the migrated hover-panel blocks fill the
   width instead of running off the bottom; overflow spills into further
   columns and is reachable by scrolling horizontally. The mode-choice panel
   opts out of the column flow (info-panel--mc) and scrolls vertically instead:
   at the panel's 180px it is a table plus one side block, and a column flow
   would push the side block off the right edge rather than let it scroll.
   The original brief said no scrolling at all; the dock is now firmly 180px,
   which makes a scrollbar the honest alternative to silent clipping. */
#measurements {
  /* Sized from the inside since PLAN-dock-controls S3 (ruling S3-b): the
     scroller under the header has the height (--mp-body-h, the old 180px
     dock less its header) and the header adds its own. --mp-body-h is what
     the resize grip moves (dock_resize.js, inline on <body>). The
     parameter strip sits in the header bar on desktop (R25, 2026-09-24),
     so it costs nothing until the header runs out of width and wraps to a
     second line -- and then that line costs map, not results (S3-b). */
  /* Fixed over the map's foot (G1): see "Map area". z 8, the phone sheet's:
     over MapLibre's controls (2) and the Flip frame (1), under the legend
     (900), the OD hint (950) and the menus (960). */
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 8;
  background: var(--ground-raised); border-top: 1px solid var(--n-cc);
  display: flex; flex-direction: column;
  font-size: 12px; color: var(--n-33);
}
/* The resize grip: a 7px band straddling the dock's top edge (desktop only —
   the phone stylesheet hides it, the sheet's drag row is the phone's). Lit
   on hover and while dragging so the edge reads as a handle. */
#mp-resize {
  position: absolute; left: 0; right: 0; top: -4px; height: 7px;
  cursor: ns-resize; z-index: 3; touch-action: none;
}
#mp-resize:hover, #mp-resize.dragging { background: var(--sel-border); opacity: 0.6; }
/* A visible handle at rest, centred on the edge (Phase 2: "more obviously
   visible"), so the band reads as draggable before it is hovered. */
#mp-resize::after {
  content: ""; position: absolute; left: 50%; top: 2px; transform: translateX(-50%);
  width: 40px; height: 3px; border-radius: 2px; background: var(--n-aa);
}
body.mp-resizing { cursor: ns-resize; user-select: none; }

/* ---- The summary bar (desktop rendering) ----
   The sentence bar ("D · Sentence", Taylor 2026-10-19; subject first since
   G6, 2026-10-20): line 1 (subject, verb + indicator, value) · line 2
   (scenario) · below-the-fold (traveller class, filters) read left to right
   as one sentence -- "This zone has a population density ▾ of 9,230
   people/mi² under existing conditions ▾ for all travellers ▾". Every
   clause is its own flex item of #summary-bar (the containers between are
   `display: contents`), so the row wraps only BETWEEN clauses, never inside
   a chip or between a connective and its chip. The connective before each choice
   (.sb-conn, sentence.js) is Libre Caslon italic, the brand tagline's "for
   ‹word›" face; the choices are Overpass 600 in ink. No group rules: the
   connectives do the segmenting. The indicator name alone carries the gold
   underline (the lockup's device); every other choice has a thin slate one
   at rest that turns gold on hover / focus. The underlines are inset
   box-shadows on the TEXT spans, so they hug the word, never shift layout,
   and survive the spans' ellipsis clipping. The row wraps when it runs out
   of width. The phone stylesheet makes #summary-bar `display: contents` and
   lays its parts out as the sheet. */
#summary-bar {
  flex: 0 0 auto; display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px var(--sb-clause-gap);
  padding: 7px 16px 8px; background: var(--ground); border-bottom: 1px solid var(--n-e3);
  min-height: 52px; box-sizing: border-box;
}
#summary-bar .sheet-handle, #sb-flow { display: contents; }
/* The sentence's spacing (Taylor, 2026-10-20: "reads as one sentence"): a
   connective sits a word space from the choice it introduces
   (--sb-conn-gap), and one clause a little more than that from the next
   (--sb-clause-gap) -- 14px between clauses had read as separate
   controls. On a phone the 44px targets keep their size; only the space
   between them shrinks. */
:root { --sb-conn-gap: 4px; --sb-clause-gap: 9px; }
#summary-bar .sheet-grip { display: none; }
/* The connective words. 15px Caslon reads at about Overpass 14's x-height;
   brand slate is 7.1:1 on parchment. Never the st ligature ("destination"),
   which the italic carries in `liga` (brand reference § Type). */
#summary-bar .sb-conn {
  flex: none; font-family: var(--font-conn); font-style: italic; font-weight: 400;
  font-size: 15px; letter-spacing: 0; text-transform: none; color: var(--brand-slate);
  font-variant-ligatures: no-common-ligatures; white-space: nowrap;
}
#summary-bar .sb-conn:empty { display: none; }
/* The choices' underline: thin slate at rest, gold mass on hover / focus /
   while its menu is open. The indicator's is always gold (below). */
#scenario-chip-text .scn-name, #profile-chip-text, .param-chip-text {
  box-shadow: inset 0 -1px 0 var(--brand-slate);
}
#scenario-chip-btn:is(:hover, :focus-visible, [aria-expanded="true"]) #scenario-chip-text .scn-name,
#profile-chip-btn:is(:hover, :focus-visible, [aria-expanded="true"]) #profile-chip-text,
.param-chip-btn:is(:hover, :focus-visible, [aria-expanded="true"]) .param-chip-text {
  box-shadow: inset 0 -3px 0 var(--gold);
}
/* A chip's value is a word in the sentence: "for all travellers", "by car".
   The scenario keeps its capital -- it is a name. */
#profile-chip-text::first-letter, .param-chip-text::first-letter { text-transform: lowercase; }
/* Line 1: its clauses are the bar's own flex items (see above). The verb
   phrase and the value sit a word space from what precedes them, not a
   clause gap (a negative margin against the bar's column gap). */
#mobile-summary { display: contents; }
#ms-pred {
  display: flex; align-items: baseline; gap: var(--sb-conn-gap); flex: 0 1 auto; min-width: 0;
  margin-left: calc(var(--sb-conn-gap) - var(--sb-clause-gap));
}
/* The access rows' anchor takes the traveller chip before the scenario
   ("... above or below the average for [all travellers] under existing
   conditions"): summary.js sets data-sb-order. (The benefit's figure-first
   order is a DOM move in summary.js, so it reads in order too.) */
#summary-bar[data-sb-order="travellers-first"] :is(#summary-line2, #mp-params > *) { order: 1; }
#ms-ind {
  display: flex; align-items: baseline; flex: 0 1 auto; min-width: 0;
  margin: 0; padding: 0; border: 0;
  background: none; font: inherit; color: inherit; text-align: left; cursor: pointer;
}
#ms-ind-text {
  flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 17px; font-weight: 700; color: var(--brand-ink);
  box-shadow: inset 0 -3px 0 var(--gold);
}
#ms-ind::after {
  content: "\25BE"; flex: none; margin-left: 5px;
  font-size: 13px; line-height: 1; color: var(--brand-slate);
}
#ms-ind[aria-expanded="true"]::after { content: "\25B4"; }
#ms-ind:hover #ms-ind-text { color: var(--label-em); }
#ms-ind:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 3px; }
/* The value: a figure and its unit (summary.js valueSpans), the figure the
   one large item in the bar. A value with no figure ("at class average")
   is set at the name's size, not the number's. */
#ms-val {
  flex: none; display: flex; align-items: baseline; gap: 5px;
  margin-left: calc(var(--sb-conn-gap) - var(--sb-clause-gap));
  color: var(--brand-ink); white-space: nowrap; font-variant-numeric: tabular-nums;
}
#ms-val .ms-fig { font-size: 24px; line-height: 1; font-weight: 700; color: var(--brand-ink); }
#ms-val .ms-fig--word { font-size: 17px; }
#ms-val .ms-unit { font-size: 15px; font-weight: 600; color: var(--n-44); }
#ms-val .ms-num { display: flex; align-items: baseline; gap: 5px; }
/* Line 1 as the compare benefit (summary.js renderBenefit, 2026-10-20):
   "13k person-h/yr | 5.1 min/person/yr lost" in the benefit line's colours --
   text, so the darker benefit green (--pos-text, 5.3:1 on parchment), not the
   map's data green (2.6:1); --neg for lost; ink for no change. */
#ms-val.ms-val--pos .ms-fig, #ms-val.ms-val--pos .ms-unit { color: var(--pos-text); }
#ms-val.ms-val--neg .ms-fig, #ms-val.ms-val--neg .ms-unit { color: var(--neg); }
#ms-val .ms-sep { color: var(--n-aa); font-weight: 400; }
#ms-val[title] { cursor: help; }
#ms-val:empty { display: none; }
/* summary.js tags the region figure "region" on a phone only; on a desktop
   the subject says it. */
#ms-val .ms-region, #ms-val .ms-side { display: none; }
/* Compare Outcome's second clause (G6 (c)), in line 2 after the scenario
   chip: ", up 0.4 from existing conditions", its figure a step below the
   main one, the comma tucked against the chip. */
#ms-delta {
  flex: none; display: flex; align-items: baseline; gap: var(--sb-conn-gap); white-space: nowrap;
  margin-left: -2px;
  font-size: 15px; color: var(--brand-ink); font-variant-numeric: tabular-nums;
}
#ms-delta[hidden] { display: none; }
#ms-delta .ms-fig { font-size: 17px; font-weight: 700; color: var(--brand-ink); }
#ms-delta .ms-unit { font-size: 15px; font-weight: 600; color: var(--n-44); }
#ms-delta .ms-from { font-weight: 600; }
/* The subject: the sentence's first words, in the connective face. */
#sb-subject {
  display: flex; align-items: baseline; gap: var(--sb-conn-gap); flex: none; white-space: nowrap;
}
#sb-subject-text:empty { display: none; }
/* A hover is a preview, not a choice: the subject a step quieter (still
   5.2:1 on parchment). */
#sb-subject.sb-hovered #sb-subject-text { color: var(--n-66); }
/* A pinned subject explains on hover how to change or drop it (#subject-tip,
   summary.js): a dotted underline says there is something to read. */
#sb-subject[data-tip] #sb-subject-text {
  cursor: help; text-decoration: underline dotted var(--n-aa); text-underline-offset: 3px;
}
#subject-tip {
  position: fixed; z-index: 960; max-width: 340px; padding: 5px 10px; border-radius: 6px;
  background: var(--n-22); color: var(--n-ff); font-size: 12px; line-height: 1.35;
  box-shadow: 0 2px 8px var(--shade-15); pointer-events: none;
}
#subject-tip[hidden] { display: none; }
/* Below the fold: the traveller class and the mapped row's filters, each a
   clause of the bar's own row. */
#mp-header { display: contents; }
#mp-params:not([hidden]) { display: contents; }
/* "by the most likely mode (car)": the figure's mode after the top option. */
.param-chip-which { flex: none; font-weight: 600; }
.param-chip-which:empty { display: none; }

/* ---- The indicator menu (indicator_picker.js) ---- */
/* One listbox for both layouts, opened from #ms-ind (phone) or #mp-ind
   (desktop) and positioned by the script against whichever opened it --
   upward by preference, capped at 60% of the viewport so the map stays in
   view (a 26-row Advanced list scrolls inside). Above the sheets. Section
   headings are the registry's; the active row is ticked; a greyed row keeps
   its place with its reason under the label; a ⓘ opens the row's gloss. */
#ind-menu, #profile-menu, #scenario-menu, #param-menu {
  position: fixed; left: 8px; right: auto; bottom: auto; width: 400px;
  max-height: 60vh; max-height: 60dvh; overflow-y: auto; overscroll-behavior: contain;
  /* 960: over the desktop legend (900), which a menu opened from the summary
     bar can reach; under the loading overlay (1000). */
  z-index: 960; padding: 4px 0; background: var(--ground-raised); border: 1px solid var(--n-cc);
  border-radius: 10px; box-shadow: 0 6px 24px var(--shade-22); outline: none;
}
#ind-menu[hidden], #profile-menu[hidden], #scenario-menu[hidden], #param-menu[hidden] { display: none; }
.msm-head {
  padding: 8px 14px 3px; font-size: 11px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--n-6b);
}
.msm-head + .msm-head, .msm-opt + .msm-head { border-top: 1px solid var(--n-ee); margin-top: 4px; }
/* A bare row (no heading, R10) gets the same hairline a heading does, so it
   stands apart from the section above instead of reading as its child (R22).
   Not on the menu's first row: there is nothing above it to separate. */
.msm-opt + .msm-opt--bare, .msm-note + .msm-opt--bare { border-top: 1px solid var(--n-ee); margin-top: 4px; }
/* A section greyed wholesale, its reason under the heading. */
.msm-note { padding: 0 14px 4px 34px; font-size: 12px; line-height: 16px; color: var(--n-6b); }
.msm-opt {
  display: flex; align-items: flex-start; gap: 8px; min-height: 40px;
  padding: 6px 10px 6px 34px; font-size: 14px; color: var(--n-22); cursor: pointer; position: relative;
}
.msm-opt[aria-selected="true"] { font-weight: 600; color: var(--label-em); }
.msm-opt[aria-selected="true"]::before {
  content: "\2713"; position: absolute; left: 14px; color: var(--label-em); font-weight: 700;
}
.msm-opt.msm-cur, .msm-opt:active { background: var(--sel-soft); }
.msm-swatch { flex: none; width: 12px; height: 12px; border-radius: 3px; border: 1px solid var(--shade-15);
              margin-top: 5px; }
.msm-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.msm-label { line-height: 22px; }
/* The row names are the sentence's words, lower case (G6); a list
   capitalises them. */
#ind-menu .msm-opt .msm-label::first-letter { text-transform: uppercase; }
/* A greyed row: on offer, not selectable now, and it says why. The reason
   is the tooltip the panel shows on hover, printed -- a phone has no hover. */
.msm-opt.msm-off { cursor: default; }
.msm-opt.msm-off .msm-label { color: var(--n-88); }
.msm-why, .msm-gloss, .sm-gloss, .sm-note { font-size: 12px; line-height: 16px; font-weight: 400; color: var(--n-6b); }
.msm-gloss { color: var(--nc-556); padding: 2px 0 4px; }
.msm-gloss[hidden] { display: none; }
/* The ⓘ: a 24px target at the row's right edge, tab-skipped (the listbox
   owns focus; Right / Left open and close the current row's gloss). */
.msm-info {
  flex: none; width: 24px; height: 22px; margin: 0; padding: 0;
  border: 1px solid var(--n-cc); border-radius: 50%; background: var(--n-ff);
  font: 700 11px/1 Georgia, serif; font-style: italic; color: var(--n-6b); cursor: pointer;
}
.msm-info:hover, .msm-info[aria-expanded="true"] { background: var(--sel-soft); border-color: var(--sel-border); color: var(--label-em); }
/* The profile menu (2026-09-21): #ind-menu's box, holding one block per
   modelled dimension -- a heading with its ⓘ, the gloss beneath when opened,
   and a wrapping row of option pills (role=radio buttons). The checked pill
   is filled; a pill no class would match is dimmed and inert. The footer is
   the live count, or the no-match sentence in the warning colour. */
#profile-menu { width: 420px; padding: 4px 0 0; }
/* The selection chips' menu (param_menu.js): #ind-menu's rows. A row under
   another -- a category under its purpose, a household option under Car --
   is indented one step; a pickable heading (a purpose group's "All <group>"
   mixture) keeps the heading's type but takes the row's tick and hover. */
#param-menu { width: 360px; }
.msm-opt--sub { padding-left: 52px; min-height: 34px; }
.msm-opt--sub[aria-selected="true"]::before { left: 34px; }
/* In a menu whose rows carry swatches the labels start 20 px further in, so
   a sub-row indents from there or it reads as outdented (the household rows
   under Car). */
#param-menu.pmn-swatched .msm-opt--sub { padding-left: 74px; }
#param-menu.pmn-swatched .msm-opt--sub[aria-selected="true"]::before { left: 56px; }
.msm-opt--head {
  min-height: 0; padding-top: 8px; padding-bottom: 3px; font-size: 11px; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--n-6b);
}
.msm-opt--head .msm-label { line-height: 18px; }
.msm-opt--head[aria-selected="true"] { color: var(--label-em); }
.msm-opt + .msm-opt--head, .msm-head + .msm-opt--head { border-top: 1px solid var(--n-ee); margin-top: 4px; }
.msm-opt--head + .msm-head { border-top: 1px solid var(--n-ee); margin-top: 4px; }
.pm-dim[hidden] { display: none; }
.pm-dim + .pm-dim { border-top: 1px solid var(--n-ee); margin-top: 6px; }
.pm-head {
  display: flex; align-items: center; gap: 8px; padding: 8px 14px 4px;
  font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--n-6b);
}
.pm-head .pm-info { margin-left: auto; }
.pm-gloss { padding: 0 14px 6px; }
.pm-opts { display: flex; flex-wrap: wrap; gap: 6px; padding: 2px 14px 6px; }
/* The pill. The scenario menu shared these selectors while its options were
   pills too; since S5's second round (§0i: the matrix) it draws radios in a
   grid instead and the pill is the profile menu's own again. */
.pm-opt {
  flex: none; min-height: 32px; margin: 0; padding: 4px 12px;
  border: 1px solid var(--n-cc); border-radius: 999px; background: var(--n-ff);
  font: inherit; font-size: 13px; line-height: 20px; color: var(--n-22); cursor: pointer;
}
.pm-opt:hover { border-color: var(--sel-border); background: var(--sel-soft); }
.pm-opt[aria-checked="true"] {
  font-weight: 600; color: var(--label-em); background: var(--sel-soft); border-color: var(--sel-border);
}
.pm-opt[aria-disabled="true"] { color: var(--n-88); border-style: dashed; background: none; cursor: default; }
.pm-opt:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
/* Sticky: the menu scrolls inside its 60 vh cap and the footer -- the count
   and Done -- must stay in view whatever the row above is doing. */
.pm-footer {
  position: sticky; bottom: 0; z-index: 1; background: var(--ground-raised);
  display: flex; align-items: center; gap: 10px; padding: 8px 14px 8px;
  border-top: 1px solid var(--n-ee); margin-top: 6px;
}
#pm-foot {
  flex: 1 1 auto; min-width: 0; font-size: 12px; line-height: 16px; color: var(--n-6b);
  font-variant-numeric: tabular-nums;
}
#pm-foot.pm-foot--none { color: var(--warn-text); }
/* Done closes; the choices are live already. Filled like a checked pill so it
   reads as the affirmative end of the menu. */
#pm-done {
  flex: none; margin: 0; padding: 4px 14px; min-height: 32px;
  border: 1px solid var(--sel-border); border-radius: 999px; background: var(--sel-soft);
  font: inherit; font-size: 13px; font-weight: 600; line-height: 20px; color: var(--label-em); cursor: pointer;
}
#pm-done:hover { background: var(--sel); color: var(--n-ff); }
#pm-done:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
/* The profile chip (S4-c; home settled 2026-09-20): one line after the
   indicator name -- who the map is about, as subsetLabel spells it, and a
   bare ✕ while narrowed. Ellipsis rather than wrap (the full label is the
   title). A subset in flight underlines the text with a progress bar; a
   combination no class matches is the label in the warning colour. Since
   2026-09-21 the label is a button with a ▾ (the #mp-ind convention beside
   it) opening #profile-menu; the button inherits the chip's type so the
   text is still one line at the chip's size. */
#mp-profile {
  position: relative; display: flex; align-items: baseline; gap: var(--sb-conn-gap);
  flex: 0 1 auto; min-width: 0;
  font-size: 15px; color: var(--brand-ink); white-space: nowrap;
}
#mp-profile[hidden] { display: none; }
#profile-chip-btn {
  display: flex; align-items: baseline; gap: 4px; flex: 0 1 auto; min-width: 0;
  margin: 0; padding: 0; border: 0; background: none; font: inherit; color: inherit;
  cursor: pointer; text-align: left;
}
#profile-chip-btn:hover #profile-chip-text, #profile-chip-btn[aria-expanded="true"] #profile-chip-text { color: var(--label-em); }
#profile-chip-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; border-radius: 2px; }
.profile-chip-caret { flex: none; font-size: 11px; color: var(--brand-slate); }
#profile-chip-btn:hover .profile-chip-caret, #profile-chip-btn[aria-expanded="true"] .profile-chip-caret { color: var(--label-em); }
#profile-chip-text { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; font-weight: 600; }
.profile-chip-text--none { color: var(--warn-text); }
#profile-chip-bar {
  position: absolute; left: 0; right: 0; bottom: -3px; height: 2px;
  background: var(--hover-2); overflow: hidden;
}
#profile-chip-bar[hidden] { display: none; }
#profile-chip-bar > i { display: block; height: 100%; background: var(--sel); transition: width 120ms linear; }
/* The selection chips (applet-ui-cleanup rulings 7-8): the travel mode and
   the purpose / destination, in the traveller chip's idiom so the three read
   alike: a connective ("by", "on trips to"), the value, a caret. */
#mp-profile .param-chip-label { flex: none; }
.param-chip { color: var(--brand-ink); white-space: nowrap; min-width: 0; }
#mp-params .mp-param.param-chip:not([hidden]) { display: flex; align-items: baseline; gap: var(--sb-conn-gap); font-size: 15px; }
.param-chip-btn {
  display: flex; align-items: baseline; gap: 4px; flex: 0 1 auto; min-width: 0;
  margin: 0; padding: 0; border: 0; background: none; font: inherit; color: inherit;
  cursor: pointer; text-align: left;
}
.param-chip-text { flex: 0 1 auto; min-width: 0; max-width: 240px; overflow: hidden; text-overflow: ellipsis; font-weight: 600; }
.param-chip-caret { flex: none; font-size: 11px; color: var(--brand-slate); }
.param-chip-btn:hover .param-chip-text, .param-chip-btn[aria-expanded="true"] .param-chip-text,
.param-chip-btn:hover .param-chip-caret, .param-chip-btn[aria-expanded="true"] .param-chip-caret { color: var(--label-em); }
.param-chip-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; border-radius: 2px; }
/* ---- The scenario chip and its menu (scenario_picker.js, PLAN-dock-controls S5) ---- */
/* The chip is map furniture, not dock chrome: what the map is OF belongs with
   the legend, and it is styled as the legend is -- the same veil ground, border,
   radius and shadow, and the legend's z-index in each layout (900 here, 5 on the
   phone, where the sheets sit at 8/9). Top-left is the corner the desktop legend
   leaves free; on a phone the two share it and the legend moves down by --scn-h.
   The label itself is #profile-chip-btn's idiom -- quiet text, a small caret,
   the full coordinate list in the title -- so the applet's two disclosure chips
   read alike. Ellipsis rather than wrap: a five-axis label is folded by the
   label rule, not by the box. */
/* Line 2 of the summary bar (desktop): "under Existing conditions ▾" or
   "comparing A → B ▾ [Change|Outcome|Flip]", the connective in .sb-conn.
   The chip is plain text with a caret here, not a floating box. */
#summary-line2 {
  display: flex; align-items: baseline; gap: var(--sb-conn-gap); flex: 0 1 auto; min-width: 0;
}
#summary-line2[hidden] { display: none; }
#summary-line2 #view-ctl { align-self: center; margin-left: calc(var(--sb-clause-gap) - var(--sb-conn-gap)); }
#scenario-chip {
  display: flex; align-items: baseline; min-width: 0; max-width: 460px;
  font-size: 15px; color: var(--brand-ink);
}
#scenario-chip[hidden] { display: none; }
#scenario-chip-btn {
  display: flex; align-items: baseline; gap: 4px; max-width: 100%;
  margin: 0; padding: 0; border: 0; background: none; font: inherit; color: inherit;
  cursor: pointer; text-align: left;
}
#scenario-chip-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 3px; }
#scenario-chip-text {
  flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; font-weight: 600; color: var(--brand-ink);
}
.scenario-chip-caret { flex: none; font-size: 11px; color: var(--brand-slate); }

/* Compare's Flip view (PLAN-each-view S2): the A | B control replaces the
   chip's text, and the chip's button shrinks to the caret that opens the menu
   (D6; REVIEW-S0 F14 keeps the element). scenario_picker.js sets `.chip-flip`;
   #flip-seg lives in the chip on both layouts. One
   line per button, the side's name only (D31); the pressed button is slate
   for the reference, gold for the alternative, the frame's colours (D28).
   The two buttons are EQUAL and of a definite width -- the control's width
   split in half -- because that width is the budget D29's live fit measures
   each name against (scenario_picker.js fitFlipNames): a content-sized
   button would always "fit". */
#scenario-chip.chip-flip { gap: var(--sb-conn-gap); max-width: none; align-items: center; align-self: center; }
#scenario-chip.chip-flip #scenario-chip-text { display: none; }
#scenario-chip.chip-flip #scenario-chip-btn { flex: none; padding: 4px 2px; }
#scenario-chip.chip-flip .scenario-chip-caret { font-size: 11px; }
#flip-seg {
  display: flex; min-width: 0; flex: none; width: clamp(220px, 24vw, 360px);
  border: 1px solid var(--n-cc); border-radius: 5px; overflow: hidden;
  background: var(--n-ff);
}
#flip-seg[hidden] { display: none; }
.flip-btn {
  display: flex; align-items: center; flex: 1 1 0; min-width: 0; margin: 0; padding: 3px 10px;
  border: 0; background: none; font: inherit; color: var(--n-44);
  cursor: pointer; text-align: center;
}
.flip-btn + .flip-btn { border-left: 1px solid var(--n-cc); }
/* The name: a nowrap block as wide as the button's content box, clipped and
   NOT ellipsized -- the fit picks a name that needs neither (D29). */
.flip-label {
  display: block; flex: 1 1 auto; width: 100%; min-width: 0; overflow: hidden; white-space: nowrap;
  font-size: 12px; line-height: 16px; font-weight: 700; color: var(--n-44);
}
.flip-btn:hover:not([aria-pressed="true"]) { background: var(--n-f4); }
.flip-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: -3px; }
.flip-btn[aria-pressed="true"][data-side="a"] { background: var(--side-a); }
.flip-btn[aria-pressed="true"][data-side="a"] .flip-label { color: var(--on-dark); }
.flip-btn[aria-pressed="true"][data-side="b"] { background: var(--side-b); }
.flip-btn[aria-pressed="true"][data-side="b"] .flip-label { color: var(--on-accent); }
/* --n-55: 6.4:1 on parchment, 5.9 inside the white kbd (--n-77 was 4.1). */
#flip-hint { flex: none; font-size: 10px; color: var(--n-55); white-space: nowrap; }
/* "Space flips" is a keyboard affordance: pointer-gated, not layout-gated
   (TABLE row 19). */
@media (pointer: coarse) { #flip-hint { display: none !important; } }
/* Compare's view (scenario_picker.js syncViewCtl): pills here, the native
   drop-down on a phone (mobile.css swaps them). */
#view-ctl { display: flex; flex: none; }
#view-ctl[hidden] { display: none; }
#view-select { display: none; }
.view-pills { display: inline-flex; border: 1px solid var(--n-cc); border-radius: 5px; overflow: hidden; }
.view-pill {
  margin: 0; padding: 2px 9px; border: 0; background: var(--n-ff);
  font: inherit; font-size: 11px; color: var(--n-55); cursor: pointer;
}
.view-pill + .view-pill { border-left: 1px solid var(--n-cc); }
.view-pill:hover:not(:disabled):not([aria-pressed="true"]) { background: var(--hover); }
.view-pill[aria-pressed="true"] { background: var(--sel-soft); color: var(--n-22); font-weight: 700; }
.view-pill:disabled { color: var(--n-bb); cursor: default; }
.view-pill:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
#flip-hint[hidden] { display: none; }
#flip-hint kbd {
  font: inherit; font-size: 9px; padding: 0 3px;
  border: 1px solid var(--n-cc); border-radius: 3px; background: var(--n-ff);
}
/* The side frame (D28): an inset border round the map, in the visible side's
   colour. Above the canvas, below MapLibre's corner controls (z 2, in the
   same stacking context) so it never paints over a button, and it takes no
   events. It stops at the dock's top edge (--sheet-h, both layouts). */
#flip-frame {
  position: absolute; inset: 0 0 var(--sheet-h, 0px); z-index: 1; pointer-events: none;
  border: 4px solid var(--side-b);
}
#flip-frame[data-side="a"] { border-color: var(--side-a); }
#flip-frame[hidden] { display: none; }
#scenario-chip-btn:hover #scenario-chip-text,
#scenario-chip-btn[aria-expanded="true"] #scenario-chip-text,
#scenario-chip-btn:hover .scenario-chip-caret,
#scenario-chip-btn[aria-expanded="true"] .scenario-chip-caret { color: var(--label-em); }

/* The menu shares #ind-menu's box (above). What is its own (S5 second round,
   PLAN §0i): the two segmented strips (mode, view), a column header naming the
   two compare sides, and -- per axis -- a RADIO
   MATRIX: one row per option, one radio column per side, both sides' picks
   visible at once. The footer carries two buttons rather than one, because the
   picks here are a DRAFT (S5-c) and there is something to cancel. */
/* --sm-lab / --sm-info / --sm-cell are the track widths the header row and
   every grid beneath it share, so a side word sits over the radios it names.
   The label track is CAPPED rather than 1fr (Taylor, 2026-09-22 -- "too much
   white space in the middle"): with 1fr the labels stretched to the menu's
   width and pushed the radio columns to the far edge, half a menu away from
   the words they answer -- worst in Explore, where one column sat alone at
   x=776 of an 838px menu. Capping the labels and parking the leftover in a
   trailing spacer track brings the columns back to the labels. The cap
   degrades gracefully: where the menu is narrower than the tracks want (the
   phone, edge to edge), the label track simply takes what is left, which is
   what 1fr gave it before. 420px, #profile-menu's width, now that the radio
   columns are 64px rather than 96 -- the fourth column no longer needs the
   extra 20. */
#scenario-menu {
  width: 420px; padding: 4px 0 0;
  --sm-lab: 190px; --sm-cell: 64px; --sm-info: 28px;
}
.sm-strip { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; padding: 8px 14px 6px; }
.sm-seg {
  flex: 0 1 auto; min-height: 32px; margin: 0; padding: 4px 12px;
  border: 1px solid var(--n-cc); border-radius: 999px; background: var(--n-ff);
  font: inherit; font-size: 13px; line-height: 20px; color: var(--n-22); cursor: pointer;
}
.sm-seg:hover:not(:disabled) { border-color: var(--sel-border); background: var(--sel-soft); }
.sm-seg[aria-pressed="true"] {
  font-weight: 600; color: var(--label-em); background: var(--sel-soft); border-color: var(--sel-border);
}
.sm-seg:disabled { color: var(--n-88); border-style: dashed; background: none; cursor: default; }
.sm-seg:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }

/* The column header: the same tracks as .sm-grid below. */
.sm-cols {
  display: grid;
  grid-template-columns:
    minmax(0, var(--sm-lab)) var(--sm-info) var(--sm-cell) var(--sm-cell) minmax(0, 1fr);
  align-items: center; padding: 6px 14px 2px;
}
/* The two words hug the boundary between their columns rather than centring
   on them, so the pair reads as one group -- REFERENCE  ALTERNATIVE. (A ⇄ swap
   sat in the gap until PLAN-each-view S1 removed it, D16; the padding that
   cleared it now just keeps the words apart.) Each still sits over the column
   it names -- its INNER edge is that column's inner edge -- but each SPANS
   outward past it: REFERENCE over the label and ⓘ tracks to its left,
   ALTERNATIVE over the trailing spacer to its right. The words are wider than
   a 64px radio column, and a word that overflows its track overflows to the
   RIGHT whatever `text-align` says (the line box starts at the content edge
   when there is no slack). Spanning gives each word the slack that makes its
   alignment mean something. */
.sm-col {
  position: relative;
  font-size: 10px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--n-6b); white-space: nowrap;
}
.sm-col-a { grid-column: 1 / 4; text-align: right; padding-right: 15px; }
.sm-col-b { grid-column: 4 / -1; text-align: left; padding-left: 15px; }

/* One block per axis, the #profile-menu .pm-dim rhythm. */
.sm-axis + .sm-axis, .sm-axis + .sm-strip, .sm-strip + .sm-axis, .sm-cols + .sm-axis {
  border-top: 1px solid var(--n-ee); margin-top: 6px;
}
.sm-head {
  display: flex; align-items: center; gap: 8px; padding: 8px 14px 4px;
  font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--n-6b);
}
.sm-head .sm-info { margin-left: auto; }
/* A description, PRINTED rather than hovered (a phone has no hover) -- the
   axis's own behind the heading's ⓘ, a scenario's behind its row's. */
.sm-gloss { padding: 0 14px 6px; color: var(--nc-556); }
.sm-gloss[hidden] { display: none; }
/* Inside the grid the block already carries the 14px gutter, and the gloss is
   indented under its own label rather than under the radio columns. */
.sm-grid > .sm-gloss { padding: 0 0 6px 0; }

/* The matrix. Every child is placed by explicit grid-row / grid-column from
   scenario_picker.js (its module header says why), so these tracks are the
   whole layout: label | ⓘ | one or two radio columns. */
.sm-grid {
  display: grid; align-items: center; padding: 0 14px 6px;
  grid-template-columns:
    minmax(0, var(--sm-lab)) var(--sm-info) var(--sm-cell) var(--sm-cell) minmax(0, 1fr);
}
.sm-grid[data-cols="1"] {
  grid-template-columns: minmax(0, var(--sm-lab)) var(--sm-info) var(--sm-cell) minmax(0, 1fr);
}
/* The radiogroups are per COLUMN; they draw no box of their own, so the cells
   they hold land on the coordinates above. */
.sm-group { display: contents; }
.sm-lab {
  font-size: 13px; line-height: 20px; color: var(--n-22);
  padding: 5px 8px 5px 0; min-height: 26px; display: flex; align-items: center;
}
.sm-info-gap { display: block; }
/* The radio: the whole cell is the target, the circle is 18px inside it. */
.sm-radio {
  display: flex; align-items: center; justify-content: center;
  margin: 0; padding: 0; min-height: 30px; border: 0; background: none;
  font: inherit; color: inherit; cursor: pointer;
}
.sm-radio::before {
  content: ""; display: block; box-sizing: border-box;
  width: 18px; height: 18px; border-radius: 50%;
  border: 1.5px solid var(--n-aa); background: var(--n-ff);
}
.sm-radio:hover:not([aria-disabled="true"])::before { border-color: var(--sel-border); background: var(--sel-soft); }
.sm-radio[aria-checked="true"]::before {
  border: 2px solid var(--sel-border);
  background: radial-gradient(circle, var(--sel) 0 5px, var(--n-ff) 5px);
}
/* A cell the study never ran, judged per side: dashed and inert in place, the
   reason as its title, rather than a select that silently reverts (what the
   panel's table did). */
.sm-radio.sm-off { cursor: default; }
.sm-radio.sm-off::before { border-style: dashed; border-color: var(--n-cc); background: none; }
.sm-radio:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; border-radius: 6px; }
.sm-note { padding: 0 14px 6px; color: var(--n-6b); }
/* Sticky, as #profile-menu's footer is: the menu scrolls inside its 60 vh cap
   and Apply must stay in view whatever the rows above are doing. */
.sm-foot {
  position: sticky; bottom: 0; z-index: 1; background: var(--ground-raised);
  display: flex; align-items: center; gap: 10px; padding: 8px 14px;
  border-top: 1px solid var(--n-ee); margin-top: 6px;
}
#sm-summary {
  flex: 1 1 auto; min-width: 0; font-size: 12px; line-height: 16px; color: var(--n-6b);
}
#sm-summary.sm-summary--none { color: var(--warn-text); }
#sm-cancel, #sm-apply {
  flex: none; margin: 0; padding: 4px 14px; min-height: 32px; border-radius: 999px;
  font: inherit; font-size: 13px; line-height: 20px; cursor: pointer;
}
#sm-cancel { border: 1px solid var(--n-cc); background: var(--n-ff); color: var(--n-44); }
#sm-cancel:hover { border-color: var(--sel-border); background: var(--sel-soft); color: var(--label-em); }
#sm-apply {
  border: 1px solid var(--sel-border); background: var(--sel-soft);
  font-weight: 600; color: var(--label-em);
}
#sm-apply:hover:not(:disabled) { background: var(--sel); color: var(--n-ff); }
/* Nothing to apply is not a button. */
#sm-apply:disabled { border-color: var(--n-dd); background: none; color: var(--n-aa); cursor: default; }
#sm-cancel:focus-visible, #sm-apply:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }

/* The settings panel's scenario picker is dev-only since PLAN-dock-controls S5
   (ruling S5-e's sibling to S3-e): the chip above is the reader's control, and
   the table survives as the second renderer the sweeps drive. Written as a
   `html:not(.dev)` rule rather than S3's `#sec-indicators` PAIR because the
   phone sheet re-declares `#scenario-picker { display: flex }` (applet-2's
   in-panel layout, mobile 273-335, left as it is): a plain
   `#scenario-picker { display: none }` here would lose to it on specificity
   ties, while this selector outranks both. initScenarioPicker's
   `style.display = ''` CLEARS the inline value rather than setting one, so the
   sheet still decides. Goes with the section at S6. */
html:not(.dev) #scenario-picker { display: none; }

/* The wrapper under the summary bar: holds the body at its own --mp-body-h
   height (the column flow fills by height). On the phone it is the sheet's
   one scroller. */
/* The body's tab strip (body_tabs.js), above the scroller: hidden while the
   mapped indicator has one tab. Overlapping trapezoid folder tabs (Taylor,
   2026-10-18; options in ives-notes applet-ui-cleanup/SKETCH-tab-polish):
   near-vertical sides, rounded top corners, a concave flare into the strip's
   rule at the foot, 11px overlap (Taylor, 2026-10-20, was 18). Inactive tabs are plate with a soft slate edge; the active tab,
   the same size, sits in front with a brand-slate edge, the body's fill and a
   4px gold bar on top (gold as mass, per the brand reference), its open
   bottom joining the body over the strip's rule. A CSS border doesn't follow a
   curved outline, so each tab draws two masked layers: ::before the edge,
   ::after the fill inset 1px. The mask is three pieces: a 14px SVG cap each
   side (flare, slope, round top; stretched to the tab's height) and a plain
   middle. Tracked caps per the plate-caption style. Not the view
   pills -- these switch what the body shows of one subject, not the map.
   Stacking cascades from the selected tab (G5, Taylor 2026-10-20): the
   chosen tab in front, each tab one step away from it (either side) one
   layer further back. body_tabs.js writes each tab's z-index inline (number
   of tabs - distance from the active tab) when it draws the strip; the
   stylesheet sets none. */
#mp-tabs {
  --tab-cap-l: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 25' preserveAspectRatio='none'%3E%3Cpath d='M0 25C2.5 25 3.2 24 3.6 22L6.6 4.5C7.1 1.8 8.6 0 11.5 0H14V25Z'/%3E%3C/svg%3E");
  --tab-cap-r: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 25' preserveAspectRatio='none'%3E%3Cpath d='M14 25C11.5 25 10.8 24 10.4 22L7.4 4.5C6.9 1.8 5.4 0 2.5 0H0V25Z'/%3E%3C/svg%3E");
  display: flex; flex: none; align-items: flex-end; gap: 0; padding: 6px 14px 0 18px;
  border-bottom: 1px solid var(--brand-slate);
}
#mp-tabs[hidden] { display: none; }
/* An OD row with no origin picked maps nothing: no placeholder scale, range
   or swatches in the legend (indicators.js refreshDisplay sets the class). */
#legend.leg-od-empty #leg-bar, #legend.leg-od-empty .leg-labels,
#legend.leg-od-empty #leg-modes, #legend.leg-od-empty #leg-cats,
#legend.leg-od-empty #leg-floor { display: none !important; }
/* A declared section (body_tabs.js showBodySections) is a wrapper for the
   sweep and the code, not a box: the body's own layout sees its content. */
.body-section { display: contents; }
.mp-tab {
  position: relative; margin: 0 -11px -1px 0;
  padding: 6px 24px 5px; border: 0; border-radius: 0; background: none;
  font: inherit; font-family: var(--font-ui); font-size: 11px; font-weight: 700;
  letter-spacing: .09em; text-transform: uppercase; color: var(--brand-slate); cursor: pointer;
}
.mp-tab::before, .mp-tab::after {
  content: ""; position: absolute; z-index: -1;
  -webkit-mask: var(--tab-cap-l) left / 14px 100% no-repeat, linear-gradient(#000, #000) center / calc(100% - 20px) 100% no-repeat,
    var(--tab-cap-r) right / 14px 100% no-repeat;
  mask: var(--tab-cap-l) left / 14px 100% no-repeat, linear-gradient(#000, #000) center / calc(100% - 20px) 100% no-repeat,
    var(--tab-cap-r) right / 14px 100% no-repeat;
}
.mp-tab::before { inset: 0 0 1px; background: var(--n-aa); }
.mp-tab::after { inset: 1px 1px 1px; background: var(--plate); }
.mp-tab:hover:not(.active) { color: var(--brand-ink); }
.mp-tab:hover:not(.active)::after { background: var(--gold-wash); }
.mp-tab.active { color: var(--brand-ink); cursor: default; }
.mp-tab.active::before { inset: 0; background: var(--brand-slate); }
.mp-tab.active::after { inset: 1px 1px 0; background: linear-gradient(var(--gold) 0 4px, var(--ground-raised) 4px); }
.mp-tab:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
#mp-scroll {
  /* Capped at the body's height: anything under the body scrolls here rather
     than growing the dock into the map. */
  flex: 0 0 auto; min-height: 0; max-height: var(--mp-body-h); overflow-y: auto;
  display: flex; flex-direction: column;
}
/* The filters (below the fold, beside the traveller class) and the legend's
   scaling controls: the mapped row's parameters, labelled, wrapping.
   `hidden` when the row takes none. */
#mp-params { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px var(--sb-clause-gap); flex: 0 1 auto; min-width: 0; }
#mp-params[hidden], #leg-params[hidden] { display: none; }
#leg-params { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; margin: 4px 0 6px; }
#leg-params .mp-param-label { font-weight: 400; }
.mp-param { display: flex; align-items: center; gap: 6px; margin: 0; font-size: 11px; }
.mp-param[hidden] { display: none; }
.mp-param-label { font-weight: 600; color: var(--n-66); white-space: nowrap; }
.mp-param .norm-toggle { margin: 0; }
.mp-param select {
  padding: 1px 4px; border: 1px solid var(--n-cc); border-radius: 3px;
  background: var(--n-ff); font: inherit; font-size: 11px; color: var(--n-44); cursor: pointer;
  max-width: 220px;
}
#mp-body {
  flex: 0 0 var(--mp-body-h); height: var(--mp-body-h); min-height: 0;
  /* Asymmetric bottom padding: at 180px the mode panel's formula line lands
     within ~2px of the fold, and a symmetric 8px was enough on its own to
     raise a 6px scrollbar on the common case. */
  padding: 6px 12px 4px;
  columns: 380px 3; column-gap: 22px; column-rule: 1px solid var(--n-ee);
  overflow-x: auto; overflow-y: auto;
}
#mp-body > * { break-inside: avoid; }
#mp-placeholder {
  flex: 0 0 var(--mp-body-h); height: var(--mp-body-h); display: none;
  align-items: center; justify-content: center;
  color: var(--n-99); font-size: 12px; text-align: center; padding: 0 20px;
}
#measurements.mp-empty #mp-body { display: none; }
#measurements.mp-empty #mp-placeholder { display: flex; }
#mp-body .pn-name { font-weight: 700; margin-bottom: 5px; color: var(--n-22); font-size: 13px; }
.pn-row { display: flex; justify-content: space-between; gap: 8px; padding: 1px 0; }
.pn-label { color: var(--n-66); }
.pn-val { font-weight: 600; color: var(--n-22); }
#mp-body .pn-row { display: flex; justify-content: space-between; gap: 8px; padding: 1px 0; }
#mp-body .pn-label { color: var(--n-66); }
#mp-body .pn-val { font-weight: 600; color: var(--n-22); }
.pn-dens-swatch {
  display: inline-block; width: 10px; height: 10px;
  border-radius: 2px; margin-right: 5px;
}
#mp-body .pn-section { margin-top: 5px; padding-top: 4px; border-top: 1px solid var(--n-ee); }
#mp-body .pn-mode-table { width: 100%; border-collapse: collapse; }
#mp-body .pn-mode-table th {
  text-align: right; color: var(--n-66); font-weight: 500; padding: 1px 4px 3px 4px;
  font-size: 10px; border-bottom: 1px solid var(--n-dd);
}
#mp-body .pn-mode-table th:first-child { text-align: left; }
#mp-body .pn-mode-table td { text-align: right; padding: 2px 4px; color: var(--n-44); font-size: 11px; }
#mp-body .pn-mode-table td:first-child { text-align: left; color: var(--n-55); }
#mp-body .pn-mode-table tr.pn-likeliest td { font-weight: 700; color: var(--n-11); }
/* The row the `pmode_{MODE}` choropleth is showing (SPEC-s22 §6). Deliberately
   a different channel from pn-likeliest's weight — the two rows are often
   different modes and must stay separately readable — so this is a rule on the
   row box plus an underline on the Share cell the map is actually painting. */
#mp-body .pn-mode-table tr.pn-mapped-mode td { box-shadow: inset 0 -2px 0 -1px var(--n-44); }
#mp-body .pn-mode-table tr.pn-mapped-mode td:first-child { box-shadow: inset 2px -2px 0 -1px var(--n-44); }
#mp-body .pn-mode-table td.pn-mapped-cell {
  font-weight: 700; color: var(--n-11); background: var(--shade-07);
}
/* `info-panel--wide` was the floating panel's 340px -> 560px widening. The
   measurements panel is always full-width, so the class is inert here; the
   JS still sets it and the rule is kept as the documented no-op. */
#mp-body.info-panel--wide { max-width: none; }
#mp-body .pn-util-purpose { font-weight: 600; color: var(--nc-445); font-size: 11px; }
/* .od-purpose (the labelled select block that hosted Trip purpose,
   Destination category and Destination type) is gone as of session 27; the
   selects are in the dock's #mp-params strip since PLAN-dock-controls S3. */
/* The mode table's totals row closes its table and keeps the border-top
   above. (The population panel's user-class cross-tab and its .pn-uc-* rules
   were retired in session 31 — that panel carries the profile tree, .pdc-tbl.) */
#mp-body .pn-mode-table tr.pn-total-row td {
  font-weight: 600; border-top: 1px solid var(--n-dd); color: var(--n-22);
}
/* The trips dock's bar(s): the City Snapshot's rows, in the table column. */
#mp-body .cum-bars { margin: 2px 0 6px; max-width: 520px; }
#mp-body .cum-bars .snap-mode-lbl { width: 64px; }

/* ---- Logsums utility breakdown ---- */
#mp-body .pn-util { margin-top: 6px; }
#mp-body .pn-util-head { display: flex; align-items: center; gap: 8px; }
#mp-body .pn-util-toggle {
  font-weight: 700; color: var(--nc-234); cursor: pointer; user-select: none; font-size: 12px;
}
#mp-body .pn-util-toggle:hover { color: var(--label-em); }
#mp-body .pn-util-group { font-size: 11px; padding: 1px 3px; }
#mp-body .pn-util-note { color: var(--n-66); font-size: 10px; margin: 3px 0; line-height: 1.35; }
/* The OD family's regional stand-in (info_panel.js renderOdRegionStandIn):
   the one line that says the dock below is the region's figure, not a pair's.
   A flex child of the stats body, so it never shrinks under the columns. */
#mp-body .pn-standin { flex: 0 0 auto; font-size: 10.5px; color: var(--n-55); margin: 0 0 4px; }
/* Utility decomposition table. Numeric cells must not wrap — a wrapped numeric
   cell doubles the row height and the panel is already the tallest thing on the
   map. Headers are the exception: they are glossed ("Long-walk penalty (kink)",
   not "kink"), which makes them roughly twice as wide as bare model jargon, and
   left nowrap that one header alone eats ~150px. Two header lines cost ~11px
   once; a nowrap header costs width on every row. The mode column and the
   mode-specific chip cell wrap too. */
/* The decomposition table spans the whole panel rather than flowing inside one
   380px column. It is eight columns wide by design and cannot be squeezed into
   a column of the width the surrounding prose blocks want; left in the flow it
   fragments, and its last rows land in a neighbouring column detached from the
   header that names them — an unreadable table that still looks like a table. */
#mp-body .pn-decomp { margin-top: 0; width: auto; column-span: all; }
#mp-body .pn-decomp td { white-space: nowrap; }
#mp-body .pn-decomp th { white-space: normal; vertical-align: bottom; }
#mp-body .pn-decomp td:first-child { white-space: normal; }
/* The inline factors that are not shared across modes. Ragged by construction —
   car under PARKING_CHOICE carries the most — so it wraps within a bounded
   width rather than pushing the table into horizontal scroll. */
#mp-body .pn-decomp td.pn-decomp-chips {
  white-space: normal; max-width: 300px; text-align: left; line-height: 1.5;
}
#mp-body .pn-chip {
  display: inline-block; margin: 0 3px 1px 0; padding: 0 4px;
  border-radius: 3px; background: var(--veil-62); color: var(--nc-334);
  font-size: 10px; white-space: nowrap; cursor: help;
}
/* The unexplained-residual tripwire. Styled as a defect, because that is what
   it is: on a complete build it never renders. */
#mp-body .pn-chip-warn {
  background: var(--warn-bg); color: var(--warn-text); border: 1px solid var(--warn-border);
}
/* ---- Mode-choice panel layout (table | model constants) ---- */
/* The column flow is off here: the two blocks are laid out explicitly, the
   table shrink-to-fit at its natural width and the constants taking what is
   left. `column-span: all` on .pn-decomp is inert under `columns: auto`. */
#mp-body.info-panel--mc { columns: auto; column-gap: normal; column-rule: none; }
.pn-mc-main { display: flex; gap: 22px; align-items: flex-start; }
.pn-mc-tbl { flex: 0 0 auto; }
.pn-mc-side { flex: 0 1 auto; min-width: 0; padding-top: 2px; }
#mp-body .pn-mc-side .pn-util-note { margin: 2px 0; }
/* The collapsed unavailable-mode row: one line for every mode the pair cannot
   use, grouped by reason. Left-aligned and wrapping, unlike every other cell
   in this table — it is a sentence, not a number. */
#mp-body .pn-mode-unavail td {
  text-align: left; white-space: normal; color: var(--n-88); font-style: italic;
  font-size: 10px; padding-top: 3px; border-top: 1px solid var(--n-ee);
}
#mp-body .pn-decomp-formula {
  border-top: 1px solid var(--n-ee); padding-top: 2px; margin-top: 1px; color: var(--nc-556);
}
#mp-body .pn-util-sub { font-weight: 600; color: var(--nc-445); font-size: 11px; margin-top: 5px; }
/* Destination-choice symbol · meaning · value rows */
#mp-body .pn-dc-tbl { margin-top: 2px; width: auto; }
#mp-body .pn-dc-tbl td { padding: 1px 6px 1px 0; font-size: 11px; }
#mp-body .pn-dc-tbl .pn-dc-sym { text-align: left; color: var(--nc-445); font-weight: 600; white-space: nowrap; }
#mp-body .pn-dc-tbl .pn-dc-meaning { text-align: left; color: var(--n-88); }
#mp-body .pn-dc-tbl .pn-dc-val { text-align: right; color: var(--n-22); white-space: nowrap; }
/* Operand cell: the arithmetic that produces the value, sitting just left of
   it — light, right-aligned, so each force stays one row and the value column
   stays the sum's spine. */
#mp-body .pn-dc-tbl .pn-dc-op { text-align: right; color: var(--n-99); white-space: nowrap; }
#mp-body .pn-util-tbl th { font-size: 10px; }
#mp-body .pn-util-tbl td { font-size: 11px; }

/* ---- Destination-choice panel (dc_* indicators; plan session 4) ---- */
/* Flex replaces the base multicol: this panel manages its own three regions,
   and the category table scrolls INSIDE its region rather than fragmenting
   into columns. Click-to-pin is what makes an in-panel scrollbar usable --
   once the pair is frozen the list stops swapping under the cursor. */
#mp-body.info-panel--dc {
  columns: auto; column-gap: normal;
  display: flex; flex-direction: column;
  /* The category list is this panel's own scroller, so the outer box does not
     scroll horizontally. It does scroll VERTICALLY as a safety valve: at the
     dock's 180px the fixed regions (head + forces column) land within a few
     px of the fold, and clipping the last force silently is worse than a
     scrollbar that engages only when they genuinely overrun. */
  overflow: hidden auto;
}
/* .dcp-head is gone — it restated the dock header's O -> D slots, and what it
   did not restate moved to #mp-context (renderDestChoicePanel). */
.dcp-cols { display: flex; gap: 22px; flex: 1 1 auto; min-height: 0; }
.dcp-col-forces { flex: 0 0 auto; }
/* The outputs sit beside the forces, not under them: stacked they were the
   127px that overran a 120px box. The rule marks the seam between "what the
   sum is made of" and "what the sum produces". Under ~1500px of dock they
   stack again (.dcp-narrow, set by the renderer) — there the width matters
   more than the height, and the height is no longer the binding constraint. */
.dcp-col-sum { display: flex; gap: 22px; flex: 0 0 auto; }
.dcp-col-out { flex: 0 0 auto; border-left: 1px solid var(--n-ee); padding-left: 16px; }
.dcp-cols.dcp-narrow .dcp-col-sum { flex-direction: column; gap: 0; }
.dcp-cols.dcp-narrow .dcp-col-out {
  border-left: none; padding-left: 0;
  margin-top: 4px; padding-top: 3px; border-top: 1px solid var(--n-ee);
}
.dcp-col-mid { flex: 0 0 auto; max-width: 350px; }
/* Takes the width the outputs column freed, so a two-up category list has
   somewhere to go. Still scrolls, as the fallback past two columns' worth. */
.dcp-col-cats { flex: 1 1 auto; min-width: 0; overflow-y: auto; padding-right: 4px; }
/* Tight gap on purpose: two-up runs ~7px over the column at 1700px with the
   18px the other seams use, and the two sub-tables are one list. */
.dcp-cat-cols { display: flex; gap: 12px; align-items: flex-start; }
/* ---- Compare-mode destination-choice panel (compare pass 2) ---- */
/* Five columns instead of the explore panel's four: symbol, meaning, A, B, Δ.
   The three numeric columns share .pn-dc-val, so only the header row and the
   two summing rows need rules of their own. */
#mp-body .pn-dc-cmp th {
  font-size: 10px; color: var(--n-88); font-weight: 600; text-align: right;
  padding: 0 6px 1px 0; white-space: nowrap;
}
#mp-body .pn-dc-cmp tr.pn-dc-strong td { font-weight: 700; color: var(--n-11); }
#mp-body .pn-dc-cmp tr.pn-dc-strong .pn-dc-meaning { font-weight: 400; color: var(--n-77); }
/* A band constant that MOVED between the two scenarios: the recalibration
   case a single ladder would otherwise misreport silently. */
.dcp-band-moved { border-color: var(--n-99); background: var(--moved-bg); }
.dcp-band-dlt { font-size: 9px; font-weight: 600; }

.dcp-ladder { display: flex; gap: 3px; }
.dcp-band {
  border: 1px solid var(--n-dd); border-radius: 3px; padding: 1px 6px;
  text-align: center; font-size: 10px; color: var(--nc-667); cursor: help;
}
.dcp-band-val { font-weight: 600; color: var(--nc-334); font-size: 11px; }
.dcp-band-cur { background: var(--sel-soft); border-color: var(--sel-border); }
.dcp-band-cur .dcp-band-val { color: var(--label-em); }
/* Sized to content, not stretched: `#mp-body .pn-mode-table { width: 100% }`
   out-specifies a bare `.dcp-cat-tbl`, and at 469px the four columns drifted
   so far apart the rows were hard to track across. */
#mp-body .dcp-cat-tbl { border-collapse: collapse; width: auto; }
/* Headers stay on one line: two-up, the narrower sub-table wrapped "Size here"
   and "Δ removed" onto a second line and stood taller than its twin. */
#mp-body .dcp-cat-tbl th { white-space: nowrap; }
/* Category labels too: content-sizing let "Shop" and its `map` tag split over
   two lines, which knocked the two-up sub-tables' rows out of alignment with
   each other. A long label widens the column instead of doubling a row. */
#mp-body .dcp-cat-tbl td:first-child { white-space: nowrap; }
/* Sticky header so the category list scrolls under its own labels. */
#mp-body .dcp-cat-tbl thead th { position: sticky; top: 0; background: var(--ground-trim); }
#mp-body .dcp-cat-tbl tr.dcp-cat-cur td { font-weight: 700; color: var(--n-11); background: var(--sel-soft); }

/* ---- Statistics panels (population / destination) ----
   Same shape as .dcp-cols: the column flow off, a leading column that
   decomposes the mapped number, and the rest of the width for the group's
   other indicators as one table. The multicol these panels used to ride ran
   the destination list across arbitrary breaks and the population panel's
   old cross-tab 343px off the right edge at 1280. */
#mp-body.info-panel--stats {
  columns: auto; column-gap: normal; column-rule: none;
  display: flex; flex-direction: column;
  /* The table column is this panel's own scroller, like the dc category list,
     so the outer box does not scroll horizontally. Vertically it still does,
     as the safety valve for a lead column that overruns 148px. */
  overflow: hidden auto;
}
/* Never shorter than its content (slice 2, 2026-10-19): shrinkable, the
   columns let a tall lead column (access Compare, pinned: ~143px in a 112px
   box) spill under the explainer row below it -- "What does this number
   mean?" printed over the last two stats. At its content height the body's
   own vertical scroll is the safety valve, as #mp-body.info-panel--stats
   intends; the phone sheet already sized it so (mobile.css). */
.stp-cols { display: flex; gap: 22px; flex: 1 0 auto; }
/* Shrinkable but not growable: the row labels set its natural width, and past
   ~300px it is only stretching a two-column key/value list. */
.stp-col-lead { flex: 0 1 300px; min-width: 0; }
/* Scrolls on BOTH axes, and it is the table's own adjacent scroller rather
   than #mp-body's off-screen fourth column — which is what made the old
   343px overflow silent. The population tree's compare twin is the widest
   table here (~1,000px at burlington), so a study with a wider roster gets a
   visible scrollbar right under the table instead of a clip. */
.stp-col-tbl { flex: 1 1 auto; min-width: 0; overflow: auto; padding-right: 4px; }
/* Overall Access puts TWO tables in the table column: purposes and modes are
   two families that share no columns. Side by side rather than stacked --
   #mp-body is 148px tall and stacked they want ~218, where side by side the
   taller (the six-row mode table, measured at 128px) still fits. Below the
   width where both fit, .stp-col-tbl's own adjacent scrollbar takes over,
   which is the visible failure mode the silent clip was not. Gap matches
   .dcp-cat-cols: the two tables are one region. */
#mp-body .cum-tbls { display: flex; gap: 12px; align-items: flex-start; }
/* The mode table's rows carry their mode's hue inline (info_panel.js). The
   cell background .dcp-cat-cur paints for the active row would sit on top of
   it, so here the highlight is carried by the row's own darker tint plus the
   bold text the rule already sets. */
#mp-body .cum-mode-tbl tr.dcp-cat-cur td { background: transparent; }

/* Compare Zone Summary (session 34). The lead column is a four-column A/B/Δ
   table rather than the explore panel's label/value pairs, so its label cell
   has to be allowed to wrap -- .pn-dc-val keeps the three numeric columns
   nowrap, and without this the labels ("This zone vs. region") force the whole
   lead column past its 300px basis and push the tables into the ladder for a
   reason that is not the tables' width. */
#mp-body .pn-cum-lead .pn-dc-meaning { white-space: normal; line-height: 1.25; }
#mp-body .pn-cum-lead td { vertical-align: top; }
/* A per-zone change below the noise floor (compare-floor S2): muted, no sign
   colour, like the zero it stands for. */
.cmp-nochange { color: var(--n-88); white-space: nowrap; }
/* Mode choice's Compare O→D lead (polish (k), 2026-10-02): with no tree
   beside it the 300px basis squeezed a four-column table into a fifth of
   the dock, its labels wrapping three and four lines. Sized to its content
   instead -- the A/B/Δ columns stay next to their labels rather than
   stretching to the dock's far edge -- with short one-line labels (the long
   forms are in the row titles). */
.stp-col-lead.stp-col-lead--fit { flex: 0 0 auto; max-width: 620px; }
#mp-body .pn-msplit-cmp { width: auto; }
#mp-body .pn-msplit-cmp .pn-dc-meaning { white-space: nowrap; padding-right: 12px; }
#mp-body .pn-msplit-cmp td.pn-dc-val { padding-left: 10px; }
/* The uncalibrated-NHB marker, rendered as a row rather than a title= for the
   same reason the explore panel renders it: it is why the row is safe to show. */
#mp-body tr.pn-cum-warn td { color: var(--cum-warn); font-size: 10px; }

.dcp-map-tag {
  display: inline-block; font-size: 9px; font-weight: 600;
  color: var(--on-accent); background: var(--sel); border-radius: 3px;
  padding: 0 3px; margin-left: 3px; vertical-align: middle;
}

/* ---- Legend ---- */
/* Top-left of the map (G2, Taylor 2026-10-20), the phone's corner, and the
   page's head since G7: its first row is the page head (lockup | Connectome |
   Map ▾), its last the Display fold. The max-height keeps the legend above
   the dock at any dock height (--sheet-h, the dock's cover). The width is
   FIXED (G7, ruled at about 248px): a legend sized to its content changed
   width with every title and every fold, and the page menu at its right
   edge jumped with it. Long titles wrap. The phone keeps its own placement,
   width and 70vh (mobile sheet). */
#legend {
  position: absolute; top: 10px; left: 10px;
  background: var(--veil-ground-94); border: 1px solid var(--n-cc);
  border-radius: 6px; padding: 8px 10px; z-index: 900;
  font-size: 11px; width: 248px;
  box-shadow: 0 2px 6px var(--shade-15);
  max-height: calc(100% - var(--sheet-h, 0px) - 20px); overflow-y: auto;
}
#legend .leg-title { font-weight: 700; margin-bottom: 5px; color: var(--n-33); font-size: 11px; }
#legend #leg-compare-view {
  font-size: 10px; color: var(--n-55); font-style: italic;
  margin: -3px 0 5px;
}
/* Flip and fold notices only, both layouts (TABLE row 28, Taylor 2026-10-18). */
#legend #leg-compare-view:not(.leg-flip):not(.leg-notice) { display: none !important; }
#leg-advanced-body { margin-top: 4px; }
#leg-scale-row {
  display: flex; align-items: center; gap: 6px; margin-bottom: 6px;
}
#leg-scale-row label { font-size: 11px; font-weight: 600; color: var(--label-em); }
/* The strip reuses .norm-toggle / .norm-btn so it reads as the same kind of
   control as the per-km²/total pills, but it is never auto-sized away from
   the legend's left edge the way those are inside the sub-control column. */
#leg-scale-toggle { margin: 0; }
#leg-scale-toggle .norm-btn:disabled {
  background: var(--n-f4); color: var(--n-bb); cursor: not-allowed;
}
#leg-opacity-row, #leg-height-row { margin-bottom: 6px; }
#leg-opacity-row label, #leg-height-row label {
  font-size: 11px; font-weight: 600; color: var(--label-em);
}
#leg-opacity-row #leg-opacity-slider,
#leg-height-row #height-slider {
  width: 100%; margin-top: 3px; cursor: pointer;
}
#leg-cap-slider-row {
  display: flex; align-items: center; gap: 4px;
  font-size: 10px; color: var(--n-55);
}
#leg-cap-slider-row label { white-space: nowrap; }
#leg-cap-slider { flex: 1; min-width: 60px; cursor: pointer; }
#leg-cap-value {
  font-variant-numeric: tabular-nums;
  width: 48px; text-align: right; color: var(--n-33);
  font-size: 10px; padding: 1px 2px;
  border: 1px solid var(--n-cc); border-radius: 2px;
  background: var(--n-ff);
}
#leg-cap-value:focus { outline: none; border-color: var(--focus); }
#leg-cap-unit { color: var(--n-55); font-size: 10px; }
.leg-cap-reset {
  background: none; border: none; padding: 0; cursor: pointer;
  font-size: 10px; color: var(--n-88); text-decoration: underline;
}
.leg-cap-reset:hover { color: var(--n-55); }
#legend .leg-bar {
  height: 13px; border-radius: 3px; margin-bottom: 4px;
  background: linear-gradient(to right, var(--n-cc), var(--n-cc));
}
#legend .leg-labels { display: flex; justify-content: space-between; color: var(--n-55); }
#legend .leg-divider { border-top: 1px solid var(--n-dd); margin: 5px 0 4px; }
#legend .leg-modes-row {
  display: flex; flex-wrap: wrap;
  gap: 4px 10px;
  padding: 1px 0;
}
#legend .leg-mode-chip {
  display: inline-flex; align-items: center; gap: 5px;
}
#legend .leg-mode-swatch {
  display: inline-block; width: 12px; height: 12px;
  border-radius: 2px; flex-shrink: 0;
}
#legend .leg-mode-label { color: var(--n-44); }
/* Categorical legend rows (density classes) */
#legend .leg-cat-row {
  display: flex; align-items: center; gap: 5px;
  padding: 1px 0;
}
#legend .leg-cat-swatch {
  display: inline-block; width: 12px; height: 12px;
  border-radius: 2px; flex-shrink: 0;
}
#legend .leg-cat-label { color: var(--n-44); }
#legend .leg-cat-range { color: var(--n-88); margin-left: auto; }
#legend .leg-cat-absent { opacity: 0.4; }
#legend .leg-cat-note { color: var(--n-88); font-size: 10px; margin-top: 3px; }
/* Noise-floor band (compare-floor S2): the swatch is the paper itself, so it
   carries an edge to read against the legend's own ground. */
#legend #leg-floor { margin-top: 4px; max-width: 220px; }
#legend .leg-floor-swatch { border: 1px solid var(--n-bb); box-sizing: border-box; }

/* The 5-column `.cum-grid` table (Total / Per km² / Per person / % x one row
   per mode) and its `.ind-btn-sm` dots are gone (SPEC-s22 S4): the table's
   two axes are now the normalization strips (§5.3) and the shared mode chip
   row (§5.1), so the cumulative group is plain `.ind-btn` rows like every
   other group. */

/* ---- Modal-mix bars (mode_bars.js; the names are City Snapshot's, which is gone) ---- */
.snap-tooltip {
  position: absolute; background: var(--n-33); color: var(--on-dark); font-size: 10px;
  padding: 3px 6px; border-radius: 3px; pointer-events: none;
  display: none; white-space: nowrap; z-index: 10;
}
.snap-mode-rows { margin-bottom: 4px; position: relative; }
.snap-mode-row  { display:flex; align-items:center; gap:4px; margin-bottom:2px; }
.snap-mode-lbl  { font-size:9px; color:var(--n-88); width:88px; flex-shrink:0;
                  text-align:right; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.snapshot-modal-bar {
  display: flex;
  height: 22px;
  border-radius: 4px;
  overflow: hidden;
  margin-bottom: 4px;
}
.snapshot-modal-bar div {
  transition: width 300ms ease;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 600;
  color: var(--on-dark);
  overflow: hidden;
  white-space: nowrap;
}
/* ---- The legend's Display fold (G7; "Map Overlays" until then) ---- */
/* Under the ramp, a rule above it, collapsed at every load (map.js). Its body,
   in order: the Map | Satellite pair, zone opacity, the overlay rows, and the
   Advanced checkbox at the foot with Units and the Advanced drawer under it. */
#legend-overlays { border-top: 1px solid var(--n-dd); margin: 7px 0 0; padding: 5px 0 0; }
.leg-section-title {
  cursor: pointer; font-size: 11px; font-weight: 700; color: var(--n-55);
  padding: 0; user-select: none;
}
.leg-section-title:hover { color: var(--n-33); }
.leg-section-title:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; border-radius: 2px; }
.collapse-arrow { font-size: 10px; color: var(--n-99); margin-left: 4px; }
#legend-overlays-body { padding-top: 4px; }
#legend-overlays-body #leg-opacity-row { margin: 0 0 6px; }
#legend-overlays-body .adv-toggle { margin: 6px 0 0; padding-top: 6px; border-top: 1px solid var(--n-ee); }
#legend-overlays-body #units-row { margin: 6px 0 0; }
#legend-overlays-body #leg-advanced { margin: 6px 0 0; }
/* The Map | Satellite pair (basemap.js mountImageryToggle; G4's single
   preview tile until G7): two tiles, each a picture of the view it gives,
   its label over a dark foot so it reads on imagery and parchment alike. The
   active one carries a 2px gold ring and nothing else (no check mark; ruled
   on the G7 sketch) -- the selected-state gold of rung 4, as a shape around
   a picture rather than a fill. The pictures are background images the
   module sets once the fold first shows; until then (or if one fails) a
   tile is plain ground. 64x48 on a desktop; the phone's are 72x54. */
.legend-imagery[hidden] { display: none; }
.imagery-pair { display: flex; gap: 8px; padding: 0 0 8px; }
.imagery-tile {
  position: relative; display: block; width: 64px; height: 48px; margin: 0; padding: 0;
  border: 0; border-radius: 6px; overflow: hidden; cursor: pointer;
  background: var(--n-dd); box-shadow: 0 0 0 1px var(--n-cc);
}
.imagery-tile:hover { box-shadow: 0 0 0 1px var(--brand-slate), 0 1px 4px var(--shade-15); }
.imagery-tile[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--gold), 0 1px 4px var(--shade-15); }
.imagery-tile:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
.imagery-thumb { position: absolute; inset: 0; background: center / cover no-repeat; }
.imagery-label {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 9px 2px 3px;
  background: linear-gradient(transparent, var(--shade-62));
  color: var(--on-dark); font-size: 10.5px; font-weight: 600; line-height: 1; text-align: center;
}
.imagery-tile[aria-pressed="true"] .imagery-label { font-weight: 800; }

/* ---- 3D toggle button ---- */
/* The legend's 3D button is the state's source (map.js setup3DToggle) but no
   longer a control: 2D/3D has one home on both layouts, the #ctrl-3d map
   control sheets.js relays to it (TABLE row 23). */
#btn-3d-toggle { display: none !important; }
#ctrl-3d button { font-size: 11px; font-weight: 700; color: var(--n-33); display: flex; align-items: center; justify-content: center; }
#ctrl-3d button:disabled { color: var(--n-bb); }

/* MapLibre zoom control margin */
.maplibregl-ctrl-top-right { margin-top: 10px; }

/* MapLibre's own furniture on the map -- the control group and the attribution
   bar -- ships white in maplibre-gl.css. They are chrome the reader sees inside
   our applet, so they take the same grounds as the legend beside them: the
   raised ground for the buttons, the raised veil for the attribution (which sits
   over the basemap and needs to stay translucent). Cartography pass, 2026-09-12. */
.maplibregl-ctrl-group { background: var(--ground-raised); }
.maplibregl-ctrl-attrib,
.maplibregl-ctrl-attrib.maplibregl-compact { background-color: var(--veil-ground-94); }
.maplibregl-ctrl-attrib a { color: var(--brand-slate); }

/* ---- Scenario picker ---- */
#scenario-picker {
  border-bottom: 1px solid var(--n-dd);
  padding: 7px 10px 8px;
  background: var(--ground-band);
}
.picker-label {
  font-size: 10px; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--n-77); display: block; margin-bottom: 4px;
}
#scenario-picker select {
  width: 100%; padding: 4px 6px; border: 1px solid var(--n-cc);
  border-radius: 4px; background: var(--n-ff); font-size: 12px; cursor: pointer;
  text-overflow: ellipsis; overflow: hidden; white-space: nowrap;
  min-width: 0;
}

/* ---- Unified scenario table (axes + flat-scenario, Explore + Compare) ----
   CSS grid; axis-name column is capped + wraps so the dropdown columns get
   the bulk of the panel width. */
.scenario-table {
  display: grid;
  gap: 4px 6px;
  align-items: center;
  margin-top: 4px;
}
.scenario-table-explore {
  grid-template-columns: minmax(0, 110px) minmax(0, 1fr);
}
.scenario-table-compare {
  grid-template-columns: minmax(0, 110px) minmax(0, 1fr) minmax(0, 1fr);
}
.scenario-table .st-cell {
  min-width: 0;
}
.scenario-table .st-header {
  font-size: 9px; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--n-77);
  text-align: center;
}
.scenario-table .st-header.st-axis-name { text-align: left; }
.scenario-table .st-axis-name {
  font-size: 11px; color: var(--n-55); font-weight: 600;
  line-height: 1.15;
  padding-right: 4px;
  word-wrap: break-word;
}
.scenario-table .st-header.st-ref {
  position: relative;
}
.scenario-table select {
  width: 100%; padding: 3px 4px; border: 1px solid var(--n-cc);
  border-radius: 4px; background: var(--n-ff); font-size: 10px; cursor: pointer;
  text-overflow: ellipsis; overflow: hidden; white-space: nowrap;
  min-width: 0;
}
.scenario-table select option:disabled { color: var(--n-bb); }

/* ---- Explore/Compare toggle strip ---- */
.mode-toggle-strip {
  display: flex; border: 1px solid var(--n-cc); border-radius: 4px;
  overflow: hidden;
}
.scenario-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 4px;
}
.mode-btn {
  flex: 1; padding: 4px 8px; border: none;
  background: var(--n-ff); font-size: 11px; cursor: pointer; color: var(--n-55);
  font-weight: 600;
}
.mode-btn:hover { background: var(--hover); }
.mode-btn.active { background: var(--sel); color: var(--on-accent); }


/* ---- The aggregate dock's per-profile decomposition (PLAN-s37 S4) ----
   The map's number is a mixture over ~100 traveller profiles; this table is
   what it is a mixture OF, collapsed income → car ownership → real class. It
   rides the statistics panels' two-column shape (.stp-cols), so the tree gets
   the table column's own scroller — 100 leaves will not fit a 148px dock and
   the scroller is the visible failure mode a clip would not be. */
#mp-body .pdc-tbl { border-collapse: collapse; width: auto; }
#mp-body .pdc-tbl th { white-space: nowrap; font-weight: 600; }
#mp-body .pdc-tbl thead th { position: sticky; top: 0; background: var(--ground-trim); z-index: 1; }
#mp-body .pdc-tbl td { white-space: nowrap; }
/* The label column is the SECOND cell (the radio is the first), so the
   mode-table rule that right-aligns everything after the first cell was
   pushing the tree's names to the right and hiding the depth indent that is
   the tree's whole shape (found on the trips dock's copy, session 29). */
#mp-body .pdc-tbl thead tr:first-child th:nth-child(2),
#mp-body .pdc-tbl td:nth-child(2) { text-align: left; }
/* The one interactive control in the dock: a branch row toggles its children.
   Leaves are inert, and say so by not changing the cursor. */
#mp-body .pdc-tbl tr.pdc-open { cursor: pointer; }
#mp-body .pdc-tbl tr.pdc-open:hover td { background: var(--hover); }
#mp-body .pdc-tbl tr.pdc-total td {
  color: var(--n-11); border-bottom: 1px solid var(--n-bb);
}
/* The access tree in Compare (slice 5, the flat Per-profile change table
   folded in): a row whose gain share is under half its population share is
   tinted, the flat table's rule; the checked row's gold below still wins.
   The "Per period" header sorts the tree by that column. */
#mp-body .pdc-tbl tr.pdc-behind td { background: var(--neg-tint); }
#mp-body .pdc-tbl tr.pdc-behind.pdc-open:hover td { background: var(--neg-tint-2); }
#mp-body .pdc-tbl th.pdc-sort { cursor: pointer; text-decoration: underline dotted; text-underline-offset: 2px; }
#mp-body .pdc-tbl th.pdc-sort:hover, #mp-body .pdc-tbl th.pdc-sort:focus-visible { background: var(--hover); }
#mp-body .pdc-tbl th.pdc-sorted { color: var(--n-11); }
/* ---- The population selector (DESIGN-uc-radios, 2026-09-12) ----
   One radio per visible row, Total included: the tree is a control now, not
   only a readout. Its own narrow column, because selecting a row and expanding
   it are independent gestures and a shared hit target would make one of them
   unreachable. `line-height: 0` on the cell keeps a 13px control from setting
   the row height taller than the 9px mode bar beside it already does. */
#mp-body .pdc-tbl th.pdc-radio, #mp-body .pdc-tbl td.pdc-radio {
  width: 18px; padding: 0 3px; text-align: center; line-height: 0;
}
#mp-body .pdc-tbl .pdc-radio-in {
  margin: 0; cursor: pointer; vertical-align: middle;
  width: 12px; height: 12px;
}
/* The checked row is the population the map, the legend and the lead columns
   are showing, so it reads as selected in the brand's one selected idiom:
   a gold fill with the label's own ink on it (rung 4 — gold is a shape with
   mass, never text). Colour only, no weight: in this table bold belongs to
   the most likely mode of each row, and neither the checked row nor the
   Total row may take it (Taylor, 2026-09-14; supersedes the 2026-09-12
   "both" ruling). After the hover rule above, so a hovered checked row stays
   gold rather than dropping to the neutral hover wash — the two are the same
   family, and the selected state is the stronger statement. */
#mp-body .pdc-tbl tr.pdc-checked td { background: var(--sel-soft); }
#mp-body .pdc-tbl tr.pdc-checked:hover td { background: var(--sel-soft-hover); }
/* A COLLAPSED branch the checked row is folded inside. D1 says visibility is
   the reader's gesture, so the tree does not open itself to reveal the
   selection — but an unmarked fold would leave "no radio is checked" meaning
   two different things (it is hidden / the selection has no row at all). The
   expander triangle goes gold; a whole gold row would claim the branch IS the
   selection, and it only contains it. */
#mp-body .pdc-tbl tr.pdc-anc .pdc-exp { color: var(--gold-deep); }
.pdc-exp { display: inline-block; width: 11px; color: var(--nc-789); font-size: 9px; }
.pdc-exp--leaf { color: transparent; }
/* Member count / profile count — present but never competing with the label. */
.pdc-n { color: var(--nc-aab); font-size: 9px; }
.pdc-nodata { color: var(--n-bb); font-size: 10px; }
/* Stacked mode-share bar. Fixed width so the rows read as a column of
   comparable bars rather than a ragged edge; the numbers are in the title. */
.pdc-bar {
  display: inline-flex; width: 84px; height: 9px; vertical-align: middle;
  border-radius: 2px; overflow: hidden; background: var(--n-f0);
}
.pdc-bar i { display: block; height: 100%; }
.pdc-hint, .pdc-note { font-size: 10px; color: var(--n-88); line-height: 1.35; }
.pdc-hint { max-width: 320px; }
.pdc-note { margin-top: 4px; }

/* ---- ...and its compare twin: one tree, A | B | Δ per measure ----
   Same rows on both sides, so the two scenarios are merged into one table
   rather than juxtaposed — see `renderCompareProfileDecomp` for why. That buys
   row alignment and costs two header rows, which is the only thing here that
   needs its own CSS: the base rule sticks every `thead th` at `top: 0`, and
   with two rows the second one has to be offset by the first one's height.

   Measured against the scroller at three scroll positions, not guessed
   (`s45_compare_decomp_shot.mjs` prints it): the group row is 15.5px tall, so
   the second row pins at 15 and the two overlap by half a pixel. Deliberately
   a hair under rather than over — an overlap hides nothing, where a gap would
   show a sliver of a scrolling body row between the two header rows.

   ⚠ When re-measuring: "Who travels", "Mode split" and "Most likely" are
   `rowspan=2`, so the FIRST cell of the group row has the height of BOTH rows.
   Reading it instead of a `colspan` cell reports a 16px overlap that is not
   there, and sends you looking for a sticky bug that does not exist — it did,
   twice, while this was being built. The sticky is on the cells, like the
   explore twin's; it does not need to be on the rows. */
#mp-body .pdc-cmp thead tr.pdc-grp th { top: 0; }
#mp-body .pdc-cmp thead tr.pdc-sub th { top: 15px; }
#mp-body .pdc-cmp thead tr.pdc-grp th[colspan] {
  text-align: center; border-bottom: 1px solid var(--n-e8);
}
/* The sub-header names a side, not a quantity, so it reads quieter than the
   group label above it. */
#mp-body .pdc-cmp thead tr.pdc-sub th { color: var(--nc-667); font-weight: 500; }
/* One hairline at the end of each measure group: with twelve columns the eye
   needs to know where "Share of A→B" stops and "P(d|o)" starts. */
#mp-body .pdc-cmp .pdc-dcol { border-right: 1px solid var(--n-ee); padding-right: 6px; }
/* A and B's mode bars stacked in one cell. Two bars rather than two columns:
   a Δ of a stacked bar is not a stacked bar, and this costs one row of height
   instead of ~170px of width in a table that is already twelve columns wide. */
.pdc-bars {
  display: grid; grid-template-columns: auto auto;
  gap: 1px 3px; align-items: center;
}
.pdc-bars-k { color: var(--nc-aab); font-size: 8px; line-height: 1; }
/* Half-height bars so a two-bar cell is not a two-line row. */
.pdc-bars .pdc-bar { height: 7px; }
/* A disagreement between the rows and the baked average they decompose is a
   defect, not a display state — it is loud on purpose. */
.pdc-warn {
  font-size: 10px; line-height: 1.35; margin-top: 4px;
  color: var(--warn-text-2); background: var(--warn-bg-2); border: 1px solid var(--warn-border-2);
  border-radius: 3px; padding: 3px 5px; max-width: 380px;
}

/* ---- The level roster's chip row (PROPOSAL-level-roster §4) ----
   Below the table, and quiet: this is a control over the VIEW, and the table
   above it is the thing being read. So it takes the note's ink and size rather
   than the label column's, and the only thing at full strength is the criterion
   name the reader is aiming at.

   The buttons are 20px square — the dock's touch target for a control that is
   not a row — around an 11px glyph, so the hit area is bigger than the mark.
   A disabled end button keeps its space rather than collapsing: the chips would
   otherwise change width as they moved past the ends, and a row of controls
   that reflows under your finger is the phone bug this workstream keeps
   finding. */
#mp-body .pdc-levels {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px;
  margin-top: 8px; padding-top: 6px; border-top: 1px solid var(--n-e3);
  font-size: 10px; color: var(--n-88);
}
#mp-body .pdc-levels-cap { margin-right: 2px; }
#mp-body .pdc-chip {
  display: inline-flex; align-items: center;
  border: 1px solid var(--n-ee); border-radius: 3px; background: var(--n-f0);
}
#mp-body .pdc-chip-name {
  padding: 0 2px; font-size: 10px; color: var(--n-33); white-space: nowrap;
}
#mp-body .pdc-chip-btn {
  width: 20px; height: 20px; padding: 0; line-height: 1;
  border: 0; background: none; cursor: pointer;
  font-size: 11px; color: var(--nc-789);
}
#mp-body .pdc-chip-btn:hover:not(:disabled) { color: var(--n-11); background: var(--hover); }
/* Spacing kept, affordance withdrawn -- see the reflow note above. */
#mp-body .pdc-chip-btn:disabled { color: var(--n-ee); cursor: default; }
/* One value in this scope: the criterion has collapsed out of the tree, so the
   chip reads as inactive while staying movable. */
#mp-body .pdc-chip--flat { opacity: 0.55; }

/* ---- The why-table under the tree (Taylor, 2026-09-15) ----
   Same scroller as the tree, below it: the reader scrolls down to it at
   180px. A rule and a caption mark the switch from WHO to WHY. The single
   class's table is `.pn-decomp` as the retired mode panel rendered it; the
   mixture's is the same class with fewer columns. Its thead is NOT sticky —
   two sticky headers in one scroller would stack. */
#mp-body .pdc-why { margin-top: 10px; padding-top: 6px; border-top: 1px solid var(--n-e3); }
#mp-body .pdc-why-cap { font-size: 11px; color: var(--n-33); margin-bottom: 3px; }
#mp-body .pdc-why .pn-decomp thead th { position: static; }
#mp-body .pdc-why .pn-decomp { width: auto; }

/* Plain-language explainer for the deviation-painted access rows (s58/s59):
   rendered in the dock rather than hover-only, so phone readers get it too.
   Muted like the util note, a step larger for readability, capped so it stays
   a caption and never competes with the numbers. A one-row disclosure that is
   the LAST thing in the dock, zone or region alike (Taylor, 2026-09-18) —
   below the lead, the tables and the tree, so the numbers are never pushed
   apart by it. The dock body is a flex column, so the toggle row is always in
   view and the columns above it take what is left. */
#mp-body .pn-explain, #mp-placeholder .pn-explain {
  color: var(--n-66); font-size: 10.5px; line-height: 1.4;
  margin: 6px 0 2px; max-width: 60em;
  border-top: 1px solid var(--n-dd); padding-top: 5px;
}
#mp-body .pn-explain, #mp-body .pn-explain-toggle { flex: 0 0 auto; }
#mp-placeholder .pn-explain {
  text-align: left; margin: 10px auto 0; border-top: none;
  max-width: 46em; color: var(--n-77); font-size: 11px;
}
#mp-body .pn-explain-toggle {
  color: var(--n-77); font-size: 10.5px; margin-top: 6px;
  cursor: pointer; user-select: none;
  border-top: 1px solid var(--n-dd); padding-top: 5px;
}
#mp-body .pn-explain-toggle:hover { color: var(--n-44); }
/* Under the toggle the body needs no second hairline. */
#mp-body .pn-explain-toggle + .pn-explain { border-top: none; padding-top: 0; margin-top: 3px; }

/* The traveller chip on a row that takes no class (D1, app.js syncSubControls). */
#mp-profile.mp-profile--row-off { display: none !important; }
