@import url("https://fonts.googleapis.com/css2?family=Oswald:wght@400;600;700&family=Share+Tech+Mono&display=swap");

body {
  background: #050505;
  font-family: "Oswald", sans-serif;
  margin: 0;
  overflow-x: hidden;
}

#chromatic-bg {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  z-index: -1;
  pointer-events: none;
}

.font-mono-game {
  font-family: "Share Tech Mono", monospace;
}

.custom-scrollbar::-webkit-scrollbar {
  width: 6px;
}

.custom-scrollbar::-webkit-scrollbar-track {
  background: rgba(0, 0, 0, 0.2);
  border-radius: 3px;
}

.custom-scrollbar::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.1);
  border-radius: 3px;
}

.custom-scrollbar::-webkit-scrollbar-thumb:hover {
  background: rgba(255, 255, 255, 0.2);
}

@keyframes spin-blur {

  0%,
  100% {
    filter: blur(0px) brightness(1);
  }

  50% {
    filter: blur(3px) brightness(1.4);
  }
}

@keyframes reel-roll {
  0% {
    transform: translateY(-50%);
  }

  100% {
    transform: translateY(0);
  }
}

@keyframes tilt-pulse {

  0%,
  100% {
    box-shadow: 0 0 30px 5px rgba(202, 138, 4, 0.2);
  }

  50% {
    box-shadow: 0 0 60px 15px rgba(202, 138, 4, 0.5);
  }
}

@keyframes win-glow {

  0%,
  100% {
    text-shadow: 0 0 10px currentColor;
  }

  50% {
    text-shadow:
      0 0 30px currentColor,
      0 0 60px currentColor;
  }
}

@keyframes float-up {
  0% {
    opacity: 1;
    transform: translateY(0);
  }

  100% {
    opacity: 0;
    transform: translateY(-60px);
  }
}

@keyframes wheel-spin {
  0% {
    transform: rotate(0deg);
  }

  100% {
    transform: rotate(var(--wheel-end-deg));
  }
}

/* (bounce-stop keyframes live further down, next to rain-down — a second
   px-based copy here used to shadow-fight the %-based one.) */

@keyframes win-pop {
  0% {
    transform: scale(0.3);
    opacity: 0;
  }

  50% {
    transform: scale(1.1);
    opacity: 1;
  }

  100% {
    transform: scale(1);
    opacity: 1;
  }
}

@keyframes win-pulse-scale {

  0%,
  100% {
    transform: scale(1);
  }

  50% {
    transform: scale(1.05);
  }
}

@keyframes super-win-pop {
  0% {
    transform: scale(0.5);
    opacity: 0;
  }

  50% {
    transform: scale(1.15);
    opacity: 1;
  }

  100% {
    transform: scale(1);
    opacity: 1;
  }
}

@keyframes ultra-win-pop {
  0% {
    transform: scale(0.3) rotate(-15deg);
    opacity: 0;
  }

  30% {
    transform: scale(1.2) rotate(10deg);
    opacity: 1;
  }

  50% {
    transform: scale(0.9) rotate(-5deg);
    opacity: 1;
  }

  70% {
    transform: scale(1.1) rotate(5deg);
    opacity: 1;
  }

  100% {
    transform: scale(1) rotate(0deg);
    opacity: 1;
  }
}

.animate-ultra-win {
  animation:
    ultra-win-pop 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards,
    win-pulse-scale 1s ease-in-out 0.8s infinite;
}

.animate-super-win {
  animation:
    super-win-pop 0.7s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards,
    win-pulse-scale 1.2s ease-in-out 0.7s infinite;
}

.animate-win-pop {
  animation:
    win-pop 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards,
    win-pulse-scale 1.5s ease-in-out 0.6s infinite;
}

/* MEGA sits between BIG (win-pop) and SUPER (super-win-pop): a punchier pop with
   a little settle dip so it reads as a distinct step up, not a clone of SUPER. */
@keyframes mega-win-pop {
  0%   { transform: scale(0.4);  opacity: 0; }
  45%  { transform: scale(1.13); opacity: 1; }
  72%  { transform: scale(0.97); opacity: 1; }
  100% { transform: scale(1);    opacity: 1; }
}

.animate-mega-win {
  animation:
    mega-win-pop 0.65s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards,
    win-pulse-scale 1.35s ease-in-out 0.65s infinite;
}

/* Big-win headline: slams in from above (rotated + oversized), overshoots
   back to upright for impact, then settles into a continuous pulse/wobble. */
@keyframes big-win-impact {
  0% {
    transform: translateY(-30px) scale(1.5) rotate(-12deg);
    opacity: 0;
  }

  45% {
    transform: translateY(0) scale(0.92) rotate(7deg);
    opacity: 1;
  }

  70% {
    transform: scale(1.05) rotate(-3deg);
    opacity: 1;
  }

  100% {
    transform: scale(1) rotate(0deg);
    opacity: 1;
  }
}

@keyframes big-win-pulse {

  0%,
  100% {
    transform: scale(1) rotate(0deg);
  }

  50% {
    transform: scale(1.05) rotate(1.5deg);
  }
}

.animate-big-win-text {
  animation:
    big-win-impact 0.75s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards,
    big-win-pulse 1.4s ease-in-out 0.75s infinite;
}

/* Swirling themed light ring drawn around the reels while a slot feature
   applies mid-spin. Color comes from per-slot --swirl-color / --swirl-rgb. */
@property --swirl-angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

@keyframes feature-swirl {
  to {
    --swirl-angle: 360deg;
  }
}

.feature-swirl-border {
  --swirl-color: #ffffff;
  --swirl-rgb: 255, 255, 255;
  position: absolute;
  inset: -4px;
  border-radius: 14px;
  padding: 5px;
  pointer-events: none;
  z-index: 0;
  background: conic-gradient(from var(--swirl-angle),
      transparent 0deg,
      var(--swirl-color) 60deg,
      #ffffff 90deg,
      var(--swirl-color) 120deg,
      transparent 180deg,
      var(--swirl-color) 240deg,
      #ffffff 270deg,
      var(--swirl-color) 300deg,
      transparent 360deg);
  -webkit-mask:
    linear-gradient(#fff 0 0) content-box,
    linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  filter:
    drop-shadow(0 0 4px var(--swirl-color)) drop-shadow(0 0 9px rgba(var(--swirl-rgb), 0.6));
  animation: feature-swirl 2.4s linear infinite;
}

.animate-tilt-pulse {
  animation: tilt-pulse 1.5s ease-in-out infinite;
}

.animate-win-glow {
  animation: win-glow 1s ease-in-out infinite;
}

.animate-float-up {
  animation: float-up 2s ease-out forwards;
}

/* Note: no static transform here — the reel-roll animation owns `transform`
   while spinning (any scaleY set alongside it is dead-on-arrival and would
   also break the seamless -50% wrap). will-change promotes the strip to its
   own compositor layer BEFORE the first frame, so spin-up doesn't hitch. */
.reel-spinning .reel-strip {
  animation: reel-roll 0.12s linear infinite;
  filter: blur(1px);
  will-change: transform;
}

.reel-spinning-fast .reel-strip {
  animation: reel-roll 0.05s linear infinite;
  filter: blur(2px) brightness(1.3);
  will-change: transform;
}

.symbol-win {
  animation: win-glow 0.8s ease-in-out infinite;
  box-shadow: 0 0 20px currentColor;
}

@keyframes win-float {

  0%,
  100% {
    transform: translateY(0) scale(1.05);
  }

  50% {
    transform: translateY(-8px) scale(1.08);
  }
}

@keyframes win-shimmer {
  0% {
    filter: brightness(1) drop-shadow(0 0 4px rgba(251, 191, 36, 0.3));
  }

  25% {
    filter: brightness(1.4) drop-shadow(0 0 12px rgba(251, 191, 36, 0.8));
  }

  50% {
    filter: brightness(1.1) drop-shadow(0 0 6px rgba(251, 191, 36, 0.5));
  }

  75% {
    filter: brightness(1.5) drop-shadow(0 0 16px rgba(255, 255, 255, 0.6));
  }

  100% {
    filter: brightness(1) drop-shadow(0 0 4px rgba(251, 191, 36, 0.3));
  }
}

@keyframes win-border-pulse {

  0%,
  100% {
    border-color: rgba(251, 191, 36, 0.6);
    box-shadow:
      0 0 15px 2px rgba(251, 191, 36, 0.3),
      inset 0 0 8px rgba(251, 191, 36, 0.1);
  }

  50% {
    border-color: rgba(255, 255, 255, 0.9);
    box-shadow:
      0 0 30px 6px rgba(251, 191, 36, 0.6),
      inset 0 0 15px rgba(251, 191, 36, 0.2);
  }
}

.symbol-winning {
  animation:
    win-float 1.2s ease-in-out infinite,
    win-shimmer 1.5s ease-in-out infinite,
    win-border-pulse 1s ease-in-out infinite;
  z-index: 10;
  position: relative;
}

@keyframes win-fade-out {
  0% {
    opacity: 1;
  }

  70% {
    opacity: 1;
  }

  100% {
    opacity: 0;
  }
}

.win-popup-auto-hide {
  animation: win-pop 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
}

@keyframes cash-lock {
  0% {
    transform: scale(0.3) rotate(-10deg);
    opacity: 0;
  }

  50% {
    transform: scale(1.2) rotate(5deg);
    opacity: 1;
  }

  100% {
    transform: scale(1) rotate(0deg);
    opacity: 1;
  }
}

.animate-cash-lock {
  animation: cash-lock 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
}

@keyframes heist-pulse {

  0%,
  100% {
    box-shadow: 0 0 8px rgba(251, 191, 36, 0.3);
  }

  50% {
    box-shadow: 0 0 25px rgba(251, 191, 36, 0.7);
  }
}

.heist-cell-locked {
  animation: heist-pulse 1.5s ease-in-out infinite;
}

::-webkit-scrollbar {
  width: 6px;
}

::-webkit-scrollbar-track {
  background: #1c1917;
}

::-webkit-scrollbar-thumb {
  background: #57534e;
  border-radius: 3px;
}

@keyframes spin-background {
  from {
    transform: translate(-50%, -50%) rotate(0deg);
  }

  to {
    transform: translate(-50%, -50%) rotate(360deg);
  }
}

@keyframes shimmer-edge {
  0% {
    transform: translateX(-100%);
  }

  100% {
    transform: translateX(100%);
  }
}

.jackpot-card {
  position: relative;
  overflow: hidden;
}

.jackpot-card::before {
  content: "";
  position: absolute;
  z-index: 0;
  left: -50%;
  top: -50%;
  width: 200%;
  height: 200%;
  background-image: conic-gradient(from 90deg at 50% 50%,
      transparent 0%,
      transparent 35%,
      rgba(255, 255, 255, 0.4) 50%,
      transparent 65%,
      transparent 100%);
  animation: spin-background 6s linear infinite;
  filter: blur(12px);
}

.jackpot-card::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 200%;
  height: 100%;
  background: linear-gradient(110deg,
      transparent 0%,
      transparent 30%,
      rgba(255, 255, 255, 0.25) 50%,
      transparent 70%,
      transparent 100%);
  animation: shimmer-edge 4s ease-in-out infinite;
  transform: translateX(-100%);
  pointer-events: none;
}

.jackpot-grand {
  box-shadow:
    0 0 15px 2px rgba(251, 191, 36, 0.4),
    0 0 30px 5px rgba(239, 68, 68, 0.2);
}

.jackpot-grand::before {
  animation-duration: 2s;
  background-image: conic-gradient(from 90deg at 50% 50%,
      transparent 0%,
      transparent 35%,
      rgba(255, 215, 0, 0.6) 50%,
      transparent 65%,
      transparent 100%);
}

.jackpot-grand::after {
  animation-duration: 2.5s;
  background: linear-gradient(110deg,
      transparent 0%,
      transparent 30%,
      rgba(255, 255, 255, 0.6) 50%,
      transparent 70%,
      transparent 100%);
}

@keyframes floaty-hover {

  0%,
  100% {
    transform: translateY(0);
  }

  50% {
    transform: translateY(-20px);
  }
}

@keyframes rhythmic-pulse {

  0%,
  100% {
    transform: scale(1);
    filter: brightness(1);
  }

  50% {
    transform: scale(1.05);
    filter: brightness(1.3);
  }
}

@keyframes particle-drift {
  0% {
    transform: translate(0, 0) scale(1);
    opacity: 0;
  }

  20% {
    opacity: 0.8;
  }

  80% {
    opacity: 0.8;
  }

  100% {
    transform: translate(var(--tx), var(--ty)) scale(0.5);
    opacity: 0;
  }
}

@keyframes overlay-fade-in {
  0% {
    opacity: 0;
  }

  100% {
    opacity: 1;
  }
}

@keyframes xp-fill {
  from {
    width: var(--xp-from, 0%);
  }

  to {
    width: var(--xp-to, 100%);
  }
}

@keyframes level-up-pop {
  0% {
    transform: scale(0.3) rotate(-10deg);
    opacity: 0;
  }

  40% {
    transform: scale(1.3) rotate(5deg);
    opacity: 1;
  }

  60% {
    transform: scale(0.9) rotate(-3deg);
    opacity: 1;
  }

  80% {
    transform: scale(1.05) rotate(1deg);
    opacity: 1;
  }

  100% {
    transform: scale(1) rotate(0deg);
    opacity: 1;
  }
}

@keyframes tier-shimmer {
  0% {
    background-position: -200% center;
  }

  100% {
    background-position: 200% center;
  }
}

