/* ============================================================================
   Tiny Track Party — "Sticker Bash" theme
   ----------------------------------------------------------------------------
   ONE source of truth for the look. Shared by the display and the controller
   (both <link> this before their own page CSS). To add a new UI element in the
   right style, reach for a TOKEN (a --custom-property) or a KIT class below
   before writing bespoke CSS — that keeps every screen consistent for free.

   The language: die-cut STICKERS on the TV glass. Flat saturated colour on
   warm paper, thick warm-ink outlines (never pure #000), hard UN-BLURRED
   offset shadows, everything slightly rotated as if slapped on by hand.
   Chrome colours are red/green/blue/purple ONLY — yellow/amber and pink are
   liveries, never chrome. Celebration is RED.

   Layout:
     1. @font-face        — self-hosted Fredoka + Nunito (see /assets/fonts)
     2. :root tokens      — palette, type, radii, shadows  (the design system)
     3. base / reset      — box-sizing, default type, .hidden
     4. component kit     — .card .btn .chip .pill .field .dot  (reusable bits)
     5. wordmark          — the one die-cut TINY TRACK / PARTY! badge
     6. paper stage       — .scene__* (grass band + sticker clouds + confetti)

   Pages own their LAYOUT (grid, positioning); the theme owns the LANGUAGE
   (colour, type, surface, shadow). Keep it that way.
   ========================================================================= */

/* 1. Fonts — variable woff2, one file per family, weight interpolated. ----- */
@font-face {
  font-family: 'Fredoka';
  font-style: normal;
  font-weight: 300 700;            /* variable axis */
  font-display: swap;
  src: url('/assets/fonts/fredoka-variable.woff2') format('woff2');
}
@font-face {
  font-family: 'Nunito';
  font-style: normal;
  font-weight: 200 1000;           /* variable axis */
  font-display: swap;
  src: url('/assets/fonts/nunito-variable.woff2') format('woff2');
}

/* 2. Design tokens -------------------------------------------------------- */
:root {
  /* -- car palette (mirrors CAR_COLORS in shared/protocol.js) --------------
     Liveries only — amber/pink/orange/cyan exist HERE and nowhere in chrome. */
  --car-red:    #e6492d;
  --car-amber:  #f2b134;
  --car-green:  #2bb673;
  --car-blue:   #2d9cdb;
  --car-purple: #9b51e0;
  --car-pink:   #eb5e9c;
  --car-orange: #f2784b;
  --car-cyan:   #56ccf2;

  /* -- sticker palette (the 4-colour chrome + paper/ink) ------------------ */
  --paper:  #FFF6EB;             /* full-screen board background              */
  --ink:    #2A2735;             /* outlines, text, dark chips — never #000   */
  --red:    #FF5040;             /* energy, celebration, BRAKE                */
  --green:  #22C46E;             /* go / ready / primary CTA                  */
  --blue:   #339CF2;             /* info                                      */
  --purple: #A259E6;             /* items                                     */

  /* -- semantic roles ---------------------------------------------------- */
  --brand:  var(--green);        /* primary action                            */
  --accent: var(--red);          /* highlights, celebration (was amber — RED) */
  --danger: var(--red);          /* brake / destructive                       */
  --item:   var(--purple);       /* ITEM button, item accents                 */
  --car:    var(--brand);        /* per-player livery (controller sets this)  */

  /* -- connection quality (controller latency chip) ---------------------- */
  --ping-good: var(--green);     /* low latency      */
  --ping-ok:   var(--blue);      /* moderate         */
  --ping-bad:  var(--red);       /* high / no signal */

  /* -- ink (text) -------------------------------------------------------- */
  /* --ink above is headings / primary text                                  */
  --ink-2: #5d5768;              /* body / secondary                          */
  --ink-3: #a49a86;              /* muted / captions / open slots             */

  /* -- surfaces ----------------------------------------------------------- */
  --surface:   #ffffff;          /* sticker cards                             */
  --surface-2: #FBF0DF;          /* sunken fields                             */
  --hairline:  var(--ink);       /* every border is ink now                   */
  --grass:     #A9DF83;          /* the paper stage's grass band              */

  /* -- type -------------------------------------------------------------- */
  --font-display: 'Fredoka', system-ui, sans-serif;   /* everything display-ish */
  --font-body:    'Nunito', system-ui, -apple-system, sans-serif; /* rare running text */

  /* -- radii ------------------------------------------------------------- */
  --r-sm:   12px;
  --r:      16px;
  --r-lg:   22px;
  --r-pill: 999px;

  /* -- shadows (hard offset, ZERO blur — the die-cut drop) -----------------
     ONE rule: offset ≈ 2× the border width, alpha always 0.18 — so every
     sticker reads as stamped by the same machine. Small elements (≤2.5px
     border) take pop, 3px-bordered cards take card. Stage scenery (clouds)
     deliberately runs lighter — it's backdrop, not chrome. */
  --shadow-ink:   rgba(42, 39, 53, 0.18);             /* the one shadow colour */
  --shadow-pop:   3px 3px 0 var(--shadow-ink);        /* chips, small stickers */
  --shadow-card:  6px 6px 0 var(--shadow-ink);        /* big sticker cards     */
  --shadow-float: 6px 6px 0 var(--shadow-ink);        /* alias — same as card  */
  --btn-ledge:    rgba(42, 39, 53, 0.9);              /* the push-button ledge */
  /* Ledge geometry — ONE depth for every push-button in the app (chrome buttons
     AND the big BRAKE/ITEM drive pads), so nothing sits deeper than anything
     else. --btn-sink MUST stay under --btn-drop: press travel equal to the drop
     would punch the button through its own ledge and flatten it. The pressed
     ledge is always the difference, so the two can never drift apart. */
  --btn-drop:     4px;                                /* ledge depth at rest   */
  --btn-sink:     3px;                                /* travel while pressed  */
}

