@import url('https://fonts.googleapis.com/css2?family=Chakra+Petch:wght@300;400;500;600;700&display=swap');

:root {
  --bg: #090d12;
  --surface: #10161d;
  --surface2: #151d26;
  --surface3: #1b2631;
  --ink: #edf2f6;
  --muted: #8f9eac;
  --faint: #627282;
  --line: #263441;
  --accent: #8bd3ff;
  --good: #8ee3a4;
  --warn: #e9bf69;
  --bad: #ee8585;
  --paper: #e9edf0;
  --paperInk: #17212a;
  --shadow: 0 18px 50px rgba(0, 0, 0, 0.35);
  /* Two faces and three sizes for the whole board: anything that needs to
     stand out does it with weight, colour, or case, not another size. */
  --font:
    'Chakra Petch', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
    sans-serif;
  --mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;
  --fs-lg: 1.25rem;
  --fs-md: 0.85rem;
  --fs-sm: 0.68rem;
  --software: #7fb4d6;
  --vendor: #8eb69a;
  --ai: #a892d2;
  --action: #d2b36f;
  --upgrade: #a58fc2;
  /* Rarity metals (#224): mid-luminance so the glyph reads on both the dark
     app background and the light paper card face, without borrowing --action's
     lighter gold. */
  --rarity-common: #9c6b3e;
  --rarity-uncommon: #737f89;
  --rarity-rare: #b3860d;
}
* {
  box-sizing: border-box;
  /* Every scroller in this app sits inside a panel, so none of them should
     wear browser chrome: a hairline in the panel-edge colour, no track.
     scrollbar-width does not inherit, hence the universal selector. */
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}
html,
body {
  margin: 0;
  color: var(--ink);
  font-family: var(--font);
}
html {
  background: var(--bg);
}
body {
  min-height: 100vh;
  background: transparent;
}
.hidden {
  display: none !important;
}
/* ── Rail type: four roles, and nothing else inside the rail ──────────────
   Line heights are whole pixels so every row, and every bar drawn beside
   one, lands on a whole pixel: a 6px bar at a fractional y is smeared, and a
   dark one looks thinner than a bright one.

   Label: the name of a thing. Title: one per detail, plus the world event
   and a rival in the closed rail. Body: any full sentence; italic flavour is
   its one variation, and a bold run-in word ("At Close:") its one emphasis.
   Data: every number and every caption to one. Muted and state colours
   change colour only, never family, size or weight. Shared renderers keep
   their own styles outside the rail; inside it these win. */
/* One line per role, so the four read as a table. */
.right-rail :is(.eyebrow, .rail-tab, .inspect-kicker, .inspect-section h4, .evo-level, .fc-label, .work-segments-label) {
  font: 400 var(--fs-sm) / 16px var(--mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--accent);
}
.right-rail :is(.inspect-title, .rival-seg) {
  font: 820 var(--fs-lg) / 22px var(--font); letter-spacing: 0; text-transform: none; color: var(--ink);
}
.right-rail :is(.inspect-rule, .world-name, .world-text, .flavor, .sub, .mini-list, .evo-text, .fc-attribution, .quarter-warning, .quarter-dire, #inspector button:not(.rival-seg, .fc-tab, .quick-action:not(.action-wide), .work-segment)) {
  font: 400 var(--fs-md) / 19px var(--font); letter-spacing: 0; text-transform: none;
}
.right-rail :is(.stat-pill, .inspect-tag, .prop-status, .forecast-legend, .section-note, .op-metric, .rival-name, .race-row-head, .race-label, .prereq, .fc-tab, .fc-tick, .fc-value, .metric-value, .funding-history, .arch-list, .close-burn, .market-clock, .rival-seg small, #inspector .delta, .work-segment, .work-segments-hint, :is(.funding-history, .arch-list, .cap-table) :is(b, small)) {
  font: 400 var(--fs-sm) / 17px var(--mono); letter-spacing: 0; text-transform: none;
}
.right-rail :is(.world-text, .flavor) {
  font-style: italic;
}
/* In the closed rail the event is its name and what it does; the quote is
   the detail's. */
.right-rail .world-text {
  color: var(--muted);
}
.right-rail .world-flavor {
  display: none;
}
/* The chart is an SVG scaled to the rail, 470 units to about 495px at
   1440x900, so its text is sized in units that land on the roles' pixels. */
.right-rail #inspector :is(.fc-tick, .fc-value, .fc-label) {
  font-size: calc(var(--fs-sm) * 470 / 495);
}
.right-rail :is(.fc-tick, .fc-label) {
  fill: var(--muted);
}
.right-rail .rail-tab:not(.current),
.right-rail .rival-seg:not(.current),
.right-rail .inspect-section h4,
.right-rail .sub {
  color: var(--muted);
}
.right-rail .quarter-warning {
  color: var(--warn);
}
.right-rail .quarter-dire {
  color: var(--bad);
}
button,
input,
select,
textarea {
  font: inherit;
}
button {
  background: var(--surface3);
  color: var(--ink);
  border: 0;
  padding: 0.6rem 0.78rem;
  cursor: pointer;
}
button:hover {
  background: #243443;
}
button:disabled {
  opacity: 0.38;
  cursor: not-allowed;
}
.button-link {
  background: var(--surface3);
  color: var(--ink);
  padding: 0.6rem 0.78rem;
  text-decoration: none;
}
.button-link:hover {
  background: #243443;
}
.shortcut-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.4em;
  margin-right: 0.55em;
  padding: 0 0.3em;
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.2);
  font-family: var(--mono);
  font-size: var(--fs-sm);
  line-height: 1.5;
  vertical-align: 1px;
}
body.no-shortcut-badges .shortcut-badge,
body.shortcuts-off :is(.shortcut-badge, .toggle-shortcut-badges) {
  display: none;
}
@media (max-width: 720px) {
  .shortcut-badge,
  #showShortcuts,
  .toggle-shortcut-badges {
    display: none;
  }
}
.shortcut-toggles {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.primary {
  background: #1d5069;
}
.primary:hover {
  background: #266681;
}
.danger {
  background: #3a2225;
  color: #ffd9d9;
}
.quiet {
  background: transparent;
  color: var(--muted);
  padding: 0.45rem 0.55rem;
}
.page-header {
  position: sticky;
  top: 0;
  z-index: 20;
  width: min(1120px, calc(100% - 40px));
  min-height: 62px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  background: rgba(9, 13, 18, 0.94);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid rgba(139, 211, 255, 0.2);
}
.page-header-brand {
  color: var(--ink);
  text-decoration: none;
  font-family: var(--font);
  font-size: var(--fs-lg);
  font-weight: 900;
  letter-spacing: 0.24em;
}
.page-close {
  flex: none;
  display: inline-flex;
  align-items: center;
  font-family: var(--mono);
  font-size: var(--fs-md);
  font-weight: 800;
}
.good {
  color: var(--good);
}
.action-pending [data-action] {
  pointer-events: none;
  opacity: 0.38;
}
.mono {
  font-family: var(--mono);
}
.sub {
  font-size: var(--fs-md);
  color: var(--muted);
}
.app {
  max-width: 1600px;
  margin: 0 auto;
  padding: 0 16px calc(var(--command-bar-h, 60px) + 24px);
}
.topbar {
  display: grid;
  grid-template-columns: minmax(180px, 1fr) auto minmax(180px, 1fr);
  align-items: center;
  gap: 18px;
  position: sticky;
  top: 0;
  z-index: 20;
  background: rgba(9, 13, 18, 0.94);
  backdrop-filter: blur(14px);
  padding: 13px 0 10px;
  border-bottom: 1px solid #18232d;
}
.brand,
.start-brand-name {
  font-family: var(--font);
  font-weight: 900;
  letter-spacing: 0.2em;
  font-size: var(--fs-lg);
  margin-left: 8px;
}
.top-actions {
  justify-self: end;
  display: flex;
  gap: 4px;
}
/* In the game's top bar the menu trigger is one of the quiet top-bar
   buttons, not the landing page's tall one: the bar must not grow for it. */
.top-actions .start-menu-trigger {
  height: auto;
  padding: 0.3rem 0.55rem;
  gap: 6px;
}
.top-actions .start-menu-trigger svg {
  width: 18px;
  height: 18px;
}
/* Kept for screen readers where the visible label has been dropped. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.hud {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  flex-wrap: wrap;
}
.hud-button {
  cursor: pointer;
  border: 1px solid transparent;
  padding: 4px 10px;
  transition:
    0.14s background,
    0.14s border-color;
}
.hud-button:hover {
  background: #131c25;
  border-color: #2d4050;
}
.hud-button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.metric {
  display: flex;
  align-items: baseline;
  gap: 5px;
  white-space: nowrap;
}
.metric b {
  font-family: var(--mono);
  font-size: var(--fs-md);
}
.metric.work b {
  color: var(--good);
}
.metric.debt b {
  color: var(--bad);
}
/* 6+ Debt costs Morale at Close. The outline pads the fill without moving
   the chips beside it. */
.metric.debt-heavy {
  background: var(--bad);
  outline: 3px solid var(--bad);
}
.metric.debt-heavy b,
.metric.debt-heavy .game-icon {
  color: #131b24;
}
.metric.culture b {
  color: var(--good);
}
.metric.strained b {
  color: var(--warn);
}
.metric.broken b {
  color: var(--bad);
}
.game-term {
  white-space: nowrap;
}
.game-icon {
  display: inline-block;
  width: 0.9em;
  height: 0.9em;
  vertical-align: -0.12em;
  margin-right: 0.35em;
}
/* Compact rarity indicator (#224): just the glyph, no box, background, or
   separator dot — the metal color lives on the filled icon itself, so it
   never competes with card-type art. The kicker/sub-label around it is a
   flex row (`align-items: center`) so the icon is centered on the same
   cross-axis line as the text next to it, not baseline-aligned. Focus still
   gets its own ring since the badge is itself in the tab order for its
   `title` tooltip. */
.rarity-badge {
  display: inline-flex;
  align-items: center;
}
.rarity-icon {
  width: 0.95em;
  height: 0.95em;
}
.rarity-badge:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.rarity-common {
  color: var(--rarity-common);
}
.rarity-uncommon {
  color: var(--rarity-uncommon);
}
.rarity-rare {
  color: var(--rarity-rare);
}
/* Icon sizes stay in em everywhere so a glyph scales with the type around it
   rather than staying pinned to a fixed pixel box as the view is zoomed. */
.metric .game-icon {
  width: 0.9em;
  height: 0.9em;
  margin-right: 0;
}
.connection-banner {
  grid-column: 1/-1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: 8px;
  padding: 7px 10px;
  border: 1px solid currentColor;
  background: var(--surface);
  color: var(--warn);
  font-family: var(--mono);
  font-size: var(--fs-sm);
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.connection-banner::before {
  content: '';
  width: 8px;
  height: 8px;
  flex: none;
  background: currentColor;
}
.connection-banner.is-online {
  color: var(--good);
}
.connection-banner.is-terminal {
  color: var(--bad);
}
@media (max-width: 700px) {
  .connection-banner {
    margin-inline: calc(-1 * env(safe-area-inset-left)) calc(-1 * env(safe-area-inset-right));
    padding: 7px max(10px, env(safe-area-inset-right)) 7px max(10px, env(safe-area-inset-left));
  }
}

.status {
  grid-column: 1/-1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 11px;
  flex-wrap: wrap;
  margin-top: 9px;
  padding: 10px 14px;
  font-family: var(--mono);
  font-weight: 800;
  letter-spacing: 0.13em;
  font-size: var(--fs-md);
  text-transform: uppercase;
}
.status .dot {
  width: 9px;
  height: 9px;
  background: currentColor;
  flex: none;
}
.status .who {
  color: var(--ink);
  font-weight: 800;
}
/* A long company name must not push the action count out of the bar (#541). */
.status .who.name {
  display: inline-block;
  max-width: 24ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}
.status .note {
  letter-spacing: 0.06em;
  font-weight: 600;
  opacity: 0.72;
  text-transform: none;
  font-size: var(--fs-md);
}
.status .pips {
  display: flex;
  gap: 4px;
}
.status .pip {
  width: 18px;
  height: 5px;
  background: currentColor;
  opacity: 0.22;
}
.status .pip.on {
  opacity: 1;
}
.status.mine {
  background: rgba(142, 227, 164, 0.1);
  color: var(--good);
  box-shadow: inset 0 0 0 1px rgba(142, 227, 164, 0.34);
}
.status.mine.actions-spent {
  background: rgba(233, 191, 105, 0.12);
  color: var(--warn);
  box-shadow: inset 0 0 0 2px var(--warn);
}
.status.actions-spent .who {
  color: var(--warn);
}
.status.theirs {
  background: rgba(233, 191, 105, 0.08);
  color: var(--warn);
  box-shadow: inset 0 0 0 1px rgba(233, 191, 105, 0.26);
}
.status.theirs .dot {
  animation: tick 1.5s ease-in-out infinite;
}
@keyframes tick {
  0%,
  100% {
    opacity: 0.2;
  }
  50% {
    opacity: 1;
  }
}
.qcard {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  top: 150px;
  z-index: 70;
  width: min(520px, calc(100vw - 24px));
  background: #16212c;
  padding: 14px 16px;
  box-shadow: 0 22px 60px rgba(0, 0, 0, 0.6);
  border: 1px solid #2c3f50;
  cursor: pointer;
  animation: qin 0.22s ease-out;
}
body {
  font-size: var(--fs-md);
}
h1,
h2,
h3,
h4 {
  font-size: var(--fs-lg);
}
.qcard h3 {
  margin: 0;
  font-family: var(--mono);
  font-size: var(--fs-lg);
  letter-spacing: 0.16em;
  color: var(--accent);
}
.qhead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.qcard .qclose {
  padding: 2px 8px;
}
.qcard .qsub {
  font-size: var(--fs-sm);
  color: var(--faint);
  margin-top: 2px;
}
/* One row per company, one clause per Close consequence. Shared with the
   final-Close block in the game-over panel, so it is not nested under .qcard. */
.qevent {
  margin-top: 10px;
  padding: 7px 9px;
  background: #1c2836;
  border-left: 3px solid var(--warn);
  font-size: var(--fs-md);
  line-height: 1.45;
  color: #d3dde6;
}
.qevent-label {
  display: block;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--warn);
}
.qrows {
  margin: 10px 0 0;
  padding: 0;
  list-style: none;
  font-size: var(--fs-md);
  color: #d3dde6;
}
.qrows li {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 3px 0;
  border-top: 1px solid #22303e;
  line-height: 1.45;
}
.qrows li:first-child {
  border-top: 0;
}
.qwho {
  flex: 0 0 auto;
  min-width: 86px;
  max-width: 18ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--mono);
  letter-spacing: 0.06em;
  color: var(--faint);
}
.qwho.you {
  color: var(--accent);
}
.qclauses {
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0 6px;
}
/* Trailing rather than leading, so a clause that wraps to the next line does
   not start it with a stray separator. */
