/*
 * citysim viewer — UI design system (viewer/DESIGN-UI.md).
 *
 * Order: token layer, base, then one block per component. Everything the map
 * itself draws (zone colours, ramps, congestion tint) stays in palette.ts —
 * only the chrome lives here.
 */

/* ---------------------------------------------------------------- 1. tokens */
/* 2026-09-25 (DESIGN.md "Flat map"): the map is always dark slate, so the chrome is
   one dark palette in both themes — the prototype's panels (#1b1f29), warm amber
   accent, the map's own green/amber/red for good/warn/bad, no glass blur. */

:root {
  color-scheme: dark;

  --font-ui:
    -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display", Inter, "Segoe UI", Roboto,
    system-ui, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  --fs-11: 11px;
  --lh-11: 14px;
  --fs-12: 12px;
  --lh-12: 16px;
  --fs-13: 13px;
  --lh-13: 18px;
  --fs-15: 15px;
  --lh-15: 20px;
  --fs-17: 17px;
  --lh-17: 22px;
  --fs-22: 22px;
  --lh-22: 26px;
  --fs-28: 28px;
  --lh-28: 34px;
  --fw-regular: 400;
  --fw-medium: 500;
  --fw-semibold: 600;
  --tracking-tight: -0.01em;
  --tracking-wide: 0.005em;

  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;
  --sp-7: 48px;

  --r-xs: 6px;
  --r-sm: 8px;
  --r-md: 14px;
  --r-lg: 18px;
  --r-pill: 999px;

  --h-badge: 18px;
  --h-sm: 24px;
  --h-md: 28px;
  --h-lg: 32px;
  --h-rail: 40px;
  --h-bar: 48px;

  --w-inspector: 360px;
  --w-rail: 56px;
  --w-cards: 280px;
  --w-build: 328px;

  /* Materials — light. The map's own ground is always dark (`BG` in palette.ts),
     so light glass composites *down*; these alphas are the point where 13 px
     body text and the 11 px tertiary line still clear 4.5:1 over it. */
  --mat-chrome: rgba(27, 31, 41, 0.95);
  --mat-panel: rgba(27, 31, 41, 0.95);
  --mat-sheet: rgba(31, 35, 46, 0.98);
  /* what the same surfaces become with no blur to hide behind */
  --mat-chrome-solid: #1b1f29;
  --mat-panel-solid: #1b1f29;
  --blur-chrome: none;
  --blur-panel: none;
  --hairline: rgba(255, 255, 255, 0.08);
  --hairline-strong: rgba(255, 255, 255, 0.16);
  --sheen: inset 0 1px 0 rgba(255, 255, 255, 0.05);
  --shadow-1: 0 2px 4px rgba(0, 0, 0, 0.25), 0 8px 24px rgba(0, 0, 0, 0.35);
  --shadow-2: 0 4px 10px rgba(0, 0, 0, 0.3), 0 20px 48px rgba(0, 0, 0, 0.45);
  --scrim: rgba(10, 12, 18, 0.5);

  /* colour — light */
  --fg: #e9ecf2;
  --fg-2: #b3bac8;
  --fg-3: #8f98ab; /* the 4.5:1 floor over glass on the dark map */
  --fg-4: #5b6272; /* decorative only: never text */
  --fill-1: rgba(255, 255, 255, 0.05);
  --fill-2: rgba(255, 255, 255, 0.09);
  --fill-3: rgba(255, 255, 255, 0.15);
  /* white on this blue clears 4.5:1; #007AFF does not */
  --accent: #f2b45c;
  --accent-fg: #1b1f29;
  --accent-soft: rgba(242, 180, 92, 0.2);
  --good: #5cf07e;
  --warn: #f5b942;
  --bad: #ff5a52;
  --capital: #c07ae0;

  /* news kinds — light values re-tuned to clear 4.5:1 on the composited panel */
  --kind-traffic: #ff7a6e;
  --kind-economy: #f5b942;
  --kind-people: #6ea8fe;
  --kind-build: #8ee6a8;
  --kind-policy: #c58fe0;
  --kind-alert: #ff5a52;

  /* chart series — same hues as the map keeps, darkened for a light ground */
  --chart-blue: #6ea8fe;
  --chart-amber: #f5b942;
  --chart-red: #ff7a6e;
  --chart-green: #8ee6a8;
  --chart-purple: #c58fe0;
  --chart-teal: #3fc1c9;
  --chart-orange: #ec8a4f;
  --chart-grey: #8f98ab;

  --dur-1: 150ms;
  --dur-2: 200ms;
  --dur-3: 250ms;
  --ease-out: cubic-bezier(0.32, 0.72, 0, 1);
  --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
}

/* materials + colour — dark. Repeated for the media query and the manual
   toggle so each one wins in its own direction (the toggle is stored by
   theme.ts as data-theme on <html>). */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --mat-chrome: rgba(27, 31, 41, 0.95);
    --mat-panel: rgba(27, 31, 41, 0.95);
    --mat-sheet: rgba(31, 35, 46, 0.98);
    --mat-chrome-solid: #1b1f29;
    --mat-panel-solid: #1b1f29;
    --hairline: rgba(255, 255, 255, 0.08);
    --hairline-strong: rgba(255, 255, 255, 0.16);
    --sheen: inset 0 1px 0 rgba(255, 255, 255, 0.05);
    --shadow-1: 0 2px 4px rgba(0, 0, 0, 0.25), 0 8px 24px rgba(0, 0, 0, 0.35);
    --shadow-2: 0 4px 10px rgba(0, 0, 0, 0.3), 0 20px 48px rgba(0, 0, 0, 0.45);
    --scrim: rgba(10, 12, 18, 0.5);
    --fg: #e9ecf2;
    --fg-2: #b3bac8;
    --fg-3: #8f98ab;
    --fg-4: #5b6272;
    --fill-1: rgba(255, 255, 255, 0.05);
    --fill-2: rgba(255, 255, 255, 0.09);
    --fill-3: rgba(255, 255, 255, 0.15);
    --accent: #f2b45c;
    --accent-soft: rgba(242, 180, 92, 0.2);
    --good: #5cf07e;
    --warn: #f5b942;
    --bad: #ff5a52;
    --capital: #c07ae0;
    --kind-traffic: #ff7a6e;
    --kind-economy: #f5b942;
    --kind-people: #6ea8fe;
    --kind-build: #8ee6a8;
    --kind-policy: #c58fe0;
    --kind-alert: #ff5a52;
    --chart-blue: #6ea8fe;
    --chart-amber: #f5b942;
    --chart-red: #ff7a6e;
    --chart-green: #8ee6a8;
    --chart-purple: #c58fe0;
    --chart-teal: #3fc1c9;
    --chart-orange: #ec8a4f;
    --chart-grey: #8f98ab;
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --mat-chrome: rgba(27, 31, 41, 0.95);
  --mat-panel: rgba(27, 31, 41, 0.95);
  --mat-sheet: rgba(31, 35, 46, 0.98);
  --mat-chrome-solid: #1b1f29;
  --mat-panel-solid: #1b1f29;
  --hairline: rgba(255, 255, 255, 0.08);
  --hairline-strong: rgba(255, 255, 255, 0.16);
  --sheen: inset 0 1px 0 rgba(255, 255, 255, 0.05);
  --shadow-1: 0 2px 4px rgba(0, 0, 0, 0.25), 0 8px 24px rgba(0, 0, 0, 0.35);
  --shadow-2: 0 4px 10px rgba(0, 0, 0, 0.3), 0 20px 48px rgba(0, 0, 0, 0.45);
  --scrim: rgba(10, 12, 18, 0.5);
  --fg: #e9ecf2;
  --fg-2: #b3bac8;
  --fg-3: #8f98ab;
  --fg-4: #5b6272;
  --fill-1: rgba(255, 255, 255, 0.05);
  --fill-2: rgba(255, 255, 255, 0.09);
  --fill-3: rgba(255, 255, 255, 0.15);
  --accent: #f2b45c;
  --accent-soft: rgba(242, 180, 92, 0.2);
  --good: #5cf07e;
  --warn: #f5b942;
  --bad: #ff5a52;
  --capital: #c07ae0;
  --kind-traffic: #ff7a6e;
  --kind-economy: #f5b942;
  --kind-people: #6ea8fe;
  --kind-build: #8ee6a8;
  --kind-policy: #c58fe0;
  --kind-alert: #ff5a52;
  --chart-blue: #6ea8fe;
  --chart-amber: #f5b942;
  --chart-red: #ff7a6e;
  --chart-green: #8ee6a8;
  --chart-purple: #c58fe0;
  --chart-teal: #3fc1c9;
  --chart-orange: #ec8a4f;
  --chart-grey: #8f98ab;
}

/* No backdrop-filter (old Firefox, some Android): go opaque instead of muddy. */
/* `:root:root` matches the specificity of the [data-theme] blocks above and comes
   later, so these three overrides win even when a theme has been chosen by hand. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  :root:root {
    --mat-chrome: var(--mat-chrome-solid);
    --mat-panel: var(--mat-panel-solid);
  }
}

@media (prefers-reduced-transparency: reduce) {
  :root:root {
    --mat-chrome: var(--mat-chrome-solid);
    --mat-panel: var(--mat-panel-solid);
    --blur-chrome: none;
    --blur-panel: none;
  }
}

@media (prefers-contrast: more) {
  :root:root {
    --hairline: var(--hairline-strong);
    --fg-3: var(--fg-2);
    --mat-chrome: var(--mat-chrome-solid);
    --mat-panel: var(--mat-panel-solid);
  }
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-1: 1ms;
    --dur-2: 1ms;
    --dur-3: 1ms;
  }
  * {
    animation: none !important;
    transition-property: opacity, background-color !important;
    transition-duration: 120ms !important;
  }
}

/* ------------------------------------------------------------------ 2. base */

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  /* A swipe that runs off the map is not "back" or "reload". */
  overscroll-behavior: none;
  background: var(--mat-chrome-solid);
  color: var(--fg);
  font:
    var(--fw-regular) var(--fs-13) / var(--lh-13) var(--font-ui);
  -webkit-font-smoothing: antialiased;
}

button,
select,
input,
textarea {
  font: inherit;
  color: inherit;
}

button {
  cursor: pointer;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: inherit;
}

.num {
  font-variant-numeric: tabular-nums slashed-zero;
  font-weight: var(--fw-medium);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

#skip {
  position: fixed;
  top: -100px;
  left: var(--sp-2);
  z-index: 50;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm);
  background: var(--accent);
  color: var(--accent-fg);
  text-decoration: none;
}
#skip:focus {
  top: var(--sp-2);
}

