/* FOOM layout — responsive shell, regions, overlays. Mobile portrait first; the region placement per layout mode
   (phone · land · tablet · wide, set by src/ui/layout.js as #app[data-layout]) lives in the "layout modes" block
   below. Rules for authors: docs/RESPONSIVE.md. Never letterbox, never scroll sideways. */

/* ---------- backdrop (desktop) ---------- */
#backdrop {
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background-color: var(--ink);
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='64' height='64' shape-rendering='crispEdges'%3E%3Cg fill='%23313952'%3E%3Crect x='5' y='9' width='1' height='1'/%3E%3Crect x='41' y='3' width='1' height='1'/%3E%3Crect x='23' y='29' width='1' height='1'/%3E%3Crect x='56' y='37' width='1' height='1'/%3E%3Crect x='12' y='51' width='1' height='1'/%3E%3Crect x='35' y='58' width='1' height='1'/%3E%3C/g%3E%3Cg fill='%2346506e'%3E%3Crect x='49' y='18' width='1' height='1'/%3E%3Crect x='17' y='40' width='1' height='1'/%3E%3C/g%3E%3Cg fill='%237fb2ff' opacity='.55'%3E%3Crect x='30' y='14' width='1' height='1'/%3E%3C/g%3E%3C/svg%3E"),
    repeating-linear-gradient(0deg, rgba(23, 15, 63, .0) 0 6px, rgba(23, 15, 63, .35) 6px 8px),
    linear-gradient(180deg, #0f1436 0%, #170f3f 45%, #0b0c14 100%);
  background-size: 256px 256px, 100% 8px, 100% 100%;
  image-rendering: pixelated;
}
/* pixel skyline silhouette along the bottom */
#backdrop::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 160px;
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='40' shape-rendering='crispEdges'%3E%3Cg fill='%23151827'%3E%3Crect x='0' y='22' width='14' height='18'/%3E%3Crect x='14' y='12' width='10' height='28'/%3E%3Crect x='24' y='26' width='18' height='14'/%3E%3Crect x='42' y='6' width='8' height='34'/%3E%3Crect x='50' y='18' width='16' height='22'/%3E%3Crect x='66' y='28' width='22' height='12'/%3E%3Crect x='88' y='14' width='12' height='26'/%3E%3Crect x='100' y='24' width='14' height='16'/%3E%3Crect x='114' y='8' width='10' height='32'/%3E%3Crect x='124' y='20' width='20' height='20'/%3E%3Crect x='144' y='16' width='16' height='24'/%3E%3C/g%3E%3Cg fill='%23ffc25a'%3E%3Crect x='17' y='16' width='1' height='1'/%3E%3Crect x='45' y='12' width='1' height='1'/%3E%3Crect x='45' y='20' width='1' height='1'/%3E%3Crect x='91' y='20' width='1' height='1'/%3E%3Crect x='117' y='14' width='1' height='1'/%3E%3Crect x='148' y='22' width='1' height='1'/%3E%3C/g%3E%3Cg fill='%232de6e8'%3E%3Crect x='54' y='24' width='1' height='1'/%3E%3Crect x='128' y='26' width='1' height='1'/%3E%3C/g%3E%3C/svg%3E") repeat-x left bottom / 640px 160px;
  image-rendering: pixelated;
  opacity: .9;
}

/* ---------- app shell: always the full (dynamic) viewport ---------- */
#app {
  /* The City's full size: the height where the 320×200 diorama reaches its width-limited crisp scale (~63% of
     the width). In portrait it is an adaptive header that scrolling folds (tabs.js sets --city-cur). */
  --city-rest: clamp(156px, min(63cqw, 28cqh), 300px);
  --city-h: var(--city-rest);
  position: relative; z-index: 1;
  display: flex; flex-direction: column;
  width: 100%; height: 100vh; height: 100dvh;
  overflow: hidden; overflow-x: clip;
  background: var(--bg);
  padding-left: var(--safe-l); padding-right: var(--safe-r);
  container: app / size;   /* children size against the app box: cqw / cqh */
}
html, body { overflow-x: hidden; max-width: 100vw; }

/* ---------- HUD ---------- */
.hud {
  flex: none; display: flex; gap: 6px; align-items: stretch;
  padding: calc(var(--safe-t) + 6px) 8px 8px;
  background: var(--surface);
  box-shadow: inset 0 calc(-1 * var(--px)) 0 0 var(--ink), inset 0 calc(-2 * var(--px)) 0 0 var(--n3);
  position: relative; z-index: 3;
}
/* each stat is a recessed ink well: dark top lip, light bottom lip */
.hud__stat {
  flex: 1 1 0; min-width: 0; display: flex; align-items: center; gap: 4px; padding: 4px 6px 4px 2px; container-type: inline-size;
  background: var(--ink); --accent: var(--n9);
  box-shadow: inset 0 var(--px) 0 0 #000, inset 0 calc(-1 * var(--px)) 0 0 var(--n3), inset var(--px) 0 0 0 var(--n1), inset calc(-1 * var(--px)) 0 0 0 var(--n1);
}
.hud__stat--wide { flex-grow: 1.45; }
.hud__stat--compact { flex: 0 0 auto; padding-right: 8px; position: relative; container-type: normal; }
.hud__stat.is-button { cursor: pointer; }
.hud__stat.is-button:active { translate: 0 var(--px); }
.hud__badge { display: none; position: absolute; right: -2px; top: -4px; padding: 1px 3px; font-family: var(--font-small); font-size: var(--fs-small);
  line-height: 12px; color: var(--ink); background: linear-gradient(90deg, var(--gold) 0 40%, var(--gold-hi) 40% 55%, var(--gold) 55%); box-shadow: 0 0 0 var(--px) var(--ink); }