.qclause:not(:last-child)::after {
  content: ' ·';
  color: #45596c;
}
.qclause.bad {
  color: var(--bad);
}
.qclause.good {
  color: var(--good);
}
.qclause.faint {
  color: var(--faint);
}
.qnote {
  margin-top: 8px;
  font-size: var(--fs-sm);
  color: var(--faint);
}
@keyframes qin {
  from {
    opacity: 0;
    transform: translateX(-50%) translateY(-8px);
  }
  to {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
  }
}
@media (max-width: 720px) {
  .qcard {
    top: 132px;
    padding: 12px 13px;
  }
  .qrows li {
    display: block;
  }
  .qwho {
    display: block;
  }
}
.board {
  display: grid;
  grid-template-columns: minmax(220px, 0.72fr) minmax(480px, 1.75fr) minmax(220px, 0.72fr);
  gap: 12px;
  margin-top: 14px;
  min-height: 470px;
}
.surface {
  background: var(--surface);
  padding: 14px;
}
.section-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 11px;
}
.section-head h2 {
  font-size: var(--fs-md);
  margin: 0;
  font-weight: 750;
}
.section-note {
  font-family: var(--mono);
  font-size: var(--fs-sm);
  color: var(--faint);
}
.requirements-panel {
  display: flex;
  flex-direction: column;
}
.req-list {
  display: flex;
  flex-direction: column;
  gap: 9px;
}
.req-slot {
  padding: 0;
}
.slot-label {
  font-family: var(--mono);
  font-size: var(--fs-sm);
  color: var(--faint);
  letter-spacing: 0.14em;
  margin: 0 0 4px 4px;
}
.req {
  position: relative;
  background: var(--surface2);
  padding: 10px 10px 9px;
  cursor: pointer;
  transition:
    0.14s transform,
    0.14s background;
}
.req:hover {
  background: #1a2530;
}
.req.selected {
  outline: 2px solid var(--accent);
}
.req.legal-target {
  outline: 2px dotted var(--accent);
  animation: legal-target-pulse 1.2s ease-in-out infinite;
}
.req.now {
  border-left: 3px solid var(--bad);
}
.req.next {
  border-left: 3px solid var(--warn);
}
.req.later {
  border-left: 3px solid #506170;
}
.req-top {
  display: flex;
  align-items: start;
  justify-content: space-between;
  gap: 8px;
}
.req-name {
  font-size: var(--fs-md);
  font-weight: 760;
  line-height: 1.15;
}
.req-stats {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  color: var(--muted);
  margin-top: 7px;
}
.req-penalty {
  color: var(--bad);
}
.progress {
  height: 5px;
  background: #080c10;
  overflow: hidden;
  margin-top: 7px;
}
.progress > i {
  display: block;
  height: 100%;
  background: var(--accent);
}
.architecture-panel {
  position: relative;
  overflow: hidden;
  background: var(--surface2);
}
.arch {
  min-height: 395px;
  padding: 8px 2px 18px;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
}
/* Auto, not a percentage: the inline depth indent is a margin, and a set
   width plus that margin pushes a nested System's stats off the right edge. */
.arch-node {
  max-width: 420px;
  background: rgba(25, 36, 46, 0.92);
  padding: 10px 12px;
  cursor: pointer;
  position: relative;
  transition:
    0.14s transform,
    0.14s background;
}
.arch-node:hover {
  background: #1d2b37;
}
.arch-node.selected {
  outline: 2px solid var(--accent);
}
.arch-node.legal-target {
  outline: 2px dotted var(--accent);
  animation: legal-target-pulse 1.2s ease-in-out infinite;
}
.arch-node:before {
  content: '';
  position: absolute;
  left: -12px;
  top: 50%;
  width: 10px;
  height: 1px;
  background: #334757;
}
.arch-node.root:before {
  display: none;
}
.arch-node + .arch-node {
  margin-top: 8px;
}
.arch-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.arch-name {
  font-weight: 780;
  font-size: var(--fs-md);
}
.arch-stats {
  font-family: var(--mono);
  font-size: var(--fs-sm);
  color: var(--muted);
}
.arch-meta {
  margin-top: 5px;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 7px;
  align-items: center;
  font-size: var(--fs-sm);
  color: var(--faint);
}
/* A crowded System wraps whole chips onto a new line rather than breaking
   "AI system" or "5 upgrades" mid-phrase. */
.arch-meta > * {
  white-space: nowrap;
}
.type-dot {
  width: 7px;
  height: 7px;
  display: inline-block;
  background: var(--software);
}
.type-dot.vendor {
  background: var(--vendor);
}
.type-dot.ai {
  background: var(--ai);
}
.debt-mark {
  color: var(--bad);
  font-family: var(--mono);
}
.upgrade-mark {
  color: #b8a4d4;
}
.compute-cost {
  color: var(--bad);
}
.compute-gain {
  color: var(--good);
}
/* A dormant System's draw or generation: what waking it would change. */
.compute-cost.compute-muted,
.compute-gain.compute-muted,
.stat-pill.compute-muted {
  color: var(--muted);
}
.empty-state {
  margin: auto;
  color: var(--faint);
  font-size: var(--fs-md);
  text-align: center;
  max-width: 280px;
  line-height: 1.5;
}
.arch-node.dormant {
  filter: grayscale(0.85);
  opacity: 0.58;
  transform: rotate(-1.4deg);
  border: 1px dashed #65717c;
}
.dormant-mark {
  color: var(--warn);
  font-family: var(--mono);
}
.progress-pips {
  letter-spacing: 0.12em;
  color: var(--faint);
}
.progress-pips .on {
  color: var(--ai);
}
.ai-winter {
  color: var(--bad);
  font-weight: 800;
}
.prereq {
  margin-top: 7px;
  line-height: 1.5;
}
.prereq .met {
  color: var(--good);
}
.prereq .unmet {
  color: var(--warn);
}
.right-rail {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.world-card,
.race-card,
.quarter-metrics {
  background: var(--surface);
  padding: 13px;
}
.quarter-metrics {
  cursor: pointer;
  transition: 0.14s background;
}
.quarter-metrics:hover {
  background: #131c25;
}
.quarter-metrics:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* Everything you can inspect is in the Tab order (#526). Inset, so a
   scrolling panel never clips the ring. */
[data-kind][tabindex]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
/* The rail's whole forecast: one solid stripe, costs then revenue, each
   segment flex-grown by its own dollar amount so the split is the reading. */
.forecast-line {
  display: flex;
  height: 6px;
  margin-top: 10px;
  background: #080c10;
}
.forecast-seg {
  flex-basis: 0;
  min-width: 0;
  height: 100%;
}
.forecast-seg.costs {
  background: var(--bad);
}
.forecast-seg.revenue {
  background: var(--good);
}
.forecast-legend {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  margin-top: 10px;
}
/* The rail row's markup puts the bar between its two figures so the desk can
   lay them out on one line. Everywhere else it reads as it always has: both
   figures on the legend line, the bar under them. */
#quarterMetrics .forecast-legend {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 0 8px;
}
#quarterMetrics .forecast-legend .revenue {
  grid-column: 3;
  grid-row: 1;
}
#quarterMetrics .forecast-legend .forecast-line {
  grid-column: 1/-1;
  grid-row: 2;
}
.forecast-legend .costs {
  color: var(--bad);
}
.forecast-legend .revenue {
  color: var(--good);
}
.quarter-warning,
.quarter-dire {
  margin-top: 10px;
  color: var(--warn);
}
.quarter-dire {
  color: var(--bad);
}
.funding-history {
  display: grid;
  gap: 7px;
  margin: 9px 0 0;
  padding: 0;
  list-style: none;
}
.funding-history li {
  display: grid;
  gap: 2px;
  border-left: 2px solid var(--good);
  padding-left: 8px;
}
.funding-history small {
  color: var(--muted);
  text-transform: capitalize;
}
.world-card {
  cursor: pointer;
}
.race-card-button {
  cursor: pointer;
  border: 1px solid transparent;
  transition:
    0.14s background,
    0.14s border-color;
}
.race-card-button:hover {
  background: #131c25;
  border-color: #2d4050;
}
.race-card-button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.race-card-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.race-details-hint {
  color: var(--faint);
  font-size: var(--fs-sm);
  letter-spacing: 0.08em;
}
.eyebrow {
  font-family: var(--mono);
  color: var(--accent);
  font-size: var(--fs-sm);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.world-name {
  font-weight: 780;
  font-size: var(--fs-md);
  margin-top: 4px;
}
.world-text {
  font-size: var(--fs-md);
  color: #b3bfca;
  line-height: 1.4;
  margin-top: 6px;
}
.world-flavor {
  font-style: italic;
  color: #748697;
  font-size: var(--fs-sm);
  line-height: 1.35;
  margin-top: 8px;
}
.opponents {
  display: grid;
  gap: 6px;
}
/* A rival is one line in the Rivals card: name, then Valuation, Cash and
   Revenue, each centred in one of three equal columns: the same three the AI
   race's steps use, so the two rows read as one table. */
.opponent-card {
  display: flex;
  align-items: baseline;
  gap: 8px;
  cursor: pointer;
}
/* Off the desk there is no ledger grid to line up with, so a name gets a
   sane length of its own rather than the ledger's narrow column. */
.rival-name {
  flex: 0 1 auto;
  max-width: 12ch;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rival-stats {
  flex: 1 1 auto;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  justify-items: center;
  gap: 4px;
}
.opponent-card:hover {
  background: #131c25;
}
.opponent-card.legal-target {
  outline: 2px dotted var(--accent);
  animation: legal-target-pulse 1.2s ease-in-out infinite;
}
@keyframes legal-target-pulse {
  0%,
  100% {
    outline-color: var(--accent);
  }
  50% {
    outline-color: rgba(139, 211, 255, 0.3);
  }
}
/* Motion. Every render rebuilds its markup, so these one-shot effects are
   keyed by a class the script keeps on while they run, and --fx-delay resumes
   one mid-flight (see fx() in app.ts). Short, flat, no bounce. The script
   never adds them under prefers-reduced-motion. */
/* A drawn card lands in the hand face down, then turns over. The back drops
   away at the edge-on frame, where the swap cannot be seen. Every frame stays
   inside the card's own box (no offset, no perspective), so a card mid-deal
   never pushes the hand or the page into scrolling. */
.card.dealt {
  position: relative;
  animation: deal 560ms var(--fx-delay, 0ms) backwards;
}
/* A snap position follows the transformed box, so a card in motion would drag
   the phone's snapping hand sideways with it. Outranks the phone's snap rule. */
.hand .card.dealt {
  scroll-snap-align: none;
}
.card.dealt::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2; /* over .art-img */
  opacity: 0;
  background: #090d12 url('/art/thumb/monolith-card-back.webp') center/cover no-repeat;
  box-shadow: inset 0 0 0 2px #466174;
  animation: deal-back 560ms var(--fx-delay, 0ms) backwards;
}
@keyframes deal {
  0% {
    opacity: 0;
    transform: scale(0.88) rotateY(180deg);
    animation-timing-function: cubic-bezier(0.2, 0.7, 0.3, 1);
  }
  40% {
    opacity: 1;
    transform: scale(1) rotateY(180deg);
    animation-timing-function: cubic-bezier(0.5, 0, 1, 0.6);
  }
  70% {
    transform: rotateY(90deg);
    animation-timing-function: cubic-bezier(0, 0.4, 0.3, 1);
  }
  100% {
    transform: rotateY(0deg);
  }
}
@keyframes deal-back {
  0%,
  69.9% {
    opacity: 1;
  }
  70%,
  100% {
    opacity: 0;
  }
}
/* A deployed System drops into place with a brief outline. */
.arch-node.dealt {
  animation: land 420ms cubic-bezier(0.2, 0.7, 0.3, 1) var(--fx-delay, 0ms) backwards;
}
@keyframes land {
  from {
    opacity: 0;
    transform: translateY(-8px);
    box-shadow: 0 0 0 2px var(--accent);
  }
  to {
    box-shadow: 0 0 0 2px transparent;
  }
}
/* A changed HUD number rolls (script) and floats its delta above itself. */
.metric {
  position: relative;
}
.metric b {
  font-variant-numeric: tabular-nums;
}
.metric-delta {
  position: absolute;
  left: 50%;
  top: 100%;
  font: normal 800 var(--fs-sm) / 1 var(--mono);
  white-space: nowrap;
  pointer-events: none;
  animation: metric-delta 1200ms ease-out var(--fx-delay, 0ms) both;
}
.metric-delta.good {
  color: var(--good);
}
.metric-delta.bad {
  color: var(--bad);
}
@keyframes metric-delta {
  0% {
    opacity: 0;
    transform: translate(-50%, -2px);
  }
  15%,
  65% {
    opacity: 1;
    transform: translate(-50%, 2px);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, 6px);
  }
}
/* Your turn starts with a wipe across the status bar; a spent action pip
   flares and drains. */