#view {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  cursor: grab;
  touch-action: none;
}
#view:active {
  cursor: grabbing;
}

.glass {
  background: var(--mat-chrome);
  -webkit-backdrop-filter: var(--blur-chrome);
  backdrop-filter: var(--blur-chrome);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow-1), var(--sheen);
}

.glass-panel {
  background: var(--mat-panel);
  -webkit-backdrop-filter: var(--blur-panel);
  backdrop-filter: var(--blur-panel);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow-1), var(--sheen);
}

.icon {
  flex: none;
  display: block;
  pointer-events: none;
}

.section-head {
  margin: var(--sp-3) 0 var(--sp-1);
  font-size: var(--fs-11);
  line-height: var(--lh-11);
  font-weight: var(--fw-semibold);
  color: var(--fg-3);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* --------------------------------------------------------------- 3. buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: var(--h-md);
  padding: 0 var(--sp-3);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
  background: var(--fill-1);
  color: var(--fg);
  transition:
    background-color var(--dur-1) var(--ease-out),
    border-color var(--dur-1) var(--ease-out);
}

@media (hover: hover) {
  .btn:hover:not(:disabled):not([aria-disabled="true"]) {
    background: var(--fill-2);
    border-color: var(--hairline-strong);
  }
}
.btn:active:not(:disabled):not([aria-disabled="true"]) {
  background: var(--fill-3);
  border-color: var(--hairline-strong);
}

.btn.armed,
.btn[aria-pressed="true"] {
  background: var(--accent);
  border-color: transparent;
  color: var(--accent-fg);
}

.btn:disabled,
.btn[aria-disabled="true"] {
  color: var(--fg-4);
  opacity: 0.45;
  cursor: not-allowed;
}

.btn-plain {
  border-color: transparent;
  background: transparent;
}

.btn-accent {
  background: var(--accent);
  border-color: transparent;
  color: var(--accent-fg);
}
@media (hover: hover) {
  .btn-accent:hover:not(:disabled) {
    background: var(--accent);
    filter: none;
    border-color: var(--hairline-strong);
  }
}

.btn-danger {
  color: var(--bad);
}
@media (hover: hover) {
  .btn-danger:hover:not(:disabled) {
    background: color-mix(in srgb, var(--bad) 12%, transparent);
    border-color: var(--bad);
  }
}

/* Icon-only: square, no label, the tooltip and aria-label carry the words. */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--h-md);
  height: var(--h-md);
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--fg);
  transition: background-color var(--dur-1) var(--ease-out);
}
@media (hover: hover) {
  .icon-btn:hover:not(:disabled):not([aria-disabled="true"]) {
    background: var(--fill-2);
  }
}
.icon-btn:active:not(:disabled) {
  background: var(--fill-3);
}
.icon-btn[aria-pressed="true"] {
  background: var(--accent);
  color: var(--accent-fg);
}
.icon-btn:disabled,
.icon-btn[aria-disabled="true"] {
  color: var(--fg-4);
  opacity: 0.45;
  cursor: not-allowed;
}

/* A control the tree has not opened: same shape, plainly out of reach. */
.locked {
  position: relative;
  opacity: 0.45;
  cursor: not-allowed;
}
.locked .lock {
  position: absolute;
  right: 2px;
  bottom: 2px;
  width: 10px;
  height: 10px;
}

/* ---------------------------------------------------- 4. segmented controls */

.seg {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  border-radius: var(--r-pill);
  background: var(--fill-1);
}

.seg-btn {
  min-height: var(--h-sm);
  min-width: 34px;
  padding: 0 var(--sp-2);
  border: 0;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--fg-2);
  font-size: var(--fs-12);
  line-height: var(--lh-12);
  font-weight: var(--fw-medium);
  font-variant-numeric: tabular-nums;
  transition:
    background-color var(--dur-2) var(--ease-out),
    color var(--dur-2) var(--ease-out);
}
@media (hover: hover) {
  .seg-btn:hover[aria-checked="false"],
  .seg-btn:hover[aria-selected="false"] {
    color: var(--fg);
  }
}
.seg-btn[aria-checked="true"],
.seg-btn[aria-selected="true"] {
  background: var(--mat-panel-solid);
  box-shadow: var(--shadow-1);
  color: var(--fg);
}
:root[data-theme="dark"] .seg-btn[aria-checked="true"],
:root[data-theme="dark"] .seg-btn[aria-selected="true"] {
  background: var(--fill-3);
  box-shadow: none;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .seg-btn[aria-checked="true"],
  :root:not([data-theme="light"]) .seg-btn[aria-selected="true"] {
    background: var(--fill-3);
    box-shadow: none;
  }
}

/* --------------------------------------------------------------- 5. toolbar */

#topbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  height: var(--h-bar);
  padding: 0 var(--sp-2);
  border-width: 0 0 1px;
}

.bar-group {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
}

.bar-spacer {
  flex: 1;
  min-width: var(--sp-2);
}

.bar-rule {
  width: 1px;
  height: 20px;
  background: var(--hairline);
  flex: none;
}

#clockGroup {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 0 var(--sp-2);
  color: var(--fg);
  white-space: nowrap;
}
#clock {
  font-variant-numeric: tabular-nums slashed-zero;
  font-size: var(--fs-13);
}
#clock.weekend {
  color: var(--warn);
}
#weatherGlyph {
  display: inline-flex;
  color: var(--fg-2);
}

#treasuryGroup {
  display: inline-flex;
  align-items: baseline;
  gap: var(--sp-2);
  padding: 0 var(--sp-2);
  white-space: nowrap;
}
#treasury {
  font-size: var(--fs-17);
  line-height: var(--lh-17);
  letter-spacing: var(--tracking-tight);
  color: var(--fg);
}
#upkeep {
  font-size: var(--fs-12);
  color: var(--fg-2);
}
#upkeep.bad {
  color: var(--bad);
}

/* progress widget: milestone, the road to the next one, what there is to spend */
#progress {
  gap: var(--sp-2);
  padding: 0 var(--sp-2);
  white-space: nowrap;
}
#progMilestone {
  font-size: var(--fs-13);
  font-weight: var(--fw-semibold);
}
#progress .bar {
  width: 64px;
  height: 4px;
  border-radius: var(--r-pill);
  background: var(--fill-2);
  overflow: hidden;
}
#progFill {
  display: block;
  height: 100%;
  width: 0;
  border-radius: var(--r-pill);
  background: var(--accent);
  transition: width var(--dur-2) linear;
}
#progress.top #progFill {
  background: var(--good);
}
#progPop {
  font-size: var(--fs-12);
  color: var(--fg-2);
}
#progCapital {
  font-size: var(--fs-12);
  color: var(--capital);
  font-style: normal;
}
#progTokens {
  font-size: var(--fs-12);
  color: var(--good);
  font-style: normal;
}

.badge {
  display: inline-flex;
  align-items: center;
  height: var(--h-badge);
  padding: 0 6px;
  border-radius: var(--r-pill);
  font-size: var(--fs-11);
  line-height: 1;
  font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums;
}
#badge {
  background: var(--warn);
  color: #12141a;
  letter-spacing: 0.04em;
}

/* --------------------------------------------------- 6. menus and popovers */

.pop {
  position: fixed;
  z-index: 20;
  min-width: 200px;
  max-width: 320px;
  padding: var(--sp-1);
  border-radius: var(--r-md);
  background: var(--mat-panel);
  -webkit-backdrop-filter: var(--blur-panel);
  backdrop-filter: var(--blur-panel);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow-1), var(--sheen);
  animation: pop-in var(--dur-2) var(--ease-out);
}
.pop[hidden] {
  display: none;
}

@keyframes pop-in {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
}

.pop-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  width: 100%;
  min-height: var(--h-md);
  padding: 0 var(--sp-2);
  border: 0;
  border-radius: var(--r-xs);
  background: transparent;
  color: var(--fg);
  text-align: left;
  font-size: var(--fs-13);
  transition: background-color var(--dur-1) var(--ease-out);
}
@media (hover: hover) {
  .pop-row:hover:not([aria-disabled="true"]) {
    background: var(--fill-1);
  }
}
.pop-row[aria-checked="true"],
.pop-row.armed {
  background: var(--accent-soft);
}
.pop-row[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
}
.pop-row .name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pop-row .cost {
  font-size: var(--fs-12);
  color: var(--fg-3);
  font-variant-numeric: tabular-nums;
}
.pop-row .check {
  color: var(--accent);
}
.pop-row .lock {
  color: var(--fg-3);
}
.pop-head {
  padding: var(--sp-1) var(--sp-2) 0;
  font-size: var(--fs-11);
  line-height: var(--lh-11);
  font-weight: var(--fw-semibold);
  color: var(--fg-3);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.pop-note {
  padding: var(--sp-1) var(--sp-2);
  font-size: var(--fs-12);
  color: var(--fg-3);
}
.pop-rule {
  height: 1px;
  margin: var(--sp-1) 0;
  background: var(--hairline);
}
.pop-field {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 0 var(--sp-2);
  min-height: var(--h-md);
  font-size: var(--fs-13);
  color: var(--fg-2);
}
.pop-field select,
.pop-field input {
  flex: 1;
  min-width: 0;
  min-height: var(--h-sm);
  padding: 0 var(--sp-1);
  border: 1px solid var(--hairline);
  border-radius: var(--r-xs);
  background: var(--fill-1);
  color: var(--fg);
}
.pop-field input[type="checkbox"] {
  flex: none;
  accent-color: var(--accent);
}
/* A slider is not a text field: no box round it, and a full 28 px to grab. */
.pop-field input[type="range"] {
  height: var(--h-md);
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  accent-color: var(--accent);
}
.swatch {
  width: 10px;
  height: 10px;
  border-radius: 3px;
  flex: none;
}
.swatch.ramp {
  width: 64px;
  height: 8px;
  border-radius: 4px;
}

/* ------------------------------------------------------------- 7. tool rail */

#rail {
  position: fixed;
  left: var(--sp-2);
  top: calc(var(--h-bar) + var(--sp-2));
  z-index: 10;
  width: var(--w-rail);
  padding: var(--sp-2);
  border-radius: var(--r-lg);
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}

#tools {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}

