* { box-sizing: border-box; }

/* While the wheel spins (idle-spin / spin animation) the corners of the
   square rotor box stick out past the .wheel-wrap bounds — without locking
   overflow, some mobile browsers scroll/shift the page horizontally because
   of it. Locked at the root level so the page stays put (doesn't touch the
   visual layers). */
html {
  overflow-x: hidden;
  width: 100%;
}

body {
  margin: 0;
  overflow-x: hidden;
  width: 100%;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  /* Layered: two soft ambient glow blobs + a faint tech grid, both over the
     original warm-maroon base radial — the grid is what pushes the page
     from "dark background" to "control-room panel" without needing to
     touch layout. background-position on the two grid layers is animated
     (gridDrift) for a slow, subtle "alive" drift instead of a static print. */
  background-image:
    radial-gradient(circle at 12% 12%, rgba(239, 68, 68, 0.12) 0%, transparent 35%),
    radial-gradient(circle at 88% 88%, rgba(239, 68, 68, 0.1) 0%, transparent 40%),
    repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.03) 0px, rgba(255, 255, 255, 0.03) 1px, transparent 1px, transparent 48px),
    repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.03) 0px, rgba(255, 255, 255, 0.03) 1px, transparent 1px, transparent 48px),
    radial-gradient(circle at 50% 20%, #2a1414 0%, #1c0e0e 55%, #100808 100%);
  background-position: 0 0, 0 0, 0 0, 0 0, center;
  animation: gridDrift 50s linear infinite;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  position: relative;
}

@keyframes gridDrift {
  from { background-position: 0 0, 0 0, 0 0, 0 0, center; }
  to { background-position: 0 0, 0 0, 48px 48px, 48px 48px, center; }
}

/* Very faint horizontal scanlines drifting slowly down — a cheap, classic
   CRT/HUD cue. Kept subtle (2% white) so it reads as texture, not noise. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background: repeating-linear-gradient(
    0deg,
    rgba(255, 255, 255, 0.025) 0px,
    rgba(255, 255, 255, 0.025) 1px,
    transparent 1px,
    transparent 3px
  );
  animation: scanlineDrift 12s linear infinite;
}

@keyframes scanlineDrift {
  from { background-position: 0 0; }
  to { background-position: 0 120px; }
}

/* Cinematic vignette — darkens the corners so the eye lands on the wheel
   instead of the page edges, the classic "framed shot" cue. Fixed/full
   viewport rather than scoped to .widget, since the point is to frame the
   whole screen, not just the card. */
.cinematic-vignette {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background: radial-gradient(ellipse at 50% 45%, transparent 38%, rgba(0, 0, 0, 0.35) 78%, rgba(0, 0, 0, 0.72) 100%);
}

/* Film grain — a tiny tiled fractal-noise SVG (data URI, no network
   request/asset file) blended with "overlay" at very low opacity, stepped
   through a handful of offsets so it flickers like real grain instead of
   sitting there as a static texture. */
.cinematic-grain {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  opacity: 0.05;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  animation: grainFlicker 0.5s steps(4) infinite;
}

@keyframes grainFlicker {
  0% { transform: translate(0, 0); }
  25% { transform: translate(-1%, 1%); }
  50% { transform: translate(1%, -1%); }
  75% { transform: translate(-1%, -1%); }
  100% { transform: translate(1%, 1%); }
}

.widget {
  width: 620px;
  max-width: 96vw;
  padding: 30px 24px 36px;
  text-align: center;
  background: transparent;
  color: #fff;
  position: relative;
  z-index: 1; /* stay above the fixed body::before scanline layer */
}

/* No backing plate anymore (was reviewed as a distracting box around the
   text) — the div is kept only as a layout wrapper, no visual of its own. */
.neon-plate {
  display: inline-flex;
  justify-content: center;
  margin: 0 auto 10px;
}

/* A real neon tube sign — not just glowing text. The recipe: a soft warm
   core plus a few smoothly-graduated red shadows (the light it throws on
   the wall around it). Kept the white DOWN to a thin hint rather than a
   full bright layer — a strong white core read as harsh; a mostly-red glow
   with just a whisper of white at the very center is what actually looks
   smooth, like real neon glass rather than an LED. A connected script
   typeface (Pacifico, loaded in index.html) stands in for the bent-glass-
   tube shape a block font can't fake. The flicker keyframes dip both the
   core color and the glow at a few irregular points in the loop (never
   evenly spaced — a real loose-connection flicker doesn't repeat on a
   clean beat), then ease back — "power cutting in and out," not a sharp
   on/off. */
