/* ==========================================================================
   Pickyroo — game stylesheet
   --------------------------------------------------------------------------
   Loaded only on game pages. Order of sections matches the order a visitor
   meets them: the shell, then the shared bits (winner modal, confetti,
   commentary), then one section per game.

   Rule followed throughout: anything that moves every frame is driven by a
   CSS custom property that JavaScript writes (--x, --p, --rot), never by
   JavaScript writing `style.left` directly. That keeps the animation on the
   compositor and keeps the layout maths in one readable place.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Game shell
   -------------------------------------------------------------------------- */
.game-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 335px;
  gap: 1.25rem;
  align-items: start;
  padding: 1.25rem 0 0;
}
@media (max-width: 1000px) {
  .game-layout { grid-template-columns: minmax(0, 1fr); }
}

.stage-head { margin-bottom: .9rem; }
.stage-head h1 { margin-bottom: .15rem; }
.stage-head .tagline { color: var(--muted); margin: 0; font-size: .95rem; }

.stage {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-2);
  padding: 1.1rem;
  min-height: 340px;
  display: grid;
  align-content: center;
  overflow: hidden;
}

/* Sits under the stage: the big go button plus the small secondary actions. */
.stage-controls {
  display: flex; flex-wrap: wrap; gap: .6rem; align-items: center; justify-content: center;
  margin-top: 1rem;
}
.stage-controls .secondary { display: flex; gap: .4rem; flex-wrap: wrap; justify-content: center; }

.panel { display: grid; gap: 1rem; }
.panel-card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); box-shadow: var(--sh-1);
  padding: .95rem 1rem 1.05rem;
}
.panel-card > h2 {
  font-size: .95rem; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); margin: 0 0 .7rem;
  display: flex; align-items: center; gap: .4rem;
}
.panel-card .options { display: grid; gap: .8rem; }

/* Empty-state shown before there are enough entrants to play. */
.empty-state { text-align: center; color: var(--muted); padding: 2rem 1rem; }
.empty-state .big { font-size: 2.6rem; }

/* Presentation mode — hides the furniture so a wheel can be thrown on a
   classroom projector or a shared screen without the config panel in shot. */
body.presenting .topbar,
body.presenting .panel,
body.presenting .site-footer,
body.presenting .ad-slot,
body.presenting .stage-head .tagline { display: none; }
body.presenting .game-layout { grid-template-columns: 1fr; padding-top: .5rem; }
body.presenting .stage { min-height: min(76vh, 900px); }

/* --------------------------------------------------------------------------
   Roster editor (shared by every game)
   -------------------------------------------------------------------------- */
.roster-list { display: grid; gap: .4rem; margin-bottom: .7rem; }
.roster-row {
  display: grid;
  grid-template-columns: 2.1rem 1fr auto auto;
  gap: .35rem; align-items: center;
}
.roster-row.is-dim { opacity: .45; }

/* The emoji button doubles as the colour swatch — one control, two jobs, and
   it makes the row scannable: you find "the orange fox" at a glance. */
.emoji-btn {
  font-size: 1.15rem; line-height: 1;
  width: 2.1rem; height: 2.1rem; padding: 0;
  border-radius: 50%; border: 2px solid var(--ec, var(--brand));
  background: color-mix(in srgb, var(--ec, var(--brand)) 16%, var(--surface));
  cursor: pointer; display: grid; place-items: center;
}
.emoji-btn:hover { transform: scale(1.08); }

.roster-row input[type="text"] { padding: .38rem .5rem; }
.weight-input { width: 3.4rem; text-align: center; padding: .38rem .25rem !important; }
.row-del {
  border: 0; background: none; cursor: pointer; color: var(--muted);
  font-size: 1rem; padding: .25rem .35rem; border-radius: var(--r-sm);
}
.row-del:hover { background: color-mix(in srgb, var(--coral) 18%, transparent); color: var(--coral); }

.roster-tools { display: flex; flex-wrap: wrap; gap: .35rem; }
.roster-count { font-size: .8rem; color: var(--muted); margin-top: .55rem; }
.roster-count.over { color: var(--coral); font-weight: 600; }