/* 3. Base / reset --------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

body {
  font-family: var(--font-body);
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

.hidden { display: none !important; }

/* 4. Component kit -------------------------------------------------------- */

/* Card — the white sticker panel everything important sits on. */
.card {
  background: var(--surface);
  border: 3px solid var(--ink);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-card);
}

/* Button — chunky sticker push-button: flat colour, ink border, white text,
   hard ink ledge. Set --btn-bg to recolour; pressed = translateY onto the
   ledge. Modifiers below cover the common roles. */
.btn {
  --btn-bg: var(--brand);
  display: inline-block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.15rem;
  line-height: 1.1;
  color: #fff;
  text-align: center;
  border: 3px solid var(--ink);
  border-radius: var(--r);
  padding: 0.8em 1.2em;
  background: var(--btn-bg);
  box-shadow: 0 var(--btn-drop) 0 var(--btn-ledge);
  cursor: pointer;
  transition: transform 0.06s ease, box-shadow 0.06s ease, filter 0.06s ease;
}
.btn:hover { filter: brightness(1.04); }
.btn:active, .btn.is-pressed {
  transform: translateY(var(--btn-sink));
  box-shadow: 0 calc(var(--btn-drop) - var(--btn-sink)) 0 var(--btn-ledge);
}
.btn--brand  { --btn-bg: var(--brand); }
.btn--accent { --btn-bg: var(--accent); }   /* red now — white text stands */
.btn--danger { --btn-bg: var(--danger); }
.btn--item   { --btn-bg: var(--item); }
.btn--ghost  {                     /* quiet, secondary action — same ledge DEPTH
                                      as the primary, just a lighter ink */
  --btn-bg: var(--surface);
  color: var(--ink);
  box-shadow: 0 var(--btn-drop) 0 rgba(42, 39, 53, 0.35);
}
.btn--ghost:active, .btn--ghost.is-pressed {
  box-shadow: 0 calc(var(--btn-drop) - var(--btn-sink)) 0 rgba(42, 39, 53, 0.35);
}
.btn:disabled, .btn[disabled] {       /* e.g. join button while connecting */
  opacity: 0.5; cursor: default; filter: none;
}
.btn:disabled:active { transform: none; box-shadow: 0 var(--btn-drop) 0 var(--btn-ledge); }

/* Icon button — a small square sticker button for a single glyph (e.g. pause). */
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 3rem; height: 3rem; padding: 0;
  color: var(--ink);
  background: var(--surface);
  border: 3px solid var(--ink);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-pop);
  cursor: pointer;
  transition: transform 0.06s ease, box-shadow 0.06s ease, filter 0.06s ease;
}
.icon-btn:hover:not(:disabled) { filter: brightness(1.03); }
.icon-btn:active:not(:disabled) { transform: translate(2px, 2px); box-shadow: none; }
.icon-btn:disabled { opacity: 0.5; cursor: default; }

/* Pause glyph — two rounded bars, sized off the button's text colour. */
.pause-glyph { display: flex; gap: 4px; }
.pause-glyph::before, .pause-glyph::after {
  content: ''; width: 4px; height: 16px; border-radius: 2px; background: currentColor;
}