h1.brand-logo {
  margin: 0;
  font-family: "Pacifico", "Segoe Script", cursive;
  font-size: 42px;
  font-weight: 400;
  letter-spacing: 0.5px;
  line-height: 1;
  color: #ffe4e4;
  text-shadow:
    0 0 6px rgba(255, 255, 255, 0.55),
    0 0 16px #ef4444,
    0 0 32px #ef4444,
    0 0 58px rgba(239, 68, 68, 0.75),
    0 0 90px rgba(239, 68, 68, 0.4);
  animation: neonSignFlicker 6s ease-in-out infinite;
}

@keyframes neonSignFlicker {
  0%, 5%, 10%, 33%, 38%, 68%, 100% {
    color: #ffe4e4;
    text-shadow:
      0 0 6px rgba(255, 255, 255, 0.55),
      0 0 16px #ef4444,
      0 0 32px #ef4444,
      0 0 58px rgba(239, 68, 68, 0.75),
      0 0 90px rgba(239, 68, 68, 0.4);
  }
  7%, 35%, 70% {
    color: #7a2323;
    text-shadow: 0 0 4px rgba(122, 35, 35, 0.5);
  }
  8.5%, 36.5% {
    color: #ffd0d0;
    text-shadow: 0 0 5px rgba(255, 255, 255, 0.4), 0 0 18px rgba(239, 68, 68, 0.6);
  }
}

.subtitle {
  margin: 0 0 40px;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.2px;
  color: #cfd6dc;
  text-shadow: 0 0 10px rgba(239, 68, 68, 0.35);
}

.step.hidden { display: none; }

/* Loading step — flex-centered within a fixed-ish min-height instead of the
   old fixed top margin on the spinner, which only centered it by accident on
   one specific viewport height and looked adrift/off-balance elsewhere. */
#loadingStep {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 360px;
}

/* #loadingStep (ID, specificity 1-0-0) would otherwise beat .step.hidden
   (0-2-0) and stay visible even with the "hidden" class added — this makes
   sure hidden always wins regardless of source order. */
#loadingStep.hidden {
  display: none;
}

/* Loading spinner — a plain spinning ring instead of a "Loading" label */
.spinner {
  width: 64px;
  height: 64px;
  border: 5px solid rgba(239, 68, 68, 0.15);
  border-top-color: #ef4444;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
  filter: drop-shadow(0 0 12px rgba(239, 68, 68, 0.35));
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

.error {
  color: #ff8080;
  font-size: 14px;
  line-height: 1.5;
  padding: 20px 8px;
}

button {
  position: relative;
  width: 100%;
  padding: 14px;
  border: 1px solid #ef4444;
  border-radius: 12px;
  /* Elite black + neon-red-edge button — dark body, the red lives in the
     border/glow, not as a flat fill. */
  background: linear-gradient(344deg, #120c0c, #1f1616);
  color: #f87171;
  font-weight: 800;
  font-size: 16px;
  letter-spacing: 1px;
  cursor: pointer;
  box-shadow: 0 8px 24px rgba(239, 68, 68, 0.3), inset 0 1px 0 rgba(255,255,255,0.08);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

button:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: 0 12px 28px rgba(239, 68, 68, 0.45), inset 0 1px 0 rgba(255,255,255,0.08);
}

button:active:not(:disabled) {
  transform: translateY(0);
}

button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  box-shadow: none;
}

/* SPIN button — set apart from the other buttons (Close, Go to Game, etc.)
   with a VIP/embossed-coin feel (layered gradient + inner/outer shadow) and
   a continuous sweeping shine, to give it a "premium" feel.

   IMPORTANT: Chromium excludes the rounded-off corners of a border-radius
   box from hit-testing (clicks there fall through to whatever's behind) —
   confirmed by testing (corners missed, center/edges worked). So the actual
   <button> element stays a full RECTANGLE (no border-radius) for 100%
   clickable area everywhere, including corners. The rounded gold look comes
   from .spin-btn-bg, a child layer with its own border-radius — purely
   visual, doesn't shrink the real click target. */
#spinBtn {
  border: none;
  border-radius: 0;
  background: none;
  box-shadow: none;
  padding: 16px;
  font-size: 17px;
  letter-spacing: 1.8px;
  /* Without this, #spinBtn has no z-index of its own, so its z-indexed
     children (.spin-btn-bg at -1, the loading ::before/::after at 0) don't
     get compared against EACH OTHER in a contained local stack — they get
     hoisted up and compared against whatever else on the page happens to
     have a z-index, which can produce visible seams/gaps. isolation:isolate
     forces a real local stacking context so these layers only ever stack
     against each other. */
  isolation: isolate;
}