.rail-btn {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--h-rail);
  height: var(--h-rail);
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--fg);
  transition: background-color var(--dur-1) var(--ease-out);
}
@media (hover: hover) {
  .rail-btn:hover:not([aria-disabled="true"]) {
    background: var(--fill-2);
  }
}
.rail-btn:active:not([aria-disabled="true"]) {
  background: var(--fill-3);
}
.rail-btn[aria-pressed="true"] {
  background: var(--accent);
  color: var(--accent-fg);
}
.rail-btn[aria-disabled="true"] {
  color: var(--fg-4);
  opacity: 0.45;
  cursor: not-allowed;
}
.rail-btn .digit {
  position: absolute;
  right: 3px;
  bottom: 1px;
  font-size: 9px;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--fg-3);
}
.rail-btn[aria-pressed="true"] .digit {
  color: var(--accent-fg);
  opacity: 0.8;
}
.rail-btn .more {
  position: absolute;
  right: 3px;
  top: 3px;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--fg-3);
}
.rail-btn[aria-pressed="true"] .more {
  background: var(--accent-fg);
}

.rail-rule {
  height: 1px;
  margin: var(--sp-1) 0;
  background: var(--hairline);
}

/* RCI demand, docked under the rail: how badly the town wants each zone. */
/* Five bars have to live inside a 56 px rail, so they borrow its padding. */
#demand {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 2px;
  margin: 0 -4px;
  padding-top: 2px;
}
#demand[hidden] {
  display: none;
}
.demand-bar {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  cursor: help;
}
.demand-bar .track {
  width: 7px;
  height: 24px;
  border-radius: var(--r-xs);
  background: var(--fill-2);
  display: flex;
  align-items: flex-end;
  overflow: hidden;
}
.demand-bar .fill {
  display: block;
  width: 100%;
  height: 0;
  border-radius: var(--r-xs);
  transition: height var(--dur-2) linear;
}
.demand-bar b {
  font-size: 10px;
  line-height: 1;
  font-weight: var(--fw-semibold);
  color: var(--fg-3);
}
.demand-bar.locked {
  opacity: 0.35;
}
.demand-bar.locked .fill {
  background: transparent !important;
}

/* -------------------------------------------------------------- 8. inspector */

#inspector {
  position: fixed;
  top: var(--h-bar);
  right: 0;
  bottom: 0;
  z-index: 10;
  width: var(--w-inspector);
  display: flex;
  flex-direction: column;
  border-width: 0 0 0 1px;
  border-radius: 0;
}
#inspector.collapsed {
  width: 36px;
}
#inspector.collapsed .tabs,
#inspector.collapsed .panel {
  display: none;
}

#inspectorHead {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2);
  border-bottom: 1px solid var(--hairline-strong);
}
#inspectorHead .tabs {
  flex: 1;
  min-width: 0;
  display: flex;
  height: var(--h-lg);
}
#inspectorHead .seg-btn {
  flex: 1;
  min-width: 0;
  height: var(--h-md);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-1);
  font-size: var(--fs-12);
}
#inspector.collapsed #inspectorHead {
  padding: var(--sp-2) 4px;
}
#newsBadge {
  background: var(--accent);
  color: var(--accent-fg);
  padding: 0 5px;
  height: 16px;
}
#inspectorToggle .icon {
  transition: transform var(--dur-2) var(--ease-out);
}
#inspector.collapsed #inspectorToggle .icon {
  transform: rotate(180deg);
}

.panel {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
/* `display: flex` above would otherwise beat the browser's [hidden] rule. */
.panel[hidden] {
  display: none;
}

#inspectorBody {
  margin: 0;
  padding: var(--sp-3);
  overflow: auto;
  flex: 1;
  min-height: 0;
  font-family: var(--font-ui);
  font-size: var(--fs-13);
  line-height: var(--lh-13);
  color: var(--fg);
}
/* The fallback when describe_at does not parse: the raw text, as it came. */
#inspectorBody .raw {
  margin: 0;
  white-space: pre-wrap;
  font-family: var(--font-mono);
  font-size: var(--fs-11);
  line-height: var(--lh-11);
  color: var(--fg-2);
}
.insp-title {
  font-size: var(--fs-15);
  line-height: var(--lh-15);
  font-weight: var(--fw-semibold);
}
.insp-sub {
  margin-top: 2px;
  font-size: var(--fs-12);
  line-height: var(--lh-12);
  color: var(--fg-2);
}
.insp-kv {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--sp-1) var(--sp-3);
  margin-top: var(--sp-3);
}
.insp-kv dt {
  color: var(--fg-2);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.insp-kv dd {
  margin: 0;
  text-align: right;
  font-variant-numeric: tabular-nums slashed-zero;
  font-weight: var(--fw-medium);
}
.insp-note {
  margin-top: var(--sp-2);
  color: var(--fg-2);
  white-space: pre-wrap;
}
/* A new object inside the same description (the driver under a vehicle). */
.insp-subtitle {
  margin-top: var(--sp-4);
  font-size: var(--fs-13);
  font-weight: var(--fw-semibold);
}
/* A wrapped or nested line under the row above it. */
.insp-detail {
  margin-top: 2px;
  padding-left: var(--sp-3);
  font-size: var(--fs-12);
  line-height: var(--lh-12);
  color: var(--fg-2);
  font-variant-numeric: tabular-nums;
}

/* The level block of a service building (DESIGN.md "Service placement tool and
   building levels"): what it is built to, today's book, and the next level. */
#inspectorService {
  flex: none;
  padding: var(--sp-2) var(--sp-3) var(--sp-3);
  border-top: 1px solid var(--hairline);
  display: grid;
  gap: var(--sp-2);
}
.svc-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--sp-2);
}
.svc-badge {
  font-size: var(--fs-11);
  line-height: var(--lh-11);
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--fg-1);
  background: var(--fill-2);
  border-radius: var(--r-sm);
  padding: 2px var(--sp-2);
}
.svc-load {
  font-size: var(--fs-12);
  line-height: var(--lh-12);
  color: var(--fg-2);
  font-variant-numeric: tabular-nums;
}
.svc-note,
.svc-why {
  font-size: var(--fs-11);
  line-height: var(--lh-11);
  color: var(--fg-3);
}
.svc-upgrade {
  width: 100%;
  justify-content: center;
  text-align: center;
  white-space: normal;
}
.svc-upgrade:disabled {
  opacity: 0.5;
  cursor: default;
}

/* The Line panel (DESIGN.md "Transit (v1)" -> "Viewer"): the calls in order, the
   four settings the core clamps, and the two ways out. */
#inspectorStop,
#inspectorLine {
  flex: none;
  padding: var(--sp-2) var(--sp-3) var(--sp-3);
  border-top: 1px solid var(--hairline);
  display: grid;
  gap: var(--sp-2);
}
#inspectorStop[hidden],
#inspectorLine[hidden] {
  display: none;
}
.line-head {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
}
.line-swatch {
  width: 10px;
  height: 10px;
  border-radius: 3px;
  flex: none;
  align-self: center;
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.25);
}
.line-name {
  font-size: var(--fs-13);
  font-weight: var(--fw-semibold);
  flex: 1 1 auto;
}
.line-status {
  font-size: var(--fs-11);
  line-height: var(--lh-11);
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--fg-1);
  background: var(--fill-2);
  border-radius: var(--r-sm);
  padding: 2px var(--sp-2);
}
.line-calls {
  display: grid;
  gap: 1px;
  max-height: 168px;
  overflow: auto;
}
.line-call {
  display: grid;
  grid-template-columns: 18px minmax(0, 1fr) auto auto auto;
  gap: var(--sp-1);
  align-items: center;
  min-height: 22px;
}
.line-num {
  color: var(--fg-3);
  font-size: var(--fs-11);
  text-align: right;
}
.line-stop {
  font-size: var(--fs-12);
  line-height: var(--lh-12);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.line-empty,
.line-why {
  font-size: var(--fs-11);
  line-height: var(--lh-11);
  color: var(--fg-3);
}
.line-why {
  color: var(--warn);
}
/* The ▲▼✕ and −/+ controls: small, square, no chrome until they are pointed at. */
.line-move {
  width: 20px;
  height: 20px;
  min-width: 20px;
  font-size: 11px;
  line-height: 1;
  color: var(--fg-2);
}
.line-move:disabled {
  opacity: 0.3;
  cursor: default;
}
.line-field {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 22px;
}
.line-field input[type="range"] {
  flex: 1 1 auto;
  min-width: 0;
}
.line-label {
  font-size: var(--fs-12);
  color: var(--fg-2);
  flex: none;
  min-width: 56px;
}
.line-value {
  font-size: var(--fs-12);
  color: var(--fg-1);
  text-align: right;
  flex: 1 1 auto;
}
.line-stepper .line-value {
  min-width: 108px;
}
.line-toggle {
  cursor: pointer;
}
.line-stat {
  font-size: var(--fs-11);
  line-height: var(--lh-11);
  color: var(--fg-2);
}
.line-actions {
  display: flex;
  gap: var(--sp-2);
}

/* The confirm sheet's own two buttons; everything else is the shared .pop. */
.pop-actions {
  display: flex;
  gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-2) var(--sp-2);
}

/* Zero-size anchor parked at the last click, so a confirm sheet has a rect. */
#popAt {
  position: fixed;
  width: 0;
  height: 0;
  pointer-events: none;
}

#inspectorReqs {
  flex: none;
  padding: var(--sp-2) var(--sp-3) var(--sp-3);
  border-top: 1px solid var(--hairline);
  display: grid;
  gap: 2px;
}
.req-head {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-2);
  font-size: var(--fs-11);
  line-height: var(--lh-11);
  font-weight: var(--fw-semibold);
  color: var(--fg-3);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding-bottom: var(--sp-1);
}
.req-head.bad {
  color: var(--warn);
}
.req-row {
  display: grid;
  grid-template-columns: 14px 1fr auto;
  gap: var(--sp-2);
  align-items: baseline;
  min-height: 20px;
}
.req-row.hard .req-label {
  font-weight: var(--fw-semibold);
}
.req-mark {
  font-style: normal;
  text-align: center;
}
.req-row.met .req-mark {
  color: var(--good);
}
.req-row.unmet .req-mark {
  color: var(--bad);
}
.req-detail {
  font-size: var(--fs-11);
  color: var(--fg-3);
  font-variant-numeric: tabular-nums;
}
/* Why a lot with a clean checklist is still empty: the town is not asking for
   what it would build. It reads as prose, not as another checklist row. */
.req-note {
  margin-top: var(--sp-2);
  font-size: var(--fs-12);
  line-height: var(--lh-12);
  color: var(--fg-2);
}

