/* DRS Econ Arcade — the one stylesheet for the whole arcade.
   Sections: tokens · base · buttons · layout · game kit · fed kit · hub kit · graph kit · engine kit · keyframes · reduced motion.
   Blocks marked "compose with .panel" expect markup like <div class="panel breaking">…</div>. */
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700;800;900&family=JetBrains+Mono:wght@400;700&display=swap');

/* ===== TOKENS — the Level Up palette, verbatim. Never invent a color outside this block. ===== */
:root {
  --bg: #131F24;
  --bg2: #1B2B33;
  --bg3: #233640;
  --card: #1E2D36;
  --card-hover: #253A45;
  --green: #58CC02;
  --green-dark: #46A302;
  --green-glow: rgba(88,204,2,0.3);
  --gold: #FFC800;
  --gold-dark: #E5B400;
  --gold-glow: rgba(255,200,0,0.3);
  --red: #FF4B4B;
  --red-dark: #E53E3E;
  --red-glow: rgba(255,75,75,0.25);
  --blue: #1CB0F6;
  --blue-dark: #0E9BD8;
  --purple: #CE82FF;
  --purple-dark: #B560F0;
  --orange: #FF9600;
  --text: #FFFFFF;
  --text2: #AFAFBF;
  --text3: #7A7A8E;
  --border: #37464F;
  --correct: #58CC02;
  --wrong: #FF4B4B;
  --font: 'Nunito', -apple-system, sans-serif;
  --font-mono: 'JetBrains Mono', monospace;
  --radius: 16px;
  --radius-sm: 12px;
}

/* ===== BASE ===== */
* { box-sizing: border-box; }
/* `hidden` means hidden, whatever display the element's class gave it: the hearts row is a flex
   box and the timer bar a block, and both leave the play screen in practice. */
[hidden] { display: none !important; }
/* No pull-to-refresh. A drag that starts on the headline card — above the graph, outside its
   touch-action:none surface — would otherwise reload the page and throw the run away mid-round. */
html { color-scheme: dark; overscroll-behavior-y: contain; }
/* One focus ring for the whole arcade: the tiles, dial and heat cells are all real controls now,
   and a Chromebook keyboard has to be able to see where it is. */
:focus-visible { outline: 3px solid var(--blue); outline-offset: 2px; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  min-height: 100svh;
  overscroll-behavior-y: contain;
  -webkit-tap-highlight-color: transparent;
}

/* ===== BUTTONS — chunky Duolingo press: the shadow is the button's height, :active spends it. ===== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 48px;
  padding: 14px 24px;
  font-family: var(--font);
  font-weight: 800;
  font-size: 18px;
  text-transform: uppercase;
  letter-spacing: .04em;
  text-decoration: none;
  border: 0;
  border-radius: var(--radius);
  background: var(--green);
  color: #fff;
  box-shadow: 0 4px 0 var(--green-dark);
  cursor: pointer;
  transition: transform .06s, box-shadow .06s;
}
.btn:active { transform: translateY(4px); box-shadow: none; }
.btn:disabled { opacity: .45; box-shadow: none; transform: none; }
.btn-gold { background: var(--gold); color: #3b2f00; box-shadow: 0 4px 0 var(--gold-dark); }
.btn-blue { background: var(--blue); box-shadow: 0 4px 0 var(--blue-dark); }
.btn-red { background: var(--red); box-shadow: 0 4px 0 var(--red-dark); }
.btn-ghost { background: transparent; color: var(--text); border: 2px solid var(--border); box-shadow: 0 4px 0 var(--border); }
/* A pressed toggle has to look pressed. 🗣 and 📺 are the same glyph in both states, so aria-pressed
   was the only signal a sighted student had — and it is not a signal you can see. */
.btn-ghost[aria-pressed="true"] { border-color: var(--green); color: var(--green); box-shadow: 0 4px 0 var(--green-dark); }
/* Smaller type and padding, never a smaller target: 48 px is the floor everywhere. */
.btn-sm { min-height: 48px; padding: 8px 14px; font-size: 15px; }
.btn-block { display: flex; width: 100%; }

/* ===== LAYOUT ===== */
.app {
  /* 30rem is the same 480 px column at the default root font, and a column that grows with the type
     under Arcade Night's 150 % root — a projector should not read a phone-width strip. */
  max-width: 30rem;
  margin: 0 auto;
  padding: 12px 12px calc(12px + env(safe-area-inset-bottom));
}
/* A screen clips sideways: the breaking card slides in from 60 px off the right edge, and for those
   300 ms the page could be flicked horizontally. Nothing a screen holds is meant to leave its column. */
.screen { display: none; overflow-x: clip; }
.screen.active { display: block; animation: slideUp .3s ease; }
.topbar { display: flex; gap: 10px; align-items: center; }
.topbar-back { width: 48px; height: 48px; flex: 0 0 48px; padding: 0; }
.topbar-stats { display: flex; gap: 12px; align-items: center; margin-left: auto; font-family: var(--font-mono); }
.panel {
  background: var(--card);
  border: 2px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
}
.panel-title { margin: 0; font-weight: 900; font-size: 20px; }
.sheet {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  max-width: 30rem;
  margin: 0 auto;
  z-index: 90;
  background: var(--bg2);
  border-top: 2px solid var(--border);
  border-radius: 16px 16px 0 0;
  padding: 16px 16px calc(16px + env(safe-area-inset-bottom));
  transform: translateY(100%);
  transition: transform .25s;
}
.sheet.open { transform: none; }
.sheet.correct { border-top-color: var(--green); }
.sheet.wrong { border-top-color: var(--red); }
.sheet-msg { font-weight: 900; font-size: 20px; }
.sheet-why { font-weight: 400; font-size: 16px; line-height: 1.45; color: var(--text2); }
.stack { display: flex; flex-direction: column; gap: 12px; }
.row { display: flex; gap: 8px; align-items: center; }
.row.wrap { flex-wrap: wrap; }
.mono { font-family: var(--font-mono); }
.muted { color: var(--text2); }
.h1 {
  margin: 0;
  font-weight: 900;
  font-size: clamp(1.75rem, 8vw, 2.5rem);
  line-height: 1.1;
  background: linear-gradient(135deg, var(--green), var(--blue));
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}
/* A failed level is not a celebration: flat red where the win screen is a gradient, and the panel
   takes the same red edge the wrong-answer sheet has. Both are tokens the arcade already owns. */
.tone-fail { border-color: var(--red); }
.tone-fail .h1 {
  background: none;
  color: var(--red);
  -webkit-text-fill-color: var(--red);
}
.tag {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--bg3);
  color: var(--text2);
  font-weight: 800;
  font-size: 12px;
  text-transform: uppercase;
}