.hud__badge.is-on { display: block; }
.hud__stat.is-hidden { display: none; }
.hud__gear { flex: none; align-self: stretch; display: grid; place-items: center; min-width: 44px; min-height: 44px;
  background: var(--ink); color: var(--muted); box-shadow: inset 0 var(--px) 0 0 #000, inset 0 calc(-1 * var(--px)) 0 0 var(--n3); }
.hud__gear.is-hidden { display: none; }
.hud__gear:active { transform: translateY(1px); color: var(--text-hi); }
.hud__icon { display: grid; place-items: center; flex: none; }
/* narrow phones (360 px): a stat well under ~120 px drops its icon so the rate line (e.g. "HUMAN T/S") isn't cut to "HUM…" */
@container (max-width: 118px) { .hud__icon { display: none; } .hud__text { padding-left: 4px; } }
.hud__text { display: grid; gap: 4px; min-width: 0; }
.hud__value { color: var(--accent); white-space: nowrap; overflow: hidden; text-overflow: clip; line-height: 1.125; font-variant-numeric: tabular-nums; text-shadow: var(--px) var(--px) 0 #000; }
.hud__rate { font-family: var(--font-small); font-size: var(--fs-small); color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1; text-transform: uppercase; }
/* the headline stat grows to 24px when its well is wide enough for it */
@container (min-width: 156px) { .hud__stat--wide .hud__value { font-size: var(--fs-big); } }
.hud__stat--gold { --accent: var(--gold); }
.hud__stat--cyan { --accent: var(--cyan); }
.hud__stat--violet { --accent: var(--lav); }
.hud__stat--green { --accent: var(--phos); }
.hud__stat--red { --accent: var(--hot); }
.hud__stat.is-spend .hud__value { animation: spend .35s steps(3); }
@keyframes spend { 0% { color: var(--hot); translate: 0 2px; } 100% { color: var(--accent); translate: 0 0; } }

/* ---------- scene ---------- */
.city-view { flex: none; height: var(--city-h); display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.scene-wrap {
  position: relative; overflow: hidden; z-index: 1; min-width: 0; min-height: 0;
  background: var(--scene-bg, var(--ink));
  /* The live diorama follows the City region without replacing the section. */
  flex: 1 1 0; height: auto;
}
/* the canvas covers the box exactly (scene host sizes + centers it; the diorama is never stretched, never barred) */
.scene { display: block; position: absolute; left: 0; top: 0; image-rendering: pixelated; touch-action: manipulation; }
.scene-frame { position: absolute; inset: 0; pointer-events: none;
  box-shadow: inset 0 var(--px) 0 0 rgba(0, 0, 0, .55), inset 0 calc(-1 * var(--px)) 0 0 rgba(0, 0, 0, .55), inset 0 calc(-2 * var(--px)) 0 0 rgba(0, 0, 0, .25); }

/* The section title beside the City (phone landscape). */
.viewbar { flex: none; display: flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 8px 0 12px;
  background: var(--surface); box-shadow: inset 0 calc(-1 * var(--px)) 0 0 var(--ink); }
.viewbar__title { flex: 1 1 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--gold); font-family: var(--font-small); font-size: var(--fs-small); text-transform: uppercase; }
#app:not([data-layout=wide]) .ticker { display: none; }
#app[data-layout=wide] .viewbar { display: none; }
/* Portrait phones/tablets: the City is an adaptive header laid over the top of the section (same grid cell).
   Sections pad their content below its full height; scrolling folds it 1:1 (tabs.js → --city-cur) by cropping
   the diorama from both edges — it is never rescaled. The NEXT bar rides its bottom edge. */
@media (orientation: portrait) {
  #app[data-layout=phone], #app[data-layout=tablet] {
    display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr) auto;
    grid-template-areas: "hud" "main" "tabs";
  }
  #app[data-layout=phone] .hud, #app[data-layout=tablet] .hud { grid-area: hud; }
  #app[data-layout=phone] .tabbar, #app[data-layout=tablet] .tabbar { grid-area: tabs; }
  #app[data-layout=phone] .panels, #app[data-layout=tablet] .panels { grid-area: main; }
  #app[data-layout=phone] .viewbar, #app[data-layout=tablet] .viewbar { display: none; }
  #app[data-layout=phone] .city-view, #app[data-layout=tablet] .city-view {
    grid-area: main; align-self: start; position: relative; z-index: 4; overflow: hidden;
    height: var(--city-cur, var(--city-rest));
  }
  #app[data-layout=phone] .city-view > .scene-wrap, #app[data-layout=tablet] .city-view > .scene-wrap {
    flex: none; height: var(--city-rest);
    transform: translateY(calc((var(--city-cur, var(--city-rest)) - var(--city-rest)) / 2));
  }
  #app[data-layout=phone] .city-view.is-folded, #app[data-layout=tablet] .city-view.is-folded { visibility: hidden; }
  #app[data-layout=phone] .panels > .panel:not(.panel--fill), #app[data-layout=tablet] .panels > .panel:not(.panel--fill) { padding-top: calc(var(--city-rest) + var(--sp-3)); }
  #app[data-layout=phone] .panels > .panel.panel--fill, #app[data-layout=tablet] .panels > .panel.panel--fill { top: calc(30px + var(--city-fill, var(--city-rest))); }
  /* fill tabs fold/return the header as a whole: animate that step */
  #app[data-layout=phone] .city-view.is-snap, #app[data-layout=tablet] .city-view.is-snap { transition: height .2s steps(5); }
  #app[data-layout=phone] .city-view.is-snap > .scene-wrap, #app[data-layout=tablet] .city-view.is-snap > .scene-wrap { transition: transform .2s steps(5); }
  #app[data-layout=phone] .panels.is-snap > .panel--fill, #app[data-layout=tablet] .panels.is-snap > .panel--fill, #app .panels.is-snap > .guide { transition: top .2s steps(5), transform .2s steps(5); }
}

