* { box-sizing: border-box; }
:root {
  --ground-l: .161;
  --chrome-flip: clamp(0, calc((var(--ground-l) - .40) * 1000), 1);
  --chrome-surface: oklab(
    calc((1 - var(--chrome-flip)) * (.10 + var(--ground-l) * .10)
       + var(--chrome-flip) * (.93 + (var(--ground-l) - .40) * .03))
    .004 -.012 / .93);
  --chrome-text: oklab(calc(.97 - var(--chrome-flip) * .87) 0 0);
  --chrome-edge: color-mix(in oklab, var(--chrome-text) 22%, transparent);
  --chrome-radius: 12px;
  --chrome-blur: 6px;
  --rail-flip: var(--chrome-flip);
  --rail-surface: var(--chrome-surface);
  --rail-text: var(--chrome-text);
}
html, body { margin: 0; height: 100%; overflow: hidden; }
body { font: 15px/1.45 system-ui, sans-serif; color: #e8e8ec; background: #101420; }

#map { position: fixed; inset: 0; width: 100%; height: 100%; display: block; background: #101420; }
/* #overlay draws the static district-boundary outline (Task 6) in a plain 2D
   canvas above #map, never intercepting input — mirrors #map's positioning
   exactly (same stacking context) minus the background (transparent so #map
   shows through). */
#overlay { position: fixed; inset: 0; width: 100%; height: 100%; display: block; pointer-events: none; }

/* ---------- bottom control bar ---------- */
#chrome { position: fixed; left: 10px; right: 10px; bottom: 10px; z-index: 6; padding: 10px 16px 14px;
  background: var(--chrome-surface); color: var(--chrome-text);
  border: 1px solid var(--chrome-edge); border-radius: var(--chrome-radius);
  backdrop-filter: blur(var(--chrome-blur)); display: flex; flex-direction: column; gap: 8px; }

#city-picker { display: flex; gap: 6px; flex-wrap: wrap; }
#aoi-picker { display: flex; gap: 6px; flex-wrap: wrap; }
#city-picker button, #aoi-picker button, #play-pause, #transport button {
  padding: 6px 14px; border: 1px solid var(--chrome-edge); background: var(--chrome-surface); color: var(--chrome-text);
  border-radius: 999px; cursor: pointer; font: inherit; font-size: 13px; transition: all .15s;
}
#city-picker button.active, #aoi-picker button.active {
  background: #6fd3e6; color: #061014; border-color: #6fd3e6; font-weight: 600;
}
#transport { display: inline-flex; gap: 6px; align-items: center; }
#play-pause { min-width: 40px; text-align: center; }
#clock { font-variant-numeric: tabular-nums; font-size: 15px; color: var(--chrome-text); }
#clock-daypart { min-height: 1.15em; color: var(--chrome-text); font-size: 12px; font-weight: 650; letter-spacing: .04em; }
/* 1x whisper: a quiet "next ripple · 12s" line under the clock (spec §4) */
#whisper { font-size: 12px; color: color-mix(in oklab, var(--chrome-text) 72%, transparent); min-height: 1.2em; font-variant-numeric: tabular-nums; }

#timeline { position: relative; width: 100%; padding-top: 22px; }
#scrubber { width: 100%; margin: 0; background: transparent;
  -webkit-appearance: none; appearance: none; height: 14px; cursor: pointer; }
#scrubber::-webkit-slider-runnable-track {
  height: 4px; border-radius: 999px;
  background: color-mix(in oklab, var(--chrome-text) 22%, transparent); }
#scrubber::-moz-range-track {
  height: 4px; border-radius: 999px;
  background: color-mix(in oklab, var(--chrome-text) 22%, transparent); }
#scrubber::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; width: 13px; height: 13px;
  border-radius: 50%; background: #6fd3e6; border: none; margin-top: -4.5px; }
#scrubber::-moz-range-thumb {
  width: 13px; height: 13px; border-radius: 50%; background: #6fd3e6; border: none; }