/* The diary keeps the monospace: it is a log, and the HH:MM column is the point. */
#inspectorDiary {
  flex: 0 0 auto;
  max-height: 45%;
  overflow: auto;
  border-top: 1px solid var(--hairline);
  font-family: var(--font-mono);
  font-size: var(--fs-11);
  line-height: var(--lh-11);
}
#inspectorDiary[hidden] {
  display: none;
}
.diary-head {
  position: sticky;
  top: 0;
  padding: var(--sp-1) var(--sp-3);
  background: var(--mat-panel-solid);
  color: var(--fg-3);
  font-family: var(--font-ui);
  font-size: var(--fs-11);
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.diary-line {
  display: flex;
  gap: var(--sp-2);
  padding: 3px var(--sp-3);
}
.diary-when {
  color: var(--fg-3);
  flex: none;
}
.diary-what {
  color: var(--fg);
  white-space: pre-wrap;
}

/* ------------------------------------------------------------------ 9. news */

#newsFilters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1);
  padding: var(--sp-2) var(--sp-2) var(--sp-1);
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-height: var(--h-sm);
  padding: 0 var(--sp-2);
  border: 0;
  border-radius: var(--r-pill);
  background: var(--fill-1);
  color: var(--fg-2);
  font-size: var(--fs-11);
  transition: background-color var(--dur-1) var(--ease-out);
}
.chip .dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: transparent;
  border: 1px solid var(--fg-4);
}
.chip.on {
  background: var(--fill-2);
  color: var(--fg);
}
.chip.on .dot {
  background: var(--kind);
  border-color: var(--kind);
}

#newsNote {
  padding: var(--sp-5) var(--sp-3);
  text-align: center;
  font-size: var(--fs-12);
  color: var(--fg-3);
}
#newsNote[hidden] {
  display: none;
}
#newsList {
  flex: 1;
  min-height: 0;
  overflow: auto;
  padding: 0 var(--sp-2) var(--sp-2);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.news-row {
  position: relative;
  display: grid;
  grid-template-columns: 46px 8px 1fr;
  align-items: baseline;
  gap: var(--sp-2);
  width: 100%;
  padding: 6px 10px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  text-align: left;
  transition: background-color var(--dur-1) var(--ease-out);
}
/* Filtered-out rows: `display: grid` above outranks the [hidden] default. */
.news-row[hidden] {
  display: none;
}
@media (hover: hover) {
  .news-row:hover {
    background: var(--fill-1);
  }
}
.news-row.active {
  background: var(--accent-soft);
}
.news-row.active::before {
  content: "";
  position: absolute;
  left: 0;
  top: 4px;
  bottom: 4px;
  width: 2px;
  border-radius: var(--r-pill);
  background: var(--accent);
}
.news-row.alert .news-text {
  color: var(--bad);
}
.news-when {
  font-size: var(--fs-11);
  color: var(--fg-3);
  font-variant-numeric: tabular-nums;
}
.news-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--kind);
  align-self: center;
}
.news-text {
  color: var(--fg);
  font-size: var(--fs-12);
  line-height: var(--lh-12);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* --------------------------------------------------------------- 10. develop */

#panelDevelop {
  overflow: auto;
  padding: 0 var(--sp-3) var(--sp-3);
}
.dev-head {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--sp-2);
  padding: var(--sp-2) 0 var(--sp-1);
  background: var(--mat-panel-solid);
  font-size: var(--fs-15);
  font-weight: var(--fw-semibold);
}
.dev-head b {
  font-weight: var(--fw-medium);
  font-size: var(--fs-13);
  font-variant-numeric: tabular-nums;
  color: var(--capital);
}
.dev-note {
  padding: var(--sp-1) 0 var(--sp-2);
  font-size: var(--fs-12);
  color: var(--fg-3);
}
.dev-branch {
  margin: var(--sp-3) 0 var(--sp-1);
  font-size: var(--fs-11);
  line-height: var(--lh-11);
  font-weight: var(--fw-semibold);
  color: var(--fg-3);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.dev-node {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  width: 100%;
  min-height: var(--h-md);
  margin-bottom: 2px;
  padding: 0 var(--sp-2);
  text-align: left;
  border: 0;
  border-left: 2px solid var(--hairline);
  border-radius: 0 var(--r-xs) var(--r-xs) 0;
  background: transparent;
  color: var(--fg);
  transition: background-color var(--dur-1) var(--ease-out);
}
@media (hover: hover) {
  .dev-node.available:hover {
    background: var(--fill-1);
  }
}
.dev-node.child {
  width: calc(100% - 12px);
  margin-left: 12px;
}
.dev-node .cost {
  margin-left: auto;
  font-size: var(--fs-12);
  color: var(--fg-3);
  font-variant-numeric: tabular-nums;
}
.dev-node.available {
  border-left-color: var(--accent);
}
.dev-node.available .cost {
  color: var(--capital);
}
.dev-node.unlocked {
  border-left-color: var(--good);
}
.dev-node.unlocked .cost {
  color: var(--good);
}
.dev-node.locked {
  opacity: 0.45;
  cursor: not-allowed;
}
.dev-node:disabled {
  cursor: default;
}
/* The inline confirm that replaces window.confirm for an expensive node. */
.dev-confirm {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 2px 0 var(--sp-2) 12px;
  padding: var(--sp-2);
  border-radius: var(--r-sm);
  background: var(--fill-1);
  font-size: var(--fs-12);
  color: var(--fg-2);
}
.dev-confirm .btn {
  min-height: var(--h-sm);
  padding: 0 var(--sp-2);
  font-size: var(--fs-12);
}
.dev-confirm .grow {
  flex: 1;
}

/* --------------------------------------------------------------- 11. budget */

#panelBudget {
  overflow: auto;
  padding: var(--sp-3);
}
.kv-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--sp-1) var(--sp-3);
  align-items: baseline;
  min-height: 22px;
}
.kv-row span {
  color: var(--fg-2);
}
.kv-row b {
  font-weight: var(--fw-medium);
  font-variant-numeric: tabular-nums slashed-zero;
  color: var(--fg);
}
.kv-row b.bad {
  color: var(--bad);
}
.budget-chart {
  width: 100%;
  height: 64px;
  margin-top: var(--sp-2);
}
.policy-row {
  display: grid;
  grid-template-columns: 88px 1fr 56px;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--h-md);
}
.policy-row span {
  color: var(--fg-2);
}
.policy-row b {
  font-weight: var(--fw-medium);
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--fg);
}
.policy-row input[type="range"] {
  width: 100%;
  height: var(--h-md);
  margin: 0;
  padding: 0;
  accent-color: var(--accent);
  background: transparent;
}
.policy-row input[type="checkbox"] {
  width: 16px;
  height: 16px;
  accent-color: var(--accent);
}
.policy-row.off b,
.policy-row.off span,
.policy-row.locked span,
.policy-row.locked b {
  opacity: 0.45;
}

/* ---------------------------------------------------- 12. bottom-left cards */

#bottomleft {
  position: fixed;
  left: var(--sp-2);
  bottom: var(--sp-2);
  z-index: 9;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  width: var(--w-cards);
  max-height: calc(100vh - var(--h-bar) - var(--sp-4));
}

.card {
  padding: var(--sp-2);
  border-radius: var(--r-md);
  color: var(--fg);
}
.card-head {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-2);
  font-size: var(--fs-15);
  line-height: var(--lh-15);
  font-weight: var(--fw-semibold);
}
.card-head .grow {
  flex: 1;
  min-width: 0;
}
/* A menu button: the current choice plus a chevron, no box until hover. */
.menu-btn {
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  gap: var(--sp-1);
  min-height: var(--h-md);
  padding: 0 var(--sp-2);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--fg);
  font-size: var(--fs-13);
  font-weight: var(--fw-semibold);
  transition: background-color var(--dur-1) var(--ease-out);
}
@media (hover: hover) {
  .menu-btn:hover {
    background: var(--fill-1);
  }
}
/* One chevron in the set: pointing down means "there is a list under this". */
.menu-btn .icon,
#legendCollapse .icon {
  transform: rotate(90deg);
  color: var(--fg-3);
}
#legend.shut #legendCollapse .icon {
  transform: rotate(-90deg);
}
#legend.shut #legendOverlay,
#legend.shut #mapKey {
  display: none;
}
#legend.shut .card-head {
  margin-bottom: 0;
}
.legend-scale {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-11);
  color: var(--fg-3);
  font-variant-numeric: tabular-nums;
}
.legend-scale .ramp {
  flex: 1;
  min-width: 60px;
}
.legend-rows {
  display: flex;
  flex-wrap: wrap;
  gap: 2px var(--sp-3);
}
.legend-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  white-space: nowrap;
  font-size: var(--fs-12);
  color: var(--fg-2);
}
#mapKey {
  margin-top: var(--sp-2);
  border-top: 1px solid var(--hairline);
  padding-top: var(--sp-1);
}
#mapKey summary {
  cursor: pointer;
  font-size: var(--fs-12);
  color: var(--fg-3);
  list-style: none;
}
#mapKey summary::-webkit-details-marker {
  display: none;
}
#mapKey summary::before {
  content: "▸ ";
}
#mapKey[open] summary::before {
  content: "▾ ";
}
#legendBody {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2px var(--sp-3);
  padding: var(--sp-2) 0 2px;
  max-height: 30vh;
  overflow: auto;
}
#legendBody .legend-row:has(.ramp) {
  grid-column: 1 / -1;
}

/* --------------------------------------------------------------- 13. charts */

#chartHead .value {
  font-size: var(--fs-17);
  line-height: var(--lh-17);
  font-variant-numeric: tabular-nums slashed-zero;
  font-weight: var(--fw-medium);
  color: var(--series, var(--fg));
  white-space: nowrap;
}
#chartCanvas {
  display: block;
  width: 100%;
  height: 72px;
}
#chartChips {
  display: flex;
  gap: var(--sp-1);
  margin-top: var(--sp-1);
  overflow-x: auto;
  scrollbar-width: none;
}
#chartChips::-webkit-scrollbar {
  display: none;
}
.spark {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 3px 4px;
  border: 0;
  border-radius: var(--r-xs);
  background: transparent;
  transition: background-color var(--dur-1) var(--ease-out);
}
@media (hover: hover) {
  .spark:hover {
    background: var(--fill-1);
  }
}
.spark[aria-pressed="true"] {
  background: var(--fill-2);
}
.spark canvas {
  display: block;
  width: 32px;
  height: 12px;
}
.spark b {
  font-size: 9px;
  line-height: 1;
  font-weight: var(--fw-medium);
  color: var(--fg-3);
  max-width: 40px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.spark[aria-pressed="true"] b {
  color: var(--fg);
}

/* --------------------------------------------------- 14. toast & milestone */

#toast {
  position: fixed;
  left: 50%;
  bottom: var(--sp-5);
  z-index: 20;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  height: var(--h-lg);
  padding: 0 var(--sp-4);
  border-radius: var(--r-pill);
  background: var(--mat-panel);
  -webkit-backdrop-filter: var(--blur-panel);
  backdrop-filter: var(--blur-panel);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow-1), var(--sheen);
  font-size: var(--fs-13);
  color: var(--fg);
  opacity: 0;
  transform: translate(-50%, 8px);
  transition:
    opacity var(--dur-1) ease-in,
    transform var(--dur-1) ease-in;
  pointer-events: none;
  max-width: min(560px, 80vw);
}
#toast.show {
  opacity: 1;
  transform: translate(-50%, 0);
  transition:
    opacity var(--dur-2) var(--ease-out),
    transform var(--dur-2) var(--ease-out);
}
#toast .icon {
  color: var(--good);
}
#toast.bad .icon {
  color: var(--bad);
}

