/*
 * Fruit Ninja — chrome, on the Screenagers Studio design system.
 *
 * Tokens are lifted verbatim from the design system's token layer
 * (~/screenagers-studio-handoff/tokens.css) rather than re-derived, so this
 * file and the studio site cannot drift apart by transcription. The canvas
 * paints the board; everything here is the frame around it.
 *
 * Two house rules from the brief that are easy to undo by accident:
 *   - Corners are sharp. Radii are 0/2/4 and a pill; nothing else gets rounded.
 *   - Small text is ONE treatment: mono, 11px, uppercase, 0.14em tracking.
 *     It is the system's signature. Reuse `.label`, don't invent a second.
 */

/* ── Tokens ──────────────────────────────────────────────────────────────── */

:root {
  /* Brand */
  --lime: #E1E926;
  --teal: #26B6AD;
  --green: #8CC24A;
  --grad-brand: linear-gradient(135deg, var(--teal) 0%, var(--green) 45%, var(--lime) 100%);

  /* Ink scale — dark-first brand */
  --ink-0: #070907;
  --ink-1: #0C0F0C;
  --ink-2: #131713;
  --ink-3: #1B201B;
  --ink-4: #262C26;

  /* Foreground */
  --fg-1: #F8F8F8;
  --fg-2: rgba(248, 248, 248, .68);
  --fg-3: rgba(248, 248, 248, .42);
  --fg-on-lime: #0C0F0C;

  /* Lines */
  --line-1: rgba(248, 248, 248, .14);
  --line-2: rgba(248, 248, 248, .08);
  --line-lime: rgba(225, 233, 38, .4);

  /* Semantic */
  --ok: #8CC24A;
  --warn: #E9B426;
  --danger: #E94F26;
  --info: #26B6AD;

  /* Aliases */
  --surface-page: var(--ink-0);
  --surface-card: var(--ink-1);
  --surface-raised: var(--ink-2);
  --surface-overlay: rgba(7, 9, 7, .8);
  --text-body: var(--fg-1);
  --text-muted: var(--fg-2);
  --text-faint: var(--fg-3);
  --accent: var(--lime);
  --accent-hover: #EDF352;
  --focus-ring: 0 0 0 2px var(--ink-0), 0 0 0 4px var(--lime);

  /* Type */
  --font-display: 'Space Grotesk', ui-sans-serif, system-ui, sans-serif;
  --font-body: 'Space Grotesk', ui-sans-serif, system-ui, sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', monospace;
  --text-xs: 12px;
  --text-sm: 13px;
  --text-base: 15px;
  --text-lg: 18px;
  --text-xl: 24px;
  --text-2xl: 32px;
  --text-3xl: 44px;
  --text-4xl: 64px;
  --leading-tight: 1.02;
  --leading-snug: 1.2;
  --leading-body: 1.55;
  --tracking-display: -0.03em;
  --tracking-label: 0.14em;
  --weight-body: 400;
  --weight-medium: 500;
  --weight-display: 700;

  /* Space */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;

  /* Shape */
  --radius-0: 0px;
  --radius-1: 2px;
  --radius-2: 4px;
  --radius-pill: 999px;
  --skew-brand: -12deg;

  /* Glows, not drop shadows — this is a dark UI */
  --glow-lime: 0 0 24px rgba(225, 233, 38, .25);

  /*
   * Card-scale shape and material. The radius scale deliberately stops at
   * --radius-2 (4px) and --radius-pill, which leaves a card with no token at
   * all — so every component that wants one invents its own number, and we
   * had 10px here against 14 in the mode card against 14 in the reference.
   * 14 is the reference. --blur-card is the same argument for the glass:
   * one material, one constant.
   */
  --radius-card: 14px;
  --radius-card-sm: 12px;
  --blur-card: 8px;
  /*
   * 10px uppercase mono at 0.14em tracking is below the legibility floor and
   * was hardcoded in two places. One token, at 11px.
   */
  --text-2xs: 11px;
  /* Card accents, from the v2 lobby reference. Lime is the system's own; the
     other two exist only to tell the three ways in apart at a glance. */
  --tint-hand: #3B8BFF;
  --tint-mouse: #FF7A45;
  --glow-teal: 0 0 24px rgba(38, 182, 173, .25);
  --shadow-overlay: 0 24px 64px rgba(0, 0, 0, .6);

  /* Motion */
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --ease-snap: cubic-bezier(.7, 0, .2, 1);
  --dur-fast: 120ms;
  --dur-base: 220ms;
  --dur-slow: 480ms;
  --blur-overlay: 12px;
}

* { box-sizing: border-box; }

/*
 * `hidden` must win over layout.
 *
 * The UA sheet's [hidden]{display:none} is a plain author-level default, so any
 * explicit `display` here beats it — which silently un-hides every element laid
 * out with flex or grid. That is what put the pairing panel on screen in mouse
 * mode. One rule, rather than remembering a per-element override each time.
 */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  background: var(--surface-page);
  color: var(--text-body);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
  /* The blade is a pointer gesture; browsers must not read it as a swipe. */
  overscroll-behavior: none;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}

::selection { background: var(--lime); color: var(--fg-on-lime); }

:focus-visible { outline: none; box-shadow: var(--focus-ring); }

#board { position: fixed; inset: 0; }
#board canvas { display: block; }

/* The system's one small-text treatment. */
.label,
#src-status,
.keys,
.stats small {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: var(--weight-body);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  line-height: var(--leading-snug);
}

/* ── HUD ─────────────────────────────────────────────────────────────────── */

/* The lobby owns the screen: its top bar and footer replace the board's. */
body.menu-open #hud,
body.menu-open #hud-foot { display: none; }


#hud {
  position: fixed;
  inset: 0 0 auto 0;
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  padding: var(--space-5) var(--space-6);
  pointer-events: none;
  z-index: 5;
}
#hud button { pointer-events: auto; }

.hud-left { display: flex; align-items: center; gap: var(--space-5); }

.score-block { display: flex; align-items: baseline; gap: var(--space-3); }
#score {
  font-family: var(--font-display);
  font-size: var(--text-3xl);
  font-weight: var(--weight-display);
  letter-spacing: var(--tracking-display);
  line-height: var(--leading-tight);
  font-variant-numeric: tabular-nums;
}
.score-block small {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-faint);
}
.score-block b { color: var(--lime); font-weight: var(--weight-medium); }

#lives { display: flex; gap: 6px; font-size: var(--text-xl); color: var(--danger); }
#lives[hidden] { display: none; }
#lives::before { content: '●●●'; letter-spacing: 4px; color: var(--line-1); }
#lives[data-left="3"]::before { color: var(--danger); }
#lives[data-left="0"]::before { color: var(--line-1); }

.hud-right { display: flex; align-items: center; gap: var(--space-3); }

/* Status pill and Menu, in the lobby's dark-glass language. */
#src-status {
  color: var(--text-muted);
  padding: 7px var(--space-3);
  border: 1px solid var(--line-1);
  border-radius: var(--radius-0);
  background: rgba(12, 15, 12, 0.72);
  backdrop-filter: blur(8px);
}
#menu-btn {
  height: 32px;
  padding: 0 var(--space-4);
  border: 1px solid var(--line-1);
  background: rgba(12, 15, 12, 0.72);
  backdrop-filter: blur(8px);
  color: var(--fg-1);
  font-family: var(--font-display);
  font-size: var(--text-sm);
  font-weight: var(--weight-display);
  letter-spacing: -0.01em;
}
/*
 * Hover is a pointer capability, not a universal one.
 *
 * On a touch device :hover latches after a tap and does not clear, so a tapped
 * control stays in its hover state and the screen lies about what is live.
 * Every :hover rule in this sheet is behind this guard, wrapped in place so
 * source order and the cascade are unchanged.
 *
 * :focus-visible is deliberately NOT behind it: keyboard focus has to survive
 * on every device, including the ones that also have a touchscreen.
 *
 * (hover: hover) ALONE, not "and (pointer: fine)". Both queries describe the
 * PRIMARY pointer rather than whichever one is touching the glass, so on a
 * hybrid — a laptop with a touchscreen — both report hover and fine, the rules
 * pass either way, and a finger can still latch. The stricter query buys
 * nothing where the risk actually is. What it does do is exclude devices that
 * hover with a COARSE pointer — a TV remote, a presentation pointer — which
 * hover without tapping, carry no latch risk, and would lose the feedback for
 * nothing. This game runs on a big screen at an event, which is exactly where
 * such a pointer turns up.
 */