#daypart-markers { position: absolute; inset: 0 8px auto; height: 24px; }
.daypart-marker { position: absolute; left: calc(var(--pos) * 100%); transform: translateX(-50%); width: 22px; height: 22px; padding: 0; border: 1px solid rgba(111,211,230,.72); border-radius: 50%; background: var(--chrome-surface); color: var(--chrome-text); box-shadow: 0 1px 5px rgba(0,0,0,.55); font: 12px/1 system-ui; cursor: pointer; }
.daypart-marker:focus-visible { outline: 2px solid var(--chrome-text); outline-offset: 2px; }
.daypart-marker:disabled { opacity: .32; border-color: var(--chrome-edge); color: var(--chrome-text); cursor: not-allowed; }

/* ---------- export row ----------
   Lives inside #chrome (so hideChrome takes it out of its own capture) and
   borrows the pill language of #transport's buttons so it reads as part of the
   same bar rather than a bolted-on tool. The command itself is quiet, tabular
   and selectable — it is reference text, not a control. */
#export-row { display: flex; gap: 8px; align-items: center;
  justify-content: center; flex-wrap: wrap; }
#export-png, #capture-cmd-copy {
  padding: 5px 12px; border: 1px solid var(--chrome-edge); background: var(--chrome-surface);
  color: var(--chrome-text); border-radius: 999px; cursor: pointer; font: inherit;
  font-size: 12px; transition: all .15s; white-space: nowrap;
}
#export-png:hover, #capture-cmd-copy:hover { border-color: #6fd3e6; color: #fff; }
#export-png:focus-visible, #capture-cmd-copy:focus-visible,
#capture-cmd:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
#export-png:disabled { opacity: .5; cursor: default; border-color: var(--chrome-edge); }
#capture-cmd {
  font: 11px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--chrome-text); background: var(--chrome-surface);
  border: 1px solid var(--chrome-edge); border-radius: 6px; padding: 3px 8px;
  user-select: all; max-width: min(56ch, 60vw);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

#status { font: 11px monospace; color: #5b6270; }

/* City-switch loading indicator. The old city keeps animating during a switch
   by design (app.js:270-273 -- teardown-then-fetch would black-screen on a
   network failure), so without this a switch is indistinguishable from a hang. */
#loading {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  padding: 0.6rem 1.1rem; border-radius: 999px;
  background: rgba(0, 0, 0, 0.72); color: #fff;
  font: 500 0.85rem/1 system-ui, sans-serif; letter-spacing: 0.02em;
  pointer-events: none; z-index: 20;
}
#loading[hidden] { display: none; }

/* Quiet, always-visible re-entry into the guided tour (Task 4 v2.1): a
   small circular chip, easy to ignore, easy to find. */
#help-btn {
  position: fixed; right: 14px; bottom: 14px; z-index: 7;
  width: 28px; height: 28px; padding: 0; border-radius: 50%;
  border: 1px solid var(--chrome-edge); background: var(--chrome-surface); color: var(--chrome-text);
  font: 13px/1 system-ui, sans-serif; font-weight: 600; cursor: pointer;
  opacity: .55; transition: opacity .15s, background .15s;
}
#help-btn:hover, #help-btn:focus-visible { opacity: 1; background: var(--chrome-surface); }
#help-btn:focus-visible { outline: 2px solid var(--chrome-text); outline-offset: 2px; }

/* Credits opener — same chip language as #help-btn, parked just to its left.
   (This slot previously held #posters-link, which pointed at a posters.html
   that does not exist in this project; it would have 404'd once published.) */
#credits-btn {
  position: fixed; right: 50px; bottom: 14px; z-index: 7;
  width: 28px; height: 28px; padding: 0; border-radius: 50%;
  border: 1px solid var(--chrome-edge); background: var(--chrome-surface); color: var(--chrome-text);
  font: 13px/1 system-ui, sans-serif; font-weight: 600; text-align: center;
  cursor: pointer; opacity: .55; transition: opacity .15s, background .15s;
}
#credits-btn:hover, #credits-btn:focus-visible { opacity: 1; background: var(--chrome-surface); }
#credits-btn:focus-visible { outline: 2px solid var(--chrome-text); outline-offset: 2px; }