/* ---------- ticker ---------- */
.ticker {
  flex: none; display: flex; align-items: stretch; height: 28px; overflow: hidden; position: relative; z-index: 2;
  background: var(--ink);
  box-shadow: inset 0 var(--px) 0 0 var(--n3), inset 0 calc(-1 * var(--px)) 0 0 var(--n2);
}
.ticker__label {
  flex: none; display: grid; place-items: center; padding: 0 6px; margin: 6px 0 6px 8px;
  font-family: var(--font-small); font-size: var(--fs-small); line-height: 1; color: var(--umber); background: var(--gold);
  box-shadow: inset 0 var(--px) 0 0 var(--gold-hi), 0 var(--px) 0 0 var(--gold-0);
}
.ticker__label.is-breaking { background: var(--hot); color: var(--n9); box-shadow: inset 0 var(--px) 0 0 var(--orange), 0 var(--px) 0 0 var(--wine); animation: blink 1s steps(1) 3; }
.ticker__track { position: relative; flex: 1; overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 12px, #000 calc(100% - 12px), transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 12px, #000 calc(100% - 12px), transparent 100%); }
.ticker__text {
  position: absolute; left: 0; top: 50%; margin-top: -6px; white-space: nowrap; line-height: 12px;
  font-family: var(--font-small); font-size: var(--fs-small); color: var(--n7); text-transform: uppercase;
  transform: translateX(var(--from, 100%)); will-change: transform;
}
.ticker__text.is-static { position: absolute; left: 8px; right: 8px; overflow: hidden; text-overflow: ellipsis; transform: none; }
@keyframes ticker { from { transform: translateX(var(--from)); } to { transform: translateX(var(--to)); } }

/* ---------- panels ---------- */
.panels { flex: 1 1 auto; min-height: 0; min-width: 0; position: relative; background: var(--bg); }
.panel {
  position: absolute; inset: 0; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch;
  padding: var(--sp-3) 12px 28px; overscroll-behavior: contain; touch-action: pan-y;
  container: panel / inline-size;
}
/* wide panels (tablet portrait, the dock): lists of cards flow into columns; one column below 600 px */
@container panel (min-width: 600px) {
  /* auto-fit: a section with fewer cards than columns stretches them instead of leaving empty columns */
  .panel .card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 380px), 1fr)); column-gap: var(--sp-3); align-items: start; }
  .panel .card-grid > :not(.card) { grid-column: 1 / -1; }
}
@container panel (min-width: 900px) {
  .panel { padding-left: max(12px, calc(50% - 600px)); padding-right: max(12px, calc(50% - 600px)); }
}
.panel.is-entering { animation: panelIn .18s steps(3); }
@keyframes panelIn { from { opacity: .3; translate: 0 6px; } to { opacity: 1; translate: 0 0; } }
.panel__error { padding: 16px; color: var(--hot); font-family: var(--font-small); font-size: var(--fs-small); }

/* ---------- tab bar ---------- */
.tabbar {
  flex: none; display: flex; justify-content: center; overflow-x: auto; overflow-y: hidden; scrollbar-width: none;
  padding: 4px 4px calc(var(--safe-b) + 4px);
  background: var(--surface);
  box-shadow: inset 0 var(--px) 0 0 var(--ink), inset 0 calc(2 * var(--px)) 0 0 var(--n3);
  position: relative; z-index: 3;
}
.tabbar::-webkit-scrollbar { display: none; }
.tab {
  position: relative; flex: 1 0 58px; min-width: 0; max-width: 104px; display: grid; grid-template-columns: minmax(0, 1fr); justify-items: center; align-content: center; gap: 2px;
  min-height: 56px; padding: 6px 2px 4px; color: var(--muted);
}
.tab__icon { display: grid; place-items: center; height: 32px; filter: grayscale(.85) brightness(.75); transition: filter .1s steps(2); }
.tab__label { font-family: var(--font-small); font-size: var(--fs-small); line-height: 1; white-space: nowrap; text-transform: uppercase; }
/* Localized labels stay inside their tab; the full name remains in its accessible label. */
.tabbar > .tab > .tab__label { display: block; width: 100%; overflow: hidden; text-overflow: ellipsis; text-align: center; }
.tab.is-active { color: var(--n9); background: var(--n2); box-shadow: inset 0 calc(2 * var(--px)) 0 0 var(--gold), inset 0 calc(-1 * var(--px)) 0 0 var(--n1), inset var(--px) 0 0 0 var(--n3), inset calc(-1 * var(--px)) 0 0 0 var(--n3); }
.tab.is-active .tab__label { color: var(--n9); }
.tab.is-active .tab__icon { filter: none; transform: translateY(-1px); }
.tab:active .tab__icon { transform: translateY(1px); }
.tab__badge {
  position: absolute; top: 4px; left: calc(50% + 8px); min-width: 16px; height: 16px; padding: 0 3px; display: none;
  font-family: var(--font-small); font-size: var(--fs-small); line-height: 16px; text-align: center;
  color: var(--n9); background: var(--red); box-shadow: 0 0 0 var(--px) var(--ember), inset 0 var(--px) 0 0 var(--hot);
}
.tab__badge.is-on { display: block; }
.tab.is-new .tab__icon { animation: tabNew 1s steps(2) infinite; }
@keyframes tabNew { 50% { filter: none; transform: translateY(-2px); } }

