/* @module  public/assets/css/app.css
   @purpose App-level surface styling that sits ON TOP of the jotf bricks (never edits them):
            paints the page from the design tokens so the .dark Big Board is full-bleed dark on
            a projector, and gives each surface basic layout/rhythm. Uses var(--…) tokens only —
            no raw values — so it re-themes with the kit.
   @layer   Presentation */

html,
body {
  margin: 0;
  min-height: 100%;
}

body {
  background: var(--color-background);
  color: var(--color-foreground);
  font-family: var(--font-sans, "Satoshi", system-ui, -apple-system, sans-serif);
  line-height: 1.5;
}

/* The `hidden` attribute must actually hide — some jotf bricks set display:flex/grid, which
   would otherwise override the UA `[hidden]{display:none}` and keep JS-hidden blocks visible
   (e.g. the Buzzer's qr-join/open-team-list/team-fork after a device joins a team). */
[hidden] {
  display: none !important;
}

/* ── Big Board: projected, spacious, centered ─────────────────────────────── */
.board__main {
  max-width: 1100px;
  margin: 0 auto;
  padding: clamp(1.5rem, 4vw, 3rem);
}
.board__main h1 {
  font-family: var(--font-display, "Clash Display", var(--font-sans, sans-serif));
  text-align: center;
  margin: 0 0 2rem;
}