@media (hover: hover) {
  #menu-btn:hover { border-color: var(--line-lime); color: var(--lime); }
}

/* ── Footer strip, shared by the lobby and the board ─────────────────────── */

#menu-foot, #hud-foot {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  color: var(--text-faint);
}
#hud-foot {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  justify-content: space-between;
  padding: var(--space-3) var(--space-6);
  border-top: 1px solid var(--line-2);
  background: rgba(7, 9, 7, 0.6);
  backdrop-filter: blur(8px);
  pointer-events: none;
  z-index: 5;
}
#menu-foot { justify-content: space-between; padding-top: var(--space-3); border-top: 1px solid var(--line-2); }
#menu-foot #foot-players { display: none; }

/* Status bottom-left, version bottom-right. The wifi mark IS the status light:
   lit and lime when the relay is there, dim when it is not. */
#relay-state { display: flex; align-items: center; gap: var(--space-3); color: var(--lime); }
#relay-state.off { color: var(--text-faint); }
#app-version { color: var(--text-faint); font-family: var(--font-mono); }

.g-wifi { display: block; position: relative; width: 18px; height: 14px; flex: none; }
.g-wifi i {
  position: absolute; left: 50%; bottom: 0; transform: translateX(-50%);
  border: 2px solid currentColor;
  border-bottom-color: transparent; border-left-color: transparent; border-right-color: transparent;
  border-radius: 50%; opacity: .28;
  transition: opacity var(--dur-base) var(--ease-out);
}
.g-wifi i:nth-child(1) { width: 18px; height: 18px; }
.g-wifi i:nth-child(2) { width: 11px; height: 11px; }
.g-wifi i:nth-child(3) { width: 5px; height: 5px; border-radius: 50%; border-color: currentColor; background: currentColor; }
#relay-state:not(.off) .g-wifi i { opacity: 1; }

.foot-brand { display: flex; align-items: center; gap: var(--space-3); }
.foot-brand img { height: 18px; width: auto; display: block; }
.foot-brand i { font-style: normal; }

.dot-net {
  display: inline-block;
  width: 7px; height: 7px;
  border-radius: var(--radius-pill);
  background: var(--line-1);
  margin-right: 6px;
}
.dot-net.on { background: var(--ok); box-shadow: 0 0 8px rgba(140, 194, 74, .6); }
.dot-net.err { background: var(--danger); }

#foot-players, .foot-players { display: flex; gap: var(--space-2); }
#foot-players i, .foot-players i {
  width: 7px; height: 7px;
  border-radius: var(--radius-pill);
  background: var(--ink-4);
  display: block;
}
#foot-players i.on, .foot-players i.on { background: var(--lime); box-shadow: var(--glow-lime); }

/* ── Combo / flash ───────────────────────────────────────────────────────── */

#combo, #flash {
  position: fixed;
  left: 50%;
  transform: translateX(-50%) translateY(-6px);
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
  pointer-events: none;
  z-index: 6;
}
#combo {
  top: 96px;
  padding: var(--space-2) var(--space-4);
  background: var(--lime);
  color: var(--fg-on-lime);
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: var(--weight-display);
  letter-spacing: 0.02em;
  box-shadow: var(--glow-lime);
  /* The logo's italic slant — the system uses it on badges and marks. */
  transform: translateX(-50%) translateY(-6px) skewX(var(--skew-brand));
}
#combo.show { transform: translateX(-50%) skewX(var(--skew-brand)); }
#flash {
  bottom: var(--space-7);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-muted);
  background: var(--surface-overlay);
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--line-1);
  backdrop-filter: blur(var(--blur-overlay));
}
#combo.show, #flash.show { opacity: 1; }
#flash.show { transform: translateX(-50%); }

/* ── Pause ───────────────────────────────────────────────────────────────── */

/* Its own dim over the board, not the lobby returning: the round is still
   there, frozen, and the tunnel keeps drifting behind it. */
#pause {
  position: fixed; inset: 0; z-index: 30;
  display: grid; place-items: center;
  background: rgba(7, 9, 7, 0.6);
  backdrop-filter: blur(3px);
}
#pause[hidden] { display: none; }

.pause-card {
  --tint: var(--lime);
  position: relative; overflow: hidden;
  width: min(92vw, 380px);
  display: grid; justify-items: center; gap: var(--space-3);
  padding: var(--space-6) var(--space-5) var(--space-5);
  border: 1px solid var(--line-1);
  background:
    radial-gradient(ellipse at 50% -20%, rgba(225, 233, 38, 0.10), transparent 62%),
    rgba(10, 13, 10, 0.92);
  box-shadow: var(--shadow-overlay);
  text-align: center;
}
/* Lime rule along the top, and the grain the panels use. */
.pause-card::before {
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 2px;
  background: var(--lime); box-shadow: var(--glow-lime);
}
.pause-card::after {
  content: ''; position: absolute; inset: 0; pointer-events: none; opacity: .5;
  background-image:
    radial-gradient(rgba(225, 233, 38, .10) .5px, transparent .5px),
    radial-gradient(rgba(248, 248, 248, .05) .5px, transparent .5px);
  background-size: 7px 7px, 11px 11px;
  background-position: 0 0, 3px 5px;
}
.pause-card > * { position: relative; z-index: 1; }
#pause-glyph { color: var(--lime); }
.pause-card h2 {
  margin: 0;
  font-family: var(--font-display); font-size: var(--text-2xl); font-weight: var(--weight-display);
  letter-spacing: -0.02em; text-transform: uppercase;
}
#pause-sub { margin: 0 0 var(--space-3); color: var(--text-muted); }
.pause-card button { width: 100%; }
.pause-card .primary {
  height: 52px; border: 0; border-radius: var(--radius-pill);
  background: var(--lime); color: var(--fg-on-lime);
  font-family: var(--font-display); font-size: var(--text-base); font-weight: var(--weight-display);
  letter-spacing: 0.06em; text-transform: uppercase; cursor: pointer;
  box-shadow: var(--glow-lime);
}
@media (hover: hover) {
  .pause-card .primary:hover { background: var(--accent-hover); }
}
.pause-card .outline.wide {
  height: 44px; border: 1px solid var(--line-1); border-radius: var(--radius-pill);
  background: transparent; color: var(--text-muted);
  font-family: var(--font-body); font-size: var(--text-sm); cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
@media (hover: hover) {
  .pause-card .outline.wide:hover { border-color: var(--line-lime); color: var(--lime); }
}

/* ── Overlay ─────────────────────────────────────────────────────────────── */

#overlay {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: stretch;
  padding: 0;
  /* Lighter than --surface-overlay so the tunnel stays legible behind the
     lobby; the panels below carry their own dark glass. */
  background: rgba(7, 9, 7, 0.42);
  backdrop-filter: blur(2px);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-base) var(--ease-out);
  z-index: 10;
  overflow: auto;
}
#overlay.show { opacity: 1; visibility: visible; }

/* ── Lobby ───────────────────────────────────────────────────────────────── */

#panel-menu {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: var(--space-4);
  width: 100%;
  min-height: 100%;
  padding: var(--space-5) var(--space-6) var(--space-4);
}

#menu-top { display: flex; align-items: flex-start; justify-content: space-between; }

.wordmark { display: flex; align-items: center; gap: var(--space-3); }
.wordmark img { height: 30px; width: auto; display: block; }
.wordmark span {
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: var(--weight-display);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  line-height: 1.15;
  color: var(--fg-1);
}