#fullscreen-btn {
  position: fixed; right: 86px; bottom: 14px; z-index: 7;
  width: 28px; height: 28px; padding: 0; border-radius: 50%;
  border: 1px solid var(--chrome-edge); background: var(--chrome-surface); color: var(--chrome-text);
  font: 13px/1 system-ui, sans-serif; text-align: center; cursor: pointer;
  opacity: .55; transition: opacity .15s, background .15s;
}
#fullscreen-btn:hover, #fullscreen-btn:focus-visible { opacity: 1; background: var(--chrome-surface); }
#fullscreen-btn:focus-visible { outline: 2px solid var(--chrome-text); outline-offset: 2px; }

/* #utility groups the three chips so narrow viewports can lay them out as one
   row (see the max-width:430px block). display:contents makes the wrapper
   generate no box here, so each button keeps the position:fixed corner
   placement above and desktop rendering is byte-identical to before. */
#utility { display: contents; }

#transport { justify-content: center; }
#speed-readout { min-width: 3.2em; text-align: center; color: var(--chrome-text);
  font-variant-numeric: tabular-nums; font-size: 13px; }

/* ---------- unified left control rail ---------- */
#control-rail {
  /* The rail follows the ground so it never reads as debris stuck to a bright
     map. Both tracks are DELIBERATELY steep and OPPOSED: the surface rises
     with the ground while the text falls, so the pair crosses over and the
     rail flips from "light on dark" to "dark on light" as the sun comes up.
     The first attempt ramped the surface only .12 -> .275 while the text got
     LIGHTER, which left a near-black slab on a #c0d3e8 noon map -- visibly
     the same bug the redesign was meant to fix.
     Surface stays just off the ground's own lightness (a hair darker at night,
     a hair lighter than mid by day) so the rail always separates from the map;
     text is pinned to the opposite end, keeping >= 4.5:1 at every --ground-l. */
  /* The rail follows the ground so it never reads as debris stuck to a bright
     map -- but it FLIPS between two regimes rather than ramping through the
     middle. Two failed attempts, both measured with
     tools/check_rail_contrast.mjs, are why:
       1. Surface ramping .12 -> .275 while text got LIGHTER left a near-black
          slab on the #c0d3e8 noon map -- the very bug this redesign targets.
       2. Opposed LINEAR ramps (surface up, text down) CROSS: at --ground-l .52
          the text was exactly the surface, 1.00:1, invisible mid-morning.
          (Same crossing failure as the mode palette's sunrise collapse --
          see sun-palette-spec.md. A ramp through mid-lightness cannot work:
          nothing contrasts against mid grey, so the best flat-ramp solution
          tops out at 4.38:1.)
     So BOTH surface and text switch at one threshold, and each ramps only
     WITHIN its own regime -- the surface is never mid-lightness. Dark regime:
     light text on a dark panel, as today. Light regime: dark text on a pale
     panel. Worst case across the whole range is 16.7:1. */
  position: fixed; left: 10px; bottom: calc(var(--chrome-clear, 178px) + 8px);
  z-index: 6; width: min(172px, calc(100vw - 12px)); padding: 10px;
  display: flex; flex-direction: column; gap: 12px; overflow-y: auto;
  max-height: calc(100vh - var(--chrome-clear, 178px) - 16px);
  background: var(--chrome-surface); color: var(--chrome-text);
  border: 1px solid var(--chrome-edge); border-radius: var(--chrome-radius);
  backdrop-filter: blur(var(--chrome-blur));
}
#control-rail[hidden] { display: none !important; }
#mode-rail, #sun-rail, #view-rail, #mode-filter {
  position: static; width: 100%; min-width: 0; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 5px;
  background: none; border: 0; border-radius: 0; transform: none;
}
#mode-rail[hidden], #sun-rail[hidden], #view-rail[hidden], #mode-filter[hidden] { display: none !important; }
.rail-label {
  font: 11px/1 monospace; letter-spacing: .1em; text-transform: uppercase;
  color: color-mix(in oklab, var(--rail-text) 72%, transparent); margin-bottom: 1px;
}
.rail-btn {
  position: relative; width: 100%; height: 32px; min-width: 0; padding: 0 9px;
  display: flex; align-items: center; gap: 6px; overflow: hidden;
  border: 1px solid color-mix(in oklab, var(--rail-text) 22%, transparent);
  border-radius: 7px; background: color-mix(in oklab, var(--rail-text) 6%, transparent);
  color: inherit; cursor: pointer; font: 12px/1 system-ui, sans-serif;
  text-align: left; white-space: nowrap; transition: background .15s, border-color .15s;
}
.rail-btn:hover:not([disabled]) { border-color: color-mix(in oklab, var(--rail-text) 48%, transparent); }
.rail-btn.active, .rail-btn[aria-pressed="true"] { background: color-mix(in oklab, #6fd3e6 13%, transparent); font-weight: 600; }
.rail-btn.active::before, .rail-btn[aria-pressed="true"]::before {
  content: ""; position: absolute; inset: 4px auto 4px 0; width: 2px;
  border-radius: 0 2px 2px 0; background: #6fd3e6;
}
.rail-btn:focus-visible { outline: 2px solid var(--rail-text); outline-offset: 2px; }
#sun-seasons { display: grid; grid-template-columns: repeat(2, 1fr); gap: 4px; }
#sun-rail[data-sun="off"] #sun-seasons { opacity: .38; pointer-events: none; }
#view-rail:empty { display: none !important; }
#mode-filter .swatch { width: 10px; height: 10px; border-radius: 2px; background: var(--swatch); flex: none; }
#mode-filter .rail-btn[aria-pressed="false"] { opacity: .38; }
#clock, #clock-daypart, #whisper { text-align: center; }

/* Idle-hide: in fullscreen the chrome gets out of the way and returns on move. */
body.chrome-hidden #chrome,
body.chrome-hidden #fullscreen-btn,
body.chrome-hidden #help-btn,
body.chrome-hidden #credits-btn,
body.chrome-hidden #district-panel,
body.chrome-hidden #control-rail { opacity: 0; pointer-events: none; transition: opacity .4s; }
/* ---------- intro overlay + click-stepper ----------
   Bremer/Visual Cinnamon pattern: quiet, semi-transparent dark panels,
   generous whitespace, one idea per screen, click-to-advance only. */
#intro, #stepper, #credits {
  position: fixed; inset: 0; z-index: 10; display: flex; align-items: center;
  justify-content: center; padding: 24px;
  background: radial-gradient(ellipse at center, #10142000 0%, #060810b3 75%);
}
/* Above #intro's z-index so credits stay reachable from the intro card too. */
#credits { z-index: 11; }
/* The [hidden] UA-stylesheet rule (display:none, specificity 0,0,1,0 via
   attribute selector) loses to the ID selector above (specificity 0,1,0,0),
   so setting the `hidden` IDL property alone never visually hides these
   overlays. Force it explicitly, higher-specificity than the base rule. */
#intro[hidden], #stepper[hidden], #chrome[hidden], #credits[hidden] { display: none !important; }
#stepper { align-items: flex-end; justify-content: center; padding-bottom: 96px; background: none; pointer-events: none; }
#intro-card, #stepper-card, #credits-card {
  background: var(--chrome-surface); color: var(--chrome-text);
  border: 1px solid var(--chrome-edge); border-radius: var(--chrome-radius);
  padding: 28px 32px; max-width: 480px; width: 100%;
  box-shadow: 0 12px 40px rgba(0,0,0,.28);
  backdrop-filter: blur(var(--chrome-blur));
}
/* ---------- credits / attribution dialog ----------
   Same panel language as the intro card. Kept scrollable because the three
   attributions plus the disclaimer can exceed a short viewport. */
#credits-card { max-height: 80vh; overflow-y: auto; }
#credits-title { margin: 0 0 16px; font-size: 18px; font-weight: 600; color: var(--chrome-text); }
#credits-card dl { margin: 0 0 16px; }
#credits-card dt {
  color: color-mix(in oklab, var(--chrome-text) 62%, transparent); font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .08em; margin-top: 14px;
}
#credits-card dt:first-child { margin-top: 0; }
#credits-card dd { margin: 4px 0 0; color: var(--chrome-text); font-size: 13px; line-height: 1.5; }
#credits-card a { color: #6fd3e6; text-decoration: none; border-bottom: 1px solid #6fd3e644; }
#credits-card a:hover { border-bottom-color: #6fd3e6; }
#credits-card .disclaimer {
  margin: 0 0 18px; padding-top: 14px; border-top: 1px solid var(--chrome-edge);
  color: color-mix(in oklab, var(--chrome-text) 72%, transparent); font-size: 12px; line-height: 1.5;
}
#credits-close {
  padding: 8px 20px; border: 1px solid var(--chrome-edge); background: transparent; color: var(--chrome-text);
  border-radius: 999px; cursor: pointer; font: inherit; font-size: 13px; transition: all .15s;
}
#credits-close:hover { background: color-mix(in oklab, var(--chrome-text) 8%, transparent); }
#credits-close:focus-visible { outline: 2px solid var(--chrome-text); outline-offset: 2px; }
#stepper-card { pointer-events: auto; max-width: 420px; text-align: center; }
#intro-title { margin: 0 0 12px; font-size: 22px; font-weight: 600; color: var(--chrome-text); letter-spacing: .01em; }
#intro-card p, #step-caption { margin: 0 0 18px; color: var(--chrome-text); font-size: 15px; line-height: 1.5; }
#step-caption { margin-bottom: 16px; min-height: 3em; }
/* Per-city coverage line on the intro card (NOT a permanent chrome row —
   #chrome already stacks seven elements). Selector is #intro-card-qualified so
   it outranks the `#intro-card p` margin rule above; :empty hides it entirely
   when a city has no coverage_note, so no stray gap appears. */