/* ===== GAME KIT — .breaking, .level-tile and .toast compose with .panel ===== */
/* The play column is the one screen that has to fit a 740 px phone whole — headline, graph, gauges
   and hint all above the fold — so it runs tighter than the rest of the kit. The graph never gives. */
.stack.tight { gap: 8px; }
.breaking { animation: slideInRight .3s; padding: 12px; }
.breaking .tag { background: var(--red); color: var(--text); }
.breaking-emoji { font-size: 44px; line-height: 1; }
.breaking-head { font-weight: 900; font-size: 22px; }
.breaking-sub { color: var(--text2); font-size: 15px; }
.timer-bar { height: 10px; border-radius: 999px; background: var(--bg3); overflow: hidden; }
.timer-fill { width: 100%; height: 100%; background: var(--green); transform-origin: left; }
.timer-bar.warn .timer-fill { background: var(--red); }
.hearts { display: flex; gap: 4px; font-size: 22px; }
.heart.lost { opacity: .25; filter: grayscale(1); }
.heart.crack { animation: crack .45s; }
.combo { font-family: var(--font-mono); font-weight: 800; }
.combo.s1 { color: var(--gold); }
.combo.s2 { color: var(--orange); }
.combo.s3 { color: var(--red); }
.combo.s4 { color: var(--purple); animation: pulse 1s infinite; }
.score { font-family: var(--font-mono); font-weight: 900; font-size: 24px; }
.medal {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  border: 3px solid var(--border);
  font-weight: 900;
}
.medal.bronze { border-color: #cd7f32; color: #cd7f32; }
.medal.silver { border-color: #c0c0c0; color: #c0c0c0; }
.medal.gold { border-color: var(--gold); color: var(--gold); }
.medal.none { border-style: dashed; border-color: var(--border); color: var(--text3); }
.ap-stamp {
  display: inline-block;
  padding: 4px 14px;
  border: 4px solid var(--red);
  border-radius: 8px;
  color: var(--red);
  font-weight: 900;
  font-size: 44px;
  letter-spacing: .08em;
  transform: rotate(-8deg);
}
.ap-stamp.slam { animation: stampSlam .4s ease-out; }
.ribbon {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  border-radius: 999px;
  background: var(--gold);
  color: #3b2f00;
  font-weight: 800;
  font-size: 12px;
  text-transform: uppercase;
}
.ribbon::before { content: '\2713'; }
/* The earned title. It carries a whole sentence on the hub, so it wraps onto its own line beside
   the initials chip on a phone, and it is sized in rem to grow with Arcade Night's 150 % root.
   `width: fit-content` keeps it a pill inside an end screen's column stack, where a flex item
   would otherwise stretch the full panel. `.up` is the rung the run just earned. */
.title-chip {
  width: fit-content;
  max-width: 100%;
  padding: .3rem .75rem;
  border: 2px solid var(--border);
  border-radius: 999px;
  background: var(--bg3);
  color: var(--text2);
  font-weight: 800;
  font-size: .9rem;
  line-height: 1.35;
}
.title-chip.up { border-color: var(--gold); color: var(--gold); }
/* A level tile is two real controls side by side: `.tile-open` plays the level, `.tile-btn` replays
   the Exam Sprint. Both clear 48 px. The tile runs tight — one text column, badges wrapping onto the
   line under the name — so all seven levels fit a 740 px phone without scrolling. */
.level-tile { display: flex; gap: 8px; align-items: center; padding: 8px 12px; }
.level-tile.locked { opacity: .45; }
.tile-open {
  display: flex;
  flex: 1;
  gap: 8px;
  align-items: center;
  min-width: 0;
  min-height: 48px;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.tile-open:disabled { cursor: default; }
.tile-info { flex: 1; min-width: 0; }
.tile-name { display: block; font-weight: 900; font-size: 18px; line-height: 1.25; }
.tile-meta { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.tile-meta .muted { font-family: var(--font-mono); font-size: 13px; }
/* The sprint is the tile's second action, so it reads as a chip beside the medal, not a third row:
   two short lines of the same chunky button, never under 48 px. */
.tile-btn {
  flex: 0 0 auto;
  width: 68px;
  min-height: 48px;
  padding: 4px 6px;
  font-size: 13px;
  line-height: 1.15;
  white-space: normal;
}
.level-tile .medal { width: 40px; height: 40px; border-width: 2px; font-size: 15px; }
.level-num { font-family: var(--font-mono); font-weight: 900; font-size: 24px; }
/* Exam Sprint — the arcade's only multiple choice. A choice composes with .btn.btn-ghost.btn-block,
   so it needs the sentence case and left edge a chunky uppercase button does not have. */
.exam-stem { font-weight: 700; font-size: 17px; line-height: 1.45; }
.choice { justify-content: flex-start; text-align: left; text-transform: none; letter-spacing: normal; font-size: 16px; font-weight: 700; }
.choice.marked { pointer-events: none; opacity: .5; }
.choice.right { border-color: var(--green); color: var(--green); box-shadow: 0 4px 0 var(--green-dark); opacity: 1; }
.choice.wrong { border-color: var(--red); color: var(--red); box-shadow: 0 4px 0 var(--red-dark); opacity: 1; }
.toast {
  position: fixed;
  top: 16px;
  left: 0;
  right: 0;
  z-index: 120;
  width: max-content;
  max-width: calc(100% - 24px);
  margin: 0 auto;
  font-weight: 700;
  animation: slideUp .25s;
}
.initials { display: flex; gap: 8px; justify-content: center; }
.initials .slot {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 64px;
  border: 2px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg2);
  font-family: var(--font-mono);
  font-weight: 900;
  font-size: 32px;
}
.initials .slot.active { border-color: var(--green); }
/* Six 48 px keys fit a 360 px sheet exactly, and nothing narrower — a game's title panel, or a
   Chromebook's classic scrollbar — leaves 43 px keys. auto-fit drops to five wider columns instead
   of shaving the tap target. */
.az-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(48px, 1fr)); gap: 8px; }
.az-grid .btn { min-height: 48px; padding: 0; font-size: 18px; }
.gauge-strip { display: block; width: 100%; }

/* ===== FED KIT — .advisor and .pipe-chip compose with .panel ===== */
.ticker {
  overflow: hidden;
  border-top: 2px solid var(--border);
  border-bottom: 2px solid var(--border);
  font-family: var(--font-mono);
  font-size: 15px;
}
.ticker-inner { display: inline-block; white-space: nowrap; padding: 6px 0; animation: marquee 18s linear infinite; }
/* Compact on purpose: two of these sit between the ticker and the dial on a board that has to fit a
   740 px phone with the meters row above them. The name runs into the quote. */
.advisor { display: flex; gap: 8px; align-items: flex-start; padding: 8px 12px; }
.advisor-avatar { font-size: 24px; line-height: 1.1; }
.advisor-text { font-size: 13px; line-height: 1.35; }
.advisor.hawk { border-left: 4px solid var(--red); }
.advisor.dove { border-left: 4px solid var(--blue); }
.dial { position: relative; height: 56px; }
.dial-track {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 8px;
  margin-top: -4px;
  border-radius: 999px;
  background: var(--bg3);
}
.dial-window {
  position: absolute;
  top: 50%;
  height: 8px;
  margin-top: -4px;
  border-radius: 999px;
  background: var(--green-glow);
}
.dial-thumb {
  position: absolute;
  top: 50%;
  width: 48px;
  height: 48px;
  margin-top: -24px;
  margin-left: -24px;
  border-radius: 50%;
  background: var(--gold);
}
.pipeline { display: flex; gap: 6px; }
.pipe-chip { padding: 6px 10px; border-radius: var(--radius-sm); font-weight: 700; font-size: 13px; }
.pipe-chip.now { border-color: var(--green); }
.newspaper {
  padding: 20px;
  border-radius: 6px;
  background: #F4ECD8;
  color: #1B1B1B;
  font-family: 'Playfair Display', Georgia, serif;
}
/* A masthead that wraps is not a masthead. The column is the viewport less the app's 24 px and the
   paper's 40 px, and the widest fallback serif needs ~14.6 units of width per unit of size, so this
   is the largest size the name still fits on one line — capped at the 28 px the design wants. */
.np-masthead {
  border-top: 3px double #1B1B1B;
  border-bottom: 3px double #1B1B1B;
  padding: 6px 0;
  text-align: center;
  text-transform: uppercase;
  white-space: nowrap;
  font-weight: 900;
  font-size: min(28px, calc((100vw - 68px) / 14.6));
}
.np-head { font-weight: 900; font-size: 26px; }
.np-sub { font-style: italic; font-size: 16px; }
.np-body { font-size: 15px; }
@media (min-width: 420px) { .np-body { columns: 2; } }
.coin {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 96px;
  height: 96px;
  border-radius: 50%;
  background: var(--gold);
  transform-style: preserve-3d;
  animation: spin 3s linear infinite;
}

/* Fed Chair's own parts, added for markup the kit above has no class for. Same tokens, no
   new colours: the dial's drag surface, the credibility meter, the advisors' two text lines,
   the FOMC gavel stamp, the newspaper's dateline and sidebar, and the history chart. */
.dial { touch-action: none; }
/* The thumb is 48 px wide and centred on its value, so the axis it rides is inset by its
   radius — otherwise the ends of the dial would hang past the app column at 360 px. */
.dial-track { left: 24px; right: 24px; }
/* the quarter and the Chair's name, sized to sit on one line beside a 48 px back button */
.turn-when { font-family: var(--font-mono); font-size: 13px; line-height: 1.2; }
.pips { display: flex; gap: 4px; }
.pip { width: 10px; height: 10px; border-radius: 50%; background: var(--bg3); border: 2px solid var(--border); }
.pip.on { background: var(--gold); border-color: var(--gold-dark); }
/* The three meters under the gauges — the street, Washington, credibility — five pips each. Red is
   the street's anger, blue is Washington's pressure, and credibility keeps the gold it always had. */
.meters { display: flex; gap: 8px; }
.meter { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 2px; min-width: 0; }
.meter-row { display: flex; align-items: center; gap: 4px; }
.meter-emoji { font-size: 15px; line-height: 1; margin-right: 2px; }
.meter-label { font-size: 10px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; color: var(--text2); white-space: nowrap; }
.meter.street .pip.on { background: var(--red); border-color: var(--red-dark); }
.meter.washington .pip.on { background: var(--blue); border-color: var(--blue-dark); }
.advisor-name { font-weight: 900; font-size: 12px; text-transform: uppercase; letter-spacing: .04em; margin-right: 2px; }
.advisor-line { color: var(--text2); }
.coin-face { color: #3b2f00; font-weight: 900; font-size: 44px; }
/* The gavel lands over the board and leaves again; the mount holds the position so the slam
   keyframe owns the transform. */
.fomc-mount { position: fixed; left: 0; right: 0; top: 34%; z-index: 95; display: flex; justify-content: center; pointer-events: none; }
.fomc-mount[hidden] { display: none; }
.fomc-stamp {
  padding: 6px 16px;
  border: 4px solid var(--gold);
  border-radius: 8px;
  background: var(--bg);
  color: var(--gold);
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 22px;
  letter-spacing: .06em;
  transform: rotate(-6deg);
}
.fomc-stamp.slam { animation: stampSlam .4s ease-out; }
.np-dateline { text-align: center; font-size: 12px; letter-spacing: .1em; text-transform: uppercase; }
.np-sidebar { border: 3px solid currentColor; padding: 8px; text-align: center; font-weight: 900; font-size: 15px; }
.hist-chart { display: block; width: 100%; height: auto; }
.hist-grid { stroke: var(--border); stroke-width: 1; }
.hist-axis { stroke: var(--text3); stroke-width: 2; }
.hist-label { font: 700 11px var(--font); fill: var(--text2); }
.hist-line { fill: none; stroke-width: 3; stroke-linejoin: round; stroke-linecap: round; }
.hist-line.player { stroke: var(--green); }
.hist-line.actual { stroke: var(--gold); stroke-dasharray: 7 6; }
/* The legend names the series and its unit before the two keys, so it wraps at 360 px. The caption
   keeps the tag's pill but not its uppercase: the CED writes the Phillips axis "π%", not "Π%". */
.hist-legend { flex-wrap: wrap; }
.hist-series { text-transform: none; }
.hist-key { display: inline-block; width: 22px; border-top: 3px solid currentColor; }
.hist-key.player { color: var(--green); }
.hist-key.actual { color: var(--gold); border-top-style: dashed; }

/* ===== HUB KIT — .game-card and .heat-cell compose with .panel ===== */
/* The hub is one column with a slower rhythm than a game screen: 24 px between the blocks, 14 px
   between cards, a labelled section for what can be played and one for what cannot yet, and the
   player's chrome kept clear of the first card. */
.hub { display: flex; flex-direction: column; gap: 24px; }
.hub-head { gap: 6px; }
.hub-head p { margin: 0; }
.hub-tagline { font-weight: 700; }
.hub-intro { font-size: .95rem; line-height: 1.45; }
.hub-controls { gap: 10px; }
.section-title { margin: 0; font-size: .8rem; font-weight: 900; letter-spacing: .1em; text-transform: uppercase; color: var(--text2); }
.games { gap: 14px; }
.game-card { position: relative; padding-right: 34px; transition: transform .06s; }
.game-card:active { transform: translateY(2px); }
/* A chevron says "this opens": the card is a link, and a link should look like one. */
.game-go { position: absolute; right: 14px; top: 50%; transform: translateY(-50%); font-size: 1.8rem; font-weight: 900; line-height: 1; color: var(--text3); }
.marquee { overflow: hidden; }
.marquee-inner { display: inline-block; white-space: nowrap; animation: marquee 22s linear infinite; }
.game-card { display: flex; gap: 12px; align-items: center; color: inherit; text-decoration: none; }
.game-info { flex: 1; min-width: 0; }
/* Every size on a game card is in rem: Arcade Night raises the root font to 150 %, and the card is
   the thing a class reads from the back of the room. */
.game-emoji { font-size: 2.5rem; line-height: 1; }
.game-title { font-weight: 900; font-size: 1.25rem; }
.game-blurb { color: var(--text2); font-size: .875rem; }
.game-best { font-family: var(--font-mono); font-size: 1rem; }
.game-card .tag { font-size: .75rem; }
.game-card .medal { width: 3.5rem; height: 3.5rem; font-size: 1rem; }
/* Seven levels' medals on one card: small pips, the level number where there is no medal yet, tight
   enough that all seven sit on one row beside a 360 px phone's emoji and chevron. */
.game-card .medal.mini { width: 1.5rem; height: 1.5rem; font-size: .7rem; border-width: 2px; }
.medal-row { gap: 4px; }
.game-card.soon { opacity: .5; }
.heat { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.heat-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 64px;
  padding: 6px;
  text-align: center;
  font-weight: 700;
}
.heat-cell.good { background: color-mix(in srgb, var(--green) 25%, transparent); border-color: var(--green); }
.heat-cell.warn { background: color-mix(in srgb, var(--gold) 25%, transparent); border-color: var(--gold); }
.heat-cell.bad { background: color-mix(in srgb, var(--red) 25%, transparent); border-color: var(--red); }
.heat-cell.none { border-style: dashed; }
/* The readiness panel's own parts. Its heat cells are <button>s — real 48 px targets that
   toggle a unit's topics — so they need the page's font and colour, not the UA's. Every size
   the panel adds is in rem, so a bigger root font (a student's browser setting, or Arcade
   Night's) actually enlarges it. */
.heat-cell { font-family: var(--font); color: inherit; cursor: pointer; }
.heat-unit { font-size: .8rem; color: var(--text2); }
.heat-acc { font-size: 1.1rem; font-weight: 900; }
.heat-count { font-size: .65rem; color: var(--text3); }
.heat-detail { grid-column: 1 / -1; display: flex; flex-direction: column; gap: 6px; text-align: left; }
.heat-detail-title { font-weight: 900; font-size: .95rem; }
.heat-detail .stat-row { font-size: .85rem; }
.heat-detail .mono { white-space: nowrap; }
.weakest { font-weight: 700; font-size: .95rem; }
.band { font-weight: 900; font-size: 1.25rem; }
.band-note { font-size: .85rem; }
/* "Play this next" carries the recommendation under its own label, so the button stacks. */
.play-next { flex-direction: column; gap: 2px; }
.play-next-label { font-size: .8rem; font-weight: 700; text-transform: none; letter-spacing: normal; }
.code-out {
  width: 100%;
  min-height: 4.5rem;
  padding: 8px;
  border: 2px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg2);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: .85rem;
}
/* A game card's stats wrap, so Shift's three medals drop below the best line at 360 px; the
   readiness panel's rows wrap for the same reason. */
.game-card .row, .readiness .row { flex-wrap: wrap; }

/* CLASS BOARD — the shared leaderboard, or this device's bests until a class is connected. Every
   size is in rem so Arcade Night's 150 % root is a 150 % board, and on the projector it leads the
   column. The tabs are one scrolling row: eight of them do not fit a phone and must not stack. */
.board-head { justify-content: space-between; }
.board-head .panel-title { flex: 1 1 auto; min-width: 0; }
.board-refresh { min-width: 48px; padding: 8px 12px; font-size: 20px; }
.board-note { font-size: .85rem; }
.board-tabs { flex-wrap: nowrap; gap: 6px; overflow-x: auto; padding-bottom: 4px; scrollbar-width: thin; -webkit-overflow-scrolling: touch; }
.board-tab { flex: 0 0 auto; padding: 8px 12px; font-size: .8rem; }
.board-tab[aria-selected="true"] { border-color: var(--blue); color: var(--blue); box-shadow: 0 4px 0 var(--blue-dark); }
.board-rows { gap: 6px; }
.board-row { display: flex; gap: .6rem; align-items: center; padding: .4rem .6rem; border: 2px solid var(--border); border-radius: var(--radius-sm); background: var(--bg2); }
/* The student's own row is gold, the way an earned thing is gold everywhere else in the arcade. */
.board-row.you { border-color: var(--gold); background: color-mix(in srgb, var(--gold) 12%, var(--bg2)); }
.board-rank { flex: 0 0 2.2rem; font-size: 1.1rem; font-weight: 900; text-align: center; color: var(--text2); }
.board-row.you .board-rank { color: var(--gold); }
.board-who { flex: 1 1 auto; min-width: 0; }
.board-initials { font-size: 1.25rem; font-weight: 900; line-height: 1.1; }
.board-sub { font-size: .75rem; color: var(--text2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.board-value { margin-left: auto; font-size: 1.1rem; font-weight: 900; white-space: nowrap; }
html.big-type { font-size: 150%; }
html.big-type .readiness { display: none; }
/* On the projector the board is the point of the night, so it leads the column. */
html.big-type .board { order: -1; }

/* ===== GRAPH KIT — shared/graph.js renders into these classes ===== */
.graph-wrap { width: 100%; }
.graph {
  width: 100%;
  aspect-ratio: 1;
  display: block;
  touch-action: none;
  user-select: none;
  -webkit-touch-callout: none;
}
.plot-bg { fill: var(--bg2); }
.axis { stroke: var(--text2); stroke-width: 2; }
.axis-label { fill: var(--text); font: 800 13px var(--font); }
/* The reference tick: a gold mark on the axis with its name beside it, the same pairing the gauges'
   reference ticks use, and smaller than the axis label it now sits clear of. */
.axis-tickmark { stroke: var(--gold); stroke-width: 2; }
.axis-tick { fill: var(--gold); font: 800 12px var(--font); }
.curve .line { fill: none; stroke-width: 4; stroke-linecap: round; }
.curve .halo { fill: none; stroke-width: 14; opacity: .14; }
.curve[data-curve=AD] .line, .curve[data-curve=AD] .halo,
.curve[data-curve=D] .line, .curve[data-curve=D] .halo,
.curve[data-curve=MD] .line, .curve[data-curve=MD] .halo,
.curve[data-curve=SRPC] .line, .curve[data-curve=SRPC] .halo { stroke: var(--blue); }
.curve[data-curve=SRAS] .line, .curve[data-curve=SRAS] .halo,
.curve[data-curve=S] .line, .curve[data-curve=S] .halo,
.curve[data-curve=MS] .line, .curve[data-curve=MS] .halo { stroke: var(--gold); }
.curve[data-curve=LRAS] .line, .curve[data-curve=LRAS] .halo,
.curve[data-curve=LRPC] .line, .curve[data-curve=LRPC] .halo { stroke: var(--purple); }
.curve .ghost { fill: none; stroke-width: 3; stroke-dasharray: 6 6; opacity: .35; }
.curve.grabbed .line { stroke-width: 6; }
.curve.accepted .line { stroke: var(--green); }
.curve .tag { font: 900 14px var(--font); fill: currentColor; color: inherit; }
/* The ghost trail and the curve's own name take the colour of the line they belong to: the
   `.ghost` above sets no stroke, and the label carries class="tag", which the pill rule near the
   top of this file paints `--text2` ON the text element itself — so `currentColor` resolved grey
   until `.curve .tag` took its colour back by inheriting the <g>'s. Same three tokens as `.line`. */
.curve[data-curve=AD], .curve[data-curve=D], .curve[data-curve=MD], .curve[data-curve=SRPC] { color: var(--blue); }
.curve[data-curve=SRAS], .curve[data-curve=S], .curve[data-curve=MS] { color: var(--gold); }
.curve[data-curve=LRAS], .curve[data-curve=LRPC] { color: var(--purple); }
.curve[data-curve=AD] .ghost, .curve[data-curve=D] .ghost,
.curve[data-curve=MD] .ghost, .curve[data-curve=SRPC] .ghost { stroke: var(--blue); }
.curve[data-curve=SRAS] .ghost, .curve[data-curve=S] .ghost, .curve[data-curve=MS] .ghost { stroke: var(--gold); }
.curve[data-curve=LRAS] .ghost, .curve[data-curve=LRPC] .ghost { stroke: var(--purple); }
.eq { fill: var(--text); stroke: var(--bg); stroke-width: 3; }
.eq-ghost { fill: var(--text2); opacity: .5; }
/* The dot's "you can grab me" ring. Present only while the dot is actually draggable — the engine
   puts .grabbable on the group — and never in the way of a finger. Its radius is written by the
   engine from the grab radius in force, so the ring IS the zone; only its opacity breathes, because
   animating the radius would draw a circle that lies about where the target ends. */
.eq-halo { fill: none; stroke: var(--text2); stroke-width: 1.5; opacity: 0; pointer-events: none; }
.dot.grabbable .eq-halo { animation: eqPulse 2.4s ease-in-out infinite; }
/* The tutorial's "this one": the curve's own halo breathes until the curve is grabbed. */
.curve.hinted .halo { opacity: .5; animation: haloBreathe 1.4s ease-in-out infinite; }
.guides line { stroke: var(--text3); stroke-dasharray: 4 4; }
.gap-shade { fill: #fff; opacity: 0; }
.gap-shade.on { opacity: .08; }
.gap-shade.good { fill: var(--green); opacity: .25; }
.gap-shade.bad { fill: var(--red); opacity: .25; }
.zone { fill: transparent; }
.hit-plane { fill: transparent; }
.gauge-arc { fill: none; stroke: var(--bg3); stroke-width: 8; }
.gauge-fill { fill: none; stroke: var(--blue); stroke-width: 8; stroke-linecap: round; }
.gauge-needle { stroke: var(--text); stroke-width: 3; }
.gauge-val { font: 700 15px var(--font-mono); fill: var(--text); }
.gauge-label { font: 800 11px var(--font); fill: var(--text2); }
.gauge-ref { stroke: var(--gold); stroke-width: 2; }
/* The reference's name goes on the cell's last line, small and gold like the tick it explains, so it
   stops crowding the value's digits above it. */
.gauge-reflabel { font: 800 10px var(--font); fill: var(--gold); text-anchor: middle; }
/* The needle has run out of dial but the number under it has not: the figure is the true one and
   goes gold to say the arc is no longer measuring it. */
.gauge-val.pinned { fill: var(--gold); }
.gauge.flash-red .gauge-fill { stroke: var(--red); }

/* ===== ENGINE KIT — the few classes shared/arcade.js generates that the kit above lacks.
   No new colors: every value below is an existing token. ===== */
.confetti { position: fixed; inset: 0; z-index: 200; pointer-events: none; }
.flash-overlay { position: fixed; inset: 0; z-index: 200; pointer-events: none; background: var(--gold-glow); }
.flash-red { background: var(--red-glow); }
.combo.crack { animation: crack .45s; }
.end-head { display: flex; gap: 12px; align-items: center; }
.end-head .ap-stamp { margin-left: auto; }
/* The score and the accuracy under it are two different numbers, and on a zero-score fail they read
   as one ("0" over "0% correct" scans as "00%"). The gap and the smaller line separate them; the
   word "Accuracy" in front of the second one does the rest. */
.end-score { display: block; font-size: 40px; line-height: 1; }
.end-score + .muted { margin-top: 6px; font-size: 14px; }
.stat-row { display: flex; gap: 12px; justify-content: space-between; }

/* ===== KEYFRAMES ===== */
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  20%, 60% { transform: translateX(-6px); }
  40%, 80% { transform: translateX(6px); }
}
@keyframes slideUp {
  from { opacity: 0; transform: translateY(24px); }
  to { opacity: 1; transform: translateY(0); }
}
@keyframes slideInRight {
  from { opacity: 0; transform: translateX(60px); }
  to { opacity: 1; transform: translateX(0); }
}
@keyframes correctPop {
  0% { transform: scale(1); }
  50% { transform: scale(1.03); }
  100% { transform: scale(1); }
}
@keyframes pulse {
  0%, 100% { box-shadow: 0 0 12px var(--green-glow); }
  50% { box-shadow: 0 0 26px var(--green-glow), 0 0 48px rgba(88,204,2,0.15); }
}
@keyframes eqPulse {
  0%, 100% { opacity: .16; }
  50% { opacity: .40; }
}
@keyframes haloBreathe {
  0%, 100% { opacity: .2; }
  50% { opacity: .6; }
}
@keyframes crack {
  0% { transform: rotate(0) scale(1.3); }
  30% { transform: rotate(-12deg) scale(1.3); }
  60% { transform: rotate(12deg) scale(1.15); }
  100% { transform: rotate(0) scale(1); }
}
@keyframes stampSlam {
  from { opacity: 0; transform: scale(3) rotate(-20deg); }
  to { opacity: 1; transform: scale(1) rotate(-8deg); }
}
@keyframes marquee {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}
@keyframes spin {
  from { transform: rotateY(0); }
  to { transform: rotateY(360deg); }
}

/* ===== REDUCED MOTION — every animation above becomes an instant state change ===== */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001s !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001s !important;
  }
  /* The ring stays — it is the affordance, not the decoration — it just stops breathing. */
  .dot.grabbable .eq-halo { animation: none; opacity: .3; }
  .curve.hinted .halo { animation: none; opacity: .45; }
}

/* ===== KEYBOARD LAYER — the way onto the graph that is not a finger =====
   The control row is out of the way until a Tab reaches it, and then it is an ordinary visible row
   of 48 px buttons: a phone never sees it, a Chromebook keyboard cannot miss it. */
.sr-only, .kbd-answers {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.kbd-answers:focus-within {
  position: static;
  width: auto;
  height: auto;
  margin: 0;
  overflow: visible;
  clip-path: none;
  white-space: normal;
}
.kbd-answers .btn { flex: 1 1 auto; padding: 8px 10px; font-size: 14px; }

/* ===== HOW TO PLAY — one screen of illustrated cards, scrollable on a 360x740 phone ===== */
.howto-card { display: flex; gap: 12px; align-items: flex-start; padding: 12px; }
.howto-emoji { flex: 0 0 auto; font-size: 30px; line-height: 1.1; }
.howto-head { font-weight: 900; font-size: 17px; }
.howto-body { color: var(--text2); font-size: 15px; line-height: 1.4; }
.howto-demo { max-width: 200px; margin: 0 auto; }
.howto-caption { text-align: center; }
.topbar .howto-btn { width: 48px; height: 48px; flex: 0 0 48px; padding: 0; }
/* A topbar title takes the slack, so the ? beside it lands on the right edge and not on its heels. */
.topbar .panel-title { flex: 1 1 auto; min-width: 0; }
/* The 🎮 beside it opens the game switcher: any other game is two taps from any menu screen. */
.topbar .switch-btn { font-size: 20px; }
/* Fed Chair's turn label sits between ← and two icon buttons now; it wraps to two short lines. */
.topbar .turn-when { flex: 1 1 auto; min-width: 0; }

/* ===== GAME SWITCHER — the sheet behind every 🎮: the hub and the seven games, two to a row ===== */
.switch-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.switch-item { justify-content: flex-start; gap: 10px; padding: 8px 12px; font-size: 15px; text-transform: none; letter-spacing: normal; text-decoration: none; text-align: left; }
.switch-emoji { font-size: 22px; line-height: 1; flex: 0 0 auto; }
.switch-name { line-height: 1.2; }
/* Where you already are: green, and it only closes the sheet. */
.switch-item.current { border-color: var(--green); color: var(--green); box-shadow: 0 4px 0 var(--green-dark); }

/* ===== GLOSSARY — the sheet behind every 📖: a search box, the terms by unit, one definition a tap ===== */
.sheet.glossary { max-height: 84vh; overflow-y: auto; }
.glossary-head { position: sticky; top: -16px; z-index: 1; margin: -16px -16px 0; padding: 16px 16px 8px; background: var(--bg2); gap: 8px; }
.glossary-head .row { justify-content: space-between; }
.glossary-head .sheet-msg { flex: 1 1 auto; min-width: 0; }
.glossary-close { width: 48px; height: 48px; flex: 0 0 48px; padding: 0; }
.glossary-find { width: 100%; min-height: 48px; padding: 10px 14px; border: 2px solid var(--border); border-radius: var(--radius-sm); background: var(--bg); color: var(--text); font: 600 16px var(--font); }
.glossary-find:focus { outline: none; border-color: var(--blue); }
.glossary-list { display: flex; flex-direction: column; gap: 4px; }
.glossary-unit { margin-top: 8px; font-size: 12px; font-weight: 900; letter-spacing: .08em; text-transform: uppercase; color: var(--text2); }
.glossary-term { display: flex; width: 100%; min-height: 48px; align-items: center; justify-content: space-between; gap: 8px; padding: 8px 12px; border: 2px solid var(--border); border-radius: var(--radius-sm); background: var(--card); color: var(--text); font: 800 15px var(--font); text-align: left; cursor: pointer; }
.glossary-term::after { content: '+'; flex: 0 0 auto; color: var(--text3); font-weight: 900; }
.glossary-item.open .glossary-term { border-color: var(--blue); }
.glossary-item.open .glossary-term::after { content: '−'; }
.glossary-def { display: none; padding: 6px 12px 10px; font-size: 15px; line-height: 1.45; color: var(--text2); }
.glossary-item.open .glossary-def { display: block; }
/* The 📖 button carries a sentence: sentence case, like the practice switch. */
.glossary-btn { text-transform: none; letter-spacing: normal; font-size: 15px; }

/* ===== WHAT IS THIS? — the purpose panel on a title screen, one emoji and one sentence a line ===== */
.intro-title { font-weight: 900; font-size: 13px; letter-spacing: .06em; text-transform: uppercase; color: var(--text2); }
.intro-line { display: flex; gap: 10px; align-items: flex-start; }
.intro-emoji { flex: 0 0 auto; font-size: 22px; line-height: 1.2; }
.intro-text { font-size: 15px; line-height: 1.4; }
/* Two pixels a line: Shift's and the Investor's title screens ran 3–4 px past a 740 px phone with the
   stack's 12 px gap, and this is the difference between the ← Arcade button fitting and not. */
.intro { gap: 10px; }
/* The practice switch carries a sentence, so it keeps the button's height and drops its shouting. */
.practice-btn { text-transform: none; letter-spacing: normal; font-size: 15px; }
/* Where the hearts were: a blue tag, because it is a state, not a warning. */
.mode-tag { background: var(--blue); color: var(--text); }
/* The consequence read-out on the verdict sheet: three tags, coloured by direction. */
.sheet-chips .tag { font-size: 13px; text-transform: none; letter-spacing: normal; }
.chip-up { color: var(--green); }
.chip-down { color: var(--red); }
.chip-flat { color: var(--text2); }
/* The missed headlines on an end screen, above the buttons that offer them back. */
.missed-list { font-size: 14px; line-height: 1.45; }
.missed-head { font-weight: 800; }

/* ===== SORT CIRCUIT — two bins and the card between them =====
   The whole game is the card's x position: it follows the finger, the bin it leans toward
   lights up, and a release past the commit line drops it in. */
.sort-head { justify-content: space-between; align-items: center; flex-wrap: nowrap; }
.sort-head .tag { white-space: nowrap; flex: 0 0 auto; }
.sort-head .mono { white-space: nowrap; flex: 0 0 auto; }
.sort-head .muted:not(.mono) { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tag.lightning { background: var(--gold); color: #1a1a1a; }
.sort-table {
  position: relative; height: 330px;
  display: grid; grid-template-columns: 1fr 1fr; gap: 10px;
  touch-action: none; user-select: none; -webkit-user-select: none;
}
.bin {
  border: 2px dashed var(--border); border-radius: var(--radius);
  display: flex; align-items: flex-end; justify-content: center; padding: 12px;
  transition: border-color .15s, background .15s;
}
.bin-label { font-weight: 900; font-size: 15px; color: var(--text2); text-align: center; }
.bin.near { border-color: var(--blue); background: rgba(28,176,246,.08); }
.bin.near .bin-label { color: var(--text); }
.bin.good { border-color: var(--green); background: var(--green-glow); }
.bin.good .bin-label { color: var(--green); }
.bin.bad { border-color: var(--red); background: var(--red-glow); }
.bin.bad .bin-label { color: var(--red); }
.sort-card {
  position: absolute; left: 50%; top: 18px; width: 76%; margin-left: -38%; min-height: 170px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
  text-align: center; cursor: grab; touch-action: none;
  box-shadow: 0 8px 24px rgba(0,0,0,.35); will-change: transform;
}
.sort-card.grabbed { cursor: grabbing; box-shadow: 0 14px 32px rgba(0,0,0,.45); }
.sort-card.deal { animation: dealIn .3s ease-out; }
@keyframes dealIn { from { opacity: 0; transform: translateY(-40px) scale(.9); } to { opacity: 1; transform: translateY(0) scale(1); } }
.sort-emoji { font-size: 40px; line-height: 1; }
.sort-text { font-weight: 800; font-size: 17px; line-height: 1.3; }
@media (prefers-reduced-motion: reduce) { .sort-card.deal { animation: none; } }

/* ===== CALC BLITZ — a problem card and a dial =====
   The dial is a snapped number line: the knob follows the finger, the readout above shows
   the value in big type, and a release after real travel hands the number over. */
.calc-problem { font-weight: 800; font-size: 17px; line-height: 1.35; min-height: 96px; display: flex; align-items: center; }
.calc-problem.deal { animation: dealIn .3s ease-out; }
.calc-dial { display: flex; flex-direction: column; gap: 8px; padding: 12px 0 4px; touch-action: none; user-select: none; -webkit-user-select: none; }
.calc-readout { display: flex; align-items: baseline; justify-content: center; gap: 6px; min-height: 56px; }
.calc-value { font-family: var(--font-mono); font-weight: 800; font-size: 44px; line-height: 1; color: var(--text); }
.calc-unit { font-weight: 900; font-size: 18px; color: var(--text2); }
.calc-track { position: relative; height: 72px; border-radius: 36px; background: var(--bg3); border: 2px solid var(--border); touch-action: none; cursor: grab; }
.calc-ticks { position: absolute; left: 28px; right: 28px; top: 0; bottom: 0; }
.calc-tick { position: absolute; top: 50%; width: 2px; height: 10px; margin: -5px 0 0 -1px; background: var(--border); border-radius: 1px; }
.calc-tick.major { height: 20px; margin-top: -10px; background: var(--text3); }
.calc-knob {
  position: absolute; top: 6px; left: 0; width: 56px; height: 56px; border-radius: 50%;
  background: var(--blue); border: 3px solid var(--text); box-shadow: 0 6px 16px rgba(0,0,0,.4);
  transition: background .15s;
}
.calc-knob.grabbed { background: var(--blue-dark); transform: scale(1.08); }
.calc-knob.good { background: var(--green); }
.calc-knob.bad { background: var(--red); }
.calc-knob:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }
.calc-answer { position: absolute; top: -8px; width: 4px; height: 84px; margin-left: -2px; background: var(--green); border-radius: 2px; box-shadow: 0 0 0 4px var(--green-glow); }
.calc-ends { justify-content: space-between; padding: 0 6px; }
@media (prefers-reduced-motion: reduce) { .calc-problem.deal { animation: none; } .calc-knob.grabbed { transform: none; } }

/* ===== GRAPH DOCTOR — a wounded graph and a probe =====
   The engine draws the market; the game edits the svg to plant errors. The probe is a token in
   the tray under the graph that the student drags onto the wrong part; its tip rides above the
   finger. A healed part flashes green through .healed on the engine's own elements. */
.doc-stage { display: flex; flex-direction: column; gap: 6px; }
.doc-graph svg.graph { touch-action: none; }
.doc-tray { display: flex; align-items: center; gap: 14px; min-height: 64px; padding: 4px 6px; }
.doc-probe {
  width: 56px; height: 56px; border-radius: 50%; flex: 0 0 56px;
  background: var(--purple); border: 3px solid var(--text); box-shadow: 0 6px 16px rgba(0,0,0,.4);
  display: flex; align-items: center; justify-content: center; cursor: grab;
  touch-action: none; user-select: none; -webkit-user-select: none; position: relative; z-index: 3; will-change: transform;
}
.doc-probe-icon { font-size: 28px; line-height: 1; }
.doc-probe.dragging { cursor: grabbing; background: var(--purple-dark); box-shadow: 0 14px 32px rgba(0,0,0,.5); }
.doc-probe.dragging::after {
  /* the tip: a ring above the token, where the drop is measured */
  content: ''; position: absolute; left: 50%; top: -44px; width: 22px; height: 22px; margin-left: -11px;
  border: 3px solid var(--gold); border-radius: 50%; box-shadow: 0 0 0 3px rgba(0,0,0,.35);
}
.doc-found { font-family: var(--font-mono); font-weight: 800; }
.curve.healed .line { stroke: var(--green) !important; filter: drop-shadow(0 0 6px var(--green)); }
.curve.healed .tag { fill: var(--green) !important; }
text.healed { fill: var(--green) !important; }
circle.healed { fill: var(--green) !important; stroke: var(--green) !important; filter: drop-shadow(0 0 6px var(--green)); }
@media (prefers-reduced-motion: reduce) { .curve.healed .line, circle.healed { filter: none; } }

/* ===== THE INVESTOR — five buckets and the coins between them =====
   A bucket is a drop target; pressing one with coins lifts a coin token that follows the
   finger and lands in whichever bucket is under it on release. */
.inv-signal { display: flex; flex-direction: column; gap: 4px; padding: 10px 12px; }
.inv-news { font-weight: 900; font-size: 15px; line-height: 1.3; }
.inv-whisper { font-size: 13px; color: var(--text2); font-style: italic; }
.inv-fed { font-weight: 800; font-size: 14px; color: var(--gold); min-height: 18px; }
.inv-board { display: grid; grid-template-columns: repeat(5, 1fr); gap: 5px; touch-action: none; user-select: none; -webkit-user-select: none; }
.bucket {
  position: relative; min-height: 138px; padding: 6px 3px 8px; border-radius: var(--radius-sm);
  background: var(--card); border: 2px solid var(--border); text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 3px; cursor: grab; touch-action: none;
  transition: border-color .12s, background .12s, transform .12s;
}
.bucket.over { border-color: var(--blue); background: var(--card-hover); transform: scale(1.04); }
.bucket.from { border-color: var(--purple); }
.bucket.empty { cursor: default; }
.bucket-emoji { font-size: 20px; line-height: 1; }
.bucket-name { font-size: 9px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; color: var(--text2); white-space: nowrap; }
.bucket-share { font-family: var(--font-mono); font-weight: 900; font-size: 17px; line-height: 1.1; }
.bucket-value { font-family: var(--font-mono); font-size: 10px; color: var(--text2); white-space: nowrap; }
.bucket-pips { display: flex; flex-wrap: wrap; justify-content: center; gap: 2px; width: 40px; min-height: 12px; }
.bucket-pip { width: 8px; height: 8px; border-radius: 50%; background: var(--gold); border: 1px solid var(--gold-dark); }
.bucket-ret { font-family: var(--font-mono); font-weight: 800; font-size: 11px; min-height: 14px; }
.bucket-ret.up { color: var(--green); }
.bucket-ret.down { color: var(--red); }
.inv-token {
  position: fixed; left: 0; top: 0; width: 52px; height: 52px; margin: -26px 0 0 -26px; border-radius: 50%;
  background: var(--gold); border: 3px solid var(--text); box-shadow: 0 8px 20px rgba(0,0,0,.45);
  display: flex; align-items: center; justify-content: center; font-weight: 900; font-size: 14px; color: #1a1a1a;
  pointer-events: none; z-index: 30;
}
.inv-totals { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; font-family: var(--font-mono); font-weight: 800; padding: 0 4px; }
.inv-totals .real { color: var(--green); font-size: 18px; }
.inv-totals .nominal { color: var(--text2); font-size: 13px; }
.inv-totals .prices { color: var(--text3); font-size: 12px; }
.inv-badges { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; }

/* ===== CRISIS COUNTRY — a dashboard, a desk, a hand of policy cards =====
   The hand scrolls sideways under the desk; a card pressed in the hand follows the finger and
   is enacted when it lands on the desk. */
.cc-dash { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
.cc-gauge { padding: 4px 4px; border-radius: var(--radius-sm); background: var(--card); border: 2px solid var(--border); text-align: center; min-width: 0; }
.cc-gauge-label { font-size: 9px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; color: var(--text2); white-space: nowrap; }
.cc-gauge-value { font-family: var(--font-mono); font-weight: 900; font-size: 17px; line-height: 1.15; }
.cc-gauge-delta { font-family: var(--font-mono); font-size: 10px; min-height: 12px; color: var(--text3); }
.cc-gauge.good { border-color: var(--green); }
.cc-gauge.warn { border-color: var(--orange); }
.cc-gauge.bad { border-color: var(--red); }
.cc-gauge.good .cc-gauge-value { color: var(--green); }
.cc-gauge.warn .cc-gauge-value { color: var(--orange); }
.cc-gauge.bad .cc-gauge-value { color: var(--red); }
.cc-brief { display: flex; flex-direction: column; gap: 4px; padding: 8px 12px; }
.cc-headline { font-weight: 900; font-size: 15px; line-height: 1.3; }
.cc-advisor { font-size: 12px; color: var(--text2); line-height: 1.3; }
.cc-advisor b { color: var(--text); }
.cc-desk {
  min-height: 64px; border: 2px dashed var(--border); border-radius: var(--radius);
  display: flex; align-items: center; justify-content: center; gap: 8px; padding: 6px 10px;
  color: var(--text2); font-weight: 800; font-size: 13px; transition: border-color .12s, background .12s;
}
.cc-desk.over { border-color: var(--gold); background: var(--gold-glow); color: var(--text); }
.cc-hand { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; touch-action: none; user-select: none; -webkit-user-select: none; }
.cc-card {
  min-height: 54px; border-radius: var(--radius-sm);
  background: var(--card); border: 2px solid var(--border); padding: 4px 6px;
  display: flex; flex-direction: row; align-items: center; gap: 6px; text-align: left; cursor: grab;
  touch-action: none; user-select: none; -webkit-user-select: none; min-width: 0;
}
.cc-card.lifted { opacity: .35; }
.cc-card-emoji { font-size: 20px; line-height: 1; flex: 0 0 auto; }
.cc-card-name { font-weight: 900; font-size: 11px; line-height: 1.2; min-width: 0; }
.cc-card-kind { display: none; font-size: 9px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: var(--text3); }
.cc-card.fiscal { border-color: var(--green-dark); }
.cc-card.monetary { border-color: var(--blue-dark); }
.cc-card.exchange { border-color: var(--purple-dark); }
.cc-card.trade { border-color: var(--orange); }
.cc-card.imf { border-color: var(--gold-dark); }
.cc-token {
  position: fixed; left: 0; top: 0; width: 120px; margin: -70px 0 0 -60px; border-radius: var(--radius-sm);
  background: var(--card-hover); border: 2px solid var(--gold); box-shadow: 0 12px 28px rgba(0,0,0,.5);
  padding: 8px 6px; display: flex; flex-direction: row; align-items: center; gap: 6px; text-align: left;
  pointer-events: none; z-index: 30;
}
.cc-token .cc-card-emoji { font-size: 22px; }
.cc-token .cc-card-name { font-weight: 900; font-size: 12px; }
.cc-chain { font-size: 14px; line-height: 1.45; }