@keyframes tier-card-entrance {
  0% {
    opacity: 0;
    transform: translateY(30px) scale(0.9);
  }

  100% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

@keyframes vip-glow-pulse {

  0%,
  100% {
    box-shadow: 0 0 8px var(--tier-glow, rgba(205, 127, 50, 0.4));
  }

  50% {
    box-shadow: 0 0 20px var(--tier-glow, rgba(205, 127, 50, 0.6));
  }
}

.animate-level-up {
  animation: level-up-pop 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
}

.animate-tier-shimmer {
  background-size: 200% auto;
  animation: tier-shimmer 3s linear infinite;
}

.animate-vip-glow {
  animation: vip-glow-pulse 2s ease-in-out infinite;
}

/* ============================================================
   MULTI-SLOT THEME SYSTEM (CSS Custom Properties)
   ============================================================ */
:root {
  --theme-primary: #fbbf24;
  --theme-bg: #050505;
  --theme-accent: #daa520;
  --theme-glow: rgba(251, 191, 36, 0.4);
  --theme-border: rgba(161, 98, 7, 0.5);
}

/* ============================================================
   ANIMATED NUMBER POP (count-up win feedback)
   ============================================================ */
@keyframes num-pop {
  0%   { transform: scale(1); }
  30%  { transform: scale(1.22); filter: brightness(1.5) drop-shadow(0 0 6px currentColor); }
  100% { transform: scale(1); }
}
.animate-num-pop {
  display: inline-block;
  animation: num-pop 0.5s ease-out;
}

/* ============================================================
   PLAY-NOW BUTTON BORDER LIGHT
   ============================================================ */
@keyframes border-spin {
  from { transform: translate(-50%, -50%) rotate(0deg); }
  to   { transform: translate(-50%, -50%) rotate(360deg); }
}

.play-btn-wrap {
  position: relative;
  border-radius: 0.75rem;
  padding: 3px;
  overflow: hidden;
}

/* Spinning conic-gradient — only visible in the padding ring because
   .play-btn-inner's opaque background covers the center.
   aspect-ratio:1 keeps the gradient a true square so the light doesn't
   stretch/accelerate across the button's long edges. */
.play-btn-wrap::before {
  content: '';
  position: absolute;
  left: 50%; top: 50%;
  width: 250%;
  aspect-ratio: 1 / 1;
  transform: translate(-50%, -50%) rotate(0deg);
  background: conic-gradient(
    from 0deg,
    transparent 0deg,
    transparent 165deg,
    var(--btn-color2, #fbbf24) 210deg,
    var(--btn-color, #fbbf24) 260deg,
    #ffffff 305deg,
    var(--btn-color, #fbbf24) 340deg,
    transparent 360deg
  );
  filter: blur(1.5px) saturate(1.5) drop-shadow(0 0 4px var(--btn-color, #fbbf24));
  opacity: 0;
  animation: border-spin 4s linear infinite;
  transition: opacity 0.3s ease;
}

.lobby-card:hover .play-btn-wrap::before {
  opacity: 1;
  filter: blur(1px) saturate(1.8) brightness(1.3) drop-shadow(0 0 7px var(--btn-color, #fbbf24));
}

/* Must be opaque — any alpha here lets the light bleed through the center */
.play-btn-inner {
  position: relative;
  z-index: 1;
  border-radius: calc(0.75rem - 3px);
  overflow: hidden;
}

/* ============================================================
   LOBBY STYLES
   ============================================================ */
@keyframes lobby-float {

  0%,
  100% {
    transform: translateY(0px);
  }

  50% {
    transform: translateY(-12px);
  }
}

@keyframes lobby-glow-pulse {

  0%,
  100% {
    box-shadow: 0 0 20px var(--card-glow, rgba(251, 191, 36, 0.2)), 0 8px 32px rgba(0, 0, 0, 0.5);
  }

  50% {
    box-shadow: 0 0 40px var(--card-glow, rgba(251, 191, 36, 0.4)), 0 8px 32px rgba(0, 0, 0, 0.5);
  }
}

@keyframes lobby-shimmer {
  0% {
    background-position: -200% center;
  }

  100% {
    background-position: 200% center;
  }
}

@keyframes lobby-entrance {
  0% {
    opacity: 0;
    transform: translateY(40px) scale(0.9);
  }

  100% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

/* Entrance uses fill-mode `backwards` (not `both`): it still hides the card
   during its staggered delay, but releases `transform` once finished. With
   `both`, the filled final keyframe kept overriding the hover tilt (CSS
   animations outrank inline styles), so cards never lifted under the cursor. */
.lobby-card {
  animation: lobby-entrance 0.6s ease-out backwards, lobby-glow-pulse 3s ease-in-out infinite;
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}

/* While hovered, stop the looping box-shadow pulse and lift the card with a
   themed glow (--card-glow is set inline per machine). This is the ONLY
   hover transform — a single source, so the motion always settles cleanly. */
.lobby-card:hover {
  animation-name: lobby-entrance, none;
  transform: translateY(-8px) scale(1.03);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.55), 0 0 26px var(--card-glow, rgba(251, 191, 36, 0.25));
  z-index: 10;
}

/* ============================================================
   LOGIN SCREEN ANIMATIONS
   ============================================================ */
@keyframes login-particle-float {
  0%, 100% {
    opacity: 0;
    transform: translateY(0) scale(0.8);
  }
  15% {
    opacity: 0.12;
  }
  50% {
    opacity: 0.18;
    transform: translateY(-40px) scale(1.1) rotate(15deg);
  }
  85% {
    opacity: 0.08;
  }
}

@keyframes login-card-enter {
  0% {
    opacity: 0;
    transform: translateY(30px) scale(0.95);
  }
  100% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

@keyframes login-form-slide {
  0% {
    opacity: 0;
    transform: translateX(20px);
  }
  100% {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes login-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-8px); }
  40% { transform: translateX(6px); }
  60% { transform: translateX(-4px); }
  80% { transform: translateX(2px); }
}

@keyframes login-glow-pulse {
  0%, 100% { opacity: 0.5; transform: scale(1); }
  50% { opacity: 1; transform: scale(1.15); }
}

/* ============================================================
   LAVA THEME ANIMATIONS
   ============================================================ */
@keyframes lava-bubble {
  0% {
    transform: translateY(0) scale(1);
    opacity: 0.6;
  }

  50% {
    transform: translateY(-20px) scale(1.3);
    opacity: 0.9;
  }

  100% {
    transform: translateY(-40px) scale(0.8);
    opacity: 0;
  }
}

@keyframes lava-heat-shimmer {

  0%,
  100% {
    filter: blur(0px) brightness(1);
    transform: scaleX(1);
  }

  25% {
    filter: blur(0.5px) brightness(1.1);
    transform: scaleX(1.002);
  }

  50% {
    filter: blur(0px) brightness(1.05);
    transform: scaleX(0.998);
  }

  75% {
    filter: blur(0.5px) brightness(1.08);
    transform: scaleX(1.001);
  }
}

@keyframes lava-flow {
  0% {
    background-position: 0% 0%;
  }

  100% {
    background-position: 200% 200%;
  }
}

@keyframes ember-drift {
  0% {
    transform: translate(0, 0) scale(1);
    opacity: 0;
  }

  15% {
    opacity: 0.9;
  }

  85% {
    opacity: 0.6;
  }

  100% {
    transform: translate(var(--ember-tx, 30px), var(--ember-ty, -80px)) scale(0.3);
    opacity: 0;
  }
}

@keyframes magma-meter-fill {
  0% {
    background-position: 0% 100%;
  }

  100% {
    background-position: 0% 0%;
  }
}

@keyframes magma-glow {

  0%,
  100% {
    box-shadow: 0 0 8px rgba(255, 69, 0, 0.3), inset 0 0 8px rgba(255, 69, 0, 0.1);
  }

  50% {
    box-shadow: 0 0 20px rgba(255, 69, 0, 0.6), inset 0 0 15px rgba(255, 69, 0, 0.2);
  }
}

@keyframes eruption-shake {

  0%,
  100% {
    transform: translateX(0);
  }

  10% {
    transform: translateX(-3px) rotate(-0.5deg);
  }

  30% {
    transform: translateX(3px) rotate(0.5deg);
  }

  50% {
    transform: translateX(-2px);
  }

  70% {
    transform: translateX(2px) rotate(-0.3deg);
  }

  90% {
    transform: translateX(-1px);
  }
}

.lava-border {
  border-image: linear-gradient(180deg, #FF4500, #FF6347, #FF8C00, #FF4500) 1;
}

.magma-meter-active {
  animation: magma-glow 1.5s ease-in-out infinite;
}

.eruption-active {
  animation: eruption-shake 0.5s ease-in-out;
}

@keyframes emoji-fountain {
  0% {
    transform: translate(-50%, 50px) scale(0.5) rotate(0deg);
    opacity: 1;
  }
  20% {
    opacity: 1;
  }
  100% {
    transform: translate(calc(-50% + var(--tx)), var(--ty)) scale(1.5) rotate(var(--rot));
    opacity: 0;
  }
}
.animate-emoji-fountain {
  animation: emoji-fountain ease-out forwards;
}

@keyframes shimmer {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(100%); }
}

/* ============================================================
   NEW UI ENHANCEMENTS (Glassmorphism, Gradients, Pulses)
   ============================================================ */

.glass-panel {
  background: rgba(15, 15, 20, 0.4);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid rgba(255, 255, 255, 0.08);
  box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
  border-radius: 12px;
}

.text-gradient-gold {
  background: linear-gradient(to right, #fbbf24, #f59e0b, #fbbf24);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  color: transparent;
  background-size: 200% auto;
  animation: tier-shimmer 3s linear infinite;
}

.text-gradient-neon {
  background: linear-gradient(to right, #00f2fe, #4facfe, #00f2fe);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  color: transparent;
  background-size: 200% auto;
  animation: tier-shimmer 3s linear infinite;
}

/* Scale stays on the element (compositor-friendly); the gold glow moved to a
   pseudo-element whose opacity pulses over a static shadow. The old version
   animated box-shadow on the idle SPIN button 100% of the time, repainting the
   busiest control on screen even while nothing else moved. */
@keyframes spin-btn-pulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.025); }
}
@keyframes spin-btn-glow {
  0%, 100% { opacity: 0.45; }
  50%      { opacity: 1; }
}

.btn-spin-pulse {
  position: relative;
  animation: spin-btn-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
  transition: all 0.2s ease;
}
.btn-spin-pulse::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  box-shadow: 0 0 12px rgba(251, 191, 36, 0.35);
  animation: spin-btn-glow 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
  will-change: opacity;
}

.btn-spin-pulse:active {
  transform: scale(0.95);
  animation: none;
}
.btn-spin-pulse:active::after { animation: none; opacity: 0.45; }
.reduced-motion .btn-spin-pulse::after { animation: none !important; opacity: 0.45; }

/* (The old .hover-3d-tilt JS pointer-tracking tilt is gone: three transform
   sources — parallax wrapper, CSS hover, per-mousemove inline styles — could
   never settle. Lobby cards now hover via .lobby-card:hover alone.) */

@keyframes stroke-dash {
  to {
    stroke-dashoffset: 0;
  }
}

@keyframes sweep-reflection {
  0% { transform: translateX(-150%) skewX(-45deg); }
  100% { transform: translateX(200%) skewX(-45deg); }
}

.jackpot-shimmer {
  position: relative;
  overflow: hidden;
}

.jackpot-shimmer::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 50%;
  height: 100%;
  background: linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,0.4) 50%, rgba(255,255,255,0) 100%);
  animation: sweep-reflection 4s cubic-bezier(0.4, 0, 0.2, 1) infinite;
  z-index: 20;
  pointer-events: none;
}

@keyframes bounce-stop {
  0% { transform: translateY(-8%); }
  50% { transform: translateY(4%); }
  100% { transform: translateY(0); }
}

@keyframes rain-down {
  0% { transform: translateY(-10vh) rotate(0deg) scale(0.5); opacity: 0; }
  10% { transform: translateY(2vh) rotate(calc(var(--rot) * 0.1)) scale(1); opacity: 1; }
  90% { opacity: 1; }
  100% { transform: translateY(110vh) rotate(var(--rot)) scale(1.5); opacity: 0; }
}

/* ============================================================
   COIN BURST — floats up from balance display on win
   ============================================================ */
@keyframes coin-float {
  0%   { opacity: 0; transform: translateY(0) scale(0.4) rotate(-15deg); }
  18%  { opacity: 1; transform: translateY(-12px) scale(1.15) rotate(8deg); }
  100% { opacity: 0; transform: translateY(-90px) scale(0.7) rotate(25deg); }
}
.animate-coin-float {
  animation: coin-float 1.5s ease-out forwards;
}

/* ============================================================
   REDUCED MOTION — toggled via .reduced-motion on <html>
   Silences ambient loops; keeps win/reel feedback intact.
   ============================================================ */
.reduced-motion .lobby-card { animation: none !important; }
.reduced-motion .btn-spin-pulse { animation: none !important; }
.reduced-motion .jackpot-card::before,
.reduced-motion .jackpot-card::after { animation: none !important; }
.reduced-motion .jackpot-shimmer::after { animation: none !important; }
.reduced-motion .animate-vip-glow { animation: none !important; }
.reduced-motion .play-btn-wrap::before { animation: none !important; }
.reduced-motion .text-gradient-gold,
.reduced-motion .text-gradient-neon { animation: none !important; }
.reduced-motion .animate-tilt-pulse { animation: none !important; }
.reduced-motion .feature-swirl-border { animation: none !important; }
.reduced-motion .animate-coin-float { animation: none !important; }
.reduced-motion * { transition-duration: 0.05s !important; }

/* ============================================================
   FEATURE: Win Breakdown Strip
   ============================================================ */
@keyframes win-chip-in {
  0%   { opacity: 0; transform: translateY(-10px) scale(0.8); }
  70%  { transform: translateY(2px) scale(1.05); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}
.win-chip-enter {
  animation: win-chip-in 0.32s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;
}
.reduced-motion .win-chip-enter { animation: none; opacity: 1; }

/* One-shot themed glow pulse around the reel board when a spin wins. Host sets
   --win-glow-rgb to the slot theme's primary RGB. */
@keyframes reel-win-flash {
  0%   { box-shadow: 0 0 0 0 rgba(var(--win-glow-rgb, 251,191,36), 0); }
  22%  { box-shadow: 0 0 28px 5px rgba(var(--win-glow-rgb, 251,191,36), 0.55); }
  100% { box-shadow: 0 0 0 0 rgba(var(--win-glow-rgb, 251,191,36), 0); }
}
.reel-area-win { animation: reel-win-flash 1.1s ease-out 1; border-radius: 1rem; }
.reduced-motion .reel-area-win { animation: none; }

/* ============================================================
   FEATURE: Reel Cabinet themed breathing glow
   ============================================================ */
/* The breathing glow lives on a pseudo-element with a STATIC box-shadow whose
   opacity pulses. Animating box-shadow directly forced the whole cabinet (and
   the reels inside it) to repaint every frame for the entire session — a
   constant background tax that competed with the spin animation itself. */
@keyframes glow-breathe {
  0%, 100% { opacity: 0.35; }
  50%      { opacity: 1; }
}
.cabinet-breathe {
  position: relative;
  box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.4);
}
.cabinet-breathe::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  pointer-events: none;
  box-shadow: 0 0 18px var(--cabinet-color, rgba(245, 158, 11, 0.4));
  animation: glow-breathe 3s ease-in-out infinite;
  will-change: opacity;
}
.reduced-motion .cabinet-breathe::after { animation: none; opacity: 0.5; }

/* ============================================================
   FEATURE: Anticipation — scatter reel glow during tease
   ============================================================ */
@keyframes anticipation-scatter-pulse {
  0%, 100% { box-shadow: 0 0 10px rgba(250,204,21,0.4), inset 0 0 6px rgba(250,204,21,0.08); border-color: rgba(250,204,21,0.55); }
  50%       { box-shadow: 0 0 26px rgba(250,204,21,0.85), inset 0 0 14px rgba(250,204,21,0.18); border-color: rgba(250,204,21,1); }
}
.reel-scatter-anticipation {
  border: 2px solid rgba(250,204,21,0.6);
  border-radius: 0.5rem;
  animation: anticipation-scatter-pulse 0.65s ease-in-out infinite;
  z-index: 25;
}
.reduced-motion .reel-scatter-anticipation {
  animation: none;
  box-shadow: 0 0 18px rgba(250,204,21,0.7);
  border: 2px solid rgb(250,204,21);
}

/* ============================================================
   FEATURE: Session Stats Rail
   ============================================================ */
@keyframes stats-rail-slide-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
.stats-rail-enter {
  animation: stats-rail-slide-in 0.22s ease-out both;
}
.reduced-motion .stats-rail-enter { animation: none; }


/* ============================================================
   BONUS WHEEL — delight upgrades
   Chasing rim bulbs, peg-synced pointer kick, idle bob,
   spin glow ramp, ready-hub pulse, winning-slice celebration.
   ============================================================ */
/* Opacity/transform only — element opacity fades the (static) box-shadow with
   it, so the dim phase still reads as a faint glow, but the 16 bulbs animate
   entirely on the compositor instead of repainting shadows every frame. */
@keyframes bulb-chase {
  0%, 100% {
    opacity: 0.28;
    transform: scale(0.78);
  }
  50% {
    opacity: 1;
    transform: scale(1.25);
  }
}
.wheel-bulb {
  position: absolute;
  top: 0;
  left: 50%;
  margin-left: -5px;
  width: 10px;
  height: 10px;
  border-radius: 9999px;
  background: radial-gradient(circle at 35% 30%, #fffbe6, #fbbf24 55%, #b45309);
  box-shadow: 0 0 8px 2px rgba(253, 224, 71, 0.95), 0 0 16px rgba(251, 191, 36, 0.7);
  animation: bulb-chase 1s ease-in-out infinite;
  will-change: opacity, transform;
}

@keyframes wheel-bob {
  0%, 100% { transform: translateY(0) rotate(-0.6deg); }
  50%      { transform: translateY(-7px) rotate(0.6deg); }
}
.wheel-idle-bob { animation: wheel-bob 3.2s ease-in-out infinite; }

@keyframes wheel-rim-glow {
  0%, 100% {
    box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.8), 0 0 22px 4px rgba(251, 191, 36, 0.35);
  }
  50% {
    box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.8),
      0 0 50px 12px rgba(251, 191, 36, 0.8), 0 0 80px 18px rgba(239, 68, 68, 0.3);
  }
}
.wheel-spinning-glow { animation: wheel-rim-glow 0.9s ease-in-out infinite; }

@keyframes pointer-kick {
  0%   { transform: translateX(-50%) rotate(0deg); }
  30%  { transform: translateX(-50%) rotate(-18deg); }
  100% { transform: translateX(-50%) rotate(0deg); }
}
.pointer-kick {
  animation: pointer-kick 0.16s cubic-bezier(0.3, 0.7, 0.4, 1);
  transform-origin: 50% 22%;
}

@keyframes hub-ready-pulse {
  0%, 100% { transform: translate(-50%, -50%) scale(1);    filter: brightness(1); }
  50%      { transform: translate(-50%, -50%) scale(1.07); filter: brightness(1.4); }
}
.wheel-hub-ready { animation: hub-ready-pulse 1.1s ease-in-out infinite; }

@keyframes wheel-winner-glow {
  0%, 100% { filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.9)) drop-shadow(0 0 15px rgba(251, 191, 36, 0.85)); }
  50%      { filter: drop-shadow(0 0 13px rgba(255, 255, 255, 1)) drop-shadow(0 0 34px rgba(251, 191, 36, 1)); }
}
.wheel-winner-slice { animation: wheel-winner-glow 0.7s ease-in-out infinite; }

