@import url('https://fonts.googleapis.com/css2?family=Lato:wght@500;900&display=swap');

* {
  box-sizing: border-box;
  margin: 0; padding: 0;
  -webkit-tap-highlight-color: transparent;
}

/* Unified font across the entire UI — Lato everywhere. Form controls
   (button, input, select, textarea) don't inherit font from body by
   default, so we enforce it explicitly here. */
body, button, input, select, textarea {
  font-family: 'Lato', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}

/* Google Material Symbols Outlined — loaded from Google Fonts in the
   HTML head. Text nodes like "info", "home", … become the glyphs via the
   `liga` OpenType feature. We default to wght 500 so the icon lines read
   cleanly against both light and dark backgrounds. */
.material-symbols-outlined {
  font-family: 'Material Symbols Outlined';
  font-weight: normal;
  font-style: normal;
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  display: inline-block;
  white-space: nowrap;
  word-wrap: normal;
  direction: ltr;
  -webkit-font-feature-settings: 'liga';
  font-feature-settings: 'liga';
  -webkit-font-smoothing: antialiased;
  font-variation-settings: 'FILL' 0, 'wght' 500, 'GRAD' 0, 'opsz' 24;
  user-select: none;
}

:root {
  --reel-bg:    transparent;
  --row-height: 79px;   /* updated by JS to actual reel width (square cells) */
  --sym-size:   65px;
}

/* ── Background layers ── */
.bg-dark {
  position: absolute;
  inset: 0;
  background: #0a0a0c;
  z-index: 0;
}
.bg-image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
  pointer-events: none;
  /* Light desat to pull the blue tint down without blacking out the art. */
  filter: saturate(0.75);
}

html, body {
  height: 100%;
  overflow: hidden;
  font-family: 'Lato', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  background: #0a0a0c;
}

/* ── Custom scrollbar — themed to match the dark glass UI.
   Firefox uses `scrollbar-*` shorthand; Chromium/Safari uses
   `::-webkit-scrollbar-*` pseudo-elements. Only applied to elements
   that actually scroll (modal bodies, etc.) so we don't decorate the
   page itself (html/body has overflow:hidden anyway). */
.modal-sheet,
.modal-sheet *,
.autoplay-body,
.info-body {
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.22) transparent;
}
.modal-sheet ::-webkit-scrollbar,
.autoplay-body::-webkit-scrollbar,
.info-body::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}
.modal-sheet ::-webkit-scrollbar-track,
.autoplay-body::-webkit-scrollbar-track,
.info-body::-webkit-scrollbar-track {
  background: rgba(255, 255, 255, 0.04);
  border-radius: 999px;
  margin: 6px 0;
}
.modal-sheet ::-webkit-scrollbar-thumb,
.autoplay-body::-webkit-scrollbar-thumb,
.info-body::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.05);
}
.modal-sheet ::-webkit-scrollbar-thumb:hover,
.autoplay-body::-webkit-scrollbar-thumb:hover,
.info-body::-webkit-scrollbar-thumb:hover {
  background: rgba(255, 255, 255, 0.32);
}
.modal-sheet ::-webkit-scrollbar-corner,
.autoplay-body::-webkit-scrollbar-corner,
.info-body::-webkit-scrollbar-corner {
  background: transparent;
}

/* ── Preloader — Figma 359:13459 ──
   Near-black background (rgba(0,0,0,0.75) over the fruit tile + blur),
   single logo PNG, 285×10 thin bar with #0f2749 bg and the imported
   purple-diagonal gradient fill from bar-fill.svg. */
#preloader {
  position: fixed; inset: 0; z-index: 999;
  background:
    radial-gradient(60% 50% at 50% 35%, #1d1408 0%, #07050a 60%, #050308 100%),
    #050308;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 36px; transition: opacity 0.4s;
}
#preloader.fade-out { opacity: 0; pointer-events: none; }

/* Anubis Rising hero logo on the preloader. */
.pre-logo {
  width: clamp(120px, 18vw, 180px);
  filter: drop-shadow(0 8px 20px rgba(0,0,0,0.65))
          drop-shadow(0 0 18px rgba(245, 197, 66, 0.18));
}

/* Striped progress bar — gold frame, dark inner track, gold-amber fill
   with diagonal sheen stripes. Matches the Figma loader (525:4035) style
   recoloured into the Anubis Rising gold palette. */
.pre-bar {
  position: relative;
  width: clamp(260px, 60vw, 520px);
  height: 28px;
  border-radius: 999px;
  background: #0a0606;
  /* Gold double-stroke frame: outer warm bevel, inner thin highlight. */
  box-shadow:
    inset 0 0 0 2px #2a1a08,
    inset 0 0 0 4px #f5c542,
    inset 0 0 0 5px #b07e1a,
    inset 0 0 0 7px #2a1a08,
    0 4px 14px rgba(0, 0, 0, 0.55),
    0 0 24px rgba(245, 197, 66, 0.18);
  overflow: hidden;
  padding: 7px;
  box-sizing: border-box;
}
.pre-fill {
  position: relative;
  height: 100%;
  width: 0%;
  border-radius: 999px;
  background:
    /* diagonal stripes (lighter sheen over base gold) */
    repeating-linear-gradient(
      -55deg,
      rgba(255, 240, 170, 0.40) 0 10px,
      rgba(255, 240, 170, 0)    10px 22px
    ),
    linear-gradient(180deg, #ffe98a 0%, #f5c542 45%, #b07e1a 100%);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 200, 0.45),
    inset 0 -2px 4px rgba(120, 70, 8, 0.55),
    0 0 14px rgba(245, 197, 66, 0.45);
  transition: width 0.3s ease;
  background-size: 32px 32px, 100% 100%;
  /* slow shimmer of the stripes — subtle motion while loading */
  animation: tomb-loader-stripes 1.4s linear infinite;
}
@keyframes tomb-loader-stripes {
  from { background-position: 0 0, 0 0; }
  to   { background-position: 32px 0, 0 0; }
}

/* ── Welcome carousel — Figma 359:13060 (Step1) / 13465 (Step2) /
   13831 (Step3). Near-black bg matching Figma's rgba(0,0,0,0.75) over
   damask + 4.8px backdrop-blur. Logo PNG, owl-carousel drag, dots,
   purple pill CTA. */
#tapToStart {
  position: fixed; inset: 0; z-index: 998;
  background: #0a0a0c;
  color: #fff;
  font-family: 'Lato', sans-serif;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  padding: 48px 24px;
  /* Generous vertical breathing room between logo / CONTINUE / SOUND.
     Same gap inside .tts-bottom--stack keeps the rhythm uniform. */
  gap: 56px;
  overflow: hidden;
  transition: opacity 0.45s ease;
}
#tapToStart.fade-out { opacity: 0; pointer-events: none; }

/* ── Floating blurred fruits — energetic drift/rotate/scale per fruit.
   Durations 4–6s (was 11–16s) and travel distances ~±60px so the fruits
   visibly "fly" instead of barely nudging. */
.tts-bg { position: absolute; inset: 0; pointer-events: none; z-index: 0; }
.tts-fruit {
  position: absolute;
  /* Figma fruits are very dim on the dark bg — heavy blur + lower alpha */
  filter: blur(14px) saturate(1.1);
  opacity: 0.45;
  object-fit: contain;
  pointer-events: none;
  will-change: transform;
  animation: tts-float var(--dur, 5s) ease-in-out infinite alternate;
}
@keyframes tts-float {
  0%   { transform: translate(var(--x0,0), var(--y0,0)) rotate(var(--r0,-15deg)) scale(var(--s0, 0.94)); }
  50%  { transform: translate(var(--x1,0), var(--y1,0)) rotate(var(--r1, 12deg)) scale(var(--s1, 1.08)); }
  100% { transform: translate(var(--x2,0), var(--y2,0)) rotate(var(--r2, -6deg)) scale(var(--s2, 1.00)); }
}
/* Each fruit gets distinct drift path via CSS vars — no two in sync. */
.tts-fruit.f1 { top: 8%;  left: -6%;  width: 150px; height: 150px; --x0:-30px; --y0:-40px; --x1: 40px; --y1: 30px; --x2:-10px; --y2: 20px; --r0:-18deg; --r1: 14deg; --r2:-6deg;  --dur:5.2s; animation-delay: -0.6s; }
.tts-fruit.f2 { top: 12%; left: 46%;  width: 170px; height: 170px; --x0: 20px; --y0:-50px; --x1:-40px; --y1: 40px; --x2: 10px; --y2:-10px; --r0:  8deg; --r1:-16deg; --r2: 4deg;  --dur:4.6s; animation-delay: -1.8s; }
.tts-fruit.f3 { top: 26%; right:-7%;  width: 140px; height: 140px; --x0: 40px; --y0: 20px; --x1:-50px; --y1:-60px; --x2: 20px; --y2: 40px; --r0:-10deg; --r1: 18deg; --r2:-4deg;  --dur:5.8s; animation-delay: -3.1s; }
.tts-fruit.f4 { top: 58%; left: -6%;  width: 130px; height: 130px; --x0:-20px; --y0: 50px; --x1: 50px; --y1:-30px; --x2:-10px; --y2: 30px; --r0: 14deg; --r1:-20deg; --r2: 8deg;  --dur:4.8s; animation-delay: -0.3s; }
.tts-fruit.f5 { top: 54%; left: 42%;  width: 110px; height: 110px; --x0: 30px; --y0:-40px; --x1:-30px; --y1: 50px; --x2: 40px; --y2:-10px; --r0:-12deg; --r1: 16deg; --r2:-8deg;  --dur:5.5s; animation-delay: -2.4s; }
.tts-fruit.f6 { top: 78%; right:28%;  width: 120px; height: 120px; --x0:-30px; --y0: 30px; --x1: 50px; --y1:-50px; --x2:-10px; --y2: 40px; --r0: 16deg; --r1:-14deg; --r2: 6deg;  --dur:4.4s; animation-delay: -1.2s; }
.tts-fruit.f7 { top: 70%; right: 6%;  width: 130px; height: 130px; --x0: 40px; --y0:-20px; --x1:-30px; --y1: 50px; --x2: 10px; --y2:-30px; --r0:-16deg; --r1: 10deg; --r2:-12deg; --dur:6.0s; animation-delay: -4.5s; }
.tts-fruit.f8 { top: 86%; left: 14%;  width: 110px; height: 110px; --x0:-20px; --y0:-50px; --x1: 30px; --y1: 40px; --x2:-40px; --y2: 10px; --r0: 12deg; --r1:-18deg; --r2: 4deg;  --dur:4.2s; animation-delay: -2.8s; }

/* ── Logo — same wordmark as the in-game logo (logo.svg + sparkles) so
   welcome and game share one brand mark. The wrapper is positioned so
   the absolute-positioned sparkles register against it. ── */
.tts-logo-wrap {
  position: relative; z-index: 2;
  width: clamp(120px, 18vw, 180px);
  margin-top: 0;
  display: inline-block;
}
.tts-logo {
  display: block;
  width: 100%;
  filter: drop-shadow(0 6px 14px rgba(0,0,0,0.65));
}

/* ── Slides — owl-carousel-style drag. Viewport has a fixed content
   height (hero + caption) so the layout doesn't bounce between slides
   of different hero aspect ratios. ── */
.tts-slides {
  position: relative; z-index: 2;
  flex: 0 0 auto;
  width: 100%;
  /* Hero 192 + caption ~48 + gap 16 ≈ 256 — keeps things tight */
  height: 280px;
  overflow: hidden;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
  cursor: grab;
  /* Inter-item spacing now lives on the parent #tapToStart {gap}. */
  margin: 0;
}
.tts-slides:active { cursor: grabbing; }
.tts-track {
  display: flex;
  height: 100%;
  width: 100%;
  transform: translate3d(0, 0, 0);
  transition: transform 0.42s cubic-bezier(0.22, 0.61, 0.36, 1);
  will-change: transform;
}
.tts-track.is-dragging { transition: none; }
.tts-slide {
  flex: 0 0 100%;
  min-width: 100%;
  display: flex; flex-direction: column;
  align-items: center; justify-content: flex-start;
  gap: 16px;
  padding: 0 8px;
  box-sizing: border-box;
}

/* Hero image per slide — native Figma box (271.5 × 192, aspect 140:99).
   All three assets (slide1 736×424, slide2 543×384, slide3 615×371) are
   fitted inside this box via `object-fit: contain` so they don't blow
   up past the Figma content area. */
.tts-hero {
  display: block;
  width: 271.5px;
  height: 192px;
  aspect-ratio: 140 / 99;
  /* The slide images have different native aspects, so `cover` was
     cropping the wider first slide. Back to `contain` — the BOX size
     is constant across slides (no layout shift on swipe); the drawn
     image inside may letterbox slightly, but no content gets cut. */
  object-fit: contain;
  filter: drop-shadow(0 6px 14px rgba(0,0,0,0.5));
}
.tts-caption {
  /* Welcome-slide caption — unified on Lato across the whole UI. */
  font-family: 'Lato', sans-serif;
  font-weight: 900;
  font-size: 18px;
  color: #ffffff;
  text-align: center;
  line-height: normal;
  max-width: 319px;
  margin: 4px auto 0;
}