.status {
  position: relative;
  isolation: isolate;
}
.status::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: currentColor;
  opacity: 0;
  pointer-events: none;
}
.status.turn-start::after {
  animation: turn-wipe 900ms cubic-bezier(0.3, 0.7, 0.2, 1) var(--fx-delay, 0ms) both;
}
@keyframes turn-wipe {
  0% {
    opacity: 0.28;
    clip-path: inset(0 100% 0 0);
  }
  40% {
    opacity: 0.28;
    clip-path: inset(0);
  }
  100% {
    opacity: 0;
    clip-path: inset(0);
  }
}
.status .pip.spent {
  animation: pip-spend 480ms ease-out var(--fx-delay, 0ms) backwards;
}
@keyframes pip-spend {
  from {
    opacity: 1;
    transform: scaleY(2);
  }
}
@media (prefers-reduced-motion: reduce) {
  .req.legal-target,
  .arch-node.legal-target,
  .opponent-card.legal-target {
    animation: none;
  }
}
.op-metric {
  color: var(--muted);
}
.race-row {
  margin-top: 9px;
}
.race-row-head {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-family: var(--mono);
  color: var(--muted);
  font-size: var(--fs-sm);
}
.race-track {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 4px;
  margin-top: 5px;
}
.race-step {
  height: 6px;
  background: #24303b;
}
.race-step.on {
  background: var(--ai);
}
.race-label {
  color: var(--muted);
  margin-top: 7px;
}
.cash-forecast-chart {
  margin-top: 12px;
}
/* Quarter tabs: one per quarter this company has played, the live one last. */
.fc-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 12px;
}
.fc-tab {
  background: var(--surface3);
  color: var(--muted);
  padding: 0.3rem 0.55rem;
}
.fc-tab.active {
  background: var(--accent);
  color: #0b1218;
}
.fc-attribution {
  color: var(--muted);
  line-height: 1.7;
}
.fc-attribution b {
  color: var(--ink);
}
.fc-attribution .game-icon {
  vertical-align: -2px;
}
.fc-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 8px;
  font: 400 var(--fs-sm) / 17px var(--mono);
}
.fc-table caption {
  text-align: left;
  font-weight: 700;
  color: var(--ink);
  padding-bottom: 2px;
}
.fc-table :is(th, td) {
  text-align: left;
  padding: 2px 6px;
  border-bottom: 1px solid var(--line);
}
.fc-table th {
  color: var(--muted);
  font-weight: 400;
}
.fc-table td:first-child {
  white-space: nowrap;
  width: 1%;
}
.fc-table .game-icon {
  vertical-align: -2px;
}
.cash-forecast-note .quarter-warning,
.cash-forecast-note .quarter-dire {
  margin-top: 0;
}
.cash-forecast-chart svg {
  display: block;
  margin: 0 auto;
}
.fc-grid {
  stroke: var(--line);
  stroke-width: 1;
}
.fc-grid.zero {
  stroke: var(--faint);
  stroke-width: 1.5;
}

.fc-col {
  outline: none;
}
.fc-hit {
  fill: transparent;
}
.fc-col:hover .fc-hit {
  fill: rgba(255, 255, 255, 0.05);
}
.fc-col:focus-visible .fc-hit {
  stroke: var(--accent);
  stroke-width: 2;
}
.fc-bar.cash {
  fill: var(--accent);
}
.fc-bar.revenue {
  fill: var(--good);
}
.fc-bar.costs {
  fill: var(--bad);
}
.fc-value {
  fill: var(--ink);
}
/* Metric rows: one company's metrics, each a Label, its squares and its
   number, on the same columns as the rail's ledger rows so the squares start
   where the Forecast bar does. Ten squares a row. */
.right-rail {
  --ledger-label: 74px;
  --ledger-left: 64px;
  --ledger-right: 44px;
  --metric-square: 12px;
}
.metric-rows {
  margin: 12px 0;
}
.metric-group {
  display: grid;
  gap: 6px;
}
.metric-group + .metric-group {
  margin-top: 12px;
}
.metric-row {
  display: grid;
  grid-template-columns: calc(var(--ledger-label) + var(--ledger-left) + 8px) minmax(0, 1fr) auto;
  column-gap: 8px;
  align-items: start;
  padding: 3px 0;
}
.metric-name,
.metric-value {
  line-height: var(--metric-square);
}
.metric-name {
  display: flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
}
.metric-name .game-icon {
  width: 11px;
  height: 11px;
}
/* Ten squares a row fill the width at the base size; more columns share the
   same width, every square one whole-pixel size. */
.metric-squares {
  --waffle-width: calc(10 * var(--metric-square) + 9 * 2px);
  --waffle-square: max(
    2px,
    round(
      down,
      (var(--waffle-width) + var(--waffle-gap, 2px)) / var(--waffle-cols, 10) - var(--waffle-gap, 2px),
      1px
    )
  );
  display: grid;
  grid-template-columns: repeat(var(--waffle-cols, 10), var(--waffle-square));
  grid-auto-rows: var(--waffle-square);
  gap: var(--waffle-gap, 2px);
  min-height: var(--metric-square);
}
.metric-squares i {
  background: var(--metric-color);
}
.metric-value {
  text-align: right;
  white-space: nowrap;
}
.metric-you {
  color: var(--muted);
}
.waffle-baseline {
  stroke: var(--line);
  stroke-width: 1;
}
.waffle-col {
  outline: none;
}
.waffle-hit {
  fill: transparent;
}
.waffle-col:hover .waffle-hit {
  fill: rgba(255, 255, 255, 0.05);
}
.waffle-col:focus-visible .waffle-hit {
  stroke: var(--accent);
  stroke-width: 2;
}
.waffle-value {
  font-family: var(--mono);
  font-weight: 700;
  font-size: 13px;
  fill: var(--ink);
}
.waffle-label {
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  fill: var(--muted);
}
.ai-race-checklist {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.ai-race-detail {
  padding: 12px 13px;
  border: 1px solid var(--line);
  background: #0d141b;
}
.ai-race-detail .race-row {
  margin-top: 0;
}
.ai-race-detail .prereq {
  margin-top: 11px;
}
.funding-bar {
  height: 6px;
  background: #24303b;
  overflow: hidden;
  margin-top: 9px;
}
.funding-bar i {
  display: block;
  height: 100%;
  background: var(--good);
}
.race-card .race-row:first-of-type {
  margin-top: 9px;
}
.funding-details {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 12px;
}
.funding-detail,
.detail-block {
  padding: 12px 13px;
  border: 1px solid var(--line);
  background: #0d141b;
}
.funding-detail .race-row,
.funding-detail .close-burn,
.funding-detail .race-label:first-child {
  margin-top: 0;
}
/* The rival inspector reuses the funding/AI-race detail block so the two read
   as one component. Its blocks always lead with an .eyebrow, so the first
   child after it carries the spacing instead of the block's own padding. */
.detail-stack {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 12px;
}
.detail-block .race-row,
.detail-block .inspect-stats {
  margin: 9px 0 0;
}
.detail-block .race-card-title {
  margin-bottom: 0;
}
.arch-list {
  display: grid;
  gap: 7px;
  margin: 9px 0 0;
  padding: 0;
  list-style: none;
}
.arch-list li {
  display: grid;
  gap: 2px;
  border-left: 2px solid var(--line);
  padding-left: 8px;
}
.arch-list li span {
  color: var(--muted);
}
.cap-table {
  font-family: var(--mono);
  font-size: var(--fs-sm);
}
.cap-cells {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 4px;
  margin-top: 8px;
}
.cap-cell {
  display: grid;
  gap: 2px;
  padding: 6px 4px;
  border: 1px solid var(--line);
  text-align: center;
}
.cap-cell span {
  color: var(--muted);
}
.cap-cell b {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
}
.cap-cell.current {
  border-color: var(--accent);
  color: var(--accent);
}
.cap-cell.current span {
  color: var(--accent);
}
.funding-choices + #fundingCaps {
  margin-top: 14px;
}
.close-burn {
  margin-top: 8px;
  color: var(--muted);
}
.race-card .close-burn {
  margin-top: 10px;
  padding-top: 9px;
  border-top: 1px solid var(--line);
}
.close-burn.short {
  color: var(--bad);
}
.close-burn-warning {
  margin-top: 4px;
}
.close-burn-missed {
  margin-top: 4px;
  color: var(--warn);
}
.close-burn-missed.short {
  color: var(--bad);
}
.market-clock {
  margin-top: 10px;
  padding-top: 9px;
  border-top: 1px solid var(--line);
  color: var(--warn);
}
.hand-shell {
  margin-top: 12px;
  background: var(--surface);
  padding: 13px 14px 10px;
}
.hand {
  display: flex;
  gap: 9px;
  overflow-x: auto;
  /* Room for a selected card's 3px ring and the 2px hover lift: the row
     scrolls sideways, so anything outside its padding is clipped. */
  padding: 5px 4px 9px;
  scroll-snap-type: x proximity;
}
/* A hand card is sized for its worst case, not its average: the still at
   one fixed height, then room for a three-line title, the type row and one
   row of stats. The height is built from those parts, so a font change moves
   it rather than clipping it. Short titles leave the gap in the middle, and
   the type and stats rows line up across the hand. */