/* =====================================================================================================================
   LAYOUT MODES — src/ui/layout.js sets #app[data-layout] = phone | land | tablet | wide (docs/RESPONSIVE.md).
   phone is the default flex column above. Only containers live here; component looks stay in their own sheets.
   ===================================================================================================================== */
#app { --rail-w: 80px; --side-w: clamp(360px, 34cqw, 480px); }

/* ---- phone, small (< 360 px wide): tighter gutters so 320 px phones keep full-width cards ---- */
@media (max-width: 359px) {
  .panel { padding-left: 8px; padding-right: 8px; }
  .hud { gap: 4px; padding-left: 4px; padding-right: 4px; }
}

/* ---- tablet: a roomier card grid; portrait keeps the resizable City above it, sized for the big screen ---- */
#app[data-layout=tablet] { --city-rest: clamp(220px, min(63cqw, 36cqh), 800px); }
#app[data-layout=tablet] .panel { padding: var(--sp-4) 20px 32px; }
#app[data-layout=tablet] .hud { padding-left: 16px; padding-right: 16px; }
#app[data-layout=tablet] .tab { max-width: 120px; }

/* ---- rail: the tab bar turns into a vertical column of tabs (land + wide) ---- */
#app[data-layout=land] .tabbar, #app[data-layout=wide] .tabbar {
  flex-direction: column; justify-content: flex-start; align-items: stretch;
  width: var(--rail-w); overflow-x: hidden; overflow-y: auto;
  padding: calc(var(--safe-t) + 4px) 4px calc(var(--safe-b) + 4px);
}
#app[data-layout=land] .tab, #app[data-layout=wide] .tab { flex: 0 0 auto; max-width: none; min-height: 56px; }
#app[data-layout=wide] .tabbar { box-shadow: inset calc(-1 * var(--px)) 0 0 0 var(--ink), inset calc(-2 * var(--px)) 0 0 0 var(--n3); }
#app[data-layout=land] .tabbar { box-shadow: inset var(--px) 0 0 0 var(--ink), inset calc(2 * var(--px)) 0 0 0 var(--n3); }
/* the docked tab's rail button: a cyan pip ("it's on screen, under the map") */
.tab.is-docked::after { content: ''; position: absolute; left: 50%; bottom: 3px; width: 12px; height: var(--px); translate: -50% 0; background: var(--cyan); }
.tabsheet--rail { grid-template-columns: repeat(2, 1fr); width: 220px; }

/* ---- short landscape: modest City left, broad section right, navigation at the edge ---- */
#app[data-layout=land] {
  --city-h: auto;
  display: grid;
  grid-template-columns: minmax(0, calc((100cqw - var(--rail-w)) * .25)) minmax(0, 1fr) var(--rail-w);
  grid-template-rows: auto 44px minmax(0, 1fr);
  grid-template-areas: "hud hud rail" "scene viewbar rail" "scene panels rail";
}
#app[data-layout=land] .viewbar { grid-area: viewbar; }
#app[data-layout=land] .city-view { grid-area: scene; }
#app[data-layout=land] .hud { grid-area: hud; gap: 4px; padding: calc(var(--safe-t) + 4px) 6px 6px; }
#app[data-layout=land] .panels { grid-area: panels; }
#app[data-layout=land] .panel { padding: var(--sp-2) 10px 20px; }
#app[data-layout=land] .tabbar { grid-area: rail; }

/* Tablets reserve ~640px for management whenever the viewport can afford it. Their City column is the diorama at
   its own shape with the docked feed below it (tabs.js).
   Tiny phone landscape uses bottom navigation to avoid a cramped right rail. */
@media (orientation: landscape) {
  #app[data-layout=tablet], #app[data-layout=phone] {
    --city-h: auto;
    display: grid;
    grid-template-columns: minmax(0, min(35cqw, max(160px, calc(100cqw - 600px)))) minmax(0, 1fr);
    grid-template-rows: auto 44px minmax(0, 1fr) auto;
    grid-template-areas: "hud hud" "scene viewbar" "scene panels" "tabs tabs";
  }
  #app[data-layout=phone] { grid-template-columns: minmax(0, 25cqw) minmax(0, 1fr); }
  #app[data-layout=tablet] .hud, #app[data-layout=phone] .hud { grid-area: hud; }
  #app[data-layout=tablet] .city-view, #app[data-layout=phone] .city-view { grid-area: scene; }
  #app[data-layout=tablet] .viewbar, #app[data-layout=phone] .viewbar { grid-area: viewbar; }
  #app[data-layout=tablet] .panels, #app[data-layout=phone] .panels { grid-area: panels; }
  #app[data-layout=tablet] .tabbar, #app[data-layout=phone] .tabbar { grid-area: tabs; }
  #app[data-layout=tablet] {
    --city-col: min(56cqw, max(35cqw, calc(100cqw - 640px)));
    --city-land: min(calc(var(--city-col) * .63), 60cqh);
    grid-template-columns: minmax(0, var(--city-col)) minmax(0, 1fr);
    grid-template-rows: auto minmax(0, var(--city-land)) minmax(0, 1fr) auto;
    grid-template-areas: "hud hud" "scene panels" "dock panels" "tabs tabs";
  }
  /* folded dock (the player closed it): the City takes the column and the chip bar stays under it so the dock can come
     back. Every visible region needs a named area — an unplaced dock creates implicit tracks that crush the panel
     to 0 px (TestFlight build 7 bug). */
  #app[data-layout=tablet]:not(.has-dock) {
    grid-template-rows: auto minmax(0, var(--city-land)) minmax(0, 1fr) auto auto;
    grid-template-areas: "hud hud" "scene panels" "scene panels" "dock panels" "tabs tabs";
  }
  #app[data-layout=tablet] .viewbar { display: none; }
  #app[data-layout=tablet] .dock:not([hidden]) { grid-area: dock; display: flex; flex-direction: column; min-height: 0; min-width: 0; background: var(--bg);
    box-shadow: inset 0 var(--px) 0 0 var(--ink), inset 0 calc(2 * var(--px)) 0 0 var(--n2); }
}

