:root {
  /* Warm candy palette — game, not console. */
  --bg: #ffe9b8;
  --bg-deep: #ffd689;
  --panel: #fffdf4;
  --panel-2: #fff3d1;
  --border: #f0cf7d;
  --border-strong: #e0b457;
  --text: #5a4320;
  --muted: #a38958;
  --accent: #ff9f1c;
  --accent-deep: #f28a00;
  --you: #4faf63;
  --agent: #ff6b4a;
  --danger: #e8564f;
  --tile-shadow: rgba(120, 78, 20, 0.30);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  /* 2026-09-19: the Phaser shell was ported without the generated wide backdrop
     (assets/cat/backdrop_wide.webp — soft-clay cat garden, deliberately
     low-contrast with an empty centre for the board). web/style.css has always
     carried it; here the wash was opaque, so the page read as flat cream and
     the art looked "lost". Restored with the legacy recipe: the warm wash drops
     to ~0.6 alpha so the backdrop shows through and the board stays the hero. */
  background:
    radial-gradient(1200px 600px at 15% -10%, rgba(255, 255, 255, 0.55), transparent 60%),
    radial-gradient(1000px 500px at 100% 0%, rgba(255, 196, 112, 0.45), transparent 55%),
    linear-gradient(160deg, rgba(255, 233, 184, 0.62) 0%, rgba(255, 214, 137, 0.58) 100%),
    url("assets/cat/backdrop_wide.webp") center / cover no-repeat;
  background-color: var(--bg-deep);
  background-attachment: fixed;
  color: var(--text);
  font-family: "Segoe UI Rounded", "Nunito", "Varela Round", system-ui, "Segoe UI Emoji", "Noto Color Emoji", "Apple Color Emoji", sans-serif;
}

/* Floating soft candy blobs (decorative, behind everything, non-interactive). */
body::before {
  content: "";
  position: fixed;
  inset: -40px;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(circle 90px at 8% 24%, rgba(255, 255, 255, 0.55), transparent 70%),
    radial-gradient(circle 140px at 22% 70%, rgba(255, 170, 90, 0.35), transparent 70%),
    radial-gradient(circle 100px at 45% 18%, rgba(255, 255, 255, 0.4), transparent 70%),
    radial-gradient(circle 170px at 68% 78%, rgba(255, 130, 80, 0.30), transparent 70%),
    radial-gradient(circle 120px at 86% 32%, rgba(255, 255, 255, 0.5), transparent 70%),
    radial-gradient(circle 90px at 94% 64%, rgba(255, 190, 70, 0.35), transparent 70%);
  animation: blobsDrift 26s ease-in-out infinite alternate;
}
@keyframes blobsDrift {
  from { transform: translate(0, 0) rotate(0deg); }
  to   { transform: translate(18px, -14px) rotate(2deg); }
}