/* Emoji picker popover */
.emoji-pop {
  position: fixed; z-index: 120;
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--r); box-shadow: var(--sh-3); padding: .5rem;
  width: min(94vw, 300px);
}
.emoji-pop-grid {
  display: grid; grid-template-columns: repeat(8, 1fr); gap: .1rem;
  max-height: 190px; overflow-y: auto;
}
.emoji-pop-grid button {
  font-size: 1.15rem; border: 0; background: none; cursor: pointer;
  padding: .2rem 0; border-radius: var(--r-sm); line-height: 1.3;
}
.emoji-pop-grid button:hover { background: var(--brand-soft); }
.emoji-pop .swatches { display: flex; flex-wrap: wrap; gap: .25rem; margin-top: .5rem; padding-top: .5rem; border-top: 1px solid var(--line); }
.emoji-pop .swatches button {
  width: 20px; height: 20px; border-radius: 50%; border: 2px solid transparent; cursor: pointer; padding: 0;
}
.emoji-pop .swatches button[aria-pressed="true"] { border-color: var(--ink); }

/* Bulk paste mode */
.bulk-edit { display: grid; gap: .5rem; }

/* --------------------------------------------------------------------------
   History list
   -------------------------------------------------------------------------- */
.history-list { display: grid; gap: .3rem; max-height: 220px; overflow-y: auto; margin: 0; padding: 0; list-style: none; }
.history-list li {
  display: flex; align-items: center; gap: .5rem;
  font-size: .88rem; padding: .3rem .45rem; border-radius: var(--r-sm);
  background: var(--surface-2);
}
.history-list li .rank { color: var(--muted); font-size: .75rem; font-variant-numeric: tabular-nums; min-width: 1.4rem; }
.history-list li .who { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.history-list li .when { margin-left: auto; color: var(--muted); font-size: .74rem; white-space: nowrap; }
.history-empty { color: var(--muted); font-size: .86rem; margin: 0; }

/* --------------------------------------------------------------------------
   Winner modal
   -------------------------------------------------------------------------- */
.fs-modal {
  position: fixed; inset: 0; z-index: 200;
  display: grid; place-items: center; padding: 1rem;
  background: color-mix(in srgb, var(--ink) 55%, transparent);
  backdrop-filter: blur(3px);
  animation: fadein .18s ease both;
}
.fs-modal[hidden] { display: none; }
@keyframes fadein { from { opacity: 0; } }

.fs-modal-card {
  width: min(100%, 440px); text-align: center;
  background: var(--surface); border-radius: var(--r-lg);
  box-shadow: var(--sh-3); padding: 1.8rem 1.5rem 1.4rem;
  animation: cardin .34s cubic-bezier(.2, 1.3, .5, 1) both;
  border-top: 6px solid var(--wc, var(--brand));
}
@keyframes cardin { from { opacity: 0; transform: scale(.86) translateY(14px); } }
.fs-modal-card .kicker { color: var(--muted); font-size: .78rem; letter-spacing: .12em; text-transform: uppercase; }
.fs-modal-card .emo { font-size: 3.6rem; line-height: 1.1; margin: .2rem 0; animation: cheer 1.1s ease-in-out infinite; }
@keyframes cheer { 50% { transform: translateY(-8px) rotate(4deg); } }
.fs-modal-card .who {
  font-size: clamp(1.5rem, 1.1rem + 2vw, 2.1rem); font-weight: 600;
  color: var(--wc, var(--brand)); overflow-wrap: anywhere; margin: 0 0 .2rem;
}
.fs-modal-card .detail { color: var(--muted); font-size: .9rem; margin-bottom: 1.1rem; }
.fs-modal-actions { display: flex; gap: .5rem; flex-wrap: wrap; justify-content: center; }

#fs-confetti {
  position: fixed; inset: 0; z-index: 190; pointer-events: none;
}

/* --------------------------------------------------------------------------
   Commentary feed (races, bracket, dice)
   -------------------------------------------------------------------------- */
.commentary {
  margin-top: .8rem; padding: .55rem .75rem;
  background: var(--surface-2); border-radius: var(--r);
  font-size: .88rem; min-height: 2.6rem;
  display: grid; align-content: end; gap: .15rem;
  /* Four lines of commentary at 1.4 line-height, plus the padding above.
     Undersize this and the oldest line is sliced in half rather than hidden. */
  max-height: 7.2rem; overflow: hidden;
}
.commentary p { margin: 0; animation: slideup .3s ease both; }
.commentary p:not(:last-child) { opacity: .5; }
@keyframes slideup { from { opacity: 0; transform: translateY(6px); } }

/* --------------------------------------------------------------------------
   1. Spin wheel
   -------------------------------------------------------------------------- */