#milestone {
  position: fixed;
  left: 50%;
  top: 38%;
  z-index: 40;
  width: 360px;
  transform: translate(-50%, -50%);
  padding: var(--sp-4);
  border-radius: var(--r-lg);
  background: var(--mat-sheet);
  -webkit-backdrop-filter: var(--blur-panel);
  backdrop-filter: var(--blur-panel);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow-2), var(--sheen);
  text-align: center;
  animation: milestone-in var(--dur-3) var(--ease-out);
}
#milestone[hidden] {
  display: none;
}
@keyframes milestone-in {
  from {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.96);
  }
}
#milestone .eyebrow {
  font-size: var(--fs-11);
  line-height: var(--lh-11);
  font-weight: var(--fw-semibold);
  color: var(--fg-3);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
#milestone .tier {
  margin-top: var(--sp-1);
  font-size: var(--fs-28);
  line-height: var(--lh-28);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-tight);
}
#milestone .line {
  margin-top: var(--sp-1);
  font-size: var(--fs-13);
  color: var(--fg-2);
}
#milestone .btn {
  margin-top: var(--sp-3);
}

/* ------------------------------------------------------------- 15. dialogs */

dialog {
  width: 520px;
  max-width: calc(100vw - var(--sp-6));
  padding: var(--sp-4);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background: var(--mat-sheet);
  -webkit-backdrop-filter: var(--blur-panel);
  backdrop-filter: var(--blur-panel);
  box-shadow: var(--shadow-2), var(--sheen);
  color: var(--fg);
}
dialog::backdrop {
  background: var(--scrim);
}
.dialog-head {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
  font-size: var(--fs-15);
  line-height: var(--lh-15);
  font-weight: var(--fw-semibold);
}
.dialog-head .grow {
  flex: 1;
}
#saveText {
  width: 100%;
  height: 120px;
  padding: var(--sp-2);
  resize: vertical;
  white-space: pre;
  overflow-wrap: normal;
  overflow-x: auto;
  font-family: var(--font-mono);
  font-size: var(--fs-11);
  line-height: 1.45;
  color: var(--fg);
  background: var(--fill-1);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
}
.card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-top: var(--sp-2);
}
.card-note {
  margin-top: var(--sp-3);
  font-size: var(--fs-12);
  color: var(--fg-3);
}
.keys {
  display: grid;
  grid-template-columns: 96px 1fr;
  gap: var(--sp-1) var(--sp-3);
  max-height: 60vh;
  overflow: auto;
}
.keys dt {
  font-family: var(--font-mono);
  font-size: var(--fs-12);
  color: var(--fg-2);
  text-align: right;
}
.keys dd {
  margin: 0;
  font-size: var(--fs-13);
}

#busy {
  position: fixed;
  inset: 0;
  z-index: 30;
  display: grid;
  place-items: center;
  background: var(--scrim);
  color: var(--fg);
  font-size: var(--fs-15);
}
#busy[hidden] {
  display: none;
}

/* Cursor-following label for the street under the pointer. */
#hoverinfo {
  position: fixed;
  z-index: 11;
  padding: 3px var(--sp-2);
  border-radius: var(--r-pill);
  background: var(--mat-panel);
  -webkit-backdrop-filter: var(--blur-panel);
  backdrop-filter: var(--blur-panel);
  border: 1px solid var(--hairline);
  font-size: var(--fs-11);
  color: var(--fg);
  pointer-events: none;
  white-space: nowrap;
}
#hoverinfo[hidden] {
  display: none;
}

/* ---------------------------------------------------------- 16. build panel */

/* Docked beside the rail, over the map, at the same elevation as the rail and
   the inspector — one glass layer, never glass on glass (DESIGN-UI.md §4.19). */
#buildPanel {
  position: fixed;
  left: calc(var(--w-rail) + var(--sp-2) * 2);
  top: calc(var(--h-bar) + var(--sp-2));
  z-index: 10;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  width: var(--w-build);
  max-height: calc(100vh - var(--h-bar) - var(--sp-4));
  padding: var(--sp-2);
  border-radius: var(--r-lg);
}

#buildHead {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
#buildCats {
  flex: 1;
  min-width: 0;
}
#buildCats .seg-btn {
  flex: 1 1 0;
  min-width: 0;
  padding: 0 var(--sp-1);
  font-size: var(--fs-11);
}
#buildClose {
  flex: none;
}

#buildOptions {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
#buildOptions .seg {
  flex: none;
}
#buildOptions .seg-btn[aria-disabled="true"] {
  color: var(--fg-4);
  opacity: 0.45;
  cursor: not-allowed;
}
.build-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  height: var(--h-md);
  padding: 0 var(--sp-2);
}
.build-toggle .cost {
  color: var(--fg-3);
  font-size: var(--fs-12);
  font-variant-numeric: tabular-nums;
}
.build-toggle[aria-pressed="true"] .cost {
  color: var(--accent-fg);
  opacity: 0.85;
}

#buildCards {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.build-card {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--sp-2);
  width: 100%;
  padding: var(--sp-1);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--fg);
  text-align: left;
  transition: background-color var(--dur-1) var(--ease-out);
}
@media (hover: hover) {
  .build-card:hover {
    background: var(--fill-1);
  }
}
.build-card:active {
  background: var(--fill-2);
}
.build-card[aria-pressed="true"] {
  background: var(--accent-soft);
  box-shadow: inset 2px 0 0 var(--accent);
}

.card-thumb {
  flex: none;
  display: block;
  width: 96px;
  height: 56px;
  border-radius: var(--r-xs);
}
.card-glyph {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--fill-1);
  color: var(--fg-2);
}

.card-body {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}
.card-name {
  font-size: var(--fs-13);
  line-height: var(--lh-13);
  font-weight: var(--fw-semibold);
}
.card-cost {
  font-size: var(--fs-12);
  line-height: var(--lh-12);
  color: var(--fg-2);
}
/* The ground a service needs, under its price. */
.card-size {
  font-size: var(--fs-11);
  line-height: var(--lh-11);
  color: var(--fg-3);
}
.card-effect,
.card-why {
  font-size: var(--fs-11);
  line-height: var(--lh-11);
  color: var(--fg-3);
}
.card-why {
  color: var(--warn);
}

/* Locked keeps the reason fully legible: the thumbnail greys out and the lock
   glyph appears, rather than fading the words that explain the refusal. */
.build-card.locked .card-thumb {
  opacity: 0.4;
  filter: grayscale(1);
}
.build-card.locked .card-name {
  color: var(--fg-3);
}
.build-card .lock {
  color: var(--fg-3);
}
.build-card .check {
  color: var(--accent);
}
.build-toggle.locked {
  opacity: 0.45;
  cursor: not-allowed;
}

#buildNote {
  padding: 0 var(--sp-1);
  font-size: var(--fs-12);
  line-height: var(--lh-12);
  color: var(--fg-3);
}

/* One glass layer: the legend and chart cards move out from under the docked
   panel instead of being covered by it. */
@media (min-width: 1024px) {
  body.build-open #bottomleft {
    left: calc(var(--w-rail) + var(--w-build) + var(--sp-2) * 3);
  }
}

/* ----------------------------------------------------------- 17. game shell */

/* The city and, when it came from one, the save it was loaded from. */
#cityGroup {
  display: inline-flex;
  align-items: baseline;
  gap: var(--sp-1);
  padding: 0 var(--sp-1);
  /* The toolbar is a flex row that squeezes: the city's name is not a place to
     take the space from. */
  flex: none;
  white-space: nowrap;
}
#cityName {
  font-size: var(--fs-13);
  font-weight: var(--fw-semibold);
  color: var(--fg);
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 22ch;
}
#citySlot {
  font-size: var(--fs-12);
  color: var(--fg-3);
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 18ch;
}

/* The sheet covers the map: the game behind it is paused, so nothing on it is
   reachable while it is up (DESIGN-UI.md §4.22). */
#shell {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: grid;
  /* One definite track: an auto row grows to the sheet's whole content and the
     percentage max-height then resolves against that, so a long Settings tab was
     centred inside a row taller than the viewport. */
  grid-template-rows: minmax(0, 1fr);
  place-items: center;
  padding: var(--sp-4);
  background: var(--scrim);
}
#shell[hidden] {
  display: none;
}

.shell-sheet {
  display: flex;
  flex-direction: column;
  width: min(920px, 100%);
  min-height: 0;
  max-height: min(680px, 100%);
  padding: var(--sp-4);
  border-radius: var(--r-lg);
  background: var(--mat-sheet);
  -webkit-backdrop-filter: var(--blur-panel);
  backdrop-filter: var(--blur-panel);
  box-shadow: var(--shadow-2), var(--sheen);
  animation: shell-in var(--dur-3) var(--ease-out);
}
@keyframes shell-in {
  from {
    opacity: 0;
    transform: scale(0.98);
  }
}

.shell-head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}
.shell-title {
  font-size: var(--fs-17);
  line-height: var(--lh-17);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-tight);
}
.shell-tabs {
  flex: 1;
  justify-content: center;
}
.shell-tabs .seg-btn {
  min-width: 88px;
  min-height: var(--h-md);
  font-size: var(--fs-13);
}

.shell-body {
  flex: 1;
  min-height: 0;
  overflow: auto;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.shell-note {
  margin: 0;
  font-size: var(--fs-12);
  line-height: var(--lh-12);
  color: var(--fg-3);
}
.shell-hint {
  font-size: var(--fs-12);
  line-height: var(--lh-12);
  color: var(--fg-3);
}
.shell-label {
  font-size: var(--fs-13);
  font-weight: var(--fw-medium);
  color: var(--fg);
}
.shell-eyebrow {
  font-size: var(--fs-11);
  line-height: var(--lh-11);
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--fg-3);
}