#intro-card #coverage-note { font-size: 12px; color: color-mix(in oklab, var(--chrome-text) 62%, transparent); margin: 8px 0 0; }
#intro-card #coverage-note:empty { display: none; }

#intro-begin, #step-next, #step-explore {
  padding: 9px 22px; border: 1px solid #6fd3e6; background: #6fd3e6; color: #061014;
  border-radius: 999px; cursor: pointer; font: inherit; font-size: 14px; font-weight: 600;
  transition: all .15s;
}
#intro-begin:hover, #step-next:hover, #step-explore:hover { background: #8fe0f0; }
#intro-begin:focus-visible, #step-next:focus-visible, #step-explore:focus-visible,
#play-pause:focus-visible, #aoi-picker button:focus-visible,
#transport button:focus-visible {
  outline: 2px solid #fff; outline-offset: 2px;
}

#stepper-counter { font: 12px/1 monospace; letter-spacing: .08em; text-transform: uppercase;
  color: #6fd3e6; margin-bottom: 10px; }
#stepper-actions { display: flex; gap: 10px; justify-content: center; }
#step-explore { background: transparent; color: #6fd3e6; }
#step-explore:hover { background: rgba(111,211,230,.12); }

/* ---------- v2.2 right-side district panel ---------- */
#district-panel { position: fixed; top: 10px; right: 10px; bottom: 10px; z-index: 6;
  display: flex; align-items: center; pointer-events: none;
  background: var(--chrome-surface); color: var(--chrome-text);
  border: 1px solid var(--chrome-edge); border-radius: var(--chrome-radius);
  backdrop-filter: blur(var(--chrome-blur)); }