.card {
  --card-w: 154px;
  --card-art-h: 72px;
  --card-pad-y: 8px;
  --card-pad-x: 7px;
  --card-title-lh: calc(var(--fs-md) * 1.08);
  --card-row-h: calc(var(--fs-sm) * 1.4);
  --card-title-gap: 6px;
  --card-row-gap: 3px;
  /* Density: how many title lines the card is sized for, and clamps to. */
  --card-title-lines: 3;
  display: flex;
  flex-direction: column;
  min-width: var(--card-w);
  width: var(--card-w);
  height: calc(
    var(--card-art-h) + 2 * var(--card-pad-y) + var(--card-title-lines) * var(--card-title-lh) +
      var(--card-title-gap) +
      2 * var(--card-row-h) + var(--card-row-gap)
  );
  background: var(--paper);
  color: var(--paperInk);
  overflow: hidden;
  cursor: pointer;
  scroll-snap-align: start;
  box-shadow: 0 7px 20px rgba(0, 0, 0, 0.18);
  transition:
    0.14s transform,
    0.14s box-shadow;
}
.card:hover {
  transform: translateY(-2px);
}
.card.selected {
  box-shadow:
    0 0 0 3px var(--accent),
    0 12px 25px rgba(0, 0, 0, 0.28);
}
.card-art {
  flex: 0 0 var(--card-art-h);
  height: var(--card-art-h);
  background: linear-gradient(145deg, #526779, #1b2731);
  position: relative;
  overflow: hidden;
}
.card-art:after {
  content: '';
  position: absolute;
  inset: 18% -20% -25% 24%;
  transform: rotate(-12deg);
  border: 1px solid rgba(255, 255, 255, 0.16);
  box-shadow:
    0 0 0 14px rgba(255, 255, 255, 0.025),
    0 0 0 28px rgba(255, 255, 255, 0.018);
}
.art-vendor {
  background: linear-gradient(145deg, #668574, #1b2923);
}
.art-action {
  background: linear-gradient(145deg, #8a7041, #282015);
}
.art-upgrade {
  background: linear-gradient(145deg, #716482, #251f2e);
}
.art-ai {
  background: linear-gradient(145deg, #725f91, #1c1928);
}
.art-danger {
  background: linear-gradient(145deg, #794c4c, #28191b);
}
.card-body {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: var(--card-pad-y) var(--card-pad-x);
}
/* Three lines is the budget; the clamp only guards it. */
.card-title {
  font-size: var(--fs-md);
  font-weight: 830;
  line-height: var(--card-title-lh);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: var(--card-title-lines, 3);
  line-clamp: var(--card-title-lines, 3);
  overflow: hidden;
}
.card-sub {
  display: flex;
  align-items: center;
  gap: 0.4em;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.05em;
  color: #5f6f7d;
  text-transform: uppercase;
  height: var(--card-row-h);
  line-height: var(--card-row-h);
  margin-top: auto;
}
/* One line of stats, always: five is the most any card has. */
.card-costs {
  display: flex;
  flex-wrap: nowrap;
  gap: 3px;
  height: var(--card-row-h);
  line-height: var(--card-row-h);
  margin-top: var(--card-row-gap);
  white-space: nowrap;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  color: #42515e;
  font-weight: 800;
}
.card-costs .game-icon {
  margin-right: 1px;
}
.hand-empty {
  padding: 24px;
  color: var(--faint);
}
.art-img,
.art-under {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 1;
}
.world-art {
  position: relative;
  height: 62px;
  overflow: hidden;
  margin: 9px 0 2px;
  background: #1b2731;
}
.inspect-art {
  position: relative;
  height: 108px;
  overflow: hidden;
  margin: 12px 0 2px;
  background: #1b2731;
  cursor: zoom-in;
}
.ai-claim-modal-card {
  width: min(560px, 100%);
  border: 1px solid rgba(199, 160, 255, 0.5);
  box-shadow:
    0 0 0 1px rgba(199, 160, 255, 0.1),
    0 30px 80px rgba(0, 0, 0, 0.7);
}
.ai-claim-kicker {
  font-family: var(--mono);
  color: var(--ai);
  font-size: var(--fs-sm);
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.prereq-link {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--ai);
  text-decoration: underline;
  cursor: pointer;
}
.ai-claim-costs {
  list-style: none;
  padding: 0;
  margin: 16px 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}
.ai-claim-costs li {
  background: #1b222a;
  border-left: 3px solid var(--ai);
  padding: 10px;
  font-size: var(--fs-md);
}
.ai-claim-modal-card .start-actions {
  justify-content: flex-end;
}
.exit-modal-card,
.funding-modal-card {
  width: min(560px, 100%);
  border: 1px solid rgba(142, 227, 164, 0.5);
  box-shadow:
    0 0 0 1px rgba(142, 227, 164, 0.1),
    0 30px 80px rgba(0, 0, 0, 0.7);
}
.funding-kicker {
  font-family: var(--mono);
  color: var(--good);
  font-size: var(--fs-sm);
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.funding-modal-card .start-actions {
  justify-content: flex-end;
}
.exit-modal-card .exit-actions {
  display: flex;
  justify-content: flex-end;
}
.funding-choices {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
  margin-top: 18px;
}
.funding-choice {
  display: grid;
  gap: 8px;
  min-width: 0;
  padding: 14px 10px;
  text-align: left;
}
.funding-choice strong,
.funding-choice span {
  display: block;
}
.funding-choice small {
  color: var(--muted);
}
@media (max-width: 620px) {
  .funding-choices {
    grid-template-columns: 1fr;
  }
  .funding-choice {
    width: 100%;
  }
}
.section-title {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 8px;
}
.section-title h2 {
  margin: 0;
}
.modal.full {
  padding: 0;
  place-items: stretch;
}
.modal.full .modal-card {
  width: 100%;
  max-width: none;
  height: 100vh;
  max-height: 100vh;
  padding: 20px 24px;
}
.cardsheet {
  width: min(1180px, 100%);
}
.cards-search {
  flex: 1;
  max-width: 320px;
  margin: 0 auto;
}
.sticky-modal-header {
  position: sticky;
  top: -21px;
  background: #121a22;
  padding: 14px 0 10px;
  z-index: 2;
}
.cards-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 0 0 12px;
}
.cards-chip {
  text-transform: uppercase;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  padding: 0.35rem 0.6rem;
}
.cards-chip.on {
  background: var(--accent);
  color: var(--paperInk);
}
.cards-chip.on .sub {
  color: inherit;
}
.cards-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 14px;
  margin-top: 6px;
}
.cg-empty {
  grid-column: 1 / -1;
  color: var(--muted);
  padding: 24px 0;
}
.cg-card {
  background: var(--paper);
  color: var(--paperInk);
  overflow: hidden;
  cursor: pointer;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.22);
  transition:
    0.14s transform,
    0.14s box-shadow;
}
.cg-card:hover {
  transform: translateY(-3px);
  box-shadow: 0 12px 26px rgba(0, 0, 0, 0.34);
}
.cg-art {
  position: relative;
  height: 78px;
  background: #1b2731;
  overflow: hidden;
}
.cg-body {
  padding: 8px 9px 10px;
}
.cg-title {
  font-size: var(--fs-md);
  font-weight: 830;
  line-height: 1.12;
  min-height: 31px;
}
.cg-sub {
  display: flex;
  align-items: center;
  gap: 0.4em;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.05em;
  color: #5f6f7d;
  text-transform: uppercase;
  margin-top: 3px;
}
.cg-group {
  grid-column: 1/-1;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 14px 0 -4px;
  padding-top: 8px;
  border-top: 1px solid #24313d;
}
.cg-group:first-child {
  margin-top: 0;
  border-top: 0;
  padding-top: 0;
}
.zoomcard {
  width: min(880px, 100%);
}
.zoom-nav {
  position: fixed;
  top: 50%;
  transform: translateY(-50%);
  font-size: 22px;
  padding: 10px 14px;
  z-index: 1;
}
.zoom-prev {
  left: 12px;
}
.zoom-next {
  right: 12px;
}
.zoom-nav.hidden {
  display: none;
}
/* Close floats top right over the art, like the inspector's. Zero height so
   it takes no room; sticky so it stays reachable while the card scrolls. */
#cardZoom .start-actions {
  position: sticky;
  top: 12px;
  z-index: 3;
  height: 0;
  margin: 12px 12px 0;
  justify-content: flex-end;
}
.zoom-art {
  position: relative;
  height: min(46vh, 300px);
  overflow: hidden;
  background: #1b2731;
}
.zoom-body {
  margin-top: 15px;
}
.zoom-body h2 {
  margin: 0 0 2px;
  font-size: var(--fs-lg);
  line-height: 1.1;
}
.zoom-body .inspect-tags {
  margin-top: 14px;
}
@media (max-width: 900px) {
  .cards-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}
@media (max-width: 620px) {
  .cards-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 10px;
  }
}
.command-bar {
  position: fixed;
  left: 50%;
  bottom: 14px;
  transform: translateX(-50%);
  width: min(1568px, calc(100% - 32px));
  z-index: 55;
  background: rgba(12, 18, 24, 0.96);
  backdrop-filter: blur(14px);
  border: 1px solid #1c2830;
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.45);
  transition: box-shadow 0.15s ease;
}
/* The game's smallest type at every size: nothing in it renders below 12px. */
#app {
  --fs-sm: 0.75rem;
}
/* ── The actions bar ─────────────────────────────────────────────────────
   One component at every size; layouts only place it and choose the
   button grid. A header row with Undo and the chevron at its right end,
   then the buttons. Collapsed, the header is the whole bar and says whose
   turn it is and how to end it. */
.command-bar {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 6px;
  padding: 6px 10px 8px;
}
.command-bar-head {
  grid-column: 2;
  grid-row: 1;
  display: flex;
  align-items: center;
  gap: 10px;
  width: auto;
  padding: 0;
  background: none;
  border: 0;
  text-align: left;
  cursor: pointer;
  font: inherit;
}
.command-bar.collapsed .command-bar-head {
  grid-column: 1/-1;
  width: 100%;
}
.command-bar-peek {
  display: none;
  flex: 1;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  font-weight: 800;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.command-bar.collapsed .command-bar-peek {
  display: block;
}
.command-bar.collapsed.mine .command-bar-peek {
  color: var(--good);
}
.command-bar.collapsed.theirs .command-bar-peek {
  color: var(--warn);
}
.command-bar-head:hover .command-bar-peek {
  color: var(--ink);
}
/* A 24px square to look at, a 44px square to hit. */
.command-bar-chevron {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  margin-left: auto;
  color: var(--ink);
  background: var(--surface3);
  border: 1px solid var(--line);
}
.command-bar-chevron::after {
  content: '';
  position: absolute;
  inset: -10px;
}
.command-bar.collapsed .command-bar-chevron {
  transform: rotate(180deg);
}
.command-bar-body,
.command-bar-actions {
  display: contents;
}
.command-bar.collapsed .command-bar-body {
  display: none;
}
.undo-btn {
  grid-column: 1;
  grid-row: 1;
  justify-self: end;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  padding: 0.28rem 0.5rem;
}
.undo-btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}
/* Equal cells, as many columns as the layout gives. End turn always opens
   a new row, so it and Pass, which follows it, share the last one. */
.quick-actions {
  grid-column: 1/-1;
  display: grid;
  grid-template-columns: repeat(var(--action-cols, 2), minmax(0, 1fr));
  grid-auto-rows: 1fr;
  gap: 6px;
}
.quick-actions .end-turn-btn {
  grid-column-start: 1;
}
/* The row End turn and Pass share. Only the phone dock draws it as a strip
   of its own; everywhere else its buttons are cells of the grid above. */
.turn-row {
  display: contents;
}
/* An action button: its name, then its cost. Narrow, in the bar's grid,
   they stack; wide, in the detail, they share a line with the cost at the
   right. Nothing is clipped: a name too long for its box wraps, and the
   tests treat a third line in the bar as the bug it is. */
.quick-action {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: stretch;
  min-width: 0;
  min-height: 44px;
  padding: 5px 8px;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  font-weight: 720;
  line-height: 16px;
  text-align: left;
  border-top: 2px solid #365064;
}
.quick-action-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.quick-action-name {
  overflow-wrap: anywhere;
}
.quick-action-cost {
  display: flex;
  flex-wrap: wrap;
  column-gap: 5px;
  font-weight: 600;
}
.cost-free,
.action-note {
  color: var(--muted);
}
.quick-action .shortcut-badge {
  margin-right: 5px;
  line-height: 14px;
}
/* Wide, in a detail: the same parts in the detail's own type, a Body name
   and a Data cost, at production's size and fill. */
.quick-action.action-wide {
  min-height: 44px;
  padding: 12px 16px;
  border-top: 0;
}
.action-wide .quick-action-text {
  flex-direction: row;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}
.action-wide .quick-action-cost {
  flex: none;
}
/* Partial Work on a requirement, three amounts or more: the Label, a row of
   44px squares, and the price once. Squares never stretch. */
.work-segments {
  display: flex;
  align-items: center;
  gap: 10px;
}
.work-segments-row {
  display: flex;
  gap: 4px;
}
.inspect-actions .work-segment {
  position: relative;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  text-align: center;
  background: #1d5069;
}
.inspect-actions .work-segment:hover {
  background: #266681;
}
.work-segment .shortcut-badge {
  position: absolute;
  top: 2px;
  left: 2px;
  margin: 0;
  line-height: 12px;
}
.work-segments-hint {
  color: var(--muted);
}
.quick-action[data-action-type='FUNDING'],
.quick-action[data-action-type='IPO'] {
  border-top-color: var(--good);
}
.quick-action[data-action-type='AI_RACE'] {
  border-top-color: var(--ai);
}
/* Listed but refused: the same two lines, muted. The reason is the tooltip
   and, on a click, a toast; still clickable, so no not-allowed cursor. */
button[aria-disabled='true'] {
  opacity: 0.38;
}
/* Pass is the only irreversible move: outlined and set apart, never filled.
   A filled button reads as "the thing to click", which is exactly wrong here. */
.quick-actions button.pass-btn {
  color: var(--warn);
  box-shadow: inset 0 0 0 1px rgba(233, 191, 105, 0.4);
}
/* When nothing else is legal, Pass is the move: a heavier outline marks it,
   but it stays unfilled so it is never a reflex click. */
.quick-actions button.pass-btn.suggested {
  box-shadow: inset 0 0 0 2px var(--warn);
  border-top-color: var(--warn);
}
.quick-actions button.end-turn-btn {
  font-weight: 700;
}
.support-step-meters {
  margin: 0;
  font-family: var(--mono);
  font-size: var(--fs-md);
}
.support-step-meters .over {
  color: var(--bad);
}
.inspector {
  position: fixed;
  right: 16px;
  top: 78px;
  bottom: calc(var(--command-bar-h, 60px) + 16px);
  width: min(390px, calc(100vw - 32px));
  z-index: 60;
  background: #131b24;
  box-shadow: var(--shadow);
  overflow: auto;
  overscroll-behavior: contain;
  padding: 16px;
}
.inspector-head {
  align-items: flex-start;
}
/* The heading takes the header's free width, so a rival switch has room. */
#inspectorHeading {
  flex: 1 1 auto;
  min-width: 0;
}
/* With two or more rivals the detail's title is a switch between them. */
.rival-switch {
  display: flex;
  gap: 1px;
  min-width: 0;
  margin-top: 2px;
}
.rival-seg {
  flex: 1 1 0;
  min-width: 0;
  padding: 2px 8px 4px;
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  color: var(--muted);
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}
.rival-seg:hover {
  color: var(--ink);
}
.rival-seg.current {
  color: var(--ink);
  border-bottom-color: var(--accent);
}
.rival-seg small {
  color: var(--faint);
}
.rival-seg:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
.inspect-kicker {
  display: flex;
  align-items: center;
  gap: 0.4em;
  font-family: var(--mono);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: var(--fs-sm);
  color: var(--accent);
  margin-bottom: 5px;
}
.inspect-stats {
  display: flex;
  gap: 7px;
  flex-wrap: wrap;
  margin: 14px 0;
}
.stat-pill {
  background: #1c2731;
  padding: 5px 8px;
  font-family: var(--mono);
  font-size: var(--fs-sm);
}
.inspect-rule {
  font-size: var(--fs-md);
  line-height: 1.5;
  color: #d7e0e8;
  margin: 13px 0;
}
/* #58/#282: named card properties read as a labelled list. Flat bars, square
   corners — the accent bar is the only status cue. */