/* ── Slider pagination — chevron arrows flanking the dots ──────────── */
.tts-pagination {
  position: relative; z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 14px;
  margin: 16px auto 0;          /* small gap below the slider on mobile */
}
.tts-nav-arrow {
  width: 32px; height: 32px;
  padding: 0;
  background: transparent;
  border: none;
  color: rgba(255, 255, 255, 0.55);
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  transition: color 0.15s ease, background 0.15s ease, opacity 0.15s ease;
}
.tts-nav-arrow:hover  { color: #fff; }
.tts-nav-arrow:active { transform: scale(0.92); }
.tts-nav-arrow:disabled {
  opacity: 0.32;
  cursor: default;
  pointer-events: none;
}
.tts-nav-arrow .material-symbols-outlined { font-size: 26px; line-height: 1; }

/* ── Pagination dots — Figma 360:15120 SliderSteps (gap 8px, 10×10).
   No `background` transition — browsers can't cross-fade to a gradient
   smoothly and the flash reads as flicker. State swap is instant. ── */
.tts-dots {
  display: flex; gap: 8px; align-items: center;
  margin: 0;
}
.tts-dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: rgba(255,255,255,0.32);           /* imgProperty1Inactive */
}
.tts-dot.is-active {
  /* imgProperty1Active: linear-gradient(180deg, #a23ec9 0%, #9602cf 50%, #a23ec9 100%) */
  background: linear-gradient(180deg, #a23ec9 0%, #9602cf 50%, #a23ec9 100%);
}

/* ── Bottom row on the welcome — SOUND toggle | CONTINUE | VOLATILITY.
   On mobile the side widgets are hidden so only CONTINUE shows; on
   desktop the row spans the slot width and lays the three slots out. */
.tts-bottom {
  position: relative; z-index: 2;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
}
.tts-sound, .tts-volatility { display: none; }
/* SOUND toggle on the welcome — uses the same `.ui-switch` widget as
   the in-game menu so the splash and the game share one switcher. */
.tts-sound {
  align-items: center;
  gap: 12px;
  background: transparent;
  border: none;
  padding: 8px 12px;
  cursor: pointer;
  color: var(--ui-icon);
  font: inherit;
  border-radius: 14px;
  /* No hover-fill — the icon + label + switch alone signal the affordance,
     and a hover halo on a transparent button felt heavier than its weight. */
}
.tts-sound-icon { font-size: 22px; line-height: 1; color: var(--ui-icon); }
.tts-sound-label {
  font-family: 'Lato', sans-serif; font-size: 12px; font-weight: 800;
  letter-spacing: 1.4px; text-transform: uppercase;
  color: var(--ui-icon);
}
.tts-vol-label {
  font-family: 'Lato', sans-serif; font-size: 11px; font-weight: 700;
  letter-spacing: 1.4px; text-transform: uppercase;
  color: var(--ui-label);
}
.tts-vol-bolts { display: inline-flex; gap: 4px; align-items: center; }
.tts-vol-bolt {
  /* Figma `bolt` (node 385:2287) — recoloured to match the welcome's
     gold palette via `background` + `mask-image`. The previous CSS
     polygon was an approximation of the shape; the SVG mask gives the
     exact silhouette from the design file. */
  width: 12px; height: 18px;
  background: linear-gradient(180deg, #ffe08a 0%, #ffd055 50%, #f0a500 100%);
  -webkit-mask: url('assets/icon-bolt.svg') center / contain no-repeat;
          mask: url('assets/icon-bolt.svg') center / contain no-repeat;
  filter: drop-shadow(0 0 4px rgba(255, 208, 85, 0.55));
}

/* ── Purple pill CTA — Figma 352:8735 / 352:8736 ──
   Gradient: from #a23ec9 (35.76%) → via #9602cf (45.78%) → to #a23ec9 (125%),
   vertical (to bottom). 2px solid #b1741e border. Border-radius 26px.
   Text: Roboto Black 18px white, tracking 1px. */
.tts-btn {
  position: relative; z-index: 2;
  border: 2px solid #5a3f20; cursor: pointer;
  padding: 15px 40px;
  min-width: 240px;
  border-radius: 999px;
  font-family: 'Lato', sans-serif;
  font-weight: 900;
  font-size: 18px;
  line-height: 18px;
  letter-spacing: 1.5px;
  color: #2a1a08;
  background: linear-gradient(
    to bottom,
    #ffe98a 0%,
    #f5c542 45%,
    #b07e1a 100%
  );
  text-shadow: none;
  box-shadow:
    0 6px 16px rgba(245, 197, 66, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.55),
    inset 0 -2px 4px rgba(120, 70, 8, 0.45);
  transition: transform 0.12s ease, filter 0.12s ease;
}
.tts-btn:active { transform: translateY(1px); filter: brightness(0.92); }
.tts-btn:hover  { filter: brightness(1.06); }

/* Hide the carousel viewport until we have copy to show, but keep its
   elements in the DOM so the welcome init JS doesn't crash. */
.tts-slides.tts-slides--hidden { display: none !important; }

/* ── History button (replaces Autoplay slot in Anubis Rising).
   Visual is the SAME .ui-round shell as Menu — round 56 px chip with a
   26 px Material Symbols glyph. Always disabled (placeholder until the
   history view ships); the disabled opacity matches the bet +/- buttons
   (.ui-step:disabled @ opacity 0.35) instead of the generic .ui-round
   default (0.4) so all permanently-inert chrome reads at the same weight.
   The `button#autoplayBtn` qualifier wins over `.ui-round:disabled`. */
button#autoplayBtn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

/* ── Anubis Rising — START / NEW / TAKE button (idle / busted / cashed)
   in gold; CASH OUT (playing-with-cleared) in emerald green. Both share
   the shimmer animation. */
.ui-spin[data-tomb-phase="idle"],
.ui-spin[data-tomb-phase="busted"],
.ui-spin[data-tomb-phase="cashed"] {
  background:
    linear-gradient(180deg, #ffe98a 0%, #f5c542 50%, #b07e1a 100%) !important;
  border: 2px solid #b07e1a !important;
  /* Replaced the dark "moat" with a deep-gold ring so the black outline
     becomes part of the gold styling. */
  box-shadow:
    0 0 0 4px #6a4a1a,
    0 0 0 6px #2a1a08,
    0 8px 22px rgba(245, 197, 66, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.6),
    inset 0 -2px 4px rgba(120, 70, 8, 0.5) !important;
  position: relative;
  overflow: hidden;
}
.ui-spin[data-tomb-phase="playing"]:not(:disabled) {
  background:
    linear-gradient(180deg, #58e088 0%, #2db657 50%, #0f6b34 100%) !important;
  border: 2px solid #0f6b34 !important;
  box-shadow:
    0 0 0 4px #18794a,
    0 0 0 6px #0a3a22,
    0 8px 24px rgba(45, 182, 87, 0.65),
    inset 0 1px 0 rgba(255, 255, 255, 0.55),
    inset 0 -2px 4px rgba(10, 64, 34, 0.55) !important;
  position: relative;
  overflow: hidden;
}
/* Idle/busted/cashed gold START button always shimmers — a diagonal
   white sheen sweeps across like sunlight glinting off polished gold.
   Hover keeps the soft pulse but the shine itself is permanent so
   touch users see the same effect. */
.ui-spin[data-tomb-phase="idle"]:hover,
.ui-spin[data-tomb-phase="busted"]:hover,
.ui-spin[data-tomb-phase="cashed"]:hover {
  animation: tomb-btn-pulse 2.6s ease-in-out infinite;
}
.ui-spin[data-tomb-phase="playing"]:not(:disabled):hover {
  animation: tomb-btn-pulse-green 2.0s ease-in-out infinite;
}
.ui-spin[data-tomb-phase="idle"]::before,
.ui-spin[data-tomb-phase="busted"]::before,
.ui-spin[data-tomb-phase="cashed"]::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: linear-gradient(
    100deg,
    transparent 30%,
    rgba(255, 255, 255, 0.55) 50%,
    transparent 70%
  );
  background-size: 200% 100%;
  background-position: -100% 0;
  pointer-events: none;
  z-index: 1;
  animation: tomb-spin-shine 3.4s ease-in-out infinite;
}
.ui-spin[data-tomb-phase="playing"]:not(:disabled):hover::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: linear-gradient(
    100deg,
    transparent 30%,
    rgba(255, 255, 255, 0.55) 50%,
    transparent 70%
  );
  background-size: 200% 100%;
  background-position: -100% 0;
  pointer-events: none;
  z-index: 1;
  animation: tomb-spin-shine 2.6s ease-in-out infinite;
}
@keyframes tomb-btn-pulse-green {
  0%, 100% {
    box-shadow:
      0 0 0 4px var(--ui-spin-moat),
      0 8px 24px rgba(45, 182, 87, 0.55),
      inset 0 1px 0 rgba(255, 255, 255, 0.55),
      inset 0 -2px 4px rgba(10, 64, 34, 0.55);
  }
  50% {
    box-shadow:
      0 0 0 4px var(--ui-spin-moat),
      0 8px 32px rgba(88, 224, 136, 0.85),
      inset 0 1px 0 rgba(255, 255, 255, 0.65),
      inset 0 -2px 4px rgba(10, 64, 34, 0.55);
  }
}
@keyframes tomb-spin-shine {
  0%   { background-position: -100% 0; }
  60%  { background-position: 200% 0; }
  100% { background-position: 200% 0; }
}
/* Disabled CASH OUT (round just started — no rows cleared yet). Muted
   sandstone tone so it reads as inactive but stays in the Egyptian
   palette. No shimmer, no pulse. */
.ui-spin[data-tomb-phase="playing"]:disabled {
  background:
    linear-gradient(180deg, #b9a481 0%, #8a7553 50%, #4a3820 100%) !important;
  border: 2px solid #2a1c0a !important;
  box-shadow:
    0 0 0 4px #5a4628,
    0 0 0 6px #2a1c0a,
    0 4px 12px rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 240, 200, 0.30),
    inset 0 -2px 4px rgba(40, 26, 8, 0.55) !important;
  cursor: not-allowed;
  animation: none !important;
}
.ui-spin[data-tomb-phase="playing"]:disabled::before { content: none; }

/* Stack CONTINUE on top of SOUND with the same gap as the parent
   #tapToStart so logo → continue → sound spacing reads uniform. */
.tts-bottom.tts-bottom--stack {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 56px;
  margin: 0;
}

/* CONTINUE button — solid gold with a subtle shimmer that sweeps across
   every couple of seconds (like a watch face catching the light). */
.tts-btn.tts-btn--gold-stripe {
  position: relative;
  overflow: hidden;
  background: linear-gradient(180deg, #ffe98a 0%, #f5c542 50%, #b07e1a 100%);
  border: 2px solid #5a3f20;
  color: #2a1a08;
  text-shadow: none;
  box-shadow:
    0 6px 18px rgba(245, 197, 66, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.55),
    inset 0 -2px 4px rgba(120, 70, 8, 0.45);
  animation: tomb-btn-pulse 2.6s ease-in-out infinite;
}
.tts-btn.tts-btn--gold-stripe::before {
  /* Diagonal light streak that sweeps left → right. */
  content: '';
  position: absolute;
  top: 0; bottom: 0;
  width: 70%;
  left: -80%;
  background: linear-gradient(
    100deg,
    transparent 30%,
    rgba(255, 255, 255, 0.55) 50%,
    transparent 70%
  );
  pointer-events: none;
  animation: tomb-btn-shine 2.6s ease-in-out infinite;
}
@keyframes tomb-btn-pulse {
  0%, 100% { box-shadow:
    0 6px 18px rgba(245, 197, 66, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.55),
    inset 0 -2px 4px rgba(120, 70, 8, 0.45); }
  50%      { box-shadow:
    0 6px 26px rgba(245, 197, 66, 0.75),
    inset 0 1px 0 rgba(255, 255, 255, 0.65),
    inset 0 -2px 4px rgba(120, 70, 8, 0.45); }
}
@keyframes tomb-btn-shine {
  0%   { left: -80%; }
  60%  { left: 130%; }
  100% { left: 130%; }
}

/* ── Anubis Rising welcome — single Coming Soon card ─────────────────── */
.tts-slide.tts-slide--text {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 24px;
  text-align: center;
}
.tts-coming {
  font-family: 'Lato', sans-serif;
  font-weight: 900;
  font-size: 38px;
  line-height: 1;
  letter-spacing: 3px;
  margin: 0;
  background: linear-gradient(180deg, #ffe98a 0%, #f5c542 50%, #b07e1a 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 2px 12px rgba(245, 197, 66, 0.35));
}
.tts-caption.tts-caption--muted {
  font-family: 'Lato', sans-serif;
  font-weight: 700;
  letter-spacing: 1.4px;
  font-size: 15px;
  color: rgba(255, 240, 200, 0.72);
  margin: 0;
}

/* ── App shell ── */
.app {
  display: flex; flex-direction: column;
  align-items: center;
  width: 100%;
  height: 100dvh;
  margin: 0 auto;
  background: transparent;
  overflow: hidden;
  position: relative;
  z-index: 1;
}

/* ── Reels area ── */
.reels-area {
  position: relative;
  display: flex;
  gap: 0;
  flex-shrink: 0;
  padding: 0 12px;
  width: 100%;
  z-index: 2;
}

.reel {
  flex: 1 0 0;
  min-width: 0;
  /* Figma "One slot": 76 × 228 → aspect 1:3. Height is derived from width so 3
     square cells (aspect 1:1) always fit EXACTLY — no rounding, no 4th row. */
  aspect-ratio: 1 / 3;
  height: auto;
  overflow: hidden;
  background: radial-gradient(ellipse at center, #f8f6f0 30%, #d5d0bc 65%, #b3aa87 100%);
  border-radius: 16px;
  /* Border drawn as inset shadow → does NOT eat layout space (Figma renders it
     as a 3px overlay on top of the cells). Adjacent reels still visually merge. */
  box-shadow: inset 0 0 0 3px #cfc487;
  position: relative;
}
/* Reels 2..5 overlap by 3px (Figma mr-[-3px]) so adjacent borders merge into
   a single 3px line matching outer thickness. */
.reel + .reel { margin-left: -3px; }

/* ── Anticipation (Figma node 295:10530) ──
   Reel border imitates a neon lamp — a thicker-than-usual purple tube
   extending inward from the reel edge, with a bright white inner core
   and a deep inward-reaching purple bloom. Lamp "breathes" smoothly
   (ease-in-out, no abrupt flicker) between full intensity and a softer
   state. ::before paints a dense star-dust field that twinkles
   independently underneath. */
.reel--anticipating {
  background:
    radial-gradient(ellipse at center, rgba(130,55,159,0.95) 0%, rgba(114,48,200,0.65) 60%, rgba(161,62,200,0.45) 100%),
    radial-gradient(ellipse at center, #f8f6f0 30%, #d5d0bc 65%, #b3aa87 100%);
  z-index: 3;
  animation: reel-neon-breathe 1.8s ease-in-out infinite alternate;
}

/* Smooth breathing — two keyframes only; alternate ensures a seamless
   back-and-forth with ease-in-out, no jerks. The tube itself is 2.5px
   white core + 7px purple = 9.5px of lamp extending INWARD from the
   reel edge; an additional 22px inset bloom softly lights the symbols
   near the border. */
@keyframes reel-neon-breathe {
  from {
    box-shadow:
      inset 0 0 0 2.5px #ffffff,
      inset 0 0 0 7px  #b850d8,
      inset 0 0 18px   rgba(208,99,239,0.70),
      0 0 8px          rgba(255,200,255,0.60),
      0 0 22px         rgba(208,99,239,0.70),
      0 0 48px         rgba(161,62,200,0.45),
      0 0 90px         rgba(114,48,200,0.28);
  }
  to {
    box-shadow:
      inset 0 0 0 2.5px #ffffff,
      inset 0 0 0 7px  #d063ef,
      inset 0 0 26px   rgba(208,99,239,1.00),
      0 0 12px         rgba(255,200,255,0.90),
      0 0 30px         rgba(208,99,239,0.95),
      0 0 64px         rgba(161,62,200,0.65),
      0 0 120px        rgba(114,48,200,0.42);
  }
}

/* ── Winning reel — gold halo + sparkling stars around the border ──
   Applied to a `.reel` that contains at least one winning cell. The
   gold tube around the reel pulses; tiny twinkling stars sweep along
   the perimeter. Distinct from `.reel--anticipating` (purple neon)
   so the player reads "this reel paid" at a glance. */
.reel--winning {
  /* No z-index lift — the win-lines-overlay (z:3) and win-symbols-overlay
     (z:4) must render ABOVE the reel halo so the payline is visible. */
  animation: reel-win-glow 1.0s ease-in-out infinite alternate;
}
@keyframes reel-win-glow {
  from {
    box-shadow:
      inset 0 0 0 2px #fff7c8,
      inset 0 0 0 5px #ffd055,
      inset 0 0 14px rgba(255, 208, 85, 0.55),
      0 0 8px  rgba(255, 232, 138, 0.55),
      0 0 22px rgba(255, 208, 85, 0.55),
      0 0 44px rgba(240, 165, 0, 0.35);
  }
  to {
    box-shadow:
      inset 0 0 0 2.5px #ffffff,
      inset 0 0 0 6px #ffe08a,
      inset 0 0 22px rgba(255, 224, 138, 0.95),
      0 0 14px rgba(255, 232, 138, 0.95),
      0 0 32px rgba(255, 208, 85, 0.85),
      0 0 64px rgba(240, 165, 0, 0.55);
  }
}
/* Sparkling stars across the entire reel — two layers (::before + ::after)
   each with ~12 4-pointed sparkle SVGs scattered all over the surface.
   The two layers run different-length animations so when one dims the other
   brightens → reads as individual stars twinkling out of phase.
   Lifted to z:3 so .reel-strip (z:2) doesn't hide them. */
.reel--winning::before,
.reel--winning::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 14px;
  pointer-events: none;
  z-index: 3;
  background-repeat: no-repeat;
  /* The sparkle: bright white 4-pointed cross over a soft yellow halo.
     Same SVG used for every star — varied through background-size only. */
  --star: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><defs><radialGradient id='g'><stop offset='0' stop-color='%23ffffff' stop-opacity='0.9'/><stop offset='0.4' stop-color='%23fff5c4' stop-opacity='0.4'/><stop offset='1' stop-color='%23fff5c4' stop-opacity='0'/></radialGradient></defs><circle cx='50' cy='50' r='44' fill='url(%23g)'/><path d='M50 4 L53 47 L96 50 L53 53 L50 96 L47 53 L4 50 L47 47 Z' fill='%23ffffff'/></svg>");
}
/* Layer A — 12 stars across the whole reel (mostly larger).
   Slow 1.7s twinkle that lingers at full opacity. */
.reel--winning::before {
  background-image:
    var(--star), var(--star), var(--star), var(--star),
    var(--star), var(--star), var(--star), var(--star),
    var(--star), var(--star), var(--star), var(--star);
  background-size:
    24px, 16px, 28px, 20px,
    18px, 26px, 14px, 22px,
    16px, 30px, 18px, 24px;
  background-position:
    /* top third */
    8% 4%, 32% 9%, 58% 3%, 86% 7%,
    /* middle third — between rows */
    4% 32%, 28% 38%, 56% 30%, 88% 36%,
    /* bottom third */
    12% 64%, 42% 68%, 72% 62%, 94% 70%;
  animation: reel-win-stars-a 1.7s ease-in-out infinite;
}
/* Layer B — 12 different positions + sizes (mostly smaller).
   Faster 1.1s twinkle, opposite phase to Layer A. */
.reel--winning::after {
  background-image:
    var(--star), var(--star), var(--star), var(--star),
    var(--star), var(--star), var(--star), var(--star),
    var(--star), var(--star), var(--star), var(--star);
  background-size:
    18px, 14px, 22px, 16px,
    26px, 14px, 20px, 16px,
    24px, 18px, 16px, 22px;
  background-position:
    /* top — interleaved with layer A */
    20% 2%, 46% 8%, 72% 4%, 96% 12%,
    /* upper-mid */
    16% 22%, 44% 18%, 68% 24%, 92% 18%,
    /* lower-mid */
    8% 50%, 38% 52%, 62% 48%, 84% 52%,
    /* bottom — interleaved with layer A */
    24% 88%, 52% 94%, 78% 90%, 4% 92%;
  animation: reel-win-stars-b 1.1s ease-in-out infinite;
}
/* Layer A stays brightest in the first half, fades in the second. */
@keyframes reel-win-stars-a {
  0%   { opacity: 0.20; transform: scale(0.85); }
  35%  { opacity: 1.00; transform: scale(1.10); }
  70%  { opacity: 0.40; transform: scale(0.92); }
  100% { opacity: 0.20; transform: scale(0.85); }
}
/* Layer B is offset — brightens when A dims and vice versa. */
@keyframes reel-win-stars-b {
  0%   { opacity: 0.95; transform: scale(1.05); }
  40%  { opacity: 0.20; transform: scale(0.85); }
  75%  { opacity: 0.85; transform: scale(1.02); }
  100% { opacity: 0.95; transform: scale(1.05); }
}

/* Star dust — many small white dots scattered across the reel, twinkling
   out of phase with the neon so the tube and the dust feel independent. */
.reel--anticipating::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 16px;
  pointer-events: none;
  z-index: 4;
  background:
    radial-gradient(circle 1.5px at 8%   6%, rgba(255,255,255,0.95) 0%, rgba(255,255,255,0) 70%),
    radial-gradient(circle 1px   at 24%  3%, rgba(255,255,255,0.90) 0%, rgba(255,255,255,0) 70%),
    radial-gradient(circle 2px   at 46%  8%, rgba(255,255,255,0.95) 0%, rgba(255,255,255,0) 70%),
    radial-gradient(circle 1px   at 63%  4%, rgba(255,255,255,0.90) 0%, rgba(255,255,255,0) 70%),
    radial-gradient(circle 1.5px at 87%  7%, rgba(255,255,255,0.95) 0%, rgba(255,255,255,0) 70%),
    radial-gradient(circle 1px   at 94% 15%, rgba(255,255,255,0.85) 0%, rgba(255,255,255,0) 70%),
    radial-gradient(circle 1.5px at  4% 26%, rgba(255,255,255,0.90) 0%, rgba(255,255,255,0) 70%),
    radial-gradient(circle 1px   at 19% 38%, rgba(255,255,255,0.85) 0%, rgba(255,255,255,0) 70%),
    radial-gradient(circle 2px   at 38% 48%, rgba(255,255,255,0.95) 0%, rgba(255,255,255,0) 70%),
    radial-gradient(circle 1px   at 55% 44%, rgba(255,255,255,0.85) 0%, rgba(255,255,255,0) 70%),
    radial-gradient(circle 1.5px at 72% 55%, rgba(255,255,255,0.90) 0%, rgba(255,255,255,0) 70%),
    radial-gradient(circle 1px   at 88% 48%, rgba(255,255,255,0.85) 0%, rgba(255,255,255,0) 70%),
    radial-gradient(circle 1px   at  6% 68%, rgba(255,255,255,0.85) 0%, rgba(255,255,255,0) 70%),
    radial-gradient(circle 1.5px at 28% 74%, rgba(255,255,255,0.90) 0%, rgba(255,255,255,0) 70%),
    radial-gradient(circle 1px   at 48% 82%, rgba(255,255,255,0.85) 0%, rgba(255,255,255,0) 70%),
    radial-gradient(circle 2px   at 68% 78%, rgba(255,255,255,0.95) 0%, rgba(255,255,255,0) 70%),
    radial-gradient(circle 1px   at 82% 88%, rgba(255,255,255,0.85) 0%, rgba(255,255,255,0) 70%),
    radial-gradient(circle 1.5px at 92% 94%, rgba(255,255,255,0.90) 0%, rgba(255,255,255,0) 70%),
    radial-gradient(circle 1px   at 14% 92%, rgba(255,255,255,0.85) 0%, rgba(255,255,255,0) 70%),
    radial-gradient(circle 1px   at 34% 96%, rgba(255,255,255,0.85) 0%, rgba(255,255,255,0) 70%);
  animation: reel-stardust-twinkle 1.8s ease-in-out infinite alternate;
}
@keyframes reel-stardust-twinkle {
  0%   { opacity: 0.30; transform: scale(0.90); }
  50%  { opacity: 0.95; transform: scale(1.05); }
  100% { opacity: 0.45; transform: scale(0.95); }
}

/* Fire container — one per reel; custom canvas emitter fades in when the
   reel is anticipating. Sits BEHIND the fruits (z:1, below .reel-strip's
   z:2) so flames peek through the cells while symbols stay crisp. */
.reel-fire {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1;
  opacity: 0;
  transition: opacity 0.35s ease;
}
.reel-fire canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.reel--anticipating .reel-fire { opacity: 1; }


.reel-strip {
  display: flex; flex-direction: column;
  will-change: transform, filter;
  /* Positioned + z-index so the fire canvas (z:1) renders BEHIND the
     fruits. Without this the static strip sits below any positioned
     sibling regardless of z-index. */
  position: relative;
  z-index: 2;
}

/* Line overlay ABOVE all reel content — line renders over dimmed cells clearly.
   Winning cells (.sym-win) get their own bounce + are rendered via _drawWinLines
   at a higher layer so they appear ABOVE the line. */
.win-lines-overlay {
  position: absolute;
  top: 0;
  left: 12px;
  right: 12px;
  bottom: 0;
  z-index: 3;
  pointer-events: none;
  overflow: visible;
}

/* Copies of winning symbols rendered ABOVE the line — line appears BEHIND them */
.win-symbols-overlay {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  overflow: visible;
}
.win-sym-clone {
  position: absolute;
  object-fit: contain;
  animation: sym-win-bounce 0.7s ease-in-out infinite;
}
.win-lines-overlay svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.sym {
  height: var(--row-height);
  width: 100%;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  user-select: none;
  -webkit-user-select: none;
  overflow: hidden;          /* never let a fruit spill into adjacent cell */
  box-sizing: border-box;
  position: relative;
}
.sym-img {
  /* PNGs are pre-composited onto a 304×304 canvas with Figma proportions
     (see assets/fruit-*.png). Fill the full cell — object-fit preserves
     whatever padding the PNG encodes. */
  object-fit: contain;
  display: block;
  width: 100%;
  height: 100%;
  filter: drop-shadow(1px 1px 3px rgba(0, 0, 0, 0.45));
}
.sym-emoji {
  font-size: 68px;
  line-height: 1;
  font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
  filter: drop-shadow(0 2px 3px rgba(0,0,0,0.2));
}

/* win-line: hidden but kept in DOM for JS */
.win-line { display: none; }

/* ── Win animations (clean: no colored glow, light bounce) ── */
@keyframes sym-win-bounce {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.08); }
}
.sym-win {
  animation: sym-win-bounce 0.7s ease-in-out infinite;
}

/* Non-winning cells during win state — Figma "Not Win" variant (opacity 0.5) */
.sym-notwin {
  opacity: 0.5;
  transition: opacity 0.3s ease;
}

/* ── Win lines SVG (styling only — see primary .win-lines-overlay rule above) ── */
.win-lines-overlay svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}
.win-line-path {
  fill: none;
  stroke: #3eb134;
  stroke-width: 6;
  stroke-linecap: round;
  stroke-linejoin: round;
  filter: drop-shadow(0 0 3px rgba(62, 177, 52, 0.9));
  animation: win-line-draw 0.4s ease-out;
}

/* Fade out line and badge — symbols continue bouncing */
.win-fade-out {
  transition: opacity 0.4s ease-out, transform 0.4s ease-out;
  opacity: 0 !important;
}
.win-badge.win-fade-out {
  transform: translate(-50%, -50%) scale(0.6);
}
@keyframes win-line-draw {
  from { stroke-dasharray: 1000; stroke-dashoffset: 1000; }
  to   { stroke-dasharray: 1000; stroke-dashoffset: 0; }
}

/* ── Green pill badge with win amount (gold border, green fill) ── */
.win-badge {
  /* Figma "Win badge / Default" (node 14:2156):
       197×56 rounded pill, 4px gold border, dark-green→mid-green→dark-green
       vertical gradient, 32px italic Lato ExtraBold white text. Dynamic
       width is set per-badge by _sizeBadgeForText. */
  position: absolute;
  left: 50%;
  transform: translate(-50%, -50%);
  background: linear-gradient(to bottom, #193a0e 0%, #44a027 49%, #193a0e 100%);
  border: 4px solid #aca06f;
  border-radius: 48px;
  padding: 10px 32px;
  min-width: 140px;
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  font-family: 'Lato', sans-serif;
  font-weight: 900;
  font-style: italic;
  font-size: 32px;
  color: white;
  /* Tabular digits so mid-count sub-pixel wobble can't jiggle the pill. */
  font-variant-numeric: tabular-nums;
  box-sizing: border-box;
  text-shadow: 0 2px 4px rgba(0,0,0,0.4);
  box-shadow: 0 4px 12px rgba(0,0,0,0.4), inset 0 2px 4px rgba(255,255,255,0.25);
  z-index: 4;
  pointer-events: none;
  white-space: nowrap;
  animation: win-badge-pop 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes win-badge-pop {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(0.3); }
  100% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

/* Bonus variant — Figma node 295:10577 (purple "Variant2") */
.win-badge--bonus {
  background: linear-gradient(to bottom, #5f137e 0%, #a23eca 49%, #5f137e 100%);
}

/* Pulse for the bonus ×N reveal: grows under the bonus-win jingle, then
   returns to normal size before the coin counter runs. Duration synced with
   soundResult() (~1.6s). */
.win-badge--pulse {
  animation: win-badge-pulse 1.6s ease-in-out;
}
@keyframes win-badge-pulse {
  0%   { transform: translate(-50%, -50%) scale(1); }
  25%  { transform: translate(-50%, -50%) scale(1.65); }
  50%  { transform: translate(-50%, -50%) scale(1.35); }
  75%  { transform: translate(-50%, -50%) scale(1.55); }
  100% { transform: translate(-50%, -50%) scale(1); }
}

/* ── Result text ── plain white, same style as Balance/Total Bet labels */
.result-text {
  font-family: 'Lato', sans-serif;
  font-size: 18px;
  font-weight: 500;
  color: #fff;
  opacity: 0;
  transition: opacity 0.25s;
}
.result-text.show  { opacity: 1; }
.result-text.plain { color: #fff; }
.result-text.lose  { color: rgba(255,255,255,0.45); }

/* ══════════════════════════════════════════════════════════════════════════
   Slot UI template — reusable control bar + menu tiles.
   Drives its look from --ui-* tokens in theme.css so each game can re-skin
   without rewriting component styles.
   ══════════════════════════════════════════════════════════════════════════ */

/* Controls row: [menu] [−] [SPIN] [+] [autospin].
   justify-content:space-between pins the outer buttons to the edges; gap on
   the inner step/spin trio is handled by the step buttons' own margin.
   Horizontal padding is kept tight so the pill + side buttons fit on
   narrow viewports without overflowing past the content box. */
.ui-controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  width: 100%;
  padding: 8px 14px 0;
}

/* Small round buttons at the row edges (menu / autoplay). */
.ui-round {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--ui-surface);
  border: 1px solid var(--ui-stroke);
  color: var(--ui-icon);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; flex-shrink: 0;
  padding: 0;
  transition: transform 0.12s ease, background 0.12s ease, border-color 0.12s ease;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.ui-round:not(:disabled):hover  { background: var(--ui-surface-hover); border-color: var(--ui-stroke-strong); }
.ui-round:not(:disabled):active { transform: scale(0.92); background: var(--ui-surface-press); }
.ui-round:disabled { opacity: 0.4; cursor: not-allowed; }
/* Icon box centred optically via an inline-flex with explicit size — the
   Material Symbols em-box has asymmetric baseline metrics so a plain
   `display: block` would render the glyph ~1–2px above geometric centre. */
.ui-round-ico {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  font-size: 26px;
  line-height: 1;
  color: currentColor;
}

/* Autoplay button — icons swap based on data-running. `idle` state shows
   the dashed-arc + play arrow; `run` state shows a solid STOP square. */
#autoplayBtn[data-running="false"] .ap-icon-run  { display: none; }
#autoplayBtn[data-running="true"]  .ap-icon-idle { display: none; }

/* Bet pill — a single continuous pill visually, made of two adjacent
   half-pill buttons. They share a seam at the centre; SPIN sits on top
   of that seam, visually "slicing" the pill while staying its own
   clickable circle. */
.ui-bet-pill {
  position: relative;                /* SPIN anchors here */
  display: flex;
  align-items: center;
}

/* Half-pill button. Outer edge rounded, inner edge flat so the two
   halves butt together seamlessly to look like one pill. Icons are
   pushed toward the OUTER side so SPIN overlapping the centre never
   covers them. */
.ui-step {
  width: 110px;
  height: 54px;
  /* Same palette as .ui-round (menu + autoplay) so all edge-row controls
     read as one visual family. */
  background: var(--ui-surface);
  border: 1px solid var(--ui-stroke);
  color: var(--ui-icon-muted);
  display: flex;
  align-items: center;
  cursor: pointer;
  flex-shrink: 0;
  padding: 0;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease, opacity 0.12s ease;
}
/* Halves touch without a gap — the seam is covered by the opaque SPIN
   circle so the pill reads as one continuous shape in the normal state.
   Each half still has its full border so if SPIN is ever removed /
   hidden, the split stays visible. */
.ui-step--left  {
  border-radius: 999px 0 0 999px;     /* rounded left, flat right */
  justify-content: flex-start;
  padding-left: 20px;
}
.ui-step--right {
  border-radius: 0 999px 999px 0;     /* flat left, rounded right */
  justify-content: flex-end;
  padding-right: 20px;
}
.ui-step:not(:disabled):hover  { color: var(--ui-icon); background: var(--ui-surface-hover); border-color: var(--ui-stroke-strong); }
.ui-step:not(:disabled):active { color: var(--ui-icon); background: var(--ui-surface-press); }
.ui-step:disabled              { opacity: 0.35; cursor: not-allowed; }
.ui-step-ico {
  font-size: 24px;
  color: currentColor;
  display: block;
  font-variation-settings: 'FILL' 0, 'wght' 600, 'GRAD' 0, 'opsz' 24;
}

/* Big spin button — white icon on a much darker circle than the pill,
   plus a light halo ring so the button reads as a distinct object in
   front of the pill, not as a colored patch inside it. The two-layer
   box-shadow paints: (1) a thin solid white ring outlining the circle,
   (2) a soft drop shadow for depth. */
.ui-spin {
  width: var(--ui-spin-size);
  height: var(--ui-spin-size);
  border-radius: 50%;
  background: var(--ui-spin-bg);
  color: var(--ui-spin-fg);
  border: 2px solid var(--ui-spin-stroke);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; flex-shrink: 0;
  padding: 0;
  /* SPIN is absolutely positioned over the pill's centre seam — sits
     on top of the two half-pills so it visually "slices" them without
     needing any cut in their geometry. */
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  margin: 0;
  transition: transform 0.12s ease, background 0.12s ease, filter 0.12s ease;
  box-shadow:
    0 0 0 4px var(--ui-spin-moat),      /* solid moat — tidies the edge against any background */
    0 8px 20px rgba(0, 0, 0, 0.5),      /* drop shadow for depth */
    inset 0 0 0 1px rgba(255, 255, 255, 0.05);
}
.ui-spin:not(:disabled):hover  { filter: brightness(1.12); }
/* Force cursor:pointer on the children too — without this, hovering over
   the inner <span class="ui-spin-ico"> or absolutely-positioned <svg
   class="ui-spin-ring"> sometimes shows the default arrow because the
   inherited cursor wasn't picked up after a state change (Chrome only
   recomputes cursor on the next pointermove, and animated children that
   redraw mid-hover can race that). Safer to set it explicitly. */
.ui-spin:not(:disabled),
.ui-spin:not(:disabled) .ui-spin-ico,
.ui-spin:not(:disabled) .ui-spin-counter,
.ui-spin:not(:disabled) .ui-spin-ring,
.ui-spin:not(:disabled)::before {
  cursor: pointer !important;
}
/* Press-state feedback via filter + inset shadow — keeps the centering
   translate so the button doesn't shift on press. */
.ui-spin:not(:disabled):active {
  transform: translate(-50%, -50%);
  filter: brightness(0.88);
  box-shadow:
    0 0 0 4px var(--ui-spin-moat),
    inset 0 4px 10px rgba(0, 0, 0, 0.55),
    0 3px 8px rgba(0, 0, 0, 0.35);
}
/* `transform` must KEEP the centering translate even when disabled —
   setting it to `none` (which was the old value) caused SPIN to jump
   down-right the moment slots.js flipped `spinBtn.disabled = true` on
   spin start, because `left:50%/top:50%` without the translate puts the
   element's top-left corner at the pill centre.
   Dimming is applied to the inner icon (and any counter/ring children)
   instead of the whole button — the dark background + moat box-shadow
   STAY fully opaque, so the half-pill +/- buttons behind the SPIN
   circle can't bleed through. Setting `opacity` on the button itself
   was the old behaviour and produced the "ghosted +/-" artifact users
   saw mid-spin. */
.ui-spin:disabled { cursor: not-allowed; transform: translate(-50%, -50%); }
.ui-spin:disabled .ui-spin-ico,
.ui-spin:disabled .ui-spin-counter,
.ui-spin:disabled .ui-spin-ring {
  opacity: 0.45;
}
.ui-spin-ico {
  font-size: 52px;
  color: currentColor;
  display: block;
  font-variation-settings: 'FILL' 0, 'wght' 600, 'GRAD' 0, 'opsz' 48;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,0.35));
}

/* ── SPIN button autoplay state ───────────────────────────────────────
   When the autoplay loop is active we hide the refresh icon and show
   a big counter in the middle + a progress ring around the circle.
   The ring stroke-dashoffset is set in JS each tick (remaining/total). */
.ui-spin-counter {
  display: none;
  position: relative;
  z-index: 2;
  font-family: 'Lato', sans-serif;
  font-size: 30px;
  font-weight: 800;
  line-height: 1;
  color: #ffffff;
  letter-spacing: 0.2px;
  text-shadow: 0 1px 2px rgba(0,0,0,0.45);
  font-variant-numeric: tabular-nums;
}
/* Infinity glyph reads better slightly bigger than digits and needs a
   touch of extra vertical room so the curves aren't clipped by the
   text-shadow. data-infinity is written by _updateAutoplayRing. */
.ui-spin-counter[data-infinity="true"] { font-size: 44px; }
.ui-spin-ring {
  display: none;
  position: absolute;
  /* Sit just outside the main button border so the ring doesn't collide
     with the moat/halo shadow. */
  inset: -6px;
  width: calc(100% + 12px);
  height: calc(100% + 12px);
  pointer-events: none;
  z-index: 1;
}
.ui-spin-ring-track { stroke: rgba(255, 255, 255, 0.15); }
.ui-spin-ring-fill {
  stroke: #ffffff;
  transition: stroke-dashoffset 0.35s ease;
  filter: drop-shadow(0 0 6px rgba(255, 255, 255, 0.35));
}

/* Swap: autoplay ON → hide icon, show counter + ring. */
.ui-spin[data-autoplay="true"] .ui-spin-ico     { display: none; }
.ui-spin[data-autoplay="true"] .ui-spin-counter { display: block; }
.ui-spin[data-autoplay="true"] .ui-spin-ring    { display: block; }

/* Status row: BALANCE (left) / BET (right) */
.ui-status {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  width: 100%;
  /* Horizontal padding matches the controls row so BALANCE/BET visually
     align under the menu / autoplay buttons. */
  padding: 14px 22px 22px;
}
.ui-status-col {
  display: flex;
  flex-direction: column;
  gap: 2px;
  align-items: flex-start;
}
.ui-status-col--right { align-items: flex-end; }
.ui-status-label {
  font-family: 'Lato', sans-serif;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 1.2px;
  color: var(--ui-label);
  text-transform: uppercase;
}
.ui-status-value {
  font-family: 'Lato', sans-serif;
  font-size: 17px;
  font-weight: 900;
  color: var(--ui-value);
  display: inline-flex;
  align-items: center;
  gap: 2px;
}
/* CASHOUT column.
   - Label uses the same muted-gold colour as BALANCE / BET (inherits
     from `.ui-status-label`), no green/white override here.
   - Value (the STR amount) stays emerald green — that's the "ready to
     bank" colour cue, in the CASH OUT button palette.
   - The leading multiplier prefix (×1.24 etc.) is rendered in the same
     warm gold as the active-row mult labels next to the tiles, so the
     mult reads as part of the same "earning" colour world rather than
     fighting the green of the cash amount.
   - "BUSTED" + "Line N/12" tails are pure white. */
.ui-status-value--cashout {
  color: #58e088 !important;
  text-shadow: none !important;
  /* Smooth fade from the gold flash (set by `cashout-coin-arrival`)
     back to the resting emerald — driven by `bumpCashoutFromCoin()`
     in overlay.ts on each coin-shower landing. */
  transition: color 0.32s ease, text-shadow 0.32s ease;
  /* Cashout amount inherits the BALANCE/BET value typography (17 px on
     desktop, 19 px on mobile via the media-query in style.css below) so
     CASHOUT, BALANCE, and BET all read at the same weight + size — only
     the colour differs.  Earlier we shrank the cashout to 15 px to fit a
     7-digit payout, but the player wants visual parity with BALANCE. */
  letter-spacing: 0.2px;
}
.ui-status-value--cashout .ui-cur {
  color: #58e088 !important;
  transition: color 0.32s ease, text-shadow 0.32s ease;
}
/* Gold-coin flash: applied for ~360 ms each time a coin-shower sprite
   lands on the chrome (overlay.ts → bumpCashoutFromCoin). The amount
   AND the trailing FUN badge both flip to coin-gold so the whole row
   reads as "coin received", then transition back to the resting
   emerald via the rules above. */
.ui-status-value--cashout.cashout-coin-arrival,
.ui-status-value--cashout.cashout-coin-arrival .ui-cur {
  color: #f5c542 !important;
  text-shadow: none !important;
}
.ui-status-value--cashout .ui-mult {
  color: #f5c542 !important;
  text-shadow: none;
}
/* Inline LINE N/12 prefix — sits before the multiplier inside the
   cashout value. White, but otherwise inherits the same font /
   weight / size as the surrounding spans (STR + amount + ×mult) so
   the row reads as one unified line of text instead of mixed
   typography. */
.ui-status-value--cashout .ui-line {
  color: #ffffff !important;
  text-shadow: none;
  margin-right: 8px;
}
/* GOOD LUCK mode — only the value is shown (no label), centred both
   axes inside the column. The column stretches to the row's full
   height so the text lines up with the vertical centre of the
   sibling BALANCE / BET stacks, not their bottoms. */
.ui-status-col--win.is-good-luck {
  align-self: stretch;
  justify-content: center;
  align-items: center;
}
.ui-star {
  width: 0.78em;
  height: 0.78em;
  fill: var(--c-accent);
  vertical-align: -0.08em;
  display: inline-block;
}

/* Footer container — transparent; the dark fade is owned by the outer
   `.controls-footer` wrapper.
   Status row now sits ABOVE the controls row. Extra ~60px padding-bottom
   reserves the floor space that the status row used to occupy when it
   was the last child, so the controls row stays at the exact Y position
   it had in the old layout. */
.ui-footer {
  background: transparent;
  /* SPIN circle (100 px) sits centred on the 54 px pill, so its lower
     hemisphere overhangs ~23 px below the pill plus a soft shadow halo.
     Reserve ~36 px + the OS safe-area inset so the button can never get
     clipped against the viewport floor on any device. */
  padding-bottom: calc(36px + max(12px, env(safe-area-inset-bottom)));
}

/* ── Menu tile grid (2 cols) ─────────────────────────────────────────── */
.ui-menu-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  margin-bottom: 4px;
}
/* Actions grid sits below the toggles + slider — extra top margin
   separates "settings" from "actions" so HOME isn't mis-tapped while
   reaching for the volume slider. Tiles in this variant stack their icon
   ABOVE the label, both centred — reads as a "button card" rather than
   a settings row. Label uses button typography (heavier, tighter) so
   action tiles read punchier than toggle-row section titles. */
.ui-menu-grid--actions { margin-top: 18px; margin-bottom: 0; }
.ui-menu-grid--actions .ui-tile {
  flex-direction: column;
  justify-content: center;
  text-align: center;
  gap: 12px;
  padding: 22px 14px;
}
.ui-menu-grid--actions .ui-tile-label {
  font-size: 14px;
  font-weight: 800;
  letter-spacing: 0.5px;
  opacity: 1;
}
/* Icon-chip wrapper: the material-symbol glyph sits inside a small
   rounded-square with a subtle fill, echoing the reference design. */
.ui-menu-grid--actions .ui-tile-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.08);
  font-size: 24px;
  color: currentColor;
}
.ui-tile {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 18px;
  background: var(--ui-surface);
  border: 1px solid var(--ui-stroke);
  border-radius: 14px;
  cursor: pointer;
  color: var(--ui-icon);
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
  text-align: left;
  font-family: 'Lato', sans-serif;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.ui-tile-icon {
  font-size: 24px;
  color: currentColor;
  flex-shrink: 0;
  display: block;
}
.ui-tile:not(:disabled):hover  { background: var(--ui-surface-hover); border-color: var(--ui-stroke-strong); }
.ui-tile:not(:disabled):active { transform: scale(0.97); }

/* Tile label (used both as a section row title AND as an action-tile
   label). Default look is the "section title" style: lighter weight,
   wider tracking, slightly softer so it reads as a calm header. Action
   tiles override back to punchier button typography further down. */
.ui-tile-label {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 1.6px;
  color: var(--ui-value);
  text-transform: uppercase;
  opacity: 0.92;
}

.ui-tile:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}