.menu-nav { display: flex; align-items: center; gap: var(--space-4); color: rgba(248, 248, 248, 0.8); }
.menu-nav .divider { width: 1px; height: 14px; background: var(--line-1); }
.linkish {
  background: none; border: 0; padding: 0; cursor: pointer;
  font: inherit; letter-spacing: inherit; text-transform: inherit;
  color: rgba(248, 248, 248, 0.8);
  transition: color var(--dur-fast) var(--ease-out);
}
@media (hover: hover) {
  .linkish:hover:not(:disabled) { color: var(--lime); }
}
.linkish:disabled { opacity: .45; cursor: not-allowed; }
.menu-nav button { display: inline-flex; align-items: center; gap: var(--space-2); }

/* The top bar's narrow treatment.

   It had none at any width: #menu-top is a flex row with space-between, and
   neither child can shrink — a flex item's automatic minimum size is its
   content — so the row is stuck at its intrinsic 331px and simply runs off the
   page below ~366px. Nothing reveals it: #overlay does not scroll, so the
   document never grows a scrollbar and HELP just leaves the viewport. The
   nav colliding with the wordmark at 390 is the last width where the damage
   is still visible.

   Wrapping rather than shrinking, because every alternative destroys
   something: truncating eats the labels, and dropping the wordmark text loses
   the brand word on the brand screen. Wrap keeps all of it and costs one row
   of height on a layout that already scrolls at this width. The tighter
   padding and nav gap are what let 390 stay on one line — the wrap only
   engages below that, where nothing else would fit anyway. */
@media (max-width: 480px) {
  #panel-menu { padding: var(--space-4) var(--space-4) var(--space-3); }
  #menu-top { flex-wrap: wrap; row-gap: var(--space-2); }
  .menu-nav { gap: var(--space-3); }
}

/* A gear: a disc with a ring of teeth cast by one box-shadow, and a hole. */
.g-gear { display: block; position: relative; width: 15px; height: 15px; border-radius: 50%; background: currentColor; }
.g-gear::before {
  content: ''; position: absolute; inset: 0; margin: auto; width: 3px; height: 21px;
  background: currentColor;
  box-shadow: 0 0 0 0 currentColor;
  transform: rotate(45deg);
}
.g-gear::after { content: ''; position: absolute; inset: 0; margin: auto; width: 5px; height: 5px; border-radius: 50%; background: var(--bg-0, #070907); }
/* A ringed question mark, so HELP reads as a control rather than punctuation. */
.g-quest {
  display: grid; place-items: center; width: 17px; height: 17px;
  border-radius: 50%; border: 1.5px solid currentColor;
  font-family: var(--font-display); font-size: 11px; font-style: normal; line-height: 1;
}
.g-quest::before { content: '?'; }

/* ── Stage: hero + the mode cards ────────────────────────────────────────── */

/* One region, one stage visible. Everything sits over the live tunnel, so
   surfaces are glass rather than fills. */
/* One row that fills the region, so the visible stage is as tall as the space
   between the top bar and the footer — that is what lets the card row sit on
   the bottom instead of floating in the middle. */
#lobby-stage { display: grid; grid-template-rows: 1fr; min-height: 0; }
#stage-phone, #stage-hand, #stage-mouse { align-content: center; }
.stage[hidden] { display: none; }
#stage-cards { display: grid; grid-template-rows: auto 1fr auto; gap: var(--space-5); min-height: 0; }
#stage-cards #cards { align-self: end; }

#hero h1 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: clamp(52px, 9vw, 132px);
  line-height: 0.88;
  letter-spacing: -0.04em;
  text-transform: uppercase;
  display: flex;
  flex-direction: column;
}
#hero h1 .l1 { color: var(--fg-1); }
#hero h1 .l2 { color: var(--lime); }

/*
 * Distressed display type.
 *
 * A CSS mask over live text rather than a baked image of each word. The hero
 * is fluid — clamp(52px, 9vw, 132px) — so a baked PNG would be resampled at
 * almost every viewport, which is the one thing this look cannot survive. The
 * mask erodes identically at every size the clamp produces, and the text stays
 * live: selectable, searchable, and read aloud.
 *
 * The tile is fixed in px and not scaled with the type on purpose: worn ink
 * has a grain of its own, and a texture that grows with the letters reads as a
 * pattern printed inside them instead.
 */
.distress {
  -webkit-mask-image: url('/assets/art/distress.png');
  mask-image: url('/assets/art/distress.png');
  -webkit-mask-size: 256px 256px;
  mask-size: 256px 256px;
  -webkit-mask-repeat: repeat;
  mask-repeat: repeat;
}
/* Nothing eaten out of type that is already small enough to be fragile. */
@media (max-width: 900px) {
  .stage-title .distress { -webkit-mask-image: none; mask-image: none; }
}
@media (prefers-contrast: more) {
  .distress { -webkit-mask-image: none; mask-image: none; }
}
.tagline { margin: var(--space-4) 0 0; color: var(--text-muted); font-size: var(--text-sm); letter-spacing: 0.22em; }

/* The high score lost its tile; it belongs next to the name anyway. */
#best-chip {
  display: inline-flex; align-items: center; gap: var(--space-2);
  margin: var(--space-4) 0 0; padding: 5px var(--space-3);
  border: 1px solid var(--line-1);
  background: rgba(12, 15, 12, 0.6);
  color: var(--text-faint);
}
#best-chip b { color: var(--lime); font-weight: var(--weight-medium); }

/* ── Mode cards ──────────────────────────────────────────────────────────── */

/* Three ways in, equal weight. Each is tinted glass over the live tunnel with
   a 2px accent edge and an outer glow — the accent is the only thing that
   differs, and it is what tells them apart at a glance. */
#cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-4); }
/* The .mode-card block lived here. Every property it set is redeclared in
   css/mode-card.css, which loads after this file — see docs/mode-card.md.
   Deleted rather than left to be overridden: a rule that happens to lose is
   not the same as a rule that is gone. */
/* The dot-grid .mode-card::after lived here. The component paints a real
   grain tile with mix-blend-mode: overlay in its place. */
.mode-card > * { position: relative; z-index: 1; }

/* The card's hover and focus states lived here, including a transform:
   scale(1.04) on .card-art. The component owns both now, and uses translateY
   instead — .card-art is a 1-bit dither rasterised at exactly its display
   size, and scaling it resamples the checker into grey for the whole hover.
   The hover guard this file introduced lives on in mode-card.css.
   Every OTHER :hover rule in this sheet is still guarded; see below. */
.card-art { transition: transform var(--dur-base) var(--ease-out); }
.mode-card:active:not(:disabled) { transform: translateY(0); }
@media (prefers-reduced-motion: reduce) {
  .mode-card, .card-art { transition: none; }
  .mode-card:hover:not(:disabled) { transform: none; }
  .mode-card:hover:not(:disabled) .card-art, .mode-card:focus-visible .card-art { transform: none; }
}
.mode-card:disabled { opacity: .45; cursor: not-allowed; }

#card-phone { --tint: var(--lime); }
#card-hand  { --tint: var(--tint-hand); }
#card-mouse { --tint: var(--tint-mouse); }

/* ── Dithered illustrations ──────────────────────────────────────────────── */

/* Every one is shown at the exact size it was rasterised at. A dither is
   defined per pixel: at any other size the browser resamples it and the
   checker averages back into the grey it was encoding. So the sizes here and
   the sizes in scripts/gen-art.mjs are one list, and `pixelated` is not
   optional. */
.art {
  background-repeat: no-repeat;
  background-position: center;
  image-rendering: pixelated;
  flex: none;
}
.art-40 { width: 40px; height: 40px; background-size: 40px 40px; }
.art-120 { width: 120px; height: 120px; background-size: 120px 120px; }
.art-200 { width: 200px; height: 200px; background-size: 200px 200px; }
.art-240 { width: 240px; height: 240px; background-size: 240px 240px; }