/* A map picture: the image is a background so a missing one degrades to ground. */
.shell-shot {
  flex: none;
  width: 176px;
  height: 110px;
  border-radius: var(--r-sm);
  border: 1px solid var(--hairline);
  background-color: var(--fill-2);
  background-size: cover;
  background-position: center;
}
.shell-shot-sm {
  width: 96px;
  height: 60px;
}
.shell-shot.empty {
  background-image: linear-gradient(140deg, var(--fill-2), var(--fill-1));
}

/* --- Continue --- */

.shell-hero {
  display: flex;
  gap: var(--sp-4);
  padding: var(--sp-3);
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: var(--fill-1);
}
.shell-hero-words {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  min-width: 0;
}
.shell-hero-name {
  font-size: var(--fs-22);
  line-height: var(--lh-22);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-tight);
}
.shell-hero-line {
  font-size: var(--fs-13);
  color: var(--fg-2);
  font-variant-numeric: tabular-nums slashed-zero;
}
.shell-hero-sub {
  font-size: var(--fs-12);
  color: var(--fg-3);
}
.shell-hero-go {
  align-self: flex-start;
  margin-top: var(--sp-2);
  min-height: var(--h-lg);
  padding: 0 var(--sp-5);
}

/* --- New game --- */

.shell-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--sp-2);
}
.shell-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-2);
  text-align: left;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: var(--fill-1);
  color: var(--fg);
  transition:
    background-color var(--dur-1) var(--ease-out),
    border-color var(--dur-1) var(--ease-out);
}
@media (hover: hover) {
  .shell-card:hover {
    background: var(--fill-2);
    border-color: var(--hairline-strong);
  }
}
.shell-card.on {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.shell-card .shell-shot {
  width: 100%;
  height: 96px;
}
.shell-card-words {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.shell-card-name {
  font-size: var(--fs-15);
  line-height: var(--lh-15);
  font-weight: var(--fw-semibold);
  text-transform: capitalize;
}
.shell-card-town {
  font-size: var(--fs-12);
  color: var(--fg-2);
  min-height: var(--lh-12);
}
.shell-card-note {
  font-size: var(--fs-12);
  line-height: var(--lh-12);
  color: var(--fg-3);
}

.shell-foot {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-3);
  padding-top: var(--sp-2);
  border-top: 1px solid var(--hairline);
}
.shell-start {
  margin-left: auto;
  min-height: var(--h-lg);
  padding: 0 var(--sp-5);
}

.shell-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
.shell-row-head {
  padding-bottom: var(--sp-1);
}
.shell-row .grow,
.shell-input.grow {
  flex: 1;
  min-width: 0;
}
.shell-input {
  min-height: var(--h-md);
  padding: 0 var(--sp-2);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
  background: var(--fill-1);
  color: var(--fg);
  font: inherit;
  font-size: var(--fs-13);
}
.shell-editor {
  padding: var(--sp-2);
  border: 1px solid var(--accent);
  border-radius: var(--r-md);
  background: var(--accent-soft);
}

/* --- Saves --- */

.shell-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}
.shell-slot {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-3);
  padding: var(--sp-2);
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: var(--fill-1);
}
.shell-slot.on {
  border-color: var(--accent);
}
.shell-slot-words {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.shell-slot-name {
  font-size: var(--fs-15);
  line-height: var(--lh-15);
  font-weight: var(--fw-semibold);
  overflow: hidden;
  text-overflow: ellipsis;
}
.shell-slot-line {
  font-size: var(--fs-13);
  color: var(--fg-2);
  font-variant-numeric: tabular-nums slashed-zero;
}
.shell-slot-sub {
  font-size: var(--fs-12);
  color: var(--fg-3);
}
.shell-slot-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

/* --- Settings --- */

.shell-field {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--hairline);
}
.shell-field:last-child {
  border-bottom: 0;
}
.shell-field-words {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.shell-field input[type="range"] {
  width: 160px;
  accent-color: var(--accent);
}
.shell-field input[type="checkbox"] {
  width: 18px;
  height: 18px;
  accent-color: var(--accent);
}
.shell-field input:disabled,
.shell-field select:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.shell-group {
  margin-top: var(--sp-3);
  padding-bottom: var(--sp-1);
}
.shell-group:first-child {
  margin-top: 0;
}
.shell-range {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
.shell-range output {
  min-width: 4ch;
  text-align: right;
  font-size: var(--fs-12);
  color: var(--fg-2);
  font-variant-numeric: tabular-nums;
}

/* --- Controls: one row per action, the key on the right --- */

.shell-keys {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.shell-keyrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  width: 100%;
  min-height: var(--h-md);
  padding: var(--sp-1) var(--sp-2);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--fg);
  text-align: left;
  transition:
    background-color var(--dur-1) var(--ease-out),
    border-color var(--dur-1) var(--ease-out);
}
@media (hover: hover) {
  .shell-keyrow:hover {
    background: var(--fill-1);
  }
}
/* Armed: the border says it, and so does the "Press a key…" line under the label. */
.shell-keyrow.on {
  background: var(--accent-soft);
  border-color: var(--accent);
}
.shell-keyrow-words {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.shell-key {
  flex: none;
  min-width: 28px;
  padding: 2px 6px;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r-xs);
  background: var(--fill-1);
  font-family: var(--font-mono);
  font-size: var(--fs-12);
  line-height: var(--lh-12);
  text-align: center;
  color: var(--fg);
}

/* --- Title and pause: the two modes of one sheet (DESIGN.md "Game menu") --- */

/* Title: no scrim and a lighter glass, so the demo city reads through. The alpha is
   the highest that still lets the map show — 13 px body text stays above 4.5:1. */
#shell {
  --mat-title: rgba(252, 252, 254, 0.85);
  --scrim-pause: rgba(0, 0, 0, 0.45);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) #shell {
    --mat-title: rgba(30, 35, 44, 0.85);
  }
}
:root[data-theme="dark"] #shell {
  --mat-title: rgba(30, 35, 44, 0.85);
}
@media (prefers-reduced-transparency: reduce) {
  #shell {
    --mat-title: var(--mat-sheet);
  }
}
#shell.title {
  background: transparent;
}
/* The HUD would be reporting on the demo, which is nobody's city (`Ui.setTitleMode`). */
body.title-mode :is(#skip, #topbar, #rail, #bottomleft, #inspector, #firstRun, #hoverinfo) {
  display: none;
}
#shell.title .shell-sheet {
  background: var(--mat-title);
}
#shell.pause {
  background: var(--scrim-pause);
}
#shell.pause .shell-sheet {
  max-height: min(85vh, 100%);
}
.shell-title.big {
  font-size: var(--fs-28);
  line-height: var(--lh-28);
}
.shell-sheet.compact {
  width: min(380px, 100%);
}
.shell-sheet.compact .shell-tabs {
  display: none;
}

.shell-menu {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}
.shell-menu-item {
  justify-content: flex-start;
  min-height: var(--h-lg);
  padding: 0 var(--sp-3);
  font-size: var(--fs-15);
  line-height: var(--lh-15);
  border-color: transparent;
  background: transparent;
}
.shell-menu-item:first-child {
  font-weight: var(--fw-semibold);
}
/* An inline question or name field sits under the item that asked, indented. */
.shell-ask,
.shell-menu .shell-editor {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin: 0 0 var(--sp-1) var(--sp-3);
  padding: var(--sp-2);
  border: 1px solid var(--accent);
  border-radius: var(--r-md);
  background: var(--accent-soft);
}
.shell-ask .shell-hint {
  color: var(--fg-2);
  flex: 1 1 100%;
}

.shell-about {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  max-width: 60ch;
}
.shell-about p {
  margin: 0;
  font-size: var(--fs-13);
  line-height: var(--lh-13);
  color: var(--fg-2);
}

/* --- Colour vision: the chrome's tell-apart colours under a deficiency --- */

/* Okabe & Ito's set, one assignment for all three modes; `applyColourVision`
   (shell.ts) puts `--zone-*` and `--congestion-*` inline on the root and stamps
   `data-cv`, and these blocks give the theme-aware tokens their light and dark
   values — light ones darkened until the readouts that use them as text clear
   4.5:1 on the sheet. Tritanopia keeps green/red for good/bad: those survive it. */
:root[data-cv="deuteranopia"],
:root[data-cv="protanopia"],
:root[data-cv="tritanopia"] {
  --chart-blue: #0b5d91;
  --chart-amber: #7a5400;
  --chart-red: #9d4300;
  --chart-green: #007251;
  --chart-purple: #8f4a72;
  --chart-teal: #1d6e99;
  --chart-orange: #6b6400;
  --chart-grey: #4f5764;
  --good: #0b5d91;
  --warn: #7a5400;
  --bad: #9d4300;
}
:root[data-cv="tritanopia"] {
  --good: #007251;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"])[data-cv="deuteranopia"],
  :root:not([data-theme="light"])[data-cv="protanopia"],
  :root:not([data-theme="light"])[data-cv="tritanopia"] {
    --chart-blue: #3a97d9;
    --chart-amber: #e69f00;
    --chart-red: #ef7a2a;
    --chart-green: #1fbf90;
    --chart-purple: #d98fb8;
    --chart-teal: #56b4e9;
    --chart-orange: #f0e442;
    --chart-grey: #8fa3bf;
    --good: #3a97d9;
    --warn: #e69f00;
    --bad: #ef7a2a;
  }
  :root:not([data-theme="light"])[data-cv="tritanopia"] {
    --good: #1fbf90;
  }
}
:root[data-theme="dark"][data-cv="deuteranopia"],
:root[data-theme="dark"][data-cv="protanopia"],
:root[data-theme="dark"][data-cv="tritanopia"] {
  --chart-blue: #3a97d9;
  --chart-amber: #e69f00;
  --chart-red: #ef7a2a;
  --chart-green: #1fbf90;
  --chart-purple: #d98fb8;
  --chart-teal: #56b4e9;
  --chart-orange: #f0e442;
  --chart-grey: #8fa3bf;
  --good: #3a97d9;
  --warn: #e69f00;
  --bad: #ef7a2a;
}
:root[data-theme="dark"][data-cv="tritanopia"] {
  --good: #1fbf90;
}

/* --- the resume pill --- */