#district-panel[hidden] { display: none !important; }
#dp-tab { pointer-events: auto; writing-mode: vertical-rl; padding: 14px 6px;
  border: 1px solid var(--chrome-edge); border-radius: var(--chrome-radius);
  background: var(--chrome-surface); color: var(--chrome-text); cursor: pointer;
  font: 12px/1 system-ui, sans-serif; letter-spacing: .12em; text-transform: uppercase;
  opacity: .6; transition: opacity .15s; }
#dp-tab:hover, #district-panel.open #dp-tab { opacity: 1; }
#dp-body { pointer-events: auto; width: 260px; max-height: 78vh; overflow-y: auto;
  background: var(--chrome-surface); color: var(--chrome-text); border: 1px solid var(--chrome-edge);
  border-radius: var(--chrome-radius); padding: 10px 8px; backdrop-filter: blur(var(--chrome-blur)); }
#dp-body[hidden] { display: none; }
.dp-city { font: 11px/1 monospace; letter-spacing: .1em; text-transform: uppercase;
  color: #6fd3e6; margin: 10px 6px 4px; }
.dp-city:first-child { margin-top: 2px; }
#dp-list button { display: block; width: 100%; text-align: left; padding: 5px 10px;
  border: none; border-radius: 7px; background: transparent; color: var(--chrome-text);
  cursor: pointer; font: inherit; font-size: 13px; }