.spin-btn-bg {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: 14px;
  /* Clips the shine sweep (::before below) to the rounded shape — this
     CANNOT live on #spinBtn itself (that would bring back the corner
     click-through bug), so it's on this purely-decorative child instead.
     Without it, the sweep's translateX(-130%→130%) carries it outside the
     button's silhouette and it renders as a stray diagonal streak floating
     in the page next to the button (reported bug, confirmed via screenshot). */
  overflow: hidden;
  /* Deep crimson-to-black, not a pale pink highlight — a sharper, more
     "aggressive" primary CTA instead of a soft pastel one. */
  background: linear-gradient(165deg, #f87171 0%, #b91c1c 30%, #120c0c 100%);
  box-shadow:
    0 10px 34px rgba(239, 68, 68, 0.55),
    inset 0 2px 0 rgba(255, 255, 255, 0.35),
    inset 0 -4px 10px rgba(0, 0, 0, 0.5),
    0 0 0 1px rgba(248, 113, 113, 0.5);
  pointer-events: none;
}

.spin-btn-label {
  position: relative;
  /* White, not the base button's red text — the button's own gradient
     already carries the red, white text stays legible across all of it
     (including the near-black bottom-right corner) and reads as sharper. */
  color: #ffffff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
  /* A slight chromatic-aberration-style double-shadow (red + cool white)
     that's normally near-invisible, briefly separating into a visible
     ghosted edge — a projected-hologram flicker rather than a constant
     rainbow fringe (which would just look like a rendering bug at rest). */
  animation: labelHoloFlicker 4.5s ease-in-out infinite;
}

@keyframes labelHoloFlicker {
  0%, 88%, 100% {
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
  }
  90% {
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6), -1.5px 0 0 rgba(239, 68, 68, 0.65), 1.5px 0 0 rgba(190, 225, 255, 0.5);
  }
  92% {
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
  }
}

.spin-btn-bg::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, transparent 35%, rgba(255, 255, 255, 0.6) 50%, transparent 65%);
  transform: translateX(-130%);
  animation: btnShine 3.4s ease-in-out infinite;
  pointer-events: none;
}

@keyframes btnShine {
  0%, 25% { transform: translateX(-130%); }
  65%, 100% { transform: translateX(130%); }
}

#spinBtn:hover:not(:disabled) .spin-btn-bg {
  box-shadow:
    0 14px 40px rgba(239, 68, 68, 0.7),
    inset 0 2px 0 rgba(255, 255, 255, 0.4),
    inset 0 -4px 10px rgba(0, 0, 0, 0.5),
    0 0 0 1px rgba(248, 113, 113, 0.65);
}

#spinBtn:hover:not(:disabled) {
  transform: translateY(-2px) scale(1.008);
}

#spinBtn:active:not(:disabled) {
  transform: translateY(0) scale(0.99);
}

#spinBtn:disabled .spin-btn-bg {
  box-shadow: none;
}

#spinBtn:disabled .spin-btn-bg::before {
  display: none;
}

/* The button is also disabled while spinning (to prevent double-clicks), but
   that's exactly the moment it should look most alive — we cancel the
   generic button:disabled dimming (opacity 0.4) here so it stays bright and
   pulsing until the spin finishes. */
#spinBtn:disabled.spinning {
  opacity: 1;
}

/* A large, high-contrast spinner (gold on a dark backdrop) shown on the
   button while checking status (cooldown/restriction) or waiting for the
   spin result — the old one was thin/dark-on-dark and nearly invisible
   against the gold background. */
#spinBtn.loading {
  color: transparent;
  pointer-events: none;
}

/* A dark medallion (backdrop) instead of the shine — the spinner ring sits
   ON TOP of it, staying clearly visible instead of disappearing into the
   gold background. */