.art-phone.art-40 { background-image: url('/assets/art/phone-40.png'); }
.art-hand.art-40 { background-image: url('/assets/art/hand-40.png'); }
.art-mouse.art-40 { background-image: url('/assets/art/mouse-40.png'); }
.art-phone.art-120 { background-image: url('/assets/art/phone-120.png'); }
.art-hand.art-120 { background-image: url('/assets/art/hand-120.png'); }
.art-mouse.art-120 { background-image: url('/assets/art/mouse-120.png'); }
.art-phone.art-200 { background-image: url('/assets/art/phone-200.png'); }
.art-hand.art-200 { background-image: url('/assets/art/hand-200.png'); }
.art-mouse.art-200 { background-image: url('/assets/art/mouse-200.png'); }
.art-phone.art-240 { background-image: url('/assets/art/phone-240.png'); }
.art-qr.art-40 { background-image: url('/assets/art/step-qr-40.png'); }
.art-motion.art-40 { background-image: url('/assets/art/step-motion-40.png'); }
.art-hold.art-40 { background-image: url('/assets/art/step-hold-40.png'); }
.art-swing.art-40 { background-image: url('/assets/art/step-swing-40.png'); }
.art-hand.art-240 { background-image: url('/assets/art/hand-240.png'); }
.art-mouse.art-240 { background-image: url('/assets/art/mouse-240.png'); }

@media (min-resolution: 2dppx) {
  .art-phone.art-40 { background-image: url('/assets/art/phone-40@2x.png'); }
  .art-hand.art-40 { background-image: url('/assets/art/hand-40@2x.png'); }
  .art-mouse.art-40 { background-image: url('/assets/art/mouse-40@2x.png'); }
  .art-phone.art-120 { background-image: url('/assets/art/phone-120@2x.png'); }
  .art-hand.art-120 { background-image: url('/assets/art/hand-120@2x.png'); }
  .art-mouse.art-120 { background-image: url('/assets/art/mouse-120@2x.png'); }
  .art-phone.art-200 { background-image: url('/assets/art/phone-200@2x.png'); }
  .art-hand.art-200 { background-image: url('/assets/art/hand-200@2x.png'); }
  .art-mouse.art-200 { background-image: url('/assets/art/mouse-200@2x.png'); }
  .art-phone.art-240 { background-image: url('/assets/art/phone-240@2x.png'); }
  .art-qr.art-40 { background-image: url('/assets/art/step-qr-40@2x.png'); }
  .art-motion.art-40 { background-image: url('/assets/art/step-motion-40@2x.png'); }
  .art-hold.art-40 { background-image: url('/assets/art/step-hold-40@2x.png'); }
  .art-swing.art-40 { background-image: url('/assets/art/step-swing-40@2x.png'); }
  .art-hand.art-240 { background-image: url('/assets/art/hand-240@2x.png'); }
  .art-mouse.art-240 { background-image: url('/assets/art/mouse-240@2x.png'); }
}

.card-art { justify-self: center; align-self: center; }

.card-copy { display: flex; flex-direction: column; }
.card-copy b {
  display: flex; flex-direction: column;
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: var(--weight-display);
  line-height: 1.02;
  letter-spacing: -0.01em;
  text-transform: uppercase;
}
.card-copy b em { font-style: normal; color: var(--tint); }
.card-copy small {
  display: block; margin-top: var(--space-3);
  color: var(--text-muted); font-size: var(--text-sm); line-height: var(--leading-snug);
}

/* Thin rule, then who it is for. */
.card-meta {
  display: flex; align-items: flex-start; gap: var(--space-2);
  margin-top: var(--space-4); padding-top: var(--space-3);
  border-top: 1px solid var(--line-1);
  color: var(--text-muted);
}
.card-meta em { display: block; font-style: normal; margin-top: 3px; font-size: var(--text-2xs); color: var(--text-faint); }

/* People glyphs, drawn: fixed boxes whose parts are placed by auto margins. */
.g-person, .g-people { display: block; position: relative; width: 14px; height: 13px; flex: none; margin-top: 1px; }
.g-person::before, .g-people::before {
  content: ''; position: absolute; inset: 0 0 auto; margin: 0 auto;
  width: 7px; height: 7px; border-radius: 50%; background: currentColor;
}
.g-person::after, .g-people::after {
  content: ''; position: absolute; inset: auto 0 0; margin: 0 auto;
  width: 13px; height: 6px; border-radius: 7px 7px 2px 2px; background: currentColor;
}
/* The second figure, offset behind — "more than one" without a second box. */
.g-people { width: 20px; }
.g-people::before, .g-people::after { margin-right: 7px; }
.g-people i, .g-people > * { display: none; }
.g-people { box-sizing: content-box; }
.g-people::before { box-shadow: 7px 1px 0 -1px currentColor; }
.g-people::after { box-shadow: 7px 0 0 0 currentColor; }

/* The .card-go disc lived here. The component sizes and positions it from
   --go-size / --go-inset, which is what lets .card-meta keep clear of it.
   The arrow drawn inside it is still ours — mode-card.css does not redeclare
   these two pseudo-elements. */
.card-go::before {
  content: ''; position: absolute; inset: 0; margin: auto;
  width: 16px; height: 2px; background: currentColor;
}
.card-go::after {
  content: ''; position: absolute; inset: 0; margin: auto; margin-right: 14px;
  width: 9px; height: 9px;
  border-top: 2px solid currentColor; border-right: 2px solid currentColor;
  transform: rotate(45deg);
}

/* Stacked, three cards would run past a laptop viewport at their full height,
   so they compress. This block has to come AFTER the rules it overrides: it
   matches on the same specificity, so source order is what decides it. */
@media (max-width: 900px) {
  /* minmax(min-content, 1fr), not 1fr. A grid item's automatic minimum size is
     content-based only while its overflow computes to `visible`; .mode-card is
     `hidden`, so the minimum collapses to zero and a 1fr track is free to size
     the row below its own copy — silently, because the card clips it. Naming
     min-content explicitly puts the floor back while 1fr still distributes the
     space, so the rows stay equal whenever the content fits and only a row that
     needs more takes more. Verified by squeezing the tracks: with 1fr the phone
     card lost 29.9px of its last meta line (scrollHeight 32 over clientHeight);
     with this it refuses, growing to 151.9px instead. */
  #cards { grid-template-columns: 1fr; grid-auto-rows: minmax(min-content, 1fr); gap: var(--space-3); }
  .mode-card {
    min-height: 120px;
    grid-template-columns: 120px minmax(0, 1fr);
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
  }
  .card-copy b { font-size: var(--text-lg); }
  .card-copy small { margin-top: var(--space-2); font-size: var(--text-xs); }
  .card-meta { margin-top: var(--space-3); padding-top: var(--space-2); }
  .card-go { width: 36px; height: 36px; right: var(--space-4); bottom: var(--space-3); }
  #hero h1 { font-size: clamp(38px, 7vw, 62px); }
  #stage-cards { gap: var(--space-4); }
  /* The stacked card's column is 120, so the 120px rasterisation is the one
     that belongs in it — scaling the 200 is the mush all of this avoids. */
  .card-art.art-200 { width: 120px; height: 120px; background-size: 120px 120px; }
  #card-phone .card-art { background-image: url('/assets/art/phone-120.png'); }
  #card-hand .card-art { background-image: url('/assets/art/hand-120.png'); }
  #card-mouse .card-art { background-image: url('/assets/art/mouse-120.png'); }
}
@media (max-width: 900px) and (min-resolution: 2dppx) {
  #card-phone .card-art { background-image: url('/assets/art/phone-120@2x.png'); }
  #card-hand .card-art { background-image: url('/assets/art/hand-120@2x.png'); }
  #card-mouse .card-art { background-image: url('/assets/art/mouse-120@2x.png'); }
}

/* ── The countdown ───────────────────────────────────────────────────────── */

#stage-countdown {
  display: grid; justify-items: center; align-content: center; gap: var(--space-5);
  text-align: center;
}
.cd-eyebrow { color: var(--fg-1); font-size: var(--text-sm); letter-spacing: 0.32em; }