/* Fullscreen glyph — four warm-ink corner brackets, drawn in the same weight as
   the pause bars. The button carries both (enter/exit) and shows one, picked by
   aria-pressed so the visual and the a11y state can never drift apart. */
.fs-glyph {
  width: 20px; height: 20px;
  fill: none; stroke: currentColor; stroke-width: 2.6;
  stroke-linecap: round; stroke-linejoin: round;
}
[aria-pressed="false"] .fs-glyph--exit, [aria-pressed="true"] .fs-glyph--enter { display: none; }

/* Chip — a small white sticker token with a colour dot (player in a roster). */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  background: var(--surface);
  border: 3px solid var(--ink);
  border-radius: var(--r-pill);
  padding: 0.45rem 0.95rem 0.45rem 0.6rem;
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--ink);
  box-shadow: var(--shadow-pop);
}
.chip--off { opacity: 0.45; }

/* Pill — an ink label sticker: white uppercase caps on an ink pill
   (e.g. "JOIN AT", "LAP 2/3"). */
.pill {
  display: inline-block;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #fff;
  background: var(--ink);
  border-radius: var(--r-pill);
  padding: 0.32em 0.95em 0.38em;
}

/* Field — a sticker input/value box. */
.field {
  background: var(--surface);
  border: 3px solid var(--ink);
  border-radius: var(--r-sm);
  padding: 0.85rem 1rem;
  color: var(--ink);
  font-weight: 700;
  box-shadow: var(--shadow-pop);
}

/* Track schematic (built by schematicSvg in shared/trackPicker.js) — a field
   with the toy road ribbon. Shared by the phone's track picker and the
   display's lobby cup slot; the container sizes it. */