#dp-list button.dp-child { padding-left: 24px; font-size: 12px; color: color-mix(in oklab, var(--chrome-text) 72%, transparent); }
#dp-list button:hover { background: rgba(111,211,230,.10); color: var(--chrome-text); }
#dp-list button.active { background: rgba(111,211,230,.18); color: #6fd3e6; }
#dp-list button:focus-visible { outline: 2px solid #fff; outline-offset: -2px; }

/* The country row sits ABOVE the city chips and is deliberately quieter than
   them: it filters the list below, it does not navigate. Smaller, uppercase and
   un-filled so the eye still lands on the city chips first. */
#dp-countries { display: flex; flex-wrap: wrap; gap: 4px; margin: 2px 6px 6px;
  padding-bottom: 6px; border-bottom: 1px solid var(--chrome-edge); }
#dp-countries[hidden] { display: none; }
.dp-country { padding: 3px 8px; border: 1px solid var(--chrome-edge); border-radius: 999px;
  background: transparent; color: var(--chrome-text); cursor: pointer; font: inherit;
  font-size: 11px; letter-spacing: .04em; }
.dp-country:hover { color: var(--chrome-text); background: rgba(111,211,230,.08); }
.dp-country.active { color: var(--chrome-text); font-weight: 600;
  border-color: color-mix(in oklab, #6fd3e6 55%, var(--chrome-edge));
  background: color-mix(in oklab, #6fd3e6 22%, transparent); }
.dp-country:focus-visible { outline: 2px solid #6fd3e6; outline-offset: -1px; }

#dp-cities { display: flex; flex-wrap: wrap; gap: 6px; margin: 2px 6px 8px; }
.dp-cityswitch { padding: 4px 10px; border: 1px solid var(--chrome-edge); border-radius: 999px;
  background: var(--chrome-surface); color: var(--chrome-text); cursor: pointer; font: inherit;
  font-size: 12px; }
.dp-cityswitch.active { background: #6fd3e6; color: #061014; border-color: #6fd3e6;
  font-weight: 600; }
#dp-search { width: calc(100% - 12px); margin: 0 6px 8px; padding: 6px 10px;
  border: 1px solid var(--chrome-edge); border-radius: 8px; background: var(--chrome-surface);
  color: var(--chrome-text); font: inherit; font-size: 13px; }
#dp-search:focus-visible { outline: 2px solid #6fd3e6; outline-offset: -1px; }
.dp-empty { padding: 8px 10px; color: var(--chrome-text); font-size: 12px; }

/* ---------- Deserts: the per-subarea ranking ----------
   Shown inside #dp-body only while Deserts mode is active (app.js's
   renderDesertPanel adds and removes the whole #dp-deserts block), so it needs
   no hidden state of its own.

   It reports rather than navigates, so it is deliberately quieter than the
   place rows above it: no hover, no cursor, no focus ring, nothing that
   suggests it is clickable. Same rationale as .dp-country being quieter than
   .dp-cityswitch — the eye should land on what it can act on first.

   The numbers are tabular-figure monospace so the percent column stays aligned
   down four rows (Helsinki's case); a proportional font would let 32.0% and
   3.8% wander and turn a ranking into a list. */
#dp-deserts { margin-top: 10px; padding-top: 8px; border-top: 1px solid #222833; }
.dp-desert-row { display: grid; grid-template-columns: 1fr auto; column-gap: 8px;
  align-items: baseline; padding: 4px 10px; font-size: 12px;
  color: color-mix(in oklab, var(--chrome-text) 72%, transparent); }
.dp-desert-name { color: var(--chrome-text); overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
/* The percentage is the ranked quantity, so it is the one bright thing here --
   warm grey, matching the plate's own #8a8578 rather than the cyan the
   navigation uses, so panel and map read as one statement. */
.dp-desert-pct { color: #b3ac9c; font-variant-numeric: tabular-nums;
  font-family: ui-monospace, monospace; }
/* desert / total cells: the denominator, quietest of the three, spanning both
   columns on its own line so it never competes with the percentage. */
.dp-desert-cells { grid-column: 1 / -1; color: #6d7480; font-size: 11px;
  font-variant-numeric: tabular-nums; font-family: ui-monospace, monospace; }

@media (max-width: 640px) {
  /* bottom sheet: tab sits above the chrome bar; body slides up full-width */
  #district-panel { top: auto; right: 10px; left: 10px; bottom: 10px; align-items: flex-end;
    flex-direction: column; z-index: 8; }
  #dp-tab { writing-mode: horizontal-tb; align-self: flex-end;
    margin: 0 14px 148px 0; border-radius: var(--chrome-radius); }
  #dp-body { width: 100%; max-height: 46vh; border-radius: var(--chrome-radius); }
}

/* ---------- MOBILE ---------- */
@media (max-width: 640px) {
  #chrome { padding: 8px 10px 12px; }
  #aoi-picker { justify-content: center; }
  #aoi-picker button, #play-pause, #transport button { padding: 5px 10px; font-size: 12px; }
  #clock { text-align: center; }
  #intro-card, #stepper-card { padding: 20px 20px; }
  #intro-title { font-size: 19px; }
  #stepper { padding-bottom: 140px; }

  #control-rail { bottom: calc(var(--chrome-clear, 254px) + 8px); max-height: calc(100vh - var(--chrome-clear, 254px) - 16px); }
}

/* ---------- NARROW PHONES ----------
   The three utility chips (help / credits / fullscreen) are position:fixed at
   bottom:14px, right 14/50/86px, so they own the rightmost ~100px of the same
   vertical band the centred #transport row occupies. On a wide viewport there
   is clearance; at 390px the transport row grows into that band and the "+15m"
   button collides with the fullscreen chip.

   Rather than shrink everything further, stop making two layers compete for one
   row: below 430px the utility chips leave the fixed layer and become a normal
   centred row INSIDE #chrome, above the transport controls. One column, no
   overlap, and nothing has to be hunted for in a corner. */
@media (max-width: 430px) {
  /* The wrapper becomes a real flex ROW so the three chips share one line.
     (display:contents on desktop means this rule is the only place #utility
     ever behaves as a box.) */
  #utility {
    display: flex; justify-content: center; align-items: center; gap: 14px;
    order: 6;                /* last in #chrome's column: clock, whisper,
                                scrubber, transport, mode toggle, then this row */
    margin-top: 2px;
  }
  #help-btn, #credits-btn, #fullscreen-btn {
    position: static;        /* leave the fixed layer that caused the overlap */
    opacity: .75;            /* no longer floating over the map, so lift them
                                out of the near-invisible .55 resting state */
    width: 34px; height: 34px;   /* 28px is a small touch target; these are now
                                    primary controls, not corner affordances */
  }

  #transport { order: 4; flex-wrap: wrap; gap: 5px; }
  #transport button, #play-pause { padding: 6px 9px; font-size: 12px; }

  /* The mode switcher is no longer a #chrome flex child (it is the fixed
     left-edge #mode-rail), so the `order: 5` slot it used to occupy here is
     gone. #utility keeps order 6 — the values need not be contiguous, and
     renumbering it would be churn for no layout change. */

  #control-rail { width: min(156px, calc(100vw - 12px)); padding: 8px; gap: 9px; }

  /* The panel tab's 148px bottom margin was cleared for the OLD chrome height;
     the utility row adds ~40px, so lift it to keep the tab clear of the bar. */
  #dp-tab { margin-bottom: 190px; }
  #stepper { padding-bottom: 180px; }
}

