  :root {
    --bg: #1a1d24;
    --panel: #242832;
    --panel-border: #343a48;
    --text: #e8eaf0;
    --text-dim: #9aa2b5;
    --accent: #4a9eff;
  }
  * { margin: 0; padding: 0; box-sizing: border-box; }
  html, body { height: 100%; overflow: hidden; background: var(--bg); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
  /* The map fills the window and the bar floats over it, so the play area never shrinks
     when the toolbar grows a row. Zoom then only changes the scale of the map, never
     how much screen you get. */
  #app { position: relative; height: 100%; overflow: hidden; }

  #toolbar {
    position: absolute; top: 0; left: 0; right: 0; z-index: 20;
    display: flex; flex-direction: column; gap: 4px; padding: 6px 12px;
    background: rgba(36,40,50,0.94); border-bottom: 1px solid var(--panel-border);
    user-select: none; backdrop-filter: blur(6px);
  }
  /* each bar row lays out horizontally and may wrap within itself */
  /* A single thin row: identity left, the grouped readouts dead centre, clock right.
     Three columns rather than flex + spacers, so the middle is centred against the
     WINDOW and does not drift as the city name grows or shrinks. The right column is
     empty — it reserves the clock's space, since the clock is positioned absolutely. */
  #bar-main {
    display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
    gap: 8px; padding-right: 0;
  }
  #bar-left { display: flex; align-items: center; gap: 5px; min-width: 0; }
  #bar-main > #stats { justify-self: center; }

  /* ── View rail, down the left edge ──────────────────────────────────────────
     Overlays and the underground view are lenses you look THROUGH, not things you
     build, so they no longer share a bar with the tools. */
  #view-rail {
    position: absolute; left: 12px; z-index: 8;
    display: flex; flex-direction: column; gap: 6px; align-items: flex-start;
  }
  .rail-btn {
    display: flex; align-items: center; gap: 7px; height: 34px; padding: 0 11px 0 9px;
    border-radius: 9px; border: 1px solid var(--panel-border);
    background: rgba(36,40,50,0.92); color: var(--text);
    cursor: pointer; font-family: inherit; font-size: 11px; white-space: nowrap;
  }
  .rail-btn:hover { background: #363c4e; }
  .rail-btn.active { border-color: var(--accent); background: #2a3a52; box-shadow: 0 0 0 1px var(--accent) inset; }
  .rail-btn .ico { font-size: 15px; }
  /* the rail is on the left edge, so its menu has to open sideways, not downwards */
  #view-rail .dd-menu { top: 0; left: calc(100% + 6px); }
  /* Date and speed are pinned to the top-right corner so they stay there whatever the
     rest of the bar does — left in the flow they dropped to a second line on a narrow
     window. #bar-main reserves the space with padding so nothing runs underneath. */
  #clock { position: absolute; top: 6px; right: 12px; }
  #toolbar .title { font-weight: 700; color: var(--text); margin-right: 8px; letter-spacing: 0.5px; }
  /* One height for every control on the build bar — tabs, the tools inside the strip,
     and Inspect/Bulldoze. Defined on :root, NOT #toolbar: the build bar is a sibling of
     the toolbar now, so anything scoped to #toolbar would not reach it. */
  :root { --btn-h: 46px; }
  /* Identity: the globe opens the World menu, the name beside it renames the city. */
  #city-block {
    display: flex; align-items: center; gap: 2px; height: var(--btn-h);
    padding-right: 8px; margin-right: 4px; border-right: 1px solid var(--panel-border);
  }
  /* the .dd wrapper needs a height of its own, or the button's 100% has nothing to
     resolve against and collapses to its content */
  #city-block > .dd { display: flex; height: 100%; }
  .globe-btn {
    display: flex; align-items: center; gap: 1px; height: 100%; padding: 0 7px;
    border-radius: 8px; border: 1px solid var(--panel-border);
    background: #2c3140; color: var(--text); cursor: pointer; font-family: inherit;
  }
  .globe-btn:hover { background: #363c4e; }
  .globe-btn .ico { font-size: 18px; line-height: 1; }
  .globe-btn .caret { font-size: 8px; color: var(--text-dim); }
  /* The city you are playing, renameable in place */
  #city-title {
    background: none; border: 1px solid transparent; color: var(--text);
    font-family: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
    padding: 3px 8px; border-radius: 7px; margin-right: 8px; max-width: 220px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  #city-title:hover { background: #2c3140; border-color: var(--panel-border); }
  #city-title.unsaved { color: var(--text-dim); font-style: italic; font-weight: 500; }
  #city-title.viewing { color: #e8c15a; }
  .tool-btn {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 1px; height: var(--btn-h);
    padding: 4px 6px; border-radius: 7px; border: 1px solid var(--panel-border);
    background: #2c3140; color: var(--text); cursor: pointer; font-size: 11px;
    min-width: 52px; transition: background 0.1s, border-color 0.1s;
  }
  .tool-btn:hover { background: #363c4e; }
  .tool-btn.active { border-color: var(--accent); background: #2a3a52; box-shadow: 0 0 0 1px var(--accent) inset; }
  .tool-btn .swatch { width: 20px; height: 12px; border-radius: 3px; border: 1px solid rgba(255,255,255,0.25); }
  .tool-btn .cost { color: var(--text-dim); font-size: 9px; }
  /* Not yet earned. Still legible — the point is to advertise what is coming, so this
     dims it without hiding it, and the chip stays colourful enough to recognise later. */
  .tool-btn.locked { opacity: 0.45; }
  .tool-btn.locked .ico { filter: grayscale(1); }
  .tool-btn.locked .cost { color: #e8c15a; }
  .tool-btn.locked:hover { background: #2c3140; }
  /* Every button icon renders in the same box, whatever the emoji's natural size */
  .tool-btn .ico, .cat-btn .ico {
    font-size: 15px; line-height: 16px; height: 16px; width: 18px;
    display: inline-block; text-align: center; font-family: "Apple Color Emoji", "Segoe UI Emoji", sans-serif;
  }
  /* colour chip ties each icon back to how it looks on the map */
  .tool-btn .chip { width: 20px; height: 3px; border-radius: 2px; margin-top: 1px; }

  /* Compact single-line buttons for panels and file actions */
  .mini-btn {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 5px 9px; border-radius: 7px; border: 1px solid var(--panel-border);
    background: #2c3140; color: var(--text); cursor: pointer; font-size: 11px;
    white-space: nowrap; height: 28px;
  }
  .mini-btn:hover { background: #363c4e; }
  .mini-btn.active { border-color: var(--accent); background: #2a3a52; box-shadow: 0 0 0 1px var(--accent) inset; }
  .mini-btn .ico {
    font-size: 14px; width: 16px; text-align: center;
    font-family: "Apple Color Emoji", "Segoe UI Emoji", sans-serif;
  }
  .mini-btn.danger { background: #40282c; }
  .mini-btn.danger:hover { background: #542f35; }
  /* Everything that is not building. Two-high columns, so a PAIR of these stands exactly
     as tall as one build button — they no longer set the height of the whole bar.
     With an odd number the last column simply has one cell. */
  #panel-row { display: flex; gap: 5px; align-items: center; }
  /* the little ▾ that marks a button as opening a menu */
  .mini-btn .caret { font-size: 8px; color: var(--text-dim); margin-left: 1px; }
  .mini-btn.active .caret { color: var(--text); }

  /* Dropdown groups (overlays, file actions) */
  .dd { position: relative; }
  .dd-menu {
    position: absolute; top: calc(100% + 6px); left: 0; z-index: 30;
    background: var(--panel); border: 1px solid var(--panel-border);
    border-radius: 10px; padding: 6px; min-width: 210px;
    box-shadow: 0 12px 32px rgba(0,0,0,0.55);
  }
  .dd-menu[hidden] { display: none; }
  .dd-item {
    display: flex; align-items: center; gap: 9px; width: 100%;
    padding: 7px 9px; border-radius: 7px; border: 1px solid transparent;
    background: none; color: var(--text); font-size: 12px; cursor: pointer; text-align: left;
  }
  /* the overlay menu now lists every service, so it needs to scroll */
  #overlay-menu { max-height: 70vh; overflow-y: auto; }
  .dd-sep {
    font-size: 9px; letter-spacing: 0.6px; text-transform: uppercase;
    color: var(--text-dim); padding: 8px 9px 3px; margin-top: 4px;
    border-top: 1px solid var(--panel-border);
  }
  .dd-item:hover { background: #363c4e; }
  .dd-item.active { border-color: var(--accent); background: #2a3a52; }
  .dd-item .ico { font-size: 15px; width: 18px; text-align: center; }
  .dd-item .lbl { flex: 1; }
  .dd-item kbd {
    font-size: 9px; color: var(--text-dim); background: rgba(255,255,255,0.08);
    border-radius: 3px; padding: 1px 5px; font-family: inherit;
  }

  /* Category row + the tool strip it reveals */
  /* ── Build bar, along the bottom ────────────────────────────────────────────
     Building is the core loop, so it gets a bar to itself rather than fighting the
     readouts for room on row one. Two fixed slots: the tool strip on top, the tabs
     underneath. The tabs never move, whatever is open. */
  #build-bar {
    position: absolute; left: 0; right: 0; bottom: 0; z-index: 20;
    display: flex; align-items: flex-end; gap: 14px;
    padding: 8px 12px 10px;
    background: linear-gradient(to top, rgba(20,22,28,0.97), rgba(20,22,28,0.88));
    border-top: 1px solid var(--panel-border);
  }
  /* the readouts you watch constantly, bottom left */
  #build-left { display: flex; align-items: center; gap: 12px; flex: 0 0 auto; }
  /* building owns the middle and takes all the slack, so the tabs stay centred */
  #build-center {
    flex: 1 1 auto; min-width: 0;
    display: flex; flex-direction: column; align-items: center; gap: 6px;
  }
  #cat-row { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; justify-content: center; }
  /* Inspect and Bulldoze are verbs, not categories — kept apart at the END of the row,
     past a divider, but sized exactly like the tabs so the strip reads as one row of
     equal buttons rather than two different shapes of thing. */
  #cat-row .verbs {
    display: flex; gap: 6px; align-items: center;
    padding-left: 10px; margin-left: 4px; border-left: 1px solid var(--panel-border);
  }
  /* matches .cat-btn exactly — square, icon-only, same icon size */
  .tool-btn.verb {
    flex-direction: row; justify-content: center; gap: 0;
    height: var(--btn-h); width: var(--btn-h); padding: 0; min-width: 0;
  }
  .tool-btn.verb .ico { font-size: 15px; width: auto; }
  .tool-btn.verb .cost, .tool-btn.verb .chip { display: none; }
  /* Icon-only tabs. Eight labelled ones plus a strip never fit a row; the name is
     still there as the strip's heading and in the hover help. */
  .cat-btn {
    display: flex; align-items: center; justify-content: center;
    height: var(--btn-h); width: var(--btn-h);
    border-radius: 8px; border: 1px solid var(--panel-border);
    background: #2c3140; color: var(--text); cursor: pointer;
  }
  .cat-btn .ico { font-size: 21px; line-height: 1; height: auto; width: auto; }
  .cat-btn:hover { background: #363c4e; }
  .cat-btn.active { border-color: var(--accent); background: #2a3a52; box-shadow: 0 0 0 1px var(--accent) inset; }
  .cat-btn .ico { font-size: 15px; }
  /* The tool strip opens inline, immediately to the right of the category button
     that owns it, so it stays on the first row and the later buttons shift along.
     The bracket styling is what ties the tools back to their parent button. */
  /* The open category's tools, in a fixed slot above the tabs. It may wrap to as many
     lines as it needs — nothing else on screen depends on its height. */
  #tool-strip {
    display: flex; gap: 4px; align-items: center; flex-wrap: wrap; justify-content: center;
    padding: 5px 8px; border-radius: 10px; max-width: 100%;
    border: 1px solid var(--accent); background: rgba(90,150,255,0.10);
  }
  /* `hidden` alone loses to display:flex — the same trap as #whoami */
  #tool-strip[hidden] { display: none; }
  .strip-head {
    display: flex; align-items: center; gap: 5px; padding: 0 8px 0 2px; margin-right: 2px;
    font-size: 12px; font-weight: 700; color: var(--text);
    border-right: 1px solid var(--panel-border); align-self: stretch;
  }
  .strip-head .ico { font-size: 15px; }
  /* A group wraps as a unit, so a label never strands itself on the line above. It also
     wraps INTERNALLY, so a single big group (Power has seven) still fits a narrow
     window rather than overflowing. */
  .strip-group { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; justify-content: center; }
  /* the first group label follows the heading, which already draws a rule */
  .strip-head + .strip-group .strip-sep { border-left: none; margin-left: 0; padding-left: 0; }
  /* divides a merged strip back into its old groups (Power | Water) */
  .strip-sep {
    display: flex; align-items: center; align-self: stretch;
    padding: 0 7px 0 5px; margin-left: 3px;
    border-left: 1px solid var(--panel-border);
    font-size: 10px; font-weight: 600; color: var(--text-dim); white-space: nowrap;
  }
  /* keep their natural width: the STRIP scrolls when space is short, the buttons must
     not squash into each other */
  #tool-strip .tool-btn { height: calc(var(--btn-h) - 8px); flex: 0 0 auto; }

  /* Floating "−$25" spend feedback at the cursor */
  #spend {
    position: absolute; pointer-events: none; z-index: 7;
    font-size: 14px; font-weight: 700; color: #ff9a9a;
    text-shadow: 0 1px 3px rgba(0,0,0,0.9);
    opacity: 0; transform: translateY(6px);
    transition: opacity 0.18s, transform 0.18s;
  }
  #spend.show { opacity: 1; transform: translateY(-4px); }
  #spend.refund { color: #7ee08a; }
  #spend.preview { color: #cfe0ff; }   /* estimate while dragging, before you commit */
  .tool-btn kbd {
    font-size: 9px; color: var(--text-dim); background: rgba(255,255,255,0.08);
    border-radius: 3px; padding: 0 4px; font-family: inherit;
  }
  /* Horizontal variant so Bulldoze matches the category buttons' height */
  .tool-btn.inline {
    flex-direction: row; gap: 6px; padding: 0 11px; min-width: 0;
    font-size: 12px; font-weight: 600; height: var(--btn-h);
  }
  .tool-btn.inline .cost, .tool-btn.inline .chip { display: none; }
  #spacer { flex: 1; }
  /* Demand · population · money — the three you watch constantly, pinned left */
  /* Population is a headline number, not a chip — it was unreadable at chip size.
     It sits in #build-left, NOT #stats, so it never inherited that rule's colour and
     was falling back to the browser default (black on a black bar). Set it here. */
  #build-left #stat-pop {
    font-size: 17px; font-weight: 700; padding: 2px 6px; gap: 6px;
    background: none; letter-spacing: 0.2px; color: #fff;
  }
  #build-left #stat-pop .v { color: #fff; }
  #build-left #stat-pop .si { font-size: 15px; width: 18px; }
  /* Population reads like the treasury: headline number, monthly change beneath it.
     Same trap as #stat-pop — nothing here inherits #stats' colour, so it is set outright. */
  /* Both bottom-left readouts open a panel, so they get the same affordance as #tier-block:
     a hover plate saying "this is a button", not just a tooltip cursor. */
  #pop-block {
    display: flex; flex-direction: column; align-items: flex-start; cursor: pointer;
    padding: 2px 8px; border-radius: 7px; border: 1px solid transparent;
  }
  #pop-block:hover { background: #2c3140; border-color: var(--panel-border); }
  #pop-net {
    font-size: 10px; color: var(--text-dim); padding-left: 8px;
    font-variant-numeric: tabular-nums; cursor: help;
  }
  #pop-net.pos { color: #7ee08a; } #pop-net.neg { color: #ff8a8a; }
  /* the treasury opens Finance, so it has to look clickable */
  #money-block {
    display: flex; flex-direction: column; align-items: flex-start; cursor: pointer;
    padding: 2px 8px; border-radius: 7px; border: 1px solid transparent;
  }
  #money-block:hover { background: #2c3140; border-color: var(--panel-border); }
  /* the remaining readouts follow on the right of the same line */
  #money { color: #7ee08a; font-weight: 700; font-size: 14px; line-height: 1.15; font-variant-numeric: tabular-nums; }
  #money.flash, #money.debt { color: #ff6b6b; }
  #net { font-size: 10px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
  #net.pos { color: #7ee08a; } #net.neg { color: #ff8a8a; }
  /* Stats read as a dense strip of chips rather than two wide lines */
  #stats {
    display: flex; flex-wrap: wrap; align-items: center; gap: 2px 4px;
    color: var(--text); font-size: 11px; font-variant-numeric: tabular-nums;
    /* may shrink, must NOT grow: #spacer already absorbs the slack, and letting the
       stats row grow too pushed the treasury onto a line of its own on a wide window */
    flex: 0 1 auto; min-width: 0;
  }
  #stats .stat {
    display: inline-flex; align-items: center; gap: 3px;
    padding: 1px 5px; border-radius: 5px; background: rgba(255,255,255,0.05);
    white-space: nowrap; cursor: help;
  }
  #stats .stat .si {
    font-size: 11px; width: 13px; text-align: center;
    font-family: "Apple Color Emoji", "Segoe UI Emoji", sans-serif;
  }
  /* grouped chips carry more weight than the old single-value ones */
  #stats .stat.grp { font-size: 12px; padding: 3px 8px; gap: 5px; }
  #stats .stat.grp .si { font-size: 13px; width: 15px; }
  #stats .warn { color: #ffb347; background: rgba(255,179,71,0.14); font-weight: 600; }
  #stats .crit { color: #ff6b6b; background: rgba(255,107,107,0.16); font-weight: 600; }
  #hint { color: var(--text-dim); font-size: 10px; text-align: center; opacity: 0.75; }
  .sep { width: 1px; height: 30px; background: var(--panel-border); margin: 0 2px; }
  #clear-btn { background: #40282c; }
  #clear-btn:hover { background: #542f35; }

  #clock { display: flex; flex-direction: column; align-items: center; gap: 2px; }
  #date { color: var(--text); font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; }
  #clock .btns { display: flex; gap: 4px; }
  #clock button {
    background: #2c3140; border: 1px solid var(--panel-border); color: var(--text);
    border-radius: 6px; font-size: 11px; padding: 2px 8px; cursor: pointer;
  }
  #clock button:hover { background: #363c4e; }

  #rci { display: flex; gap: 6px; align-items: center; }
  .rci-col { display: flex; flex-direction: column; align-items: center; gap: 2px; }
  .rci-track {
    width: 12px; height: 44px; background: #1c1f27; border: 1px solid var(--panel-border);
    border-radius: 3px; position: relative; overflow: hidden;
  }
  .rci-track::after {
    content: ''; position: absolute; left: 0; right: 0; top: 50%; height: 1px;
    background: rgba(255,255,255,0.35);
  }
  .rci-fill { position: absolute; left: 1px; right: 1px; }
  .rci-col span { font-size: 9px; color: var(--text-dim); font-weight: 700; }

  /* full window, behind the floating bar */
  #canvas-wrap { position: absolute; inset: 0; overflow: hidden; }
  canvas { display: block; width: 100%; height: 100%; cursor: crosshair; }
  canvas.panning { cursor: grabbing; }
  /* Inspect mode: a magnifying glass, drawn twice so the white lens keeps a dark
     outline and stays legible over both grass and water. Falls back to `help`. */
  canvas.inspect {
    cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='26' height='26' viewBox='0 0 26 26'%3E%3Cg fill='none' stroke='%23000' stroke-width='4.5' stroke-linecap='round' opacity='.55'%3E%3Ccircle cx='10' cy='10' r='6.5'/%3E%3Cpath d='M15 15L22 22'/%3E%3C/g%3E%3Cg fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='10' cy='10' r='6.5'/%3E%3Cpath d='M15 15L22 22'/%3E%3C/g%3E%3C/svg%3E") 10 10, help;
  }

  /* The toolbar floats over the map, and it wraps, so every right-hand widget
     hangs off --bar-h (measured from the real toolbar in 15-toolbar.js) rather
     than a guessed offset. Stacking down the right edge: minimap, then zoom. */
  #zoom-ctrl {
    position: absolute; top: calc(var(--bar-h, 150px) + 184px); right: 12px;
    display: flex; flex-direction: column; gap: 6px; z-index: 6;
    transition: top 0.15s ease-out;   /* the bar changes height as menus open */
  }
  #zoom-ctrl button {
    width: 36px; height: 36px; border-radius: 8px; border: 1px solid var(--panel-border);
    background: rgba(36,40,50,0.92); color: var(--text); font-size: 18px; font-weight: 700;
    cursor: pointer; line-height: 1;
  }
  #zoom-ctrl button:hover { background: #363c4e; }

  /* Whole-city minimap, top right, clear of the floating toolbar */
  #minimap {
    position: absolute; right: 12px; top: calc(var(--bar-h, 150px) + 12px); z-index: 7;
    width: 160px; height: 160px; border-radius: 8px; cursor: crosshair;
    transition: top 0.15s ease-out;
    border: 1px solid var(--panel-border); background: rgba(20,23,30,0.9);
    box-shadow: 0 6px 20px rgba(0,0,0,0.5);
  }
  #toast {
    position: absolute; left: 50%; bottom: 24px; transform: translateX(-50%);
    background: rgba(20,22,28,0.92); color: var(--text); padding: 8px 16px;
    border-radius: 8px; border: 1px solid var(--panel-border); font-size: 13px;
    opacity: 0; transition: opacity 0.25s; pointer-events: none;
  }
  #toast.show { opacity: 1; }

  /* Standing problems — they persist until fixed, and jump you to the trouble. They now
     live in the right-hand zone of the build bar rather than floating over the map. The
     max-height is set from the middle zone in measureBar(), so a long list scrolls
     instead of driving the whole bar taller. */
  #alerts {
    flex: 0 0 auto; width: 300px;
    display: flex; flex-direction: column; align-items: stretch; gap: 5px;
    overflow-y: auto; overscroll-behavior: contain;
  }
  #alerts::-webkit-scrollbar { width: 8px; }
  #alerts::-webkit-scrollbar-thumb { background: #3a4152; border-radius: 4px; }
  .alert {
    display: flex; align-items: center; gap: 7px; text-align: left;
    padding: 6px 10px; border-radius: 8px; cursor: pointer;
    font-size: 12px; font-family: inherit; line-height: 1.25;
    background: rgba(24,27,35,0.94); color: var(--text);
    border: 1px solid var(--panel-border); border-left-width: 3px;
    box-shadow: 0 4px 14px rgba(0,0,0,0.45);
  }
  .alert:hover { background: #2f3543; }
  .alert.crit { border-left-color: #ff6b6b; }
  .alert.warn { border-left-color: #ffb347; }
  .alert .ai { font-size: 15px; font-family: "Apple Color Emoji", "Segoe UI Emoji", sans-serif; }
  .alert .go { color: var(--text-dim); font-size: 10px; margin-left: 2px; }

  #tip {
    position: absolute; background: rgba(18,21,29,0.95); color: var(--text);
    border: 1px solid var(--panel-border); border-radius: 8px; padding: 6px 10px;
    font-size: 12px; line-height: 1.5; pointer-events: none; white-space: nowrap;
    z-index: 5; display: none;
  }
  /* Instant tooltip for HUD readouts and menu buttons (native title= is too slow
     and gets cancelled whenever the HUD refreshes) */
  #hud-tip {
    position: fixed; display: none; z-index: 50; max-width: 320px;
    background: rgba(18,21,29,0.97); color: var(--text);
    border: 1px solid var(--panel-border); border-radius: 8px;
    padding: 7px 10px; font-size: 12px; line-height: 1.45;
    pointer-events: none; box-shadow: 0 6px 20px rgba(0,0,0,0.5);
    /* Help text is set as textContent, so newlines need honouring for the multi-line
       readouts. pre-WRAP rather than pre-line: pre-line collapses leading spaces, which
       flattened the indented sub-items under "Services" and "Utilities". */
    white-space: pre-wrap;
  }
  .stat { cursor: help; }
  #tip .dim { color: var(--text-dim); }
  #tip .bad { color: #ff8a8a; }
  #tip .good { color: #7ee08a; }

  .modal {
    position: absolute; inset: 0; background: rgba(10,12,16,0.55);
    /* ABOVE the toolbar and the build bar, both of which sit at 20. At 10 a modal was
       underneath them, so anything tall — the region map's legend, a Close button — was
       hidden behind the chrome with no way to reach it. */
    display: flex; align-items: center; justify-content: center; z-index: 40;
  }
  .modal[hidden] { display: none; }
  .modal-box {
    background: var(--panel); border: 1px solid var(--panel-border); border-radius: 12px;
    padding: 20px 24px; min-width: 380px; max-width: 480px; max-height: 80%;
    overflow-y: auto; color: var(--text);
    box-shadow: 0 12px 40px rgba(0,0,0,0.5);
  }
  .modal-box h3 { margin-bottom: 4px; font-size: 16px; }
  .modal-box h4 { margin: 14px 0 6px; font-size: 13px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.5px; }
  .modal-box .note { color: var(--text-dim); font-size: 11px; margin: 2px 0 10px; }
  .tax-row { display: flex; align-items: center; gap: 10px; margin: 8px 0; font-size: 13px; }
  .tax-row label { width: 110px; }
  .tax-row input[type=range] { flex: 1; accent-color: var(--accent); }
  .tax-row output { width: 38px; text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
  /* Tax headroom: current rate against what each sector will accept */
  #tax-headroom { font-size: 12px; font-variant-numeric: tabular-nums; }
  #tax-headroom .row { display: flex; align-items: center; gap: 8px; padding: 2px 0; }
  #tax-headroom .row > span:first-child { width: 66px; color: var(--text-dim); }
  #tax-headroom .bar { width: 78px; height: 7px; background: rgba(255,255,255,0.07); border-radius: 4px; overflow: hidden; flex: none; }
  #tax-headroom .bar > span { display: block; height: 100%; border-radius: 4px; }
  #fin-budget { font-size: 12px; line-height: 1.7; font-variant-numeric: tabular-nums; }
  #fin-budget .row { display: flex; justify-content: space-between; }
  #fin-budget .pos { color: #7ee08a; } #fin-budget .neg { color: #ff8a8a; }
  #fin-budget .row.sub { padding-left: 14px; font-size: 11px; color: var(--text-dim); }
  #fin-budget .row.sub .pos { color: #6aab77; }
  #fin-budget .total { border-top: 1px solid var(--panel-border); margin-top: 4px; padding-top: 4px; font-weight: 700; }
  #fin-loans { font-size: 12px; }
  #fin-loans .loan-btns { display: flex; gap: 8px; margin-top: 6px; }
  .modal-box button {
    background: #2c3140; border: 1px solid var(--panel-border); color: var(--text);
    border-radius: 6px; font-size: 12px; padding: 6px 12px; cursor: pointer;
  }
  .modal-box button:hover { background: #363c4e; }
  .modal-box button:disabled { opacity: 0.4; cursor: not-allowed; }
  .modal-box button:disabled:hover { background: #2c3140; }
  .modal-box .close-row { text-align: right; margin-top: 14px; }
  #goals-list .goal {
    display: flex; align-items: center; gap: 10px; padding: 7px 8px;
    border-radius: 8px; font-size: 12px; border: 1px solid transparent;
  }
  #goals-list .goal.done { background: rgba(126,224,138,0.10); border-color: rgba(126,224,138,0.35); }
  #goals-list .goal .ico { font-size: 17px; width: 22px; text-align: center; }
  #goals-list .goal .txt { flex: 1; }
  #goals-list .goal .txt b { display: block; }
  #goals-list .goal .txt span { color: var(--text-dim); }
  #goals-list .goal .when { color: #7ee08a; font-size: 11px; white-space: nowrap; }
  #goals-list .goal .prog {
    color: var(--text-dim); font-size: 11px; white-space: nowrap; font-variant-numeric: tabular-nums;
  }
  #goals-list .goal.locked .ico { filter: grayscale(1); opacity: 0.55; }
  /* City tiers — the ladder, one row per rung. Shares the milestone row's shape so the
     two panels read as siblings, but every rung carries its full requirement list. */
  #tiers-list .tier-row {
    display: flex; align-items: flex-start; gap: 10px; padding: 7px 8px;
    border-radius: 8px; font-size: 12px; border: 1px solid transparent;
  }
  #tiers-list .tier-row.done { background: rgba(126,224,138,0.10); border-color: rgba(126,224,138,0.35); }
  #tiers-list .tier-row.cur { background: rgba(125,180,255,0.13); border-color: rgba(125,180,255,0.45); }
  #tiers-list .tier-row.locked .ico { filter: grayscale(1); opacity: 0.55; }
  #tiers-list .tier-row .ico { font-size: 17px; width: 22px; text-align: center; }
  #tiers-list .tier-row .txt { flex: 1; }
  #tiers-list .tier-row .txt b { display: block; }
  #tiers-list .reqs { display: flex; flex-direction: column; gap: 1px; margin-top: 2px; }
  #tiers-list .req { font-size: 11px; color: var(--text-dim); }
  #tiers-list .req.met { color: #7ee08a; }
  #tiers-list .req.unmet { color: #ff8a8a; }
  #tiers-list .req .dim { color: var(--text-dim); }
  /* What the rung pays out. Set apart from the requirements above it by a hairline, so
     "what it costs" and "what it gives" do not read as one run-on list. */
  #tiers-list .grants {
    display: block; margin-top: 5px; padding-top: 4px; font-size: 11px;
    border-top: 1px solid rgba(255,255,255,0.07); color: var(--text-dim); line-height: 1.6;
  }
  #tiers-list .grants-lbl {
    display: inline-block; margin-right: 6px; font-size: 9px; letter-spacing: 0.08em;
    text-transform: uppercase; color: #8b93a5;
  }
  #tiers-list .grants .cash { display: block; color: #7ee08a; font-size: 12px; }
  #tiers-list .grants .gitems { display: block; }
  #tiers-list .grants .sep { opacity: 0.4; }
  #tiers-list .tier-row.locked .grants { color: #b9a25f; }
  #tiers-list .tier-row .when { color: var(--text-dim); font-size: 11px; white-space: nowrap; }
  #tiers-list .tier-row.cur .when { color: #9fc4ff; font-weight: 700; }
  #tiers-list .tier-row.done .when { color: #7ee08a; }
  /* Promotion panel — the one modal the game opens for you, so it is centred and
     celebratory rather than laid out as another list to read. */
  #promo-modal .modal-box { text-align: center; max-width: 420px; }
  #promo-crest { font-size: 54px; line-height: 1.1; margin: 4px 0 2px; }
  #promo-modal h3 { justify-content: center; }
  .promo-grant {
    margin: 12px 0; padding: 9px; border-radius: 9px; font-size: 15px;
    background: rgba(126,224,138,0.12); border: 1px solid rgba(126,224,138,0.4);
  }
  .promo-grant .dim { color: var(--text-dim); font-size: 12px; }
  .promo-head {
    font-size: 10px; letter-spacing: 0.09em; text-transform: uppercase;
    color: var(--text-dim); margin-top: 12px;
  }
  .promo-lead { font-size: 17px; font-weight: 700; margin-top: 3px; }
  .promo-rest { color: var(--text-dim); font-size: 12px; margin-top: 5px; line-height: 1.5; }
  .promo-none { color: var(--text-dim); font-size: 12px; margin-top: 12px; }
  /* Demographics panel */
  #demo-vitals, #demo-services {
    display: grid; grid-template-columns: 1fr 1fr; gap: 3px 14px;
    font-size: 12px; font-variant-numeric: tabular-nums;
  }
  #demo-vitals .row, #demo-services .row { display: flex; justify-content: space-between; }
  #demo-vitals .row span:first-child, #demo-services .row span:first-child { color: var(--text-dim); }
  #demo-vitals .good, #demo-services .good { color: #7ee08a; }
  #demo-vitals .bad, #demo-services .bad { color: #ff8a8a; }
  /* Per-need bars, worst first — happiness AND health render identically, so the rules
     are keyed on .need itself. Scoping them to #demo-happy is what left the health rows
     as a run of unstyled text when that section was added. */
  #demo-happy .need, #demo-health .need {
    display: flex; align-items: center; gap: 8px; font-size: 12px;
    padding: 2px 0; cursor: help;
  }
  #demo-happy .ni, #demo-health .ni { width: 16px; text-align: center; font-family: "Apple Color Emoji", "Segoe UI Emoji", sans-serif; }
  #demo-happy .nl, #demo-health .nl { width: 86px; color: var(--text-dim); }
  #demo-happy .nbar, #demo-health .nbar { flex: 1; height: 8px; background: rgba(255,255,255,0.06); border-radius: 4px; overflow: hidden; }
  #demo-happy .nbar > span, #demo-health .nbar > span { display: block; height: 100%; border-radius: 4px; }
  #demo-happy .nv, #demo-health .nv { width: 26px; text-align: right; font-variant-numeric: tabular-nums; }
  #demo-pyramid { font-size: 10px; font-variant-numeric: tabular-nums; }
  #demo-pyramid .band { display: flex; align-items: center; gap: 6px; height: 13px; }
  #demo-pyramid .lbl { width: 42px; color: var(--text-dim); text-align: right; }
  #demo-pyramid .barwrap { flex: 1; background: rgba(255,255,255,0.04); border-radius: 2px; }
  #demo-pyramid .bar { height: 9px; border-radius: 2px; }
  #demo-pyramid .num { width: 52px; color: var(--text-dim); }
  /* Cities panel */
  #auth-row, #city-new-row { display: flex; gap: 6px; margin: 6px 0; }
  #auth-row input, #city-new-row input {
    flex: 1; min-width: 0; background: #1c1f27; border: 1px solid var(--panel-border);
    color: var(--text); border-radius: 7px; padding: 6px 9px; font-size: 12px; font-family: inherit;
  }
  #auth-row input:focus, #city-new-row input:focus { outline: none; border-color: var(--accent); }
  #whoami { display: flex; align-items: center; gap: 10px; font-size: 12px; margin: 8px 0; }
  /* an explicit `display` beats the hidden ATTRIBUTE's default display:none, so the
     signed-in row has to opt out again or it shows while signed out */
  #whoami[hidden] { display: none; }
  #whoami b { color: var(--text); }
  #city-list .city {
    border: 1px solid var(--panel-border); border-radius: 9px;
    padding: 8px 10px; margin-bottom: 7px; background: rgba(255,255,255,0.02);
  }
  /* your own cities are picked out; the one you are playing more strongly still */
  #city-list .city.mine { border-color: #3c5a7a; }
  #city-list .city.playing { border-color: var(--accent); background: rgba(74,158,255,0.07); }
  #city-list .ch { display: flex; align-items: baseline; gap: 8px; }
  #city-list .cn { font-weight: 700; font-size: 13px; }
  #city-list .co { font-size: 10px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.4px; }
  #city-list .cp { font-size: 10px; color: var(--accent); text-transform: uppercase; letter-spacing: 0.4px; }
  #city-list .cm { font-size: 11px; color: var(--text); margin-top: 2px; }
  #city-list .cm.dim, #city-list .dim { color: var(--text-dim); }
  #city-list .cb { display: flex; gap: 5px; margin-top: 7px; flex-wrap: wrap; }
  #city-list .cb .mini-btn { height: 24px; font-size: 11px; padding: 3px 8px; }

  /* Your own roadmap notes */
  #idea-row { display: flex; gap: 6px; margin: 6px 0 8px; }
  #idea-row input {
    flex: 1; background: #1c1f27; border: 1px solid var(--panel-border);
    color: var(--text); border-radius: 7px; padding: 6px 9px; font-size: 12px;
    font-family: inherit;
  }
  #idea-row input:focus { outline: none; border-color: var(--accent); }
  #roadmap-mine .idea {
    display: flex; align-items: center; gap: 7px; font-size: 12.5px;
    padding: 3px 0; line-height: 1.5;
  }
  /* built = struck through; tested = your sign-off, shown in green */
  #roadmap-mine .idea.done .rt { text-decoration: line-through; color: var(--text-dim); }
  #roadmap-mine .idea.tested .rt { color: #7ee08a; }
  #roadmap-mine .tick.test { font-size: 10px; opacity: 0.85; }
  #roadmap-mine .idea .rt { flex: 1; }
  #roadmap-mine .tick, #roadmap-mine .del {
    background: none; border: none; color: var(--text-dim); cursor: pointer;
    font-size: 12px; padding: 0 2px; line-height: 1;
  }
  #roadmap-mine .del:hover { color: #ff8a8a; }
  #roadmap-list { font-size: 13px; line-height: 1.9; }
  #roadmap-list .done { color: var(--text-dim); }
  #roadmap-list .done .rt { text-decoration: line-through; }

  /* External connections panel — one block per neighbouring city */
  .conn-city {
    border: 1px solid var(--panel-border); border-radius: 9px;
    padding: 8px 10px; margin: 8px 0; background: rgba(255,255,255,0.02);
  }
  /* A flex row, NOT a floated button. Floating the Disconnect button pulled it out of
     flow so it landed alongside the first slider and read as belonging to Power rather
     than to the city — and it covered the end of the "via …" line. */
  .conn-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
  .conn-head > b { flex: 0 0 auto; }
  /* the corridor list is what gives way when the row is tight, and it ellipsises */
  .conn-head .dim {
    color: var(--text-dim); font-size: 11px;
    flex: 1 1 auto; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  /* `.row` is styled per-panel elsewhere, so these need their own layout or the label
     and its value run together into "Road at the borderno" */
  #conn-body .row, .conn-city .row {
    display: flex; justify-content: space-between; gap: 12px; padding: 2px 0; font-size: 12px;
  }
  #conn-body .row > span:first-child, .conn-city .row > span:first-child { color: var(--text-dim); }
  .conn-toggle {
    display: flex; align-items: center; gap: 8px; padding: 4px 0;
    font-size: 12px; cursor: pointer;
  }

  /* One "what share do we send" slider per resource, inside a connection block */
  .share-row {
    display: flex; align-items: center; gap: 8px; padding: 3px 0; font-size: 11px;
  }
  .share-row label { width: 170px; color: var(--text-dim); flex: 0 0 auto; }
  .share-row input[type=range] { flex: 1 1 auto; min-width: 80px; }
  .share-row output { width: 38px; text-align: right; font-variant-numeric: tabular-nums; }
  .share-row .dim { color: var(--text-dim); }
  .conn-btns { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 5px; }
  .conn-btns .mini-btn[disabled] { opacity: 0.45; cursor: default; }
  .conn-head .conn-drop { flex: 0 0 auto; margin-left: auto; height: 22px; padding: 0 8px; }

  /* ---------- Region map ---------- */
  /* A wider box than the reading panels: the point of this one is the picture. */
  .modal-box.wide { max-width: 760px; }
  .region-map {
    width: 100%; max-height: 62vh; display: block;
    background: #10131a; border: 1px solid var(--panel-border); border-radius: 10px;
  }
  /* Two cities side by side with nothing joining them — the opportunity, not the link */
  .region-map .rm-ghost { stroke: rgba(255,255,255,0.16); stroke-width: 3; stroke-dasharray: 5 6; }
  /* Every link is a dark casing under a coloured core, so where several converge on one
     tile the crossings still read as separate lines rather than one blue smear. */
  .region-map .rm-case { stroke: #10131a; stroke-width: 8; stroke-linecap: round; }
  /* One coloured strand per corridor the link carries, over the casing. */
  .region-map .rm-strand { stroke-width: 3.5; stroke-linecap: round; }
  .region-map .rm-strand.mine { stroke-width: 4.5; }
  /* connected on paper, but nothing on the ground joins them yet */
  .region-map .rm-idle { stroke: #6b7280; stroke-width: 3; stroke-linecap: round; stroke-dasharray: 2 7; }
  .region-map .rm-hex { fill: #232936; stroke: #3a4356; stroke-width: 2; }
  .region-map .rm-hex.mine { stroke: var(--accent); stroke-width: 3; }
  .region-map .rm-hex.current { fill: #2a3a52; stroke: #7ee08a; stroke-width: 4; }
  .region-map .rm-city { cursor: pointer; }
  .region-map .rm-city:hover .rm-hex { fill: #2d3547; }
  .region-map .rm-name { fill: #e8eef7; font-size: 13px; font-weight: 700; text-anchor: middle; pointer-events: none; }
  .region-map .rm-pop  { fill: #9fb4c7; font-size: 12px; text-anchor: middle; pointer-events: none; font-variant-numeric: tabular-nums; }
  .region-map .rm-owner{ fill: #6f7d8d; font-size: 10px; text-anchor: middle; pointer-events: none; }
  #map-tools {
    display: flex; align-items: center; gap: 8px; margin-bottom: 8px;
    font-size: 11px; color: var(--text-dim);
  }
  #map-tools .mini-btn { height: 26px; padding: 0 10px; font-size: 13px; }
  #map-zoom-pct { min-width: 42px; text-align: center; font-variant-numeric: tabular-nums; }
  /* Dragging to pan must not select the labels — without this every pan left the
     tiles smeared with blue selection boxes. */
  .region-map { cursor: grab; touch-action: none; user-select: none; -webkit-user-select: none; }
  .region-map.dragging { cursor: grabbing; }
  /* Your treasury, on your own cities only */
  .region-map .rm-cash { fill: #7ee08a; font-size: 12px; text-anchor: middle; pointer-events: none; font-variant-numeric: tabular-nums; }
  .region-map .rm-cash.neg { fill: #ff8a8a; }

  #map-legend { display: flex; align-items: center; gap: 12px; margin-top: 8px; }
  #map-legend span { display: inline-flex; align-items: center; gap: 5px; }
  #map-legend i { width: 16px; height: 3px; border-radius: 2px; display: inline-block; }
  /* New-city size choice */
  .size-opt {
    display: flex; gap: 10px; align-items: flex-start; padding: 8px 10px;
    border: 1px solid var(--panel-border); border-radius: 8px; margin-bottom: 6px;
    cursor: pointer; font-size: 12px;
  }
  .size-opt:hover { background: #2c3140; }
  .size-opt input { margin-top: 3px; }
  .size-opt .dim { color: var(--text-dim); font-size: 11px; }
  /* The settlement's rank, beside the city name. Hover it for the checklist to the
     next rung; it flashes amber while the city is failing its held tier. */
  /* The Statecraft rank beside the treasury: rank on top, the climb quantified below,
     mirroring how money shows its monthly net. Hover for the full checklist. */
  #tier-block {
    display: flex; flex-direction: column; align-items: flex-start; cursor: pointer;
    padding: 2px 8px; border-radius: 7px; border: 1px solid transparent;
  }
  #tier-block:hover { background: #2c3140; border-color: var(--panel-border); }
  #tier-cur { color: #e8eaf0; font-weight: 700; font-size: 13px; line-height: 1.15; white-space: nowrap; }
  #tier-next { font-size: 10px; color: var(--text-dim); font-variant-numeric: tabular-nums; white-space: nowrap; }
  #tier-next.warn { color: #e8c15a; }


/* Roadmap panel: reference numbers, type badges, filters, and a close button that is
   always reachable. 🔴 The close X is fixed to the modal box, not placed after the list —
   a long roadmap had to be scrolled to the bottom just to dismiss it. */
.modal-box { position: relative; }
.modal-x {
  position: sticky; top: 0; float: right; z-index: 5;
  margin: -4px -4px 0 8px; width: 30px; height: 30px; line-height: 28px;
  border-radius: 8px; border: 1px solid var(--line, #39405a);
  background: #232a3d; color: #cfd8ea; font-size: 15px; cursor: pointer;
}
.modal-x:hover { background: #2e3750; color: #fff; }
#idea-row { display: flex; gap: 6px; align-items: center; }
#idea-row #idea-input { flex: 1 1 auto; min-width: 0; }
#idea-kind {
  background: #232a3d; color: #cfd8ea; border: 1px solid var(--line, #39405a);
  border-radius: 6px; padding: 5px 6px; font: inherit; font-size: 12px;
}
#idea-filters { display: flex; gap: 6px; margin: 8px 0 4px; flex-wrap: wrap; }
.filter-btn {
  background: #1c2233; color: #9fb0cc; border: 1px solid var(--line, #39405a);
  border-radius: 999px; padding: 3px 11px; font: inherit; font-size: 12px; cursor: pointer;
}
.filter-btn:hover { color: #e6edf8; }
.filter-btn.active { background: #33507d; color: #fff; border-color: #4a74ad; }
.idea .rnum {
  flex: 0 0 auto; min-width: 34px; text-align: right;
  color: #7f8ca6; font-variant-numeric: tabular-nums; font-size: 12px;
}
.idea .kind { flex: 0 0 auto; font-size: 12px; }
.idea.bug { border-left: 3px solid #c0563f; }

.idea.dir { border-left: 3px solid #4a74ad; }


/* New-city terrain sliders */
.terra-row { display: flex; align-items: center; gap: 10px; margin: 6px 0; }
.terra-row label { flex: 0 0 52px; color: #9fb0cc; font-size: 13px; }
.terra-row input[type=range] { flex: 1 1 auto; min-width: 0; accent-color: #4a74ad; }
.terra-val { flex: 0 0 130px; text-align: right; color: #cfd8ea; font-size: 12px; }