#spinBtn.loading::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  /* Was 42px around a 26px ring — an 8px gap of bare dark backdrop on every
     side made it read as "big dark blob with a tiny ring adrift inside",
     not one cohesive spinner. Tightened to a ~4px halo around the ring. */
  width: 34px;
  height: 34px;
  margin: -17px 0 0 -17px;
  background: radial-gradient(circle, rgba(10, 12, 8, 0.62), rgba(10, 12, 8, 0.42) 70%, transparent 100%);
  border-radius: 50%;
  animation: none;
  transform: none;
  z-index: 0;
}

#spinBtn.loading::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 28px;
  height: 28px;
  margin: -14px 0 0 -14px;
  /* The dim part of the ring was only 30% opacity — against the dark
     medallion, that read as barely-there, so any frozen frame looked like a
     bright crescent floating off-center rather than a full ring with one
     bright highlight. Raised so the full circle stays legible at rest. */
  border: 3px solid rgba(255, 228, 228, 0.55);
  border-top-color: #ffe4e4;
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
  z-index: 1;
}

.wheel-wrap {
  position: relative;
  /* Full container width (matches the SPIN button and title exactly) instead
     of a fixed 560px — a fixed value left a 6px gap on each side versus the
     button/title (which are 100% width), so the wheel's edges didn't line up
     with them. Percentage width also means: on narrow viewports (mobile
     iframe) it naturally shrinks with the container, and aspect-ratio keeps
     the height in lockstep so the wheel never looks oval/squashed. */
  width: 100%;
  aspect-ratio: 1 / 1;
  margin: 34px auto 28px;
}

/* Removed — a second rotating/pulsing glow layer behind the wheel on top
   of .wheel-glow, the reactor ring, and the rim arcs was too much at once
   (reviewed as cluttered/amateurish). .wheel-glow alone carries the ambient
   halo now. */
.light-rays {
  display: none;
}

/* A single, even, soft gold halo — wraps the wheel symmetrically, no blotches */
.wheel-glow {
  position: absolute;
  inset: -28px;
  border-radius: 50%;
  background: radial-gradient(
    circle,
    rgba(239, 68, 68, 0.22) 0%,
    rgba(239, 68, 68, 0.06) 48%,
    transparent 70%
  );
  filter: blur(26px);
  animation: pulse 4.5s ease-in-out infinite;
  z-index: 0;
}

@keyframes pulse {
  0%, 100% { opacity: 0.55; transform: scale(1); }
  50% { opacity: 0.8; transform: scale(1.025); }
}

.wheel-glow.active {
  animation: pulseFast 0.5s ease-in-out infinite;
}

@keyframes pulseFast {
  0%, 100% { opacity: 0.7; transform: scale(1.02); }
  50% { opacity: 1; transform: scale(1.12); }
}

#spinBtn.spinning {
  animation: btnPulse 0.6s ease-in-out infinite;
}

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

/* Lightning-bolt pointer instead of the usual arrow/flag — deliberately off
   the standard wheel-pointer look for a more "authentic"/unconventional
   feel. The outer container (.pointer) only fixes the POSITION; the actual
   visual and spin animation live in the inner .pointer-shape — the "catch on
   segment boundary" effect (see wheel.js) rotates only the inner piece like
   a hinge at the top, without disturbing the position. */
.pointer {
  position: absolute;
  top: -5.5%;
  left: 50%;
  width: 11%;
  height: 16%;
  transform: translateX(-50%);
  z-index: 4;
}

/* A cut energy-crystal shard instead of the lightning bolt — matches the
   chrome frame + reactor ring language elsewhere on the wheel (faceted
   metal/glass, not a novelty zigzag). Flat faceted shoulders taper to one
   sharp point touching the wheel, still reading clearly as "the pointer"
   at a glance. */