.cd-ring { position: relative; width: min(38vh, 300px); aspect-ratio: 1; display: grid; place-items: center; }
.cd-ring svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
/* Segmented: the dash pattern makes the ring, the mask makes it deplete. */
.cd-track, .cd-arc {
  fill: none; stroke-width: 7; stroke-linecap: butt;
  stroke-dasharray: 2.1 1.2;
}
.cd-track { stroke: rgba(225, 233, 38, 0.16); }
.cd-arc { stroke: var(--lime); filter: drop-shadow(0 0 10px rgba(225, 233, 38, 0.75)); }
.cd-inner { fill: none; stroke: rgba(225, 233, 38, 0.10); stroke-width: 1; }
#cd-digit {
  position: relative;
  font-family: var(--font-display); font-weight: var(--weight-display);
  font-size: min(20vh, 150px); line-height: 1; color: var(--lime);
  text-shadow: 0 0 40px rgba(225, 233, 38, 0.5);
  font-variant-numeric: tabular-nums;
}

.cd-pill {
  --tint: var(--lime);
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-4) var(--space-6);
  border: 1px solid color-mix(in srgb, var(--tint) 40%, transparent);
  background: rgba(10, 13, 10, 0.72);
  backdrop-filter: blur(8px);
  text-align: left;
}
.cd-pill.tint-hand { --tint: var(--tint-hand); }
.cd-pill.tint-mouse { --tint: var(--tint-mouse); }
.cd-pill > i { color: var(--tint); flex: none; }
.cd-pill b {
  display: block; color: var(--fg-1);
  font-family: var(--font-display); font-size: var(--text-lg); font-weight: var(--weight-display);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.cd-pill b em { font-style: normal; color: var(--tint); }
.cd-pill small { display: block; margin-top: 2px; color: var(--text-faint); }

@media (prefers-reduced-motion: reduce) {
  .cd-arc { filter: none; }
}

/* ── Stages ──────────────────────────────────────────────────────────────── */

.stage-back {
  display: inline-flex; align-items: center; gap: var(--space-3);
  height: 34px; padding: 0 var(--space-4) 0 var(--space-3);
  border: 1px solid var(--line-1); border-radius: var(--radius-pill);
  background: rgba(12, 15, 12, 0.6); color: var(--text-muted); cursor: pointer;
  white-space: nowrap;
  transition: border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
@media (hover: hover) {
  .stage-back:hover { border-color: var(--line-lime); color: var(--lime); }
}
.g-back { display: block; position: relative; width: 16px; height: 12px; }
.g-back::before { content: ''; position: absolute; inset: 0; margin: auto; width: 14px; height: 2px; background: currentColor; }
.g-back::after {
  content: ''; position: absolute; inset: 0; margin: auto; margin-left: 12px;
  width: 8px; height: 8px;
  border-left: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
}

.stage-title {
  margin: var(--space-4) 0 0;
  display: flex; flex-direction: column;
  font-family: var(--font-display);
  font-size: clamp(34px, 3.4vw, 60px);
  font-weight: var(--weight-display);
  line-height: 0.94;
  letter-spacing: -0.03em;
  text-transform: uppercase;
}
.stage-title em { font-style: normal; color: var(--tint, var(--lime)); }
.stage-sub { letter-spacing: 0.16em; }
.tint-phone { color: var(--lime); }
.tint-hand { color: var(--tint-hand); }
.tint-mouse { color: var(--tint-mouse); }
.stage-sub { margin: var(--space-2) 0 0; color: var(--text-muted); }
.stage-body { margin-top: var(--space-5); display: grid; grid-template-columns: minmax(280px, 420px) minmax(0, 1fr); gap: var(--space-6); align-items: start; }
@media (max-width: 900px) { .stage-body { grid-template-columns: 1fr; } }

#stage-actions[hidden] { display: none; }
/* In the phone panel's right column the two actions stack, START first: it is
   the decision, and CHANGE CONTROL is the way back out of it. */
#stage-actions.in-column { margin-top: var(--space-4); padding-top: var(--space-4); border-top: 1px solid var(--line-2); }
#stage-actions.in-column .stage-buttons { flex-direction: column-reverse; align-items: stretch; gap: var(--space-2); }
#stage-actions.in-column #play { width: 100%; }
#stage-actions.in-column .ghost { width: 100%; justify-content: center; display: flex; align-items: center; }
.stage-buttons { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-top: var(--space-4); }
#stage-actions .hint, #stage-actions .error { margin: 0; font-size: var(--text-xs); }

/* START GAME, and its gated state. The primary rules in the buttons block are
   scoped to the game-over panel, so the lobby's own start button styles here —
   one button, one place. */
#stage-actions .ghost {
  height: 46px; padding: 0 var(--space-5);
  border: 1px solid var(--line-1); border-radius: var(--radius-pill);
  background: rgba(12, 15, 12, 0.6); color: var(--text-muted);
  font-family: var(--font-body); font-size: var(--text-sm); cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
@media (hover: hover) {
  #stage-actions .ghost:hover { border-color: var(--line-lime); color: var(--lime); }
}
#stage-actions .ghost:focus-visible, .stage-back:focus-visible,
.outline:focus-visible, #practice-again:focus-visible, .pause-card button:focus-visible {
  outline: none; box-shadow: var(--focus-ring);
}
#play:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring), 0 0 24px color-mix(in srgb, var(--tint) 40%, transparent);
}

/* The start button wears the panel's accent: it is that panel's decision. */
#stage-actions { --tint: var(--lime); }
#stage-actions.tint-hand { --tint: var(--tint-hand); }
#stage-actions.tint-mouse { --tint: var(--tint-mouse); }
#play {
  display: grid; grid-template-columns: 1fr auto; align-items: center; column-gap: var(--space-5);
  min-height: 54px; padding: var(--space-2) var(--space-5) var(--space-2) var(--space-6);
  border: 0; border-radius: var(--radius-pill);
  background: var(--tint); color: var(--fg-on-lime);
  font-family: var(--font-display); cursor: pointer;
  box-shadow: 0 0 24px color-mix(in srgb, var(--tint) 30%, transparent);
  transition: background var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out),
              opacity var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.play-text { display: flex; flex-direction: column; align-items: flex-start; }
#play b { font-size: var(--text-lg); font-weight: var(--weight-display); text-transform: uppercase; letter-spacing: 0.04em; line-height: 1.1; }
#play small { color: rgba(12, 15, 12, .72); font-size: 10px; }
#play:not(:disabled) small { display: none; }
@media (hover: hover) {
  #play:hover:not(:disabled) { background: var(--accent-hover); }
}
#play:active:not(:disabled) { transform: translateY(1px); }
/* Gated: a glowing button you cannot press reads as broken rather than as
   waiting, so it drops the lime entirely and says what it is waiting for. */
#play:disabled {
  background: rgba(19, 23, 19, 0.72); color: var(--text-faint);
  box-shadow: none; opacity: .7; cursor: not-allowed;
}
#play:disabled small { color: var(--text-faint); }
.g-arrow { display: block; position: relative; width: 22px; height: 12px; }
.g-arrow::before { content: ''; position: absolute; inset: 0; margin: auto; width: 20px; height: 2px; background: currentColor; }
.g-arrow::after {
  content: ''; position: absolute; inset: 0; margin: auto; margin-right: 2px;
  width: 9px; height: 9px;
  border-top: 2px solid currentColor; border-right: 2px solid currentColor;
  transform: rotate(45deg);
}
/* Stale advice: in state B the phones are already in and moving. */
#stage-phone.live ~ #stage-actions .hint,
#stage-hand:not([hidden]) ~ #stage-actions .hint,
#stage-mouse:not([hidden]) ~ #stage-actions .hint { display: none; }
.plain-tips { margin: 0; padding-left: var(--space-5); color: var(--text-muted); line-height: var(--leading-body); }

/* ── Stage: play with phone ──────────────────────────────────────────────── */