/* Full-width toggle row: [icon] [label] ────────── [switch]. Used for
   TURBO / SOUND / MUSIC so the on/off state is glanceable at a distance
   and the control matches the volume slider aesthetic. */
.ui-toggle-row {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 18px 20px;
  margin-top: 12px;
  background: var(--ui-surface);
  border: 1px solid var(--ui-stroke);
  border-radius: 14px;
  cursor: pointer;
  color: var(--ui-icon);
  text-align: left;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.1s ease, opacity 0.15s ease;
}
.ui-toggle-row:not(:disabled):hover  { background: var(--ui-surface-hover); border-color: var(--ui-stroke-strong); }
/* No scale-down on the whole row — the feedback moves to the switch thumb
   (see `.ui-switch-thumb` :active rules below). */
.ui-toggle-row .ui-tile-label { margin-right: auto; }     /* pushes switch to the far right */
.ui-toggle-row[data-on="false"]                  { color: var(--ui-icon-muted); }
.ui-toggle-row[data-on="false"] .ui-tile-label   { color: var(--ui-label); }
.ui-toggle-row:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  pointer-events: none;
}

/* ── Switch (mirrors the volume-slider's neutral-white aesthetic) ──
   Off: track translucent white, thumb at left.
   On:  track bright white, thumb at right.
   Thumb is always white; a subtle 1px outline on the "on" state
   differentiates it against the bright track. */