@keyframes wheel-win-ring {
  0%   { transform: scale(0.82); opacity: 0; }
  40%  { opacity: 1; }
  100% { transform: scale(1.18); opacity: 0; }
}
.wheel-win-ring {
  position: absolute;
  inset: -6px;
  border-radius: 9999px;
  border: 4px solid rgba(253, 224, 71, 0.9);
  box-shadow: 0 0 24px rgba(251, 191, 36, 0.8);
  pointer-events: none;
  animation: wheel-win-ring 1s ease-out infinite;
}

/* ============================================================
   CASH RESPINS (VAULT CRACK) — delight upgrades
   Floating cash, vault-fill meter, near-grand alarm,
   respin reset flash, low-respin urgency, safe shine.
   ============================================================ */
@keyframes value-float {
  0%   { opacity: 0; transform: translate(-50%, 6px) scale(0.6); }
  20%  { opacity: 1; transform: translate(-50%, -6px) scale(1.1); }
  100% { opacity: 0; transform: translate(-50%, -42px) scale(1); }
}
.heist-value-float {
  position: absolute;
  left: 50%;
  top: 4px;
  transform: translateX(-50%);
  font-family: "Share Tech Mono", monospace;
  font-weight: 700;
  font-size: 0.85rem;
  color: #4ade80;
  text-shadow: 0 0 8px rgba(74, 222, 128, 0.9), 0 1px 2px rgba(0, 0, 0, 0.9);
  pointer-events: none;
  z-index: 40;
  animation: value-float 1.1s ease-out forwards;
}

@keyframes safe-shine {
  0%   { transform: translateX(-160%) skewX(-20deg); }
  100% { transform: translateX(260%) skewX(-20deg); }
}
.heist-safe-shine {
  position: absolute;
  top: 0;
  left: 0;
  width: 45%;
  height: 100%;
  background: linear-gradient(to right, transparent, rgba(255, 255, 255, 0.5), transparent);
  animation: safe-shine 2.6s ease-in-out infinite;
  pointer-events: none;
}

@keyframes grand-alarm {
  0%, 100% {
    border-color: rgba(220, 38, 38, 0.55);
    box-shadow: 0 0 30px rgba(220, 38, 38, 0.35), inset 0 0 14px rgba(220, 38, 38, 0.12);
  }
  50% {
    border-color: rgba(248, 113, 113, 1);
    box-shadow: 0 0 60px rgba(220, 38, 38, 0.75), inset 0 0 22px rgba(220, 38, 38, 0.25);
  }
}
.heist-grand-alarm { animation: grand-alarm 0.7s ease-in-out infinite; }

@keyframes respin-reset-flash {
  0%   { opacity: 0; transform: translateY(6px) scale(0.7); }
  30%  { opacity: 1; transform: translateY(0) scale(1.1); }
  80%  { opacity: 1; transform: translateY(0) scale(1); }
  100% { opacity: 0; transform: translateY(-8px) scale(0.95); }
}
.respin-reset-flash { animation: respin-reset-flash 1.1s ease-out forwards; }