/* ---- roomy desktop: rail · (HUD, City, ticker, optional dock) · side panel ---- */
#app[data-layout=wide] {
  display: grid; max-width: 2560px; margin: 0 auto;
  grid-template-columns: var(--rail-w) minmax(0, 1fr) var(--side-w);
  grid-template-rows: auto minmax(0, 1fr) auto auto;
  grid-template-areas: "rail hud panels" "rail scene panels" "rail ticker panels" "rail dock panels";
}
#app[data-layout=wide] .tabbar { grid-area: rail; }
#app[data-layout=wide] .hud { grid-area: hud; }
#app[data-layout=wide] .city-view { grid-area: scene; height: auto; }
#app[data-layout=wide] .ticker { grid-area: ticker; }
#app[data-layout=wide] .dock { grid-area: dock; }
#app[data-layout=wide] .panels { grid-area: panels; box-shadow: inset var(--px) 0 0 0 var(--ink), inset calc(2 * var(--px)) 0 0 0 var(--n2); }
#app[data-layout=wide] .panel { padding: var(--sp-4) 16px 32px; }
/* with a docked panel the scene keeps 16:10 of the main column (≤ 58 % of the height) and the dock takes the rest */
#app[data-layout=wide].has-dock { grid-template-rows: auto auto auto minmax(0, 1fr); }
#app[data-layout=wide].has-dock .city-view { height: min(calc((100cqw - var(--rail-w) - var(--side-w)) * .625), 58cqh, calc(100cqh - 400px)); }

/* ---- the dock (wide only): chip bar + a second tab panel ---- */
.dock { display: none; }
#app[data-layout=wide] .dock:not([hidden]) { display: flex; flex-direction: column; min-height: 0; min-width: 0; background: var(--bg); }
.dock__bar { flex: none; display: flex; align-items: center; gap: 4px; padding: 6px 8px; overflow-x: auto; scrollbar-width: none;
  background: var(--surface); box-shadow: inset 0 calc(-1 * var(--px)) 0 0 var(--ink); }
.dock__bar::-webkit-scrollbar { display: none; }
.dock__chip { flex: none; display: flex; align-items: center; gap: 6px; min-height: 28px; padding: 4px 8px; color: var(--muted); background: var(--n1);
  font-family: var(--font-small); font-size: var(--fs-small); line-height: 1; text-transform: uppercase;
  box-shadow: inset 0 var(--px) 0 0 var(--n2), 0 0 0 var(--px) var(--ink); }
.dock__chip .px-icon { filter: grayscale(.85) brightness(.75); }
.dock__chip:hover { color: var(--text); }
.dock__chip.is-on { color: var(--n9); background: var(--n2); box-shadow: inset 0 calc(-2 * var(--px)) 0 0 var(--cyan), 0 0 0 var(--px) var(--ink); }
.dock__chip.is-on .px-icon { filter: none; }
.dock__hint { margin-left: auto; padding-left: 8px; font-family: var(--font-small); font-size: var(--fs-small); color: var(--dim); white-space: nowrap; }
.dock__body { position: relative; flex: 1 1 0; min-height: 0; min-width: 0; overflow: hidden; display: none; }
#app.has-dock .dock__body { display: block; }
/* Fill tabs own one bounded internal scroller, equally in the main host and dock.
   Apply after mode gutters: these tabs provide their own chrome and content padding. */
#app .panel.panel--fill { padding: 0; overflow: hidden; display: flex; flex-direction: column; min-height: 0; min-width: 0; }