.wheel-stage { justify-items: center; }
.wheel-holder { position: relative; width: min(100%, 520px); margin-inline: auto; aspect-ratio: 1; }
.wheel-holder canvas { width: 100%; height: 100%; border-radius: 50%; }
/* Pointer sits at the top, biting into the rim. */
.wheel-pointer {
  position: absolute; top: -6px; left: 50%; transform: translateX(-50%);
  width: 0; height: 0; z-index: 3;
  border-left: 15px solid transparent; border-right: 15px solid transparent;
  border-top: 30px solid var(--accent);
  filter: drop-shadow(0 3px 4px rgba(0,0,0,.28));
}
.wheel-pointer.is-ticking { animation: tick .09s ease; }
@keyframes tick { 50% { transform: translateX(-50%) rotate(-13deg); } }

/* --------------------------------------------------------------------------
   2-5. Races (horse / pig / duck / rocket)
   --------------------------------------------------------------------------
   One engine, four skins. Everything theme-specific is a custom property set
   on .track by JS: --track-a/--track-b make the ground, --sky-a/--sky-b make
   the backdrop. That is why there is no .theme-pig block full of copies.
   -------------------------------------------------------------------------- */
.race-stage { align-content: start; padding: .8rem; }
.track {
  position: relative; border-radius: var(--r);
  /* Headroom at the top only: the finish flag hangs above lane one, and the
     lanes themselves run edge to edge so there is no stripe of leftover sky
     under the last runner. */
  padding: 1.9rem 0 0;
  background: linear-gradient(180deg, var(--sky-a, #cfefff), var(--sky-b, #eaf7ff));
  overflow: hidden;
}
:root[data-theme="dark"] .track { filter: saturate(.9) brightness(.88); }

.lane {
  position: relative; height: var(--lane-h, 52px);
  display: flex; align-items: center;
  border-bottom: 2px dashed color-mix(in srgb, #fff 45%, transparent);
  background: linear-gradient(180deg, var(--track-a, #d9b98c), var(--track-b, #c9a271));
}
.lane:first-child { border-radius: var(--r-sm) var(--r-sm) 0 0; }
.lane:last-child { border-bottom: 0; border-radius: 0 0 var(--r-sm) var(--r-sm); }

/* Lane label: rank + name, pinned left, above the track art. */
.lane-tag {
  position: absolute; left: .5rem; z-index: 2;
  display: flex; align-items: center; gap: .35rem;
  font-size: .78rem; font-weight: 600; color: #fff;
  background: color-mix(in srgb, var(--lc, #000) 78%, transparent);
  padding: .1rem .5rem; border-radius: var(--r-pill);
  max-width: 42%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  text-shadow: 0 1px 2px rgba(0,0,0,.35);
  pointer-events: none;
}
.lane-tag .pos { opacity: .8; font-variant-numeric: tabular-nums; }

/* The runner. --x is 0..1 across the usable track width; JS writes it each
   frame. translateX uses a calc against the lane width so the sprite never
   overshoots the finish post on a narrow screen. */
.runner {
  position: absolute; left: 0; z-index: 3;
  height: 100%; width: var(--runner-w, 46px);
  display: grid; place-items: center;
  transform: translateX(calc(var(--x, 0) * (var(--lane-w, 600px) - var(--runner-w, 46px))));
  will-change: transform;
}
.runner .mount { font-size: var(--mount-size, 30px); line-height: 1; filter: drop-shadow(0 2px 3px rgba(0,0,0,.3)); }
.runner .rider {
  position: absolute; top: 2px; left: 52%; font-size: 15px;
  transform: rotate(-8deg);
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.35));
}
.runner.is-running .mount { animation: gallop .28s steps(2, end) infinite; }
.runner.is-boosted .mount { animation: gallop .13s steps(2, end) infinite; }
.runner.is-stuck .mount { animation: stumble .5s ease infinite; }
@keyframes gallop { 50% { transform: translateY(-5px) rotate(-4deg); } }
@keyframes stumble { 50% { transform: translateY(2px) rotate(9deg) scale(.94); } }
/* Rockets point up, not sideways, and they get a flame instead of a gallop. */
.track[data-theme="rocket"] .runner .mount { transform: rotate(45deg); }
.track[data-theme="rocket"] .runner.is-running .mount { animation: wobble .4s ease-in-out infinite; }
@keyframes wobble { 50% { transform: rotate(45deg) translateY(-4px); } }

/* Bubbles / dust / vapour trailing behind a runner. */
.trail {
  position: absolute; z-index: 2; font-size: 12px; opacity: .75;
  animation: trailaway .8s ease-out forwards; pointer-events: none;
}
@keyframes trailaway {
  to { opacity: 0; transform: translate(-26px, -12px) scale(.5); }
}

/* Hazards sit at fixed points on the track and are theme-flavoured. */
.hazard {
  position: absolute; z-index: 1; font-size: 20px; opacity: .85;
  top: 50%; transform: translate(-50%, -50%);
  pointer-events: none;
}

/* The finish post. Its position is randomised per race when the option is on,
   which is why it is absolutely positioned by percentage rather than pinned. */
.finish-post {
  position: absolute; top: 0; bottom: 0; z-index: 4;
  left: var(--finish, 100%);
  width: 12px; transform: translateX(-6px);
  background:
    repeating-conic-gradient(#1b1733 0 25%, #fff 0 50%) 0 0 / 12px 12px;
  box-shadow: 0 0 0 1px rgba(0,0,0,.25);
}
.finish-post::before {
  content: var(--finish-emoji, "🏁");
  position: absolute; top: -4px; left: 50%; transform: translate(-50%, -100%);
  font-size: 22px; filter: drop-shadow(0 2px 3px rgba(0,0,0,.3));
}
.race-countdown {
  position: absolute; inset: 0; z-index: 10;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--ink) 45%, transparent);
  font-size: clamp(3rem, 12vw, 7rem); font-weight: 700; color: #fff;
  text-shadow: 0 6px 18px rgba(0,0,0,.5);
  animation: countpulse .9s ease-out;
}
@keyframes countpulse { from { transform: scale(1.7); opacity: 0; } }

/* Photo finish strip — shown when the top two are within a nose of each other. */
.photo-finish {
  margin-top: .6rem; padding: .5rem .8rem; border-radius: var(--r);
  background: #1b1733; color: #fff; font-size: .85rem;
  display: flex; align-items: center; gap: .5rem;
  animation: slideup .3s ease both;
}
.photo-finish .flash { animation: flash 1.2s ease-out 2; }
@keyframes flash { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }

/* --------------------------------------------------------------------------
   6. Plinko
   -------------------------------------------------------------------------- */
.plinko-stage { justify-items: center; }
.plinko-holder { position: relative; width: min(100%, 520px); }
.plinko-holder canvas { width: 100%; height: auto; border-radius: var(--r); background: var(--surface-2); }
.plinko-bins { display: flex; gap: 2px; margin-top: 4px; }
.plinko-bins .bin {
  flex: 1 1 0; min-width: 0; text-align: center;
  font-size: .68rem; font-weight: 600; color: #fff;
  padding: .3rem .1rem; border-radius: 0 0 var(--r-sm) var(--r-sm);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  background: var(--bc, var(--brand)); transition: transform .15s ease, filter .15s ease;
}
.plinko-bins .bin.is-hit { transform: translateY(-5px); filter: brightness(1.25); }

/* --------------------------------------------------------------------------
   7. Name slots
   -------------------------------------------------------------------------- */
.slots-stage { justify-items: center; }
.slot-machine {
  --slot-h: 66px;
  background: linear-gradient(160deg, #ffd76e, #ff9f1a 55%, #f0563a);
  padding: 1.1rem; border-radius: var(--r-lg);
  box-shadow: inset 0 2px 0 rgba(255,255,255,.5), var(--sh-2);
  width: min(100%, 480px);
}
.slot-window {
  display: flex; gap: .4rem; padding: .4rem;
  background: #2a2440; border-radius: var(--r);
  box-shadow: inset 0 3px 10px rgba(0,0,0,.55);
  position: relative; overflow: hidden;
}
.reel { flex: 1 1 0; min-width: 0; height: calc(var(--slot-h) * 3); overflow: hidden; border-radius: var(--r-sm); background: #fff; }
.reel-strip { will-change: transform; transform: translateY(calc(var(--offset, 0) * -1px)); }
.reel-cell {
  height: var(--slot-h); display: grid; place-items: center; gap: 0;
  border-bottom: 1px solid #eee; padding: 0 .3rem; text-align: center;
}
.reel-cell .e { font-size: 1.5rem; line-height: 1; }
.reel-cell .n {
  font-size: .7rem; font-weight: 600; color: #333; max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Payline across the middle row. */
.slot-window::before, .slot-window::after {
  content: ""; position: absolute; left: 0; right: 0; height: 3px;
  background: color-mix(in srgb, var(--accent) 85%, transparent); z-index: 4; pointer-events: none;
}
.slot-window::before { top: calc(.4rem + var(--slot-h)); }
.slot-window::after  { top: calc(.4rem + var(--slot-h) * 2); }
.slot-machine.is-winner .slot-window { animation: jackpot .35s ease 3; }
@keyframes jackpot { 50% { box-shadow: inset 0 3px 10px rgba(0,0,0,.55), 0 0 0 6px rgba(255,255,255,.8); } }
.slot-lights { display: flex; justify-content: space-between; margin-bottom: .6rem; }
.slot-lights i {
  width: 12px; height: 12px; border-radius: 50%; background: #fff6; display: block;
  box-shadow: inset 0 -2px 3px rgba(0,0,0,.2);
}
.slot-machine.is-spinning .slot-lights i { animation: bulbs .6s linear infinite; }
.slot-lights i:nth-child(2n) { animation-delay: .2s; }
.slot-lights i:nth-child(3n) { animation-delay: .4s; }
@keyframes bulbs { 50% { background: #fff; box-shadow: 0 0 8px #fff; } }

/* --------------------------------------------------------------------------
   8. Dice derby
   -------------------------------------------------------------------------- */
.dice-stage { align-content: start; }
.dice-rows { display: grid; gap: .45rem; }
.dice-row { display: grid; grid-template-columns: 8.5rem 1fr 2.6rem; gap: .5rem; align-items: center; }
@media (max-width: 560px) { .dice-row { grid-template-columns: 6rem 1fr 2.4rem; } }
.dice-row .who {
  display: flex; align-items: center; gap: .35rem; font-size: .87rem; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dice-bar-track { background: var(--surface-2); border-radius: var(--r-pill); height: 26px; position: relative; overflow: hidden; }
.dice-bar {
  position: absolute; inset: 0 auto 0 0; border-radius: var(--r-pill);
  width: calc(var(--p, 0) * 100%); background: var(--dc, var(--brand));
  transition: width .45s cubic-bezier(.3, 1.2, .5, 1);
}
.dice-row .score { text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
.dice-row .die {
  position: absolute; right: .35rem; top: 50%; transform: translateY(-50%);
  font-size: 1.15rem; z-index: 2;
}
.dice-row .die.is-rolling { animation: roll .5s ease-in-out; }
@keyframes roll { 50% { transform: translateY(-50%) rotate(220deg) scale(1.3); } }
.dice-row.is-leader .who { color: var(--brand-deep); }

/* --------------------------------------------------------------------------
   9. Tournament bracket
   -------------------------------------------------------------------------- */
.bracket-stage { align-content: start; overflow-x: auto; }
.bracket { display: flex; gap: 1.1rem; min-width: min-content; padding: .3rem; }
.bracket-round { display: flex; flex-direction: column; justify-content: space-around; gap: .55rem; min-width: 148px; }
.bracket-round h3 {
  font-size: .7rem; text-transform: uppercase; letter-spacing: .07em;
  color: var(--muted); text-align: center; margin: 0 0 .2rem;
}
.match {
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface-2); overflow: hidden;
  transition: box-shadow .2s ease, transform .2s ease;
}
.match.is-live { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 55%, transparent); transform: scale(1.03); }
.seat {
  display: flex; align-items: center; gap: .35rem;
  padding: .3rem .45rem; font-size: .8rem;
  border-bottom: 1px solid var(--line);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.seat:last-child { border-bottom: 0; }
.seat.is-empty { color: var(--muted); font-style: italic; }
.seat.is-winner { background: color-mix(in srgb, var(--lime) 18%, transparent); font-weight: 600; }
.seat.is-loser { opacity: .42; text-decoration: line-through; }
.seat .bye { margin-left: auto; font-size: .68rem; color: var(--muted); }
.champion {
  align-self: center; text-align: center; padding: .7rem .9rem;
  border-radius: var(--r); background: linear-gradient(150deg, var(--sunny), var(--coral));
  color: #fff; font-weight: 600; box-shadow: var(--sh-2); min-width: 130px;
}
.champion .emo { font-size: 1.8rem; }

/* --------------------------------------------------------------------------
   10. Mystery boxes
   -------------------------------------------------------------------------- */
.boxes-stage { justify-items: center; }
.box-grid {
  display: grid; gap: .7rem; width: 100%;
  grid-template-columns: repeat(auto-fit, minmax(92px, 1fr));
  max-width: 560px;
}
.mystery-box {
  aspect-ratio: 1; border: 0; cursor: pointer; padding: .4rem;
  border-radius: var(--r); position: relative;
  background: linear-gradient(150deg, var(--bxc, var(--brand)), color-mix(in srgb, var(--bxc, var(--brand)) 55%, #000));
  color: #fff; display: grid; place-items: center; gap: .1rem;
  box-shadow: var(--sh-1);
  transform-style: preserve-3d;
  transition: transform .2s ease, box-shadow .2s ease;
}
.mystery-box:hover:not([disabled]) { transform: translateY(-5px) rotate(-2deg); box-shadow: var(--sh-2); }
.mystery-box .lid { font-size: 2rem; line-height: 1; }
.mystery-box .num { font-size: .72rem; opacity: .85; }
.mystery-box .inside { display: none; }
.mystery-box.is-open .lid, .mystery-box.is-open .num { display: none; }
.mystery-box.is-open .inside { display: grid; place-items: center; gap: .1rem; animation: cardin .3s ease both; }
.mystery-box.is-open .inside .n { font-size: .74rem; font-weight: 600; overflow-wrap: anywhere; }
.mystery-box.is-open .inside .e { font-size: 1.9rem; }
.mystery-box.is-open { background: var(--surface-2); color: var(--ink); border: 2px solid var(--bxc, var(--brand)); }
.mystery-box.is-prize { background: linear-gradient(150deg, var(--sunny), var(--accent)); color: #fff; border: 0; animation: cheer .9s ease-in-out infinite; }
.mystery-box.is-shuffling { animation: shuffle .32s ease; }
@keyframes shuffle { 50% { transform: translateY(-14px) scale(.9) rotate(6deg); } }
.mystery-box[disabled] { cursor: default; }

/* --------------------------------------------------------------------------
   11. Balloon pop
   -------------------------------------------------------------------------- */
.balloon-stage { justify-items: center; }
.balloon-grid {
  display: flex; flex-wrap: wrap; gap: .8rem 1rem; justify-content: center;
  padding: .5rem 0 1.4rem;
}
.balloon {
  position: relative; width: 74px; padding-bottom: 18px;
  animation: float 3.4s ease-in-out infinite;
  transition: opacity .3s ease;
}
.balloon:nth-child(3n) { animation-delay: -1.1s; }
.balloon:nth-child(3n+1) { animation-delay: -2.3s; }
@keyframes float { 50% { transform: translateY(-9px) rotate(2deg); } }
.balloon .body {
  width: 74px; height: 88px; border-radius: 50% 50% 48% 48%;
  background: radial-gradient(circle at 32% 28%, color-mix(in srgb, var(--blc, var(--brand)) 30%, #fff), var(--blc, var(--brand)) 68%);
  display: grid; place-items: center; gap: 0;
  box-shadow: inset -6px -8px 14px rgba(0,0,0,.16);
  position: relative;
}
.balloon .body::after {  /* the knot */
  content: ""; position: absolute; bottom: -6px; left: 50%; transform: translateX(-50%);
  border: 5px solid transparent; border-top-color: var(--blc, var(--brand));
}
.balloon .string {
  position: absolute; bottom: 0; left: 50%; width: 1px; height: 18px;
  background: var(--line-strong);
}
.balloon .e { font-size: 1.5rem; line-height: 1; }
.balloon .n {
  font-size: .64rem; font-weight: 600; color: #fff; max-width: 64px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  text-shadow: 0 1px 2px rgba(0,0,0,.35);
}
.balloon.is-popped { animation: popout .42s ease-out forwards; }
@keyframes popout {
  35% { transform: scale(1.22); }
  100% { transform: scale(.2) rotate(18deg); opacity: 0; }
}
.balloon.is-winner { animation: cheer 1s ease-in-out infinite; }
.balloon.is-winner .body { box-shadow: inset -6px -8px 14px rgba(0,0,0,.16), 0 0 0 5px color-mix(in srgb, var(--sunny) 70%, transparent); }
.pop-burst {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-size: 2rem; pointer-events: none; animation: burst .5s ease-out forwards;
}
@keyframes burst { from { transform: scale(.4); opacity: 1; } to { transform: scale(1.9); opacity: 0; } }

/* --------------------------------------------------------------------------
   Shared: "how to play" block under every game (also earns the page some
   indexable text, which a pure-canvas page badly needs)
   -------------------------------------------------------------------------- */
.game-notes { margin-top: 2rem; }
.game-notes .cols { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.game-notes h2 { font-size: 1.15rem; }
.game-notes h3 { font-size: .98rem; margin-bottom: .3rem; }
.game-notes p, .game-notes li { font-size: .92rem; color: var(--ink-soft); }

/* --------------------------------------------------------------------------
   12. Ladder Draw (amidakuji)
   -------------------------------------------------------------------------- */
.ladder-stage { align-content: start; }
.ladder-holder { width: 100%; }
.ladder-holder canvas { width: 100%; display: block; }

/* The labels above and below the ladder are laid out with the same equal
   flex basis the canvas uses for its columns, so chip centres line up with
   the vertical lines at any width. */
.ladder-row { display: flex; width: 100%; }
.ladder-label {
  flex: 1 1 0; min-width: 0; text-align: center;
  display: grid; justify-items: center; gap: .1rem;
  padding: .3rem .1rem; font-size: .7rem;
}
.ladder-label .e { font-size: 1.1rem; line-height: 1; }
.ladder-label .n {
  font-weight: 600; color: var(--lbc, var(--ink));
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ladder-label.is-prize .e { font-size: .95rem; color: var(--muted); }
.ladder-label.is-trophy .e { font-size: 1.4rem; }
.ladder-label.is-trophy { animation: cheer 1.4s ease-in-out infinite; }

/* --------------------------------------------------------------------------
   13. High Card Draw
   -------------------------------------------------------------------------- */
.cards-stage { justify-items: center; }
.card-table {
  display: flex; flex-wrap: wrap; gap: .9rem; justify-content: center;
  padding: .5rem 0;
}
.card-seat { display: grid; justify-items: center; gap: .35rem; transition: opacity .3s ease, transform .3s ease; }
.card-seat.is-out { opacity: .38; }
.card-seat.is-high { transform: translateY(-8px); }

.playing-card { width: 74px; height: 104px; perspective: 700px; }
.card-inner {
  position: relative; width: 100%; height: 100%;
  transition: transform .5s cubic-bezier(.4, 1.4, .5, 1);
  transform-style: preserve-3d;
}
.playing-card.is-flipped .card-inner { transform: rotateY(180deg); }
.card-face {
  position: absolute; inset: 0; border-radius: 9px;
  backface-visibility: hidden; -webkit-backface-visibility: hidden;
  display: grid; place-items: center;
  box-shadow: var(--sh-1); border: 1px solid var(--line-strong);
}
.card-back {
  background:
    repeating-linear-gradient(45deg, var(--brand) 0 7px, var(--brand-deep) 7px 14px);
  color: #fff; font-size: 1.7rem;
}
.card-front {
  background: #fff; color: #1b1733; transform: rotateY(180deg);
  font-weight: 600;
}
.card-front.is-red { color: #d61f4b; }
.card-front .pip { font-size: 2.1rem; line-height: 1; }
.card-front .corner { position: absolute; font-size: .72rem; letter-spacing: -.02em; }
.card-front .corner.tl { top: 5px; left: 6px; }
.card-front .corner.br { bottom: 5px; right: 6px; transform: rotate(180deg); }

.card-owner {
  display: flex; align-items: center; gap: .25rem;
  font-size: .74rem; font-weight: 600; color: var(--cc, var(--ink));
  max-width: 88px;
}
.card-owner .n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.card-seat.is-high .card-owner { color: var(--lime); }

/* --------------------------------------------------------------------------
   14. Grabber (the claw machine)
   -------------------------------------------------------------------------- */
.claw-stage { justify-items: center; }
.cabinet {
  position: relative; width: min(100%, 520px); height: 330px;
  border-radius: var(--r);
  background: linear-gradient(180deg, #2b2450, #1b1638 70%);
  overflow: hidden;
  box-shadow: inset 0 0 0 4px rgba(255,255,255,.06), var(--sh-2);
}
.claw-rail {
  position: absolute; top: 16px; left: 0; right: 0; height: 4px;
  background: rgba(255,255,255,.22);
}
.claw {
  position: absolute; top: 16px; left: var(--cx, 50%); z-index: 4;
  transform: translateX(-50%);
  transition: left .85s cubic-bezier(.4, 0, .3, 1);
}
.claw .cable {
  width: 3px; height: calc(var(--cy, 0px) + 10px);
  background: rgba(255,255,255,.5); margin: 0 auto;
  transition: height .85s cubic-bezier(.5, 0, .4, 1);
}
.claw-head { position: relative; width: 46px; height: 30px; margin-left: -21px; }
.claw-head .prong {
  position: absolute; top: 0; width: 5px; height: 26px; border-radius: 3px;
  background: linear-gradient(180deg, #cfd3e6, #8f93ad);
  transition: transform .25s ease;
  transform-origin: top center;
}
.claw-head .prong.left  { left: 8px;  transform: rotate(-26deg); }
.claw-head .prong.right { right: 8px; transform: rotate(26deg); }
.claw.is-closed .prong.left  { transform: rotate(-6deg); }
.claw.is-closed .prong.right { transform: rotate(6deg); }
.claw-head .held {
  position: absolute; top: 20px; left: 50%; transform: translateX(-50%);
  font-size: 1.5rem;
}

.capsule {
  position: absolute; bottom: var(--py, 0px); left: var(--px, 50%); z-index: 2;
  transform: translateX(-50%);
  width: 58px; padding: .3rem .2rem; border-radius: 50% 50% 44% 44%;
  background: radial-gradient(circle at 34% 28%, color-mix(in srgb, var(--pc, #888) 30%, #fff), var(--pc, #888) 70%);
  display: grid; justify-items: center; gap: 0;
  box-shadow: inset -4px -5px 10px rgba(0,0,0,.22), 0 3px 6px rgba(0,0,0,.35);
  transition: left .85s cubic-bezier(.4, 0, .3, 1), bottom .5s ease;
}
.capsule .e { font-size: 1.15rem; line-height: 1.1; }
.capsule .n {
  font-size: .58rem; font-weight: 600; color: #fff; max-width: 52px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  text-shadow: 0 1px 2px rgba(0,0,0,.5);
}
.capsule.is-held { bottom: 232px; z-index: 3; }
.capsule.is-dropped { animation: capsuledrop .42s ease-in; }
@keyframes capsuledrop { from { transform: translateX(-50%) translateY(-120px); } }
.capsule.is-won { bottom: 6px; animation: cheer 1s ease-in-out infinite; z-index: 5; }

.chute {
  position: absolute; bottom: 0; left: 0; width: 74px; height: 62px;
  border-radius: 0 var(--r) 0 var(--r);
  background: rgba(255,255,255,.1);
  border-right: 3px solid rgba(255,255,255,.2);
  display: grid; place-items: end center; padding-bottom: .35rem; font-size: 1.2rem;
}

/* --------------------------------------------------------------------------
   15. Tight Lines (the fishing pond)
   -------------------------------------------------------------------------- */
.fishing-stage { justify-items: center; }
.pond {
  position: relative; width: 100%; height: 320px; overflow: hidden;
  border-radius: var(--r);
  background: linear-gradient(180deg, #bfe9ff 0 18%, #4aaede 18%, #1d6ea8 100%);
  cursor: default;
}
.pond.can-strike { cursor: pointer; box-shadow: inset 0 0 0 4px var(--sunny); }
/* The waterline */
.pond::before {
  content: ""; position: absolute; left: 0; right: 0; top: 18%; height: 3px;
  background: rgba(255,255,255,.55);
}

.fish {
  position: absolute; top: var(--fy, 50%); left: 0; z-index: 2;
  display: flex; align-items: center; gap: .25rem;
  animation: swim var(--dur, 10s) linear var(--delay, 0s) infinite;
  will-change: transform;
}
@keyframes swim {
  0%   { transform: translateX(-70px) scaleX(1); }
  49%  { transform: translateX(calc(100vw - 40px)) scaleX(1); }
  50%  { transform: translateX(calc(100vw - 40px)) scaleX(-1); }
  99%  { transform: translateX(-70px) scaleX(-1); }
  100% { transform: translateX(-70px) scaleX(1); }
}
.fish .e { font-size: 1.5rem; }
.fish .rider { font-size: .8rem; margin-left: -.5rem; }
.fish .n {
  font-size: .68rem; font-weight: 600; color: #fff;
  background: color-mix(in srgb, var(--fc, #000) 75%, transparent);
  padding: 0 .35rem; border-radius: var(--r-pill);
  white-space: nowrap;
}
/* A hooked fish stops patrolling and darts to the float. */
.fish.is-hooked {
  animation: none; left: var(--fx, 50%); transform: translateX(-50%);
  transition: left .5s ease, top .5s ease;
  z-index: 5;
}
.fish.is-hooked .e { animation: struggle .2s ease-in-out infinite; }
@keyframes struggle { 50% { transform: rotate(-18deg); } }
.fish.is-landed { animation: reelin .9s ease-in forwards; }
@keyframes reelin { to { top: 4%; transform: translateX(-50%) scale(1.5) rotate(-20deg); } }

.bobber {
  position: absolute; top: 0; left: var(--bx, 50%); z-index: 4;
  transform: translateX(-50%);
  opacity: 0; transition: opacity .2s ease, left .3s ease;
  display: grid; justify-items: center;
}
.bobber.is-cast { opacity: 1; }
.bobber .line { width: 1px; height: 52px; background: rgba(255,255,255,.75); }
.bobber .cork { font-size: .9rem; margin-top: -3px; }
.bobber.is-bobbing { animation: bob .28s ease-in-out infinite; }
@keyframes bob { 50% { transform: translateX(-50%) translateY(7px); } }