/* ── Big Board answer grid: covered slots that FLIP to reveal (the money moment) ─── */
.board__answers {
  list-style: none;
  margin: 0 0 2.5rem;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 1rem;
}
.board__answer {
  perspective: 900px;
  min-height: 5rem;
}
.board__answer-inner {
  position: relative;
  width: 100%;
  height: 100%;
  min-height: inherit;
  transform-style: preserve-3d;
  transition: transform 0.6s cubic-bezier(0.2, 0.8, 0.25, 1);
}
.board__answer--found .board__answer-inner {
  transform: rotateX(180deg);
}
.board__answer-face {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  padding: 0.75rem;
  border-radius: var(--radius-card, 0.75rem);
  border: 1px solid var(--color-border, #333);
  backface-visibility: hidden;
  text-align: center;
}
.board__answer-face--cover {
  background: var(--color-surface-2, #1b1b1b);
  color: var(--color-muted-foreground, #999);
}
.board__answer-num {
  font-family: var(--font-display, var(--font-sans, sans-serif));
  font-size: 2rem;
  font-weight: 700;
}
.board__answer-face--front {
  background: var(--color-primary, #b8860b);
  color: var(--color-primary-foreground, #111);
  transform: rotateX(180deg);   /* pre-flipped so it faces out once the card rotates */
}
.board__answer-label {
  font-weight: 700;
  font-size: 1.05rem;
  line-height: 1.2;
}
.board__answer-points {
  font-family: var(--font-display, var(--font-sans, sans-serif));
  font-size: 1.6rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;   /* mono numerals — no jitter while counting up */
  color: var(--color-accent, var(--color-primary-foreground, #111));
}
/* The find flourish — a quick pop as a slot opens; a bigger one for the obscure top answer. */
@keyframes board-answer-pop {
  0% { transform: scale(1); }
  45% { transform: scale(1.09); }
  100% { transform: scale(1); }
}
@keyframes board-answer-pop-big {
  0% { transform: scale(1); }
  30% { transform: scale(1.2); }
  60% { transform: scale(1.05); }
  100% { transform: scale(1); }
}
.board__answer--pop { animation: board-answer-pop 0.5s ease-out; }
.board__answer--pop-big { animation: board-answer-pop-big 0.9s ease-out; }
/* Reduced motion: the slot still FLIPS STATE (front face shows) — just no spin or pop. */
@media (prefers-reduced-motion: reduce) {
  .board__answer-inner { transition: none; }
  .board__answer--pop,
  .board__answer--pop-big { animation: none; }
}

/* ── Show HUD: the shot clock + mute, shared by the Big Board and the Buzzer ─────── */
.show-hud {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  margin: 0 0 1.5rem;
}
.turn-clock {
  display: inline-flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.4rem 0.9rem;
  border: 1px solid var(--color-border, #333);
  border-radius: var(--radius-pill, 999px);
  background: var(--color-surface-2, #1b1b1b);
}
.turn-clock__label {
  font-size: 0.8rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-muted-foreground, #999);
}
.turn-clock__value {
  font-family: var(--font-display, var(--font-sans, sans-serif));
  font-size: 1.6rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;   /* mono digits — no width jitter as it ticks */
}
/* Final seconds: turn urgent + pulse. --expired holds the urgent colour without pulsing. */
.turn-clock--warning { border-color: var(--color-accent, #e0245e); }
.turn-clock--warning .turn-clock__value,
.turn-clock--expired .turn-clock__value { color: var(--color-accent, #e0245e); }
.turn-clock--warning { animation: turn-clock-pulse 1s ease-in-out infinite; }
@keyframes turn-clock-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.06); }
}
.turn-clock__mute {
  cursor: pointer;
  font-size: 1.2rem;
  line-height: 1;
  padding: 0.4rem 0.6rem;
  border: 1px solid var(--color-border, #333);
  border-radius: var(--radius-pill, 999px);
  background: var(--color-surface-2, #1b1b1b);
  color: inherit;
}
.turn-clock__mute[aria-pressed="true"] { opacity: 0.55; }
@media (prefers-reduced-motion: reduce) {
  .turn-clock--warning { animation: none; }   /* keep the urgent colour, drop the pulse */
}

/* ── Buzzer round context: the header (theme + prompt) + the waiting answer-board ─── */
.buzzer__round { margin-bottom: 0.75rem; }
.buzzer__round-num {
  display: block;
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-muted-foreground, #999);
}
.buzzer__round-title {
  display: block;
  font-family: var(--font-display, var(--font-sans, sans-serif));
  font-size: 1.3rem;
  font-weight: 700;
}
.buzzer__round-prompt { margin: 0.25rem 0 0; color: var(--color-foreground); }
/* The waiting board — one covered box per answer; found ones open to their label. */
.buzzer__waiting { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0 0 0.75rem; }
.buzzer__box {
  min-width: 3rem;
  min-height: 2.2rem;
  padding: 0.3rem 0.6rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-input, 0.4rem);
  background: var(--color-surface-2, #eee);
  color: var(--color-muted-foreground, #999);
  letter-spacing: 0.15em;
  font-size: 0.85rem;
}
.buzzer__box--found {
  background: var(--color-primary, #b8860b);
  color: var(--color-primary-foreground, #111);
  font-weight: 700;
  letter-spacing: normal;
}

/* ── Buzzer: single-column phone width ────────────────────────────────────── */
.buzzer__main {
  max-width: 460px;
  margin: 0 auto;
  padding: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

/* On your turn the Buzzer collapses to the one input — accent it so it can't be missed. */
.buzzer--my-turn .buzzer__answer {
  border: 2px solid var(--color-accent, #e0245e);
  border-radius: var(--radius-card, 0.75rem);
  padding: 1rem;
  background: var(--color-surface-2, #1b1b1b);
}
.buzzer--my-turn .buzzer__answer .form-field__control {
  font-size: 1.3rem;
}

/* ── Facilitator header: brand the vendored nav-bar + give it a centered gutter ─── */
.facil-header .nav-bar__inner {
  max-width: 1100px;
  margin: 0 auto;
  padding-inline: clamp(1rem, 4vw, 1.5rem);
}
.facil-header__logo { height: 26px; width: auto; display: block; }
.facil-header .nav-bar__drawer-header .facil-header__logo { height: 22px; }
/* Page heading below the header — the section title + any live status pill (inside .*__main). */
.facil-pagehead {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-bottom: 1rem;
}
.facil-pagehead__title {
  margin: 0;
  font-family: var(--font-headline, var(--font-display, sans-serif));
  font-size: var(--text-h3, 1.5rem);
  color: var(--color-foreground);
}

/* ── Map-prep console: import → curate → publish ──────────────────────────── */
.mapprep__main {
  max-width: 900px;
  margin: 0 auto;
  padding: 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}
.mapprep__domain {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--color-border, #333);
}
.mapprep__domain--off { opacity: 0.5; }
.mapprep__domain .form-field__control { flex: 1; min-width: 8rem; }
.mapprep__domain-actions { display: flex; gap: 0.25rem; flex-wrap: wrap; }
.mapprep__answers { flex-basis: 100%; padding: 0.5rem 0 0.5rem 2rem; }
.mapprep__answer {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.25rem 0;
}
.mapprep__answer--off { opacity: 0.5; }
.mapprep__answer-label { flex: 1; min-width: 6rem; }
.mapprep__answer-points { flex: 0 0 auto; width: 5rem; }

/* ── Host cue card: the round's full answer set with points, found vs still-open ─── */
.cue-card__prompt { margin: 0 0 0.75rem; color: var(--color-muted-foreground, #666); font-style: italic; }
.cue-card__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.3rem; }
.cue-card__row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.45rem 0.7rem;
  border-radius: var(--radius-input, 0.4rem);
  background: var(--color-surface-2, #f4f4f5);
}
.cue-card__row--found { opacity: 0.5; }         /* answered — dimmed so open ones stand out */
.cue-card__mark { width: 1.2em; text-align: center; font-weight: 700; color: var(--color-primary, #b8860b); }
.cue-card__label { flex: 1; }
.cue-card__points {
  font-family: var(--font-display, var(--font-sans, sans-serif));
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* ── Producer's Desk: roomy control layout ────────────────────────────────── */
.desk__main {
  max-width: 900px;
  margin: 0 auto;
  padding: 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

/* ── Constellation Big Board: the music universe forming (P14) ──────────────── */
/* Baked star map; unrevealed stars are dim + nameless, revealed ignite to a glow. */
.constellation {
  position: relative;
  width: 100%;
  aspect-ratio: 10 / 7;
  max-height: 78vh;
  margin: 0 auto 1.25rem;
  background: radial-gradient(ellipse at 50% 40%, #10131c 0%, #070810 70%, #04050b 100%);
  border-radius: 12px;
  overflow: hidden;
}
.constellation__svg { display: block; width: 100%; height: 100%; }

/* An unrevealed node: a small dim, nameless point that softly twinkles. */
.constellation__star {
  opacity: 0.18;
  transform-origin: center;
  transition: opacity 480ms ease, transform 480ms cubic-bezier(0.34, 1.56, 0.64, 1);
  filter: none;
  animation: constellation-twinkle 3.2s ease-in-out infinite;
  animation-delay: var(--twinkle-delay, 0s);
}
/* Ignited: full glow + a small pop (the ignite crossfade lands within ~300–600ms). */
.constellation__node--lit .constellation__star {
  opacity: 1;
  transform: scale(1.18);
  animation: none;
  filter: drop-shadow(0 0 5px currentColor) drop-shadow(0 0 11px currentColor);
}

/* The constellation is the PRIMARY Big-Board view (P14) — it supersedes the P8 covered-slot grid,
   which stays server-rendered (spoiler-safe) but is not shown here; standings stay as a secondary
   panel below. */
.board .board__answers { display: none; }

/* Labels appear only on ignite (the endpoint redacts unfound names anyway). */
.constellation__label {
  fill: #eef1f7;
  font: 600 12px var(--font-sans, sans-serif);
  opacity: 0;
  paint-order: stroke;
  stroke: #04050b;
  stroke-width: 2.5px;
  transition: opacity 520ms ease;
  pointer-events: none;
}
.constellation__node--lit .constellation__label { opacity: 0.92; }
/* REQ-4: a lower-value label the greedy pass could not place without overlap is hidden (the obscure,
   high-point discoveries win the space; the crowded hub name yields). */
.constellation__label--collided { opacity: 0 !important; }

/* Edges fan out from lit stars (P17, REQ-3). Two tiers: a COMPLETE edge (both endpoints lit) is a
   known relationship — solid + bright so it reads even next to a big hub (REQ-2); a HINT edge (one
   endpoint lit) is a breadcrumb toward a still-hidden sibling — fainter + dashed, distinct from a
   completed link. Base is invisible; the tier class fades it in. */
.constellation__edge {
  stroke: #6a8dff;
  stroke-width: 1;
  opacity: 0;
  transition: opacity 620ms ease;
}
/* REQ-2: a completed relationship must be unmistakable even when a big hub disk sits almost on top
   of a small leaf — brighter + rounded, with a dark drop-shadow CASING so the run reads against both
   the sky and the star disks it passes under (edges render beneath the nodes). */
.constellation__edge--complete {
  opacity: 0.7;
  stroke: #9db4ff;
  stroke-width: 1.8;
  stroke-linecap: round;
  filter: drop-shadow(0 0 1.4px #04050b);
}
.constellation__edge--hint {
  opacity: 0.24;
  stroke: #7d8ba8;
  stroke-dasharray: 3 5;
}
.constellation__edge--lit { opacity: 0.22; }   /* legacy alias (pre-P17); kept for safety */

@keyframes constellation-twinkle {
  0%, 100% { opacity: 0.10; }
  50%      { opacity: 0.26; }
}
/* Respect reduced-motion: snap to state, no twinkle, no crossfade. */
@media (prefers-reduced-motion: reduce) {
  .constellation__star,
  .constellation__label,
  .constellation__edge { transition: none; animation: none; }
}

/* ── QR frames ─────────────────────────────────────────────────────────────── */
/* qr-paint.js fills any [data-qr] frame with an <img>. A QR is pixel art, so scale it
   hard-edged rather than smoothed — a smoothed code degrades on a projector and can
   fail to scan. Global (not picker-scoped) because the Buzzer's qr-join uses it too. */
[data-qr] img { display: block; width: 100%; height: 100%; image-rendering: pixelated; }