/* ---------- overflow safety net (no sideways scroll, ever — tools/overflow-audit.mjs) ---------- */
.panel > *, .dock__body > * { min-width: 0; max-width: 100%; }
.stat { min-width: 0; }
.stat__value { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.toast, .toast__text { min-width: 0; max-width: 100%; }
/* large text sizes (Settings → XL): labels wrap instead of pushing their row wider than the panel */
.section__title { flex: 0 1 auto; min-width: 0; white-space: normal; }
.gt-kv > div > span { min-width: 0; white-space: normal; overflow-wrap: anywhere; }
.btn-grid > .btn, .btn__body { min-width: 0; }
.btn__label { max-width: 100%; }
.tabsheet { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.tabsheet--rail { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.tabsheet__item, .tabsheet__item .tab__label { min-width: 0; max-width: 100%; }
.tabsheet__item .tab__label { overflow: hidden; text-overflow: ellipsis; }

/* ---------- floating text (DOM) ---------- */
#fx-layer { position: fixed; inset: 0; pointer-events: none; z-index: 400; overflow: hidden; }
.float-text {
  position: absolute; transform: translate(-50%, -100%); white-space: nowrap; font-size: var(--fs);
  color: var(--phos); text-shadow: var(--px) var(--px) 0 var(--ink), calc(-1 * var(--px)) 0 0 var(--ink), 0 calc(-1 * var(--px)) 0 var(--ink);
  animation: floatUp .8s steps(10) forwards;
}
.float-text.gold { color: var(--gold); }
.float-text.bad { color: var(--hot); }
.float-text.info { color: var(--sky); }
.float-text.good { color: var(--phos); }
@keyframes floatUp { 0% { translate: 0 4px; opacity: 1; } 70% { opacity: 1; } 100% { translate: 0 -36px; opacity: 0; } }

/* ---------- toasts ---------- */
#toast-root {
  position: fixed; z-index: 300; left: 50%; transform: translateX(-50%);
  top: calc(var(--safe-t) + 64px); width: min(396px, calc(100vw - 16px));
  display: grid; gap: 8px; pointer-events: none;
}
.toast {
  --t-edge: var(--n4); --t-acc: var(--n7);
  display: flex; align-items: center; gap: 10px; padding: 10px 12px 10px 14px; pointer-events: auto;
  background: var(--surface); color: var(--text-hi); line-height: calc(20px * var(--text-scale, 1));
  box-shadow:
    inset calc(2 * var(--px)) 0 0 0 var(--t-acc), inset 0 var(--px) 0 0 var(--n2),
    0 calc(-1 * var(--px)) 0 0 var(--t-edge), 0 var(--px) 0 0 var(--t-edge), calc(-1 * var(--px)) 0 0 0 var(--t-edge), var(--px) 0 0 0 var(--t-edge),
    0 calc(2 * var(--px)) 0 0 var(--ink), 0 calc(4 * var(--px)) 0 0 rgba(0, 0, 0, .45);
  opacity: 0; translate: 0 -8px; transition: opacity .15s steps(3), translate .15s steps(3);
}
.toast.is-on { opacity: 1; translate: 0 0; }
.toast.is-leaving { opacity: 0; translate: 0 -8px; }
.toast__icon { flex: none; display: grid; place-items: center; }
.toast__text { min-width: 0; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; overflow-wrap: anywhere; }
.toast--good { --t-edge: var(--crt-1); --t-acc: var(--phos); }
.toast--bad { --t-edge: var(--wine); --t-acc: var(--hot); }
.toast--gold { --t-edge: var(--gold-1); --t-acc: var(--gold); }
.toast--info { --t-edge: var(--navy-2); --t-acc: var(--sky); }
.toast--achievement { --t-edge: var(--gold); --t-acc: var(--gold); background: var(--space); color: var(--gold); padding-top: 12px; padding-bottom: 12px;
  box-shadow:
    inset calc(2 * var(--px)) 0 0 0 var(--t-acc), inset 0 calc(2 * var(--px)) 0 0 var(--violet-1),
    0 calc(-1 * var(--px)) 0 0 var(--t-edge), 0 var(--px) 0 0 var(--t-edge), calc(-1 * var(--px)) 0 0 0 var(--t-edge), var(--px) 0 0 0 var(--t-edge),
    0 calc(2 * var(--px)) 0 0 var(--gold-0), 0 calc(4 * var(--px)) 0 0 rgba(0, 0, 0, .5); }
.toast--achievement .toast__text { text-shadow: var(--px) var(--px) 0 var(--gold-0); }

/* ---------- modals ---------- */
#modal-root { position: relative; z-index: 100; }
.modal-wrap { position: fixed; inset: 0; display: grid; grid-template-columns: minmax(0, 380px); justify-content: center; place-items: center; padding: calc(var(--safe-t) + 16px) 16px calc(var(--safe-b) + 16px); }
.modal-backdrop {
  position: absolute; inset: 0; background-color: rgba(11, 12, 20, .78);
  background-image: linear-gradient(45deg, rgba(11, 12, 20, .5) 25%, transparent 25%, transparent 75%, rgba(11, 12, 20, .5) 75%),
                    linear-gradient(45deg, rgba(11, 12, 20, .5) 25%, transparent 25%, transparent 75%, rgba(11, 12, 20, .5) 75%);
  background-size: 4px 4px; background-position: 0 0, 2px 2px;
  opacity: 0; transition: opacity .15s steps(3);
}
/* a dialog = double frame (light inner, ink outer, notched corners) with a gold-rimmed title bar */
.modal {
  position: relative; width: min(380px, 100%); max-height: calc(100vh - 64px); max-height: calc(100dvh - 64px); display: flex; flex-direction: column;
  background: var(--surface);
  box-shadow:
    inset 0 var(--px) 0 0 var(--n3),
    0 calc(-1 * var(--px)) 0 0 var(--n4), 0 var(--px) 0 0 var(--n4), calc(-1 * var(--px)) 0 0 0 var(--n4), var(--px) 0 0 0 var(--n4),
    0 calc(-2 * var(--px)) 0 0 var(--ink), 0 calc(2 * var(--px)) 0 0 var(--ink), calc(-2 * var(--px)) 0 0 0 var(--ink), calc(2 * var(--px)) 0 0 0 var(--ink),
    calc(-1 * var(--px)) calc(-1 * var(--px)) 0 0 var(--ink), var(--px) calc(-1 * var(--px)) 0 0 var(--ink),
    calc(-1 * var(--px)) var(--px) 0 0 var(--ink), var(--px) var(--px) 0 0 var(--ink),
    0 calc(5 * var(--px)) 0 0 rgba(0, 0, 0, .45);
  transform: scale(.92); opacity: 0; transition: transform .15s steps(3), opacity .15s steps(3);
}
.modal-wrap.is-on .modal { transform: none; opacity: 1; }
.modal-wrap.is-on .modal-backdrop { opacity: 1; }
.modal-wrap.is-leaving .modal { transform: scale(.96); opacity: 0; }
.modal__head { display: flex; align-items: center; gap: 10px; padding: 12px 10px 10px 12px; background: var(--n2);
  box-shadow: inset 0 calc(2 * var(--px)) 0 0 var(--gold-1), inset 0 calc(-1 * var(--px)) 0 0 var(--ink); }
.modal__icon { flex: none; display: grid; place-items: center; }
.modal__title { flex: 1; min-width: 0; color: var(--gold); letter-spacing: 1px; text-shadow: var(--px) var(--px) 0 var(--gold-0); line-height: 1.25; }
.modal__x { flex: none; width: 32px; height: 32px; display: grid; place-items: center; color: var(--muted); font-size: var(--fs); background: var(--n3);
  box-shadow: inset 0 var(--px) 0 0 var(--n4), 0 var(--px) 0 0 var(--ink); }
.modal__x:hover { color: var(--n9); }
.modal__x:active { translate: 0 var(--px); box-shadow: 0 0 0 0 var(--ink); }
.modal__body { padding: 14px 14px 6px; overflow-y: auto; overflow-x: hidden; min-width: 0; overflow-wrap: anywhere; line-height: 20px; color: var(--text); user-select: text; }
/* nothing in a dialog may be wider than the dialog: shrinkable children, long words wrap, stat rows wrap value under label */
.modal, .modal__head, .modal__body > *, .modal__actions > * { min-width: 0; max-width: 100%; }
.modal .stat { flex-wrap: wrap; row-gap: 2px; }
.modal .stat__label { white-space: normal; overflow-wrap: anywhere; flex: 1 1 96px; }
.modal .stat__value { min-width: 0; margin-left: auto; white-space: normal; overflow-wrap: anywhere; text-align: right; line-height: calc(20px * var(--text-scale, 1)); }
.modal__body p { margin: 0 0 10px; }
.modal__body p:not(.gt-small):not(.fineprint), .modal__lead, .modal__note {
  font-family: var(--font-prose); font-size: var(--fs); line-height: var(--lh-prose);
  -webkit-font-smoothing: var(--prose-smoothing, none);
}
.modal__lead { color: var(--text-hi); }
.modal__note { color: var(--muted); }
.modal__actions { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; padding: 8px 12px 14px; }
.modal__actions .btn { margin: var(--px); }
.modal__actions--stack { grid-template-columns: 1fr; }
.modal__actions--stack .btn { justify-content: flex-start; }
.modal__choice .btn__label { white-space: normal; line-height: calc(20px * var(--text-scale, 1)); }
/* decision choices carry a description line: let it wrap so long options never widen the modal */
.modal__choice .btn__sub { white-space: normal; line-height: calc(14px * var(--text-scale, 1)); text-align: left; }
.modal__choice, .modal__choice .btn__body { min-width: 0; }
/* Short viewports (phone landscape, ~360px tall): a stack of choices taller than the dialog squeezed the body to a sliver
   and spilled the last choices past the frame and off screen. Scroll the whole dialog instead; the title bar stays put. */
@media (max-height: 560px) {
  .modal { overflow-y: auto; overscroll-behavior: contain; }
  .modal__body { flex: none; overflow-y: visible; }
  .modal__actions { flex: none; }
  .modal__head { position: sticky; top: 0; z-index: 1; }
}
.offline__lines { display: grid; gap: 0; margin: 4px 0 10px; background: var(--ink); padding: 4px 0; box-shadow: inset 0 var(--px) 0 0 #000; }
.offline__lines .stat__label { color: var(--text); }
.offline__lines .stat__value { color: var(--phos); }

/* ---------- tooltip ---------- */
.tooltip {
  position: fixed; left: 0; top: 0; z-index: 450; max-width: 240px; padding: 8px 10px; pointer-events: none;
  background: var(--cream); color: var(--umber); line-height: 20px;
  box-shadow: 0 calc(-1 * var(--px)) 0 0 var(--umber), 0 var(--px) 0 0 var(--umber), calc(-1 * var(--px)) 0 0 0 var(--umber), var(--px) 0 0 0 var(--umber),
              0 calc(3 * var(--px)) 0 0 rgba(0, 0, 0, .45);
  opacity: 0; visibility: hidden; transition: opacity .1s steps(2);
}
.tooltip.is-on { opacity: 1; visibility: visible; }

/* ---------- cutscene ---------- */
.cutscene { position: fixed; inset: 0; z-index: 500; overflow: hidden; display: grid; place-items: center; background: #000; touch-action: none; animation: cutIn .2s steps(3); }
.cutscene.is-leaving { opacity: 0; transition: opacity .2s steps(3); }
@keyframes cutIn { from { opacity: 0; } }
.cutscene__canvas { display: block; image-rendering: pixelated; }
.cutscene__skip { position: absolute; top: calc(var(--safe-t) + 12px); right: calc(var(--safe-r) + 12px); opacity: .8; }
.cutscene__hint { position: absolute; bottom: calc(var(--safe-b) + 16px); left: 50%; translate: -50% 0; width: 12px; height: 8px; opacity: 0;
  background: linear-gradient(var(--gold), var(--gold)) 0 0 / 12px 2px no-repeat, linear-gradient(var(--gold), var(--gold)) 2px 2px / 8px 2px no-repeat,
              linear-gradient(var(--gold), var(--gold)) 4px 4px / 4px 2px no-repeat; }
.cutscene__hint.is-on { opacity: 1; animation: blink 1s steps(1) infinite; }

/* ---------- catch-up overlay ---------- */
.catchup { position: fixed; inset: 0; z-index: 250; display: grid; place-items: center; background: rgba(11, 12, 20, .6); }
.catchup__box { display: grid; justify-items: center; gap: 10px; width: 240px; padding: 14px; background: var(--surface); color: var(--cyan);
  box-shadow: 0 calc(-1 * var(--px)) 0 0 var(--n4), 0 var(--px) 0 0 var(--n4), calc(-1 * var(--px)) 0 0 0 var(--n4), var(--px) 0 0 0 var(--n4); }
.catchup__box .bar { width: 100%; }

/* ---------- crash ---------- */
.crash { position: fixed; inset: 0; z-index: 2000; display: grid; place-items: center; padding: 16px; background: var(--wine); }
.crash__box { width: min(380px, 100%); padding: 16px; background: var(--ink); display: grid; gap: 12px; color: var(--n8);
  box-shadow: 0 0 0 var(--px) var(--hot); font-family: var(--font-small); font-size: var(--fs-small); }
.crash__title { font-family: var(--font); font-size: var(--fs-big); color: var(--hot); }
.crash__msg { max-height: 40vh; overflow: auto; white-space: pre-wrap; color: var(--orange); margin: 0; font-size: 11px; }

/* ---------- debug overlay ---------- */
.dbg { position: fixed; z-index: 900; bottom: calc(var(--safe-b) + 76px); right: 4px; width: 216px; background: rgba(11, 12, 20, .92); color: var(--phos);
  font: 11px/1.35 ui-monospace, Menlo, Consolas, monospace; box-shadow: 0 0 0 2px var(--crt-1); -webkit-font-smoothing: auto; }
.dbg.is-collapsed { width: auto; }
.dbg.is-collapsed .dbg__body { display: none; }
.dbg__head { display: block; width: 100%; padding: 3px 8px; color: var(--crt-0); background: var(--phos); font: inherit; font-weight: bold; text-align: left; }
.dbg__body { padding: 6px; display: grid; gap: 4px; }
.dbg__stats { margin: 0; white-space: pre; font: inherit; color: var(--mint); }
.dbg__row { display: flex; gap: 4px; flex-wrap: wrap; }
.dbg__btn { flex: 1; padding: 4px 6px; background: var(--crt-1); color: var(--mint); font: inherit; }
.dbg__btn:active, .dbg__btn.is-on { background: var(--phos); color: var(--crt-0); }
@media (min-width: 900px) { .dbg { right: auto; bottom: auto; left: 12px; top: 12px; } }

/* ---------- tab bar ⋯ overflow (tabs.js layoutBar) ---------- */
.tab.is-overflow { display: none; }
.tab--more .tab__dots { font-family: var(--font-small); font-size: var(--fs-small); letter-spacing: 1px; color: var(--muted); line-height: 32px; }
.tab--more.is-open { background: var(--n2); }
/* the More button's badge is a pure pixel pip ("something is waiting in the sheet"), never a glyph */
.tab--more .tab__badge { min-width: 10px; width: 10px; height: 10px; padding: 0; font-size: 0; line-height: 0; color: transparent; top: 6px; left: calc(50% + 10px); }
.tabsheet {
  position: absolute; right: 4px; z-index: 20; display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px;
  width: min(300px, calc(100% - 8px)); padding: 6px; background: var(--surface);
  overflow-y: auto; overscroll-behavior: contain; touch-action: pan-y; -webkit-overflow-scrolling: touch;
  box-shadow: 0 0 0 var(--px) var(--ink), inset 0 0 0 var(--px) var(--n3), 0 6px 0 0 rgba(0,0,0,.45);
  animation: sheetIn .12s steps(3);
}
@keyframes sheetIn { from { transform: translateY(8px); opacity: 0; } }
.tabsheet__item { position: relative; display: grid; justify-items: center; gap: 2px; padding: 8px 4px 6px; color: var(--muted); background: var(--n1); min-height: 56px; }
.tabsheet__item .tab__icon { filter: grayscale(.6) brightness(.85); }
.tabsheet__item.is-active { color: var(--n9); box-shadow: inset 0 calc(2 * var(--px)) 0 0 var(--gold); }
.tabsheet__item.is-active .tab__icon, .tabsheet__item:hover .tab__icon { filter: none; }
.tabsheet__item.is-new .tab__icon { animation: tabNew 1s steps(2) infinite; }
/* cash runway tone (common.js rateTone): amber under 6 h of runway, red under 1 h */
.hud__rate[data-tone=warn] { color: var(--orange); }
.hud__rate[data-tone=danger] { color: var(--hot); }
/* HUD sub-line tones: good (users growing), cta (an idle trainer: start the next run) */
.hud__rate[data-tone=good] { color: var(--phos); }
.hud__rate[data-tone=cta] { color: var(--gold); animation: blink 1.2s steps(1) infinite; }
@media (prefers-reduced-motion: reduce) { .hud__rate[data-tone=cta] { animation: none; } }