.ui-switch {
  position: relative;
  width: 44px;
  height: 24px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.18);
  flex-shrink: 0;
  transition: background 0.2s ease;
}
.ui-switch-thumb {
  position: absolute;
  top: 3px; left: 3px;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: #ffffff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.45);
  transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.2s ease;
}
[data-on="true"] > .ui-switch {
  background: rgba(255, 255, 255, 0.85);
}
[data-on="true"] > .ui-switch > .ui-switch-thumb {
  transform: translateX(20px);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.45), inset 0 0 0 1px rgba(0, 0, 0, 0.08);
}

/* Thumb press-feedback — fires on any :active in the parent toggle row.
   Transform has to bake the translateX for on-state so scale stacks on
   top of it (otherwise setting `transform: scale(…)` would wipe out the
   thumb's on/off position). */
.ui-toggle-row:not(:disabled):active .ui-switch-thumb                       { transform: scale(0.82); }
.ui-toggle-row:not(:disabled):active[data-on="true"] .ui-switch-thumb       { transform: translateX(20px) scale(0.82); }

/* ── Centered confirm dialog (HOME → return-to-lobby prompt) ─────────────
   Neutral: no brand-colour border or gradients so it reads the same on
   every slot theme. Hierarchy comes from button style, not colour —
   primary YES is a solid white pill, secondary NO is a ghost outline. */
.confirm-backdrop {
  /* Overrides the generic .modal-backdrop bottom-sheet alignment so the
     dialog sits centered on screen, not flush to the bottom. Uses the
     SAME overlay token as the Game Menu so both modals dim the page
     identically. z-index stacks above the regular modal layer (1000) so
     a confirm can pop over the menu if both are open. */
  align-items: center !important;
  background: var(--c-modal-overlay);
  z-index: 1100;
}
.confirm-dialog {
  /* All visual tokens mirror `.modal-sheet` (Game Menu) so the two modal
     families share one surface. Only the SHAPE differs — confirm dialogs
     are centered cards (.confirm-backdrop overrides align-items: center)
     while .modal-sheet is a bottom sheet. Layout INSIDE uses the same
     `.modal-header` + `.modal-title` + `.modal-close` structure → padding
     and offsets around the title and × match by construction. */
  background: var(--c-modal-sheet);
  -webkit-backdrop-filter: blur(var(--modal-blur));
  backdrop-filter: blur(var(--modal-blur));
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: var(--modal-radius);
  padding: 24px 24px 24px;             /* same 24px lateral as `.modal-sheet` */
  width: calc(100% - 48px);
  max-width: var(--modal-max-w);
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.55);
}
/* Optional header row for confirm dialogs that need a title (e.g. the
   autoplay-ended prompt). Title is centered; close button is a small
   ghost on the right. */