/* ---------- dwell: media-player chrome ----------
   Two states per surface, switched by data-dwell (set by app.js from
   dwell.js). Deliberately an attribute on EACH surface, not a body class:
   surfaces sleep independently, so approaching the rail must not wake the
   bottom bar.

   Dormant is SMALL, not GONE. What is currently selected stays readable;
   only the unchosen options retract. (body.chrome-hidden, above, is the
   different, older "blank it for fullscreen" behaviour.)

   Dormant also sits at a LOWER ELEVATION -- thinner edge, less blur. Before
   this, the 172px rail and a 22px daypart pip rendered at identical weight,
   so nothing receded and everything competed. */
#control-rail, #chrome {
  transition: width .18s ease, max-height .18s ease, opacity .18s ease,
              backdrop-filter .18s ease, border-color .18s ease;
}

#control-rail[data-dwell="dormant"] {
  width: 44px;
  border-color: color-mix(in oklab, var(--chrome-text) 12%, transparent);
  backdrop-filter: blur(calc(var(--chrome-blur) * .5));
}
/* Retract everything except the ACTIVE choice in each group. */
#control-rail[data-dwell="dormant"] .rail-label,
#control-rail[data-dwell="dormant"] .rail-btn:not(.active):not([aria-pressed="true"]) {
  display: none;
}
/* Transport collapses to swatches: the colour IS the information. */
#control-rail[data-dwell="dormant"] #mode-filter .mr-label,
#control-rail[data-dwell="dormant"] #mode-filter .rail-btn > span:not(.swatch) {
  display: none;
}