@keyframes respin-low-pulse {
  0%, 100% { color: #ef4444; text-shadow: 0 0 6px rgba(239, 68, 68, 0.6); transform: scale(1); }
  50%      { color: #fca5a5; text-shadow: 0 0 16px rgba(239, 68, 68, 1); transform: scale(1.12); }
}
.respin-low-pulse { animation: respin-low-pulse 0.7s ease-in-out infinite; }

/* ============================================================
   CASH RESPINS (Hold & Win) — refreshed vault board
   ============================================================ */
.heist-board {
  background:
    radial-gradient(130% 80% at 50% -10%, rgba(251,191,36,0.08), transparent 60%),
    repeating-linear-gradient(0deg, rgba(0,0,0,0.22) 0 2px, transparent 2px 5px),
    linear-gradient(160deg, #1c1917, #0a0908);
  border: 1px solid rgba(146,104,12,0.45);
  box-shadow: inset 0 0 34px rgba(0,0,0,0.75), 0 0 0 1px rgba(251,191,36,0.05);
}
.heist-reel-col {
  background: linear-gradient(180deg, rgba(12,11,10,0.92), rgba(5,5,4,0.96));
  box-shadow: inset 0 2px 10px rgba(0,0,0,0.8), inset 0 0 0 1px rgba(255,255,255,0.025);
}
.heist-cell-empty {
  background:
    radial-gradient(circle at 50% 45%, rgba(251,191,36,0.05), transparent 60%),
    linear-gradient(180deg, rgba(20,18,16,0.6), rgba(8,8,7,0.6));
  box-shadow: inset 0 0 0 1px rgba(68,63,57,0.35);
}

/* Respin pip-lights — the round's "lives", refilled on every new lock */
.heist-pip {
  width: 13px; height: 13px; border-radius: 9999px; flex-shrink: 0;
  background: #1c1917; border: 1px solid #44403c;
  box-shadow: inset 0 0 4px rgba(0,0,0,0.85);
  transition: background 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
}
.heist-pip-lit {
  background: radial-gradient(circle at 34% 30%, #fef3c7, #f59e0b 72%);
  border-color: #fcd34d;
  box-shadow: 0 0 10px rgba(251,191,36,0.85), inset 0 0 3px rgba(255,255,255,0.7);
}
.heist-pip-spent { background: #14110f; border-color: #292524; opacity: 0.55; }
.heist-pip-danger {
  background: radial-gradient(circle at 34% 30%, #fee2e2, #ef4444 72%);
  border-color: #fca5a5;
  animation: heist-pip-danger 0.7s ease-in-out infinite;
}
@keyframes heist-pip-danger {
  0%,100% { box-shadow: 0 0 8px rgba(239,68,68,0.7); transform: scale(1); }
  50%     { box-shadow: 0 0 18px rgba(239,68,68,1); transform: scale(1.2); }
}
.heist-pip-reset { animation: heist-pip-reset 0.55s cubic-bezier(0.175,0.885,0.32,1.275); }
@keyframes heist-pip-reset { 0%{transform:scale(0.3);} 60%{transform:scale(1.35);} 100%{transform:scale(1);} }

/* Segmented vault-tumbler meter toward the GRAND */
.heist-seg {
  height: 12px; border-radius: 3px;
  background: #14110f; border: 1px solid rgba(68,63,57,0.55);
  box-shadow: inset 0 1px 3px rgba(0,0,0,0.7);
  transition: all 0.35s ease;
}
.heist-seg-on {
  background: linear-gradient(180deg, #fde047, #a16207);
  border-color: #fcd34d;
  box-shadow: 0 0 9px rgba(251,191,36,0.65), inset 0 1px 2px rgba(255,255,255,0.4);
}
.heist-seg-hot {
  background: linear-gradient(180deg, #fde047, #b91c1c);
  border-color: #fca5a5;
  box-shadow: 0 0 12px rgba(239,68,68,0.75);
}

/* Header caution stripes + vault dial + corner sirens */
.heist-header-stripes {
  position: absolute; inset: 0; opacity: 0.07; pointer-events: none;
  background: repeating-linear-gradient(45deg, #fbbf24 0 9px, transparent 9px 20px);
}
.heist-vault-dial {
  font-size: 1.85rem; line-height: 1;
  filter: drop-shadow(0 0 9px rgba(251,191,36,0.55));
  animation: heist-dial-bob 3s ease-in-out infinite;
}
@keyframes heist-dial-bob {
  0%,100% { transform: translateY(0) rotate(-5deg); }
  50%     { transform: translateY(-2px) rotate(5deg); }
}
.heist-siren {
  position: absolute; width: 9px; height: 9px; border-radius: 9999px;
  background: #ef4444; box-shadow: 0 0 16px 4px rgba(239,68,68,0.85);
  animation: heist-siren-blink 0.6s ease-in-out infinite; z-index: 30; pointer-events: none;
}
.heist-siren-tl { top: 11px; left: 11px; }
.heist-siren-tr { top: 11px; right: 11px; animation-delay: 0.3s; }
.heist-siren-bl { bottom: 11px; left: 11px; animation-delay: 0.3s; }
.heist-siren-br { bottom: 11px; right: 11px; }
@keyframes heist-siren-blink {
  0%,100% { opacity: 0.25; transform: scale(0.75); }
  50%     { opacity: 1; transform: scale(1.25); }
}

.reduced-motion .heist-vault-dial,
.reduced-motion .heist-pip-danger,
.reduced-motion .heist-pip-reset,
.reduced-motion .heist-siren { animation: none !important; }

.reduced-motion .wheel-bulb,
.reduced-motion .wheel-idle-bob,
.reduced-motion .wheel-spinning-glow,
.reduced-motion .wheel-hub-ready,
.reduced-motion .wheel-winner-slice,
.reduced-motion .wheel-win-ring,
.reduced-motion .pointer-kick,
.reduced-motion .heist-safe-shine,
.reduced-motion .heist-grand-alarm,
.reduced-motion .respin-low-pulse { animation: none !important; }

/* ============================================================
   PHASE 1 — Big-Win ceremony, Heat meter, Toasts, Reel juice
   ============================================================ */

/* --- #4 Cinematic Big-Win roll-up ceremony --- */
@keyframes ceremony-shake {
  0%, 100% { transform: translate(0, 0) rotate(0deg); }
  15% { transform: translate(-6px, 3px) rotate(-1deg); }
  30% { transform: translate(7px, -4px) rotate(1.2deg); }
  45% { transform: translate(-5px, -3px) rotate(-0.8deg); }
  60% { transform: translate(5px, 4px) rotate(0.8deg); }
  80% { transform: translate(-3px, 2px) rotate(-0.4deg); }
}
.ceremony-shake { animation: ceremony-shake 0.5s cubic-bezier(0.36, 0.07, 0.19, 0.97); }

@keyframes ceremony-flash {
  0% { opacity: 0; }
  20% { opacity: 0.85; }
  100% { opacity: 0; }
}
.ceremony-flash {
  position: fixed;
  inset: 0;
  background: radial-gradient(circle, #ffffff 0%, rgba(255, 255, 255, 0.3) 45%, transparent 75%);
  pointer-events: none;
  z-index: 120;
  animation: ceremony-flash 0.4s ease-out forwards;
}

@keyframes tier-upgrade-pop {
  0% { transform: scale(0.4) rotate(-8deg); opacity: 0; filter: brightness(2.2); }
  55% { transform: scale(1.22) rotate(4deg); opacity: 1; filter: brightness(1.6); }
  100% { transform: scale(1) rotate(0deg); opacity: 1; filter: brightness(1); }
}
.tier-upgrade-pop { animation: tier-upgrade-pop 0.55s cubic-bezier(0.175, 0.885, 0.32, 1.275); }

/* --- #2 Win-Streak "Heat" meter --- */
@keyframes heat-flame-bob {
  0%, 100% { transform: translateY(0) scale(1) rotate(-3deg); }
  50% { transform: translateY(-2px) scale(1.12) rotate(3deg); }
}
.heat-flame { display: inline-block; animation: heat-flame-bob 0.5s ease-in-out infinite; }

@keyframes heat-bar-shimmer {
  0% { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}
.heat-bar-shimmering {
  background-size: 200% 100% !important;
  animation: heat-bar-shimmer 1.2s linear infinite;
}

@keyframes heat-glow-pulse {
  0%, 100% { box-shadow: 0 0 6px var(--heat-color, #f97316); }
  50% { box-shadow: 0 0 18px var(--heat-color, #f97316), 0 0 30px var(--heat-color, #f97316); }
}
.heat-glow { animation: heat-glow-pulse 0.7s ease-in-out infinite; }

@keyframes heat-callout {
  0% { transform: translate(-50%, 8px) scale(0.5); opacity: 0; }
  25% { transform: translate(-50%, 0) scale(1.15); opacity: 1; }
  75% { transform: translate(-50%, 0) scale(1); opacity: 1; }
  100% { transform: translate(-50%, -22px) scale(0.95); opacity: 0; }
}
.heat-callout { animation: heat-callout 1.3s ease-out forwards; }

/* --- #6 Toast / milestone callouts --- */
@keyframes toast-in {
  0% { opacity: 0; transform: translateX(40px) scale(0.9); }
  100% { opacity: 1; transform: translateX(0) scale(1); }
}
@keyframes toast-out {
  0% { opacity: 1; transform: translateX(0) scale(1); max-height: 80px; margin-bottom: 0.5rem; }
  100% { opacity: 0; transform: translateX(40px) scale(0.9); max-height: 0; margin-bottom: 0; }
}
.toast-enter { animation: toast-in 0.32s cubic-bezier(0.175, 0.885, 0.32, 1.275) both; }
.toast-exit { animation: toast-out 0.35s ease-in forwards; overflow: hidden; }

/* --- #9 Reel & symbol juice --- */
@keyframes symbol-land {
  0% { transform: translateY(-12%) scaleY(1.18) scaleX(0.9); }
  45% { transform: translateY(3%) scaleY(0.86) scaleX(1.12); }
  70% { transform: translateY(-1%) scaleY(1.05) scaleX(0.97); }
  100% { transform: translateY(0) scale(1); }
}
.symbol-land { animation: symbol-land 0.34s cubic-bezier(0.2, 0.9, 0.3, 1.3); }

@keyframes impact-dust {
  0% { opacity: 0.5; transform: translateX(var(--dust-x, 0)) scale(0.4); }
  100% { opacity: 0; transform: translateX(var(--dust-x, 0)) translateY(6px) scale(1.4); }
}
.impact-dust {
  position: absolute;
  bottom: 4px;
  left: 50%;
  width: 5px;
  height: 5px;
  border-radius: 9999px;
  background: rgba(255, 255, 255, 0.55);
  opacity: 0;
  pointer-events: none;
  animation: impact-dust 0.4s ease-out forwards;
}

/* --- #5 Attract mode idle hint --- */
@keyframes attract-bob {
  0%, 100% { transform: translate(-50%, 0); }
  50%      { transform: translate(-50%, -7px); }
}
.attract-hint { animation: attract-bob 1.4s ease-in-out infinite; }

/* --- #3 Coin shower (coins fly from reels up to the balance) --- */
@keyframes coin-shower-fly {
  0%   { opacity: 0; transform: translate(0, 0) scale(0.5) rotate(0deg); }
  15%  { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--cx, 0), -260px) scale(1.1) rotate(220deg); }
}
.coin-shower-coin {
  opacity: 0;
  animation-name: coin-shower-fly;
  animation-timing-function: ease-out;
  animation-fill-mode: forwards;
  will-change: transform, opacity;
}

.reduced-motion .ceremony-shake,
.reduced-motion .ceremony-flash,
.reduced-motion .heat-flame,
.reduced-motion .heat-glow,
.reduced-motion .heat-bar-shimmering,
.reduced-motion .heat-callout,
.reduced-motion .symbol-land,
.reduced-motion .impact-dust,
.reduced-motion .coin-shower-coin { animation: none !important; }

/* ============================================================
   LEGENDARY COSMETIC EFFECTS (#7 premium vanity)
   Self-contained classes toggled when a cosmetic has tier:'legendary'.
   Each one carries its own unique animated treatment.
   ============================================================ */

/* Animated flowing prismatic name text (clip-text rainbow sweep). */
@keyframes cos-name-flow {
  0%   { background-position: 0% 50%; }
  100% { background-position: 200% 50%; }
}
.cos-legendary-name {
  background-image: linear-gradient(90deg, #f87171, #fbbf24, #34d399, #22d3ee, #a78bfa, #f472b6, #f87171);
  background-size: 200% auto;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  animation: cos-name-flow 4s linear infinite;
  filter: drop-shadow(0 0 6px rgba(168,139,250,0.55));
}

/* Rotating conic halo behind a legendary avatar frame. Host must be position:relative. */
@keyframes cos-ring-spin { to { transform: rotate(360deg); } }
.cos-legendary-ring { position: relative; z-index: 0; }
.cos-legendary-ring::before {
  content: "";
  position: absolute;
  inset: -4px;
  border-radius: 9999px;
  background: conic-gradient(from 0deg, #22d3ee, #a78bfa, #f472b6, #fbbf24, #34d399, #22d3ee);
  animation: cos-ring-spin 3.5s linear infinite;
  z-index: -1;
  filter: blur(1px) saturate(1.3);
}
.cos-legendary-ring::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: 9999px;
  background: #0c0a09;
  z-index: -1;
}

/* Rectangular rainbow border for the in-slot VIP badge when a LEGENDARY avatar
   frame is equipped — a rounded-pill rainbow rim with a dark inner so only the
   border reads as rainbow. The host sets its own border + background transparent
   so these pseudos show. NOTE: unlike the round cos-legendary-ring we must NOT
   rotate the element (a spinning rectangle sweeps out into a huge diagonal band);
   the rim stays put and the colors cycle via hue-rotate instead. */
.cos-frame-legendary { position: relative; z-index: 0; }
.cos-frame-legendary::before {
  content: "";
  position: absolute;
  inset: -2px;
  border-radius: 1rem; /* matches rounded-2xl */
  background: linear-gradient(120deg, #22d3ee, #a78bfa, #f472b6, #fbbf24, #34d399, #22d3ee);
  animation: cos-frame-hue 4s linear infinite;
  z-index: -1;
  filter: saturate(1.2);
}
.cos-frame-legendary::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: calc(1rem - 2px);
  background: rgba(12, 10, 9, 0.82);
  z-index: -1;
}
@keyframes cos-frame-hue {
  from { filter: saturate(1.2) hue-rotate(0deg); }
  to   { filter: saturate(1.2) hue-rotate(360deg); }
}
.reduced-motion .cos-frame-legendary::before { animation: none !important; }

/* Holographic shimmer sweep across a legendary spin button (overlay div, inset-0). */
@keyframes cos-holo-sweep {
  0%   { background-position: -150% 0; }
  100% { background-position: 250% 0; }
}
.cos-legendary-btn-fx {
  background: linear-gradient(110deg, transparent 25%, rgba(255,255,255,0.55) 45%, rgba(186,230,253,0.6) 50%, rgba(255,255,255,0.55) 55%, transparent 75%);
  background-size: 220% 100%;
  mix-blend-mode: overlay;
  animation: cos-holo-sweep 2.6s linear infinite;
  pointer-events: none;
}

/* Drifting nebula for a legendary background overlay (replaces the static vignette). */
@keyframes cos-bg-drift {
  0%   { transform: translate3d(0,0,0) scale(1); }
  50%  { transform: translate3d(2%, -2%, 0) scale(1.08); }
  100% { transform: translate3d(0,0,0) scale(1); }
}
.cos-legendary-bg {
  background:
    radial-gradient(45% 55% at 22% 28%, rgba(168,85,247,0.30), transparent 70%),
    radial-gradient(45% 55% at 78% 30%, rgba(34,211,238,0.26), transparent 70%),
    radial-gradient(60% 60% at 50% 92%, rgba(244,114,182,0.24), transparent 72%),
    radial-gradient(40% 40% at 70% 70%, rgba(52,211,153,0.20), transparent 70%),
    /* Dark base so the nebula reads as a full background (not a faint overlay
       washed out by the animated chromatic canvas behind it). */
    #07060c;
  background-color: #07060c;
  animation: cos-bg-drift 14s ease-in-out infinite;
  will-change: transform;
}

/* --- Per-item legendary background effects (premium + distinct) --------- */
/* Galactic Drift — a deep-space nebula that drifts, with twinkling stars. */
.cos-bg-galactic {
  overflow: hidden;
  background:
    radial-gradient(45% 55% at 22% 28%, rgba(168,85,247,0.34), transparent 70%),
    radial-gradient(45% 55% at 78% 30%, rgba(34,211,238,0.30), transparent 70%),
    radial-gradient(60% 60% at 50% 92%, rgba(244,114,182,0.26), transparent 72%),
    radial-gradient(40% 40% at 70% 70%, rgba(52,211,153,0.20), transparent 70%),
    #05040b;
  background-color: #05040b;
  animation: cos-bg-drift 16s ease-in-out infinite;
  will-change: transform;
}
.cos-bg-galactic::after {
  content: '';
  position: absolute;
  inset: -10%;
  pointer-events: none;
  background-image:
    radial-gradient(1.5px 1.5px at 15% 25%, #fff, transparent 60%),
    radial-gradient(1px 1px at 45% 60%, #cffafe, transparent 60%),
    radial-gradient(2px 2px at 78% 18%, #fff, transparent 60%),
    radial-gradient(1px 1px at 32% 82%, #e9d5ff, transparent 60%),
    radial-gradient(1.5px 1.5px at 88% 66%, #fff, transparent 60%),
    radial-gradient(1px 1px at 62% 40%, #fff, transparent 60%),
    radial-gradient(1px 1px at 8% 70%, #a5f3fc, transparent 60%);
  animation: cos-bg-twinkle 4.5s ease-in-out infinite;
}
@keyframes cos-bg-twinkle { 0%, 100% { opacity: 0.3; } 50% { opacity: 0.85; } }

/* Heist Master — a dark vault drenched in gold with a slow sweeping spotlight. */
.cos-bg-heistmaster {
  overflow: hidden;
  background:
    radial-gradient(55% 45% at 50% 6%, rgba(251,191,36,0.30), transparent 70%),
    radial-gradient(75% 55% at 50% 100%, rgba(180,120,20,0.34), transparent 72%),
    radial-gradient(38% 50% at 12% 50%, rgba(245,158,11,0.16), transparent 70%),
    radial-gradient(38% 50% at 88% 50%, rgba(245,158,11,0.16), transparent 70%),
    #0a0805;
  background-color: #0a0805;
}
.cos-bg-heistmaster::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(115deg, transparent 38%, rgba(251,191,36,0.14) 47%, rgba(255,248,220,0.22) 50%, rgba(251,191,36,0.14) 53%, transparent 62%);
  background-size: 280% 100%;
  animation: cos-bg-gold-sweep 7s linear infinite;
}
@keyframes cos-bg-gold-sweep { 0% { background-position: 170% 0; } 100% { background-position: -170% 0; } }

/* Color-cycling glow for a legendary reel cabinet + its corner gems. */
@keyframes cos-reel-rainbow {
  0%   { box-shadow: 0 0 26px rgba(34,211,238,0.45), inset 0 0 14px rgba(34,211,238,0.18); border-color: #22d3ee; }
  25%  { box-shadow: 0 0 26px rgba(168,139,250,0.45), inset 0 0 14px rgba(168,139,250,0.18); border-color: #a78bfa; }
  50%  { box-shadow: 0 0 26px rgba(244,114,182,0.45), inset 0 0 14px rgba(244,114,182,0.18); border-color: #f472b6; }
  75%  { box-shadow: 0 0 26px rgba(52,211,153,0.45), inset 0 0 14px rgba(52,211,153,0.18); border-color: #34d399; }
  100% { box-shadow: 0 0 26px rgba(34,211,238,0.45), inset 0 0 14px rgba(34,211,238,0.18); border-color: #22d3ee; }
}
.cos-legendary-reel { animation: cos-reel-rainbow 6s linear infinite; }
@keyframes cos-gem-rainbow {
  0%   { background: #22d3ee; box-shadow: 0 0 10px #22d3ee; }
  25%  { background: #a78bfa; box-shadow: 0 0 10px #a78bfa; }
  50%  { background: #f472b6; box-shadow: 0 0 10px #f472b6; }
  75%  { background: #34d399; box-shadow: 0 0 10px #34d399; }
  100% { background: #22d3ee; box-shadow: 0 0 10px #22d3ee; }
}
.cos-legendary-gem { animation: cos-gem-rainbow 6s linear infinite; }

/* Shimmering LEGENDARY rarity badge used in the shop. */
@keyframes cos-badge-shine {
  0%   { background-position: 0% 50%; }
  100% { background-position: 200% 50%; }
}
.cos-legendary-badge {
  background-image: linear-gradient(90deg, #fbbf24, #f472b6, #a78bfa, #22d3ee, #fbbf24);
  background-size: 200% auto;
  animation: cos-badge-shine 3s linear infinite;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
}
.cos-legendary-card {
  border-color: rgba(251,191,36,0.6) !important;
  box-shadow: 0 0 22px rgba(251,191,36,0.18), inset 0 0 14px rgba(168,85,247,0.08);
}

/* EPIC rarity (one tier below legendary) — the second-priciest item per
   category. A purple shimmering badge + soft violet card glow, parallel to the
   legendary treatment so the rarity ladder reads consistently in the shop. */
@keyframes cos-epic-shine {
  0%   { background-position: 0% 50%; }
  100% { background-position: 200% 50%; }
}
.cos-epic-badge {
  background-image: linear-gradient(90deg, #c084fc, #a855f7, #818cf8, #a855f7, #c084fc);
  background-size: 200% auto;
  animation: cos-epic-shine 3s linear infinite;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
}
.cos-epic-card {
  border-color: rgba(168,85,247,0.6) !important;
  box-shadow: 0 0 18px rgba(168,85,247,0.16), inset 0 0 12px rgba(129,140,248,0.07);
}

.reduced-motion .cos-legendary-name,
.reduced-motion .cos-legendary-ring::before,
.reduced-motion .cos-legendary-btn-fx,
.reduced-motion .cos-legendary-bg,
.reduced-motion .cos-bg-galactic,
.reduced-motion .cos-bg-galactic::after,
.reduced-motion .cos-bg-heistmaster::after,
.reduced-motion .cos-legendary-reel,
.reduced-motion .cos-legendary-gem,
.reduced-motion .cos-legendary-badge,
.reduced-motion .cos-epic-badge { animation: none !important; }

/* ============================================================
   DELIGHT PASS — shop & preview micro-interactions
   Tactile ripples, card tilt/sheen, preview entrance, buy burst.
   All effects self-disable under .reduced-motion (bottom of block).
   ============================================================ */

/* --- Tactile click ripple (host button needs position:relative; overflow:hidden) --- */
@keyframes ripple-expand { to { transform: scale(2.6); opacity: 0; } }
.ripple-ink {
  position: absolute;
  border-radius: 9999px;
  background: rgba(255, 255, 255, 0.45);
  transform: scale(0);
  pointer-events: none;
  mix-blend-mode: screen;
  animation: ripple-expand 0.6s ease-out forwards;
}

/* --- 3D pointer tilt + moving gloss sheen for cards --- */
.tilt-card {
  transform: perspective(700px) rotateX(var(--ty, 0deg)) rotateY(var(--tx, 0deg));
  transition: transform 0.16s cubic-bezier(0.22, 1, 0.36, 1);
  transform-style: preserve-3d;
  will-change: transform;
}
.tilt-sheen {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.25s ease;
  background: radial-gradient(160px circle at var(--mx, 50%) var(--my, 0%), rgba(255, 255, 255, 0.22), transparent 60%);
}
.tilt-card:hover .tilt-sheen { opacity: 1; }

/* --- Purchase preview entrance --- */
@keyframes preview-pop-in {
  0%   { opacity: 0; transform: translateY(16px) scale(0.92); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}
.preview-pop { animation: preview-pop-in 0.34s cubic-bezier(0.175, 0.885, 0.32, 1.275) both; }

/* --- One-shot light sweep across the showcased cosmetic --- */
@keyframes showcase-sweep {
  0%   { opacity: 1; transform: translateX(-130%) skewX(-18deg); }
  85%  { opacity: 1; }
  100% { opacity: 0; transform: translateX(240%) skewX(-18deg); }
}
.showcase-sweep { position: relative; overflow: hidden; }
.showcase-sweep::after {
  content: "";
  position: absolute;
  top: 0; bottom: 0; left: 0;
  width: 42%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.18), transparent);
  opacity: 0;
  animation: showcase-sweep 0.9s ease-out 0.18s 1 forwards;
  pointer-events: none;
}

/* --- Confirm-purchase radial sparkle burst --- */
@keyframes buy-spark-fly {
  0%   { opacity: 1; transform: translate(-50%, -50%) rotate(var(--a, 0deg)) translateY(0) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -50%) rotate(var(--a, 0deg)) translateY(-72px) scale(0.3); }
}
.buy-spark {
  position: absolute;
  top: 50%; left: 50%;
  width: 9px; height: 9px;
  border-radius: 9999px;
  pointer-events: none;
  box-shadow: 0 0 8px currentColor;
  animation: buy-spark-fly 0.7s ease-out forwards;
}

.reduced-motion .ripple-ink,
.reduced-motion .showcase-sweep::after,
.reduced-motion .buy-spark,
.reduced-motion .preview-pop { animation: none !important; }
.reduced-motion .tilt-card { transform: none !important; transition: none !important; }
.reduced-motion .tilt-sheen { display: none; }

/* --- Equipped reel-skin glow, bleeding from each symbol cell into the gaps
   around it. Adjacent cells' halos overlap in the gap, reading as one
   continuous soft flow rather than a separate per-gap effect. --- */
@keyframes reel-cell-glow-cycle {
  0%   { box-shadow: 0 0 14px 6px rgba(34,211,238,0.5); }
  25%  { box-shadow: 0 0 14px 6px rgba(167,139,250,0.5); }
  50%  { box-shadow: 0 0 14px 6px rgba(244,114,182,0.5); }
  75%  { box-shadow: 0 0 14px 6px rgba(52,211,153,0.5); }
  100% { box-shadow: 0 0 14px 6px rgba(34,211,238,0.5); }
}
.reel-cell-glow-legendary { animation: reel-cell-glow-cycle 6s linear infinite; }
.reduced-motion .reel-cell-glow-legendary { animation: none !important; box-shadow: 0 0 14px 6px rgba(167,139,250,0.5) !important; }

/* Spinning legendary reel: cycle the static gap-glow overlay's hue through the spectrum. */
@keyframes reel-glow-hue {
  from { filter: blur(2.5px) hue-rotate(0deg); }
  to   { filter: blur(2.5px) hue-rotate(360deg); }
}
.reel-glow-cycle { animation: reel-glow-hue 6s linear infinite; }
.reduced-motion .reel-glow-cycle { animation: none !important; }

/* Same color cycle for the spinning legendary reel's side glow (no blur — it's a box-shadow). */
@keyframes reel-sideglow-hue {
  from { filter: hue-rotate(0deg); }
  to   { filter: hue-rotate(360deg); }
}
.reel-sideglow-cycle { animation: reel-sideglow-hue 6s linear infinite; }
.reduced-motion .reel-sideglow-cycle { animation: none !important; }

/* ============================================================
   GLOBAL POLISH — accessibility & cross-browser consistency
   Cuts across every screen (lobby, shop, in-game) without touching
   component markup.
   ============================================================ */

/* Consistent keyboard focus ring. :focus-visible only triggers for keyboard
   navigation, so pointer/touch users see no change — this adds accessibility
   without altering the click feel. The themed gold matches the UI accent and
   the offset keeps it clear of each control's own glow/border. */
:focus-visible {
  outline: 2px solid rgba(251, 191, 36, 0.9);
  outline-offset: 2px;
}
/* Suppress the ring for mouse clicks in browsers that support :focus-visible. */
:focus:not(:focus-visible) {
  outline: none;
}

/* Firefox scrollbar parity with the WebKit styling defined at the top of this
   file (WebKit-only ::-webkit-scrollbar rules don't apply in Firefox). */
html {
  scrollbar-width: thin;
  scrollbar-color: #57534e #1c1917;
}
.custom-scrollbar {
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.18) rgba(0, 0, 0, 0.2);
}

/* ============================================================
   UI POLISH KIT — fast tooltips, invalid-action feedback,
   resource-low pulse, hover edge-glow.
   Backs ui/UIKit.js (Tooltip / IconButton). Every effect degrades
   gracefully under .reduced-motion (guards at the bottom).
   ============================================================ */

/* --- Crisp, fast hover/focus tooltip (replaces slow native title=) --- */
.ui-tip-wrap {
  position: relative;
  display: inline-flex;
}

.ui-tooltip {
  position: absolute;
  z-index: 200;
  pointer-events: none;
  white-space: nowrap;
  padding: 5px 9px;
  border-radius: 8px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1;
  color: #f5f5f4;
  background: rgba(12, 10, 9, 0.97);
  border: 1px solid var(--tip-color, rgba(251, 191, 36, 0.55));
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.6), 0 0 14px var(--tip-glow, rgba(251, 191, 36, 0.2));
  opacity: 0;
  /* Snappy: ~110ms in, instant feel. Per-placement transforms below. */
  transition: opacity 0.11s ease, transform 0.11s cubic-bezier(0.22, 1, 0.36, 1);
}

/* Small diamond arrow, re-colored to match the (themeable) border. */
.ui-tooltip::after {
  content: "";
  position: absolute;
  width: 7px;
  height: 7px;
  background: rgba(12, 10, 9, 0.97);
  border: 1px solid var(--tip-color, rgba(251, 191, 36, 0.55));
}

/* Placements — hidden + shown transforms specified together so the
   centering translate isn't clobbered by the show animation. */
.ui-tooltip.tip-top    { bottom: calc(100% + 9px); left: 50%; transform: translateX(-50%) translateY(4px) scale(0.96); }
.ui-tooltip.tip-bottom { top: calc(100% + 9px);    left: 50%; transform: translateX(-50%) translateY(-4px) scale(0.96); }
.ui-tooltip.tip-left   { right: calc(100% + 9px);  top: 50%;  transform: translateY(-50%) translateX(4px) scale(0.96); }
.ui-tooltip.tip-right  { left: calc(100% + 9px);   top: 50%;  transform: translateY(-50%) translateX(-4px) scale(0.96); }

.ui-tooltip.tip-top[data-show="true"]    { transform: translateX(-50%) translateY(0) scale(1); }
.ui-tooltip.tip-bottom[data-show="true"] { transform: translateX(-50%) translateY(0) scale(1); }
.ui-tooltip.tip-left[data-show="true"]   { transform: translateY(-50%) translateX(0) scale(1); }
.ui-tooltip.tip-right[data-show="true"]  { transform: translateY(-50%) translateX(0) scale(1); }
.ui-tooltip[data-show="true"] { opacity: 1; }

.ui-tooltip.tip-top::after    { bottom: -4px; left: 50%; margin-left: -4px; border-top: none;  border-left: none; }
.ui-tooltip.tip-bottom::after { top: -4px;    left: 50%; margin-left: -4px; border-bottom: none; border-right: none; }
.ui-tooltip.tip-left::after   { right: -4px;  top: 50%;  margin-top: -4px;  border-left: none;  border-bottom: none; }
.ui-tooltip.tip-right::after  { left: -4px;   top: 50%;  margin-top: -4px;  border-right: none; border-top: none; }

/* --- Hover edge-glow for juicy buttons (themeable via --glow-color) --- */
.ui-glow-hover {
  transition: box-shadow 0.18s ease, transform 0.15s ease, filter 0.18s ease;
}
.ui-glow-hover:hover:not(:disabled) {
  box-shadow: 0 0 0 1px var(--glow-color, rgba(251, 191, 36, 0.45)),
              0 0 16px var(--glow-color, rgba(251, 191, 36, 0.35));
  filter: brightness(1.08);
}

/* --- Invalid-action shake (e.g. SPIN with insufficient balance) --- */
@keyframes ui-error-shake {
  0%, 100% { transform: translateX(0); }
  15% { transform: translateX(-6px); }
  30% { transform: translateX(5px); }
  45% { transform: translateX(-4px); }
  60% { transform: translateX(3px); }
  75% { transform: translateX(-2px); }
}
.ui-error-shake { animation: ui-error-shake 0.42s cubic-bezier(0.36, 0.07, 0.19, 0.97); }

/* --- Resource-low pulse: richer "topped out" warning than a flat opacity blink.
   Cycles a red border + outward glow so a near-empty wallet draws the eye. --- */
@keyframes ui-resource-low {
  0%, 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); border-color: rgba(239, 68, 68, 0.5); }
  50%      { box-shadow: 0 0 18px 2px rgba(239, 68, 68, 0.5); border-color: rgba(248, 113, 113, 0.95); }
}
.ui-resource-low { animation: ui-resource-low 1.1s ease-in-out infinite; }

/* --- reduced-motion guards (mirror the rest of this file) --- */
.reduced-motion .ui-tooltip { transition: opacity 0.05s linear; }
.reduced-motion .ui-error-shake { animation: none !important; }
.reduced-motion .ui-glow-hover { transition-duration: 0.05s; }
.reduced-motion .ui-resource-low {
  animation: none !important;
  box-shadow: 0 0 12px 2px rgba(239, 68, 68, 0.45);
  border-color: rgba(248, 113, 113, 0.9);
}

/* ============================================================
   META LAYER  (#1 Daily Hub · #4 Lucky Pot)
   ============================================================ */

/* #4 — Lucky Pot meter: a sweeping shimmer once the pot is "hot". */
@keyframes lucky-pot-sweep {
  0%   { background-position: 0% 50%; }
  100% { background-position: 200% 50%; }
}
.lucky-pot-fill-hot {
  background-size: 200% 100% !important;
  animation: lucky-pot-sweep 1.1s linear infinite;
}
.reduced-motion .lucky-pot-fill-hot { animation: none !important; }

/* #1 — Daily Hub: daily reward ⚡ daily quests, split by a diagonal lightning
   bolt. Mobile stacks the two panels; ≥768px splits them on a diagonal seam. */
.daily-hub-grid { display: flex; flex-direction: column; min-height: 520px; max-height: 90vh; overflow-y: auto; }
.daily-hub-bolt { display: none; }
@media (min-width: 768px) {
  .daily-hub-card .daily-hub-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    min-height: 620px;
    max-height: 88vh;
    overflow-y: hidden;
  }
  .daily-hub-card .daily-hub-left {
    clip-path: polygon(0 0, 100% 0, calc(100% - 72px) 100%, 0 100%);
    padding-right: 4.75rem;
  }
  .daily-hub-card .daily-hub-right {
    clip-path: polygon(72px 0, 100% 0, 100% 100%, 0 100%);
    padding-left: 4.75rem;
  }
  .daily-hub-card .daily-hub-bolt {
    display: block;
    position: absolute;
    top: -2%;
    bottom: -2%;
    left: 50%;
    width: 88px;
    transform: translateX(-50%);
    z-index: 25;
  }
}
/* A subtle electric flicker on the bolt (skipped under reduced-motion). */
@keyframes hub-bolt-flicker {
  0%, 100% { opacity: 1; filter: drop-shadow(0 0 3px #fbbf24); }
  45%      { opacity: 0.78; }
  55%      { opacity: 1; filter: drop-shadow(0 0 7px #fde047); }
}
.daily-hub-bolt svg { animation: hub-bolt-flicker 2.4s ease-in-out infinite; }
.reduced-motion .daily-hub-bolt svg { animation: none !important; }

/* ============================================================
   BATTLE PASS STYLES
   ============================================================ */
.bp-track-bg {
  background-image: 
    linear-gradient(rgba(139, 92, 246, 0.15) 1px, transparent 1px),
    linear-gradient(90deg, rgba(139, 92, 246, 0.15) 1px, transparent 1px);
  background-size: 40px 40px;
  animation: bp-grid-scroll 20s linear infinite;
}

@keyframes bp-grid-scroll {
  0% { background-position: 0 0; }
  100% { background-position: -40px -40px; }
}

.bp-node {
  flex-shrink: 0;
  transform: perspective(600px) rotateX(2deg);
}

.bp-glow-effect::before {
  content: '';
  position: absolute;
  top: -12px;
  left: -12px;
  right: -12px;
  bottom: -12px;
  border-radius: 2.5rem;
  background: radial-gradient(circle, rgba(255,255,255,0.9) 0%, transparent 65%);
  z-index: -1;
  animation: bp-glow-pulse 1.5s infinite ease-in-out;
  opacity: 0.8;
}

@keyframes bp-glow-pulse {
  0% { transform: scale(0.95); opacity: 0.5; }
  50% { transform: scale(1.1); opacity: 1; filter: drop-shadow(0 0 10px white); }
  100% { transform: scale(0.95); opacity: 0.5; }
}

@keyframes animate-bounce-slight {
  0%, 100% { transform: translateY(0) perspective(600px) rotateX(2deg); }
  50% { transform: translateY(-4px) perspective(600px) rotateX(2deg); }
}

.animate-bounce-slight {
  animation: animate-bounce-slight 2s infinite;
}

/* ---- Brawl-Pass redesign (vertical ladder) ---- */

/* Claimable reward tiles gently breathe so the eye is drawn to them. */
@keyframes bp-tile-live {
  0%, 100% { transform: translateY(0) scale(1); }
  50%      { transform: translateY(-5px) scale(1.02); }
}
.bp-tile-live { animation: bp-tile-live 1.8s ease-in-out infinite; }

/* The little "Claim ▸" pill nudges left↔right like a button beckoning a tap. */
@keyframes bp-claim-pill {
  0%, 100% { transform: translateX(0); }
  50%      { transform: translateX(3px); }
}
.bp-claim-pill { animation: bp-claim-pill 1s ease-in-out infinite; }

/* Energy flowing along the travelled portion of the progression spine. */
@keyframes bp-spine-flow {
  0%   { background-position: 0 0; }
  100% { background-position: 24px 0; }
}
.bp-spine-lit {
  background-size: 24px 100% !important;
  animation: bp-spine-flow 0.8s linear infinite;
}

/* Pulsing halo around the player's current tier badge ("you are here"). */
@keyframes bp-current-ring {
  0%   { transform: scale(1);   box-shadow: 0 0 0 0 rgba(251,191,36,0.7); }
  70%  { transform: scale(1.5); box-shadow: 0 0 0 14px rgba(251,191,36,0); }
  100% { transform: scale(1.5); box-shadow: 0 0 0 0 rgba(251,191,36,0); }
}
.bp-current-ring {
  border-radius: 9999px;
  animation: bp-current-ring 1.6s ease-out infinite;
}

/* Sheen sweeping across the XP bar fill. */
@keyframes bp-xp-shine {
  0%   { background-position: -120% 0; }
  100% { background-position: 220% 0; }
}
.bp-xp-shine {
  position: relative;
  overflow: hidden;
}
.bp-xp-shine::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(110deg, transparent 35%, rgba(255,255,255,0.85) 50%, transparent 65%);
  background-size: 220% 100%;
  animation: bp-xp-shine 2.2s linear infinite;
}

/* Claim All button breathes a green glow while rewards are waiting. */
@keyframes bp-claimall-pulse {
  0%, 100% { box-shadow: 0 6px 0 #14532d, 0 0 18px rgba(34,197,94,0.45); }
  50%      { box-shadow: 0 6px 0 #14532d, 0 0 30px rgba(34,197,94,0.85); }
}
.bp-claimall-pulse { animation: bp-claimall-pulse 1.4s ease-in-out infinite; }

/* Finale hero strip: slow shifting gold→purple aurora behind the legendary. */
@keyframes bp-finale-strip {
  0%   { background-position: 0% 50%; }
  100% { background-position: 200% 50%; }
}
.bp-finale-strip {
  background: linear-gradient(100deg, rgba(120,53,15,0.55), rgba(76,29,149,0.55), rgba(120,53,15,0.55));
  background-size: 200% 100%;
  animation: bp-finale-strip 8s linear infinite;
}
@keyframes bp-finale-icon {
  0%, 100% { transform: translateY(0) rotate(-4deg); }
  50%      { transform: translateY(-6px) rotate(4deg); }
}
.bp-finale-icon { animation: bp-finale-icon 3s ease-in-out infinite; }

/* The legendary tier-30 row gets a permanent gold aura. */
.bp-finale-row {
  background: radial-gradient(ellipse at center, rgba(251,191,36,0.12), transparent 70%);
  box-shadow: inset 0 0 40px rgba(251,191,36,0.12);
}

/* Big premium-unlock banner: a slow gold↔purple aurora with a sweeping sheen. */
@keyframes bp-unlock-banner {
  0%   { background-position: 0% 50%; }
  100% { background-position: 200% 50%; }
}
.bp-unlock-banner {
  background: linear-gradient(100deg, rgba(76,29,149,0.85), rgba(120,53,15,0.85), rgba(168,85,247,0.7), rgba(76,29,149,0.85));
  background-size: 200% 100%;
  animation: bp-unlock-banner 6s linear infinite;
}
.bp-unlock-banner::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(110deg, transparent 40%, rgba(255,255,255,0.18) 50%, transparent 60%);
  background-size: 220% 100%;
  animation: bp-xp-shine 3s linear infinite;
  pointer-events: none;
}

/* Reward-haul screen: cards pop in with a little overshoot. */
@keyframes bp-haul-pop {
  0%   { transform: scale(0.4) translateY(20px); opacity: 0; }
  70%  { transform: scale(1.08) translateY(0); opacity: 1; }
  100% { transform: scale(1) translateY(0); opacity: 1; }
}
.bp-haul-pop {
  animation: bp-haul-pop 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

/* Slow rotating ray backdrop behind the haul celebration. */
@keyframes bp-ray-spin { to { transform: translate(-50%, -50%) rotate(360deg); } }
.bp-ray-spin { animation: bp-ray-spin 18s linear infinite; }

/* Confetti raining down the reward-haul screen. */
@keyframes bp-confetti-fall {
  0%   { transform: translateY(-12vh) rotate(0deg);   opacity: 0; }
  12%  { opacity: 1; }
  100% { transform: translateY(108vh) rotate(620deg); opacity: 0.9; }
}
.bp-confetti {
  position: absolute;
  top: 0;
  width: 10px;
  height: 14px;
  border-radius: 2px;
  opacity: 0;
  animation-name: bp-confetti-fall;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

/* Glossy sheen sweeping across claimable reward tiles. */
.bp-tile-live::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(110deg, transparent 42%, rgba(255,255,255,0.32) 50%, transparent 58%);
  background-size: 260% 100%;
  animation: bp-xp-shine 2.6s linear infinite;
  pointer-events: none;
}

/* Epic treatment for a tile showing the exclusive bundle gem: the TILE itself
   becomes a flowing rainbow with a pulsing glow. The rainbow gradient is set
   inline on the tile background, so the icon sits cleanly on top — nothing
   overlays or bleeds onto it. This just animates the flow + glow. */
.bp-bundle-epic {
  background-size: 100% 100%;
  background-position: center;
  animation: bp-bundle-glow 2.2s ease-in-out infinite;
}
@keyframes bp-bundle-glow {
  0%, 100% { box-shadow: 0 0 26px rgba(168,85,247,0.6), 0 10px 0 rgba(0,0,0,0.35); }
  50%      { box-shadow: 0 0 46px rgba(244,114,182,0.85), 0 10px 0 rgba(0,0,0,0.35); }
}

/* Animated gradient "BUNDLE" ribbon. */
@keyframes bp-bundle-ribbon { 0% { background-position: 0% 50%; } 100% { background-position: 300% 50%; } }
.bp-bundle-ribbon {
  background: linear-gradient(90deg, #22d3ee, #a855f7, #f472b6, #fbbf24, #22d3ee);
  background-size: 300% 100%;
  animation: bp-bundle-ribbon 3s linear infinite;
  box-shadow: 0 0 12px rgba(168,85,247,0.8);
}

/* Quick fade when an alternating bundle tile swaps faces. */
@keyframes bp-face-in { 0% { opacity: 0; transform: scale(0.85); } 100% { opacity: 1; transform: scale(1); } }
.bp-face-in { animation: bp-face-in 0.4s ease-out both; }

/* Circular tier-progress ring (header): a gold conic gradient shows % to the next
   tier; the inner dark disc (a child) masks the centre, leaving a glowing ring. */
.bp-tier-ring::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 9999px;
  background: conic-gradient(from -90deg, #fde047, #f59e0b var(--pct, 0%), rgba(41,37,36,0.85) var(--pct, 0%) 100%);
  box-shadow: 0 0 16px rgba(251,191,36,0.45);
}

/* Pulsing rarity halo marking a milestone (cosmetic-bearing) tier on the spine. */
@keyframes bp-spine-milestone {
  0%, 100% { box-shadow: 0 0 8px var(--ms); opacity: 0.85; }
  50%      { box-shadow: 0 0 18px var(--ms); opacity: 1; }
}
.bp-spine-milestone {
  border: 2px solid var(--ms);
  animation: bp-spine-milestone 2.2s ease-in-out infinite;
}

/* Shimmering rarity banner on legendary/epic haul drops. */
@keyframes bp-rarity-banner {
  0%, 100% { transform: scale(1); filter: brightness(1); }
  50%      { transform: scale(1.05); filter: brightness(1.18); }
}
.bp-rarity-banner { animation: bp-rarity-banner 1.4s ease-in-out infinite; }

/* Respect reduced-motion: kill the looping flourishes, keep the layout. */
.reduced-motion .bp-tile-live,
.reduced-motion .bp-claim-pill,
.reduced-motion .bp-spine-lit,
.reduced-motion .bp-current-ring,
.reduced-motion .bp-xp-shine::after,
.reduced-motion .bp-claimall-pulse,
.reduced-motion .bp-finale-strip,
.reduced-motion .bp-finale-icon,
.reduced-motion .bp-unlock-banner,
.reduced-motion .bp-unlock-banner::after,
.reduced-motion .bp-ray-spin,
.reduced-motion .bp-tile-live::after,
.reduced-motion .bp-bundle-epic,
.reduced-motion .bp-bundle-epic::before,
.reduced-motion .bp-bundle-ribbon,
.reduced-motion .bp-spine-milestone,
.reduced-motion .bp-rarity-banner {
  animation: none !important;
}
/* Haul cards still need to be visible (not stuck at opacity:0) without motion. */
.reduced-motion .bp-haul-pop { animation: none !important; opacity: 1; }
/* Face swaps and confetti are purely decorative under reduced-motion. */
.reduced-motion .bp-face-in { animation: none !important; opacity: 1; }
.reduced-motion .bp-confetti { display: none !important; }

/* ============================================================
   VIP DASHBOARD — tabbed shell mount + tab-switch transitions
   ============================================================ */
@keyframes vip-shell-in {
  0%   { opacity: 0; transform: scale(0.95) translateY(24px); }
  100% { opacity: 1; transform: scale(1) translateY(0); }
}
.vip-shell-in { animation: vip-shell-in 0.4s cubic-bezier(0.16, 1, 0.3, 1) both; }

@keyframes vip-tab-in {
  0%   { opacity: 0; transform: translateY(8px); }
  100% { opacity: 1; transform: translateY(0); }
}
.vip-tab-in { animation: vip-tab-in 0.28s ease-out both; }

/* Claimable daily-bonus CTA — breathes a tier-coloured glow to pull the eye. */
@keyframes vip-pulse-cta {
  0%, 100% { box-shadow: 0 0 16px var(--cta-glow, rgba(255,255,255,0.4)); }
  50%      { box-shadow: 0 0 34px var(--cta-glow, rgba(255,255,255,0.4)); }
}
.vip-pulse-cta { animation: vip-pulse-cta 1.6s ease-in-out infinite; }

/* Tab notification dot — pulses so "something's waiting" reads at a glance. */
@keyframes vip-badge-pulse {
  0%, 100% { transform: scale(1); opacity: 1; }
  50%      { transform: scale(1.35); opacity: 0.65; }
}
.vip-badge-pulse { animation: vip-badge-pulse 1.2s ease-in-out infinite; }

.reduced-motion .vip-shell-in,
.reduced-motion .vip-tab-in,
.reduced-motion .vip-pulse-cta,
.reduced-motion .vip-badge-pulse { animation: none !important; }

/* ============================================================
   WELCOME TOUR — spotlight ring + entrance
   ============================================================ */
@keyframes tour-fade-in { from { opacity: 0; } to { opacity: 1; } }
.tour-fade-in { animation: tour-fade-in 0.3s ease-out both; }
/* Opacity-only so it never clobbers the centered tooltip's translate(-50%,-50%). */
.tour-card { animation: tour-fade-in 0.3s ease-out both; }

/* Pulsing gold ring drawn around the spotlighted element. */
.tour-ring {
  position: absolute;
  inset: -3px;
  border: 3px solid #fbbf24;
  border-radius: 18px;
  pointer-events: none;
  animation: tour-ring-pulse 1.4s ease-in-out infinite;
}
@keyframes tour-ring-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(251,191,36,0.55), 0 0 14px rgba(251,191,36,0.5); }
  50%      { box-shadow: 0 0 0 8px rgba(251,191,36,0), 0 0 22px rgba(251,191,36,0.85); }
}
.reduced-motion .tour-ring,
.reduced-motion .tour-card,
.reduced-motion .tour-fade-in { animation: none !important; }

/* ============================================================
   SCREEN TRANSITIONS — cross-fade veil between top-level views
   (login → lobby → slot) so screen swaps never hard-cut.
   ============================================================ */
/* Full-viewport veil rendered only while a transition runs. Sits above
   everything (modals top out at z-[350]). */
.screen-veil {
  position: fixed;
  inset: 0;
  z-index: 500;
  background: #0c0a09;
  pointer-events: none;
}
/* Covering phase — also swallows clicks so nothing can be pressed
   mid-swap (double-taps on a lobby card can't fire twice). */
.screen-veil-out {
  animation: screen-veil-out 0.22s ease-in forwards;
  pointer-events: auto;
}
/* Reveal phase — slightly longer so the new screen eases in. */
.screen-veil-in {
  animation: screen-veil-in 0.45s ease-out forwards;
}
@keyframes screen-veil-out { from { opacity: 0; } to { opacity: 1; } }
@keyframes screen-veil-in  { from { opacity: 1; } to { opacity: 0; } }

/* Gentle rise+fade for a freshly mounted screen root, layered under the
   veil reveal so the new view settles instead of popping. */
@keyframes screen-enter {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}
.screen-enter { animation: screen-enter 0.45s ease-out both; }

/* ============================================================
   MODAL EXIT — applied by <ModalFade> to a closing modal's root
   so overlays fade away instead of vanishing on unmount.
   ============================================================ */
@keyframes modal-exit {
  from { opacity: 1; }
  to   { opacity: 0; }
}
.modal-closing {
  animation: modal-exit 0.18s ease-in forwards !important;
  pointer-events: none !important;
}

.reduced-motion .screen-veil,
.reduced-motion .screen-enter { animation-duration: 0.01s !important; }
.reduced-motion .modal-closing { animation-duration: 0.01s !important; }

/* ============================================================
   WIN-ANIMATION SMOOTHING — graceful exits for win overlays
   ============================================================ */
/* Celebratory win popup dismiss: ease the amount + dark backdrop away
   (fade + gentle sink/shrink) instead of unmounting in a single frame. */
@keyframes win-dismiss {
  from { opacity: 1; transform: scale(1); filter: blur(0); }
  to   { opacity: 0; transform: scale(0.88) translateY(8px); filter: blur(2px); }
}
.win-dismiss {
  animation: win-dismiss 0.34s cubic-bezier(0.4, 0, 0.6, 1) forwards !important;
  pointer-events: none !important;
}

/* Per-line win chip strip dismiss: quick collapse+fade so the row of
   payout pills recedes rather than blinking out. */
@keyframes win-strip-dismiss {
  from { opacity: 1; transform: translateY(0); }
  to   { opacity: 0; transform: translateY(6px); }
}
.win-strip-dismiss {
  animation: win-strip-dismiss 0.28s ease-in forwards !important;
  pointer-events: none !important;
}

/* ============================================================
   TAB CROSS-FADE — content panels that swap on a tab change
   ============================================================ */
@keyframes tab-fade-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.tab-fade-in { animation: tab-fade-in 0.28s ease-out both; }

.reduced-motion .win-dismiss,
.reduced-motion .win-strip-dismiss,
.reduced-motion .tab-fade-in { animation-duration: 0.01s !important; }

/* Big-win ceremony (BIG/MEGA/SUPER/ULTRA WIN) dismiss — the celebration
   used to blink out the instant you tapped or it timed out. Ease it away
   with a fade + slight zoom so the moment lands softly. */
@keyframes ceremony-dismiss {
  from { opacity: 1; transform: scale(1); }
  to   { opacity: 0; transform: scale(1.06); }
}
.ceremony-dismiss {
  animation: ceremony-dismiss 0.32s cubic-bezier(0.4, 0, 0.6, 1) forwards !important;
  pointer-events: none !important;
}
.reduced-motion .ceremony-dismiss { animation-duration: 0.01s !important; }

/* ============================================================
   CARD PACKS & CARD COLLECTION (ui/CardsUI.js, ui/CardArt.js)
   The pack-opening ceremony: float → shake → burst → deal →
   4-click reveal (clicks 1–3 can upgrade rarity, click 4 flips).
   ============================================================ */

/* Foil sheen that sweeps across epic/legendary/wild card faces.
   no-repeat is load-bearing: with the default repeat, the oversized gradient
   TILES and the sweep band gets sliced at tile seams — hard-edged bright
   rectangles ("boxes") drifting across the card. */
.card-foil {
  position: absolute; inset: 0; z-index: 5;
  background: linear-gradient(115deg, transparent 32%, rgba(255,255,255,0.32) 45%, rgba(255,255,255,0.10) 52%, transparent 64%);
  background-size: 250% 250%;
  background-repeat: no-repeat;
  animation: card-foil-sweep 3s ease-in-out infinite;
}
@keyframes card-foil-sweep {
  0%   { background-position: 120% 120%; }
  55%  { background-position: -30% -30%; }
  100% { background-position: -30% -30%; }
}

/* Rainbow hue-cycling (Wild pack pouch + its overlay rays) */
.pack-rainbow { animation: pack-hue-cycle 5s linear infinite; }
@keyframes pack-hue-cycle { to { filter: hue-rotate(360deg); } }

/* Animated rainbow border ring (Wild pack shop tile / inventory chip) */
.rainbow-ring {
  border: 2.5px solid transparent !important;
  background:
    linear-gradient(#151210, #151210) padding-box,
    linear-gradient(120deg, #ef4444, #f97316, #facc15, #4ade80, #38bdf8, #a855f7, #ef4444) border-box !important;
  background-size: 100% 100%, 300% 300% !important;
  animation: rainbow-ring-shift 4s linear infinite;
}
@keyframes rainbow-ring-shift {
  0%   { background-position: 0 0, 0% 50%; }
  100% { background-position: 0 0, 300% 50%; }
}

/* Wild Card face: the rainbow border-box layer (painted inline by
   WildCardFace) scrolls while the outer glow cycles through the same hues.
   Layers: tier tint, dark base (both padding-box), rainbow (border-box). */
.wild-rainbow-border {
  box-shadow: 0 0 10px rgba(244,114,182,0.4), inset 0 1px 0 rgba(255,255,255,0.08); /* static fallback */
  animation: wild-rainbow-border 4s linear infinite;
}
@keyframes wild-rainbow-border {
  0%   { background-position: 0 0, 0 0, 0% 50%;   box-shadow: 0 0 12px rgba(239,68,68,0.5),  inset 0 1px 0 rgba(255,255,255,0.08); }
  17%  { box-shadow: 0 0 12px rgba(249,115,22,0.5), inset 0 1px 0 rgba(255,255,255,0.08); }
  33%  { box-shadow: 0 0 12px rgba(250,204,21,0.5), inset 0 1px 0 rgba(255,255,255,0.08); }
  50%  { box-shadow: 0 0 12px rgba(74,222,128,0.5), inset 0 1px 0 rgba(255,255,255,0.08); }
  67%  { box-shadow: 0 0 12px rgba(56,189,248,0.5), inset 0 1px 0 rgba(255,255,255,0.08); }
  83%  { box-shadow: 0 0 12px rgba(168,85,247,0.5), inset 0 1px 0 rgba(255,255,255,0.08); }
  100% { background-position: 0 0, 0 0, 300% 50%; box-shadow: 0 0 12px rgba(239,68,68,0.5),  inset 0 1px 0 rgba(255,255,255,0.08); }
}

/* Shared notification dot — marks any menu entry with something claimable
   RIGHT NOW (never decorative, so it stays trustworthy). */
.notif-dot {
  display: inline-block;
  width: 8px; height: 8px; border-radius: 9999px;
  background: #fbbf24;
  box-shadow: 0 0 8px rgba(251,191,36,0.9);
  animation: notif-dot-pulse 1.6s ease-in-out infinite;
}
@keyframes notif-dot-pulse {
  0%, 100% { transform: scale(1);    opacity: 1; }
  50%       { transform: scale(0.72); opacity: 0.65; }
}

/* Upgrade-chance pip igniting (reveal clicks 1–3) */
.pip-burn { animation: pip-burn 0.35s cubic-bezier(0.3, 1.6, 0.5, 1); }
@keyframes pip-burn {
  0%   { transform: scale(1); }
  45%  { transform: scale(2); }
  100% { transform: scale(1); }
}

/* Soft pulse around a face-down card in the NEXT rarity's color — "an
   upgrade is still possible". --aura-rgb is set inline per card. */
.back-next-aura {
  border-radius: 1rem;
  animation: back-next-aura 1.7s ease-in-out infinite;
}
@keyframes back-next-aura {
  0%, 100% { box-shadow: 0 0 10px 1px rgba(var(--aura-rgb), 0.12); }
  50%       { box-shadow: 0 0 22px 5px rgba(var(--aura-rgb), 0.42); }
}

/* Shop purchase sparkles (spawnSparkles) — a ✦ burst from the click point */
.buy-sparkle {
  position: absolute; pointer-events: none; z-index: 30;
  color: #fde68a; text-shadow: 0 0 6px rgba(251, 191, 36, 0.9);
  animation: buy-sparkle 0.7s cubic-bezier(0.2, 0.7, 0.3, 1) forwards;
}
@keyframes buy-sparkle {
  0%   { transform: translate(-50%, -50%) scale(0.4) rotate(0deg); opacity: 1; }
  100% { transform: translate(calc(-50% + var(--sx, 0px)), calc(-50% + var(--sy, 0px))) scale(1.15) rotate(120deg); opacity: 0; }
}
.reduced-motion .buy-sparkle { display: none; }

/* Card Inspector: the big card + info panel settle in with a soft zoom */
.card-inspect-in { animation: card-inspect-in 0.28s cubic-bezier(0.2, 1.1, 0.4, 1) both; }
@keyframes card-inspect-in {
  from { transform: scale(0.82); opacity: 0; }
  to   { transform: scale(1);    opacity: 1; }
}

/* Pack rip: an intensifying wobble … */
.pack-shaking { animation: pack-shake 0.55s cubic-bezier(0.36, 0.07, 0.19, 0.97) both; }
@keyframes pack-shake {
  0%   { transform: rotate(0deg) scale(1); }
  15%  { transform: rotate(-4deg) scale(1.02) translateX(-2px); }
  30%  { transform: rotate(4deg)  scale(1.04) translateX(2px); }
  45%  { transform: rotate(-6deg) scale(1.06) translateX(-4px); }
  60%  { transform: rotate(6deg)  scale(1.08) translateX(4px); }
  75%  { transform: rotate(-8deg) scale(1.12) translateX(-5px); }
  90%  { transform: rotate(8deg)  scale(1.16) translateX(5px); }
  100% { transform: rotate(0deg)  scale(1.2); }
}
/* … then it pops */
.pack-bursting { animation: pack-burst 0.38s cubic-bezier(0.5, 0, 0.9, 0.4) forwards; }
@keyframes pack-burst {
  0%   { transform: scale(1.2); opacity: 1; filter: brightness(1); }
  40%  { transform: scale(1.45); opacity: 1; filter: brightness(2.4); }
  100% { transform: scale(2.1); opacity: 0; filter: brightness(3); }
}
.pack-burst-ring {
  position: absolute; left: 50%; top: 40%;
  width: 60px; height: 60px; margin: -30px 0 0 -30px;
  border: 4px solid; border-radius: 9999px; pointer-events: none;
  animation: pack-burst-ring 0.55s ease-out forwards;
}
@keyframes pack-burst-ring {
  from { transform: scale(0.4); opacity: 1; }
  to   { transform: scale(9); opacity: 0; }
}

/* Cards dealt from the burst — overshoot fly-in */
.card-deal-in { animation: card-deal-in 0.55s cubic-bezier(0.18, 1.4, 0.4, 1) both; }
@keyframes card-deal-in {
  from { opacity: 0; transform: translateY(90px) rotate(10deg) scale(0.3); }
  60%  { opacity: 1; }
  to   { opacity: 1; transform: translateY(0) rotate(0deg) scale(1); }
}

/* 3D flip shell */
.flip-card { position: relative; aspect-ratio: 3 / 4; perspective: 900px; }
.flip-inner {
  position: absolute; inset: 0;
  transform-style: preserve-3d;
  transition: transform 0.65s cubic-bezier(0.3, 1.4, 0.45, 1);
}
.flip-inner.flipped { transform: rotateY(180deg); }
.flip-front, .flip-back {
  position: absolute; inset: 0;
  backface-visibility: hidden; -webkit-backface-visibility: hidden;
}
.flip-back { transform: rotateY(180deg); }

/* Upgrade feedback: card pulse + expanding ring + label pop */
.card-upgrade-pop { animation: card-upgrade-pop 0.45s cubic-bezier(0.2, 1.6, 0.4, 1); }
@keyframes card-upgrade-pop {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.16) rotate(-2deg); }
  70%  { transform: scale(0.97); }
  100% { transform: scale(1); }
}
.upgrade-burst {
  position: absolute; inset: -4px; z-index: 20;
  border: 3px solid; border-radius: 0.85rem; pointer-events: none;
  animation: upgrade-burst 0.55s ease-out forwards;
}
@keyframes upgrade-burst {
  from { transform: scale(1); opacity: 1; }
  to   { transform: scale(1.8); opacity: 0; }
}

/* NEW / dupe badge pop after a flip */
.reveal-badge-pop { animation: reveal-badge-pop 0.4s cubic-bezier(0.2, 1.6, 0.4, 1) both; animation-delay: 0.3s; }
@keyframes reveal-badge-pop {
  from { opacity: 0; transform: scale(0.3) translateY(6px); }
  to   { opacity: 1; transform: scale(1) translateY(0); }
}

/* Whole-screen gold flash when a LEGENDARY flips */
.legendary-flash {
  position: absolute; inset: 0;
  background: radial-gradient(60% 60% at 50% 45%, rgba(253,230,138,0.85), rgba(251,191,36,0.35) 55%, transparent 80%);
  animation: legendary-flash 0.9s ease-out forwards;
}
@keyframes legendary-flash {
  0%   { opacity: 0; }
  15%  { opacity: 1; }
  100% { opacity: 0; }
}

/* Reduced motion: freeze the ceremony down to its essentials */
.reduced-motion .card-foil,
.reduced-motion .pack-rainbow,
.reduced-motion .rainbow-ring,
.reduced-motion .wild-rainbow-border,
.reduced-motion .pack-shaking,
.reduced-motion .upgrade-burst,
.reduced-motion .notif-dot,
.reduced-motion .back-next-aura,
.reduced-motion .pack-burst-ring { animation: none !important; }
.reduced-motion .card-deal-in,
.reduced-motion .card-upgrade-pop,
.reduced-motion .reveal-badge-pop,
.reduced-motion .legendary-flash,
.reduced-motion .pip-burn,
.reduced-motion .card-inspect-in,
.reduced-motion .pack-bursting { animation-duration: 0.01s !important; }
.reduced-motion .flip-inner { transition-duration: 0.1s; }

/* --- Pack rip 2.0: rapid clicks tear the crimped top clean off --------- */
/* A proper rattle per click (re-keyed by the overlay so it replays): quick
   side-to-side + rotation, scaled by --jolt-amt (1 → ~2 as the rip meter
   fills) so the shake gets more frantic the closer the pack is to tearing. */
.pack-click-jolt { animation: pack-click-jolt 0.18s cubic-bezier(0.3, 1.6, 0.5, 1); }
@keyframes pack-click-jolt {
  0%   { transform: translateX(0)                                scale(1)                                  rotate(0deg); }
  15%  { transform: translateX(calc(-5px * var(--jolt-amt, 1)))   scale(calc(1 + 0.05 * var(--jolt-amt, 1))) rotate(calc(-3deg * var(--jolt-amt, 1))); }
  35%  { transform: translateX(calc(4px * var(--jolt-amt, 1)))    scale(calc(1 + 0.08 * var(--jolt-amt, 1))) rotate(calc(2.5deg * var(--jolt-amt, 1))); }
  55%  { transform: translateX(calc(-3px * var(--jolt-amt, 1)))   scale(calc(1 + 0.03 * var(--jolt-amt, 1))) rotate(calc(-1.5deg * var(--jolt-amt, 1))); }
  75%  { transform: translateX(calc(2px * var(--jolt-amt, 1)))    scale(0.98)                                rotate(calc(1deg * var(--jolt-amt, 1))); }
  100% { transform: translateX(0)                                scale(1)                                  rotate(0deg); }
}
/* The torn top strip flies up and away, spinning */
.pack-top-tear { animation: pack-top-tear 0.7s cubic-bezier(0.2, 0.6, 0.4, 1) forwards; }
@keyframes pack-top-tear {
  0%   { transform: rotate(9deg) translate(6px, -5px); opacity: 1; }
  100% { transform: rotate(140deg) translate(150px, -190px); opacity: 0; }
}
/* The body holds for a beat while the top tears, THEN bursts */
.pack-bursting-delayed { animation: pack-burst 0.4s cubic-bezier(0.5, 0, 0.9, 0.4) 0.28s forwards; }
/* Urgent "keep clicking!" label while the rip meter is live */
.rip-urge { animation: rip-urge 0.35s ease-in-out infinite; }
@keyframes rip-urge {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.08); }
}
/* Pack Wheel pointer nods while the wheel spins */
.wheel-pointer-tick { animation: wheel-pointer-tick 0.22s ease-in-out infinite; transform-origin: 50% 0; }
@keyframes wheel-pointer-tick {
  0%, 100% { transform: rotate(0deg); }
  50%      { transform: rotate(-14deg); }
}
.reduced-motion .pack-click-jolt,
.reduced-motion .rip-urge,
.reduced-motion .wheel-pointer-tick { animation: none !important; }
.reduced-motion .pack-top-tear,
.reduced-motion .pack-bursting-delayed { animation-duration: 0.01s !important; animation-delay: 0s !important; }
/* Foil shrapnel flung out when the pack tears open */
.pack-shrapnel {
  position: absolute; left: 50%; top: 38%;
  width: 9px; height: 9px; border-radius: 2px; pointer-events: none;
  animation: pack-shrapnel 0.7s cubic-bezier(0.2, 0.8, 0.4, 1) forwards;
}
@keyframes pack-shrapnel {
  from { transform: translate(-50%, -50%) rotate(0deg) scale(1); opacity: 1; }
  to   { transform: translate(calc(-50% + var(--sx)), calc(-50% + var(--sy))) rotate(560deg) scale(0.5); opacity: 0; }
}
.reduced-motion .pack-shrapnel { display: none !important; }

/* ============================================================
   BUY-BUTTON SPARKLES — the SAME gold glow-dot burst the Show-Paylines
   button fires (.buy-spark), looping while you hover any "spend money"
   button. <BuySparkles/> can sit anywhere inside the button; the
   DESCENDANT selector (`button:hover .buy-sparkles`, no `>`) lights it
   even when it's nested a few layers down inside a shop tile.
   Two variants:
     • default — gold dots flung radially from centre (the paylines look)
     • --plus  — plus-shaped pips spread over the surface, for the
                 Battle Pass strip; pair with --always to run non-stop.
   ============================================================ */
.buy-sparkles {
  position: absolute; inset: 0; pointer-events: none;
  border-radius: inherit; opacity: 0; transition: opacity 0.15s ease;
  z-index: 3; color: #fbbf24;
}
button:hover .buy-sparkles,
button:focus-visible .buy-sparkles,
.buy-sparkle-host:hover .buy-sparkles { opacity: 1; }
.buy-sparkles.buy-sparkles--always { opacity: 1; }

/* Dot burst — the paylines toggle particle (.buy-spark), but bright and
   COLOR-CODED: the dot is a white core fading to the button's own colour
   (set via `color`/currentColor by <BuySparkles color=…/>), with a colour
   glow, flung radially and looped. Bigger + stronger glow = prominent. */
.buy-sparkles > i {
  position: absolute; top: 50%; left: 50%;
  width: 11px; height: 11px; border-radius: 9999px;
  background: radial-gradient(circle at 50% 50%, #fff 0%, currentColor 62%);
  box-shadow: 0 0 13px currentColor, 0 0 5px #fff;
  opacity: 0; animation: buy-spark-hover 0.95s ease-out infinite;
}
.buy-sparkles > i:nth-child(1) { --a: 18deg;  animation-delay: 0s; }
.buy-sparkles > i:nth-child(2) { --a: 74deg;  animation-delay: 0.13s; }
.buy-sparkles > i:nth-child(3) { --a: 130deg; animation-delay: 0.27s; }
.buy-sparkles > i:nth-child(4) { --a: 200deg; animation-delay: 0.42s; }
.buy-sparkles > i:nth-child(5) { --a: 262deg; animation-delay: 0.57s; }
.buy-sparkles > i:nth-child(6) { --a: 322deg; animation-delay: 0.72s; }
@keyframes buy-spark-hover {
  0%   { opacity: 0; transform: translate(-50%, -50%) rotate(var(--a, 0deg)) translateY(0) scale(0.6); }
  20%  { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -50%) rotate(var(--a, 0deg)) translateY(-32px) scale(0.35); }
}

/* Plus variant (Battle Pass unlock strip): bright plus-shaped pips in the
   button's colour, spread across the surface, twinkling in place. */
.buy-sparkles--plus > i {
  top: auto; left: auto; width: 15px; height: 15px; border-radius: 0;
  background:
    linear-gradient(currentColor, currentColor) center / 100% 2.5px no-repeat,
    linear-gradient(currentColor, currentColor) center / 2.5px 100% no-repeat;
  box-shadow: none; filter: drop-shadow(0 0 5px currentColor) drop-shadow(0 0 2px #fff);
  animation: buy-plus-twinkle 1.35s ease-in-out infinite;
}
.buy-sparkles--plus > i:nth-child(1) { left: 8%;  top: 55%; animation-delay: 0s; }
.buy-sparkles--plus > i:nth-child(2) { left: 24%; top: 24%; animation-delay: 0.2s; }
.buy-sparkles--plus > i:nth-child(3) { left: 44%; top: 66%; animation-delay: 0.4s; }
.buy-sparkles--plus > i:nth-child(4) { left: 62%; top: 28%; animation-delay: 0.6s; }
.buy-sparkles--plus > i:nth-child(5) { left: 80%; top: 60%; animation-delay: 0.8s; }
.buy-sparkles--plus > i:nth-child(6) { left: 92%; top: 34%; animation-delay: 1s; }
@keyframes buy-plus-twinkle {
  0%, 100% { opacity: 0; transform: translate(-50%, -50%) scale(0.5) rotate(0deg); }
  45%      { opacity: 1; transform: translate(-50%, -50%) scale(1.05) rotate(45deg); }
  75%      { opacity: 0.6; transform: translate(-50%, -50%) scale(0.85) rotate(90deg); }
}
.reduced-motion .buy-sparkles > i { animation: none !important; }
.reduced-motion .buy-sparkles { opacity: 0 !important; }

/* ============================================================
   LOGIN SCREEN — chasing marquee lights along the card top
   (casino-sign vibe; a dotted strip whose bulbs scroll sideways)
   ============================================================ */
@keyframes login-marquee {
  from { background-position: 0 0; }
  to   { background-position: 28px 0; }
}
.login-marquee {
  height: 12px;
  background-image: radial-gradient(circle at 7px 6px, #fbbf24 2.6px, rgba(251,191,36,0.18) 3.8px, transparent 5px);
  background-size: 28px 12px;
  background-repeat: repeat-x;
  animation: login-marquee 1.15s linear infinite;
  filter: drop-shadow(0 0 6px rgba(251,191,36,0.45));
}
.reduced-motion .login-marquee { animation: none !important; }