/* Grassy hill silhouette along the bottom edge. */
body::after {
  content: "";
  position: fixed;
  left: -8%;
  right: -8%;
  bottom: -90px;
  height: 180px;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(ellipse 55% 120px at 20% 100%, #8fd46a 0 62%, transparent 63%),
    radial-gradient(ellipse 60% 140px at 55% 100%, #6fbf52 0 64%, transparent 65%),
    radial-gradient(ellipse 55% 120px at 85% 100%, #9fdb7a 0 60%, transparent 61%);
  filter: drop-shadow(0 -2px 0 rgba(255, 255, 255, 0.35));
}

header {
  padding: 16px 24px;
  border-bottom: 2px solid var(--border);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.55), rgba(255, 255, 255, 0));
  display: flex;
  flex-wrap: wrap;
  gap: 12px 24px;
  align-items: flex-end;
  justify-content: space-between;
}
.brand h1 {
  margin: 0;
  font-size: 32px;
  font-weight: 900;
  letter-spacing: 0.03em;
  line-height: 1.1;
  background: linear-gradient(180deg, #ffd166 0%, #ff9f1c 42%, #f26d1f 72%, #e0522a 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  -webkit-text-stroke: 1.5px #9c5a12;
  filter: drop-shadow(0 2px 0 #fff8e6) drop-shadow(0 5px 10px rgba(190, 115, 30, 0.28));
}
.tagline { color: var(--muted); margin: 4px 0 0; font-size: 14px; max-width: 420px; }

/* Daily ladder rail: current level = green ball, cleared = ✓, locked = dim. */
#progress-rail {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: 1 1 100%;
  justify-content: flex-start;
  /* 50 rungs don't fit any viewport: scroll horizontally (hud.js keeps the
     current rung scrolled into view). */
  overflow-x: auto;
  scrollbar-width: thin;
}
.rail-dot {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 2px solid var(--border-strong);
  background: #2b2b31;
  color: #fff;
  font-weight: 800;
  font-size: 14px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease, background 0.12s ease;
}
.rail-dot.done { color: #6bd56b; border-color: #6bd56b; }
.rail-dot.current {
  background: radial-gradient(circle at 35% 30%, #8ce06a, #4fbf3f 60%, #2f9d33);
  border-color: #2f8a2c;
  box-shadow: 0 0 0 3px rgba(88, 200, 74, 0.25), 0 4px 10px rgba(40, 120, 30, 0.3);
  transform: translateY(-1px) scale(1.06);
}
.rail-dot:disabled { opacity: 0.5; cursor: not-allowed; }
.rail-seg { width: 18px; height: 3px; border-radius: 3px; background: var(--border); }
.rail-seg.done { background: #6bd56b; }


#controls { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.sound-btn {
  width: 36px;
  height: 36px;
  border-radius: 12px;
  border: 2px solid var(--border);
  background: var(--panel);
  color: var(--text);
  font-size: 16px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
  box-shadow: 0 2px 0 var(--border-strong);
  transition: transform 0.08s ease, filter 0.08s ease, background 0.12s ease;
}
.sound-btn:hover {
  transform: translateY(-2px);
  filter: brightness(1.06);
}
.sound-btn:active {
  transform: translateY(0);
}
.sound-btn.muted {
  opacity: 0.65;
  filter: grayscale(0.7);
  background: #f0e6d2;
}
#controls select, #controls input {
  background: var(--panel);
  color: var(--text);
  border: 2px solid var(--border);
  border-radius: 12px;
  padding: 7px 12px;
  font-weight: 600;
  box-shadow: 0 2px 0 var(--border-strong);
}
button {
  background: linear-gradient(180deg, #ffb84d, var(--accent));
  color: #5a2c00;
  border: none;
  border-radius: 12px;
  padding: 7px 16px;
  cursor: pointer;
  font-size: 14px;
  font-weight: 800;
  box-shadow: 0 3px 0 var(--accent-deep), 0 5px 12px rgba(240, 140, 0, 0.28);
  transition: transform 0.06s ease, box-shadow 0.06s ease, filter 0.1s ease;
}
button:hover:not(:disabled) { filter: brightness(1.05); }
button:active:not(:disabled) { transform: translateY(2px); box-shadow: 0 1px 0 var(--accent-deep); }
button:disabled { opacity: 0.45; cursor: not-allowed; box-shadow: none; }
button.primary { background: linear-gradient(180deg, #ffc24d, #ff9f1c); color: #5a2c00; font-weight: 800; }
#match-info { color: #8a6a35; font-size: 13px; font-weight: 700; }

/* Layout: CSS grid with named areas.
   Desktop: board on the left (spans rows), right column = leaderboard + tray,
   actions as a full-width row at the bottom. The board column hugs the board
   and the whole pair is centred, so the two columns sit a single grid gap
   apart instead of drifting across a wide 1fr track.
   Phone:  single column, stacked leaderboard -> board -> tray -> actions. */
main {
  display: grid;
  gap: 14px;
  padding: 20px 24px;
  align-items: start;
  justify-content: center;
  /* Side column flexes with the viewport (Poki 16:9 embeds) — a fixed 340px
     track starved the board column on 836×470. */
  grid-template-columns: auto minmax(200px, min(340px, 26vw));
  grid-template-areas:
    "board side"
    "board tray";
}
#board-wrap { grid-area: board; }
#side { grid-area: side; }
#tray { grid-area: tray; position: relative; }

#board-wrap {
  background: linear-gradient(165deg, #fff7e3, #ffe9bd);
  border: 2px solid var(--border-strong);
  border-radius: 18px;
  padding: 14px;
  overflow: auto;
  /* Shrink-wrap the panel to the board so there is no dead whitespace on
     either side (the board width itself is driven by the grid track, so this
     cannot feed back into the sizing loop). */
  width: max-content;
  max-width: 100%;
  justify-self: center;
  box-shadow: inset 0 2px 0 rgba(255, 255, 255, 0.7), 0 10px 24px rgba(200, 130, 30, 0.18);
}
#board {
  position: relative;
  margin: 0 auto;
}

button.tile.abs {
  position: absolute;
  margin: 0;
  padding: 0;
  border-radius: 12px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid rgba(120, 70, 15, 0.28);
  box-shadow:
    0 3px 0 var(--tile-shadow),
    inset 0 2px 0 rgba(255, 255, 255, 0.45);
  transition: transform 0.08s ease, filter 0.08s ease, outline 0.08s ease;
}
button.tile.abs.free {
  cursor: pointer;
  outline: 3px solid rgba(255, 190, 60, 0.95);
  outline-offset: -1px;
  box-shadow:
    0 3px 0 var(--tile-shadow),
    0 6px 12px rgba(120, 78, 20, 0.22),
    inset 0 2px 0 rgba(255, 255, 255, 0.5);
}
button.tile.abs.free:hover {
  transform: translateY(-3px) scale(1.05);
  filter: brightness(1.08) saturate(1.05);
  z-index: 100 !important;
  box-shadow:
    0 5px 0 var(--tile-shadow),
    0 10px 18px rgba(120, 78, 20, 0.28),
    inset 0 2px 0 rgba(255, 255, 255, 0.6);
}
button.tile.abs.covered {
  cursor: not-allowed;
  /* Dimming alone signals "not clickable" — free tiles already carry a bright
     orange outline, so covered tiles must not be desaturated as well. Stacking
     brightness + saturate + a brown scrim turned the full-colour clay figures
     into grey mud, and covered tiles are most of the board. */
  filter: brightness(0.9);
  opacity: 1;
}
button.tile.abs.covered::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Neutral, low-alpha scrim: reads as "behind glass" without tinting the art. */
  background: rgba(70, 50, 26, 0.2);
  border-radius: inherit;
  pointer-events: none;
}
button.tile.abs.suggested {
  outline: 4px solid #ff5a4d !important;
  outline-offset: 1px;
  animation: pulse 1s ease-in-out infinite;
  z-index: 120 !important;
}

/* White card-back RULER (side decks): a fixed stack of N-1 edges anchored
   at the tower base cell, climbing upward. The face tile rises through it
   as layers clear; the ruler itself NEVER moves or reacts to hover. */
.deck-ruler {
  position: absolute;
  pointer-events: none;
  z-index: 190;
}
/* Physical card-back stack. Opaque container with individual flex-distributed
   card slabs (.deck-slab), perfectly regular on any DPI display, distinctly
   colored so the stack reads unmistakably as card backs, not background. */
.deck-stack {
  position: absolute;
  box-sizing: border-box;
  pointer-events: none;
  z-index: 190;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border-radius: 9px 9px 2px 2px;
  border: 2px solid #2e4026;
  background: #475a3c;
  box-shadow:
    0 4px 8px rgba(40, 55, 30, 0.32),
    inset 0 1px 0 rgba(255, 255, 255, 0.3);
}

.deck-slab {
  flex: 1 1 0;
  min-height: 0;
  box-sizing: border-box;
  background: linear-gradient(180deg, #6c885c 0%, #566e4a 60%, #465a3c 100%);
  border-top: 1px solid rgba(255, 255, 255, 0.36);
  border-bottom: 1px solid rgba(24, 36, 18, 0.7);
}

.deck-slab.alt {
  background: linear-gradient(180deg, #658156 0%, #4f6744 60%, #405336 100%);
}

.deck-slab:first-child {
  border-top: none;
}

.deck-slab:last-child {
  border-bottom: none;
}
/* Deck faces must NOT scale/translate on hover — the ruler is fixed and
   the face must stay aligned with its slot. And they must stay ABOVE the
   ruler (z ≥ 200) so the climbing face is never covered by edges. */
button.tile.abs.deck.free:hover {
  transform: none;
  filter: brightness(1.12);
  z-index: 300 !important;
}


#side {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.panel {
  background: linear-gradient(180deg, #fffef8, var(--panel-2));
  border: 2px solid var(--border);
  border-radius: 16px;
  padding: 14px;
  box-shadow: 0 3px 0 var(--border-strong), 0 8px 18px rgba(200, 130, 30, 0.12);
}
.panel h2 {
  margin: 0 0 10px;
  font-size: 15px;
  color: #9c6a1a;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-weight: 900;
}

/* Progress race */
.race-row {
  display: grid;
  grid-template-columns: 48px 1fr auto;
  gap: 8px;
  align-items: center;
  margin-bottom: 8px;
}
.race-label { font-size: 14px; color: #8a6a35; font-weight: 800; white-space: nowrap; }
.race-pct { font-size: 13px; color: var(--text); min-width: 88px; text-align: right; font-weight: 700; font-variant-numeric: tabular-nums; }
.bar-track {
  height: 12px;
  background: #f3dfb2;
  border-radius: 999px;
  overflow: hidden;
  border: 2px solid var(--border);
  box-shadow: inset 0 1px 2px rgba(150, 100, 30, 0.2);
}
.bar {
  height: 100%;
  width: 0%;
  border-radius: 999px;
  transition: width 0.25s ease;
}
.bar.you { background: linear-gradient(90deg, #3fae57, var(--you)); }
.bar.agent { background: linear-gradient(90deg, #ff8a4d, var(--agent)); }
.race.lead-you { border-color: rgba(79, 175, 99, 0.5); }
.race.lead-agent { border-color: rgba(255, 107, 74, 0.5); }

/* Tray slots are tighter and larger than before: the tray is where each card
   is biggest on screen, so the clay characters can actually show detail there.
   The variable lives on :root because the .flyer clone is appended to <body>
   and still needs to land exactly on its slot. */
:root { --tray-slot: 54px; }
@media (max-width: 720px) {
  :root { --tray-slot: 52px; }
}
#tray-slots {
  display: flex;
  gap: 2px;
  min-height: calc(var(--tray-slot) + 4px);
  flex-wrap: wrap;
}
.slot {
  width: var(--tray-slot);
  height: var(--tray-slot);
  border-radius: 13px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  border: 2px dashed var(--border-strong);
  background: rgba(255, 255, 255, 0.5);
  box-shadow: inset 0 1px 2px rgba(150, 100, 30, 0.12);
}
.slot.filled {
  border-style: solid;
  border-color: var(--border-strong);
  box-shadow: 0 3px 0 var(--tile-shadow), inset 0 2px 0 rgba(255, 255, 255, 0.5);
}
/* Two of a kind in the tray = one away from a match. Those cards switch to
   their happy expression and bounce: cute, and it tells the player exactly
   which card to hunt for next. */
.slot.eager {
  border-color: #f09a19;
  box-shadow: 0 3px 0 var(--tile-shadow), inset 0 2px 0 rgba(255, 255, 255, 0.5),
    0 0 0 3px rgba(255, 174, 42, 0.22);
}
.slot.eager img {
  animation: eagerBounce 0.85s ease-in-out infinite;
}
@keyframes eagerBounce {
  0%, 100% { transform: translateY(0) scale(1); }
  50% { transform: translateY(-3px) scale(1.09); }
}
@media (prefers-reduced-motion: reduce) {
  .slot.eager img { animation: none; }
}

/* Tile color palette. Defined AFTER .slot so a filled tray slot (class
   `slot filled tile-<kind>`) picks up the kind's gradient instead of the
   .slot paper-white background beating it at equal specificity. Board tiles
   (button.tile.abs) set no background of their own, so they inherit these
   regardless of order.
   Faces are deliberately LIGHT pastels: the tile art is a full-colour clay
   figure, so a saturated card face used to swallow the figure (orange carrot
   on an orange face). The hue still identifies the kind; the figure carries
   the colour weight, matching the cream-parchment story art. */
.tile-A { background: linear-gradient(160deg, #ffe8d3, #ffd2ae); }
.tile-B { background: linear-gradient(160deg, #e3f1ff, #c5def9); }
.tile-C { background: linear-gradient(160deg, #e5f7d8, #cbeab3); }
.tile-D { background: linear-gradient(160deg, #ffe3e0, #ffc9c3); }
.tile-E { background: linear-gradient(160deg, #f2e7ff, #dfcbf6); }
.tile-F { background: linear-gradient(160deg, #fff5d4, #ffe8ab); }
.tile-G { background: linear-gradient(160deg, #dff8f4, #c0ece2); }
.tile-H { background: linear-gradient(160deg, #ffe6f1, #f9cede); }
.tile-I { background: linear-gradient(160deg, #e7eeff, #ccdbfb); }
.tile-J { background: linear-gradient(160deg, #fff7d4, #ffebad); }
.tile-K { background: linear-gradient(160deg, #e3f8df, #c7edbf); }
.tile-L { background: linear-gradient(160deg, #f2f2f6, #dedee6); }
/* M/N: the pack is 14 kinds (levels.js: "228 tiles · 14 kinds"; hud TILE_NAMES
   names Watermelon/Lemon). Only A-L had chip colours, so a tray slot holding an
   M or N rendered with no background at all. Hues picked to clash with neither
   the red-coral C/H nor the gold F / olive J. */
.tile-M { background: linear-gradient(160deg, #ffe0ec, #ffc2d8); }
.tile-N { background: linear-gradient(160deg, #fff6cc, #ffe9a3); }

@keyframes pulse {
  0%, 100% { filter: brightness(1); }
  50% { filter: brightness(1.2); }
}

.advisor {
  border-color: var(--border-strong);
  background: linear-gradient(180deg, #fff6dc, #ffe9b8);
  color: #7a4a10;
  font-size: 14px;
  line-height: 1.4;
}
.banter {
  display: none !important;
}
@keyframes banterSlideUp {
  from { opacity: 0; transform: translateY(14px) scale(0.92); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
.banter h2 {
  display: flex;
  align-items: center;
  gap: 6px;
  color: #b03862;
  font-size: 12px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin: 0 0 4px;
}
.banter h2::before {
  content: "🎙️";
  font-size: 14px;
}
.banter p {
  margin: 0;
  font-weight: 700;
  font-size: 14px;
  line-height: 1.35;
  color: #4a1528;
}

@media (max-width: 768px) {
  .banter {
    left: 16px;
    right: 16px;
    max-width: calc(100vw - 32px);
    bottom: 96px;
  }
}
.muted { color: #8a6a35; font-size: 13px; font-weight: 600; margin: 8px 0 0; }

.toast {
  position: fixed;
  top: 16px;
  right: 16px;
  background: #fffdf4;
  border: 2px solid var(--border-strong);
  color: var(--text);
  padding: 10px 14px;
  border-radius: 12px;
  z-index: 50;
  max-width: 360px;
  font-size: 13px;
  font-weight: 600;
  box-shadow: 0 6px 18px rgba(150, 100, 30, 0.25);
}
.toast.error { border-color: var(--danger); color: #a02a24; }

.result-modal {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: rgba(90, 58, 18, 0.55);
  backdrop-filter: blur(3px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  animation: fadeIn 0.35s ease;
}
.result-modal.hidden { display: none !important; }

/* AI-generated level experiences: H3 video + separately controlled Grok TTS.
   The cream surround and orange edge echo the board instead of becoming a
   generic black video player. */
.experience-overlay {
  position: fixed;
  inset: 0;
  z-index: 10020;
  display: grid;
  place-items: center;
  padding: clamp(12px, 3vw, 32px);
  background:
    radial-gradient(circle at 50% 35%, rgba(255, 232, 171, 0.42), transparent 44%),
    rgba(75, 48, 15, 0.72);
  backdrop-filter: blur(6px);
  animation: fadeIn 0.28s ease;
}
.experience-overlay.hidden { display: none !important; }
.experience-stage {
  position: relative;
  width: min(720px, 94vw, calc(100vh - 36px));
  aspect-ratio: 1;
  overflow: hidden;
  border: 4px solid #f09a19;
  border-radius: clamp(22px, 4vw, 38px);
  background: #fff1d2;
  box-shadow: 0 8px 0 #bd7410, 0 30px 80px rgba(62, 37, 10, 0.42);
}
.experience-stage video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  background: #f8ead0;
  pointer-events: none;
}
.experience-copy {
  position: absolute;
  z-index: 1;
  inset: auto 0 0;
  padding: clamp(52px, 10vw, 92px) clamp(18px, 4vw, 36px) clamp(16px, 3vw, 28px);
  text-align: center;
  color: #633a0b;
  pointer-events: auto;
  background: linear-gradient(transparent, rgba(255, 244, 216, 0.92) 45%, #fff2d2 100%);
}
.experience-copy h2 {
  margin: 3px 0 4px;
  font-size: clamp(25px, 5vw, 42px);
  line-height: 1.05;
  color: #9b5707;
  text-shadow: 0 2px rgba(255, 255, 255, 0.75);
}
.experience-copy p {
  margin: 0 auto 12px;
  max-width: 520px;
  font-size: clamp(13px, 2.2vw, 17px);
  font-weight: 750;
  color: #7b5827;
}
.experience-kicker {
  display: inline-block;
  padding: 4px 10px;
  border-radius: 999px;
  background: #51bd4d;
  color: #fff;
  font-size: 11px;
  font-weight: 900;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.experience-start,
.experience-skip {
  position: relative;
  z-index: 2;
  pointer-events: auto;
  min-height: 42px;
  margin: 3px 5px 0;
}
.experience-skip {
  border-color: rgba(151, 96, 22, 0.45);
  background: rgba(255, 253, 244, 0.82);
}
.experience-overlay.playing .experience-copy {
  background: transparent;
  pointer-events: none;
}
.experience-overlay.playing .experience-copy > :not(.experience-controls) {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 0.25s ease, transform 0.25s ease;
  pointer-events: none;
}
.experience-controls {
  display: flex;
  justify-content: center;
  gap: 8px;
  pointer-events: auto;
}
.experience-overlay.playing .experience-controls {
  position: fixed;
  z-index: 10030;
  right: max(14px, env(safe-area-inset-right));
  top: max(14px, env(safe-area-inset-top));
  padding: 5px;
  border-radius: 14px;
  background: rgba(255, 248, 226, 0.9);
  box-shadow: 0 4px 18px rgba(70, 42, 12, 0.24);
  pointer-events: auto;
}
.experience-overlay.playing .experience-controls button {
  min-height: 34px;
  margin: 0;
  padding: 6px 10px;
  font-size: 12px;
  pointer-events: auto;
}
.run-score {
  display: grid;
  grid-template-columns: auto auto;
  grid-template-rows: auto auto;
  align-items: center;
  gap: 0 6px;
  min-width: 84px;
  padding: 5px 10px;
  border: 2px solid #dbac48;
  border-radius: 12px;
  background: rgba(255, 253, 244, 0.82);
  color: #8a5b17;
  line-height: 1;
}
.run-score span { grid-row: 1; font-size: 10px; font-weight: 850; text-transform: uppercase; letter-spacing: .08em; }
.run-score strong { grid-row: 2; font-size: 19px; color: #4c9f45; }
.run-score small { grid-row: 1 / 3; grid-column: 2; font-weight: 900; color: #d77c0a; }
.wallet-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-height: 42px;
  padding: 7px 11px;
  border: 2px solid #deb24d;
  border-radius: 999px;
  background: linear-gradient(180deg, #fff8d5, #ffe69a);
  color: #a66008;
  box-shadow: inset 0 1px #fff, 0 2px 0 rgba(170, 104, 15, .18);
}
.wallet-badge span { color: #f09a15; font-size: 18px; }
.wallet-badge strong { font-size: 17px; font-variant-numeric: tabular-nums; }
.match-burst {
  position: fixed;
  z-index: 9100;
  left: 50%;
  top: 25%;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 10px 16px;
  border: 3px solid #f09a19;
  border-radius: 999px;
  background: #fff7d8;
  color: #8c4f07;
  box-shadow: 0 5px 0 #c87508, 0 15px 35px rgba(96, 52, 6, .25);
  transform: translate(-50%, -50%);
  pointer-events: none;
}
.match-burst span { color: #45a547; font-size: 22px; font-weight: 950; }
.match-burst.pop { animation: matchBurst 1s cubic-bezier(.16,.9,.3,1) both; }
@keyframes matchBurst {
  0% { opacity: 0; transform: translate(-50%, -20%) scale(.6); }
  25% { opacity: 1; transform: translate(-50%, -50%) scale(1.12); }
  70% { opacity: 1; transform: translate(-50%, -60%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -95%) scale(.94); }
}
body.creating-match #board-wrap::after {
  content: "Building today’s tower…";
  position: absolute;
  z-index: 500;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  padding: 10px 14px;
  border: 2px solid #e2af42;
  border-radius: 999px;
  background: rgba(255, 249, 227, .94);
  color: #895510;
  font-weight: 850;
  box-shadow: 0 4px 18px rgba(100, 60, 10, .18);
}
.story-btn { background: linear-gradient(180deg, #80da71, #4dbb51); color: #174c1e; border-color: #369a3e; font-weight: 850; }
.experience-preview {
  white-space: nowrap;
  background: linear-gradient(180deg, #76da6d, #46b84d);
  color: #15431b;
  border-color: #31953b;
  font-weight: 850;
}

@media (prefers-reduced-motion: reduce) {
  .experience-stage video { display: none; }
  .experience-stage {
    background: radial-gradient(circle at 50% 42%, #fff8e5 0 18%, #ffe6ae 65%, #ffd181 100%);
  }
  .experience-copy { inset: 0; display: grid; align-content: center; background: transparent; }
  .experience-overlay.playing .experience-copy { opacity: 1; transform: none; }
}

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}
.result-card {
  position: relative;
  background: linear-gradient(170deg, #fffdf4, #ffedc2);
  border: 3px solid var(--border-strong);
  border-radius: 24px;
  padding: 36px 32px 28px;
  max-width: 440px;
  width: 100%;
  text-align: center;
  box-shadow: 0 6px 0 var(--border-strong), 0 24px 60px rgba(120, 78, 20, 0.35);
  animation: popIn 0.45s cubic-bezier(0.2, 1.2, 0.4, 1);
  overflow: hidden;
}
@keyframes popIn {
  from { transform: scale(0.85) translateY(20px); opacity: 0; }
  to { transform: scale(1) translateY(0); opacity: 1; }
}
.result-card h2 { margin: 8px 0 12px; font-size: 26px; color: #7a4a10; }
.result-score {
  font-size: 40px;
  font-weight: 900;
  margin: 0 0 8px;
  color: #f08a00;
  text-shadow: 0 2px 0 rgba(255, 255, 255, 0.6);
}
#tray h2 span { font-size: 15px; color: #9c6a1a; font-weight: 900; }

.stars {
  display: flex;
  justify-content: center;
  gap: 8px;
  min-height: 44px;
  align-items: center;
}
.stars img { width: 40px; height: 40px; }
.result-actions { display: flex; flex-direction: column; gap: 10px; }
.result-actions button,
.result-actions .link-btn {
  width: 100%;
  padding: 12px;
  border-radius: 10px;
  font-size: 15px;
}
.result-actions .link-btn {
  display: block;
  text-decoration: none;
  background: rgba(255, 255, 255, 0.6);
  border: 2px solid var(--border-strong);
  color: #a0803c;
  text-align: center;
  font-weight: 700;
}
/* Burst decorations are injected by app.js (tile/star PNG icons —
   cross-platform identical, no system emoji). */
.result-card.won .burst,
.result-card.lead .burst {
  display: flex;
  justify-content: center;
  gap: 10px;
  min-height: 44px;
  align-items: center;
  animation: confetti 0.8s ease;
}
.result-card.won .burst img,
.result-card.lead .burst img { width: 36px; height: 36px; }
.result-card.lost:not(.lead) .burst { text-align: center; min-height: 44px; }
.result-card.lost:not(.lead) .burst svg { width: 44px; height: 44px; }
@keyframes confetti {
  0% { transform: scale(0.5); opacity: 0; }
  60% { transform: scale(1.15); opacity: 1; }
  100% { transform: scale(1); }
}

.hidden { display: none !important; }

/* Fixed-style tile icons: same on every OS and in offline recordings.
   Filled tray slots show a touch more of the figure than board tiles —
   the extra detail is the point of the larger slot. */
button.tile.abs img,
.slot img {
  width: 86%;
  height: 86%;
  object-fit: contain;
  pointer-events: none;
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.35));
}
.slot.filled img { width: 94%; height: 94%; }

@media (max-width: 720px) {
  header { padding: 10px 14px; gap: 8px 12px; align-items: center; }
  /* Header diet: the tagline is decorative and the long match line wraps —
     both eat vertical budget the board needs on a phone. */
  .tagline { display: none; }
  .brand h1 { font-size: 24px; -webkit-text-stroke-width: 1px; }
  #controls { gap: 6px; }
  #seed { width: 92px; }
  #match-info {
    font-size: 11px;
    flex: 1 1 100%;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  main {
    padding: 12px 10px;
    gap: 10px;
    /* Phone: single column, leaderboard on top, tray + actions at the bottom.
       minmax(0,1fr) — NOT 1fr — or the track's auto-minimum grows to the
       board's max-content and overflows the viewport (off-center). */
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "side"
      "board"
      "tray";
  }
  /* Fill the track so the board is symmetric inside the panel; the panel then
     spans the full content width and centers on screen. */
  #board-wrap { width: 100%; max-width: 100%; padding: 8px; justify-self: stretch; }
}

.agent-list { margin-top: 8px; display: flex; flex-direction: column; gap: 4px; }
.agent-row {
  display: flex; justify-content: space-between; gap: 8px;
  font-size: 13px; color: #8a6a35; font-weight: 600;
  padding: 4px 6px; border-radius: 6px; background: rgba(255, 255, 255, 0.5);
}
.agent-row.best { color: var(--agent); border: 1px solid rgba(255, 107, 74, 0.4); font-weight: 700; }
.agent-name { font-weight: 600; }
.agent-stat { font-variant-numeric: tabular-nums; }

/* Collapsible leaderboard: collapsed = just you + the leader's own row + a
   gentle toggle; expanding reveals the runners-up ladder. */
.agents-wrap { overflow: hidden; }
.agents-wrap .agent-list {
  margin-top: 0;
  transition: max-height 0.28s ease, opacity 0.22s ease, margin-top 0.28s ease;
}
/* collapsed => no list at all, only the toggle */
.agents-wrap.collapsed .agent-list { max-height: 0; opacity: 0; margin-top: 0; }
.agents-wrap:not(.collapsed) .agent-list { max-height: 300px; opacity: 1; margin-top: 8px; }
.agents-toggle {
  width: 100%;
  margin-top: 8px;
  padding: 7px 10px;
  border: 1.5px solid rgba(200, 130, 30, 0.35);
  border-radius: 999px;
  background: linear-gradient(180deg, #fffef8, #fff2d2);
  color: #9c6a1a;
  font-size: 12.5px;
  font-weight: 800;
  letter-spacing: 0.02em;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  box-shadow: 0 2px 0 rgba(200, 130, 30, 0.18);
  transition: transform 0.08s ease, filter 0.08s ease, border-color 0.12s ease;
}
.agents-toggle:hover { filter: brightness(1.03); }
.agents-toggle:active { transform: translateY(1px); }
.agents-toggle .chev {
  display: inline-block;
  font-size: 11px;
  transition: transform 0.22s ease;
}
.agents-wrap:not(.collapsed) .agents-toggle .chev { transform: rotate(180deg); }

/* In-browser replay cursor (no OS mouse). Cheap: CSS only. */
#ghost-cursor {
  position: fixed;
  left: 40px;
  top: 80px;
  width: 18px;
  height: 24px;
  pointer-events: none;
  z-index: 20000;
  transform: translate(-2px, -2px);
  transition: left 0.42s cubic-bezier(0.2, 0.8, 0.2, 1),
              top 0.42s cubic-bezier(0.2, 0.8, 0.2, 1),
              transform 0.08s ease;
  filter: drop-shadow(1px 2px 2px rgba(0, 0, 0, 0.55));
}
#ghost-cursor.hidden { display: none !important; }

#ghost-cursor.clicking { transform: translate(-2px, -2px) scale(0.86); }
#ghost-cursor svg { display: block; }
body.theater #controls button,
body.theater #controls select,
body.theater #controls input { pointer-events: none; opacity: 0.7; }
/* Ads own the whole page while .theater is set: no clicks reach the board,
   the tray, the skills, the rail, or any modal (Poki: no input midroll). */
body.theater #board-wrap,
body.theater #tray,
body.theater #progress-rail,
body.theater .result-modal,
body.theater .pause-overlay,
body.theater .cat-room,
body.theater .cat-story { pointer-events: none; }

/* Flashed covering tiles when clicking a covered one */
button.tile.abs.cover-source {
  outline: 3px solid #ff5a5a !important;
  outline-offset: 1px;
  z-index: 150 !important;
  animation: coverFlash 1.4s ease;
}
@keyframes coverFlash {
  0% { filter: brightness(1.6); }
  100% { filter: brightness(1); }
}

/* ---- Tray animation: fly-in, cluster slide, smoke dissolve ---- */

/* Slots animate opacity (fly-in landing) and transform (FLIP cluster slide). */
#tray-slots .slot {
  transition: opacity 0.16s ease, transform 0.36s cubic-bezier(0.2, 0.8, 0.2, 1);
}
/* A slot awaiting its fly-in lands invisible, then fades in on arrival. */
#tray-slots .slot.fly-pending {
  opacity: 0;
}

/* Flying tile clone: starts at the clicked tile and glides into its slot.
   Sized from --tray-slot so it lands exactly on the slot it replaces. */
.flyer {
  position: fixed;
  width: var(--tray-slot, 54px);
  height: var(--tray-slot, 54px);
  margin-left: calc(var(--tray-slot, 54px) / -2);
  margin-top: calc(var(--tray-slot, 54px) / -2);
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  z-index: 30000;
  pointer-events: none;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
  transition: transform 0.4s cubic-bezier(0.2, 0.8, 0.2, 1),
              opacity 0.4s ease;
}
.flyer img,
.flyer svg { width: 86%; height: 86%; object-fit: contain; pointer-events: none; }

/* Smoke puff: a cloned slot that scales up, blurs and floats away. */
.smoke-puff {
  position: fixed;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  z-index: 29000;
  pointer-events: none;
  transform-origin: center;
  margin: 0;
}
.smoke-puff img,
.smoke-puff svg { width: 86%; height: 86%; object-fit: contain; pointer-events: none; }
.smoke-puff.go {
  animation: smokeDissolve 0.62s ease-out forwards;
}
@keyframes smokeDissolve {
  0%   { opacity: 1; transform: scale(1) translateY(0); filter: blur(0); }
  60%  { opacity: 0.55; transform: scale(1.35) translateY(-8px); filter: blur(2px); }
  100% { opacity: 0; transform: scale(1.75) translateY(-18px); filter: blur(7px); }
}

/* ---- Device safe areas (iPhone notch / home indicator, Android cutouts) ----
   viewport-fit=cover + env() keeps the header and the bottom actions clear of
   the notch and the home bar. Works on every phone; degrades to 0 elsewhere. */
@supports (padding: env(safe-area-inset-bottom)) {
  body { padding-bottom: env(safe-area-inset-bottom); }
  header {
    padding-left: max(24px, env(safe-area-inset-left));
    padding-right: max(24px, env(safe-area-inset-right));
  }
  @media (max-width: 720px) {
    header {
      padding-left: max(14px, env(safe-area-inset-left));
      padding-right: max(14px, env(safe-area-inset-right));
    }
    main {
      padding-left: max(10px, env(safe-area-inset-left));
      padding-right: max(10px, env(safe-area-inset-right));
    }
  }
}

/* ---- Phone: one screen, no scrolling --------------------------------
   MUST stay last in this file. Earlier @media (max-width: 720px) blocks set
   header/main padding, brand size and gap; at equal specificity the later rule
   wins, so an identical block placed earlier was silently ignored. Only
   VERTICAL padding is touched here — horizontal padding belongs to the
   safe-area rules above and has to keep using env() for the notch.

   Why the leaderboard is hidden rather than compacted: a Hard board is twelve
   tile rows. Even at a barely-tappable 25px card the board needs ~380px, and
   the remaining panels cannot shrink below ~450px, so an 844px-tall phone
   screen cannot hold both. The owner chose no-scroll; the full race result
   (rank, percentages, every agent) is restated on the result card, so only the
   always-on panel is lost. Keep a slim one-line race strip instead if you
   prefer, and expect roughly 25px of scroll on Hard. */
@media (max-width: 720px) {
  header { padding-top: 8px; padding-bottom: 8px; }
  .brand h1 { font-size: 22px; }
  #controls { gap: 6px; }
  .run-score { min-width: 0; padding: 4px 8px; }
  .run-score strong { font-size: 16px; }
  .wallet-badge { min-height: 34px; padding: 4px 9px; }
  .wallet-badge strong { font-size: 15px; }
  #race { display: none; }
  main { gap: 6px; padding-top: 8px; padding-bottom: 8px; }
  #tray { padding: 8px; }
  #tool-info { display: none; }
  .cat-skills { padding: 6px 8px; gap: 8px; margin: 8px 0 4px; }
  .cat-skills-btns { gap: 6px; }
  .cat-skills-btns button { min-width: 0; padding: 4px 6px; font-size: 13px; }
}

/* ── Mahjong pseudo-3D tray skin (v0.2_xl a4f839b; skin-3d is the default)
   The BOARD tiles are canvas-baked (GameScene.bakeTile mirrors this exact
   construction); the DOM tray slots consume the same material here. Kept
   LAST so it overrides the legacy .slot rules above at equal specificity. */

/* per-kind saturated pastel face gradient + the kind's mid tone (wall). */
.tile-A { --face: linear-gradient(180deg, #ffd0a6, #f6a76b); --tint: #e08a4a; }
.tile-B { --face: linear-gradient(180deg, #bfe9c8, #92d0a4); --tint: #6fb284; }
.tile-C { --face: linear-gradient(180deg, #ffb4a6, #ef8069); --tint: #d95f47; }
.tile-D { --face: linear-gradient(180deg, #eed7ae, #dbb87a); --tint: #bd934f; }
.tile-E { --face: linear-gradient(180deg, #d6c6f0, #b8a0df); --tint: #9a7cc9; }
.tile-F { --face: linear-gradient(180deg, #fce4ae, #f0cd6e); --tint: #d9ac3f; }
.tile-G { --face: linear-gradient(180deg, #b0d8f6, #82bcec); --tint: #5b9fd9; }
.tile-H { --face: linear-gradient(180deg, #ffc6bd, #f1937f); --tint: #d96a52; }
.tile-I { --face: linear-gradient(180deg, #c9b4f4, #9f83e2); --tint: #7f5fc7; }
.tile-J { --face: linear-gradient(180deg, #e0efae, #bedb74); --tint: #97b845; }
.tile-K { --face: linear-gradient(180deg, #b0e2e0, #6fc6bf); --tint: #4aa39c; }
.tile-L { --face: linear-gradient(180deg, #e2e6ee, #c2cad8); --tint: #97a1b5; }
/* M/N mahjong cakes — mirrors MAHJONG in scenes/GameScene.js, so the DOM tray
   and the baked board chips are the same colour for the two extra kinds. */
.tile-M { --face: linear-gradient(180deg, #ffb3d1, #f07aae); --tint: #d4548e; }
.tile-N { --face: linear-gradient(180deg, #fff08a, #f2d92e); --tint: #d6b410; }

body.skin-3d .slot.filled,
body.skin-3d .slot.eager {
  --td: calc(var(--tray-slot) * 0.18);
  --mahjong: 0 calc(var(--td) + 2px) calc(var(--td) * 0.8) rgba(96, 58, 16, 0.28);
  --wall: linear-gradient(180deg,
      color-mix(in srgb, var(--tint, #cbb27e), white 26%) 0%,
      var(--tint, #cbb27e) 55%,
      color-mix(in srgb, var(--tint, #cbb27e), #3a2408 34%) 100%);
  border: none;
  border-radius: 16%;
  background: var(--tint, #efe2c6);
  box-shadow: var(--mahjong);
  position: relative;
  z-index: 0; /* stacking context, so the wall's z-index:-1 stays inside */
}

/* the side wall: the body extended down by --td so the tint can GRADIENT */
body.skin-3d .slot.filled::after,
body.skin-3d .slot.eager::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: calc(100% + var(--td));
  border-radius: inherit;
  pointer-events: none;
  background: var(--wall);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.85);
  z-index: -1;
}

/* the face fills the whole top layer; crown/recess via inset shadows */
body.skin-3d .slot.filled::before,
body.skin-3d .slot.eager::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.30) 0%, rgba(255, 255, 255, 0) 24%),
    var(--face, linear-gradient(180deg, #f8efd9, #e9dabb));
  box-shadow:
    inset 0 2px 3px rgba(255, 255, 255, 0.60),
    inset 0 -3px 5px rgba(120, 80, 30, 0.20),
    0 1px 3px rgba(150, 110, 60, 0.30);
}

/* art centred on the face, lifted by the wall depth */
body.skin-3d #tray-slots .slot.filled .tray-img,
body.skin-3d #tray-slots .slot.eager .tray-img {
  width: 88%;
  height: 88%;
  transform: translateY(calc(var(--td) * -0.45));
}


/* --- Purrfect Match cat systems (cat.js) ----------------------------------- */
.food-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 38px;
  padding: 4px 10px;
  background: var(--panel);
  border: 1.5px solid var(--border);
  border-radius: 999px;
  font-weight: 700;
  position: relative;
}
.food-art-img { width: 20px; height: 20px; border-radius: 6px; object-fit: cover; }

.cat-floater {
  position: absolute;
  left: 50%;
  top: -6px;
  transform: translateX(-50%);
  font-size: 15px;
  font-weight: 700;
  color: var(--accent-deep);
  text-shadow: 0 1px 0 #fff;
  pointer-events: none;
  animation: cat-float 1.4s ease-out forwards;
}
.floater-art { width: 26px; height: 26px; border-radius: 7px; object-fit: cover; vertical-align: -7px; }
@keyframes cat-float {
  0% { opacity: 0; translate: 0 6px; }
  20% { opacity: 1; }
  100% { opacity: 0; translate: 0 -34px; }
}

.item-art { width: 22px; height: 22px; border-radius: 6px; object-fit: cover; vertical-align: -5px; }
.decor-art, .shelf-art { width: 26px; height: 26px; border-radius: 7px; object-fit: cover; }
.shelf-item .shelf-art { width: 30px; height: 30px; }

/* cat room slide-over */
.cat-room {
  position: fixed;
  inset: 0;
  background: rgba(90, 67, 32, 0.35);
  z-index: 60;
  display: flex;
  justify-content: flex-end;
}
.cat-room.hidden { display: none; }
.cat-room-panel {
  width: min(340px, 92vw);
  height: 100%;
  background: var(--panel);
  border-left: 2px solid var(--border);
  padding: 14px 16px 20px;
  overflow-y: auto;
  animation: cat-room-in 0.22s ease-out;
}
@keyframes cat-room-in {
  from { transform: translateX(30px); opacity: 0.4; }
  to { transform: translateX(0); opacity: 1; }
}
.cat-room-head { display: flex; align-items: center; justify-content: space-between; }
.cat-room-head h2 { margin: 0; font-size: 20px; }
.cat-room-close {
  border: none; background: none; font-size: 26px; color: var(--muted);
  cursor: pointer; line-height: 1; padding: 2px 6px;
}
.cat-stage {
  position: relative;
  text-align: center;
  padding: 12px 0 2px;
  min-height: 150px;
}
#cat-sprite .cat-portrait, #cat-sprite span { display: inline-block; }
.cat-portrait {
  width: 128px;
  height: 128px;
  border-radius: 26px;
  object-fit: cover;
  box-shadow: 0 6px 18px rgba(120, 78, 20, 0.22), 0 0 0 3px var(--panel);
  background: var(--panel-2);
  opacity: 0;
  transition: opacity 0.25s ease;
}
.cat-portrait.loaded { opacity: 1; }
#cat-sprite.happy .cat-portrait { animation: cat-bounce 1.6s ease-in-out infinite; }
#cat-sprite.sad .cat-portrait { filter: saturate(0.55); transform: scale(0.92); }
@keyframes cat-bounce {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-7px); }
}
.cat-decor { position: absolute; top: 8px; right: calc(50% - 58px); font-size: 26px; }
.cat-stage-line { text-align: center; color: var(--muted); margin: 0 0 10px; font-size: 13px; }
.cat-bars { display: grid; gap: 8px; margin-bottom: 12px; }
.cat-bar { display: flex; align-items: center; gap: 8px; font-size: 16px; }
.cat-bar .track {
  flex: 1; height: 12px; border-radius: 999px;
  background: var(--panel-2); border: 1px solid var(--border); overflow: hidden;
}
.cat-bar .track div { height: 100%; width: 0; border-radius: 999px; background: var(--accent); transition: width 0.3s ease; }
.cat-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.cat-actions button {
  min-height: 40px;
  border: 1.5px solid var(--border);
  background: var(--panel-2);
  border-radius: 12px;
  font-weight: 700;
  color: var(--text);
  cursor: pointer;
  font-size: 14px;
}
.cat-actions button:not(:disabled):hover { border-color: var(--accent); background: #fff; }
.cat-actions button:disabled { opacity: 0.45; cursor: default; }
.cat-hint { text-align: center; font-size: 12px; margin: 10px 0; color: var(--muted); }

.cat-chest {
  display: block;
  width: 100%;
  min-height: 46px;
  margin: 4px 0 10px;
  border: 2px solid var(--border-strong);
  border-radius: 14px;
  background: linear-gradient(160deg, #fff3d1, #ffe4a8);
  color: var(--text);
  font-weight: 800;
  font-size: 15px;
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.cat-chest:not(:disabled):hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 20px rgba(242, 138, 0, 0.25);
  border-color: var(--accent);
}
.cat-chest:disabled { opacity: 0.55; cursor: default; }
.cat-shelf { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; }
.shelf-item {
  width: 44px; height: 44px; font-size: 22px;
  border: 1.5px solid var(--border); border-radius: 12px;
  background: var(--panel-2); cursor: pointer;
}
.shelf-item:disabled { opacity: 0.25; cursor: default; }
.shelf-item.on { border-color: var(--accent); background: #fff; box-shadow: 0 0 0 2px var(--accent) inset; }

/* popped zone: 6 reserved slots above the tray (cat_pop landings) */
.popped-zone {
  display: flex;
  gap: 8px;
  margin: 0 0 10px;
  padding: 8px 10px;
  border: 1.5px dashed var(--border-strong);
  border-radius: 14px;
  background: rgba(255, 243, 209, 0.65);
}
.popped-zone.hidden { display: none; }
.pslot {
  width: var(--tray-slot, 54px);
  height: var(--tray-slot, 54px);
  border: 1.5px dashed var(--border);
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.4);
}
.pslot.filled {
  border-style: solid;
  border-color: var(--accent);
  background: #fff;
  cursor: pointer;
}
/* Entrance for a tile that just landed in the zone (or overwrote a slot).
   Keyed rendering in hud.js only adds .zone-in to the changed slot, so the
   whole row never re-animates. */
.pslot.zone-in {
  animation: zone-in 0.32s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes zone-in {
  from { transform: scale(0.45); opacity: 0; }
  to { transform: scale(1); opacity: 1; }
}
.pslot.filled:hover { transform: translateY(-2px); }
.pslot .tray-img { width: 100%; height: 100%; object-fit: contain; }
.free-badge {
  display: inline-block;
  margin-left: 4px;
  padding: 1px 5px;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 0.4px;
  vertical-align: 1px;
}

/* Mochi skills chip (tray panel) */
.cat-skills {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 10px 0 6px;
  padding: 8px 10px;
  border: 1.5px solid var(--border);
  border-radius: 14px;
  background: var(--panel-2);
  transition: border-color 0.25s ease, box-shadow 0.25s ease;
}
.cat-skills.ready {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px rgba(255, 159, 28, 0.25) inset;
}
.cat-skills-avatar {
  width: 40px; height: 40px; border-radius: 12px; object-fit: cover; flex: none;
  cursor: pointer; /* opens Mochi's room */
  transition: transform 0.15s ease;
}
.cat-skills-avatar:hover { transform: scale(1.08); }
.cat-skills-body { flex: 1; min-width: 0; }
.cat-skills-bar {
  height: 10px; border-radius: 999px; background: #fff;
  border: 1px solid var(--border); overflow: hidden; margin-bottom: 9px;
}
.cat-skills-bar div {
  height: 100%; width: 0; border-radius: 999px;
  background: linear-gradient(90deg, var(--accent), #ffc46e);
  transition: width 0.35s ease;
}
/* One row, always. This was `display:flex; flex-wrap:wrap` with min-width:86px,
   which in the narrow desktop sidebar (≈250px of button body) wrapped Shuffle
   onto a second line while phones stayed on one — the same uneven dock the
   owner flagged on iPhone. A 3-column grid with minmax(0,1fr) freezes the row
   and lets the label ellipsize instead of stretching its box. */
.cat-skills-btns { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 5px; }
.cat-skills-btns button {
  min-width: 0; min-height: 30px;
  /* 2026-09-19: single-line, centered labels — the charge badge (below) is
     pinned to the corner and must never push the label into a second line
     (that wrap is what made Revert tall and Shuffle's dot escape on iPhone).
     Padding/gap are tight so "Shuffle" still fits whole in the 72px column the
     desktop sidebar gives each button. */
  display: inline-flex; align-items: center; justify-content: center; gap: 2px;
  white-space: nowrap;
  border: 1.5px solid var(--border); border-radius: 10px;
  background: var(--panel); color: var(--text);
  font-weight: 700; font-size: 12px; cursor: pointer; padding: 2px 4px;
  position: relative; line-height: 1.4;
}
/* Label in its own span so a too-narrow column ellipsizes ("Reve…") rather than
   overflowing; the icon never shrinks. */
.cat-skills-btns .skill-ico { flex: none; }
.cat-skills-btns .skill-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.cat-skills-btns button:not(:disabled):hover { border-color: var(--accent); }
.cat-skills-btns button:disabled { opacity: 0.45; cursor: default; }
.cat-skills-btns button.has-free {
  border-color: var(--accent-deep);
  background: #fff6e0;
}
.cat-skills-btns small { color: var(--muted); }

/* Skill charges render as pips (daily gift + 🎬-earned) — never energy numbers
   (2026-09-19: the bar above is the only energy read; numbers are out).
   2026-09-19 redesign: the pips are a CORNER BADGE pinned outside the text
   flow. Inline dots used to wrap under the label on narrow trays, inflating
   the buttons (uneven heights, a stray dot escaping the box) — an absolutely
   positioned badge cannot move the button box, and :empty removes it whole. */
.pips {
  position: absolute; top: -6px; right: -6px; z-index: 5;
  display: inline-flex; gap: 2.5px; align-items: center;
  padding: 2px 4.5px;
  background: #fff; border: 1.5px solid var(--accent-deep); border-radius: 999px;
  box-shadow: 0 2px 5px rgba(62, 37, 10, 0.28);
  pointer-events: none;
}
/* NOT `display: none`: cat.js flyGiftToSkill measures this element to aim the
   rewarded-gift sticker, and the badge is empty exactly when the 🎬 chip that
   triggers that flight is visible — a display:none badge reports a 0,0,0,0
   rect, which sent the sticker to the viewport corner. visibility keeps the
   (invisible, absolutely positioned) box, so the sticker still lands on the
   corner the fresh pip pops into. */
.pips:empty { visibility: hidden; }
.pips i {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent);
}

/* Rewarded top-up row: one chip per skill, shown only when that skill is out
   of charges and today's 🎬 budget is unspent. Never green (Poki), always 🎬. */
.cat-ad-row {
  display: flex; gap: 8px; justify-content: center;
  margin-top: 6px; flex-wrap: wrap;
}
.cat-ad-row.hidden { display: none; }
.ad-chip {
  min-height: 34px; padding: 4px 12px;
  border: 1.5px solid var(--border-strong); border-radius: 999px;
  background: var(--panel-2); color: var(--text);
  font-weight: 800; font-size: 13px; cursor: pointer;
}
.ad-chip:not(:disabled):hover { border-color: var(--accent); background: #fff; }
/* 2026-09-18: a denied reward (no fill / daily budget gone) dims the chip for
   a beat — silent per Poki (no ad-failure copy), but never clickable-looking-
   dead. Retryable after the cooldown. */
.ad-chip.cooling { opacity: 0.45; pointer-events: none; }
/* The granted gift flies from the chip into the skill's charge pips. */
.gift-fly {
  position: fixed; z-index: 260; pointer-events: none;
  filter: drop-shadow(0 4px 10px rgba(255, 170, 40, 0.55));
}
.gift-fly.go { animation: gift-fly 0.72s cubic-bezier(0.32, 0.72, 0.24, 1) forwards; }
@keyframes gift-fly {
  0% { transform: translate(0, 0) scale(1) rotate(0deg); opacity: 1; }
  70% { transform: translate(calc(var(--fly-x) * 0.85), calc(var(--fly-y) * 0.85 - 14px)) scale(0.82) rotate(8deg); opacity: 1; }
  100% { transform: translate(var(--fly-x), var(--fly-y)) scale(0.45) rotate(14deg); opacity: 0.15; }
}
.pips i.pop-in { animation: pip-pop 0.45s cubic-bezier(0.2, 1.6, 0.4, 1); }
@keyframes pip-pop {
  0% { transform: scale(0); }
  100% { transform: scale(1); }
}

/* Daily-gift reveal: the three skill buttons pop in with a stagger on the
   first load of a new day (Mochi's gifts), driven by cat.js. */
.cat-skills.gift-reveal .cat-skills-btns button {
  animation: gift-pop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
.cat-skills.gift-reveal .cat-skills-btns button:nth-child(1) { animation-delay: 0s; }
.cat-skills.gift-reveal .cat-skills-btns button:nth-child(2) { animation-delay: 0.18s; }
.cat-skills.gift-reveal .cat-skills-btns button:nth-child(3) { animation-delay: 0.36s; }

/* round gift */
/* ESC pause overlay (P1-9): covers the board so clicks cannot land mid-pause */
.pause-overlay {
  position: fixed; inset: 0; z-index: 65;
  background: rgba(90, 67, 32, 0.45);
  display: flex; align-items: center; justify-content: center; padding: 16px;
}
.pause-overlay.hidden { display: none; }
.pause-card {
  background: var(--panel); border: 2px solid var(--border);
  border-radius: 20px; padding: 22px 28px; text-align: center;
  max-width: 340px; width: 100%; animation: gift-pop 0.3s ease;
}
.pause-card h2 { margin: 0 0 4px; }
.pause-card p { margin: 0 0 12px; font-weight: 700; }
.pause-card .primary { min-height: 42px; cursor: pointer; }
.pause-hint { font-size: 12px; margin: 8px 0 0 !important; font-weight: 400 !important; }
.result-gift { margin: 10px 0 2px; text-align: center; }
.result-gift.hidden { display: none; }
@keyframes gift-pop {
  0% { transform: scale(1); }
  40% { transform: scale(1.45) rotate(-8deg); }
  100% { transform: scale(1); }
}
.gift-note { font-size: 14px; }
.gift-items { display: flex; gap: 10px; justify-content: center; margin-top: 8px; flex-wrap: wrap; }
.gift-items.hidden { display: none; }
.gift-item {
  background: var(--panel-2); border: 1.5px solid var(--border);
  border-radius: 999px; padding: 4px 12px; font-weight: 700; font-size: 14px;
  animation: gift-pop 0.45s ease;
}
.gift-double {
  display: block; margin: 8px auto 0; min-height: 34px; padding: 4px 16px;
  border: 1.5px solid var(--border-strong); border-radius: 999px;
  background: var(--panel-2); color: var(--text);
  font-weight: 700; font-size: 13px; cursor: pointer;
}
.gift-double:not(:disabled):hover { border-color: var(--accent); background: #fff; }
.gift-double:disabled { opacity: 0.6; cursor: default; }

/* story overlay (chest / evolution) */
.cat-story {
  position: fixed; inset: 0; z-index: 80;
  background: rgba(60, 44, 18, 0.75);
  display: flex; align-items: center; justify-content: center; padding: 16px;
}
.cat-story-stage {
  position: relative;
  width: min(560px, 94vw);
  border-radius: 22px;
  overflow: hidden;
  background: #201409;
}
.cat-story-stage video { display: block; width: 100%; height: auto; }
.cat-story-copy {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 46px 18px 14px;
  background: linear-gradient(180deg, transparent, rgba(32, 20, 9, 0.88));
  color: #fff7e3;
  text-align: center;
}
.cat-story-copy h2 { margin: 0 0 4px; font-size: 22px; }
.cat-story-copy p { margin: 0 0 10px; font-size: 14px; opacity: 0.9; }
.cat-story-copy .primary { min-height: 36px; cursor: pointer; }

/* ---- Poki short canvases (16:9 embeds: 640×360 / 836×470 / 1031×580) -------
   The game renders inside a fixed iframe on Poki: the page must never scroll
   ("scale to cover the full canvas"). These height-driven tiers reflow the
   chrome so the board stays the hero. MUST stay after the width media blocks
   above — at equal specificity the later rule wins. */

@media (max-height: 640px) {
  /* The leaderboard is restated on the result card; on short canvases the
     board wins the space. */
  #side { display: none; }
  #race { display: none; }
  header { padding-top: 6px; padding-bottom: 6px; }
  .brand h1 { font-size: 20px; }
  .tagline { display: none; }
}

@media (max-height: 560px) {
  /* Force the board+tray pair even below the phone width: a single stacked
     column cannot fit inside a 360px-tall embed. Tray rides beside the board
     so its height never competes with the board's. */
  main {
    grid-template-columns: auto minmax(150px, 210px);
    grid-template-areas:
      "board tray";
    gap: 8px;
    padding: 8px 10px;
  }
  #board-wrap {
    width: max-content;
    max-width: 100%;
    justify-self: center;
    padding: 8px;
  }
  #tray { padding: 6px; }
  #tray h2 { display: none; }
  :root { --tray-slot: 34px; }
  .cat-skills { gap: 4px; }
  .cat-skills-btns { gap: 4px; }
  .cat-skills-btns button { min-height: 30px; font-size: 11px; padding: 3px 5px; }
  /* Short Poki canvases give the tray a ~150px column: three labelled buttons
     cannot fit one row there, so the row goes icon-only instead of wrapping. */
  .cat-skills-btns .skill-label { display: none; }
  .cat-skills-avatar { width: 30px; height: 30px; }
  .ad-chip { min-height: 26px; font-size: 11px; padding: 2px 9px; }
  header .run-score, header .wallet-badge, header .food-badge { min-height: 26px; }
}

/* ---- Touch devices (iPhone / iPad) -----------------------------------------
   Poki: adaptive controls — every tap target grows to comfortable sizes on
   coarse pointers; hover-only affordances get tap-safe fallbacks. */
@media (pointer: coarse) {
  .cat-skills-btns button { min-height: 44px; font-size: 14px; }
  .cat-skills-avatar { width: 44px; height: 44px; }
  .ad-chip { min-height: 40px; font-size: 14px; }
  .rail-dot { width: 36px; height: 36px; }
  .result-actions button { min-height: 50px; font-size: 16px; }
  .gift-double { min-height: 44px; font-size: 14px; }
  .agents-toggle { padding: 10px 12px; font-size: 14px; }
  #pause-resume { min-height: 50px; }
  .slot { border-radius: 15px; }
  /* :hover styles stick after a tap on touch — neutralize the sticky state. */
  .ad-chip:not(:disabled):hover,
  .gift-double:not(:disabled):hover { background: var(--panel-2); }
}
/* iPad-class widths keep the two-column desktop layout (Poki: tablets get the
   mobile control scheme, which here is the same tap UI at comfortable sizes). */
@media (min-width: 721px) and (pointer: coarse) {
  main { gap: 12px; padding: 14px 16px; }
}

/* Daily-gift stickers: on the first load of a new day, one generated sticker
   per skill floats over the page (cat.js revealDailyGifts), then dissolves. */
#skill-gift-reveal {
  position: fixed; inset: 0; z-index: 58; pointer-events: none;
  display: flex; align-items: center; justify-content: center; gap: 14px;
}
#skill-gift-reveal img {
  width: 96px; height: 96px; border-radius: 24px;
  border: 3px solid #f09a19; background: #fff7e3;
  box-shadow: 0 12px 26px rgba(62, 37, 10, 0.35);
  opacity: 0; transform: scale(0.3) rotate(-10deg);
  transition: opacity 0.3s ease, transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
#skill-gift-reveal img.ready { opacity: 1; transform: scale(1) rotate(0deg); }
#skill-gift-reveal img:nth-child(2) { transition-delay: 0.16s; }
#skill-gift-reveal img:nth-child(3) { transition-delay: 0.32s; }
#skill-gift-reveal.bye img { opacity: 0; transform: scale(0.62) translateY(-22px); }
@media (max-height: 560px) {
  #skill-gift-reveal { gap: 10px; }
  #skill-gift-reveal img { width: 64px; height: 64px; border-radius: 16px; }
}

/* ---- Win celebration stage (src/scenes/celebrationFx.js) ------------------ *
 * Two fixed layers the show draws on: a night curtain that dims the page, and
 * an additive canvas that paints the display on top of it.
 *
 * The curtain is the whole trick. Firework sparks are light, and adding light
 * to this page's cream background does nothing you can see — the previous
 * version learned that the hard way. Against a deep, slightly warm night the
 * same particles read as fire. The tint is brown-violet rather than black so
 * it stays inside the game's palette instead of flashing a foreign colour.
 *
 * z-order: above the board and the match-burst pill (9100), below the result
 * card (9999) — the card is the show's ending, so it must never be behind it.
 */
.fx-curtain {
  position: fixed;
  inset: 0;
  z-index: 9400;
  pointer-events: none;
  opacity: 0;
  transition: opacity 620ms ease;
  background:
    radial-gradient(120% 80% at 50% 118%, rgba(255, 176, 92, 0.16), rgba(0, 0, 0, 0) 62%),
    linear-gradient(180deg, rgba(26, 18, 44, 0.9) 0%, rgba(38, 24, 52, 0.86) 52%, rgba(52, 30, 40, 0.9) 100%);
}
.fx-curtain.on { opacity: 1; }
.fx-flash {
  position: fixed;
  inset: 0;
  z-index: 9450;
  pointer-events: none;
  opacity: 0;
  mix-blend-mode: screen;
}
.fx-stage {
  position: fixed;
  inset: 0;
  z-index: 9500;
  pointer-events: none;
  transition: opacity 340ms ease;
}
.fx-stage.retiring { opacity: 0.3; }
@media (prefers-reduced-motion: reduce) {
  .fx-curtain { transition: none; }
}