.confirm-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 14px;
}
.confirm-header-title {
  flex: 1;
  text-align: center;
  padding-left: 24px;              /* balances the close button on the right */
  font-family: 'Lato', sans-serif;
  font-weight: 800;
  font-size: 14px;
  letter-spacing: 2px;
  color: #ffffff;
  text-transform: uppercase;
}
.confirm-header-close {
  width: 24px; height: 24px;
  padding: 0;
  background: none;
  border: none;
  color: rgba(255, 255, 255, 0.55);
  font-size: 22px; line-height: 1;
  cursor: pointer;
  transition: color 0.15s ease;
}
.confirm-header-close:hover { color: #ffffff; }

/* Confirm modals reuse the canonical `.modal-header` + `.modal-title` +
   `.modal-close` markup from the Game Menu. No separate confirm-* header
   classes — that way padding around × and title offsets stay identical
   across the two modal families. */
/* Title in a `.confirm-dialog` (e.g., the crash modal) has no close
   button to balance against — strip the padding-left that the Game Menu
   needs and centre the text. Without this the title sat ~16 px right of
   the dialog's centre on every breakpoint. */
.confirm-dialog .modal-header {
  display: flex;
  align-items: center;
  justify-content: center;
}
.confirm-dialog .modal-title {
  padding-left: 0 !important;
  text-align: center;
  width: 100%;
}
.confirm-text {
  color: rgba(255, 255, 255, 0.74);
  font-family: 'Lato', sans-serif;
  font-size: 14px;
  font-weight: 400;
  text-align: center;
  margin-bottom: 22px;
  line-height: 1.5;
  padding: 0 4px;
  letter-spacing: 0.15px;
}
/* Actions stack vertically — one button per row, primary on top.
   Keeps labels fully readable even when they're long ("Restart autoplay"
   etc.) and gives each button a bigger tap target. */
.confirm-actions {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
}
.confirm-btn {
  padding: 13px 10px;
  border-radius: 999px;
  border: 1px solid transparent;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.9px;
  text-transform: uppercase;
  cursor: pointer;
  transition: transform 0.1s ease, background 0.15s ease, border-color 0.15s ease, filter 0.12s ease;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}
.confirm-btn-ico { width: 16px; height: 16px; fill: currentColor; flex-shrink: 0; }
/* Variant with leading icon — tightens letter-spacing + case so the
   label sits closer to the glyph. */
.confirm-btn--ico { letter-spacing: 0.4px; text-transform: none; font-size: 15px; }
.confirm-btn:active { transform: scale(0.96); }

/* Secondary — ghost outline, low visual weight. */
.confirm-btn--no {
  background: transparent;
  border-color: rgba(255, 255, 255, 0.18);
  color: rgba(255, 255, 255, 0.78);
}
.confirm-btn--no:hover  { background: rgba(255, 255, 255, 0.06); border-color: rgba(255, 255, 255, 0.28); color: #fff; }

/* Primary — solid white, draws the eye. */
.confirm-btn--yes {
  background: #ffffff;
  color: #0a0812;
}
.confirm-btn--yes:hover { filter: brightness(0.93); }

/* ══════════════════════════════════════════════════════════════════════
   Autoplay modal — chips + collapsible limit sections + START CTA.
   Shares toggle-row + switch styles with the Game Menu so settings feel
   part of the same family.
   Layout: fixed header + scrollable body + fixed START footer so the
   primary CTA is always reachable regardless of how many limits are open.
   ══════════════════════════════════════════════════════════════════════ */
.autoplay-sheet {
  /* Content-driven height — sheet shrinks to fit whatever's visible
     (just the spins grid in Basic, taller when Advanced reveals the
     extra toggles). Capped at 94vh so the page doesn't jump if the
     content overflows; the body itself scrolls inside the cap. */
  height: auto;
  max-height: 94vh;
  padding: 0;                            /* inner slots control their own padding */
  display: flex;
  flex-direction: column;
  overflow: hidden;                      /* body scrolls; sheet itself doesn't */
  /* Smooth height transition when Advanced flips on/off so the change
     feels intentional rather than an abrupt resize. */
  transition: max-height 0.25s ease;
}
.autoplay-header {
  flex-shrink: 0;
  padding: 20px 24px 14px;
  margin-bottom: 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

/* Basic / Advanced tabs — segmented control sitting between header and
   scrollable body. Padding is symmetric top/bottom so the gap from the
   header to the tabs visually matches the gap from the tabs to the
   first body element. */
.ap-tabs {
  flex-shrink: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
  padding: 14px 24px;
}
/* Pair with a tighter body top-padding so the total gap below the tabs
   (tabs padding-bottom + body padding-top) matches the gap above them. */
.autoplay-body { padding-top: 4px; }
.ap-tab {
  padding: 10px 12px;
  border: 1px solid var(--ui-stroke);
  border-radius: 999px;
  background: var(--ui-surface);
  color: var(--ui-icon-muted);
  font-family: 'Lato', sans-serif;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
}
.ap-tab:not(.ap-tab--active):hover  { background: var(--ui-surface-hover); color: var(--ui-icon); border-color: var(--ui-stroke-strong); }
.ap-tab:not(.ap-tab--active):active { transform: scale(0.97); }
.ap-tab--active {
  background: #ffffff;
  color: #0a0812;
  border-color: #ffffff;
}

/* BASIC pane is always visible. ADVANCED pane is extra detail that
   becomes visible ONLY when the ADVANCED tab is active — it extends
   Basic rather than replacing it so the player can see all settings
   (Number of spins + Turbo/Stop/Limits) together. */
.ap-pane[data-pane="basic"]    { display: block; }
.ap-pane[data-pane="advanced"] { display: none; }
.ap-pane[data-pane="advanced"].ap-pane--active { display: block; margin-top: 10px; }
.autoplay-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 16px 24px 18px;
  /* Breathing room at the bottom so the last element isn't flush with
     the sticky START footer's divider. */
}
.autoplay-footer {
  flex-shrink: 0;
  padding: 14px 24px max(18px, env(safe-area-inset-bottom));
  background: var(--c-modal-sheet);
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

.ap-section { margin-bottom: 10px; }
/* Section title ("Number of spins") — same typographic register as the
   toggle-row labels so all "headers" in the modal read consistently. */
.ap-section-title {
  font-size: 12px;
  font-weight: 600;
  color: var(--ui-label);
  text-align: center;
  letter-spacing: 1.8px;
  text-transform: uppercase;
  margin: 4px 0 14px;
}

/* Chip grid — circular "number of spins" buttons wrapped in a panel so
   they read as one grouped setting rather than loose chips. */
.ap-chip-grid {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 10px;
  padding: 18px 14px;
  background: var(--ui-surface);
  border: 1px solid var(--ui-stroke);
  border-radius: 14px;
}
.ap-chip {
  width: 52px; height: 52px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: var(--ui-icon);
  font-size: 14px;
  font-weight: 800;
  letter-spacing: 0.3px;
  cursor: pointer;
  position: relative;
  flex-shrink: 0;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease, transform 0.1s ease;
  font-family: 'Lato', sans-serif;
}
.ap-chip:not(.ap-chip--active):hover  { background: rgba(255, 255, 255, 0.14); border-color: rgba(255, 255, 255, 0.25); }
.ap-chip:not(.ap-chip--active):active { transform: scale(0.94); }
.ap-chip--active {
  background: #ffffff;
  color: #0a0812;
  border-color: #ffffff;
}
/* Checkmark badge in the top-right of the active chip — marks the
   selection at a glance so players don't have to scan text. */
.ap-chip--active::after {
  content: '';
  position: absolute;
  top: -4px; right: -4px;
  width: 18px; height: 18px;
  border-radius: 50%;
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%230a0812' d='M9 16.2 4.8 12 3.4 13.4 9 19l12-12-1.4-1.4L9 16.2z'/%3E%3C/svg%3E") center/12px no-repeat,
    #ffffff;
  box-shadow: 0 0 0 2px var(--c-modal-sheet);
}

/* Loss / Win limit — open state merges head + body into ONE seamless
   panel by moving the surface/border to the wrapper and making the two
   children transparent. That kills the 1px divider line that used to
   sit between them and gives a single hover target. */
.ap-limit {
  margin-top: 10px;
  border-radius: 14px;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.ap-limit .ap-limit-head { margin-top: 0; }

/* When open, the parent becomes the surface; children are just layout. */
.ap-limit.is-open {
  background: var(--ui-surface);
  border: 1px solid var(--ui-stroke);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.ap-limit.is-open .ap-limit-head,
.ap-limit.is-open .ap-limit-body {
  background: transparent;
  border: none;
  border-radius: 0;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  /* Snap with no animation — otherwise the hover border (white ~22%
     alpha) visibly fades out through the transition and looks like a
     "flash" the instant you click to open the section. */
  transition: none;
}
.ap-limit.is-open:hover { background: var(--ui-surface-hover); border-color: var(--ui-stroke-strong); }
/* Suppress the individual .ui-toggle-row hover so it can't fight the
   wrapper hover. */
.ap-limit.is-open .ap-limit-head:not(:disabled):hover { background: transparent; }

/* Chip body — grid of 6 columns so row 1 fits 3 chips (span 2 each) and
   row 2 fits NO LIMIT + CUSTOM (span 3 each, a half-row apiece). */
.ap-limit-body {
  display: none;
  padding: 12px 14px 14px;
  background: var(--ui-surface);
  border: 1px solid var(--ui-stroke);
  border-top: none;
  border-radius: 0 0 14px 14px;
  gap: 8px;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.ap-limit.is-open .ap-limit-body {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
}

.ap-mult-chip {
  grid-column: span 2;
  padding: 11px 6px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: var(--ui-icon);
  /* Button typography — heavier, tighter than section titles so chips
     read as primary actions rather than labels. */
  font-size: 14px;
  font-weight: 800;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  border-radius: 999px;                  /* pill — matches the reference */
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease, transform 0.1s ease;
  font-family: 'Lato', sans-serif;
  white-space: nowrap;
}
.ap-mult-chip.ap-chip-lg,
.ap-custom-slot.ap-chip-lg { grid-column: span 3; }

.ap-mult-chip:not(.is-active):hover  { background: rgba(255, 255, 255, 0.14); border-color: rgba(255, 255, 255, 0.25); }
.ap-mult-chip:not(.is-active):active { transform: scale(0.96); }
.ap-mult-chip.is-active {
  background: #ffffff;
  color: #0a0812;
  border-color: #ffffff;
}

/* CUSTOM slot — chip by default; swaps to an accent-coloured input when
   tapped (is-editing). Input is preset to "1.0" so the player sees what
   they're editing right away. */
.ap-custom-slot {
  position: relative;
  display: flex;
  align-items: stretch;
}
.ap-custom-slot .ap-mult-chip { grid-column: auto; width: 100%; }
.ap-custom-slot .ap-mult-input { display: none; }
.ap-custom-slot.is-editing .ap-mult-chip  { display: none; }
/* Active CUSTOM input mirrors the "selected chip" visual (solid white
   pill on the UI palette — neutral, works on any slot theme). */
.ap-custom-slot.is-editing .ap-mult-input {
  display: block;
  width: 100%;
  padding: 11px 14px;
  background: #ffffff;
  color: #0a0812;
  border: 1px solid #ffffff;
  font-family: 'Lato', sans-serif;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.5px;
  border-radius: 999px;
  text-align: center;
  outline: none;
  caret-color: #0a0812;
}
.ap-custom-slot.is-editing .ap-mult-input::placeholder { color: rgba(0, 0, 0, 0.4); }
/* Kill the default browser selection highlight so the text selection
   blends with the white chip (the blue block looked out of place). */
.ap-custom-slot.is-editing .ap-mult-input::selection { background: rgba(0, 0, 0, 0.12); color: #0a0812; }

/* Hide webkit/firefox number spinners — chip presets stand in for them. */
.ap-mult-input::-webkit-outer-spin-button,
.ap-mult-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.ap-mult-input[type=number] { -moz-appearance: textfield; }

/* START CTA — primary white, shares look with confirm-btn--yes. Lives
   inside .autoplay-footer so no top margin is needed. */
.ap-start-btn {
  width: 100%;
  padding: 15px 12px;
  border: none;
  border-radius: 999px;
  background: #ffffff;
  color: #0a0812;
  font-family: 'Lato', sans-serif;
  font-size: 15px;
  font-weight: 800;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  gap: 8px;
  transition: filter 0.12s ease, transform 0.1s ease;
}
.ap-start-btn:hover  { filter: brightness(0.94); }
.ap-start-btn:active { transform: scale(0.98); }
/* Figma play_circle SVG — inline with the START label. */
.ap-start-ico { width: 22px; height: 22px; vertical-align: middle; }

/* ── Volume row (inline slider) ──────────────────────────────────────── */
.ui-volume-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 18px 20px;
  margin-top: 12px;
  background: var(--ui-surface);
  border: 1px solid var(--ui-stroke);
  border-radius: 14px;
}
.ui-volume-ico {
  font-size: 22px;
  color: var(--ui-icon-muted);
  flex-shrink: 0;
}
.ui-volume-slider { flex: 1; }
.ui-volume-value {
  font-family: 'Lato', sans-serif;
  font-size: 12px;
  font-weight: 700;
  color: var(--ui-label);
  min-width: 42px;
  text-align: right;
}

/* ── Modal primitives (.modal-backdrop, .modal-sheet, etc.) live in theme.css ── */

/* Paytable (legacy text rows — kept for back-compat with other games) */
.paytable { display: flex; flex-direction: column; gap: 6px; font-size: 14px; }
.paytable-head {
  display: flex; justify-content: space-between;
  color: var(--c-text-dim); font-weight: 600; font-size: 11px;
  text-transform: uppercase; letter-spacing: 0.8px;
  padding-bottom: 8px; border-bottom: 1px solid var(--c-border);
}
.paytable-row { display: flex; justify-content: space-between; color: var(--c-text); padding: 2px 0; }
.paytable-divider { height: 1px; background: var(--c-border); margin: 4px 0; }
.paytable-note { color: var(--c-text-dim); font-size: 11px; margin-top: 10px; }
.pay-gold   { color: var(--c-gold);   font-weight: 700; }
.pay-yellow { color: var(--c-yellow); font-weight: 700; }
.pay-purple { color: var(--c-purple); font-weight: 700; }
.pay-green  { color: var(--c-green);  font-weight: 700; }
.pay-red    { color: var(--c-red);    font-weight: 700; }
.pay-blue   { color: var(--c-blue);   font-weight: 700; }

/* ── Info modal (Paytable grid + Lines grid) ── */
.info-sheet { max-height: 90vh; display: flex; flex-direction: column; }
.info-sheet .info-body {
  overflow-y: auto;
  padding-right: 4px;
  margin-right: -4px;
}
.info-section-title {
  text-align: center;
  color: var(--c-text-subtle);
  font-size: 22px;
  font-weight: 500;
  letter-spacing: 0.5px;
  margin: 4px 0 16px;
}

.pt-grid {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 20px 8px; padding: 0 4px;
}
.pt-item {
  flex: 0 0 calc(33.333% - 8px);
  display: flex; flex-direction: column; align-items: center; text-align: center;
}
.pt-img {
  width: 68px; height: 68px; object-fit: contain; margin-bottom: 8px;
  filter: drop-shadow(var(--shadow-sm));
}
.pt-payouts { font-size: 13px; line-height: 1.45; }
.pt-payouts .k { color: var(--c-text-muted); font-weight: 500; }
.pt-payouts .v { color: var(--c-accent);     font-weight: 700; letter-spacing: 0.3px; }

.ln-grid {
  display: grid; grid-template-columns: repeat(2, 1fr);
  gap: 16px 10px; justify-items: center;
}
.ln-item { text-align: center; }
.ln-label { font-size: 13px; color: var(--c-text-subtle); margin-bottom: 5px; letter-spacing: 0.4px; }
.ln-cells {
  display: grid;
  grid-template-columns: repeat(5, 20px);
  grid-template-rows: repeat(3, 20px);
  gap: 3px;
}
.ln-cell { background: var(--c-cell-off); border-radius: 3px; }
.ln-cell.on { background: var(--c-cell-on); }

/* RTP row at bottom of info modal */
.rtp-row {
  display: flex; justify-content: space-between; align-items: center;
  margin-top: 18px; padding-top: 14px;
  border-top: 1px solid var(--c-border);
}
.rtp-label { color: var(--c-text-muted); font-size: 13px; font-weight: 600; letter-spacing: 0.6px; text-transform: uppercase; }
.rtp-value { color: var(--c-accent); font-size: 16px; font-weight: 700; letter-spacing: 0.3px; }

/* ── Bonus info block (info modal) ── */
.bonus-info {
  display: flex; flex-direction: column; align-items: center;
  gap: 10px; padding: 4px 8px;
}
.bonus-info-img {
  width: 88px; height: 88px; object-fit: contain;
  filter: drop-shadow(var(--shadow-md));
}
.bonus-info-desc {
  color: var(--c-text); font-size: 14px; line-height: 1.45; text-align: center;
  max-width: 320px;
}
.bonus-info-desc b { color: var(--c-accent); font-weight: 700; }
.bonus-mult-grid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 8px; width: 100%; max-width: 320px;
  margin-top: 4px;
}
.bonus-mult-cell {
  display: flex; flex-direction: column; align-items: center;
  padding: 10px 4px;
  background: var(--c-surface-soft); border: 1px solid var(--c-border);
  border-radius: 12px;
}
.bonus-mult-count {
  color: var(--c-text-muted); font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.6px;
}
.bonus-mult-x {
  color: var(--c-accent); font-size: 22px; font-weight: 900;
  font-style: italic; margin-top: 2px;
}

/* ── Game settings grid (info modal) ── */
.settings-grid {
  display: grid; grid-template-columns: repeat(2, 1fr);
  gap: 8px;
}
.settings-cell {
  display: flex; justify-content: space-between; align-items: center;
  padding: 10px 14px;
  background: var(--c-surface-soft); border: 1px solid var(--c-border);
  border-radius: 12px;
}
.settings-cell-label {
  color: var(--c-text-muted); font-size: 13px; font-weight: 500;
}
.settings-cell-value {
  color: var(--c-accent); font-size: 14px; font-weight: 700; letter-spacing: 0.3px;
}

/* Settings */
.settings-list { display: flex; flex-direction: column; gap: 20px; }
.settings-row {
  display: flex; justify-content: space-between; align-items: center;
}
.settings-row--col { flex-direction: column; align-items: stretch; }
.settings-info { display: flex; align-items: center; gap: 12px; }
.settings-icon { width: 20px; height: 20px; fill: rgba(255,255,255,0.55); flex-shrink: 0; }
.settings-label { color: #fff; font-weight: 600; font-size: 15px; }
.settings-sub   { color: rgba(255,255,255,0.4); font-size: 12px; margin-top: 2px; }
.settings-divider { height: 1px; background: rgba(255,255,255,0.1); }

/* Volume slider — white filled portion up to the thumb, translucent
   track after it (no accent colour, so the chrome reads the same on any
   slot theme). `--fill` drives the hard-stop position; setVolume() writes
   it. The input box is sized to match the thumb so Webkit auto-centres
   the thumb on the runnable-track rather than relying on a fragile
   margin-top offset. Firefox uses ::-moz-range-progress/track. */
.vol-slider {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 18px;
  background: transparent;
  outline: none;
  cursor: pointer;
}
.vol-slider::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 3px;
  background: linear-gradient(
    to right,
    rgba(255,255,255,0.85) 0%,
    rgba(255,255,255,0.85) var(--fill, 100%),
    rgba(255,255,255,0.15) var(--fill, 100%),
    rgba(255,255,255,0.15) 100%
  );
}
.vol-slider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 18px; height: 18px;
  border-radius: 50%; background: #fff;
  box-shadow: 0 1px 6px rgba(0,0,0,0.45);
  cursor: pointer;
  margin-top: -7px;                /* centre on the 4px runnable-track */
  transition: transform 0.1s ease;
}
/* Thumb shrinks on press for tactile feedback (matches the switches). */
.vol-slider:active::-webkit-slider-thumb { transform: scale(0.82); }
.vol-slider::-moz-range-track    { height: 4px; border-radius: 3px; background: rgba(255,255,255,0.15); }
.vol-slider::-moz-range-progress { height: 4px; border-radius: 3px; background: rgba(255,255,255,0.85); }
.vol-slider::-moz-range-thumb {
  width: 18px; height: 18px;
  border-radius: 50%; background: #fff;
  border: none; box-shadow: 0 1px 6px rgba(0,0,0,0.45);
  transition: transform 0.1s ease;
}
.vol-slider:active::-moz-range-thumb { transform: scale(0.82); }

/* Toggle */
.toggle-btn {
  position: relative; width: 56px; height: 28px;
  border-radius: 14px; background: var(--c-accent-strong);
  border: none; cursor: pointer; flex-shrink: 0;
  transition: background 0.2s;
}
.toggle-btn--off { background: #374151; }
.toggle-thumb {
  position: absolute; left: 2px; top: 2px;
  width: 24px; height: 24px;
  border-radius: 50%; background: #fff;
  box-shadow: 0 1px 4px rgba(0,0,0,0.3);
  transition: transform 0.2s;
}

/* Top-up */
.topup-btn {
  width: 100%; padding: 13px;
  border-radius: 14px;
  background: linear-gradient(90deg, var(--c-accent-deep), var(--c-accent-strong));
  color: #fff; font-weight: 700; font-size: 14px;
  border: none; cursor: pointer;
  display: flex; align-items: center; justify-content: center; gap: 8px;
}
.topup-desc {
  color: rgba(255,255,255,0.55); font-size: 14px;
  line-height: 1.5; margin-bottom: 24px;
}
.topup-confirm-btn {
  width: 100%; padding: 16px;
  border-radius: 16px;
  background: linear-gradient(90deg, var(--c-accent-deep), var(--c-accent));
  color: #fff; font-weight: 700; font-size: 16px;
  border: none; cursor: pointer;
  box-shadow: 0 4px 16px color-mix(in srgb, var(--c-accent-strong) 40%, transparent);
  display: flex; align-items: center; justify-content: center; gap: 8px;
}
.topup-cancel-btn {
  width: 100%; padding: 12px; margin-top: 8px;
  border-radius: 16px;
  background: none; color: rgba(255,255,255,0.4);
  font-size: 14px; border: none; cursor: pointer;
}
.exit-btn {
  width: 100%; padding: 13px; margin-top: 4px;
  border-radius: 14px;
  background: rgba(239,68,68,0.12);
  color: #f87171; font-weight: 700; font-size: 14px;
  border: 1px solid rgba(239,68,68,0.25); cursor: pointer;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  transition: background 0.15s;
}
.exit-btn:active { background: rgba(239,68,68,0.22); }

/* ── Footer gradient overlay ──
   Tall cinematic fade — the gradient covers roughly the bottom half
   of the viewport so the dark bleeds up from the controls almost all
   the way to the reels. Stops are tuned to show early opacity (20%
   black by 1/4 in) so the darkness reads as a halo rising from the
   buttons, not just a thin shadow at the very bottom. */
.controls-footer {
  /* Gradient is constrained to the bottom 240 px of the element so the
     padded-top zone (380 px) above the controls stays fully transparent.
     Without this clamp, the gradient's 0–28 % black top stops sit in front
     of the reels (z:5 over reels' z:2) and visibly tint the bottom of the
     reels on narrow screens. */
  background:
    linear-gradient(
      to bottom,
      rgba(0, 0, 0, 0)    0%,
      rgba(0, 0, 0, 0.32) 18%,
      rgba(0, 0, 0, 0.7)  40%,
      rgba(0, 0, 0, 0.92) 70%,
      rgba(0, 0, 0, 1)    100%
    ) bottom / 100% 340px no-repeat;
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  width: 100%;
  padding-top: 380px;                    /* reserve space above the controls; transparent now */
  z-index: 5;
  pointer-events: none;                  /* padded empty area must not steal taps from reels */
}
/* Re-enable clicks on the actual interactive content at the bottom. */
.controls-footer > * { pointer-events: auto; }

/* ── Logo ── */
.logo-wrap {
  position: relative;
  width: 88%;
  max-width: 360px;
  margin: 15px auto 8px;     /* logo sits ABOVE reels with a small gap, no overlap */
  z-index: 1;
  pointer-events: none;
}
.logo {
  width: 100%;
  display: block;
}

/* ── Logo sparkles (twinkle occasionally) ── */
.sparkle {
  position: absolute;
  width: 14px;
  height: 14px;
  background:
    radial-gradient(circle, rgba(255,255,255,1) 0%, rgba(255,255,255,0.6) 25%, rgba(255,255,255,0) 60%);
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
  transform: scale(0.3);
  filter: drop-shadow(0 0 4px rgba(255,255,255,0.9));
}
/* Star-shape cross via pseudo-elements */
.sparkle::before, .sparkle::after {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  background: linear-gradient(to right, transparent, #fff, transparent);
  transform: translate(-50%, -50%);
}
.sparkle::before { width: 200%; height: 1.5px; }
.sparkle::after  { width: 1.5px; height: 200%; }

@keyframes sparkle-twinkle {
  0%, 88%, 100% { opacity: 0; transform: scale(0.3); }
  92%           { opacity: 1; transform: scale(1.1); }
  95%           { opacity: 0.7; transform: scale(0.9); }
  98%           { opacity: 0; transform: scale(0.4); }
}

/* Positioned at visible star spots on the logo image */
.sparkle-1 { top: 18%;  left: 8%;  animation: sparkle-twinkle 5.5s infinite; animation-delay: 0.2s; }
.sparkle-2 { top: 72%;  left: 20%; animation: sparkle-twinkle 6.3s infinite; animation-delay: 2.4s; }
.sparkle-3 { top: 28%;  left: 48%; animation: sparkle-twinkle 4.8s infinite; animation-delay: 1.1s; width: 18px; height: 18px; }
.sparkle-4 { top: 15%;  left: 78%; animation: sparkle-twinkle 7.1s infinite; animation-delay: 3.3s; }
.sparkle-5 { top: 68%;  left: 88%; animation: sparkle-twinkle 5.9s infinite; animation-delay: 4.1s; }

/* ══════════════════════════════════════════════════════════════════════════
   Touch-device hover handling.
   On touch devices `:hover` sticks after a tap (nothing to "unhover" to),
   leaving buttons permanently lit. `@media (hover: none)` matches exactly
   those devices — we:
     1) neutralise every :hover declaration so there's no sticky visual
        once the finger has lifted, AND
     2) redirect the hover visuals onto :active so the user still gets
        a "pressed / highlighted" feedback DURING the tap. :active
        releases automatically on finger-up, so nothing lingers.
   ══════════════════════════════════════════════════════════════════════════ */
@media (hover: none) {
  /* ── 1) Reset :hover back to the resting state. ────────────────────── */
  .ui-round:not(:disabled):hover             { background: var(--ui-surface); border-color: var(--ui-stroke); }
  .ui-step:not(:disabled):hover              { background: var(--ui-surface); border-color: var(--ui-stroke); color: var(--ui-icon-muted); }
  .ui-spin:not(:disabled):hover              { filter: none; }
  .ui-tile:not(:disabled):hover              { background: var(--ui-surface); border-color: var(--ui-stroke); }
  .ui-toggle-row:not(:disabled):hover        { background: var(--ui-surface); border-color: var(--ui-stroke); }
  .ap-tab:not(.ap-tab--active):hover         { background: var(--ui-surface); color: var(--ui-icon-muted); border-color: var(--ui-stroke); }
  .ap-chip:not(.ap-chip--active):hover       { background: rgba(255,255,255,0.06); border-color: rgba(255,255,255,0.12); }
  .ap-mult-chip:not(.is-active):hover        { background: rgba(255,255,255,0.06); border-color: rgba(255,255,255,0.12); }
  .ap-start-btn:hover                        { filter: none; }
  .confirm-btn--no:hover                     { background: transparent; border-color: rgba(255,255,255,0.18); color: rgba(255,255,255,0.78); }
  .confirm-btn--yes:hover                    { filter: none; }
  .modal-close:hover                         { color: var(--c-text-muted); }
  .confirm-header-close:hover                { color: rgba(255,255,255,0.55); }
  .tts-btn:hover                             { filter: none; }
  .ap-limit.is-open:hover                    { background: var(--ui-surface); border-color: var(--ui-stroke); }

  /* ── 2) Paint the hover look on :active instead — appears during
     the press, clears on finger-up. Transform scale from the main
     :active blocks still fires, stacked via specificity. ────────────── */
  .ui-round:not(:disabled):active            { background: var(--ui-surface-hover); border-color: var(--ui-stroke-strong); }
  .ui-step:not(:disabled):active             { background: var(--ui-surface-hover); border-color: var(--ui-stroke-strong); color: var(--ui-icon); }
  .ui-spin:not(:disabled):active             { filter: brightness(0.88); }
  .ui-tile:not(:disabled):active             { background: var(--ui-surface-hover); border-color: var(--ui-stroke-strong); }
  .ui-toggle-row:not(:disabled):active       { background: var(--ui-surface-hover); border-color: var(--ui-stroke-strong); }
  .ap-tab:not(.ap-tab--active):active        { background: var(--ui-surface-hover); color: var(--ui-icon); border-color: var(--ui-stroke-strong); }
  .ap-chip:not(.ap-chip--active):active      { background: rgba(255,255,255,0.14); border-color: rgba(255,255,255,0.25); }
  .ap-mult-chip:not(.is-active):active       { background: rgba(255,255,255,0.14); border-color: rgba(255,255,255,0.25); }
  .ap-start-btn:active                       { filter: brightness(0.94); }
  .confirm-btn--no:active                    { background: rgba(255,255,255,0.06); border-color: rgba(255,255,255,0.28); color: #fff; }
  .confirm-btn--yes:active                   { filter: brightness(0.93); }
  .modal-close:active                        { color: var(--c-text); }
  .confirm-header-close:active               { color: #fff; }
}

/* In-pill bet readout — base rule (mobile/tablet hides it; desktop @media
   below unhides it). Placed BEFORE the @media block so the media-query
   rule wins at desktop sizes via source order at equal specificity. */
.ui-bet-readout { display: none; }

/* ══════════════════════════════════════════════════════════════════════════
   Wide layout — horizontal bottom-bar (≥ 737px).
   Layout philosophy:
     • Background scene extends edge-to-edge (already a full-bleed bg.jpg)
     • Reels DOMINATE the centre — wide, large, no side panels
     • Controls live in a single horizontal strip pinned to the bottom
     • Balance, bet, menu, ± , spin, autoplay all share the same row
   Narrow (< 737px, typical phone portrait) keeps the mobile cascade.
   ══════════════════════════════════════════════════════════════════════════ */
@media (min-width: 737px) {
  .app {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;          /* centre slot vertically, biased up by larger pb */
    height: 100dvh;
    max-width: none;
    margin: 0 auto;
    padding: 0 0 152px;               /* reserves 110 bar + 32 floor + ~10 buffer */
  }

  /* Background — full viewport, not just .app box. */
  .bg-image {
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100dvh;
  }

  /* Radial vignette — darkens the edges of the viewport, keeps the
     reel area bright. Centred at ~50% horiz / 45% vert which is
     roughly where the reels sit on a 1440×900 viewport. Uses an
     ellipse so the falloff matches the landscape aspect. */
  .app::before {
    content: '';
    position: fixed;
    inset: 0;
    pointer-events: none;
    background: radial-gradient(
      ellipse 75% 60% at 50% 45%,
      rgba(0, 0, 0, 0) 0%,
      rgba(0, 0, 0, 0) 40%,
      rgba(0, 0, 0, 0.45) 75%,
      rgba(0, 0, 0, 0.85) 100%
    );
    z-index: 1;
  }

  /* Logo — sits ABOVE the reels, almost flush (4px gap, no overlap). */
  .logo-wrap {
    width: 100%;
    max-width: 480px;
    margin: 0 auto 4px;
    z-index: 3;
    position: relative;
  }
  /* Logo PNG ships with transparent background (post-processed from the
     Figma export) — no blend-mode tricks needed. */

  /* Reels — wide, centred, dominating the visual hierarchy. Bigger than
     before (1080 max) so they fill more of the viewport. JS-measured
     square cells drive height proportionally. */
  .reels-area {
    width: 90vw;
    max-width: 1080px;
    align-self: center;
    flex: 0 0 auto;
    padding: 0 24px;
  }

  /* Anonymous result-text wrap (sits between reels and footer). */
  .reels-area + div {
    width: 100%;
    max-width: 1080px;
    margin: 0 auto;
    pointer-events: none;
    min-height: 0;
    padding: 0;
  }

  /* ── Bottom bar — floating glass strip ────────────────────────────────
     Single horizontal flex row. Children (.ui-status + .ui-controls) are
     dissolved via display:contents and ordered with `order:`. Floating
     (not flush to viewport edge) — gives breathing room around the bar.
     Layout (left→right): [menu] [balance]  ───  [pill] [SPIN] [autoplay]
     `margin-left:auto` on the pill creates the spacer gap between the
     left group and the right group (classic flex-row pattern).
     ─────────────────────────────────────────────────────────────────── */
  .controls-footer {
    position: fixed;
    bottom: 32px;                /* lifted higher off the viewport floor */
    left: 50%;
    transform: translateX(-50%);
    width: calc(100vw - 48px);
    max-width: 1080px;
    min-height: 108px;
    padding: 12px 24px;
    /* Translucent glass bar — slot art shows through at ~50% so the
       toolbar reads as a hovering panel, not a solid black strip.
       backdrop-filter blurs whatever is behind, so even at low alpha
       the contents stay readable. */
    background: rgba(8, 8, 10, 0.45);
    border: 1px solid var(--ui-stroke);
    border-radius: 18px;
    -webkit-backdrop-filter: blur(18px) saturate(120%);
    backdrop-filter: blur(18px) saturate(120%);
    z-index: 5;

    display: flex;
    align-items: center;
    gap: 20px;
  }
  .controls-footer.ui-footer { padding-bottom: 12px; }

  /* Dissolve intermediate containers so children flatten into the bar. */
  .ui-status     { display: contents; }
  .ui-controls   { display: contents; }

  /* Order on the bar (left to right). */
  .ui-controls > .ui-round[aria-label="Menu"] {
    order: 1;
    width: 48px;
    height: 48px;
  }
  .ui-status > .ui-status-col:first-child {
    order: 2;
    align-items: flex-start;
  }
  /* Sync BALANCE and BET label/value typography so the two stacks are
     identical in size, weight, line-height, and total height. */
  .ui-status-label,
  .ui-bet-readout-label { font-size: 11px; font-weight: 700; letter-spacing: 1.4px; line-height: 1; }
  .ui-status-value,
  .ui-bet-readout-value { font-size: 19px; font-weight: 900; line-height: 1.1; }

  /* Right-side group: BET-readout — [+/−] vertical pill — SPIN — autoplay.
     The pill is a 3-col / 2-row grid: readout spans both rows on the
     left, [+] top-right, [−] bottom-right, SPIN spans both rows on
     the far right. Rows are 1fr/1fr so buttons fill exactly half the
     pill height each — no visible gap between +/− halves. */
  .ui-bet-pill {
    order: 3;
    margin-left: auto;
    display: grid;
    grid-template-columns: auto auto auto;
    grid-template-rows: 1fr 1fr;
    align-items: stretch;
    column-gap: 12px;
    row-gap: 0;
    background: transparent;
    border: none;
    border-radius: 0;
    height: 84px;             /* matches SPIN size — pill column heights line up */
    padding: 0;
    margin-right: 0;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    overflow: visible;
    position: static;
  }
  .ui-bet-readout {
    grid-column: 1;
    grid-row: 1 / span 2;
    margin-left: 0;
    order: 0;
  }
  .ui-bet-pill #betPlus  { grid-column: 2; grid-row: 1; order: 0; }
  .ui-bet-pill #betMinus { grid-column: 2; grid-row: 2; order: 0; }
  .ui-bet-pill .ui-spin  { grid-column: 3; grid-row: 1 / span 2; order: 0; }
  .ui-controls > .ui-round#autoplayBtn { order: 7; width: 48px; height: 48px; }

  /* ── Modals — centred dialog on desktop ──────────────────────────────
     Mobile uses a bottom-sheet (`align-items: flex-end` + top-only
     rounded corners). On wide screens we centre the sheet vertically
     and round all four corners so it reads as a dialog, not a sheet. */
  .modal-backdrop {
    align-items: center;
  }
  .modal-sheet {
    border-radius: var(--modal-radius);
    padding: 28px 28px 28px;
    box-shadow: 0 24px 64px rgba(0, 0, 0, 0.6);
    border-top: 1px solid rgba(255, 255, 255, 0.06);
  }

  /* Autoplay sheet on desktop — content-sized, centred, body scrolls
     internally so START never overflows the dialog. Section paddings
     match Game Menu's outer padding (28px) without internal dividers. */
  .autoplay-sheet {
    height: auto;
    max-height: 80vh;
    padding: 28px 28px 0;
    overflow: hidden;
  }
  .autoplay-header {
    padding: 0;
    border-bottom: none;
    margin-bottom: 16px;
  }
  .ap-tabs       { padding: 0 0 14px; }
  .autoplay-body {
    padding: 0 8px 0 0;
    overflow-y: auto;
    flex: 1 1 auto;
    min-height: 0;
  }
  /* Solid near-black modal sheets — opaque so the bluish app bg
     doesn't bleed through the backdrop-filter blur. The autoplay-footer
     padding override lives in the 768px wide-card block above; it
     applies at all wide widths including this 1200+ tier. */
  .modal-sheet {
    background: #0a0a0c;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }

  /* ── Welcome (#tapToStart) — desktop layout ──────────────────────────
     Compact stack centred slightly above viewport centre. Logo /
     slider / dots / bottom row sit as one cluster with equal gaps
     between logo↔slider and slider↔bottom (dots tuck close to the
     slider so they read as part of it). Asymmetric padding (smaller
     top, larger bottom) biases the centred cluster upward — the
     reference is "above optical centre", not perfectly centred. */
  #tapToStart {
    padding: 6vh 24px 8vh;
    justify-content: center;
    /* Generous vertical breathing room between logo / CONTINUE / SOUND. */
    gap: 56px;
  }
  #tapToStart .tts-btn { margin-top: 0; }
  .tts-logo-wrap {
    /* Match the preloader logo size exactly. */
    width: clamp(120px, 18vw, 180px);
    max-width: 180px;
    margin: 0 auto;
  }
  .tts-slides    {
    max-width: 1000px;
    margin: 0 auto 8px;              /* tight gap to dots — they belong to the slider */
    height: 320px;
  }
  .tts-slide {
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: 64px;
    padding: 0 32px;
  }
  .tts-hero    { width: 460px; height: 320px; }
  .tts-caption { text-align: left; max-width: 380px; font-size: 24px; line-height: 1.3; margin: 0; }
  /* Pagination (arrows + dots) — drops slightly below the slider on wide
     viewports and reserves a generous 56 px gap to the action row. */
  .tts-pagination { margin: 28px auto 56px; gap: 18px; }
  .tts-nav-arrow  { width: 40px; height: 40px; }
  .tts-nav-arrow .material-symbols-outlined { font-size: 32px; }

  /* Anubis Rising — vertical stack on desktop so logo / continue / sound
     line up under each other with the same gap as the parent's. */
  .tts-bottom.tts-bottom--stack {
    width: auto;
    max-width: none;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 56px;
    grid-template-columns: none;
    column-gap: 0;
  }
  .tts-sound, .tts-volatility { display: inline-flex; align-items: center; gap: 12px; }
  .tts-sound      { justify-self: start; }
  .tts-volatility { justify-self: end; flex-direction: column; align-items: flex-end; gap: 6px; }
  .tts-vol-bolt   { width: 10px; height: 18px; }
  .tts-btn {
    justify-self: center;
    width: 480px;
    max-width: 100%;
    min-width: 0;
    padding: 18px 40px;
    font-size: 20px;
  }

  /* Hide standalone BET status-col (redundant — value is in the BET stack). */
  .ui-status > .ui-status-col--right { display: none; }

  /* + on top, − on bottom — rectangular buttons with the OUTER corners
     rounded (top corners on +, bottom corners on −). Inner edges stay
     square so they butt together cleanly. Each button fills exactly
     half the pill height (1fr/1fr rows). */
  .ui-bet-pill .ui-step {
    width: 56px;
    height: 100%;          /* fills its grid row */
    background: var(--ui-surface);
    border: 1px solid var(--ui-stroke);
    color: var(--ui-icon);
    padding: 0;
    justify-content: center;
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
  }
  .ui-bet-pill #betPlus  { border-radius: 14px 14px 0 0; }
  .ui-bet-pill #betMinus { border-radius: 0 0 14px 14px; margin-top: -1px; }
  .ui-bet-pill .ui-step-ico { font-size: 22px; }

  /* BET stack — same as .ui-status-col layout but right-aligned so
     the label/value hug the SPIN button on the right. */
  .ui-bet-readout {
    display: inline-flex;
    flex-direction: column;
    align-items: flex-end;
    justify-content: center;
    flex: 0 0 auto;
    min-width: 64px;
    padding: 0;
    gap: 2px;
  }
  .ui-bet-readout-label {
    font-family: 'Lato', sans-serif;
    color: var(--ui-label);
    text-transform: uppercase;
  }
  .ui-bet-readout-value {
    font-family: 'Lato', sans-serif;
    color: var(--ui-value);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
  }

  /* SPIN — normal grid-cell flow now that the pill wrapper is a grid.
     `position: relative` (not static) so the autoplay progress ring
     (`.ui-spin-ring { position: absolute; inset: -6px }`) anchors to
     the SPIN button itself, not to the nearest fixed ancestor.
     `top:0; left:0` reset the mobile rule (top:50%; left:50%) which
     would otherwise nudge SPIN out of its grid cell.
     Box-shadow drops the mobile `0 0 0 4px var(--ui-spin-moat)` ring
     (it existed to hide the pill seam — with no pill behind, it now
     reads as an unwanted dark halo around the circle). */
  .ui-bet-pill .ui-spin {
    position: relative;
    top: 0;
    left: 0;
    transform: none;
    width: 84px;
    height: 84px;
    margin: 0;
    flex: 0 0 auto;
    align-self: center;
    justify-self: center;
    box-shadow:
      0 8px 20px rgba(0, 0, 0, 0.5),
      inset 0 0 0 1px rgba(255, 255, 255, 0.05);
  }
  .ui-bet-pill .ui-spin:disabled                { transform: none; }
  .ui-bet-pill .ui-spin:not(:disabled):active   {
    transform: scale(0.96);
    filter: brightness(0.88);
    box-shadow:
      inset 0 4px 10px rgba(0, 0, 0, 0.55),
      0 3px 8px rgba(0, 0, 0, 0.35);
  }
  .ui-bet-pill .ui-spin-ico                     { font-size: 44px; }
  .ui-bet-pill .ui-spin-counter                       { font-size: 26px; }
  .ui-bet-pill .ui-spin-counter[data-infinity="true"] { font-size: 40px; }

  /* The standalone BET status-col is now redundant — the value lives inside
     the pill. Hide it so the pill is the single source of truth on desktop. */
  .ui-status > .ui-status-col--right { display: none; }

  /* 6) Autoplay — stays at col 6 (col 4 / BET status-col is hidden, collapses to 0). */
  .ui-controls > .ui-round#autoplayBtn {
    grid-column: 6;
    width: 48px;
    height: 48px;
  }
}


/* ══════════════════════════════════════════════════════════════════════
   Wide-screen modal scaling (≥ 768 px — tablet and desktop)
   ──────────────────────────────────────────────────────────────────────
   Mobile (≤ 767 px) keeps the compact 14–20 px type ladder. On larger
   viewports the modals look cramped — the dialog stays small, type stays
   ~14 px, and the whole thing reads as a shrunken mobile sheet plonked
   in the middle of a desktop. This block bumps the typography and the
   dialog metrics in step (title 26 px, body 16 px, buttons 16 px,
   max-width 540 px, padding 32 px) so the modal feels properly sized
   for the screen. Reference: Joker Royal Coins QUIT THE GAME modal.
   ══════════════════════════════════════════════════════════════════════ */
@media (min-width: 768px) {
  /* On wide screens the bottom-sheet pattern stops making sense — a
     full-width strip stuck to the viewport floor with flat bottom corners
     reads as a half-pill, not a modal. Float every modal as a centered
     card with all four corners rounded, top-padding == bottom-padding. */
  .modal-backdrop {
    align-items: center;
  }
  .modal-sheet {
    padding: 32px 32px 32px;
    max-width: 540px;
    border-radius: 28px;            /* all four corners — centered card */
  }
  .modal-header {
    margin-bottom: 24px;
  }
  .modal-title {
    font-size: 26px;
    padding-left: 32px;             /* still balances the 32px close button */
  }
  .modal-close {
    width: 32px; height: 32px;
    font-size: 32px;
  }

  /* Autoplay sheet had height: 94vh for the bottom-sheet phone layout;
     as a centered card it should size to its content and only cap when
     content exceeds the viewport. Footer drops its 28 px bottom padding
     and bottom border so the sheet's own 32 px padding handles spacing. */
  .autoplay-sheet {
    height: auto;
    max-height: 90vh;
    border-radius: 28px;
    /* Same 32 px on every side as the Game Menu (`.modal-sheet` wide
       override). Inner sections (header / tabs / body / footer) have
       their lateral padding zeroed below so the sheet's 32 px is the
       single source of truth for the content's lateral inset. */
    padding: 32px;
  }
  .autoplay-header,
  .ap-tabs,
  .autoplay-body,
  .autoplay-footer {
    padding-left: 0;
    padding-right: 0;
  }
  .autoplay-header {
    padding-top: 0;                   /* sheet's 32 px supplies the top gap */
  }
  .autoplay-footer {
    padding: 16px 0 0;                /* sheet's 32 px supplies the bottom gap */
    background: transparent;
    border-top: none;
  }

  /* Confirm dialogs (centered card) — same scale-up. */
  .confirm-dialog {
    padding: 32px 32px 32px;
    max-width: 540px;
    border-radius: 28px;
  }
  .confirm-text {
    font-size: 16px;
    line-height: 1.55;
    margin-bottom: 28px;
  }
  .confirm-actions {
    gap: 12px;
  }
  .confirm-btn {
    padding: 16px 14px;
    font-size: 16px;
    letter-spacing: 1px;
  }
  .confirm-btn--ico {
    font-size: 17px;
  }
}

/* ── Win overlays — Cashout + Max-win ──────────────────────────────────
   Full-bleed dark backdrop with a slowly-rotating ray burst behind a
   centred "win card". The ray burst is the Figma-served sunburst PNG
   (assets/ray-burst.png) — sized 2× the viewport so its corners stay
   covered as it rotates. Two slightly different rotation speeds give
   cashout / max-win their own feel without needing a second asset. */
.win-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(8, 6, 4, 0.78);
  /* Hidden by default; main.ts sets display:flex when triggered. */
  overflow: hidden;
}

/* Cashout — gold-framed panel per Figma "Cashed Out!" design.
   Backdrop is a deep blue radial fade so the gold frame reads warm
   against cool. The frame PNG (assets/cashout/image11-hero.png) is
   the entire decorative chrome; text sits absolutely centred inside
   its dark-blue inner screen. A slow-spinning ray-burst sits behind
   the frame to give the moment a sunburst feel without competing
   with the headline. */
.win-overlay--cashed {
  background:
    radial-gradient(ellipse at center, rgba(20, 60, 160, 0.55) 0%, rgba(4, 8, 28, 0.92) 70%),
    rgba(2, 4, 14, 0.6);
  cursor: pointer;
  /* Full overlay fades in from black so the moment of cash-out feels
     like a curtain falling rather than a snap-cut. ease-out keeps the
     fade slow at the end, settling smoothly into the final state. */
  animation: cashed-overlay-in 700ms ease-out both;
}
@keyframes cashed-overlay-in {
  0%   { opacity: 0; }
  100% { opacity: 1; }
}
.cashed-rays {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  z-index: 0;
}
.cashed-rays img {
  width: 200vmax;
  height: 200vmax;
  max-width: none;
  object-fit: cover;
  /* Hue-rotate the original blue rays into a deeper royal blue + a
     dash of saturation so they read as a slow-burning aurora rather
     than a stripe pattern. The 180s rotation is intentionally glacial
     — the rays should drift, not spin. */
  filter:
    hue-rotate(0deg)
    saturate(1.4)
    brightness(0.95)
    contrast(1.1);
  opacity: 0.45;
  animation: cashed-rays-rotate 180s linear infinite;
  transform-origin: 50% 50%;
}
@keyframes cashed-rays-rotate {
  0%   { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
.cashed-frame {
  position: relative;
  width: min(80vw, 90vh, 760px);
  aspect-ratio: 1 / 1;
  display: flex;
  align-items: center;
  justify-content: center;
  filter: drop-shadow(0 12px 24px rgba(0, 0, 0, 0.55));
  /* Frame fades + scales in AFTER the overlay-fade has begun, so the
     gold reveal lands on a settled black backdrop. The cubic curve is
     a long ease-out (out-quint-ish) — slow finish, no overshoot. */
  animation: cashed-frame-in 850ms cubic-bezier(0.22, 1, 0.36, 1) 250ms both;
}
@keyframes cashed-frame-in {
  0%   { transform: scale(0.86); opacity: 0; }
  100% { transform: scale(1); opacity: 1; }
}
.cashed-frame-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  pointer-events: none;
  user-select: none;
}
.cashed-text {
  position: relative;
  z-index: 1;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  /* Frame PNG has decorative gold around a dark-blue inner panel.
     The inner panel sits roughly inset 14% top, 22% bottom (extra
     gold pegs along the bottom edge), 13% left/right. Text fits
     within those bounds so it never overlaps the frame chrome. */
  padding: 14% 13% 22%;
  width: 100%;
  height: 100%;
  box-sizing: border-box;
}
/* Headline + amount per Figma 534-12468 / 669-1223 — chunky cartoon
   sans serif (Alegreya Sans SC Black) wrapped in a clean white
   outline. NO drop shadow on the title/amount themselves — the
   light-blue offset shadow is reserved for the smaller mult line
   below (where the banded look reads as a separate visual layer);
   stacking it under the big headline made the strokes look broken. */
.cashed-title,
.cashed-amount {
  font-family: 'Alegreya Sans SC', 'Bowlby One SC', 'Lato', sans-serif;
  font-weight: 800;
  text-transform: uppercase;
  text-shadow:
     3px      0       0 #ffffff,
     2.77px   1.15px  0 #ffffff,
     2.12px   2.12px  0 #ffffff,
     1.15px   2.77px  0 #ffffff,
     0        3px     0 #ffffff,
    -1.15px   2.77px  0 #ffffff,
    -2.12px   2.12px  0 #ffffff,
    -2.77px   1.15px  0 #ffffff,
    -3px      0       0 #ffffff,
    -2.77px  -1.15px  0 #ffffff,
    -2.12px  -2.12px  0 #ffffff,
    -1.15px  -2.77px  0 #ffffff,
     0       -3px     0 #ffffff,
     1.15px  -2.77px  0 #ffffff,
     2.12px  -2.12px  0 #ffffff,
     2.77px  -1.15px  0 #ffffff;
}
.cashed-title {
  font-size: clamp(40px, 7.4vw, 88px);
  line-height: 0.9;
  color: #012da1;
  letter-spacing: 0.5px;
  display: flex;
  flex-direction: column;
  /* Each text line drifts up a touch as it fades in. Delays stagger
     headline → amount → mult so the eye reads them in order. */
  animation: cashed-text-up 700ms cubic-bezier(0.22, 1, 0.36, 1) 600ms both;
}
.cashed-title span { display: block; }
.cashed-amount {
  font-size: clamp(38px, 7vw, 82px);
  line-height: 0.9;
  color: #ff435d;
  letter-spacing: 1px;
  margin-top: 4px;
  animation: cashed-text-up 700ms cubic-bezier(0.22, 1, 0.36, 1) 800ms both;
}
@keyframes cashed-text-up {
  0%   { opacity: 0; transform: translateY(18px); }
  100% { opacity: 1; transform: translateY(0); }
}
.cashed-mult {
  /* Multiplier line is YELLOW per Figma — pops as a third colour
     against the deep-blue title + red amount. Subtle dark drop
     shadow underneath so it lifts off the navy panel without
     the banded white-outline effect the bigger glyphs use. */
  font-family: 'Alegreya Sans SC', 'Lato', sans-serif;
  font-weight: 900;
  font-size: clamp(20px, 2.8vw, 32px);
  color: #ffd54a !important;
  letter-spacing: 0.5px;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.45);
  margin-top: 6px;
  text-transform: uppercase;
  /* Joins the staggered text-rise so all three lines settle as a
     coordinated sequence — fires last after title + amount. */
  animation: cashed-text-up 700ms cubic-bezier(0.22, 1, 0.36, 1) 1000ms both;
}
/* Max-win variant — same yellow mult, sits well on the gold panel
   thanks to the soft dark shadow underneath. */
.cashed-mult--max {
  color: #ffd54a !important;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.45);
}
/* ID-level overrides for `#cashoutMult` and `#maxwinMult` — wins
   over any cached lower-specificity rule that might have shipped
   in the prod HTML the player has open. Belt-and-braces yellow. */
#cashoutMult,
#maxwinMult {
  color: #ffd54a !important;
}
.cashed-continue {
  position: absolute;
  bottom: 7%;
  left: 50%;
  transform: translateX(-50%);
  /* Centre on the screen and centre the wrapped lines too — without
     text-align the two lines on narrow viewports stacked
     left-aligned (the parent inherits text-align: left). White-space
     keeps the phrase on a single line whenever it fits. */
  text-align: center;
  white-space: nowrap;
  width: max-content;
  max-width: 90vw;
  font-family: 'Lato', sans-serif;
  font-weight: 700;
  font-size: clamp(14px, 2vw, 25px);
  color: #ffffff;
  letter-spacing: 2px;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.7);
  pointer-events: none;
  user-select: none;
  animation: cashed-blink 1.4s ease-in-out infinite;
}

/* Top-of-screen line header — LINE x/N + CHOOSE A BLOCK.
   The dark vertical gradient sits over the canvas's top edge, fading
   from `#150905` at the top down to transparent so the climb scrolls
   "into" the gradient. Text + gradient are independent layers so
   timing-based fades / animations can target them separately. */
.line-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 90;
  pointer-events: none;
  user-select: none;
  text-align: center;
  /* `.line-header__bg` is always-on per Figma 535-12984 — only the
     text inside fades with `data-visible`. So no opacity transition
     on the parent; otherwise the gradient backdrop would dim too. */
}
/* Backdrop gradient — always present per Figma 535-12984. The dark
   vertical fade reads as the natural top-of-canvas chrome and is
   independent of whether the LINE x/N copy is showing. */
.line-header__bg {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  /* Bumped from 180 → 220 px so the dark gradient extends past the
     PRESS START headline (which now sits ~115 px from the top, with
     ~50 px of glyph height + line-hint below). */
  height: 220px;
  background: linear-gradient(to bottom, #150905 0%, rgba(21, 9, 5, 0.86) 13.4%, rgba(21, 9, 5, 0));
  pointer-events: none;
}
.line-header__text {
  position: relative;
  /* Logo now hugs the top edge (top:6 desktop / top:4 mobile) +
     ~90 px logo height → bottom ~96 → PRESS START headline lands at
     ~100 px. Tight stacking, both sit on the dark gradient backdrop. */
  padding: 100px 12px 0;
  text-shadow: 0 2px 2px rgba(0, 0, 0, 0.55);
  /* Default LINE header animation — hidden state sits ABOVE the
     resting position so the copy "drops in from above" when shown. */
  opacity: 0;
  transform: translateY(-32px);
  transition: opacity 0.45s ease, transform 0.45s ease;
}
@media (max-width: 736px) {
  .line-header__text {
    /* Logo on mobile is ~70 px tall, top:4 → bottom ~74 → headline at ~80. */
    padding-top: 80px;
  }
}
.line-header[data-visible="1"] .line-header__text {
  opacity: 1;
  transform: translateY(0);
}
.line-header__num {
  font-family: 'Alegreya Sans SC', 'Lato', sans-serif;
  font-weight: 800;
  font-size: clamp(34px, 7vw, 64px);
  line-height: 0.9;
  color: #ffffff;
  letter-spacing: 1px;
  text-transform: uppercase;
}
.line-header__hint {
  font-family: 'Alegreya Sans SC', 'Lato', sans-serif;
  font-weight: 500;
  font-size: clamp(15px, 2.6vw, 28px);
  line-height: 1;
  margin-top: 8px;
  letter-spacing: 1px;
  color: #ffffff;
  text-transform: uppercase;
}
.line-header__accent { color: #f3dd43; }
@media (max-width: 480px) {
  .line-header__bg  { height: 200px; }
  /* Compact mobile: logo (~50 px) hugs top → headline 70 px down. */
  .line-header__text { padding-top: 70px; }
}

/* Bottom variant — same typography as the LINE header but anchored
   to the bottom of the viewport, just above the chrome footer. No
   gradient backdrop since this prompt sits over the dark floor area
   below the climb panel and reads cleanly without one. */
/* Start hint sits in the SAME slot as the LINE header (top) — the
   two cross-fade in place. While hidden, the start hint's text
   slides DOWN out of frame (translateY +60) so it visually "falls
   away" as the player presses Start, just before the LINE x/N copy
   drops in from above. */
.line-header--start[data-visible="0"] .line-header__text {
  opacity: 0;
  transform: translateY(60px);
}
.line-header--start[data-visible="1"] .line-header__text {
  opacity: 1;
  transform: translateY(0);
}
@keyframes cashed-blink {
  0%, 100% { opacity: 0.65; }
  50%      { opacity: 1; }
}
@media (max-width: 480px) {
  .cashed-frame { width: 92vw; }
  .cashed-text { padding: 14% 12% 22%; gap: 10px; }
  .cashed-continue { bottom: 5%; }
}

/* Anubis side video — kept in the DOM for autoplay + alpha decoding,
   but hidden because we render its frames as a PIXI Sprite inside
   the canvas (so the character stacks BEHIND tiles + UI). Tucked
   off-screen at 1×1 px instead of `display: none` since hiding fully
   would stop the video pipeline in some browsers. */
.anubis-side {
  position: fixed;
  left: 0;
  top: 0;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
  z-index: -10;
}
.win-rays {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  z-index: 0;
  /* Soft tint so the blue rays read as a sandstone-warm sunburst. The
     filter is applied to the <img> child so the ray PNG itself gets
     re-coloured into the tomb palette. */
}
.win-rays img {
  width: 200vmax;
  height: 200vmax;
  max-width: none;
  object-fit: cover;
  /* Hue-rotate the original blue rays into a deep amber gold; saturation
     bumped so the centre flare reads as warm sunset instead of dim grey. */
  filter:
    hue-rotate(170deg)
    saturate(1.6)
    brightness(0.95)
    contrast(1.05);
  opacity: 0.55;
  animation: tomb-rays-rotate 36s linear infinite;
  transform-origin: 50% 50%;
}
.win-rays--gold img {
  filter:
    hue-rotate(160deg)
    saturate(2.2)
    brightness(1.15)
    contrast(1.1);
  opacity: 0.7;
  animation: tomb-rays-rotate 22s linear infinite;
}
@keyframes tomb-rays-rotate {
  0%   { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
.win-card {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  padding: 36px 44px 32px;
  border-radius: 22px;
  background:
    radial-gradient(circle at 50% 0%, rgba(245, 197, 66, 0.22), transparent 70%),
    linear-gradient(180deg, rgba(40, 26, 8, 0.92) 0%, rgba(20, 12, 4, 0.96) 100%);
  border: 2px solid rgba(245, 197, 66, 0.35);
  box-shadow:
    0 16px 48px rgba(0, 0, 0, 0.7),
    inset 0 1px 0 rgba(255, 240, 180, 0.18);
  min-width: 280px;
  max-width: 88vw;
  text-align: center;
}
.win-eyebrow {
  font-family: 'Lato', sans-serif;
  font-weight: 900;
  font-size: 18px;
  letter-spacing: 4px;
  color: #f5c542;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.6);
  text-transform: uppercase;
}
.win-eyebrow--max {
  color: #ffe98a;
  font-size: 20px;
  letter-spacing: 6px;
}
.win-amount {
  font-family: 'Lato', sans-serif;
  font-weight: 900;
  font-size: 56px;
  line-height: 1;
  color: #fff5c4;
  text-shadow:
    0 0 24px rgba(245, 197, 66, 0.65),
    0 2px 0 rgba(0, 0, 0, 0.5);
  letter-spacing: 1px;
}
.win-amount--max {
  font-size: 68px;
  background: linear-gradient(180deg, #ffe98a 0%, #f5c542 50%, #b07e1a 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 0 18px rgba(245, 197, 66, 0.65));
}
.win-mult {
  font-family: 'Lato', sans-serif;
  font-weight: 700;
  font-size: 22px;
  color: #d6c08a;
  letter-spacing: 1px;
}
.win-mult--max {
  color: #ffe98a;
  font-size: 26px;
}
.win-btn {
  margin-top: 8px;
  padding: 14px 48px;
  border-radius: 999px;
  border: 2px solid #5a3f20;
  font-family: 'Lato', sans-serif;
  font-weight: 900;
  font-size: 22px;
  letter-spacing: 3px;
  cursor: pointer;
  transition: transform 0.12s ease, filter 0.12s ease;
}
.win-btn--gold {
  background: linear-gradient(180deg, #ffe98a 0%, #f5c542 50%, #b07e1a 100%);
  color: #2a1a08;
  box-shadow:
    0 6px 18px rgba(245, 197, 66, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.55),
    inset 0 -2px 4px rgba(120, 70, 8, 0.45);
}
.win-btn--gold:hover { filter: brightness(1.10); }
.win-btn--gold:active { transform: scale(0.97); filter: brightness(0.92); }
@media (max-width: 480px) {
  .win-card { padding: 28px 30px 24px; gap: 14px; min-width: 0; width: 84vw; }
  .win-amount { font-size: 44px; }
  .win-amount--max { font-size: 54px; }
  .win-eyebrow { font-size: 16px; letter-spacing: 3px; }
  .win-eyebrow--max { font-size: 18px; letter-spacing: 4px; }
  .win-btn { padding: 12px 36px; font-size: 19px; letter-spacing: 2px; }
}