.card-props {
  display: grid;
  gap: 9px;
  margin: 13px 0;
  padding: 0;
  list-style: none;
  font-size: var(--fs-md);
  line-height: 1.5;
  color: #d7e0e8;
}
.card-props li {
  border-left: 3px solid #2f3f4d;
  padding-left: 9px;
}
.card-props b {
  color: #dbe5ee;
}
.card-props li[data-prop-status='active'],
.card-props li[data-prop-status='firing'] {
  border-left-color: var(--accent);
}
.card-props li[data-prop-status='firing'] .prop-status {
  color: var(--bad);
}
.card-props li[data-prop-status='ended'],
.card-props li[data-prop-status='spent'] {
  opacity: 0.6;
}
.prop-status {
  display: block;
  margin-top: 3px;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  color: var(--muted);
}
.inspect-tags {
  display: flex;
  gap: 5px;
  flex-wrap: wrap;
}
.inspect-tag {
  font-family: var(--mono);
  background: #202c37;
  color: #aab8c4;
  padding: 3px 6px;
  font-size: var(--fs-sm);
}
.flavor {
  border-top: 1px solid #263441;
  margin-top: 16px;
  padding-top: 13px;
  color: #9aa9b7;
  font-style: italic;
  line-height: 1.45;
  font-size: var(--fs-md);
}
.inspect-actions {
  display: flex;
  flex-direction: column;
  gap: 7px;
  margin-top: 12px;
  margin-bottom: 12px;
}
.inspect-actions button {
  text-align: left;
}
.inspect-section {
  margin-top: 18px;
}
.inspect-section h4 {
  font-size: var(--fs-sm);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
  margin: 0 0 8px;
}
.mini-list {
  font-size: var(--fs-md);
  color: #b6c2cc;
  line-height: 1.5;
}
.host-reasons-details {
  margin-top: 8px;
  font-size: var(--fs-sm);
}
.host-reasons-details summary {
  cursor: pointer;
  color: var(--muted);
}
.host-reasons-details .mini-list {
  margin-top: 8px;
}
.evo-path {
  display: grid;
  gap: 10px;
}
.evo-step {
  display: grid;
  grid-template-columns: 74px 1fr;
  gap: 10px;
  align-items: start;
}
.evo-level {
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  padding-top: 2px;
}
.evo-text b {
  color: #dbe5ee;
}
.evo-step[data-evo-status='current'] {
  border-left: 3px solid var(--accent);
  padding-left: 7px;
}
.evo-step[data-evo-status='reached'],
.evo-step[data-evo-status='future'] {
  opacity: 0.65;
}
.evo-note {
  margin-top: 8px;
  color: var(--muted);
}
@media (max-width: 620px) {
  .evo-step {
    grid-template-columns: 1fr;
    gap: 2px;
  }
}
.modal,
.handoff {
  position: fixed;
  inset: 0;
  background: rgba(3, 6, 9, 0.88);
  z-index: 100;
  display: grid;
  place-items: center;
  padding: 18px;
}
#start {
  background:
    linear-gradient(rgba(3, 6, 9, 0.5), rgba(3, 6, 9, 0.5)),
    url(/art/monolith-bg.webp) center / cover no-repeat var(--bg);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  padding: 0 0 18px;
  /* The start screen is the one scroller: the card must never get its own
     nested scrollbar, or a tall header leaves its top rows unreachable. */
  overflow-y: auto;
}
.start-topbar {
  width: 100%;
  min-height: 62px;
  display: grid;
  /* The brand keeps its full width; the account line is the one that gives,
     so a long handle ellipsizes instead of shoving the version number. */
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  position: relative;
  z-index: 20;
  flex: none;
  background: rgba(9, 13, 18, 0.94);
  backdrop-filter: blur(14px);
  padding: 10px 24px;
  border-bottom: 1px solid #18232d;
}
.start-brand {
  display: flex;
  align-items: baseline;
  gap: 8px;
  justify-self: start;
}
.start-brand h1 {
  margin: 0;
}
.start-topbar .button-link {
  display: inline-flex;
  align-items: center;
}
.start-menu {
  position: relative;
  justify-self: end;
}
.start-menu-trigger {
  height: 42px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0 12px;
}
.start-menu-trigger svg {
  width: 23px;
  height: 23px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
}
.start-menu-panel {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  width: max-content;
  min-width: 190px;
  max-width: calc(100vw - 24px);
  display: flex;
  flex-direction: column;
  padding: 6px;
  background: #121a22;
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
  z-index: 30;
}
/* One panel, three blocks: what the game is, who you are, and telling us it
   broke. A hairline keeps them apart without a heading each. */
.start-menu-group + .start-menu-group {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid var(--line);
}
.start-menu-panel > * > :is(button, a),
.start-menu-panel > :is(button, a) {
  display: flex;
  width: 100%;
  align-items: center;
  text-align: left;
  white-space: nowrap;
  padding: 10px 12px;
  background: transparent;
}
/* Who you are, in the topbar so it is answerable without opening the menu. */
.start-account {
  margin: 0;
  /* Stretched, not end-aligned: an end-aligned box sizes to its text and
     overflows the column, so the ellipsis below would never fire. */
  justify-self: stretch;
  text-align: right;
  color: var(--muted);
  font-size: var(--fs-md);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Not a menu item: same gutter, face and size as one, but unreachable. It
   inherits its type from the panel so it lines up with the items around it. */
/* The transparent rule above outranks the plain button:hover, so every item
   needs its highlight naming the panel too — otherwise only the links (which
   carry .button-link:hover) light up. Keyboard focus gets the same mark. */
.start-menu-panel :is(button, a):hover,
.start-menu-panel :is(button, a):focus-visible {
  background: #243443;
}
.start-content {
  min-height: 0;
  flex: 1;
  display: grid;
  justify-items: center;
  /* "safe" falls back to start-alignment once the card is taller than the
     space left over, so its first line never lands above the scroll origin. */
  align-content: safe center;
  padding: 18px 16px;
}
.start-card {
  width: min(850px, 100%);
}
/* Shown once per device, to signed-in players on a small screen (auth-client). */
.mobile-notice {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
  padding: 10px 12px;
  border: 1px solid var(--warn);
  background: var(--surface);
}
.mobile-notice p {
  margin: 0;
  flex: 1;
  color: var(--warn);
  font-size: var(--fs-sm);
}
.mobile-notice button {
  flex: none;
}
/* Needs the id to beat `.modal-card`'s own max-height/overflow further down:
   the start card scrolls with the screen, it is not a scroll box of its own. */
#start .start-card {
  max-height: none;
  overflow: visible;
}
.start-card > .rules:first-child {
  margin-top: 0;
}
.advanced {
  margin-top: 14px;
}
.advanced > summary {
  padding: 12px 2px;
  color: var(--muted);
  font-family: var(--mono);
  font-size: var(--fs-md);
  font-weight: 700;
  cursor: pointer;
}
.advanced-fields {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  padding: 0 0 14px;
}
.advanced-fields label {
  font-size: var(--fs-md);
  color: var(--muted);
}
.modal-card {
  width: min(680px, 100%);
  max-height: 88vh;
  overflow: auto;
  background: #121a22;
  padding: 20px;
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.65);
}
.modal-card h1,
.modal-card h2 {
  margin-top: 0;
}
.debt-choice-card {
  width: min(620px, 100%);
}
.rules-modal-card {
  width: min(920px, 100%);
}
.rules-layout {
  display: grid;
  grid-template-columns: 170px minmax(0, 1fr);
  gap: 24px;
  align-items: start;
}
.rules-nav {
  position: sticky;
  top: 46px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.rules-nav a {
  color: var(--muted);
  text-decoration: none;
  font-size: var(--fs-md);
  padding: 3px 0;
}
.rules-nav a:hover {
  color: var(--ink);
}
/* The modal is the scroller, so a jumped-to heading has to clear the sticky
   header that sits over it. */
.rules-layout h3 {
  scroll-margin-top: 54px;
}
.rules-quickstart {
  margin: 0;
}
@media (max-width: 900px) {
  /* minmax(0, …), not 1fr: a grid item's automatic minimum is its content, and
     the widest rules example would otherwise stretch the column past the
     screen. */
  .rules-layout {
    grid-template-columns: minmax(0, 1fr);
    gap: 10px;
  }
  .rules-nav {
    position: static;
    flex-direction: row;
    flex-wrap: wrap;
    gap: 4px 12px;
  }
}
.debt-choice-actions {
  display: grid;
  gap: 10px;
  margin-top: 18px;
}
.debt-choice {
  display: flex;
  gap: 10px;
  align-items: start;
  text-align: left;
}
.debt-choice > span {
  display: grid;
  gap: 3px;
}
.debt-choice .over {
  color: var(--bad);
}
.debt-choice .quick-action-cost {
  display: inline-flex;
  gap: 6px;
  margin-right: 6px;
}
.debt-choice small {
  color: var(--muted);
}
/* Deploy and Support-step rows: the reason a row can't be taken is printed
   under it, since a tooltip never shows on a touch screen. */
.debt-choice small.choice-reason {
  color: var(--warn);
}
.debt-choice[aria-disabled='true'] {
  opacity: 0.6;
}
.support-row {
  width: 100%;
}
#supportStepFooter .action-note {
  display: block;
  font-size: var(--fs-sm);
}
.support-row > span:not(.support-state) {
  flex: 1;
  min-width: 0;
}
.support-state {
  flex: none;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  padding: 2px 6px;
  border: 1px solid currentColor;
}
.support-state.running {
  color: var(--good);
}
.support-state.dormant {
  color: var(--muted);
}
.support-state.retiring {
  color: var(--bad);
}
.form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}
.form-row .form-span {
  grid-column: 1 / -1;
  margin: 0;
}
.form-row label {
  font-size: var(--fs-md);
  color: var(--muted);
}
input,
select,
textarea {
  width: 100%;
  background: #0b1117;
  color: var(--ink);
  border: 1px solid #31465a;
  padding: 0.65rem;
  /* Selects ignore line-height, so pin every control to the same box height. */
  line-height: 1.5;
  min-height: calc(1.5em + 1.3rem + 2px);
}
.exit-actions,
.start-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 14px;
}
.rules {
  line-height: 1.55;
  color: #dce5ee;
}
.rules h3 {
  font-family: var(--mono);
}
.rules li {
  margin: 0.35rem 0;
}
.rules-example {
  max-width: 760px;
  margin: 18px 0 24px;
  padding: 14px;
  overflow: hidden;
  background: #0d141b;
  border: 1px solid #263746;
}
.rules-example-head {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
  color: var(--muted);
  font-family: var(--mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.rules-example-head b {
  color: var(--ink);
}
.rules-waffle svg {
  max-height: 190px;
}
.rules-waffle {
  margin-inline: auto;
}
.rules-formula-label {
  max-width: 760px;
  margin: 0 0 8px;
}
.rules-valuation-formula {
  margin-inline: auto;
  overflow-x: auto;
}
.rules-valuation-formula .formula-row {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: nowrap;
  gap: 6px;
  width: max-content;
  margin-inline: auto;
  font-size: var(--fs-sm);
}
.rules-valuation-formula .formula-group {
  display: inline-flex;
  align-items: center;
  gap: 3px;
}
.rules-valuation-formula .formula-term {
  display: inline-flex;
  align-items: center;
  gap: 0.3em;
  color: var(--ink);
  white-space: nowrap;
}
.rules-valuation-formula .formula-term .game-icon {
  width: 1.1em;
  height: 1.1em;
}
.rules-valuation-formula .formula-op {
  color: var(--muted);
  font-family: var(--mono);
  font-weight: 800;
}
.rules-world-card {
  max-width: 480px;
  margin-inline: auto;
}
.rules-world-card .world-art {
  max-height: 120px;
  overflow: hidden;
  margin-top: 10px;
}
.rules-world-card .world-art img {
  width: 100%;
  height: 120px;
  object-fit: cover;
}
.rules-action-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  max-width: 760px;
  margin: 16px auto 22px;
}
.rules-status-example {
  max-width: 760px;
  margin: 16px auto 10px;
}
.rules-action-choice {
  min-height: 70px;
  padding: 10px 11px;
  background: var(--surface2);
  border-top: 2px solid #365064;
}
.rules-action-name {
  display: flex;
  align-items: center;
  margin-top: 6px;
  font-weight: 800;
}
.rules-action-name .game-term {
  margin-left: 0.28em;
}
.rules-action-choice small {
  display: block;
  margin-top: 6px;
  color: var(--muted);
  line-height: 1.3;
}
.rules-requirements {
  justify-content: flex-start;
  margin: 16px 0 24px;
}
.rules-requirements .tt-card,
.rules-harshest .tt-card {
  cursor: default;
}
.rules-requirements .tt-card:hover,
.rules-harshest .tt-card:hover {
  transform: none;
  box-shadow: 0 9px 24px rgba(0, 0, 0, 0.32);
}
.rules-harshest {
  margin: 14px 0 22px;
}
.rules-harshest-card {
  display: grid;
  grid-template-columns: 180px minmax(180px, 420px);
  align-items: center;
  gap: 18px;
}
.rules-harshest-card .tt-slot-label {
  display: none;
}
.rules-harshest-card p {
  margin: 0;
}
.rules-harshest-more {
  margin-top: 18px;
  color: var(--muted);
  font-family: var(--mono);
  font-size: var(--fs-sm);
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.rules-harshest-card ul {
  margin: 6px 0 0;
  padding-left: 1.2rem;
}
.rules-incident-card {
  width: min(700px, 100%);
  margin: 18px auto 24px;
  padding: 18px;
}
.rules-architecture {
  min-height: 0;
  padding-block: 18px;
}
.rules-funding {
  max-width: 600px;
  margin-inline: auto;
}
.rules-ai-race {
  max-width: 600px;
  margin-inline: auto;
}
@media (max-width: 700px) {
  .rules-action-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .rules-harshest-card {
    grid-template-columns: 1fr;
  }
}
.shortcuts-help-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.shortcuts-help-list li {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 8px 0;
  border-bottom: 1px solid #1e2b37;
}
.shortcuts-help-list li:last-child {
  border-bottom: none;
}
.shortcuts-help-list kbd {
  flex: 0 0 auto;
  min-width: 64px;
  text-align: center;
  font-family: var(--mono);
  font-size: var(--fs-md);
  background: #0b1117;
  border: 1px solid #31465a;
  border-bottom-width: 2px;
  padding: 3px 8px;
  color: var(--ink);
}
.log {
  max-height: 320px;
  overflow: auto;
  background: #080c10;
  padding: 8px;
  font-family: var(--mono);
  font-size: var(--fs-sm);
  color: #a8b8c8;
}
.log div {
  padding: 3px 0;
  border-bottom: 1px dotted #202c38;
}
.log .q {
  color: #7f91a4;
}
.dev-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 10px 0;
}
.small {
  font-size: var(--fs-sm);
}
.toast {
  /* It sits over the action bar; let clicks through to the buttons below (#540). */
  pointer-events: none;
  position: fixed;
  right: 12px;
  bottom: 12px;
  background: #1d2a36;
  padding: 10px 12px;
  z-index: 120;
  max-width: 360px;
  box-shadow: var(--shadow);
}
.incident-card {
  width: min(760px, 100%);
  border: 1px solid rgba(239, 111, 108, 0.55);
  box-shadow:
    0 0 0 1px rgba(239, 111, 108, 0.12),
    0 30px 80px rgba(0, 0, 0, 0.7);
}
.incident-card.morale-good {
  border-color: rgba(142, 227, 164, 0.55);
  box-shadow:
    0 0 0 1px rgba(142, 227, 164, 0.12),
    0 30px 80px rgba(0, 0, 0, 0.7);
}
.incident-card.morale-good .incident-kicker {
  color: var(--good);
}
.incident-layout {
  display: grid;
  grid-template-columns: 220px 1fr;
  gap: 20px;
}
.incident-art {
  position: relative;
  min-height: 260px;
  overflow: hidden;
  background: linear-gradient(145deg, #794c4c, #28191b);
}
.incident-art.art-morale-good {
  background: linear-gradient(145deg, #3f6b52, #16261d);
}
.incident-art .art-img {
  display: block;
}
/* The morale flag icon rides on top of the generated art (which fills the
   whole box, z-index 1) as a small corner badge instead of being covered by it. */
.incident-art .game-icon {
  position: absolute;
  z-index: 2;
  top: 14px;
  left: 14px;
  width: 26px;
  height: 26px;
  color: rgba(255, 255, 255, 0.9);
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.6));
}
.incident-kicker {
  font-family: var(--mono);
  color: var(--bad);
  font-size: var(--fs-sm);
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.incident-title {
  font-size: var(--fs-lg);
  line-height: 1.05;
  margin: 5px 0 7px;
}
.incident-target {
  color: var(--warn);
  font-family: var(--mono);
  font-size: var(--fs-md);
}
.incident-effect {
  font-size: var(--fs-md);
  line-height: 1.5;
  margin: 16px 0;
}
.pass-risks {
  margin: 10px 0 0;
  padding-left: 18px;
}
.pass-risks > li + li {
  margin-top: 8px;
}
.pass-risks ul {
  margin: 2px 0 0;
  padding-left: 18px;
  color: var(--muted);
}
.incident-quip {
  font-size: var(--fs-md);
  font-style: italic;
  color: var(--muted);
  margin: 0 0 13px;
}
.incident-results {
  list-style: none;
  padding: 0;
  margin: 13px 0;
  display: flex;
  flex-direction: column;
  gap: 7px;
}
.incident-results li {
  background: #1b222a;
  border-left: 3px solid var(--bad);
  padding: 8px 10px;
  font-size: var(--fs-md);
}
.incident-results .game-icon {
  width: 1.05em;
  height: 1.05em;
}
.incident-card .start-actions {
  justify-content: flex-end;
}
@media (max-width: 720px) {
  #start {
    padding: 0 0 9px;
  }
  .start-content {
    padding: 18px 9px;
  }
  .start-topbar {
    min-height: 0;
    grid-template-columns: auto minmax(0, 1fr) auto;
    padding: 8px 12px;
    gap: 8px;
  }
  #start .start-brand-name {
    font-size: 15px;
  }
  .start-brand-name {
    font-size: var(--fs-md);
  }
  #cardZoom {
    padding: 0;
    place-items: stretch;
  }
  #cardZoom .zoomcard {
    display: flex;
    flex-direction: column;
    width: 100%;
    max-width: none;
    height: 100dvh;
    max-height: 100dvh;
    padding: 0 15px calc(15px + env(safe-area-inset-bottom));
    box-shadow: none;
    overscroll-behavior: contain;
  }
  /* Fixed-height art in a height-capped flex column collapses to nothing
     unless it opts out of shrinking. The height is also its own: the mobile
     .card-art rule above would otherwise shrink the zoomed still to hand size. */
  #cardZoom .zoom-art {
    flex: none;
    height: 124px;
  }
  /* No room for side arrows on a full-screen sheet. */
  .zoom-nav {
    display: none;
  }
  #cardZoom .start-actions {
    position: sticky;
    top: 0;
    order: -1;
    z-index: 3;
    height: auto;
    justify-content: flex-end;
    margin: 0 -15px 15px;
    padding: calc(12px + env(safe-area-inset-top)) 15px 12px;
    background: #121a22;
    border-bottom: 1px solid var(--line);
  }
  .form-row {
    grid-template-columns: 1fr;
  }
  .advanced-fields {
    grid-template-columns: 1fr;
  }
  .incident-layout {
    grid-template-columns: 1fr;
  }
  .incident-art {
    min-height: 150px;
  }
}