.pointer-shape {
  position: relative;
  width: 100%;
  height: 100%;
  transform-origin: 50% 0%;
  /* Dark garnet at the crown, blazing white-hot at the tip touching the
     wheel — the charge builds toward the contact point, same logic as the
     old bolt, now on a faceted gem silhouette. */
  background: linear-gradient(180deg, #2a0a0a 0%, #7a1212 30%, #ef4444 62%, #f87171 82%, #ffe8e8 94%, #ffffff 100%);
  clip-path: polygon(50% 100%, 14% 58%, 30% 0%, 70% 0%, 86% 58%);
  filter:
    drop-shadow(0 4px 8px rgba(0, 0, 0, 0.55))
    drop-shadow(0 0 12px rgba(248, 113, 113, 0.9))
    drop-shadow(0 0 26px rgba(239, 68, 68, 0.55));
}

/* Facet split: left half catches light, right half sits in shadow — a flat
   gradient alone reads as a sticker; this diagonal split across the
   centerline is what makes it read as a faceted, three-dimensional cut
   stone instead. */
.pointer-shape::before {
  content: "";
  position: absolute;
  inset: 0;
  clip-path: inherit;
  background: linear-gradient(100deg, rgba(255, 255, 255, 0.55) 0%, rgba(255, 255, 255, 0.1) 46%, transparent 50%, rgba(0, 0, 0, 0.4) 100%);
}

/* A thin bright spine down the exact center — the "ridge" where the two
   front facets of a cut gem meet, catching a sharper highlight than the
   flat faces on either side of it. */
.pointer-shape::after {
  content: "";
  position: absolute;
  top: 2%;
  bottom: 2%;
  left: 48%;
  width: 4%;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.85), rgba(255, 255, 255, 0.15) 70%, rgba(255, 255, 255, 0.9));
  filter: blur(0.5px);
}

.wheel-rotor {
  position: absolute;
  inset: 0;
  z-index: 1;
}

.wheel-rotor.idle-spin {
  animation: idleRotate 26s linear infinite;
}

@keyframes idleRotate {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

#wheelCanvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  /* Brushed gunmetal frame with MULTIPLE highlight/shadow bands (not one
     smooth gradient) — a real machined metal ring catches light in several
     distinct bands around its curved cross-section, not one soft fade; more
     stops = it reads as a rounded/beveled physical rim instead of a flat
     painted circle. */
  border: 26px solid transparent;
  background-image:
    linear-gradient(#170d0d, #170d0d),
    linear-gradient(
      344deg,
      #0f0f13 0%,
      #3a3d47 12%,
      #7d828f 22%,
      #c8ccd6 30%,
      #7d828f 38%,
      #2a2c33 48%,
      #1a1b1f 58%,
      #55575f 68%,
      #9195a0 76%,
      #3a3d47 86%,
      #0f0f13 100%
    );
  background-origin: border-box;
  background-clip: content-box, border-box;
  box-shadow:
    0 0 0 2px #0a0505,
    0 0 0 5px rgba(248, 113, 113, 0.4),
    0 18px 60px rgba(0,0,0,0.7),
    inset 0 0 50px rgba(0,0,0,0.65),
    inset 0 3px 12px rgba(255,255,255,0.18);
}

/* Electric ring — lives INSIDE .wheel-rotor now (see index.html), so it
   physically spins together with the wheel. Its arcs are drawn anchored to
   the same rivet angles drawWheel() places the studs at (see wheel.js),
   which is what makes them read as current jumping between real studs on a
   spinning object instead of a decorative overlay drifting independently of
   the thing it's supposedly attached to. */
.electric-ring {
  position: absolute;
  inset: -9%;
  width: 118%;
  height: 118%;
  z-index: 2;
  pointer-events: none;
}

/* Reactor charge ring — sized/positioned like .electric-ring used to be
   (fixed, oversized so the glow can bloom past the frame's own edge). Drawn
   entirely in wheel.js (reactorTick and friends); this block is just
   placement. Sits above the metal frame (z-index 1) and the rotating
   electric arcs live INSIDE the rotor, so ordering vs. those doesn't matter
   much — kept just below the HUD corners (z-index 5) and hub (z-index 3),
   above the plain frame. */
.reactor-ring {
  position: absolute;
  inset: -9%;
  width: 118%;
  height: 118%;
  z-index: 2;
  pointer-events: none;
}

/* HUD targeting-reticle corners — the "cyberpunk panel" framing cue: an
   L-shaped bracket at each corner of the wheel, floating outside it, like a
   scanner lock-on UI. Fully static now (no pulse) — it's a fixed instrument
   bezel; the reactor ring is the one thing in this UI allowed to visibly
   animate its own state, everything else stays quiet so that reads clearly. */
.hud-corner {
  position: absolute;
  width: 30px;
  height: 30px;
  border: 2px solid rgba(248, 113, 113, 0.7);
  filter: drop-shadow(0 0 6px rgba(239, 68, 68, 0.6));
  z-index: 5;
  pointer-events: none;
}