.stage-grid {
  display: grid;
  grid-template-columns: minmax(240px, 22%) minmax(0, 1fr) minmax(260px, 24%);
  gap: var(--space-6);
  align-items: start;
  margin-top: var(--space-4);
}
#stage-phone.live .stage-grid { grid-template-columns: minmax(240px, 22%) minmax(0, 1fr); }
@media (max-width: 1100px) { .stage-grid, #stage-phone.live .stage-grid { grid-template-columns: 1fr; } }

.stage-lede .stage-title { margin-top: 0; }
.stage-sub2 { margin: var(--space-2) 0 0; color: var(--text-muted); }

.stage-art { margin-top: var(--space-5); }
#stage-phone { --tint: var(--lime); }
#stage-hand { --tint: var(--tint-hand); }
#stage-mouse { --tint: var(--tint-mouse); }

/* The glass panel the steps and the code live on. */
.panel-card {
  display: grid; grid-template-columns: minmax(180px, 40%) minmax(0, 1fr);
  gap: var(--space-5);
  padding: var(--space-5);
  border: 1px solid color-mix(in srgb, var(--tint) 46%, transparent);
  border-radius: var(--radius-2);
  background: rgba(10, 13, 10, 0.72);
  backdrop-filter: blur(10px);
  box-shadow: 0 0 30px color-mix(in srgb, var(--tint) 12%, transparent);
}
@media (max-width: 900px) { .panel-card { grid-template-columns: 1fr; } }

/* The compact QR, in the left column of state B: the panel still has to be
   scannable while two, three and four are joining. */
#connect-cta { display: none; margin: 0 0 var(--space-3); color: var(--fg-1); letter-spacing: 0.14em; }
#connect-cta em { font-style: normal; color: var(--lime); }
#connect.compact {
  margin-top: var(--space-5);
  padding: var(--space-4);
  border: 1px solid var(--line-lime);
  border-radius: var(--radius-2);
  background: rgba(10, 13, 10, 0.72);
  backdrop-filter: blur(8px);
  max-width: 232px;
}
#connect.compact #connect-cta { display: block; }
#stage-phone.live .stage-art { display: none; }
#connect.compact #qr-img { max-width: 160px; padding: 6px; }
#connect.compact .url { font-size: var(--text-xs); }
#connect[hidden] { display: none; }

#connect-note { color: var(--warn); margin: 0 0 var(--space-3); }
#connect-note[hidden] { display: none; }
#qr-wrap[hidden] { display: none; }
#qr-img { display: block; width: 100%; max-width: 240px; aspect-ratio: 1; background: #fff; padding: 8px; border: 2px solid var(--tint); border-radius: var(--radius-2); }
.url {
  display: block; margin-top: var(--space-3);
  color: var(--tint); font-family: var(--font-mono); font-size: var(--text-sm);
  overflow-wrap: break-word;
  transition: opacity var(--dur-fast) var(--ease-out);
}
@media (hover: hover) {
  .url:hover { opacity: .75; }
}

#phone-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-4); }
#phone-steps li {
  display: grid; grid-template-columns: 52px minmax(0, 1fr);
  grid-template-rows: auto auto auto; column-gap: var(--space-3);
  align-items: start;
}
#phone-steps li + li { padding-top: var(--space-4); border-top: 1px solid var(--line-2); }
#phone-steps i { grid-row: 1 / 4; align-self: center; color: var(--fg-1); }
#phone-steps .n {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: var(--tracking-label);
  color: var(--tint);
}
#phone-steps b {
  font-family: var(--font-display); font-size: var(--text-sm); font-weight: var(--weight-display);
  letter-spacing: 0.04em; text-transform: uppercase;
}
#phone-steps small { color: var(--text-faint); font-size: var(--text-xs); line-height: var(--leading-snug); }

/* ── Stage: play with hands ──────────────────────────────────────────────── */

/* Blue corner brackets — four L's cast from two elements, so the box needs no
   extra markup. */
.bracketed { position: relative; }
.bracketed::before, .bracketed::after, .bracketed > .brk {
  content: ''; position: absolute; width: 26px; height: 26px; pointer-events: none;
  border: 2px solid var(--tint);
  /* Above the feed: the canvas fills the box and is painted after them. */
  z-index: 3;
}
.bracketed::before { left: -1px; top: -1px; border-right: 0; border-bottom: 0; }
.bracketed::after { right: -1px; bottom: -1px; border-left: 0; border-top: 0; }
.bracketed > .brk.tr { right: -1px; top: -1px; border-left: 0; border-bottom: 0; }
.bracketed > .brk.bl { left: -1px; bottom: -1px; border-right: 0; border-top: 0; }

.panel-card.one-col { display: grid; grid-template-columns: 1fr; justify-items: center; align-content: center; gap: var(--space-4); min-height: 320px; text-align: center; }
#hand-permit p { margin: 0; color: var(--text-muted); }
#hand-permit.denied p { color: var(--danger); }
.outline {
  height: 46px; padding: 0 var(--space-6);
  border: 2px solid var(--tint); border-radius: var(--radius-pill);
  background: transparent; color: var(--tint);
  font-family: var(--font-display); font-size: var(--text-sm); font-weight: var(--weight-display);
  letter-spacing: 0.08em; text-transform: uppercase; cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
@media (hover: hover) {
  .outline:hover { background: color-mix(in srgb, var(--tint) 16%, transparent); }
}
.outline:disabled { opacity: .5; cursor: progress; }

.steps-list { list-style: none; margin: var(--space-5) 0 0; padding: 0; display: grid; gap: var(--space-3); max-width: 340px; }
.steps-list li { padding-top: var(--space-3); border-top: 1px solid color-mix(in srgb, var(--tint) 30%, transparent); }
.steps-list .n { display: block; font-family: var(--font-mono); font-size: 11px; letter-spacing: var(--tracking-label); color: var(--tint); }
.steps-list b { display: block; margin-top: 2px; font-family: var(--font-display); font-size: var(--text-base); font-weight: var(--weight-display); }
.steps-list small { color: var(--text-faint); font-size: var(--text-xs); }

/* The live feed. Same video element and the same model as the in-game PiP —
   this is a second view of it, not a second pipeline. */
#hand-feed {
  position: relative; overflow: hidden;
  border-radius: var(--radius-2);
  background: var(--ink-0);
  aspect-ratio: 4 / 3; max-height: 58vh;
  display: grid; place-items: center;
}
#hand-canvas { display: block; width: 100%; height: 100%; }
#stage-hand.s-c #hand-canvas { display: none; }
.feed-tag {
  position: absolute; left: var(--space-4); top: var(--space-4); z-index: 2;
  padding: 4px var(--space-3);
  border: 1px solid var(--tint); border-radius: var(--radius-pill);
  background: rgba(7, 9, 7, .7); color: var(--tint);
}
#hand-wire { display: grid; place-items: center; color: var(--tint); }
#hand-wire[hidden] { display: none; }

#hand-side { display: grid; gap: var(--space-3); align-content: start; }
.side-card {
  padding: var(--space-4);
  border: 1px solid var(--line-1); border-radius: var(--radius-2);
  background: rgba(10, 13, 10, 0.72); backdrop-filter: blur(10px);
  display: grid; justify-items: center; gap: var(--space-2); text-align: center;
}
.side-card b { font-family: var(--font-display); font-size: var(--text-base); font-weight: var(--weight-display); text-transform: uppercase; }
.side-card small { color: var(--text-faint); font-size: var(--text-xs); }
.side-card i { color: var(--tint); }
.track-head { display: flex; align-items: center; gap: var(--space-3); color: var(--text-faint); }
.track-head .dot { width: 9px; height: 9px; border-radius: var(--radius-pill); border: 2px solid var(--line-1); }
.track-head .dot.on { background: var(--lime); border-color: var(--lime); box-shadow: var(--glow-lime); }
.side-card .bar { width: 100%; height: 6px; border-radius: var(--radius-pill); background: var(--ink-3); overflow: hidden; }
.side-card .bar i { display: block; height: 100%; width: 0; background: var(--lime); box-shadow: var(--glow-lime); transition: width 120ms linear; }
.side-card.ready em { font-style: normal; color: var(--lime); }
.check-ring {
  width: 54px; height: 54px; display: grid; place-items: center;
  border-radius: var(--radius-pill); border: 3px solid var(--lime);
  box-shadow: var(--glow-lime);
}
.g-check { display: block; width: 20px; height: 11px; border-left: 3px solid var(--lime); border-bottom: 3px solid var(--lime); transform: rotate(-45deg) translate(1px, -2px); }