.invite-box {
  margin-top: 14px;
}

.auth-card {
  width: min(420px, 100%);
}
.auth-form {
  display: grid;
  gap: 10px;
}
.auth-form label {
  font-size: var(--fs-md);
  color: var(--muted);
}
/* Two sentences, under the field it explains rather than trailing the label. */
#authEmailNote {
  display: block;
  margin-top: 4px;
}
.auth-terms {
  margin: 0;
  line-height: 1.4;
}
.auth-card .sub a + a {
  margin-left: 12px;
}
.stats-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  margin-top: 14px;
  border-bottom: 2px solid var(--line);
}
.stats-tab {
  background: var(--surface2);
  color: var(--muted);
  border: 2px solid var(--line);
  border-bottom: none;
  margin-right: 6px;
  padding: 7px 14px;
  font: inherit;
  font-size: var(--fs-md);
  font-weight: 700;
  letter-spacing: 0.02em;
  cursor: pointer;
}
.stats-tab:hover {
  color: var(--ink);
}
.stats-tab[aria-selected='true'] {
  background: var(--surface3);
  color: var(--ink);
  border-color: var(--accent);
}
.stats-panel {
  margin-top: 4px;
}
/* The room code is the game's permanent address: past boards open from here. */
.room-link {
  font-family: var(--mono);
  color: var(--accent);
}

.stats-table-wrap {
  overflow-x: auto;
  margin-top: 8px;
}
.stats-table-wrap.compared {
  margin-top: 24px;
}
.stats-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-md);
}
.stats-table th,
.stats-table td {
  text-align: left;
  padding: 5px 8px;
  border-bottom: 1px solid #22313f;
  white-space: nowrap;
}
.stats-table th {
  color: var(--muted);
  font-weight: 600;
}

.handoff-card {
  text-align: center;
}

.delta {
  display: inline-flex;
  align-items: center;
  gap: 0.15em;
  font-weight: 700;
}
.delta.up {
  color: var(--good);
}
.delta.down {
  color: var(--bad);
}

.mode-picker {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}
.mode-picker > * {
  display: flex;
  align-items: center;
  gap: 16px;
  text-align: left;
  padding: 26px 22px;
}
.mode-icon {
  flex: none;
  width: 32px;
  height: 32px;
  fill: none;
  stroke: currentColor;
  stroke-width: 3;
  stroke-linecap: square;
  stroke-linejoin: miter;
}
.mode-label > span {
  display: block;
  color: var(--muted);
  font-size: var(--fs-md);
  font-weight: normal;
  margin-top: 6px;
}
#modeTitle {
  margin: 18px 0 8px;
}
#modeBack {
  padding-left: 0;
}
#playSetup .start-actions button {
  min-width: 160px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
}
/* The crossed swords and doorway these two buttons inherited from the tiles
   they replaced, at button scale. */
.btn-icon {
  flex: none;
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 3;
  stroke-linecap: square;
  stroke-linejoin: miter;
}
/* Only matchmaking is a duel; Start is just Start. */
#playSetup:not([data-setup-mode='people']) #beginPlayIcon {
  display: none;
}
.invite-box a {
  overflow-wrap: anywhere;
}
@media (max-width: 540px) {
  .mode-picker {
    grid-template-columns: 1fr;
  }
}

/* Tutorial (#347). The chip sits in the flow at the end of the tile rather
   than floating over it — absolute placement collided with the title at
   phone widths. */
/* Out of flow, in the tile's top-right corner: a badge on one tile must not
   make that tile — and with it the whole grid row — taller than the rest.
   The tile reserves room for it so the title can never run underneath. */
#startTutorial {
  position: relative;
}
.mode-chip {
  position: absolute;
  top: 10px;
  right: 10px;
  padding: 3px 8px;
  background: var(--accent);
  color: var(--paperInk);
  font-size: var(--fs-sm);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* Inline links inside body copy: the browser default is unreadable on this
   background. */
:is(.rules, .sub) a {
  color: var(--accent);
}
.tutorial-overlay {
  position: fixed;
  inset: 0;
  z-index: 200;
  pointer-events: none;
}
.tutorial-overlay.hidden {
  display: none;
}
.tutorial-backdrop {
  position: fixed;
  background: rgba(3, 6, 9, 0.72);
  pointer-events: auto;
}
.tutorial-focus-ring {
  position: fixed;
  outline: 3px solid var(--accent);
  outline-offset: 0;
  pointer-events: none;
}
.tutorial-coachmark {
  position: fixed;
  width: min(320px, calc(100vw - 24px));
  background: var(--surface2);
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
  padding: 14px;
  pointer-events: auto;
}
.tutorial-progress {
  color: var(--muted);
  font-size: var(--fs-sm);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 6px;
}
.tutorial-copy {
  margin: 0 0 12px;
  color: var(--ink);
}
.tutorial-actions {
  display: flex;
  justify-content: space-between;
  gap: 10px;
}

/* ── Rail components, shared at every size ────────────────────────────────
   The ledger rows and the Rivals lines are one component on the desk, the
   two-column desk and the phone. They sit here, after the older small-screen
   rules and before THE DESK, so they win the same cascades everywhere. */
/* ── The closed rail as a ledger ─────────────────────────────────────────
   Forecast, Funding and AI race are one line each: the label in a column
   of its own so the three line up, the numbers and bars to its right. The
   state of a row is its colour — the label and a 3px edge — and the
   sentence behind that colour lives in the row's title, its aria-label and
   the top of the detail it opens. */
/* One four-column grid shared by all three rows, so their bars start at
   the same x: label | left value | bar | right value. Every wrapper in
   between is unwrapped, so the values themselves are the grid items. */
/* 3px edge plus 7px padding: the same 10px inset as every other rail card. */
/* Fixed columns on whole-pixel rows: every bar is the same length and
   lands on the same pixels, whatever the values beside it say. */
.right-rail .ledger-row {
  display: grid;
  grid-template-columns: var(--ledger-label) var(--ledger-left) minmax(0, 1fr) var(--ledger-right);
  grid-auto-rows: 16px;
  align-items: center;
  gap: 6px 8px;
  border: 0;
  border-left: 3px solid transparent;
}
/* Beats the rail card padding on the desk, whatever the order. */
.right-rail .ledger-row.ledger-row {
  padding-left: 7px;
}
.ledger-row .ledger-label {
  grid-column: 1;
  grid-row: 1;
  margin: 0;
}
/* "Quarter forecast" everywhere else; the label column only has room for
   the noun. */
.ledger-label-long {
  display: none;
}
.ledger-row .ledger-body,
.ledger-row .race-row,
.ledger-row .race-row-head {
  display: contents;
}
/* Ledger values are Data in one grey. State colour is on the label and
   the edge, never the values. */
.ledger-row .ledger-body * {
  color: var(--muted);
}
.ledger-row .forecast-line,
.ledger-row .funding-bar,
.ledger-row .race-track {
  grid-column: 3;
  margin: 0;
  height: 6px;
}
/* Forecast: −$1M | bar | +$0M. Its legend grid is for off the desk. */
#quarterMetrics .forecast-legend {
  display: contents;
}
#quarterMetrics .forecast-legend .costs {
  grid-column: 2;
  grid-row: 1;
}
#quarterMetrics .forecast-legend .forecast-line {
  grid-column: 3;
  grid-row: 1;
}
#quarterMetrics .forecast-legend .revenue {
  grid-column: 4;
  grid-row: 1;
  justify-self: end;
}
/* Funding: Seed | bar | $2M to Series A. */
#companyStatus .race-row-head > :first-child {
  grid-column: 2;
  grid-row: 1;
}
#companyStatus .funding-bar {
  grid-row: 1;
}
#companyStatus .race-row-head > :last-child {
  grid-column: 4;
  grid-row: 1;
  justify-self: end;
}
#companyStatus .market-clock,
#companyStatus > :not(.ledger-label, .ledger-body) {
  grid-column: 1/-1;
}
/* AI race: a name, then its three steps across the bar and right columns,
   one line per player. The stage words and first movers are the tooltip's. */