.hud-corner.tl { top: -15px; left: -15px; border-right: none; border-bottom: none; }
.hud-corner.tr { top: -15px; right: -15px; border-left: none; border-bottom: none; }
.hud-corner.bl { bottom: -15px; left: -15px; border-right: none; border-top: none; }
.hud-corner.br { bottom: -15px; right: -15px; border-left: none; border-top: none; }

.wheel-wrap.charged .hud-corner {
  border-color: rgba(255, 255, 255, 0.9);
}

/* Lightning strike on landing: a white/red flash over the wheel + a jolt
   on the (non-rotating) wrap + an intensified glow on the pointer bolt —
   plays right before the result modal opens, so the win reads as "struck". */
.strike-flash {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  pointer-events: none;
  z-index: 6;
  background: radial-gradient(circle, rgba(255,255,255,0.95) 0%, rgba(248, 113, 113, 0.75) 40%, rgba(239, 68, 68, 0) 75%);
  opacity: 0;
}

.strike-flash.active {
  animation: lightningFlash 0.62s ease-out;
}

@keyframes lightningFlash {
  0% { opacity: 0; }
  5% { opacity: 1; }
  12% { opacity: 0.2; }
  20% { opacity: 1; }
  32% { opacity: 0.1; }
  46% { opacity: 0.4; }
  62% { opacity: 0; }
  100% { opacity: 0; }
}

/* Twin expanding shockwave rings — the classic "explosion" cue: a bright
   ring bursting outward past the frame's own edge and fading, not just a
   flash sitting still. Two rings offset slightly in timing/scale so it
   reads as a genuine blast (overlapping shells) rather than one circle
   growing. mix-blend-mode: screen so they only ever brighten what's under
   them (never muddy/darken the wheel as they cross it). */
.strike-flash::before,
.strike-flash::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  pointer-events: none;
  border: 3px solid rgba(255, 255, 255, 0.95);
  opacity: 0;
  mix-blend-mode: screen;
}

.strike-flash.active::before {
  animation: shockwaveExpand 0.6s cubic-bezier(0.15, 0.7, 0.4, 1);
}

.strike-flash.active::after {
  border-color: rgba(248, 113, 113, 0.9);
  animation: shockwaveExpand 0.65s cubic-bezier(0.15, 0.7, 0.4, 1) 0.07s;
}

@keyframes shockwaveExpand {
  0% { transform: scale(0.25); opacity: 0.95; border-width: 5px; }
  70% { opacity: 0.4; }
  100% { transform: scale(1.9); opacity: 0; border-width: 1px; }
}

/* Shakes the WRAP (not .wheel-rotor) — the rotor's transform is the wheel's
   actual landed rotation, set directly via JS; animating transform on it
   here would fight that and snap the wheel off its landed angle. A brief
   scale "punch" is layered into the same keyframes (not just translate) for
   extra impact — a real blast shove has a compression beat, not just a shake. */
.wheel-wrap.impact {
  animation: impactShake 0.55s ease-out;
}

@keyframes impactShake {
  0% { transform: translate(0, 0) scale(1); }
  8% { transform: translate(-3px, 2px) scale(1.035); }
  20% { transform: translate(-9px, 5px) scale(1.01); }
  34% { transform: translate(7px, -6px) scale(1.015); }
  48% { transform: translate(-6px, 3px) scale(1); }
  62% { transform: translate(4px, -3px) scale(1); }
  76% { transform: translate(-2px, 2px) scale(1); }
  90% { transform: translate(1px, -1px) scale(1); }
  100% { transform: translate(0, 0) scale(1); }
}

/* Filter-only (not transform) — .pointer-shape's transform is driven
   directly via JS for the segment-catch tick, so this only touches filter. */
.pointer-shape.strike {
  animation: boltStrike 0.6s ease-out;
}

@keyframes boltStrike {
  0%, 100% {
    filter:
      drop-shadow(0 4px 8px rgba(0, 0, 0, 0.55))
      drop-shadow(0 0 12px rgba(248, 113, 113, 0.9))
      drop-shadow(0 0 26px rgba(239, 68, 68, 0.55));
  }
  18% {
    filter:
      drop-shadow(0 4px 8px rgba(0, 0, 0, 0.55))
      drop-shadow(0 0 24px rgba(255, 255, 255, 1))
      drop-shadow(0 0 80px rgba(248, 113, 113, 1))
      drop-shadow(0 0 140px rgba(239, 68, 68, 0.9));
  }
  40% {
    filter:
      drop-shadow(0 4px 8px rgba(0, 0, 0, 0.55))
      drop-shadow(0 0 14px rgba(255, 255, 255, 0.7))
      drop-shadow(0 0 50px rgba(248, 113, 113, 0.7))
      drop-shadow(0 0 90px rgba(239, 68, 68, 0.5));
  }
}