.track-map { width: 100%; aspect-ratio: 1 / 1; display: block; border-radius: var(--r-sm); background: color-mix(in srgb, var(--brand), #fff 78%); }
/* stroke-widths are in viewBox units; the schematic viewBox is 0 0 256 256 (see
   trackSchematic VIEW) so these are ~2.56× their old 0-100 values. */
.track-map__casing { fill: none; stroke: #fff; stroke-width: 23; stroke-linejoin: round; stroke-linecap: round; }
.track-map__road { fill: none; stroke: var(--ink-2); stroke-width: 14; stroke-linejoin: round; stroke-linecap: round; }
.track-map__start { fill: var(--danger); stroke: #fff; stroke-width: 4; }

/* Dot — a livery colour swatch; set its background inline from CAR_COLORS. */
.dot {
  width: 0.85rem;
  height: 0.85rem;
  border-radius: 50%;
  border: 2px solid var(--ink);
  flex: none;
}

/* Car thumbnail (see /shared/carThumbs.js) — a box showing a pre-baked render.
   The container sizes it (width:100%); aspect-ratio reserves the box before
   images load so nothing below jumps. In spin mode an overlay sprites the
   turntable strip, stepped by a shared clock (so all cars stay in sync).
   5:4 MATCHES the baked frames (256×205, see scripts/capture-car-thumbs.js) —
   these cars are far longer than they are tall, so a square box was mostly air
   above the roof. The two must stay in step: the stills are object-fit:contain,
   so a box that disagrees with the asset letterboxes it and shrinks the car. */
.carthumb { position: relative; width: 100%; aspect-ratio: 5 / 4; }
.carthumb__still {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; display: block; transition: opacity 0.2s ease;
}
.carthumb__spin {
  position: absolute; inset: 0;
  background-repeat: no-repeat;
  background-size: 2400% 100%;   /* SPIN_FRAMES (24) * 100% — keep in sync with carThumbs.js */
  background-position: 0 0;
}

/* 5. Wordmark — ONE die-cut badge (never a row of pills, never plain text):
     <div class="wordmark"><span>TINY TRACK</span><span class="l2">PARTY!</span></div>
   The white text-stroke is the die-cut edge; without paint-order support it
   degrades to plain ink text — acceptable. Size by context via font-size. */
.wordmark {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 0.98;
  color: var(--ink);
  letter-spacing: 0.01em;
  -webkit-text-stroke: 7px #fff;
  paint-order: stroke fill;      /* white die-cut edge */
  transform: rotate(-2deg);
  filter: drop-shadow(3px 3px 0 var(--shadow-ink));
}
.wordmark span { display: block; }
.wordmark .l2 { color: var(--red); font-size: 1.24em; }

/* 6. Paper stage ----------------------------------------------------------
   Drop a <div class="scene"> with these children behind any full-screen board
   for the Sticker Bash backdrop: warm paper, a flat grass band, two sticker
   clouds and a few confetti rects. Decorative only — it never eats pointer
   events and sits at the bottom of the stacking order. NO SUN (it was yellow). */
.scene { position: absolute; inset: 0; overflow: hidden; z-index: 0; pointer-events: none; }

.scene__sky {
  position: absolute; inset: 0;
  /* a soft warm glow high in the sky gives the paper some air */
  background:
    radial-gradient(120% 90% at 50% -20%, #fffdf7 0%, rgba(255, 253, 247, 0) 55%),
    var(--paper);
}
/* rolling hills peeking over the grass band — two soft bumps for depth */
.scene__sky::before, .scene__sky::after {
  content: ''; position: absolute; border-radius: 50%;
}
.scene__sky::before { width: 74%; height: 30%; left: -16%; bottom: 11%; background: #d3edbb; }
.scene__sky::after  { width: 82%; height: 36%; right: -20%; bottom: 9%; background: #e0f3cd; }

.scene__sun { display: none; }   /* retired — the sun was yellow */

/* Sticker cloud — a white outlined pill with two outlined circle humps. The
   humps are clipped to their top 60% (clip-path) so their cut edge hides
   inside the pill's white body and the outline reads as one die-cut shape. */
.scene__cloud {
  position: absolute; background: #fff;
  border: 3px solid var(--ink); border-radius: var(--r-pill);
  box-shadow: 4px 4px 0 rgba(42, 39, 53, 0.10);
}
.scene__cloud::before, .scene__cloud::after {
  content: ''; position: absolute; background: #fff;
  border: 3px solid var(--ink); border-radius: 50%;
  clip-path: inset(0 0 40% 0);
}
/* positioned clear of the display lobby's rails (ticket left, cup slot right)
   and the centre tagline */
.scene__cloud--a { top: 9%; left: 34%; width: 150px; height: 42px; transform: rotate(-2deg); }
.scene__cloud--a::before { width: 62px; height: 62px; top: -26px; left: 24px; }
.scene__cloud--a::after  { width: 46px; height: 46px; top: -16px; left: 82px; }
.scene__cloud--b { top: 20%; right: 23%; width: 104px; height: 30px; transform: rotate(2deg); }
.scene__cloud--b::before { width: 44px; height: 44px; top: -18px; left: 18px; }
.scene__cloud--b::after  { width: 34px; height: 34px; top: -11px; left: 54px; }
.scene__cloud--c { top: 36%; right: 9%; width: 84px; height: 26px; transform: rotate(-2deg); }
.scene__cloud--c::before { width: 36px; height: 36px; top: -15px; left: 14px; }
.scene__cloud--c::after  { width: 28px; height: 28px; top: -9px; left: 44px; }

/* Flat grass band, gently rounded top — stage scenery, no outline (outlines
   belong to the stickers sitting ON the stage). */
.scene__grass {
  position: absolute; left: -2%; right: -2%; bottom: 0; height: 24%;
  background: var(--grass);
  border-radius: 50% 50% 0 0 / 22% 22% 0 0;
}
/* Confetti rect for explicit placement — position + tint per instance:
     <i class="scene__confetti" style="top:20%;left:70%;--c:var(--green);--tilt:-20deg"></i> */
.scene__confetti {
  position: absolute; width: 9px; height: 15px; border-radius: 3px;
  background: var(--c, var(--red));
  transform: rotate(var(--tilt, 0deg));
}

/* 7. Motion — the sticker SLAP-IN -----------------------------------------
   Scale overshoot with a slight rotate wobble, ~240ms. Uses the independent
   scale/rotate properties so it composes with any static transform the
   element already carries (tilts, centring translates). Applied by page CSS
   on screen entry (display:none → shown restarts CSS animations for free);
   JS re-triggers it for same-element content swaps (countdown digits).
   Reduced motion: the animation simply never exists. */
@media (prefers-reduced-motion: no-preference) {
  @keyframes sticker-slap {
    0%   { scale: 1.15; rotate: 2deg; opacity: 0; }
    60%  { scale: 0.96; rotate: -0.6deg; opacity: 1; }
    100% { scale: 1; rotate: 0deg; }
  }
  .slap { animation: sticker-slap 240ms cubic-bezier(0.2, 1.5, 0.35, 1) both; }
}