#resumePill {
  position: fixed;
  z-index: 20;
  left: 50%;
  transform: translateX(-50%);
  top: calc(var(--h-bar) + var(--sp-3));
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  height: var(--h-lg);
  padding: 0 var(--sp-2) 0 var(--sp-4);
  border-radius: var(--r-pill);
  font-size: var(--fs-13);
  animation: pill-in var(--dur-2) var(--ease-out);
}
#resumePill[hidden] {
  display: none;
}
#resumePill .pill-words {
  color: var(--fg);
  font-weight: var(--fw-medium);
}
#resumePill .btn {
  min-height: var(--h-sm);
}
@keyframes pill-in {
  from {
    opacity: 0;
    transform: translate(-50%, -8px);
  }
}

/* ------------------------------------------------------ 18. first-run coach */

/* Over the map under the toolbar, tucked against the inspector: the rail, the
   Build panel and the bottom-left cards all keep their ground (DESIGN-UI.md
   §4.23). Same elevation as the rail and the inspector, never over a popover. */
#firstRun {
  position: fixed;
  z-index: 10;
  top: calc(var(--h-bar) + var(--sp-2));
  right: calc(var(--w-inspector) + var(--sp-2));
  width: 360px;
  max-width: calc(100vw - var(--w-rail) - var(--w-inspector) - var(--sp-4));
  border-radius: var(--r-md);
}
#firstRun .card-head {
  margin-bottom: var(--sp-1);
}
#firstRunSteps {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  margin: 0;
  padding-left: 1.35em;
  font-size: var(--fs-13);
  line-height: var(--lh-13);
  color: var(--fg-2);
}
#firstRunSteps li::marker {
  font-variant-numeric: tabular-nums;
  color: var(--fg-3);
}
/* A step the city has already taken stays legible — struck through, not dimmed
   past contrast (DESIGN-UI.md §4.10: off is never unreadable). */
#firstRunSteps li.done {
  color: var(--fg-3);
  text-decoration: line-through;
  text-decoration-color: var(--fg-3);
}
#firstRun .card-note {
  margin-top: var(--sp-2);
}

/* --------------------------------------------------------- 19. narrow sizes */

@media (max-width: 1180px) {
  :root {
    --h-bar: 44px;
    --w-inspector: 320px;
    --w-rail: 44px;
    --h-rail: 36px;
    --w-cards: 232px;
  }
  #rail {
    padding: var(--sp-1);
  }
  .rail-btn .digit {
    display: none;
  }
  #upkeep,
  #progPop,
  #citySlot {
    display: none;
  }
  #cityName {
    max-width: 14ch;
  }
  #inspectorHead .seg-btn .label {
    display: none;
  }
  #inspectorHead .seg-btn {
    min-width: 0;
  }
  #inspector {
    box-shadow: var(--shadow-2), var(--sheen);
  }
  #chartCanvas {
    height: 56px;
  }
}

/* Under 1024 px there is no room beside the rail, so the Build panel becomes a
   bottom sheet and its cards lay out in columns (DESIGN-UI.md §4.19). */
@media (max-width: 1023px) {
  /* It spans the map, not the whole window: the rail keeps its column on the
     left and the inspector keeps its own on the right. */
  #buildPanel {
    right: var(--w-inspector);
    top: auto;
    bottom: 0;
    width: auto;
    max-height: 52vh;
    padding: var(--sp-2) var(--sp-3) var(--sp-3);
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    border-bottom: 0;
    box-shadow: var(--shadow-2), var(--sheen);
  }
  #buildCards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(248px, 1fr));
    gap: var(--sp-1);
  }
  /* The legend and chart cards ride above the sheet and clear of the rail
     rather than under either of them. */
  body.build-open #bottomleft {
    left: calc(var(--w-rail) + var(--sp-2) * 2);
    bottom: calc(52vh + var(--sp-2));
    max-height: calc(48vh - var(--h-bar) - var(--sp-4));
    overflow: auto;
  }
}

/* Below this the map is most of what is left, so the cards step aside; L and C
   (and their collapse buttons) bring them back. */
@media (max-width: 900px) {
  /* The toolbar has no room left for the city's name; the shell still carries it. */
  #cityGroup {
    display: none;
  }
  #bottomleft {
    max-height: calc(100vh - var(--h-bar) - var(--sp-6));
    overflow: auto;
  }
  #legendBody {
    grid-template-columns: 1fr;
  }
}

/* The hidden attribute must win over any component display rule. */
[hidden] {
  display: none !important;
}

/* ------------------------------------------------ building card (Flat map) */

.bcard { padding: 2px 0 10px; }
.bcard-title { display: flex; align-items: center; gap: 8px; font-size: var(--fs-17); line-height: var(--lh-17); font-weight: 700; }
.bcard-title i { width: 12px; height: 12px; border-radius: 4px; flex: none; }
.bcard-street { color: var(--fg-3); margin: 3px 0 10px; }
.bcard-tag { display: inline-block; padding: 3px 9px; border-radius: 7px; font-size: var(--fs-11); font-weight: 700; margin-bottom: 10px; }
.bcard-row { display: flex; justify-content: space-between; gap: 12px; padding: 7px 0; border-top: 1px solid var(--hairline); }
.bcard-row span:first-child { color: var(--fg-3); }
.bcard-bar { height: 6px; border-radius: 3px; background: var(--fill-2); margin: 0 0 4px; overflow: hidden; }
.bcard-bar b { display: block; height: 100%; background: var(--good); border-radius: 3px; }
.bcard-more { margin-top: 6px; border-top: 1px solid var(--hairline); padding-top: 8px; }
.bcard-more summary { cursor: pointer; color: var(--fg-3); font-weight: 600; list-style: none; }
.bcard-more summary::before { content: "▸ "; }
.bcard-more[open] summary::before { content: "▾ "; }
.bcard-more[open] summary { margin-bottom: 8px; }

/* Russian labels run longer than the English the layout was measured for. */
.policy-row { grid-template-columns: 128px 1fr 64px; }
.policy-row span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#buildCats { flex-wrap: wrap; row-gap: 2px; }
#buildCats .seg-btn { flex: 1 1 auto; padding-inline: 8px; white-space: nowrap; }

/* ---------------------------------------------------------------- 20. phone */

/* `body.phone` (mobile.ts): the toolbar across the top, a dock of tools along the
   bottom, and every panel a sheet that rises from the dock over the map. */
body.phone {
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --h-bar: calc(48px + var(--safe-t));
  --h-dock: calc(64px + var(--safe-b));
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
body.phone input,
body.phone textarea {
  -webkit-user-select: text;
  user-select: text;
}

/* The toolbar: play, the lit speed (a tap steps it), the clock, the purse, the
   milestone, undo and the menu. The rest is in the menu. */
body.phone #topbar {
  padding: var(--safe-t) 6px 0;
  gap: 4px;
}
body.phone #skips,
body.phone .bar-rule,
body.phone #cityGroup,
body.phone #upkeep,
body.phone #follow,
body.phone #undo,
body.phone #saveOpen,
body.phone #audioToggle,
body.phone #themeToggle,
body.phone #badge,
body.phone #progPop,
body.phone #progCapital,
body.phone #progTokens,
body.phone #progress,
body.phone .rail-rule,
body.phone #charts {
  display: none !important;
}
body.phone .icon-btn {
  min-width: 40px;
  min-height: 40px;
}
body.phone #speeds .seg-btn[aria-checked="false"] {
  display: none;
}
body.phone #speeds .seg-btn {
  min-width: 58px;
  height: 36px;
  white-space: nowrap;
  font-weight: var(--fw-semibold);
}
body.phone #clockGroup,
body.phone #treasuryGroup {
  padding: 0 2px;
  font-size: var(--fs-12);
}
body.phone #clock {
  font-size: var(--fs-12);
}
body.phone #progress {
  min-width: 0;
  flex: 0 1 auto;
  padding: 0 4px;
}
body.phone #progMilestone {
  max-width: 11ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The rail becomes the dock: one row of thumb-sized tools along the bottom. */
body.phone #rail {
  left: 0;
  right: 0;
  top: auto;
  bottom: 0;
  width: auto;
  height: var(--h-dock);
  padding: 8px 10px calc(8px + var(--safe-b));
  flex-direction: row;
  align-items: center;
  gap: 4px;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  border-width: 1px 0 0;
  overflow-x: auto;
  scrollbar-width: none;
}
body.phone #tools {
  flex-direction: row;
  gap: 4px;
}
body.phone #tools {
  flex: 8 1 0;
  min-width: 0;
  justify-content: space-between;
}
body.phone #rail {
  justify-content: space-between;
}
body.phone .rail-btn {
  flex: 0 1 48px;
  min-width: 38px;
  height: 48px;
}

/* RCI demand leaves the dock for a card of its own under the toolbar, right. */
body.phone #demand {
  position: fixed;
  top: calc(var(--h-bar) + 8px);
  right: 8px;
  z-index: 9;
  margin: 0;
  padding: 6px 8px 5px;
  gap: 4px;
  border-radius: var(--r-md);
  background: var(--mat-chrome);
  -webkit-backdrop-filter: var(--blur-panel);
  backdrop-filter: var(--blur-panel);
  border: 1px solid var(--hairline);
}
body.phone.build-open #demand {
  display: none;
}

/* Build: a sheet over the dock, the whole width. */
body.phone #buildPanel {
  left: 0;
  right: 0;
  top: auto;
  bottom: var(--h-dock);
  width: auto;
  max-height: 58vh;
  max-height: 58dvh;
  padding: 10px 12px 12px;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  border-width: 1px 0 0;
}
body.phone #buildCards {
  display: flex;
  flex-direction: column;
}
body.phone .build-card {
  flex: none;
  min-height: 56px;
}
body.phone #buildCats .seg-btn {
  min-height: 36px;
}

/* The inspector: folded, it is a strip of its tabs over the dock; open, a sheet. */
body.phone #inspector {
  top: auto;
  left: 0;
  right: 0;
  bottom: var(--h-dock);
  width: auto;
  height: 56vh;
  height: 56dvh;
  border-width: 1px 0 0;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: var(--shadow-2), var(--sheen);
}
body.phone #inspector.collapsed {
  width: auto;
  height: auto;
}
body.phone #inspector.collapsed .tabs {
  display: flex;
}
body.phone #inspector.collapsed #inspectorHead {
  padding: var(--sp-2);
  border-bottom: 0;
}
body.phone #inspectorHead .seg-btn {
  height: 40px;
}
body.phone #inspectorToggle .icon {
  transform: rotate(90deg);
}
body.phone #inspector.collapsed #inspectorToggle .icon {
  transform: rotate(-90deg);
}
body.phone.build-open #inspector {
  display: none;
}