#aiStatus .race-row-head > :first-child {
  grid-column: 2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#aiStatus .race-row-head > :last-child,
#aiStatus .ai-first-movers {
  display: none;
}
#aiStatus .race-track {
  grid-column: 3/5;
}
#aiStatus .race-step {
  height: 6px;
}
/* Amber for a quarter that is not growing, red for one that ends the
   company. Colour is the glance; the words are in the tooltip. */
.ledger-row.warning {
  border-left-color: var(--warn);
}
.ledger-row.warning .ledger-label {
  color: var(--warn);
}
.ledger-row.dire {
  border-left-color: var(--bad);
}
.ledger-row.dire .ledger-label {
  color: var(--bad);
}
.ledger-row.ready {
  border-left-color: var(--accent);
}
.ledger-row.ready .ledger-label {
  color: var(--accent);
}
#aiStatus .race-step.on {
  background: var(--accent);
}
/* Each rival line spans the value columns and shares them, so its name
   sits where the AI race names do and its figures where the bars start. */
#rivalsCard .opponent-card {
  grid-column: 2/-1;
  display: grid;
  grid-template-columns: subgrid;
}
#rivalsCard .rival-stats {
  grid-column: 2/-1;
}

/* ── Components that used to live in THE DESK ───────────────────────────
   Styled once here for every size; the layouts below only place them. */
/* An inline strip rather than a full-width bordered box: on one row the
   turn colour alone carries mine-versus-theirs, and the box cost 40px. */
#statusBar {
  margin-top: 0;
  padding: 0;
  gap: 9px;
  flex-wrap: nowrap;
  overflow: hidden;
  justify-content: flex-start;
  white-space: nowrap;
}
#statusBar.mine,
#statusBar.theirs,
#statusBar.mine.actions-spent {
  background: none;
  box-shadow: none;
}
/* The rail wanted 639px against 566px of board, so the AI Race panel fell
   off the bottom. Tighter panels and a smaller event still buy most of it
   back; the two cards whose height follows the game — the World event text
   and the Competition list — then shrink and scroll inside themselves
   rather than push the panels below them out of sight. */
.right-rail {
  gap: 8px;
}
.right-rail .world-card,
.right-rail .race-card,
.right-rail .quarter-metrics {
  padding: 9px 10px;
}
/* One gap sets the whole rail's rhythm, so no child brings a margin. The
   Competition wrapper and its list step out of the layout entirely, which
   makes each rival card a rail item on the same gap; the heading inside is
   for screen readers and positioned out of the flow. */
.right-rail > :not(#commandBar) {
  margin-top: 0;
  margin-bottom: 0;
}
.world-art {
  height: 46px;
  margin: 7px 0 2px;
}
/* The rail keeps its layers to itself: the sticky detail header and the
   pinned actions bar stack only against the rail's own content, and the
   top bar (z-index 20) and its menu stay above the whole rail. */
.right-rail {
  isolation: isolate;
}
#railTabs {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  flex: 0 0 auto;
  gap: 1px;
  min-height: 30px;
  background: var(--surface);
}
#railTabs.hidden {
  display: none;
}
.rail-tab {
  min-width: 0;
  padding: 0 6px;
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;
  cursor: pointer;
}
.rail-tab:hover {
  background: #131c25;
  color: var(--ink);
}
.rail-tab.current {
  color: var(--accent);
  border-bottom-color: var(--accent);
}
.rail-tab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
/* A long detail scrolls inside the panel; Close never scrolls away with it.
   The panel gives up its top padding so the header can carry it and stay
   flush with the top edge when it sticks. */
#inspector.inspector .inspector-head {
  position: sticky;
  top: 0;
  z-index: 1;
  margin-bottom: 0;
  padding: 10px 0 8px;
  background: var(--surface);
}
/* The rail is wider than the drawer this art was sized for, so the banner
   scales instead of stretching: production's 358x108 box, to the pixel of
   its aspect ratio, with the same object-fit crop. */
.inspect-art {
  height: auto;
  aspect-ratio: 358 / 108;
}
/* Three actions on a System should not cost a screenful. */
.inspect-actions {
  gap: 6px;
  margin: 10px 0;
}

.hand-shell {
  padding: 9px 12px 7px;
}
.hand-shell .section-head {
  margin-bottom: 7px;
}
.hand {
  padding-bottom: 6px;
}

/* ── THE DESK ───────────────────────────────────────────────────────────────
   Wherever there is room for a rail, 700px and up, the game is a desk, not a
   document: the header on top, the board in the middle, the hand along the
   bottom, the rail the full height of the right column with the actions bar
   as its last item. Panels scroll inside themselves so the page never does.

   One layout contract at every size (docs/design/dock-rail-implementation-
   plan.md): the canonical order poured into three columns at 981px and up,
   two from 700px, one below. This block is the three and the two; the only
   thing its nested 980px query changes is the grid and where the panels sit.

   Width picks the layout; height never does. Browser zoom divides the
   viewport in both directions, so a height gate only moves the cliff: a
   1366x768 laptop at 125% is about 1093x520. Height picks density instead,
   in the tiers at the end of this block, and below the last one the desk
   scrolls rather than rearranging. Zoom may cost decoration, never layout
   or type size. */
@media (min-width: 700px) {
  /* The script reads this rather than keeping its own copy of the gate. */
  :root {
    --layout: desk3;
    /* The desk's smallest type: nothing here renders below 12px. */
    --fs-sm: 0.75rem;
  }
  /* Only #app locks scrolling; html and body stay alone, because
     backstory.html, terms.html and mcp.html share this stylesheet and are
     ordinary scrolling pages. #start and the modals are fixed overlays with
     their own scrollers, so they are unaffected either way. */
  .app {
    height: 100dvh;
    overflow: hidden;
    padding: 0 16px 12px;
    display: grid;
    grid-template-columns: minmax(220px, 0.72fr) minmax(480px, 1.75fr) minmax(340px, 0.85fr);
    grid-template-rows: auto minmax(0, 1fr) auto;
    gap: 10px 12px;
  }
  /* The hand comes before the board in the DOM and stays there — tests, the
     tutorial and the phone layout all depend on that order. Explicit rows put
     it in the dock without touching the markup. */
  /* A whole-pixel height, so everything under the bar starts on a whole
     pixel. */
  .topbar {
    grid-column: 1/-1;
    grid-row: 1;
    grid-template-columns: auto minmax(0, 1fr) auto auto;
    gap: 14px;
    height: 46px;
    padding: 0;
  }
  /* One row: brand, turn status, metrics, actions. #statusBar is last in the
     DOM but belongs in the middle, so every child gets an explicit column. */
  .brand-block {
    grid-column: 1;
    grid-row: 1;
  }
  #statusBar {
    grid-column: 2;
    grid-row: 1;
  }
  #globalChips {
    grid-column: 3;
    grid-row: 1;
  }
  .top-actions {
    grid-column: 4;
    grid-row: 1;
  }
  #connectionBanner {
    grid-column: 1/-1;
    grid-row: 2;
  }
  /* One row at every desk width. When it gets tight the wordmark goes first,
     then the status's long form (its dot and pips); the quarter, whose turn
     and the actions left stay, and the chips never wrap or truncate. Measured:
     the full bar needs about 1049px of content (1100px with a long name), so
     the wordmark goes below 1140px; without it the bar needs about 963px, so
     the pips go below 1000px. */
  #globalChips {
    flex-wrap: nowrap;
  }
  @media (max-width: 1139px) {
    /* tabletop.css also styles .brand-block and loads later. */
    .topbar .brand-block {
      display: none;
    }
    #statusBar {
      grid-column: 1/3;
    }
  }
  @media (max-width: 999px) {
    #statusBar :is(.dot, .pips) {
      display: none;
    }
  }
  /* The board stops being a grid of its own: its three panels become items
     of the app grid, so the rail can run the full height of the right
     column, dock included. */
  .board {
    display: contents;
  }
  .requirements-panel {
    grid-column: 1;
    grid-row: 2;
  }
  .architecture-panel {
    grid-column: 2;
    grid-row: 2;
  }
  .right-rail {
    grid-column: 3;
    grid-row: 2/4;
  }
  .hand-shell {
    grid-column: 1/3;
    grid-row: 3;
    margin-top: 0;
    min-width: 0;
  }
  /* The dock (hand, then actions) comes after the board in the DOM, which is
     the canonical reading order. On the desk it steps out of the layout so
     the hand sits under the board and the actions bar at the bottom of the
     rail's column, over the room the rail reserves for it: collapsed it is
     one line, and the detail above it takes the height back. */
  .dock {
    display: contents;
  }
  .command-bar {
    grid-column: 3;
    grid-row: 3;
    align-self: end;
    position: static;
    transform: none;
    width: auto;
    max-width: none;
    box-shadow: none;
  }
  .right-rail {
    padding-bottom: calc(var(--command-bar-h, 0px) + 8px);
  }
  /* Overflow goes inside a panel, never out to the page. */
  .requirements-panel,
  .architecture-panel,
  .right-rail {
    min-height: 0;
  }
  .architecture-panel {
    display: flex;
    flex-direction: column;
  }
  #architecture {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
  }
  .requirements-panel,
  .right-rail {
    overflow-y: auto;
  }

  /* The inspector stops floating over the rail and takes a place in it: first
     in DOM order, so a selected card's actions sit as near the hand as the
     layout allows. It is the only child that grows, so the folded headers
     below it never scroll out of view. */
  #inspector.inspector {
    position: static;
    /* A flex item honours z-index without being positioned, so the drawer's
       60 would still lift the docked panel over the top bar and its menu. */
    z-index: auto;
    width: auto;
    top: auto;
    bottom: auto;
    right: auto;
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    /* No top padding: the sticky header carries it and stays flush. */
    padding: 0 10px 12px;
    box-shadow: none;
    background: var(--surface);
  }

  #world {
    /* When the two do have to give, the event text yields first: it is a line
       you read once a quarter, the Competition list is one you read on every
       turn. The floor keeps the event's name readable however short the
       screen gets — below it the rail's own scrollbar takes over. */
    flex-shrink: 3;
    min-height: 76px;
    overflow-y: auto;
  }


  /* ── Open rail ──────────────────────────────────────────────────────────
     A detail view takes the rail, so the rail takes the width — from
     Architecture, which has the most to spare. The move is a hard cut: no
     easing, per the house rules. */
  body.inspector-open .app {
    grid-template-columns: minmax(220px, 0.6fr) minmax(420px, 1.4fr) minmax(500px, 1.2fr);
  }
  .right-rail {
    --metric-square: 16px;
  }

  /* The rail has two states, and this is the open one: the strip, the
     detail, and the collapsed actions line. The five panels step aside
     whole — nothing is on screen twice, and nothing is half-shown. */
  body.inspector-open .right-rail > :not(#railTabs, #inspector, #commandBar) {
    display: none;
  }
  /* The dock is as tall as a hand card, so every pixel it gives up is a pixel
     the rail keeps. Tighter chrome around the row, and a shorter still on the
     card — the art is the one part of a card that loses nothing by shrinking. */
  /* The desk card is the full 154px: five stats on one line need it. Its
     height comes from the card's own parts, so the dock grows to fit. */

  /* Below 1280px the column floors themselves are the constraint, so they
     give, the rail last: the closed tracks need 890px and the open ones 910px
     of the 925px a 981px screen leaves. The chart then scales below its
     native 470px, which is the lesser loss. */
  @media (max-width: 1279px) {
    .app {
      grid-template-columns: minmax(180px, 0.72fr) minmax(380px, 1.75fr) minmax(330px, 0.85fr);
    }
    body.inspector-open .app {
      grid-template-columns: minmax(170px, 0.6fr) minmax(300px, 1.4fr) minmax(440px, 1.2fr);
    }
  }


  /* ── Two columns, 700 to 980px ──────────────────────────────────────────
     The three-column desk with Requirements moved above Architecture: the
     left column is Requirements at its content height, Architecture taking
     what is left, then the hand; the right is the rail, full height, the
     actions bar last. Open, the rail takes half the screen and the left
     column gives up the width. */
  @media (max-width: 980px) {
    :root {
      --layout: desk2;
    }
    .app {
      grid-template-columns: minmax(0, 1fr) clamp(300px, 36vw, 340px);
      grid-template-rows: auto auto minmax(0, 1fr) auto;
    }
    body.inspector-open .app {
      grid-template-columns: minmax(0, 1fr) max(360px, 50vw);
    }
    .requirements-panel {
      grid-column: 1;
      grid-row: 2;
    }
    .architecture-panel {
      grid-column: 1;
      grid-row: 3;
    }
    .right-rail {
      grid-column: 2;
      grid-row: 2/5;
    }
    .hand-shell {
      grid-column: 1;
      grid-row: 4;
    }
    .command-bar {
      grid-column: 2;
      grid-row: 4;
    }
  }

  /* The top bar keeps one row while it fits: status, chips and Menu need
     about 820px, a long company name about 50px more. Below 900px it takes
     two rows, status and Menu, then the chips. Never three. */
  @media (max-width: 899px) {
    .topbar {
      grid-template-columns: minmax(0, 1fr) auto;
      grid-template-rows: auto auto;
      height: auto;
      row-gap: 4px;
      padding: 6px 0;
    }
    #statusBar {
      grid-column: 1;
      grid-row: 1;
    }
    .top-actions {
      grid-column: 2;
      grid-row: 1;
    }
    #globalChips {
      grid-column: 1/-1;
      grid-row: 2;
      justify-content: flex-start;
    }
    #connectionBanner {
      grid-row: 3;
    }
  }

  /* ── Density by height ──────────────────────────────────────────────────
     Each tier sheds what the one above kept, always in this order, and never
     changes a font size or a card's width.
     The thresholds come from measured content, 3 players, the longest world
     event and the actions bar open: the comfortable rail needs about 590px
     and three requirements with their MISS lines a 364px board, which puts
     the floor of each tier just above what it needs.
     Comfortable (700px and up): everything as designed. */
  .hand .card {
    --card-art-h: var(--hand-art-h, 72px);
  }
  /* Compact (620 to 699px): the world event's still and the flavour lines
     go, and every hand card gets the same shorter still. The closed rail's
     rivals and AI race are already one line each at every height. */
  @media (max-height: 699px) {
    :root {
      --hand-art-h: 44px;
    }
    .world-art,
    #inspector .flavor {
      display: none;
    }
  }
  /* Tight (560 to 619px): the actions bar starts collapsed (B still wins,
     and is remembered), and hand cards become stubs: title, type and stats,
     the still left to the detail. */
  @media (max-height: 619px) {
    :root {
      --hand-art-h: 0px;
      --bar-default: collapsed;
    }
    .hand .card-art {
      display: none;
    }
  }
  /* Floor (below 560px): the desk stops locking the viewport and the page
     scrolls. Same grid, same order; it never falls back to the tablet
     layout because of height. */
  @media (max-height: 559px) {
    .app {
      height: auto;
      min-height: 100dvh;
      overflow: visible;
    }
    /* The rail is part of the page now, so the actions bar pins to the
       viewport's bottom edge while the page scrolls. */
    .right-rail {
      overflow: visible;
    }
    .command-bar {
      position: sticky;
      bottom: 0;
      z-index: 2;
    }
  }
}