#chrome[data-dwell="dormant"] {
  left: 50%; right: auto; transform: translateX(-50%);
  width: max-content; max-width: calc(100vw - 20px);
  padding: 6px 14px;
  border-color: color-mix(in oklab, var(--chrome-text) 12%, transparent);
  backdrop-filter: blur(calc(var(--chrome-blur) * .5));
  /* The base rule is `flex-direction: column`, which is exactly why dormant
     used to still render as a 3-row card (daypart label row, clock row,
     scrubber row) even with every OTHER child hidden -- hiding children
     shrinks a column, it does not make one out of it. Row + center is what
     actually produces "one line". */
  flex-direction: row; align-items: center; gap: 10px;
}
/* One line: daypart + clock + scrubber. Everything else waits for awake,
   including #utility (help/credits/fullscreen) -- on narrow viewports
   #utility is a real row (see the max-width:430px block), not the desktop
   display:contents no-op, so leaving it visible here was adding a whole
   extra row to what was supposed to be the single-line state. */
#chrome[data-dwell="dormant"] #transport,
#chrome[data-dwell="dormant"] #whisper,
#chrome[data-dwell="dormant"] #daypart-markers,
#chrome[data-dwell="dormant"] #export-row,
#chrome[data-dwell="dormant"] #utility { display: none; }
#chrome[data-dwell="dormant"] #clock-daypart {
  min-height: 0; white-space: nowrap;
}
#chrome[data-dwell="dormant"] #clock { white-space: nowrap; }
#chrome[data-dwell="dormant"] #timeline {
  /* padding-top:22px in the base rule reserves room for #daypart-markers,
     which is hidden here -- keeping the padding would leave a blank gutter
     above a scrubber that has nothing to clear. */
  padding-top: 0; width: min(46vw, 460px);
}

@media (prefers-reduced-motion: reduce) {
  #control-rail, #chrome { transition: opacity .18s ease; }
}