/* The legend folds to its title in the corner under the toolbar. */
body.phone #bottomleft {
  left: 8px;
  right: auto;
  top: calc(var(--h-bar) + 8px);
  bottom: auto;
  width: auto;
  max-width: min(260px, 62vw);
  max-height: 40vh;
  overflow: auto;
}
body.phone #legend {
  padding: 2px 4px;
}
body.phone #legend .card-head {
  margin: 0;
  font-size: var(--fs-13);
}
body.phone #legend:not(.shut) .card-head {
  margin-bottom: var(--sp-1);
}
body.phone.build-open #bottomleft {
  left: 8px;
  bottom: auto;
}

/* A finger hides what it points at, so the tool's line sits under the toolbar. */
body.phone #hoverinfo {
  left: 50% !important;
  top: calc(var(--h-bar) + 64px) !important;
  transform: translateX(-50%);
  max-width: calc(100vw - 24px);
  padding: 6px 12px;
  font-size: var(--fs-12);
  white-space: normal;
  text-align: center;
  z-index: 11;
}

/* What floats over the map keeps clear of the dock and the strip above it. */
body.phone #toast {
  bottom: calc(var(--h-dock) + 72px);
  max-width: calc(100vw - 24px);
}
body.phone #firstRun {
  top: auto;
  left: 8px;
  right: 8px;
  bottom: calc(var(--h-dock) + 64px);
  width: auto;
  max-width: none;
}
body.phone #milestone {
  width: auto;
  left: 12px;
  right: 12px;
  transform: translateY(-50%);
}
body.phone dialog {
  max-width: calc(100vw - 16px);
  max-height: calc(100dvh - 32px);
}
body.phone .pop {
  max-width: calc(100vw - 16px);
}
body.phone .pop-row {
  min-height: 44px;
}

/* A phone on its side has height to spare least: the dock stands up along the left
   edge, the panel becomes a column on the right, and the map keeps the middle. */
@media (orientation: landscape) {
  body.phone {
    --safe-l: env(safe-area-inset-left, 0px);
    --safe-r: env(safe-area-inset-right, 0px);
    --h-bar: 44px;
    --h-dock: 0px;
    --w-dock: calc(60px + var(--safe-l));
  }
  body.phone #topbar {
    padding: 0 calc(6px + var(--safe-r)) 0 calc(6px + var(--safe-l));
  }
  body.phone #rail {
    top: var(--h-bar);
    bottom: 0;
    right: auto;
    width: var(--w-dock);
    height: auto;
    padding: 6px 6px 6px calc(6px + var(--safe-l));
    flex-direction: column;
    justify-content: flex-start;
    border-radius: 0 var(--r-lg) 0 0;
    border-width: 0 1px 0 0;
    overflow-x: hidden;
    overflow-y: auto;
  }
  body.phone #tools {
    flex: none;
    flex-direction: column;
  }
  body.phone .rail-btn {
    flex: none;
    width: 46px;
    height: 44px;
  }
  body.phone #buildPanel {
    left: var(--w-dock);
    right: auto;
    top: var(--h-bar);
    bottom: 0;
    width: min(420px, 55vw);
    max-height: none;
    border-radius: 0 var(--r-lg) 0 0;
    border-width: 0 1px 0 0;
  }
  body.phone #inspector {
    top: var(--h-bar);
    left: auto;
    right: 0;
    bottom: 0;
    width: min(360px, 46vw);
    height: auto;
    padding-right: var(--safe-r);
    border-width: 0 0 0 1px;
    border-radius: var(--r-lg) 0 0 0;
  }
  body.phone #inspector.collapsed {
    bottom: auto;
    width: min(260px, 36vw);
    border-width: 0 0 1px 1px;
    border-radius: 0 0 0 var(--r-lg);
  }
  body.phone #bottomleft,
  body.phone.build-open #bottomleft {
    left: calc(var(--w-dock) + 8px);
  }
  body.phone.build-open #bottomleft {
    display: none;
  }
  body.phone #demand {
    top: auto;
    bottom: 8px;
    right: auto;
    left: calc(var(--w-dock) + 8px);
  }
  body.phone #hoverinfo {
    top: calc(var(--h-bar) + 8px) !important;
  }
  body.phone #toast {
    bottom: 12px;
  }
  body.phone #firstRun {
    left: calc(var(--w-dock) + 8px);
    right: auto;
    bottom: 8px;
    width: min(360px, 50vw);
  }
}

/* ------------------------------------------------------------ 21. engagement */

/* Money moving (DESIGN.md "Engagement"): the amount drops out under the balance and
   fades, and the balance itself flashes the colour of the movement. */
#treasuryGroup {
  position: relative;
}
#treasury {
  transition: color 900ms var(--ease-out);
}
#treasury.gain {
  color: var(--good);
  transition: none;
}
#treasury.loss {
  color: var(--bad);
  transition: none;
}
.money-float {
  position: absolute;
  left: var(--sp-2);
  top: 100%;
  z-index: 12;
  font-size: var(--fs-13);
  font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums;
  color: var(--good);
  white-space: nowrap;
  pointer-events: none;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7);
  animation: money-rise 1.7s var(--ease-out) forwards;
}
.money-float.out {
  color: var(--bad);
}
@keyframes money-rise {
  0% {
    opacity: 0;
    transform: translateY(-6px) scale(0.8);
  }
  12% {
    opacity: 1;
    transform: translateY(2px) scale(1.12);
  }
  28% {
    transform: translateY(4px) scale(1);
  }
  100% {
    opacity: 0;
    transform: translateY(20px);
  }
}
@media (prefers-reduced-motion: reduce) {
  .money-float {
    animation-duration: 1.2s;
    animation-name: money-fade;
  }
  @keyframes money-fade {
    0%,
    60% {
      opacity: 1;
    }
    100% {
      opacity: 0;
    }
  }
}

/* The goals card (DESIGN.md "Engagement"): top right of the map, where the coach sat,
   once the coach has taught the three verbs. */
#goals {
  position: fixed;
  z-index: 10;
  top: calc(var(--h-bar) + var(--sp-2));
  right: calc(var(--w-inspector) + var(--sp-2));
  width: 300px;
  max-width: calc(100vw - var(--w-rail) - var(--w-inspector) - var(--sp-4));
  padding: var(--sp-2) var(--sp-3) var(--sp-3);
  border-radius: var(--r-md);
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}
#firstRun:not([hidden]) ~ #goals {
  display: none;
}
.goals-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.goals-title {
  font-size: var(--fs-11);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-3);
}
.goals-fold {
  min-width: 24px;
  min-height: 24px;
  font-size: 12px;
  color: var(--fg-3);
}
#goals.shut .goals-fold {
  transform: rotate(-90deg);
}
.goals-tier {
  display: flex;
  flex-direction: column;
  gap: 5px;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: var(--fg);
  text-align: left;
}
.goals-line {
  font-size: var(--fs-13);
}
.goals-line b {
  font-weight: var(--fw-semibold);
}
.goals-bar {
  display: block;
  height: 7px;
  border-radius: 4px;
  background: var(--fill-2);
  overflow: hidden;
}
.goals-bar.small {
  height: 4px;
  margin: 4px 0 3px;
}
.goals-bar i {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--accent);
  transition: width 600ms var(--ease-out);
}
.goals-bar.small i {
  background: var(--good);
}
.goals-reward {
  font-size: var(--fs-11);
  color: var(--fg-3);
}
.goals-stall {
  padding: 6px 8px;
  border-radius: var(--r-sm);
  background: rgba(245, 185, 66, 0.12);
  color: var(--warn);
  font-size: var(--fs-12);
  line-height: 1.35;
}
.goals-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding-top: var(--sp-1);
  border-top: 1px solid var(--hairline);
}
.goals-row-top {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-2);
  font-size: var(--fs-12);
  font-weight: var(--fw-semibold);
}
.goals-pay {
  color: #c07ae0;
  font-variant-numeric: tabular-nums;
}
.goals-what {
  font-size: var(--fs-11);
  color: var(--fg-3);
  line-height: 1.3;
}
.goals-what b {
  color: var(--fg-2);
  font-weight: var(--fw-semibold);
  white-space: nowrap;
}
.goals-count {
  font-size: var(--fs-11);
  color: var(--fg-3);
}
#goals.shut .goals-reward,
#goals.shut .goals-list,
#goals.shut .goals-count,
#goals.shut .goals-stall {
  display: none;
}

/* On a phone the card is its tier line alone, in the gap under the toolbar. */
body.phone #goals {
  top: calc(var(--h-bar) + 8px);
  left: 50%;
  right: auto;
  width: min(150px, 38vw);
  /* The desktop cap subtracts the rail and the inspector, which a phone stacks. */
  max-width: none;
  transform: translateX(-50%);
  padding: 6px 10px;
  gap: 4px;
}
body.phone #goals .goals-unit,
body.phone #goals .goals-head,
body.phone #goals .goals-reward,
body.phone #goals .goals-list,
body.phone #goals .goals-count,
body.phone #goals .goals-stall {
  display: none;
}
body.phone #goals .goals-line {
  font-size: var(--fs-11);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
body.phone.build-open #goals {
  display: none;
}

/* The day's report (DESIGN.md "Engagement"): bottom centre, above the toast's line. */
#dayReport {
  position: fixed;
  left: 50%;
  bottom: calc(var(--sp-5) + var(--h-lg) + var(--sp-2));
  z-index: 19;
  width: min(380px, calc(100vw - 32px));
  padding: var(--sp-2) var(--sp-3) var(--sp-3);
  border-radius: var(--r-md);
  transform: translateX(-50%);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
#dayReport.in {
  animation: report-in 380ms var(--ease-out);
}
@keyframes report-in {
  from {
    opacity: 0;
    transform: translate(-50%, 12px);
  }
  to {
    opacity: 1;
    transform: translate(-50%, 0);
  }
}
.report-title {
  font-size: var(--fs-11);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-3);
}
.report-row {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  font-size: var(--fs-13);
}
.report-key {
  width: 52px;
  flex: none;
  color: var(--fg-3);
}
.report-row b {
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-semibold);
}
.report-row b.good {
  color: var(--good);
}
.report-row b.bad {
  color: var(--bad);
}
.report-why {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--fs-12);
  color: var(--fg-3);
}
.report-headline {
  margin-top: 2px;
  padding-top: 6px;
  border-top: 1px solid var(--hairline);
  font-size: var(--fs-12);
  color: var(--fg-2);
}
body.phone #dayReport {
  bottom: calc(var(--h-dock) + 64px);
}