/* A single soft breathing light behind the center logo — bigger than .hub
   and centered on it, so it reads as light spilling OUT from behind the
   medallion. Left as ONE slow, constant pulse regardless of spin state
   (removed the earlier "speeds up while charged" variant) — the reactor
   ring already communicates spin/charge state; the hub doesn't need to
   race it too. */
.hub-glow {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 52%;
  height: 52%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  z-index: 2;
  pointer-events: none;
  background: radial-gradient(
    circle,
    rgba(255, 200, 200, 0.75) 0%,
    rgba(239, 68, 68, 0.55) 32%,
    rgba(239, 68, 68, 0.2) 58%,
    transparent 76%
  );
  filter: blur(5px);
  animation: hubPulse 3.4s ease-in-out infinite;
}

@keyframes hubPulse {
  0%, 100% { opacity: 0.65; transform: translate(-50%, -50%) scale(0.94); }
  50% { opacity: 1; transform: translate(-50%, -50%) scale(1.08); }
}

.hub {
  position: absolute;
  top: 50%;
  left: 50%;
  /* Used to be a fixed px value (140px) — when .wheel-wrap narrowed on small
     screens (mobile iframe), the hub didn't shrink proportionally and
     overlapped the segment labels. Percentage-based like everything else
     (canvas, pointer, gems): 140/560 = 25%. */
  width: 25%;
  height: 25%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  /* The frame rings we used to add (three-color, then a single gold one)
     overlapped logo.png's OWN ring and produced a "crooked/double edge"
     feel. Now there are no extra rings at all — just the logo with its own
     ring, plus a subtle shadow and nothing else. */
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);
  z-index: 3;
}

.hub img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.cooldown {
  font-size: 13px;
  color: #a9adc1;
  margin-top: 14px;
}

.modal {
  position: absolute;
  inset: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(4, 5, 12, 0.78);
  backdrop-filter: blur(8px);
  border-radius: 20px;
  opacity: 0;
  animation: modalFadeIn 0.3s ease forwards;
}

.modal.hidden {
  display: none !important;
}

@keyframes modalFadeIn {
  to { opacity: 1; }
}

#confettiCanvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 0;
}

.modal-card {
  position: relative;
  width: 84%;
  padding: 30px 24px 24px;
  text-align: center;
  background: linear-gradient(160deg, #14181c, #030405);
  border: 1px solid rgba(239, 68, 68, 0.4);
  border-radius: 18px;
  box-shadow: 0 24px 60px rgba(0,0,0,0.6), 0 0 50px rgba(239, 68, 68, 0.15);
  transform: scale(0.6);
  opacity: 0;
  animation: cardPopIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) 0.1s forwards;
}

@keyframes cardPopIn {
  to { transform: scale(1); opacity: 1; }
}

/* No-win (Try Again) screen — DELIBERATELY muted/neutral compared to the win
   screen: matte silver instead of a gold border, no confetti/burst. Meant to
   read as a clean "info screen" without implying a victory. */
.modal-card--nowin {
  border-color: rgba(199, 206, 222, 0.25);
  box-shadow: 0 24px 60px rgba(0,0,0,0.6), 0 0 40px rgba(199, 206, 222, 0.06);
}

.modal-card--nowin .burst {
  display: none;
}

.burst {
  position: absolute;
  top: 62px;
  left: 50%;
  width: 200px;
  height: 200px;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle, rgba(239, 68, 68, 0.4), transparent 70%);
  z-index: 0;
  animation: burstPulse 1.8s ease-out infinite;
}

@keyframes burstPulse {
  0% { opacity: 0.9; transform: translate(-50%, -50%) scale(0.5); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1.5); }
}

.prize-icon {
  position: relative;
  width: 72px;
  height: 72px;
  margin: 0 auto 14px;
  border-radius: 50%;
  /* Black chrome medallion with a red glow ring — not a solid red disc. */
  background: radial-gradient(circle at 35% 30%, #4b525a, #1a1d21 70%, #0c0e10 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 0 4px rgba(239, 68, 68, 0.35), 0 0 24px rgba(239, 68, 68, 0.3), 0 8px 24px rgba(0,0,0,0.5);
  animation: iconBounce 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) 0.2s both;
}