/* A camera: body, lens, and the little hump on top. */
.g-camera { display: block; position: relative; width: 56px; height: 40px; border: 3px solid currentColor; border-radius: 6px; }
.g-camera::before { content: ''; position: absolute; inset: 0; margin: auto; width: 20px; height: 20px; border: 3px solid currentColor; border-radius: 50%; }
.g-camera::after { content: ''; position: absolute; left: 10px; top: -9px; width: 16px; height: 6px; border: 3px solid currentColor; border-bottom: 0; border-radius: 4px 4px 0 0; }
/* A hand: palm plus the fingers cast as one box-shadow row. */
.g-hand { display: block; position: relative; width: 26px; height: 30px; }
.g-hand::before { content: ''; position: absolute; inset: auto 0 0; margin: 0 auto; width: 22px; height: 16px; border-radius: 6px 6px 8px 8px; background: currentColor; }
.g-hand::after {
  content: ''; position: absolute; left: 3px; top: 2px; width: 4px; height: 14px; border-radius: 2px; background: currentColor;
  box-shadow: 6px -2px 0 0 currentColor, 12px -2px 0 0 currentColor, 18px 2px 0 0 currentColor;
}
.g-hand.big { width: 130px; height: 150px; opacity: .55; }
.g-hand.big::before { width: 110px; height: 80px; border-radius: 30px 30px 40px 40px; }
.g-hand.big::after { left: 15px; top: 10px; width: 20px; height: 70px; border-radius: 10px;
  box-shadow: 30px -10px 0 0 currentColor, 60px -10px 0 0 currentColor, 90px 10px 0 0 currentColor; }

/* Which of A / B / C is on screen. */
#stage-hand.s-a #hand-feed, #stage-hand.s-a #hand-side { display: none; }
#stage-hand:not(.s-a) #hand-permit { display: none; }
#stage-hand:not(.s-b) #hand-detect, #stage-hand:not(.s-b) #hand-track { display: none; }
#stage-hand:not(.s-c) #hand-ready { display: none; }
#stage-hand.s-c #hand-wire { display: grid; }
#stage-hand.s-a .stage-title .only-b { display: none; }

/* ── Stage: play with mouse ──────────────────────────────────────────────── */

/* Transparent on purpose: the board canvas behind the lobby draws the blade
   and the targets inside this rectangle, so the practice really is the game. */
#mouse-box {
  position: relative;
  display: grid; grid-template-rows: auto 1fr auto;
  aspect-ratio: 16 / 10; max-height: 56vh;
  padding: var(--space-4);
  border-radius: var(--radius-2);
  cursor: crosshair;
}
.box-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); }
.box-head b { display: block; font-family: var(--font-display); font-size: var(--text-lg); font-weight: var(--weight-display); text-transform: uppercase; letter-spacing: 0.06em; }
.box-head small { display: block; color: var(--text-faint); font-size: var(--text-xs); }
.box-readouts { display: flex; gap: var(--space-5); text-align: right; }
.box-readouts[hidden] { display: none; }
.box-readouts small { color: var(--text-faint); }
.box-readouts b { color: var(--tint); font-family: var(--font-display); font-size: var(--text-xl); font-variant-numeric: tabular-nums; }
.box-foot { text-align: center; color: var(--text-faint); }

.box-mid { position: relative; display: grid; place-items: center; }
#stage-mouse.s-b .box-mid { visibility: hidden; }
/* Four arrows round the mouse, lighting as the pointer travels that way. */
.arr { position: absolute; width: 0; height: 0; opacity: .28; transition: opacity var(--dur-fast) var(--ease-out); }
.arr.on { opacity: 1; filter: drop-shadow(0 0 8px var(--tint)); }
.arr { left: 50%; top: 50%; }
.arr.up { border-left: 9px solid transparent; border-right: 9px solid transparent; border-bottom: 14px solid var(--tint); transform: translate(-50%, -92px); }
.arr.down { border-left: 9px solid transparent; border-right: 9px solid transparent; border-top: 14px solid var(--tint); transform: translate(-50%, 78px); }
.arr.left { border-top: 9px solid transparent; border-bottom: 9px solid transparent; border-right: 14px solid var(--tint); transform: translate(-98px, -50%); }
.arr.right { border-top: 9px solid transparent; border-bottom: 9px solid transparent; border-left: 14px solid var(--tint); transform: translate(84px, -50%); }

/* A mouse: body, split line, and the wheel. */
.g-mouse { display: block; position: relative; width: 42px; height: 62px; border: 3px solid var(--tint); border-radius: 21px / 26px; }
.g-mouse::before { content: ''; position: absolute; inset: 0 0 auto; margin: 0 auto; width: 3px; height: 24px; background: var(--tint); }
.g-mouse::after { content: ''; position: absolute; inset: 10px 0 auto; margin: 0 auto; width: 5px; height: 12px; border-radius: 3px; background: var(--tint); }

#mouse-side { display: grid; gap: var(--space-3); align-content: start; }
#mouse-side .dots { display: flex; gap: var(--space-2); margin-top: var(--space-2); }
#mouse-side .dots i { width: 10px; height: 10px; border-radius: var(--radius-pill); border: 2px solid var(--line-1); }
#mouse-side .dots i.on { background: var(--tint); border-color: var(--tint); box-shadow: 0 0 12px color-mix(in srgb, var(--tint) 60%, transparent); }
.ring-count { display: flex; align-items: baseline; gap: 4px; }
.ring-count b { font-family: var(--font-display); font-size: var(--text-3xl); font-weight: var(--weight-display); color: var(--tint); }
.ring-count small { color: var(--text-faint); }
#mouse-ready { flex-direction: row; display: flex; align-items: center; justify-content: center; gap: var(--space-3); color: var(--text-faint); }
#mouse-ready .dot { width: 10px; height: 10px; border-radius: var(--radius-pill); border: 2px solid var(--line-1); }
#mouse-ready.on { color: var(--lime); border-color: color-mix(in srgb, var(--lime) 50%, transparent); }
#mouse-ready.on .dot { background: var(--lime); border-color: var(--lime); box-shadow: var(--glow-lime); }
#practice-again {
  justify-self: center; padding: 4px 2px;
  font-family: var(--font-mono); font-size: 11px;
  letter-spacing: var(--tracking-label); text-transform: uppercase;
  color: var(--text-faint); text-decoration: underline; text-underline-offset: 3px;
}
@media (hover: hover) {
  #practice-again:hover { color: var(--tint); }
}
#practice-again[hidden] { display: none; }
/* Already proven: the box keeps the blade but drops the tutorial's furniture. */
#stage-mouse.s-done .arr { display: none; }

/* ── Players list ────────────────────────────────────────────────────────── */

#players {
  padding: var(--space-4);
  border: 1px solid var(--line-1); border-radius: var(--radius-2);
  background: rgba(10, 13, 10, 0.72);
  backdrop-filter: blur(10px);
}
.players-head { display: flex; align-items: baseline; justify-content: space-between; color: var(--text-faint); }
.players-head span:first-child { color: var(--fg-1); letter-spacing: 0.14em; }
#players-count { color: var(--tint); }