/* ── Row forms, below 981px ──────────────────────────────────────────────
   With fewer columns the board's panels are narrower and the rail shares a
   column, so two panels take the ledger row's form. Requirements: one row
   a slot, the slot's Label inside the row, the title on one line, the Work
   still needed and the reward; the progress is a 3px line along the bottom
   edge, and a MISS line appears only when there are penalties. World: its
   Label and the event's name on one line, the rest in its detail. */
@media (max-width: 980px) {
  .req-list {
    gap: 4px;
  }
  .req-slot {
    position: relative;
  }
  .req-slot > .slot-label {
    position: absolute;
    z-index: 1;
    top: 10px;
    left: 13px;
    margin: 0;
    line-height: 16px;
    pointer-events: none;
  }
  .req {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto auto;
    align-items: center;
    column-gap: 12px;
    min-height: 36px;
    padding: 8px 10px 8px 64px;
  }
  .req-name {
    overflow: hidden;
    line-height: 19px;
    white-space: nowrap;
    text-overflow: ellipsis;
  }
  .req > .req-stats:not(:has(.req-penalty)) {
    display: contents;
  }
  .req > .req-stats:has(.req-penalty) {
    grid-column: 1/-1;
    margin-top: 2px;
  }
  .req .progress {
    position: absolute;
    right: 0;
    bottom: 0;
    left: 0;
    height: 3px;
    margin: 0;
  }
  .req > .sub {
    color: var(--muted);
  }
  #world {
    display: grid;
    grid-template-columns: var(--ledger-label) minmax(0, 1fr);
    align-items: center;
    column-gap: 8px;
    min-height: 0;
    border-left: 3px solid transparent;
    padding-left: 7px;
  }
  #world .eyebrow {
    margin: 0;
  }
  #world .world-name {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }
  #world :is(.world-art, .world-text, .world-flavor),
  .world-event-word {
    display: none;
  }
}

/* ── One column, below 700px ────────────────────────────────────────────
   The canonical order in one column: the top bar in two rows, Requirements,
   Architecture at its content height, the rail's five panels as ledger
   rows, then the dock, fixed to the bottom edge: the hand strip over the
   actions. The page reserves the dock's measured height. A detail is the
   full-screen sheet, the same five-tab strip under its header, over the
   dock. */
@media (max-width: 699px) {
  :root {
    --layout: single;
    --hand-art-h: 0px;
  }
  .app {
    padding: 0 8px calc(var(--command-bar-h, 0px) + 12px);
  }
  .topbar {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 2px 8px;
    padding: 4px 0;
  }
  .topbar .brand-block,
  .top-actions .start-menu-trigger > span {
    display: none;
  }
  #statusBar {
    grid-column: 1;
    grid-row: 1;
  }
  .top-actions {
    grid-column: 2;
    grid-row: 1;
  }
  #globalChips {
    grid-column: 1/-1;
    grid-row: 2;
    justify-content: flex-start;
    flex-wrap: nowrap;
    overflow-x: auto;
    /* The row is the touch target, not each chip: a 44px chip made the Debt
       warning a tall bar. Content-box keeps the 44px inside the padding, so
       a delta has room under the number without scrolling the row. */
    box-sizing: content-box;
    min-height: 44px;
  }
  #connectionBanner {
    grid-column: 1/-1;
    grid-row: 3;
  }
  /* Row 1 shares its width with Menu: the status sheds its quarter and
     notes, keeping whose turn it is and the actions left. */
  .status :is(.dot, .pips, .note, .quarter) {
    display: none;
  }
  .board {
    grid-template-columns: minmax(0, 1fr);
    gap: 8px;
    min-height: 0;
    margin-top: 8px;
  }
  .requirements-panel,
  .architecture-panel {
    padding: 10px;
  }
  .arch {
    min-height: 0;
    padding: 4px 0;
  }
  /* The sheet has to rise above the dock and the top bar, which it cannot
     from inside an isolated rail. */
  .right-rail {
    isolation: auto;
  }
  .dock {
    position: fixed;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 55;
    display: flex;
    flex-direction: column;
    max-height: 40dvh;
    overflow-y: auto;
    padding-bottom: env(safe-area-inset-bottom);
    /* Opaque: at 98% the board's text showed through the dock. */
    --dock-bg: #0c1218;
    background: var(--dock-bg);
    border-top: 1px solid #1c2830;
  }
  .hand-shell {
    margin: 0;
    padding: 2px 8px 0;
    background: none;
  }
  /* One 24px band tops the dock: the hand's count in the strip's corner,
     Undo and the chevron at the right end. */
  .hand-shell .section-head {
    justify-content: flex-start;
    align-items: center;
    min-height: 24px;
    margin: 0;
  }
  .command-bar:not(.collapsed) :is(.command-bar-head, .undo-btn) {
    position: absolute;
    top: 3px;
  }
  .command-bar:not(.collapsed) .command-bar-head {
    right: 8px;
  }
  .command-bar:not(.collapsed) .undo-btn {
    right: 40px;
  }
  .hand {
    padding: 2px 2px 4px;
    scroll-snap-type: x mandatory;
  }
  .hand .card {
    scroll-snap-align: start;
  }
  .hand .card-art {
    display: none;
  }
  .hand .card {
    --card-art-h: 0px;
    --card-title-lines: 2;
  }
  .command-bar {
    position: static;
    width: auto;
    transform: none;
    padding: 4px 8px 8px;
    background: none;
    border: 0;
    box-shadow: none;
    backdrop-filter: none;
  }
  /* As many equal columns as fit a name and a three-part price. */
  .quick-actions {
    grid-template-columns: repeat(auto-fill, minmax(128px, 1fr));
  }
  /* Capped at 40% of the screen, the dock scrolls inside itself, and the
     turn row stays pinned to its bottom edge, so End turn is always there.
     The row is one opaque strip, edge to edge, that the other actions
     scroll under, so a pinned button is never drawn over another. Its
     margins cover the gap above it and the padding around it; its padding
     puts the buttons back in the grid's columns. */
  .command-bar:not(.collapsed) .turn-row {
    position: sticky;
    bottom: 0;
    grid-column: 1/-1;
    display: grid;
    grid-template-columns: inherit;
    gap: inherit;
    margin: -6px -8px calc(-8px - env(safe-area-inset-bottom));
    padding: 5px 8px calc(8px + env(safe-area-inset-bottom));
    background: var(--dock-bg);
    border-top: 1px solid #1c2830;
  }
  /* Collapsed, the whole dock is one line: what the hand holds, with End
     turn beside it. */
  .dock:has(.command-bar.collapsed) .hand-shell {
    display: none;
  }
  .command-bar.collapsed .command-bar-head {
    grid-column: 1;
    min-height: 44px;
  }
  .command-bar.collapsed .command-bar-body {
    display: contents;
  }
  .command-bar.collapsed :is(.undo-btn, .quick-actions > :not(.turn-row)),
  .command-bar.collapsed .turn-row > :not(.end-turn-btn) {
    display: none;
  }
  .command-bar.collapsed .quick-actions {
    grid-column: 2;
    grid-row: 1;
    display: block;
  }
  /* Touch targets. */
  .top-actions .start-menu-trigger,
  .right-rail > :is(.ledger-row, #world) {
    min-height: 44px;
  }
  .requirements-panel .req {
    min-height: 44px;
  }
  .req-slot > .slot-label {
    top: 14px;
  }
  /* The sheet. */
  #inspector.inspector {
    position: fixed;
    inset: 0;
    z-index: 60;
    width: 100%;
    height: 100dvh;
    max-height: none;
    padding: 0 15px calc(15px + env(safe-area-inset-bottom));
    overflow-y: auto;
    background: #131b24;
    box-shadow: none;
  }
  #inspector.inspector .inspector-head {
    position: sticky;
    top: 0;
    z-index: 2;
    height: calc(64px + env(safe-area-inset-top));
    /* The fixed tab strip below is 38px; the rest is air before the content. */
    margin: 0 -15px 50px;
    padding: calc(12px + env(safe-area-inset-top)) 15px 12px;
    background: #131b24;
    border-bottom: 1px solid var(--line);
  }
  body.inspector-open #railTabs {
    position: fixed;
    top: calc(64px + env(safe-area-inset-top));
    right: 0;
    left: 0;
    z-index: 61;
    min-height: 38px;
    background: #131b24;
    border-bottom: 1px solid var(--line);
  }
}

/* Ambient tutorial tip: never dims or blocks the board. Pinned to the bottom on
   phones; on wide screens JS anchors it beside its target (.anchored). */
.tutorial-hint {
  position: fixed;
  z-index: 150;
  right: 12px;
  bottom: 12px;
  width: min(360px, calc(100vw - 24px));
  background: var(--surface2);
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
  padding: 14px;
}
.tutorial-hint.hidden {
  display: none;
}
.tutorial-hint.anchored {
  right: auto;
  bottom: auto;
}
.tutorial-hint .tutorial-actions {
  flex-wrap: wrap;
  justify-content: flex-start;
}
.tutorial-hint-emphasis {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}
@media (min-width: 720px) {
  .tutorial-hint:not(.anchored) {
    top: 150px;
    bottom: auto;
  }
}
@media (max-width: 719px) {
  .tutorial-hint {
    left: 12px;
    width: auto;
  }
}
/* On a phone the dock owns the bottom edge; the tip sits just above it. */
@media (max-width: 699px) {
  .tutorial-hint:not(.anchored) {
    bottom: calc(var(--command-bar-h, 0px) + 8px);
  }
}

.invite-seat {
  display: flex;
  align-items: end;
  gap: 10px;
  margin: 14px 0;
}
.invite-seat label {
  flex: 1;
  min-width: 0;
}
.invite-seat button {
  flex: none;
}

/* Play screens use two sizes and one face; only the wordmark keeps its identity. */
#start {
  font-size: 14px;
  line-height: 1.5;
}
#start :is(button, a, label, p, input, select, summary, .sub) {
  font-family: inherit;
  font-size: inherit;
  font-weight: 400;
  letter-spacing: normal;
}
#start :is(h2, h3) {
  font-family: inherit;
  font-size: 18px;
  font-weight: 600;
  line-height: 1.5;
}
#start .start-brand-name {
  font-size: 18px;
}
#start .mode-picker > *,
#start .primary,
#start strong {
  font-weight: 600;
}
#start .mode-label > span {
  font-size: inherit;
  font-weight: 400;
}

/* Account deletion: clearly separated from the email controls. */
.auth-danger {
  margin-top: 16px;
  padding-top: 12px;
  border-top: 3px solid var(--ink, #000);
}
.auth-danger h3 {
  margin: 0 0 4px;
  text-transform: uppercase;
  color: #b00020;
}
.auth-danger .danger,
#authForm[data-mode='delete'] #authSubmit {
  background: #b00020;
  color: #fff;
  border: 3px solid #000;
}
#authForm[data-mode='delete'] #authSubmit {
  box-shadow: none;
}
/* Over Compute supply or the Support limit costs Systems at the next Support
   step, so the Architecture summary has to look different from a healthy line. */
.section-note-bad {
  color: var(--bad);
}