.modal-card--nowin .prize-icon {
  background: radial-gradient(circle at 35% 30%, #3d4759, #262e3c 60%, #171d27 100%);
  box-shadow: 0 0 0 4px rgba(199, 206, 222, 0.12), 0 8px 24px rgba(0,0,0,0.4);
}

@keyframes iconBounce {
  0% { transform: scale(0); }
  70% { transform: scale(1.18); }
  100% { transform: scale(1); }
}

.result-title {
  position: relative;
  font-size: 26px;
  font-weight: 800;
  margin: 0 0 8px;
  text-shadow: 0 0 20px rgba(248, 113, 113, 0.5);
  /* Holographic sheen: an animated gradient painted THROUGH the text
     (background-clip: text) instead of a flat color — a light band sweeps
     across the letters on a loop, the classic "hologram projection"
     read, rather than a static tint. */
  background: linear-gradient(100deg, #f87171 30%, #fff 50%, #f87171 70%);
  background-size: 220% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: holoSweep 3.2s linear infinite;
}

@keyframes holoSweep {
  0% { background-position: 120% 0; }
  100% { background-position: -20% 0; }
}

.modal-card--nowin .result-title {
  /* Neutral/muted holographic sheen instead of red — keeps the deliberately
     "not a win" flatness (see the comment on .modal-card--nowin below) while
     still matching the animated background-clip:text mechanism above (a
     plain color: declaration would be invisible under color: transparent). */
  background: linear-gradient(100deg, #9aa2b1 30%, #fff 50%, #9aa2b1 70%);
  background-size: 220% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  text-shadow: none;
}

.result-text {
  position: relative;
  font-size: 21px;
  font-weight: 700;
  margin-bottom: 4px;
}

.result-text.hidden {
  display: none;
}

.result-subtext {
  position: relative;
  font-size: 14px;
  line-height: 1.5;
  color: #a9adc1;
  margin: 0 0 18px;
  /* Lets a literal \n\n in the no-win message render as a paragraph break
     (title + tagline, blank line, then the Telegram lead-in) without
     resorting to innerHTML/<br>. */
  white-space: pre-line;
}

.result-subtext.hidden {
  display: none;
}

.game-name {
  position: relative;
  font-size: 14px;
  font-weight: 600;
  color: #a9adc1;
  margin: 0 0 16px;
}

.game-name.hidden {
  display: none;
}

.game-link {
  position: relative;
  display: block;
  width: 100%;
  padding: 14px;
  margin-bottom: 10px;
  border-radius: 12px;
  background: linear-gradient(135deg, #F87171, #EF4444);
  color: #450a0a;
  font-weight: 800;
  font-size: 15px;
  letter-spacing: 0.5px;
  text-decoration: none;
  box-shadow: 0 8px 24px rgba(239, 68, 68, 0.35);
}

.game-link.hidden {
  display: none;
}

.game-link.disabled {
  opacity: 0.5;
  pointer-events: none;
}

/* Telegram brand blue — deliberately different from the other action buttons
   (gold/red) to give a "this is a separate channel" feel. It's the main
   CTA on the no-win screen, so it's a bit more prominent (icon + label, centered). */
.telegram-link {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  padding: 14px;
  margin-bottom: 10px;
  border-radius: 12px;
  background: linear-gradient(135deg, #2AABEE, #229ED9);
  color: #fff;
  font-weight: 800;
  font-size: 15px;
  letter-spacing: 0.5px;
  text-decoration: none;
  box-shadow: 0 8px 24px rgba(42, 171, 238, 0.4), inset 0 1px 0 rgba(255,255,255,0.25);
}

.telegram-link svg {
  flex-shrink: 0;
}

.telegram-link.hidden {
  display: none;
}

.secondary-btn {
  position: relative;
  width: 100%;
  /* 12px was slightly under the ~44px minimum recommended touch target
     height (measured 41px) — bumped to match the other action buttons. */
  padding: 14px;
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: 12px;
  background: transparent;
  color: #a9adc1;
  font-weight: 700;
  font-size: 14px;
  cursor: pointer;
  box-shadow: none;
}

.secondary-btn:hover {
  background: rgba(255,255,255,0.06);
  transform: none;
  box-shadow: none;
}

.secondary-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.secondary-btn:disabled:hover {
  background: transparent;
}