.player-row { list-style: none; margin: var(--space-4) 0 0; padding: 0; display: grid; gap: var(--space-2); }
.player {
  display: grid; grid-template-columns: 28px 12px minmax(0, 1fr) 16px;
  align-items: center; gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--line-1); border-radius: var(--radius-2);
  background: rgba(19, 23, 19, 0.5);
  transition: border-color var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
}
.player b { color: var(--fg-1); font-weight: var(--weight-display); font-family: var(--font-display); }
.player small { color: var(--text-faint); font-size: var(--text-xs); letter-spacing: 0; text-transform: none; }
.player .ring {
  width: 10px; height: 10px; border-radius: var(--radius-pill);
  border: 2px solid var(--line-1); background: transparent;
  transition: background var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out);
}
.player i { color: var(--text-faint); }
/* Joined: the row itself lights, not just a word in it. */
.player.joined { border-color: var(--tint); box-shadow: 0 0 18px color-mix(in srgb, var(--tint) 26%, transparent); }
.player.joined .ring { background: var(--tint); border-color: var(--tint); box-shadow: var(--glow-lime); }
.player.joined small { color: var(--tint); font-family: var(--font-mono); font-size: 11px; letter-spacing: var(--tracking-label); text-transform: uppercase; }
.player.joined i { color: var(--tint); }

#mode-block { margin-top: var(--space-4); padding-top: var(--space-4); border-top: 1px solid var(--line-2); }
#mode-block > .label { display: block; color: var(--text-faint); }
#mode-name {
  display: block; margin-top: 2px;
  font-family: var(--font-display); font-size: var(--text-2xl); font-weight: var(--weight-display);
  letter-spacing: -0.02em; text-transform: uppercase; color: var(--fg-1);
}
#mode-count { color: var(--text-faint); }

/* ── State B: one card per phone that is actually in ─────────────────────── */

#phone-live { display: grid; justify-items: center; align-content: center; gap: var(--space-4); }
#phone-cards { display: flex; gap: var(--space-4); flex-wrap: wrap; justify-content: center; }
.phone-card {
  width: 148px; min-height: 240px;
  display: grid; grid-template-rows: auto 1fr auto; justify-items: center;
  padding: var(--space-4) var(--space-3);
  border: 2px solid var(--tint); border-radius: 22px;
  background: rgba(10, 13, 10, 0.8);
  box-shadow: 0 0 34px color-mix(in srgb, var(--tint) 34%, transparent),
              inset 0 0 26px color-mix(in srgb, var(--tint) 10%, transparent);
  color: var(--tint);
}
.phone-card .g-wifi { margin: 0 auto; color: var(--tint); }
.phone-card b { font-family: var(--font-display); font-size: var(--text-2xl); font-weight: var(--weight-display); }
.phone-card .state { font-family: var(--font-mono); font-size: 11px; letter-spacing: var(--tracking-label); text-transform: uppercase; }
.phone-card .device { color: var(--text-faint); font-size: var(--text-xs); }
#phone-live-count { margin: 0; color: var(--fg-1); letter-spacing: 0.18em; }
#phone-live-dots { display: flex; gap: var(--space-3); }
/* Four dots, lit for the slots that are in: the row reads n/4 at a glance. */
#phone-live-dots i { width: 11px; height: 11px; border-radius: var(--radius-pill); border: 2px solid var(--line-1); }
#phone-live-dots i.on { background: var(--tint); border-color: var(--tint); box-shadow: var(--glow-lime); }

/* An empty slot is an invitation, not an absence — same card, dashed and dim. */
.phone-card.empty {
  border-style: dashed;
  border-color: var(--line-1);
  box-shadow: none;
  color: var(--text-faint);
}
.phone-card.empty .art { opacity: .45; }
.phone-card.empty b { color: var(--text-muted); }

/* ── Drawn glyphs for the steps and the rows ─────────────────────────────── */

.g-phone {
  display: block; position: relative; width: 16px; height: 24px;
  border: 2px solid currentColor; border-radius: 4px;
}
.g-phone::before {
  content: ''; position: absolute; inset: 2px 0 auto; margin: 0 auto;
  width: 6px; height: 1.5px; background: currentColor; border-radius: 1px;
}
#phone-steps .art { justify-self: center; }
/* Motion, held, swung — the same body, three annotations. */
.g-phone.waves::after {
  content: ''; position: absolute; left: -9px; top: 50%; width: 6px; height: 6px;
  border-left: 2px solid currentColor; border-top: 2px solid currentColor;
  border-radius: 6px 0 0 0; transform: translateY(-50%) rotate(-45deg);
}
.g-phone.hold::after {
  content: ''; position: absolute; left: -7px; bottom: 2px; width: 8px; height: 12px;
  border: 2px solid currentColor; border-right: 0; border-radius: 6px 0 0 6px;
}
.g-phone.swing::after {
  content: ''; position: absolute; inset: -5px -7px -5px -7px;
  border: 2px dashed currentColor; border-radius: 50%; opacity: .55;
}
.g-qr {
  display: block; position: relative; width: 22px; height: 22px;
  border: 2px solid currentColor;
  clip-path: polygon(0 0, 34% 0, 34% 8%, 8% 8%, 8% 34%, 0 34%, 0 100%, 34% 100%, 34% 92%, 8% 92%, 8% 66%, 0 66%,
                     0 0, 100% 0, 100% 34%, 92% 34%, 92% 8%, 66% 8%, 66% 0, 100% 0, 100% 100%, 66% 100%, 66% 92%, 92% 92%, 92% 66%, 100% 66%);
}
.g-qr::after {
  content: ''; position: absolute; inset: 0; margin: auto; width: 7px; height: 7px; background: currentColor;
}

#hand-tips {
  font-size: var(--text-xs);
  line-height: var(--leading-snug);
  margin: var(--space-4) 0 0;
  padding: var(--space-3) var(--space-3) var(--space-3) var(--space-5);
  border: 1px solid var(--line-1);
  background: rgba(19, 23, 19, 0.7);
  color: var(--text-faint);
}
#hand-tips li + li { margin-top: var(--space-2); }
#hand-tips b { color: var(--text-muted); font-weight: var(--weight-medium); }

/* ── Buttons (game-over panel) ───────────────────────────────────────────── */

#panel-over button.primary, button.ghost {
  font-family: var(--font-body);
  font-weight: 600;
  letter-spacing: 0.01em;
  border: 1px solid transparent;
  border-radius: var(--radius-0);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}
#panel-over button.primary {
  display: block;
  width: 100%;
  height: 52px;
  margin-top: var(--space-5);
  padding: 0 28px;
  background: var(--lime);
  color: var(--fg-on-lime);
  font-size: var(--text-base);
}
@media (hover: hover) {
  #panel-over button.primary:hover:not(:disabled) { background: var(--accent-hover); }
}
#panel-over button.primary:active:not(:disabled) { transform: translateY(1px); }
#panel-over button.primary:disabled { opacity: .4; cursor: not-allowed; }
button.ghost {
  height: 32px;
  padding: 0 14px;
  background: transparent;
  color: var(--text-muted);
  font-size: var(--text-sm);
}
@media (hover: hover) {
  button.ghost:hover { background: var(--surface-raised); color: var(--fg-1); }
}
button.ghost.wide {
  display: block;
  width: 100%;
  height: 40px;
  margin-top: var(--space-2);
  border-color: var(--line-1);
  background: var(--surface-raised);
}
@media (hover: hover) {
  button.ghost.wide:hover { background: var(--ink-3); border-color: var(--line-lime); }
}

/* ── Game over ───────────────────────────────────────────────────────────── */

.stats {
  display: flex;
  gap: var(--space-6);
  margin: var(--space-5) 0 var(--space-1);
  padding: var(--space-5) 0;
  border-top: 1px solid var(--line-2);
  border-bottom: 1px solid var(--line-2);
}
.stats div { display: flex; flex-direction: column; gap: var(--space-1); }
.stats b {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: var(--weight-display);
  letter-spacing: var(--tracking-display);
  font-variant-numeric: tabular-nums;
}
.stats small { color: var(--text-faint); }

.keys { margin: var(--space-4) 0 0; color: var(--text-faint); }
kbd {
  padding: 2px 6px;
  border: 1px solid var(--line-1);
  background: var(--surface-raised);
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0;
  color: var(--text-muted);
}
