/* Fonts are loaded from index.html <head> (preconnect + <link>) so they fetch in parallel
   with this stylesheet instead of after it — was a render-blocking @import here. */

/* Root-level guard: never expose a horizontal scrollbar from a transient wide
   layer (rip FX, off-screen card fly-in animations).
   The base page background lives HERE (on the root) rather than on <body>: the
   root's background is propagated across the whole viewport canvas on EVERY
   device, so it's identical on mobile and desktop. (body used
   background-attachment:fixed, which most mobile browsers ignore — that left the
   base white behind the lights on phones.) */
html {
  overflow-x: clip;
  /* CYBERPUNK CASINO base — near-black blue/violet with a neon HORIZON glow
     (magenta floor, cyan + purple sky) so the neon lights pop. On the root so it's
     propagated across the viewport canvas, mobile-safe + identical everywhere. */
  background-color: #04050d;     /* solid floor — never white */
  background-image:
    radial-gradient(ellipse 130% 58% at 50% 102%, rgba(255,45,155,0.20), transparent 62%),
    radial-gradient(circle at 12% -6%, rgba(0,229,255,0.14), transparent 55%),
    radial-gradient(circle at 88% 6%, rgba(170,85,255,0.15), transparent 55%),
    linear-gradient(180deg, #04050d 0%, #080719 46%, #120a30 100%);
  background-repeat: no-repeat;
  background-size: cover;
  min-height: 100%;
}

:root {
  /* ── Obsidian + Antique Gold palette ── */
  --bg-deep:     #07090E;
  --bg-mid:      #0D1118;
  --bg-panel:    #131924;
  --gold:        #C8A84B;
  --gold-bright: #E2C46A;
  --silver:      #A8B0C0;
  --accent:      #5B8CCC;
  --pink:        #C8A84B;       /* repurposed to gold for gradients */
  --pink-bright: #E2C46A;
  --lavender:    #7A90BC;
  --skyblue:     #6AACED;
  --text-light:  #EDE8DC;       /* warm ivory */
  --text-dim:    #7A7060;       /* warm gray */
  --line:        rgba(200,168,75,0.18);   /* subtle gold border */
  --font-display: 'Baloo 2', sans-serif;
  --font-body:    'Quicksand', sans-serif;
  --font-mono:    'IBM Plex Mono', monospace;

  /* ── Global tier colors ── */
  --tier-common:     #FFFFFF;
  --tier-uncommon:   #60C8FF;
  --tier-rare:       #C490FF;
  --tier-foil-chase: #FFD15C;
}

/* Tier CSS classes: any .tier-X element inherits the correct --tier-glow-color */
.tier-common     { --tier-glow-color: var(--tier-common);     }
.tier-uncommon   { --tier-glow-color: var(--tier-uncommon);   }
.tier-rare       { --tier-glow-color: var(--tier-rare);       }
.tier-foil-chase { --tier-glow-color: var(--tier-foil-chase); }

* { box-sizing: border-box; }

body {
  margin: 0;
  background: transparent;   /* base gradient now lives on <html> — propagated to
                                the viewport canvas, so it's mobile-safe + identical
                                on every device (no background-attachment:fixed) */
  color: var(--text-light);
  font-family: var(--font-body);
  font-weight: 500;
  min-height: 100vh;
  position: relative;
  overflow-x: clip;   /* never expose a horizontal scrollbar from a transient wide
                         layer (rip FX, off-screen card fly-in) */
}


.skip-link {
  position: absolute;
  left: -999px;
  width: 1px;
  height: 1px;
  overflow: hidden;     /* clamp to a point so it can't widen the page canvas */
}
.skip-link:focus {
  left: 8px;
  top: 8px;
  width: auto;
  height: auto;
  overflow: visible;
  background: var(--text-light);
  color: var(--bg-deep);
  padding: 6px 12px;
  z-index: 999;
}

/* ---------- header ---------- */
.ledger-bar {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;   /* one integrated, centered control cluster */
  padding: 8px 16px;
  border-bottom: 1px solid var(--line);
  background: rgba(42, 27, 87, 0.6);
  backdrop-filter: blur(6px);
  flex-wrap: wrap;
  gap: 10px;
}
/* Minimalistic: the build version isn't part of the user-facing controls. */
.app-version { display: none; }

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
}
.brand-icon {
  font-size: 22px;
  color: var(--gold);
  filter: drop-shadow(0 0 6px rgba(255,209,92,0.6));
}
.brand h1 {
  font-family: var(--font-display);
  font-size: 22px;
  letter-spacing: 0.01em;
  margin: 0;
  color: var(--text-light);
  font-weight: 700;
}

.balance {
  display: flex;
  align-items: center;
  gap: 10px;
  border: 1px solid var(--line);
  background: rgba(255, 143, 203, 0.08);
  padding: 8px 18px;
  border-radius: 999px;
}
.balance .label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.balance .amount {
  font-family: var(--font-display);
  font-size: 19px;
  color: var(--gold);
  font-weight: 700;
}

nav.tabs {
  display: flex;
  gap: 6px;
}
nav.tabs a {
  font-family: var(--font-display);
  font-size: 13px;
  text-decoration: none;
  color: var(--text-dim);
  padding: 8px 16px;
  border-radius: 999px;
  border: 1px solid transparent;
  font-weight: 600;
}
nav.tabs a.active, nav.tabs a:hover {
  color: var(--text-light);
  background: rgba(255, 143, 203, 0.16);
  border-color: var(--line);
}

/* ---------- main layout ---------- */
main {
  position: relative;
  z-index: 1;
  max-width: 1360px;
  margin: 0 auto;
  padding: 0 24px 80px;   /* no top edge — the game viewer sits topmost */
}
/* Gallery view keeps a little breathing room up top (attract mode does not). */
#galleryView { padding-top: 24px; }

.hall-title {
  text-align: center;
  margin-bottom: 28px;
}
.hall-title .eyebrow {
  display: block;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--pink-bright);
  margin-bottom: 6px;
}
.hall-title h2 {
  font-family: var(--font-display);
  font-size: 30px;
  margin: 0;
  font-weight: 700;
  color: var(--text-light);
}

/* floor-layout shows only during pull animation — pack + reveal centered */
.floor-layout {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0 24px;
  max-width: 600px;
  margin: 0 auto;
}
/* pool-panel inside floor-layout — compact strip below the animation */
.floor-layout .pool-panel {
  width: 100%;
  max-width: 500px;
}

/* ---------- pool panel (left): single-pool odds card ---------- */
.pool-panel {
  display: flex;
  flex-direction: column;
}

.odds-card {
  border: 1px solid var(--line);
  background: linear-gradient(160deg, rgba(255,255,255,0.04), transparent), var(--bg-panel);
  border-radius: 18px;
  padding: 24px 22px;
}

.odds-desc {
  font-size: 13.5px;
  color: var(--text-dim);
  margin: 0 0 18px;
  line-height: 1.5;
}

.odds-stat {
  text-align: center;
  margin-bottom: 14px;
}
.odds-stat-number {
  font-family: var(--font-display);
  font-size: 30px;
  font-weight: 700;
  color: var(--pink-bright);
  margin-right: 8px;
}
.odds-stat-label {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.05em;
  color: var(--text-dim);
}

.overview-bar-track {
  display: flex;
  height: 14px;
  border-radius: 999px;
  overflow: hidden;
  background: rgba(255,255,255,0.08);
}
.overview-bar-track span {
  display: block;
  height: 100%;
  transition: width 0.5s ease;
}
.overview-legend {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 14px;
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: var(--text-dim);
}
.overview-legend .legend-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.overview-legend .dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-right: 6px;
  border-radius: 50%;
  vertical-align: middle;
}
.overview-legend .legend-empty {
  opacity: 0.45;
}

.odds-payout {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-dim);
  text-align: center;
}

/* ---------- stage panel (right) ---------- */
.stage-panel {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.stage-frame {
  position: relative;
  border: 1px solid var(--line);
  border-radius: 22px;
  background: radial-gradient(circle at 50% 30%, rgba(183,156,255,0.12), transparent 60%), var(--bg-panel);
  min-height: 460px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.stage-sparkle-bg {
  position: absolute;
  inset: 0;
  background-image:
    radial-gradient(1.4px 1.4px at 15% 25%, #fff 100%, transparent),
    radial-gradient(1.4px 1.4px at 75% 15%, #fff 100%, transparent),
    radial-gradient(1px 1px at 55% 80%, #fff 100%, transparent),
    radial-gradient(1.4px 1.4px at 35% 65%, #fff 100%, transparent),
    radial-gradient(1px 1px at 90% 50%, #fff 100%, transparent);
  opacity: 0.4;
  pointer-events: none;
}

/* idle-stage: see the full-width definition in the carousel section below */

.idle-pack {
  position: relative;
  width: 140px;
  height: 180px;
  margin: 0 auto 20px;
  animation: idleBob 3.2s ease-in-out infinite;
}
@keyframes idleBob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-10px); }
}
.idle-pack-glow {
  position: absolute;
  inset: -30px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--idle-color, var(--pink)) 0%, transparent 70%);
  opacity: 0.35;
  animation: idleGlow 3.2s ease-in-out infinite;
  filter: blur(4px);
}
@keyframes idleGlow {
  0%, 100% { opacity: 0.25; transform: scale(0.92); }
  50% { opacity: 0.45; transform: scale(1.05); }
}
.idle-pack-body {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 14px;
  border: 2px solid var(--idle-color, var(--pink));
  background: linear-gradient(145deg, rgba(255,255,255,0.08), transparent 60%), var(--bg-mid);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 30px color-mix(in srgb, var(--idle-color, var(--pink)) 30%, transparent);
}
.idle-pack-star {
  font-size: 36px;
  color: var(--idle-color, var(--pink));
  animation: idleSpin 6s linear infinite;
  display: inline-block;
  filter: drop-shadow(0 0 10px color-mix(in srgb, var(--idle-color, var(--pink)) 60%, transparent));
}
@keyframes idleSpin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
.idle-caption {
  font-family: var(--font-display);
  font-size: 15px;
  color: var(--text-dim);
  font-weight: 600;
}

/* sealed pack shown during the rip sequence */
.pack-shell {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 200px;
  height: 280px;
  z-index: 4;
  display: none;
}
.pack-shell.active { display: block; }
.pack-half {
  position: absolute;
  left: 0;
  width: 100%;
  height: 50%;
  background: linear-gradient(135deg, #3a2470, #2a1b57 45%, #4a2e7a);
  border: 2px solid var(--pack-color, var(--pink));
}
.pack-half.top {
  top: 0;
  border-bottom: none;
  border-radius: 14px 14px 0 0;
  background-image: repeating-linear-gradient(45deg, rgba(200,168,75,0.12) 0, rgba(200,168,75,0.12) 6px, transparent 6px, transparent 14px);
}
.pack-half.bottom {
  bottom: 0;
  border-top: none;
  border-radius: 0 0 14px 14px;
  background-image: repeating-linear-gradient(-45deg, rgba(200,168,75,0.12) 0, rgba(200,168,75,0.12) 6px, transparent 6px, transparent 14px);
}
.pack-seal {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, var(--pink-bright), var(--pack-color, var(--pink)) 70%);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: 13px;
  color: var(--bg-deep);
  font-weight: 700;
  box-shadow: 0 0 18px color-mix(in srgb, var(--pack-color, var(--pink)) 60%, transparent);
}

.pack-shell.shaking {
  animation: packShake var(--shake-duration, 0.55s) ease-in-out;
}
@keyframes packShake {
  0%, 100% { transform: translate(-50%, -50%) rotate(0); }
  15% { transform: translate(calc(-50% - 6px), -50%) rotate(-2deg); }
  30% { transform: translate(calc(-50% + 6px), -50%) rotate(2deg); }
  45% { transform: translate(calc(-50% - 7px), -50%) rotate(-2.5deg); }
  60% { transform: translate(calc(-50% + 7px), -50%) rotate(2.5deg); }
  75% { transform: translate(calc(-50% - 4px), -50%) rotate(-1.5deg); }
  90% { transform: translate(calc(-50% + 4px), -50%) rotate(1.5deg); }
}
.pack-shell.tearing .pack-half.top {
  animation: tearUp 0.45s ease-in forwards;
}
.pack-shell.tearing .pack-half.bottom {
  animation: tearDown 0.45s ease-in forwards;
}
.pack-shell.tearing .pack-seal {
  animation: sealPop 0.3s ease-in forwards;
}
@keyframes tearUp { to { transform: translateY(-160px) rotate(-12deg); opacity: 0; } }
@keyframes tearDown { to { transform: translateY(160px) rotate(12deg); opacity: 0; } }
@keyframes sealPop { to { transform: translate(-50%, -50%) scale(2.4); opacity: 0; } }

/* card reveal */
.reveal-zone {
  position: relative;
  z-index: 3;
  text-align: center;
  perspective: 1200px;
  display: none;
}
.reveal-zone.active { display: block; }

.tier-banner {
  font-family: var(--font-display);
  font-size: 15px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-bottom: 16px;
  opacity: 0;
  font-weight: 700;
}
.tier-banner.show { animation: bannerIn 0.4s ease forwards; }
@keyframes bannerIn {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: translateY(0); }
}

.card-flip-wrap {
  width: 200px;
  height: 280px;
  margin: 0 auto;
  cursor: pointer;
  opacity: 0;
  transform: scale(0.4);
}
.card-flip-wrap.revealed {
  animation: cardPopIn 0.5s cubic-bezier(.2,1.4,.4,1) forwards;
}
@keyframes cardPopIn {
  0% { opacity: 0; transform: scale(0.3) rotate(-8deg); }
  60% { opacity: 1; transform: scale(1.08) rotate(2deg); }
  100% { opacity: 1; transform: scale(1) rotate(0); }
}
.card-flip-inner {
  position: relative;
  width: 100%;
  height: 100%;
  transition: transform 0.6s cubic-bezier(.2,.8,.2,1);
  transform-style: preserve-3d;
}
.card-flip-inner.flipped { transform: rotateY(180deg); }

.card-face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  border-radius: 12px;
  overflow: hidden;
  border: 2px solid var(--pink);
  background: var(--bg-mid);
  display: flex;
  align-items: center;
  justify-content: center;
}
.card-face img { width: 100%; height: 100%; object-fit: contain; display: block; }
.card-face .img-broken {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-dim);
  text-align: center;
  padding: 0 16px;
}
.card-face.back { transform: rotateY(180deg); }
.card-face.placeholder-back {
  background:
    repeating-linear-gradient(45deg, rgba(200,168,75,0.08) 0, rgba(200,168,75,0.08) 8px, transparent 8px, transparent 16px),
    var(--bg-mid);
}

.card-flip-wrap.tier-uncommon .card-face {
  border-color: var(--lavender);
  box-shadow: 0 0 0 1px var(--lavender), 0 0 16px rgba(183,156,255,0.3);
}
.card-flip-wrap.tier-rare .card-face {
  border-color: var(--skyblue);
  box-shadow: 0 0 0 1px var(--skyblue), 0 0 26px rgba(126,212,255,0.35);
}
.card-flip-wrap.tier-foil-chase .card-face {
  border-color: var(--gold);
  box-shadow: 0 0 0 2px var(--gold), 0 0 54px rgba(255,209,92,0.5);
  animation: foilChasePulse 1.8s ease-in-out infinite;
}
.card-flip-wrap.tier-foil-chase .card-face::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(115deg, transparent 30%, rgba(235,255,245,0.38) 48%, rgba(90,255,175,0.42) 52%, transparent 70%);
  background-size: 250% 250%;
  animation: holoSweep 2s ease-in-out infinite;
  pointer-events: none;
}
@keyframes foilChasePulse {
  0%, 100% { box-shadow: 0 0 0 2px var(--gold), 0 0 30px rgba(255,209,92,0.35); }
  50% { box-shadow: 0 0 0 2px var(--gold), 0 0 60px rgba(255,209,92,0.7); }
}
@keyframes holoSweep {
  0% { background-position: 0% 0%; }
  50% { background-position: 100% 100%; }
  100% { background-position: 0% 0%; }
}

.reveal-cardname {
  margin-top: 16px;
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 700;
  color: var(--text-light);
}
.reveal-hint {
  margin-top: 5px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-dim);
}

/* flash + particles, scoped to the stage frame */
.flash-pulse {
  position: absolute;
  inset: 0;
  background: #fff;
  opacity: 0;
  pointer-events: none;
  z-index: 5;
  border-radius: 22px;
}
.flash-pulse.fire { animation: flashFire 0.5s ease-out; }
@keyframes flashFire {
  0% { opacity: 0; }
  12% { opacity: 0.7; }
  100% { opacity: 0; }
}
.particle-field {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 9800;
  overflow: hidden;
  isolation: isolate;   /* separate compositing layer — prevents pixel bleed */
}
.particle {
  position: absolute;
  border-radius: 50%;
  will-change: transform, opacity;
}

/* console below the stage */
.stage-console {
  text-align: center;
}
.pull-lever {
  font-family: var(--font-display);
  font-size: 17px;
  letter-spacing: 0.02em;
  background: linear-gradient(180deg, var(--pink-bright), var(--pink));
  color: var(--bg-deep);
  border: none;
  padding: 15px 44px;
  cursor: pointer;
  border-radius: 999px;
  font-weight: 700;
  transition: filter 0.15s ease, transform 0.1s ease;
  box-shadow: 0 6px 20px rgba(255,143,203,0.3);
}
.pull-lever:hover:not(:disabled) { filter: brightness(1.08); transform: translateY(-1px); }
.pull-lever:active:not(:disabled) { transform: translateY(1px); }
.pull-lever:disabled { opacity: 0.4; cursor: not-allowed; box-shadow: none; }

.hint {
  margin-top: 10px;
  font-size: 12.5px;
  color: var(--text-dim);
}
.error-msg {
  margin-top: 10px;
  font-size: 13px;
  color: #FF9FB0;
}

.reveal-actions {
  margin-top: 14px;
  display: none;
  gap: 12px;
  justify-content: center;
}
.reveal-actions.active { display: flex; }
.btn-ghost {
  font-family: var(--font-display);
  font-size: 13px;
  background: transparent;
  border: 1px solid var(--line);
  color: var(--text-light);
  padding: 10px 20px;
  cursor: pointer;
  border-radius: 999px;
  font-weight: 600;
  transition: border-color .14s ease, color .14s ease, transform .1s ease;
}
.btn-ghost:hover { border-color: var(--pink); color: var(--pink-bright); transform: translateY(-1px); }
.btn-ghost:active { transform: translateY(1px); }
.btn-solid {
  font-family: var(--font-display);
  font-size: 13px;
  background: var(--gold);
  color: var(--bg-deep);
  border: none;
  padding: 10px 20px;
  cursor: pointer;
  border-radius: 999px;
  font-weight: 700;
  transition: filter .14s ease, transform .1s ease;
}
.btn-solid:hover { filter: brightness(1.08); transform: translateY(-1px); }
.btn-solid:active { transform: translateY(1px); }

/* ---------- cert lookup ---------- */
.cert-lookup-section {
  margin-top: 48px;
  margin-bottom: 4px;
}
.cert-lookup-inner {
  border: 1px solid var(--line);
  border-radius: 18px;
  padding: 22px 26px;
  background: rgba(255,255,255,0.025);
}
.cert-lookup-label {
  display: block;
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 700;
  color: var(--text-light);
  margin-bottom: 12px;
}
.cert-lookup-row {
  display: flex;
  gap: 10px;
  max-width: 420px;
}
.cert-input {
  flex: 1;
  background: var(--bg-mid);
  border: 1px solid var(--line);
  color: var(--text-light);
  padding: 11px 14px;
  font-family: var(--font-mono);
  font-size: 14px;
  border-radius: 10px;
}
.cert-input:focus {
  outline: 2px solid var(--pink-bright);
  outline-offset: 1px;
}
.cert-btn {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 700;
  background: linear-gradient(180deg, var(--pink-bright), var(--pink));
  color: var(--bg-deep);
  border: none;
  padding: 11px 22px;
  border-radius: 10px;
  cursor: pointer;
  white-space: nowrap;
}
.cert-btn:hover { filter: brightness(1.08); }
.cert-btn:disabled { opacity: 0.5; cursor: not-allowed; }

.cert-result {
  margin-top: 16px;
}
.cert-card {
  display: grid;
  grid-template-columns: 110px 1fr;
  gap: 18px;
  align-items: start;
}
.cert-card img {
  width: 110px;
  object-fit: contain;
  border-radius: 10px;
  border: 2px solid var(--cert-color, var(--pink));
  box-shadow: 0 0 16px color-mix(in srgb, var(--cert-color, var(--pink)) 30%, transparent);
}
.cert-card-name {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 700;
  color: var(--text-light);
  margin-bottom: 4px;
}
.cert-card-tier {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin-bottom: 10px;
}
.cert-card-rows {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.cert-card-row {
  display: flex;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 12.5px;
}
.cert-card-row .label {
  color: var(--text-dim);
  width: 110px;
  flex-shrink: 0;
}
.cert-card-row .val {
  color: var(--text-light);
  font-weight: 500;
}
.cert-card-row .val.value-num {
  color: var(--gold);
  font-size: 14px;
  font-weight: 700;
}
.cert-img-placeholder {
  width: 110px;
  border: 2px solid var(--cert-color, var(--pink));
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 700;
  color: var(--cert-color, var(--pink));
  background: rgba(255,255,255,0.03);
}
.cert-local-match {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
  flex-wrap: wrap;
}
.cert-local-tag {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--skyblue);
  font-weight: 600;
}
.cert-local-value {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--gold);
  font-weight: 700;
}
.cert-pulled-tag {
  font-family: var(--font-mono);
  font-size: 10px;
  background: rgba(255,159,176,0.15);
  color: #FF9FB0;
  padding: 2px 8px;
  border-radius: 999px;
}
.cert-avail-tag {
  font-family: var(--font-mono);
  font-size: 10px;
  background: rgba(126,212,255,0.12);
  color: var(--skyblue);
  padding: 2px 8px;
  border-radius: 999px;
}
.cert-not-in-vault {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-dim);
  margin-bottom: 10px;
}

.cert-not-found {
  font-family: var(--font-mono);
  font-size: 12.5px;
  color: #FF9FB0;
}
@media (max-width: 500px) {
  .cert-card { grid-template-columns: 80px 1fr; }
  .cert-card img, .cert-img-placeholder { width: 80px; }
}

/* ---------- top hits showcase ---------- */
.top-hits-section { margin-top: 48px; }
/* Chase Cards section heading */
.chase-heading {
  background: linear-gradient(135deg, #FFD15C 0%, #F0F8FF 40%, #C490FF 70%, #FFD15C 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  background-size: 200%;
  animation: chaseGradientShift 5s linear infinite;
  font-size: 22px;
}
@keyframes chaseGradientShift {
  0%   { background-position: 0% 50%; }
  50%  { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}

.top-hits-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 12px;
  overflow: visible;
}
/* Unified floor card tile — identical structure to admin .lib-card */
.floor-card {
  border: 1px solid color-mix(in srgb, var(--tier-glow-color, #C8A84B) 65%, transparent);
  background: var(--bg-deep);
  border-radius: 10px;
  padding: 0;
  position: relative;
  cursor: pointer;
  overflow: hidden;
  /* No fixed aspect-ratio — image height defines card height */
}
.floor-card img {
  width: 100%;
  height: auto;          /* natural height — image defines the card height, no cropping */
  object-fit: contain;
  object-position: center top;
  display: block;
}
/* (duplicate render-blocking @import removed — fonts now load from index.html head) */

/* Root-level guard: never expose a horizontal scrollbar from a transient wide
   layer (rip FX, off-screen card fly-in animations).
   The base page background lives HERE (on the root) rather than on <body>: the
   root's background is propagated across the whole viewport canvas on EVERY
   device, so it's identical on mobile and desktop. (body used
   background-attachment:fixed, which most mobile browsers ignore — that left the
   base white behind the lights on phones.) */
html {
  overflow-x: clip;
  /* CYBERPUNK CASINO base — near-black blue/violet with a neon HORIZON glow
     (magenta floor, cyan + purple sky) so the neon lights pop. On the root so it's
     propagated across the viewport canvas, mobile-safe + identical everywhere. */
  background-color: #04050d;     /* solid floor — never white */
  background-image:
    radial-gradient(ellipse 130% 58% at 50% 102%, rgba(255,45,155,0.20), transparent 62%),
    radial-gradient(circle at 12% -6%, rgba(0,229,255,0.14), transparent 55%),
    radial-gradient(circle at 88% 6%, rgba(170,85,255,0.15), transparent 55%),
    linear-gradient(180deg, #04050d 0%, #080719 46%, #120a30 100%);
  background-repeat: no-repeat;
  background-size: cover;
  min-height: 100%;
}

:root {
  /* ── Obsidian + Antique Gold palette ── */
  --bg-deep:     #07090E;
  --bg-mid:      #0D1118;
  --bg-panel:    #131924;
  --gold:        #C8A84B;
  --gold-bright: #E2C46A;
  --silver:      #A8B0C0;
  --accent:      #5B8CCC;
  --pink:        #C8A84B;       /* repurposed to gold for gradients */
  --pink-bright: #E2C46A;
  --lavender:    #7A90BC;
  --skyblue:     #6AACED;
  --text-light:  #EDE8DC;       /* warm ivory */
  --text-dim:    #7A7060;       /* warm gray */
  --line:        rgba(200,168,75,0.18);   /* subtle gold border */
  --font-display: 'Baloo 2', sans-serif;
  --font-body:    'Quicksand', sans-serif;
  --font-mono:    'IBM Plex Mono', monospace;

  /* ── Global tier colors ── */
  --tier-common:     #FFFFFF;
  --tier-uncommon:   #60C8FF;
  --tier-rare:       #C490FF;
  --tier-foil-chase: #FFD15C;
}

/* Tier CSS classes: any .tier-X element inherits the correct --tier-glow-color */
.tier-common     { --tier-glow-color: var(--tier-common);     }
.tier-uncommon   { --tier-glow-color: var(--tier-uncommon);   }
.tier-rare       { --tier-glow-color: var(--tier-rare);       }
.tier-foil-chase { --tier-glow-color: var(--tier-foil-chase); }

* { box-sizing: border-box; }

body {
  margin: 0;
  background: transparent;   /* base gradient now lives on <html> — propagated to
                                the viewport canvas, so it's mobile-safe + identical
                                on every device (no background-attachment:fixed) */
  color: var(--text-light);
  font-family: var(--font-body);
  font-weight: 500;
  min-height: 100vh;
  position: relative;
  overflow-x: clip;   /* never expose a horizontal scrollbar from a transient wide
                         layer (rip FX, off-screen card fly-in) */
}


.skip-link {
  position: absolute;
  left: -999px;
  width: 1px;
  height: 1px;
  overflow: hidden;     /* clamp to a point so it can't widen the page canvas */
}
.skip-link:focus {
  left: 8px;
  top: 8px;
  width: auto;
  height: auto;
  overflow: visible;
  background: var(--text-light);
  color: var(--bg-deep);
  padding: 6px 12px;
  z-index: 999;
}

/* ---------- header ---------- */
.ledger-bar {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;   /* one integrated, centered control cluster */
  padding: 8px 16px;
  border-bottom: 1px solid var(--line);
  background: rgba(42, 27, 87, 0.6);
  backdrop-filter: blur(6px);
  flex-wrap: wrap;
  gap: 10px;
}
/* Minimalistic: the build version isn't part of the user-facing controls. */
.app-version { display: none; }

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
}
.brand-icon {
  font-size: 22px;
  color: var(--gold);
  filter: drop-shadow(0 0 6px rgba(255,209,92,0.6));
}
.brand h1 {
  font-family: var(--font-display);
  font-size: 22px;
  letter-spacing: 0.01em;
  margin: 0;
  color: var(--text-light);
  font-weight: 700;
}

.balance {
  display: flex;
  align-items: center;
  gap: 10px;
  border: 1px solid var(--line);
  background: rgba(255, 143, 203, 0.08);
  padding: 8px 18px;
  border-radius: 999px;
}
.balance .label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.balance .amount {
  font-family: var(--font-display);
  font-size: 19px;
  color: var(--gold);
  font-weight: 700;
}

nav.tabs {
  display: flex;
  gap: 6px;
}
nav.tabs a {
  font-family: var(--font-display);
  font-size: 13px;
  text-decoration: none;
  color: var(--text-dim);
  padding: 8px 16px;
  border-radius: 999px;
  border: 1px solid transparent;
  font-weight: 600;
}
nav.tabs a.active, nav.tabs a:hover {
  color: var(--text-light);
  background: rgba(255, 143, 203, 0.16);
  border-color: var(--line);
}

/* ---------- main layout ---------- */
main {
  position: relative;
  z-index: 1;
  max-width: 1360px;
  margin: 0 auto;
  padding: 0 24px 80px;   /* no top edge — the game viewer sits topmost */
}
/* Gallery view keeps a little breathing room up top (attract mode does not). */
#galleryView { padding-top: 24px; }

.hall-title {
  text-align: center;
  margin-bottom: 28px;
}
.hall-title .eyebrow {
  display: block;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--pink-bright);
  margin-bottom: 6px;
}
.hall-title h2 {
  font-family: var(--font-display);
  font-size: 30px;
  margin: 0;
  font-weight: 700;
  color: var(--text-light);
}

/* floor-layout shows only during pull animation — pack + reveal centered */
.floor-layout {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0 24px;
  max-width: 600px;
  margin: 0 auto;
}
/* pool-panel inside floor-layout — compact strip below the animation */
.floor-layout .pool-panel {
  width: 100%;
  max-width: 500px;
}

/* ---------- pool panel (left): single-pool odds card ---------- */
.pool-panel {
  display: flex;
  flex-direction: column;
}

.odds-card {
  border: 1px solid var(--line);
  background: linear-gradient(160deg, rgba(255,255,255,0.04), transparent), var(--bg-panel);
  border-radius: 18px;
  padding: 24px 22px;
}

.odds-desc {
  font-size: 13.5px;
  color: var(--text-dim);
  margin: 0 0 18px;
  line-height: 1.5;
}

.odds-stat {
  text-align: center;
  margin-bottom: 14px;
}
.odds-stat-number {
  font-family: var(--font-display);
  font-size: 30px;
  font-weight: 700;
  color: var(--pink-bright);
  margin-right: 8px;
}
.odds-stat-label {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.05em;
  color: var(--text-dim);
}

.overview-bar-track {
  display: flex;
  height: 14px;
  border-radius: 999px;
  overflow: hidden;
  background: rgba(255,255,255,0.08);
}
.overview-bar-track span {
  display: block;
  height: 100%;
  transition: width 0.5s ease;
}
.overview-legend {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 14px;
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: var(--text-dim);
}
.overview-legend .legend-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.overview-legend .dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-right: 6px;
  border-radius: 50%;
  vertical-align: middle;
}
.overview-legend .legend-empty {
  opacity: 0.45;
}

.odds-payout {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-dim);
  text-align: center;
}

/* ---------- stage panel (right) ---------- */
.stage-panel {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.stage-frame {
  position: relative;
  border: 1px solid var(--line);
  border-radius: 22px;
  background: radial-gradient(circle at 50% 30%, rgba(183,156,255,0.12), transparent 60%), var(--bg-panel);
  min-height: 460px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.stage-sparkle-bg {
  position: absolute;
  inset: 0;
  background-image:
    radial-gradient(1.4px 1.4px at 15% 25%, #fff 100%, transparent),
    radial-gradient(1.4px 1.4px at 75% 15%, #fff 100%, transparent),
    radial-gradient(1px 1px at 55% 80%, #fff 100%, transparent),
    radial-gradient(1.4px 1.4px at 35% 65%, #fff 100%, transparent),
    radial-gradient(1px 1px at 90% 50%, #fff 100%, transparent);
  opacity: 0.4;
  pointer-events: none;
}

/* idle-stage: see the full-width definition in the carousel section below */

.idle-pack {
  position: relative;
  width: 140px;
  height: 180px;
  margin: 0 auto 20px;
  animation: idleBob 3.2s ease-in-out infinite;
}
@keyframes idleBob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-10px); }
}
.idle-pack-glow {
  position: absolute;
  inset: -30px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--idle-color, var(--pink)) 0%, transparent 70%);
  opacity: 0.35;
  animation: idleGlow 3.2s ease-in-out infinite;
  filter: blur(4px);
}
@keyframes idleGlow {
  0%, 100% { opacity: 0.25; transform: scale(0.92); }
  50% { opacity: 0.45; transform: scale(1.05); }
}
.idle-pack-body {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 14px;
  border: 2px solid var(--idle-color, var(--pink));
  background: linear-gradient(145deg, rgba(255,255,255,0.08), transparent 60%), var(--bg-mid);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 30px color-mix(in srgb, var(--idle-color, var(--pink)) 30%, transparent);
}
.idle-pack-star {
  font-size: 36px;
  color: var(--idle-color, var(--pink));
  animation: idleSpin 6s linear infinite;
  display: inline-block;
  filter: drop-shadow(0 0 10px color-mix(in srgb, var(--idle-color, var(--pink)) 60%, transparent));
}
@keyframes idleSpin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
.idle-caption {
  font-family: var(--font-display);
  font-size: 15px;
  color: var(--text-dim);
  font-weight: 600;
}

/* sealed pack shown during the rip sequence */
.pack-shell {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 200px;
  height: 280px;
  z-index: 4;
  display: none;
}
.pack-shell.active { display: block; }
.pack-half {
  position: absolute;
  left: 0;
  width: 100%;
  height: 50%;
  background: linear-gradient(135deg, #3a2470, #2a1b57 45%, #4a2e7a);
  border: 2px solid var(--pack-color, var(--pink));
}
.pack-half.top {
  top: 0;
  border-bottom: none;
  border-radius: 14px 14px 0 0;
  background-image: repeating-linear-gradient(45deg, rgba(200,168,75,0.12) 0, rgba(200,168,75,0.12) 6px, transparent 6px, transparent 14px);
}
.pack-half.bottom {
  bottom: 0;
  border-top: none;
  border-radius: 0 0 14px 14px;
  background-image: repeating-linear-gradient(-45deg, rgba(200,168,75,0.12) 0, rgba(200,168,75,0.12) 6px, transparent 6px, transparent 14px);
}
.pack-seal {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, var(--pink-bright), var(--pack-color, var(--pink)) 70%);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: 13px;
  color: var(--bg-deep);
  font-weight: 700;
  box-shadow: 0 0 18px color-mix(in srgb, var(--pack-color, var(--pink)) 60%, transparent);
}

.pack-shell.shaking {
  animation: packShake var(--shake-duration, 0.55s) ease-in-out;
}
@keyframes packShake {
  0%, 100% { transform: translate(-50%, -50%) rotate(0); }
  15% { transform: translate(calc(-50% - 6px), -50%) rotate(-2deg); }
  30% { transform: translate(calc(-50% + 6px), -50%) rotate(2deg); }
  45% { transform: translate(calc(-50% - 7px), -50%) rotate(-2.5deg); }
  60% { transform: translate(calc(-50% + 7px), -50%) rotate(2.5deg); }
  75% { transform: translate(calc(-50% - 4px), -50%) rotate(-1.5deg); }
  90% { transform: translate(calc(-50% + 4px), -50%) rotate(1.5deg); }
}
.pack-shell.tearing .pack-half.top {
  animation: tearUp 0.45s ease-in forwards;
}
.pack-shell.tearing .pack-half.bottom {
  animation: tearDown 0.45s ease-in forwards;
}
.pack-shell.tearing .pack-seal {
  animation: sealPop 0.3s ease-in forwards;
}
@keyframes tearUp { to { transform: translateY(-160px) rotate(-12deg); opacity: 0; } }
@keyframes tearDown { to { transform: translateY(160px) rotate(12deg); opacity: 0; } }
@keyframes sealPop { to { transform: translate(-50%, -50%) scale(2.4); opacity: 0; } }

/* card reveal */
.reveal-zone {
  position: relative;
  z-index: 3;
  text-align: center;
  perspective: 1200px;
  display: none;
}
.reveal-zone.active { display: block; }

.tier-banner {
  font-family: var(--font-display);
  font-size: 15px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-bottom: 16px;
  opacity: 0;
  font-weight: 700;
}
.tier-banner.show { animation: bannerIn 0.4s ease forwards; }
@keyframes bannerIn {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: translateY(0); }
}

.card-flip-wrap {
  width: 200px;
  height: 280px;
  margin: 0 auto;
  cursor: pointer;
  opacity: 0;
  transform: scale(0.4);
}
.card-flip-wrap.revealed {
  animation: cardPopIn 0.5s cubic-bezier(.2,1.4,.4,1) forwards;
}
@keyframes cardPopIn {
  0% { opacity: 0; transform: scale(0.3) rotate(-8deg); }
  60% { opacity: 1; transform: scale(1.08) rotate(2deg); }
  100% { opacity: 1; transform: scale(1) rotate(0); }
}
.card-flip-inner {
  position: relative;
  width: 100%;
  height: 100%;
  transition: transform 0.6s cubic-bezier(.2,.8,.2,1);
  transform-style: preserve-3d;
}
.card-flip-inner.flipped { transform: rotateY(180deg); }

.card-face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  border-radius: 12px;
  overflow: hidden;
  border: 2px solid var(--pink);
  background: var(--bg-mid);
  display: flex;
  align-items: center;
  justify-content: center;
}
.card-face img { width: 100%; height: 100%; object-fit: contain; display: block; }
.card-face .img-broken {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-dim);
  text-align: center;
  padding: 0 16px;
}
.card-face.back { transform: rotateY(180deg); }
.card-face.placeholder-back {
  background:
    repeating-linear-gradient(45deg, rgba(200,168,75,0.08) 0, rgba(200,168,75,0.08) 8px, transparent 8px, transparent 16px),
    var(--bg-mid);
}

.card-flip-wrap.tier-uncommon .card-face {
  border-color: var(--lavender);
  box-shadow: 0 0 0 1px var(--lavender), 0 0 16px rgba(183,156,255,0.3);
}
.card-flip-wrap.tier-rare .card-face {
  border-color: var(--skyblue);
  box-shadow: 0 0 0 1px var(--skyblue), 0 0 26px rgba(126,212,255,0.35);
}
.card-flip-wrap.tier-foil-chase .card-face {
  border-color: var(--gold);
  box-shadow: 0 0 0 2px var(--gold), 0 0 54px rgba(255,209,92,0.5);
  animation: foilChasePulse 1.8s ease-in-out infinite;
}
.card-flip-wrap.tier-foil-chase .card-face::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(115deg, transparent 30%, rgba(235,255,245,0.38) 48%, rgba(90,255,175,0.42) 52%, transparent 70%);
  background-size: 250% 250%;
  animation: holoSweep 2s ease-in-out infinite;
  pointer-events: none;
}
@keyframes foilChasePulse {
  0%, 100% { box-shadow: 0 0 0 2px var(--gold), 0 0 30px rgba(255,209,92,0.35); }
  50% { box-shadow: 0 0 0 2px var(--gold), 0 0 60px rgba(255,209,92,0.7); }
}
@keyframes holoSweep {
  0% { background-position: 0% 0%; }
  50% { background-position: 100% 100%; }
  100% { background-position: 0% 0%; }
}

.reveal-cardname {
  margin-top: 16px;
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 700;
  color: var(--text-light);
}
.reveal-hint {
  margin-top: 5px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-dim);
}

/* flash + particles, scoped to the stage frame */
.flash-pulse {
  position: absolute;
  inset: 0;
  background: #fff;
  opacity: 0;
  pointer-events: none;
  z-index: 5;
  border-radius: 22px;
}
.flash-pulse.fire { animation: flashFire 0.5s ease-out; }
@keyframes flashFire {
  0% { opacity: 0; }
  12% { opacity: 0.7; }
  100% { opacity: 0; }
}
.particle-field {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 9800;
  overflow: hidden;
  isolation: isolate;   /* separate compositing layer — prevents pixel bleed */
}
.particle {
  position: absolute;
  border-radius: 50%;
  will-change: transform, opacity;
}

/* console below the stage */
.stage-console {
  text-align: center;
}
.pull-lever {
  font-family: var(--font-display);
  font-size: 17px;
  letter-spacing: 0.02em;
  background: linear-gradient(180deg, var(--pink-bright), var(--pink));
  color: var(--bg-deep);
  border: none;
  padding: 15px 44px;
  cursor: pointer;
  border-radius: 999px;
  font-weight: 700;
  transition: filter 0.15s ease, transform 0.1s ease;
  box-shadow: 0 6px 20px rgba(255,143,203,0.3);
}
.pull-lever:hover:not(:disabled) { filter: brightness(1.08); transform: translateY(-1px); }
.pull-lever:active:not(:disabled) { transform: translateY(1px); }
.pull-lever:disabled { opacity: 0.4; cursor: not-allowed; box-shadow: none; }

.hint {
  margin-top: 10px;
  font-size: 12.5px;
  color: var(--text-dim);
}
.error-msg {
  margin-top: 10px;
  font-size: 13px;
  color: #FF9FB0;
}

.reveal-actions {
  margin-top: 14px;
  display: none;
  gap: 12px;
  justify-content: center;
}
.reveal-actions.active { display: flex; }
.btn-ghost {
  font-family: var(--font-display);
  font-size: 13px;
  background: transparent;
  border: 1px solid var(--line);
  color: var(--text-light);
  padding: 10px 20px;
  cursor: pointer;
  border-radius: 999px;
  font-weight: 600;
  transition: border-color .14s ease, color .14s ease, transform .1s ease;
}
.btn-ghost:hover { border-color: var(--pink); color: var(--pink-bright); transform: translateY(-1px); }
.btn-ghost:active { transform: translateY(1px); }
.btn-solid {
  font-family: var(--font-display);
  font-size: 13px;
  background: var(--gold);
  color: var(--bg-deep);
  border: none;
  padding: 10px 20px;
  cursor: pointer;
  border-radius: 999px;
  font-weight: 700;
  transition: filter .14s ease, transform .1s ease;
}
.btn-solid:hover { filter: brightness(1.08); transform: translateY(-1px); }
.btn-solid:active { transform: translateY(1px); }

/* ---------- cert lookup ---------- */
.cert-lookup-section {
  margin-top: 48px;
  margin-bottom: 4px;
}
.cert-lookup-inner {
  border: 1px solid var(--line);
  border-radius: 18px;
  padding: 22px 26px;
  background: rgba(255,255,255,0.025);
}
.cert-lookup-label {
  display: block;
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 700;
  color: var(--text-light);
  margin-bottom: 12px;
}
.cert-lookup-row {
  display: flex;
  gap: 10px;
  max-width: 420px;
}
.cert-input {
  flex: 1;
  background: var(--bg-mid);
  border: 1px solid var(--line);
  color: var(--text-light);
  padding: 11px 14px;
  font-family: var(--font-mono);
  font-size: 14px;
  border-radius: 10px;
}
.cert-input:focus {
  outline: 2px solid var(--pink-bright);
  outline-offset: 1px;
}
.cert-btn {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 700;
  background: linear-gradient(180deg, var(--pink-bright), var(--pink));
  color: var(--bg-deep);
  border: none;
  padding: 11px 22px;
  border-radius: 10px;
  cursor: pointer;
  white-space: nowrap;
}
.cert-btn:hover { filter: brightness(1.08); }
.cert-btn:disabled { opacity: 0.5; cursor: not-allowed; }

.cert-result {
  margin-top: 16px;
}
.cert-card {
  display: grid;
  grid-template-columns: 110px 1fr;
  gap: 18px;
  align-items: start;
}
.cert-card img {
  width: 110px;
  object-fit: contain;
  border-radius: 10px;
  border: 2px solid var(--cert-color, var(--pink));
  box-shadow: 0 0 16px color-mix(in srgb, var(--cert-color, var(--pink)) 30%, transparent);
}
.cert-card-name {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 700;
  color: var(--text-light);
  margin-bottom: 4px;
}
.cert-card-tier {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin-bottom: 10px;
}
.cert-card-rows {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.cert-card-row {
  display: flex;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 12.5px;
}
.cert-card-row .label {
  color: var(--text-dim);
  width: 110px;
  flex-shrink: 0;
}
.cert-card-row .val {
  color: var(--text-light);
  font-weight: 500;
}
.cert-card-row .val.value-num {
  color: var(--gold);
  font-size: 14px;
  font-weight: 700;
}
.cert-img-placeholder {
  width: 110px;
  border: 2px solid var(--cert-color, var(--pink));
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 700;
  color: var(--cert-color, var(--pink));
  background: rgba(255,255,255,0.03);
}
.cert-local-match {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
  flex-wrap: wrap;
}
.cert-local-tag {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--skyblue);
  font-weight: 600;
}
.cert-local-value {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--gold);
  font-weight: 700;
}
.cert-pulled-tag {
  font-family: var(--font-mono);
  font-size: 10px;
  background: rgba(255,159,176,0.15);
  color: #FF9FB0;
  padding: 2px 8px;
  border-radius: 999px;
}
.cert-avail-tag {
  font-family: var(--font-mono);
  font-size: 10px;
  background: rgba(126,212,255,0.12);
  color: var(--skyblue);
  padding: 2px 8px;
  border-radius: 999px;
}
.cert-not-in-vault {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-dim);
  margin-bottom: 10px;
}

.cert-not-found {
  font-family: var(--font-mono);
  font-size: 12.5px;
  color: #FF9FB0;
}
@media (max-width: 500px) {
  .cert-card { grid-template-columns: 80px 1fr; }
  .cert-card img, .cert-img-placeholder { width: 80px; }
}

/* ---------- top hits showcase ---------- */
.top-hits-section { margin-top: 48px; }
/* Chase Cards section heading */
.chase-heading {
  background: linear-gradient(135deg, #FFD15C 0%, #F0F8FF 40%, #C490FF 70%, #FFD15C 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  background-size: 200%;
  animation: chaseGradientShift 5s linear infinite;
  font-size: 22px;
}
@keyframes chaseGradientShift {
  0%   { background-position: 0% 50%; }
  50%  { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}

.top-hits-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 12px;
  overflow: visible;
}
/* Unified floor card tile — identical structure to admin .lib-card */


/* ---------- inventory ---------- */
.inventory-section { margin-top: 48px; }
.inventory-section .section-head,
.top-hits-section .section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  border-bottom: 1px solid var(--line);
  padding-bottom: 10px;
  margin-bottom: 20px;
}
.inventory-section h3,
.top-hits-section h3 {
  font-family: var(--font-display);
  font-size: 18px;
  margin: 0;
  font-weight: 700;
  color: var(--text-light);
}
.section-sub {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-dim);
}
.inventory-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 12px;
  overflow: visible;
  align-items: start;   /* each card shrinks to its image height, no stretching */
}
/* PERF: skip rendering + image decode for gallery cards that are scrolled off-screen.
   content-visibility:auto lets the browser bypass layout/paint for off-screen tiles
   (big win on long decks / lower-end Android), while contain-intrinsic-size reserves an
   approximate height so the scrollbar stays stable. `auto` makes the browser remember
   each card's REAL size once it has been rendered, so re-scrolling never jumps. Paired
   with the cards' loading="lazy" imgs, off-screen work is fully deferred. */
.inventory-grid .floor-card,
#inventoryGrid .floor-card {
  content-visibility: auto;
  contain-intrinsic-size: 168px 236px;              /* universal fallback */
  contain-intrinsic-size: auto 168px auto 236px;    /* modern: remember each card's real size */
}
/* Phones: a denser 4-up grid (max 4 columns) so more of the collection is visible. */
@media (max-width: 640px) {
  .inventory-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 7px; }
}
/* inventory uses same .floor-card tile defined above */
.empty-state {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-dim);
  text-align: center;
  padding: 30px 0;
    border: 1px solid rgba(255,255,255,0.07);
  border-radius: 14px;
}

/* ================================================================
   CARD HOVER PREVIEW
   ================================================================ */
.card-hover-preview {
  position: fixed;
  z-index: 9000;
  width: 310px;
  background: rgba(14, 6, 32, 0.97);
  border: 1px solid rgba(200,168,75,0.2);
  border-radius: 20px;
  padding: 14px 14px 16px;
  box-shadow:
    0 24px 80px rgba(0,0,0,0.75),
    0 0 0 1px rgba(255,255,255,0.04),
    0 0 40px color-mix(in srgb, var(--tier-color, var(--pink)) 15%, transparent);
  pointer-events: none;
  opacity: 0;
  transform: scale(0.9) translateY(10px);
  transition: opacity 0.2s ease, transform 0.2s cubic-bezier(.22,.68,0,1.2);
  overflow: hidden;
}
.card-hover-preview.visible {
  opacity: 1;
  transform: scale(1) translateY(0);
}
.hp-glow {
  position: absolute;
  inset: -60px;
  background: radial-gradient(circle at 50% 25%,
    color-mix(in srgb, var(--glow, var(--pink)) 25%, transparent) 0%,
    transparent 60%);
  pointer-events: none;
  z-index: 0;
}
.hp-img {
  position: relative;
  z-index: 1;
  width: 100%;
  height: auto;
  display: block;
  border-radius: 12px;
  border: 1.5px solid color-mix(in srgb, var(--tier-color, var(--pink)) 50%, transparent);
  margin-bottom: 12px;
  object-fit: contain;
  /* Let the browser render at full native resolution */
  image-rendering: high-quality;
  box-shadow:
    0 4px 30px rgba(0,0,0,0.5),
    0 0 24px color-mix(in srgb, var(--tier-color, var(--pink)) 40%, transparent);
}
.hp-info {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.hp-name {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 700;
  color: var(--text-light);
  line-height: 1.25;
}
.hp-tier {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  margin-bottom: 3px;
}
.hp-grade {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--gold);
  font-weight: 700;
}
.hp-meta {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-dim);
}
.hp-value {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 700;
  color: var(--gold);
  margin-top: 6px;
}

/* hoverable card — real magnification, not just a nudge */
/* Floor page cards: smooth pop, no overshoot bounce */
.hoverable-card {
  cursor: pointer;
  transition: transform 0.28s ease-out, filter 0.2s ease, box-shadow 0.2s ease;
  position: relative;
  z-index: 1;
  /* will-change only while actually hovering (set below) — keeping it on always
     promoted every card to its own layer and caused compositing jitter/bounce
     on mobile scroll. */
}
@media (hover: hover) {
  .hoverable-card:hover {
    transform: scale(1.06);    /* subtle lift — same feel as reveal cards */
    z-index: 200;
    will-change: transform;
    filter: brightness(1.18);
    box-shadow:
      0 0 0 2px var(--tier-glow-color, #EEEEEE),
      0 0 24px var(--tier-glow-color, #EEEEEE),
      0 0 50px color-mix(in srgb, var(--tier-glow-color, #EEEEEE) 50%, transparent) !important;
  }
}
/* Touch devices: never apply hover lifts/scales. On touch the :hover state
   sticks after a tap and while scrolling, so cards + the userbar pills appear
   to bounce around. Force them flat. */
@media (hover: none), (pointer: coarse) {
  .hoverable-card,
  .hoverable-card:hover,
  .sc-ticker-inner,
  .sc-ticker-inner:hover,
  .floor-card:hover,
  .pb-credits-btn:hover,
  .pb-name-btn:hover,
  .hero-stage .card-face:hover {
    transform: none !important;
    will-change: auto !important;
  }
}
/* Admin deck cards: gentle scale, buttons stay clickable */
.hoverable-lib-card {
  cursor: pointer;
  transition: transform 0.22s ease-out, filter 0.18s ease, box-shadow 0.18s ease;
  position: relative;
  z-index: 1;
}
.hoverable-lib-card:hover {
  transform: scale(1.22);
  z-index: 200;
  filter: brightness(1.1);
  box-shadow:
    0 0 0 2px var(--tier-glow-color, #EEEEEE),
    0 0 20px var(--tier-glow-color, #EEEEEE),
    0 0 44px color-mix(in srgb, var(--tier-glow-color, #EEEEEE) 45%, transparent) !important;
}
/* CAROUSEL cards: NO transform change whatsoever — eliminates 3D hover jitter.
   The orbit transform must stay identical; only glow and brightness change. */
.sc-car-card.hoverable-card:hover {
  transform:
    rotateY(calc(var(--idx) * (360deg / var(--n))))
    translateZ(350px) !important;
  filter: brightness(1.22) saturate(1.2) !important;
  box-shadow:
    0 0 0 3px var(--tier-glow-color, #FFD15C),
    0 0 24px var(--tier-glow-color, #FFD15C),
    0 0 55px color-mix(in srgb, var(--tier-glow-color, #FFD15C) 60%, transparent) !important;
}

/* overflow:visible already set in each grid */

/* ================================================================
   CINEMATIC ENTRANCE — RARE & LEGENDARY
   ================================================================ */
.entrance-screen {
  position: fixed;
  inset: 0;
  z-index: 8000;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  overflow: hidden;
  opacity: 0;
  visibility: hidden;
}
.entrance-screen.active  { visibility: visible; }
.entrance-screen.done    { opacity: 0 !important; transition: opacity 0.35s ease; }

/* ----- beams (radial light rays behind card) ----- */
.entrance-beams {
  /* A big centred SQUARE (not a viewport rectangle) so the conic-gradient rays
     always overfill the screen at EVERY rotation angle — otherwise rotating a
     viewport-shaped box exposes its rectangular edge, which on a tall phone reads
     as a cropped box spinning around. Centre via margins, NOT transform, because
     the rareBeams/foilBeams keyframes own the transform (scale + rotate). */
  position: absolute;
  inset: auto;
  top: 50%; left: 50%;
  width: 220vmax; height: 220vmax;
  margin: -110vmax 0 0 -110vmax;
  opacity: 0;
  will-change: transform, opacity;
}

/* ----- screen flash overlay ----- */
.entrance-flash {
  position: absolute;
  inset: 0;
  opacity: 0;
}

/* ----- the card itself — fills much more of the screen ----- */
.entrance-card {
  position: relative;
  z-index: 2;
  /* Large enough to show real card detail */
  width: min(440px, 65vw);
  max-height: 88vh;
  border-radius: 16px;
  overflow: hidden;
  opacity: 0;
  filter: brightness(1.4);
}
.entrance-card img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 16px;
  image-rendering: high-quality;
}

/* ---- RARE entrance: card blasts in from the left ---- */
.entrance-screen.rare {
  background: linear-gradient(135deg, rgba(10,20,50,0.96) 0%, rgba(5,10,30,0.99) 100%);
  animation: rareScreenIn 1.1s cubic-bezier(.22,1,.36,1) forwards;
}
.entrance-screen.rare .entrance-beams {
  animation: rareBeams 1.1s ease-out forwards;
  background: conic-gradient(from 0deg at 50% 50%,
    transparent 0deg, rgba(126,212,255,0.12) 5deg, transparent 10deg,
    transparent 28deg, rgba(126,212,255,0.08) 33deg, transparent 38deg,
    transparent 56deg, rgba(126,212,255,0.12) 61deg, transparent 66deg,
    transparent 84deg, rgba(126,212,255,0.08) 89deg, transparent 94deg,
    transparent 112deg, rgba(126,212,255,0.12) 117deg, transparent 122deg,
    transparent 140deg, rgba(126,212,255,0.08) 145deg, transparent 150deg,
    transparent 168deg, rgba(126,212,255,0.12) 173deg, transparent 178deg,
    transparent 196deg, rgba(126,212,255,0.08) 201deg, transparent 206deg,
    transparent 224deg, rgba(126,212,255,0.12) 229deg, transparent 234deg,
    transparent 252deg, rgba(126,212,255,0.08) 257deg, transparent 262deg
  );
}
.entrance-screen.rare .entrance-flash {
  background: #6AACED;
  animation: rareFlash 1.1s ease forwards;
}
.entrance-screen.rare .entrance-card {
  animation: rareCardEntry 1.1s cubic-bezier(.22,1,.36,1) forwards;
  box-shadow: 0 0 60px #6AACED, 0 0 120px rgba(126,212,255,0.4), -20px 0 80px rgba(126,212,255,0.3);
}

@keyframes rareScreenIn {
  0%   { opacity: 0; }
  5%   { opacity: 1; }
  100% { opacity: 1; }
}
@keyframes rareBeams {
  0%   { opacity: 0; transform: scale(0.5) rotate(-40deg); }
  30%  { opacity: 1; transform: scale(1.1) rotate(0deg); }
  100% { opacity: 0.6; transform: scale(1.3) rotate(8deg); }
}
@keyframes rareFlash {
  0%   { opacity: 0; }
  8%   { opacity: 0.6; }
  20%  { opacity: 0; }
  100% { opacity: 0; }
}
@keyframes rareCardEntry {
  0%   { opacity: 0; transform: translateX(-160vw) rotate(-20deg) scale(0.65); filter: blur(16px) brightness(2.5); }
  30%  { opacity: 1; filter: blur(4px) brightness(1.8); }
  58%  { transform: translateX(14px) rotate(2.5deg) scale(1.07); filter: blur(0) brightness(1.3); }
  75%  { transform: translateX(-6px) rotate(-1deg) scale(0.97); filter: blur(0) brightness(1.1); }
  88%  { transform: translateX(3px) rotate(0.5deg) scale(1.01); }
  100% { transform: translateX(0) rotate(0deg) scale(1); opacity: 1; filter: blur(0) brightness(1); }
}

/* ---- LEGENDARY entrance: zooms from a distant spark ---- */
.entrance-screen.foil-chase-old {
  background: radial-gradient(ellipse at center, rgba(40,20,0,0.98) 0%, rgba(5,2,15,0.99) 70%);
  animation: legScreenIn 1.8s cubic-bezier(.22,1,.36,1) forwards;
}
.entrance-screen.foil-chase-old .entrance-beams {
  animation: legBeams 1.8s ease-out forwards;
  background: conic-gradient(from 0deg at 50% 50%,
    transparent 0deg, rgba(255,209,92,0.14) 4deg, transparent 9deg,
    transparent 26deg, rgba(255,209,92,0.09) 30deg, transparent 35deg,
    transparent 52deg, rgba(255,209,92,0.14) 56deg, transparent 61deg,
    transparent 78deg, rgba(255,209,92,0.09) 82deg, transparent 87deg,
    transparent 104deg, rgba(255,209,92,0.14) 108deg, transparent 113deg,
    transparent 130deg, rgba(255,209,92,0.09) 134deg, transparent 139deg,
    transparent 156deg, rgba(255,255,255,0.1) 160deg, transparent 165deg,
    transparent 182deg, rgba(255,209,92,0.14) 186deg, transparent 191deg,
    transparent 208deg, rgba(255,209,92,0.09) 212deg, transparent 217deg
  );
}
.entrance-screen.foil-chase-old .entrance-flash {
  background: radial-gradient(circle at center, #fff 0%, #FFD15C 35%, rgba(255,209,92,0.3) 65%, transparent 80%);
  animation: legFlash 1.8s ease forwards;
}
.entrance-screen.foil-chase-old .entrance-card {
  /* Legendary gets even more of the screen (size set on the img). */
  animation: legCardEntry 1.8s cubic-bezier(.22,1,.36,1) forwards;
  box-shadow:
    0 0 80px #FFD15C,
    0 0 180px rgba(255,209,92,0.5),
    0 0 300px rgba(255,209,92,0.2);
}

@keyframes legScreenIn {
  0%   { opacity: 0; }
  4%   { opacity: 1; }
  100% { opacity: 1; }
}
@keyframes legBeams {
  0%   { opacity: 0; transform: scale(0.1) rotate(0deg); }
  25%  { opacity: 0.8; transform: scale(0.9) rotate(45deg); }
  50%  { opacity: 1; transform: scale(1.3) rotate(90deg); }
  100% { opacity: 0.7; transform: scale(1.7) rotate(125deg); }
}
@keyframes legFlash {
  0%   { opacity: 0; }
  18%  { opacity: 0; }
  28%  { opacity: 1; }
  42%  { opacity: 0.15; }
  55%  { opacity: 0.75; }
  72%  { opacity: 0; }
  100% { opacity: 0; }
}
/* ---- already-owned / replace state on floor PSA cert result ---- */
.cert-already-in-deck {
  opacity: 0.72;
  filter: grayscale(0.35);
  transition: opacity 0.2s, filter 0.2s;
}
.cert-already-in-deck:hover {
  opacity: 1;
  filter: grayscale(0);
}
.cert-already-owned {
  margin-bottom: 10px;
}
.cert-owned-badge {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 11px;
  border: 1px solid;
  border-radius: 999px;
  padding: 3px 10px;
  margin-bottom: 8px;
}
.cert-replace-btn {
  display: block;
  font-family: var(--font-mono);
  font-size: 11.5px;
  background: rgba(255,255,255,0.06);
  border: 1px solid var(--line);
  color: var(--pink-bright);
  padding: 6px 14px;
  border-radius: 8px;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.cert-replace-btn:hover { background: rgba(255,143,203,0.12); border-color: var(--pink-bright); }
.cert-replace-btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* ---- LEGENDARY: deepest zoom — starts as a genuine single pixel ---- */
@keyframes legCardEntry {
  0%   { opacity: 0;   transform: scale(0.003) rotate(900deg);  filter: blur(22px) brightness(8) saturate(2); }
  8%   { opacity: 0.5; transform: scale(0.008) rotate(600deg);  filter: blur(20px) brightness(7); }
  20%  { opacity: 0.8; transform: scale(0.04)  rotate(340deg);  filter: blur(14px) brightness(5); }
  35%  { opacity: 1;   transform: scale(0.2)   rotate(160deg);  filter: blur(7px)  brightness(3.5); }
  50%  { transform: scale(1.28) rotate(14deg); filter: blur(2px) brightness(2.2); }
  63%  { transform: scale(0.87) rotate(-6deg); filter: blur(0)   brightness(1.35); }
  74%  { transform: scale(1.07) rotate(3deg);  filter: blur(0)   brightness(1.15); }
  84%  { transform: scale(0.97) rotate(-1deg); }
  92%  { transform: scale(1.02) rotate(0.4deg); }
  100% { transform: scale(1)    rotate(0deg);  opacity: 1; filter: blur(0) brightness(1); }
}

/* ================================================================
   STAGE IDLE — 3D rotating card showcase (replaces idle pack)
   ================================================================ */

/* Full-width idle carousel stage */
/* hero-stage: always full-width; contents switch between carousel and animation */
.hero-stage {
  width: 100%;
  min-height: 360px;
  position: relative;
  /* Clip only horizontally (the conveyor wraps sideways); let cards + their glow
     show vertically so the carousel doesn't read as a hard-cropped band — it
     blends into the shared page background like the chase cards do. */
  overflow-x: clip;
  overflow-y: visible;
  /* Transparent so the page's background flows through the game area too — keeps
     the whole app on one continuous background instead of a distinct box. */
  background: transparent;
  /* Own horizontal drags for the spin gesture; let vertical scroll the page. */
  touch-action: pan-y;
  -webkit-user-select: none; user-select: none;
  -webkit-tap-highlight-color: transparent;
}
/* The per-band glow oval was REMOVED — it lit just the game area and made it
   read as a distinct, cropped box. The ambient glow now lives on the page-wide
   #stageFx layer so the whole view shares one continuous background. */
.hero-stage::before { content: none; }

/* stage-frame fills the hero-stage during animation */
.hero-stage .stage-frame {
  position: absolute;
  inset: 0;
  border: none;
  border-radius: 0;
  min-height: unset;
  background: radial-gradient(circle at 50% 30%, rgba(183,156,255,0.10), transparent 60%),
              var(--bg-deep);
}

/* Compact odds bar shown below carousel during idle */
.idle-odds-bar {
  padding: 10px 32px 18px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: 900px;
  margin: 0 auto;
}

/* 3D scene wrapper — perspective is set here, fills full idle stage */
.sc-scene {
  position: absolute;
  inset: 0;
  perspective: 800px;         /* wide angle — 6 cards visible simultaneously */
  perspective-origin: 50% 40%;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

/* Ambient glow behind the carousel */
.sc-scene-glow {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
}

/* The rotating ring — bigger cards in the full-width space */
.sc-carousel {
  position: relative;
  width: 128px;
  height: 180px;  /* 5:7 ≈ PSA slab ratio */
  transform-style: preserve-3d;
  animation: carouselSpin 0.9s linear infinite;
  z-index: 1;
}
@keyframes carouselSpin {
  from { transform: rotateY(0deg); }
  to   { transform: rotateY(360deg); }
}

/* Each card in the carousel */
.sc-car-card {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0; left: 0;
  border-radius: 10px;
  overflow: hidden;
  cursor: pointer;
  background: var(--bg-deep);
  display: flex;
  align-items: center;
  justify-content: center;
}
.sc-car-card img {
  width: 100%;
  height: auto;
  display: block;
  image-rendering: high-quality;
  transform: translateZ(0);
}
@keyframes kenBurnsOrbit {
  0%   { transform: scale(1.10) translate( 0%,  0%); }
  25%  { transform: scale(1.28) translate(-4%,  3%); }
  50%  { transform: scale(1.18) translate( 4%, -3%); }
  75%  { transform: scale(1.32) translate(-3%, -3%); }
  100% { transform: scale(1.10) translate( 0%,  0%); }
}

/* Card label (name + grade) shown at bottom of each card */
.sc-car-label {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  background: linear-gradient(transparent, rgba(10,4,28,0.85) 60%);
  padding: 20px 6px 5px;
  display: flex;
  flex-direction: column;
  gap: 1px;
  align-items: center;
}
.sc-car-grade {
  font-family: var(--font-display);
  font-size: 10px;
  font-weight: 900;
  letter-spacing: 0.08em;
}
.sc-car-name {
  font-family: var(--font-body);
  font-size: 7px;
  color: rgba(255,255,255,0.7);
  text-align: center;
  line-height: 1.2;
}

/* carousel card hover now handled by .sc-car-card.hoverable-card:hover above */

/* ----------------------------------------------------------------
   SPIN button — center overlay (NOT inside the 3D scene)
   ---------------------------------------------------------------- */
.sc-center {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 20;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  pointer-events: auto;
}

/* ---- RIP button — dark glass, gold border, dominant action ---- */
.spin-btn {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 24px 72px 20px;
  background: linear-gradient(180deg, rgba(7,9,14,0.96) 0%, rgba(7,9,14,0.99) 100%);
  border: 2px solid rgba(255,209,92,0.75);
  border-radius: 16px;
  cursor: pointer;
  box-shadow:
    0 0 0 1px rgba(255,209,92,0.15),
    0 0 40px rgba(255,209,92,0.45),
    0 0 80px rgba(196,144,255,0.18),
    inset 0 1px 0 rgba(255,255,255,0.08),
    inset 0 -1px 0 rgba(255,209,92,0.15);
  animation: ripPulse 2.8s ease-in-out infinite;
  transition: transform 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
}
@keyframes ripPulse {
  0%, 100% {
    box-shadow:
      0 0 0 1px rgba(255,209,92,0.15),
      0 0 35px rgba(255,209,92,0.4),
      0 0 70px rgba(196,144,255,0.15),
      inset 0 1px 0 rgba(255,255,255,0.08);
  }
  50% {
    box-shadow:
      0 0 0 2px rgba(255,209,92,0.3),
      0 0 60px rgba(255,209,92,0.65),
      0 0 120px rgba(196,144,255,0.3),
      inset 0 1px 0 rgba(255,255,255,0.12);
  }
}
.spin-btn:not(:disabled):hover {
  transform: scale(1.06) translateY(-2px);
  border-color: rgba(255,209,92,1);
  box-shadow:
    0 0 0 2px rgba(255,209,92,0.4),
    0 0 70px rgba(255,209,92,0.8),
    0 0 140px rgba(196,144,255,0.4),
    0 16px 48px rgba(0,0,0,0.6),
    inset 0 1px 0 rgba(255,255,255,0.14);
  animation: none;
}
.spin-btn:not(:disabled):active { transform: scale(0.97) translateY(0); }
.spin-btn:disabled {
  opacity: 0.38;
  cursor: not-allowed;
  animation: none;
  border-color: rgba(255,209,92,0.2);
}

.spin-label {
  font-family: var(--font-display);
  font-size: 40px;
  font-weight: 900;
  letter-spacing: 0.28em;
  text-indent: 0.28em;   /* compensate for letter-spacing on last char */
  line-height: 1;
  background: linear-gradient(160deg,
    #FFD15C 0%, #FFF8DC 35%, #FFD15C 55%, #F0C040 80%, #FFD15C 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  filter: drop-shadow(0 0 10px rgba(255,209,92,0.7));
}
.spin-price {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 600;
  color: rgba(255,255,255,0.45);
  letter-spacing: 0.12em;
  line-height: 1;
}

.spin-hint {
  font-family: var(--font-mono);
  font-size: 10px;
  color: rgba(255,255,255,0.38);
  text-align: center;
  letter-spacing: 0.04em;
  margin: 0;
  text-shadow: 0 0 12px rgba(0,0,0,0.8);
}

/* ================================================================
   REVEAL ZONE — expanded state: card slides left, PSA panel right
   ================================================================ */
.reveal-zone .reveal-inner {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 0;
  width: 100%;
}

/* card slides from center to left on expand */
.reveal-zone .card-flip-wrap {
  transition: transform 0.65s cubic-bezier(.22,1,.36,1), margin 0.65s ease;
}
.reveal-zone.expanded .reveal-inner {
  gap: 20px;
}
.reveal-zone.expanded .card-flip-wrap {
  transform: translateX(-10%) scale(0.85);
  flex-shrink: 0;
}

/* PSA stats panel slides in from right */
.reveal-psa-panel {
  width: 0;
  overflow: hidden;
  opacity: 0;
  text-align: center;   /* centers the inline-flex PSA slab label on desktop too */
  transition: width 0.65s cubic-bezier(.22,1,.36,1), opacity 0.5s ease 0.3s;
  flex-shrink: 0;
}
.reveal-zone.expanded .reveal-psa-panel {
  width: 180px;
  opacity: 1;
}
.rp-tier {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-bottom: 4px;
}
.rp-name {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 700;
  color: var(--text-light);
  line-height: 1.25;
  margin-bottom: 4px;
}
.rp-value {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 700;
  margin-bottom: 12px;
}
.rp-stats {
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.rp-row {
  display: flex;
  justify-content: space-between;
  gap: 6px;
  font-size: 10px;
  padding: 3px 0;
  border-bottom: 1px solid rgba(200,168,75,0.1);
  white-space: nowrap;
}
.rp-label {
  font-family: var(--font-mono);
  color: rgba(200,168,75,0.55);
  flex-shrink: 0;
}
.rp-val {
  font-family: var(--font-mono);
  color: var(--text-light);
  font-weight: 600;
  text-align: right;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rp-cert-link {
  display: inline-block;
  margin-top: 8px;
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--skyblue, #60C8FF);
  text-decoration: none;
}
.rp-cert-link:hover { text-decoration: underline; }

/* ================================================================
   CAROUSEL CARD HOVER — border intensify only, NO scale/zoom
   ================================================================ */
/* carousel hover: see rule above */

/* ================================================================
   CHASE CARDS SECTION — celebratory animated glow per card
   ================================================================ */
.chase-heading {
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 900;
  letter-spacing: 0.04em;
  background: linear-gradient(90deg, #00e5ff, #ffffff 30%, #ff2d9b 52%, #a855ff 74%, #00e5ff);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  background-size: 200% auto;
  filter: drop-shadow(0 0 10px rgba(0,229,255,.45)) drop-shadow(0 0 18px rgba(255,45,155,.3));
  animation: chaseHeadingShimmer 4s linear infinite;
}
@keyframes chaseHeadingShimmer {
  from { background-position: 0% center; }
  to   { background-position: 200% center; }
}

/* Each top-card pulses its tier glow on a staggered delay */
.floor-card.celebrate {
  animation: cardCelebrate 4s ease-in-out infinite;
  animation-delay: calc(var(--ci, 0) * 0.55s);
}
@keyframes cardCelebrate {
  0%, 100% { filter: brightness(1) saturate(1); }
  45%       { filter: brightness(1.14) saturate(1.25); }
  50%       { filter: brightness(1.18) saturate(1.3); }
  55%       { filter: brightness(1.14) saturate(1.25); }
}

/* ================================================================
   LAYOUT: chase cards (left) + odds panel (right)
   ================================================================ */
/* chase-odds-layout replaced by rip-layout */
@media (max-width: 860px) {
  .chase-odds-layout { grid-template-columns: 1fr; }
}

.odds-aside {
  border: 1px solid var(--line);
  background: linear-gradient(160deg, rgba(255,255,255,0.04), transparent), var(--bg-panel);
  border-radius: 18px;
  padding: 22px;
  position: sticky;
  top: 12px;
}
.odds-aside-title {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 700;
  color: var(--text-light);
  margin: 0 0 6px;
}

/* Pack animation stage — centered, only visible during pull */
/* ================================================================
   CAROUSEL: full-width, high-res cards (contain, not cover)
   ================================================================ */

/* ================================================================
   REVEAL in hero-stage: card fills the full display area
   ================================================================ */
.hero-stage .reveal-zone.active {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.hero-stage .reveal-inner {
  display: flex;
  align-items: center;           /* vertically centre the front / back / PSA trio */
  justify-content: center;
  gap: 28px;
  width: 100%;
  height: 100%;
  padding: 16px 32px;
  box-sizing: border-box;
  overflow: hidden;
}
/* Card is large, fills most of the hero stage height */
.hero-stage .card-flip-wrap {
  height: 100%;               /* fill stage height */
  width: auto;
  flex-shrink: 0;
  cursor: pointer;
  align-self: stretch;
}
/* Both faces shown flat side-by-side — natural aspect ratio, top-anchored */
.hero-stage .card-flip-inner {
  transform: none !important;
  transform-style: flat;
  position: relative;
  display: flex;
  flex-direction: row;
  align-items: flex-start;    /* anchor both faces to top */
  gap: 10px;
  height: 100%;
}
.hero-stage .card-face {
  position: relative !important;
  backface-visibility: visible !important;
  transform: none !important;
  flex-shrink: 0;
  height: 100%;
  width: auto;
  overflow: hidden;
  align-self: flex-start;     /* anchor to top of reveal row */
}
.hero-stage .card-flip-wrap .card-face img {
  /* Natural aspect ratio: width driven by height, never stretched */
  height: 100%;
  width: auto;
  display: block;
  object-fit: none;            /* no resizing — raw natural size at height=100% */
  vertical-align: top;
  border-radius: 8px;
}
.hero-stage .card-flip-wrap .card-face img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center top;
  background: var(--bg-deep);
  image-rendering: high-quality;
}
/* In expanded mode, card stays large and doesn't translate left */
.hero-stage .reveal-zone.expanded .card-flip-wrap {
  transform: none;
}
/* PSA stats panel */
.hero-stage .reveal-psa-panel {
  width: 0;
  overflow: hidden;
  opacity: 0;
  flex-shrink: 0;
  transition: width 0.5s cubic-bezier(.22,1,.36,1), opacity 0.4s ease 0.2s;
}
.hero-stage .reveal-zone.expanded .reveal-psa-panel {
  width: 220px;
  opacity: 1;
}
/* Tier banner and card name in hero context */
.hero-stage .tier-banner.show {
  position: absolute;
  top: 16px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 3;
}
.hero-stage .reveal-cardname {
  position: absolute;
  bottom: 16px;
  left: 50%;
  transform: translateX(-50%);
  text-align: center;
  white-space: nowrap;
  font-size: 14px;
  z-index: 3;
}

/* ================================================================
   NAV: main-tabs + admin corner button
   ================================================================ */
.main-tabs {
  display: flex;
  gap: 4px;
}
.tab-btn {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 700;
  padding: 7px 20px;
  border: 1px solid rgba(200,168,75,0.15);
  background: transparent;
  color: var(--text-dim);
  border-radius: 8px;
  cursor: pointer;
  transition: background 0.14s, color 0.14s, border-color 0.14s;
}
.tab-btn:hover { background: rgba(200,168,75,0.08); color: var(--text-light); }
.tab-btn.active {
  background: rgba(200,168,75,0.14);
  color: var(--text-light);
  border-color: rgba(200,168,75,0.4);
}
.admin-corner-btn {
  position: absolute;
  right: 24px;
  top: 50%;
  transform: translateY(-50%);
  font-family: var(--font-mono);
  font-size: 11px;
  color: rgba(255,255,255,0.35);
  text-decoration: none;
  border: 1px solid rgba(255,255,255,0.1);
  padding: 4px 10px;
  border-radius: 6px;
  transition: color 0.14s, border-color 0.14s;
}
.admin-corner-btn:hover { color: rgba(255,255,255,0.7); border-color: rgba(255,255,255,0.3); }

/* ================================================================
   GALLERY VIEW
   ================================================================ */
/* Gallery uses the SAME header language as the rip view (a gold pill tag) and a
   centered column, so the collection feels like part of the same app. */
.gallery-header {
  padding: 22px 16px 14px;
}
/* The userbar is pinned fixed-top (or docked in Admin) — it never docks in the
   gallery header's slot, so the header is just empty space that pushed the craft
   cards lower than the rip page's chase cards. Drop it so the two pages align. */
.gallery-header { display: none; }
.gallery-title {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-display);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold-bright);
  padding: 5px 14px;
  border-radius: 999px;
  background: linear-gradient(135deg, rgba(200,168,75,0.22), rgba(200,168,75,0.06));
  border: 1px solid rgba(200,168,75,0.38);
  box-shadow: 0 2px 10px rgba(0,0,0,0.25), inset 0 1px 0 rgba(255,255,255,0.06);
  margin: 0;
}
.gallery-sub {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-dim);
  max-width: 1040px;
  margin: 9px auto 0;
}
.gallery-grid {
  padding: 0 16px 18px;
  max-width: 1040px;
  margin: 8px auto 0;
}

/* ================================================================
   LIGHTBOX — right-click image zoom/pan
   ================================================================ */
.lightbox-overlay {
  position: fixed;
  inset: 0;
  z-index: 9500;
  background: rgba(0,0,0,0.92);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: zoom-out;
}
.lightbox-stage {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.lightbox-img {
  max-height: 92vh;
  max-width: 90vw;
  object-fit: contain;
  image-rendering: high-quality;
  border-radius: 14px;
  user-select: none;
  box-shadow: 0 0 80px rgba(0,0,0,0.8);
  transition: none;
}
.lightbox-close {
  position: fixed;
  top: 20px;
  right: 24px;
  z-index: 9501;
  background: rgba(255,255,255,0.12);
  border: 1px solid rgba(255,255,255,0.2);
  color: #fff;
  font-size: 20px;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.14s;
}
.lightbox-close:hover { background: rgba(255,255,255,0.22); }
.lightbox-hint {
  position: fixed;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%);
  font-family: var(--font-mono);
  font-size: 11px;
  color: rgba(255,255,255,0.4);
  pointer-events: none;
}

/* ================================================================
   Reveal action buttons styling
   ================================================================ */
.rip-again-btn {
  background: var(--pink-bright);
  color: #07090E;
  font-weight: 800;
}
.rip-again-btn:hover { background: #ffd0f0; }

/* ================================================================
   HEADER: header-right (admin + credits side by side)
   ================================================================ */
.ledger-bar {
  position: relative;  /* ensure admin absolute fallback works */
}
.header-right {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: 0;   /* centered with the toggle, not pushed to the edge */
}
/* Override old absolute positioning */
.admin-corner-btn {
  position: static !important;
  transform: none !important;
  font-family: var(--font-mono);
  font-size: 11px;
  color: rgba(255,255,255,0.38);
  text-decoration: none;
  border: 1px solid rgba(255,255,255,0.12);
  padding: 5px 12px;
  border-radius: 7px;
  transition: color 0.14s, border-color 0.14s, background 0.14s;
  white-space: nowrap;
  /* Button vs anchor reset so the "← Rip" <button> matches the "Admin" <a>. */
  background: transparent;
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
  line-height: 1.2;
  display: inline-block;
}
.admin-corner-btn:hover {
  color: rgba(255,255,255,0.75);
  border-color: rgba(255,255,255,0.32);
  background: rgba(255,255,255,0.05);
}

/* ================================================================
   RIP EMBLEM — massive centered game action (not a button)
   ================================================================ */
/* ================================================================
   RIP — portal/vortex experience, not a button
   ================================================================ */
.rip-emblem {
  background: none;
  border: none;
  cursor: pointer;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 48px 60px;
  transition: transform 0.2s ease;
}
/* Outer rotating ring */
.rip-emblem::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: conic-gradient(from 0deg,
    rgba(0,229,255,0.0) 0%,
    rgba(0,229,255,0.95) 20%,
    rgba(255,45,155,0.7) 40%,
    rgba(170,85,255,0.6) 60%,
    rgba(0,229,255,0.0) 80%);
  mask: radial-gradient(farthest-side,
    transparent calc(100% - 3px), white calc(100% - 3px));
  -webkit-mask: radial-gradient(farthest-side,
    transparent calc(100% - 3px), white calc(100% - 3px));
  animation: portalSpin 3s linear infinite;
  pointer-events: none;
}
/* Inner counter-rotating ring */
.rip-emblem::after {
  content: '';
  position: absolute;
  inset: 10px;
  border-radius: 50%;
  background: conic-gradient(from 90deg,
    rgba(255,45,155,0.0) 0%,
    rgba(255,45,155,0.75) 25%,
    rgba(0,229,255,0.55) 55%,
    rgba(255,45,155,0.0) 80%);
  mask: radial-gradient(farthest-side,
    transparent calc(100% - 2px), white calc(100% - 2px));
  -webkit-mask: radial-gradient(farthest-side,
    transparent calc(100% - 2px), white calc(100% - 2px));
  animation: portalSpin 5s linear infinite reverse;
  pointer-events: none;
}
@keyframes portalSpin { to { transform: rotate(360deg); } }
.rip-emblem:not(:disabled):hover { transform: scale(1.08); }
.rip-emblem:not(:disabled):active { transform: scale(0.93); }
.rip-emblem:disabled { opacity: 0.25; cursor: not-allowed; }
.rip-emblem:disabled::before,
.rip-emblem:disabled::after { animation: none; }
.rip-emblem-text {
  font-family: var(--font-display);
  font-size: 88px;
  font-weight: 900;
  letter-spacing: 0.26em;
  text-indent: 0.26em;
  line-height: 1;
  background: linear-gradient(155deg, #00e5ff 0%, #ffffff 40%, #ff2d9b 72%, #a855ff 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  filter: drop-shadow(0 0 14px rgba(0,229,255,0.9))
          drop-shadow(0 0 40px rgba(255,45,155,0.5));
  animation: ripTextBreath 2.6s ease-in-out infinite;
}
@keyframes ripTextBreath {
  0%, 100% { filter: drop-shadow(0 0 14px rgba(0,229,255,0.85)) drop-shadow(0 0 46px rgba(255,45,155,0.4)); }
  50%      { filter: drop-shadow(0 0 26px rgba(0,229,255,1))    drop-shadow(0 0 80px rgba(255,45,155,0.6)) drop-shadow(0 0 130px rgba(170,85,255,0.4)); }
}
.rip-emblem-price {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 600;
  color: rgba(255,255,255,0.38);
  letter-spacing: 0.14em;
}

/* sc-hoverable: glow on hover, NO scale/transform */
.sc-hoverable {
  cursor: pointer;
  pointer-events: auto;  /* override parent pointer-events:none from sc-scene */
}
/* also ensure sc-carousel itself passes through pointer events to cards */
.sc-carousel { pointer-events: none; }
.sc-car-card { pointer-events: auto; }
.sc-hoverable:hover {
  transform: scale(1.06);
  filter: brightness(1.2) saturate(1.1);
  box-shadow:
    0 0 0 2px var(--tier-glow-color, #FFD15C),
    0 0 18px var(--tier-glow-color, #FFD15C),
    0 0 40px color-mix(in srgb, var(--tier-glow-color, #FFD15C) 50%, transparent) !important;
}

/* ================================================================
   BELOW STAGE: Chase Cards (left) + Pull Rates (right) in a row
   ================================================================ */
.below-stage-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0;
  /* No divider line — the whole app is one continuous background. */
}
.sidebar-section {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: stretch;           /* graphs/grids fill width */
  justify-content: flex-start;
  gap: 0;
  padding: 14px 16px 16px;
  background: transparent;         /* share the page background (no distinct panel) */
  min-height: 220px;
  box-sizing: border-box;
}
.sidebar-section + .sidebar-section {
  border-left: none;
  background: transparent;
}

/* Modern label chip — sits above each section so the cards/graphs fill the area
   while the section stays clearly distinguished. Same look desktop + mobile. */
.section-tag {
  align-self: center;
  margin-bottom: 12px;
  display: inline-flex;
  align-items: center;
  padding: 5px 14px;
  border-radius: 7px;
  background: linear-gradient(135deg, rgba(0,229,255,0.16), rgba(255,45,155,0.08));
  border: 1px solid rgba(0,229,255,0.5);
  box-shadow: 0 0 14px rgba(0,229,255,0.28), inset 0 1px 0 rgba(255,255,255,0.08);
  pointer-events: none;
}
.section-tag > span {
  writing-mode: horizontal-tb;
  transform: none;
  font-family: var(--font-display);
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: #9af4ff;                                  /* neon cyan */
  text-shadow: 0 0 8px rgba(0,229,255,0.6);
  white-space: nowrap;
}
.pull-rates-section .section-tag {
  background: linear-gradient(135deg, rgba(120,150,200,0.22), rgba(120,150,200,0.06));
  border-color: rgba(150,170,210,0.38);
}
.pull-rates-section .section-tag > span { color: #c2d2ee; }

/* Accessible heading kept for screen readers only. */
.section-sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* Chase cards: now that the section spans the full width above the game, cap the
   grid so the 3 cards stay a sensible size instead of stretching huge. */
.chase-section .top-hits-grid { flex: 0 0 auto; align-content: center; width: 100%; max-width: 640px; margin: 0 auto; }

/* Chase Cards sit ABOVE the game on every screen size: chase → game → pull
   rates. display:contents promotes the chase/pull-rates sections to flex items
   of #ripView so `order` can move them around the hero stage. */
#ripView { display: flex; flex-direction: column; position: relative; isolation: isolate; }
#ripView .below-stage-row { display: contents; }
/* No hard dividers between sections — they share one background and should flow
   together (chase → game → pull rates) rather than read as stacked boxes. */
#ripView .chase-section { order: 1; }   /* chase cards + the big jackpot they orbit */
#ripView #heroStage { order: 3; }
#ripView .pull-rates-section { order: 4; }

/* Desktop: don't stack chase → game → pull-rates full-width (that's taller than the
   viewport). Instead sit the chase cards (left) and the pull-rates / cards-in-play
   (right) SIDE BY SIDE in one compact row above the hero stage, so the whole front
   page is visible without scrolling. Mobile keeps the stacked flow above. */
@media (min-width: 861px) {
  #ripView .below-stage-row {
    display: grid; grid-template-columns: 1fr 1fr; gap: 28px; align-items: start;
    order: 1; width: 100%; max-width: 1180px; margin: 0 auto;
  }
  #ripView #heroStage { order: 2; }
  #ripView .chase-section { order: 1; min-height: 0; }       /* left column  */
  #ripView .pull-rates-section { order: 2; min-height: 0; }  /* right column */
  /* The two-column row freed a lot of vertical space — let the carousel band GROW
     to fill the rest of the viewport so the cards are big and reach toward the
     bottom, without overflowing. The hero flexes to fill whatever the row leaves.
     (JS sizes each card to 92% of the band height.) */
  body.rip-active main { padding-bottom: 24px; }   /* trim the 80px tail so the page fits */
  #ripView { min-height: calc(100dvh - 24px); }
  #ripView #heroStage { flex: 1 1 auto; min-height: 360px; }
  /* Each column's content fills its own half now (no centering inside a full-width
     band) so the two sit neatly shoulder to shoulder. (#ripView prefix beats the
     later max-width:640px base rules.) */
  #ripView .chase-section .top-hits-grid,
  #ripView .pull-rates-section .pr-graph-block { max-width: 100%; }
  /* During a rip (spin/whirl), the reveal, or a craft: drop the two-column row so
     the chase-section becomes a full-width flex child of #ripView again — the
     existing spin-cine rules then CENTER the chase cards across the whole viewport,
     and the pull-rates / stats are hidden for the duration. */
  body.spin-cine #ripView .below-stage-row,
  body.rip-reveal #ripView .below-stage-row,
  body.craft-reveal #ripView .below-stage-row { display: contents; }
}

/* Chase header row: the "✦ Chase Cards" label on the left, the dev login buttons
   (signed out) or the userbar (signed in) on the right — all on one row. */
/* Constrain the header to the card-grid width (centered) so the label and the
   userbar sit close together over the cards — like mobile — instead of being
   flung to the far edges of a full-width row. */
.chase-head { display: flex; align-items: center; justify-content: flex-end; flex-wrap: nowrap; gap: 8px; width: 100%; max-width: 640px; margin: 0 auto 12px; }
.chase-head .section-tag { margin-bottom: 0; flex: 0 1 auto; min-width: 0; overflow: hidden; }
.chase-head .section-tag > span { overflow: hidden; text-overflow: ellipsis; }
/* The dev-login bar and the userbar occupy the SAME box (same width + height +
   shape) so swapping between them on login/logout is seamless — no jump. */
.chase-head-right { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; --acct-w: 244px; --acct-h: 34px; }
.dev-switch-slot:empty, .userbar-slot:empty { display: none; }
/* Docked userbar: sits in the chase header in-flow (not the floating overlay). */
.player-hud.hud-docked { position: static; inset: auto; top: auto; right: auto; z-index: auto; }
/* The docked userbar sizes to its content so the FULL username is shown. */
.player-hud.hud-docked .player-bar { height: 34px; box-sizing: border-box; width: auto; max-width: 92vw; }
.player-hud.hud-docked .pb-credits-btn,
.player-hud.hud-docked .pb-name-btn,
.player-hud.hud-docked .pb-view-btn,
.player-hud.hud-docked .pb-buy-btn,
.player-hud.hud-docked .pb-crystals,
.player-hud.hud-docked .pb-icon { height: 34px; box-sizing: border-box; }
.player-hud.hud-docked .pb-credits-btn { flex: 0 0 auto; justify-content: flex-start; }
.player-hud.hud-docked .pb-name-btn { flex: 0 0 auto; min-width: 0; max-width: none; }
.player-hud.hud-docked .pb-icon { width: 34px; flex: 0 0 auto; }

/* Uniform userbar type: ONE font (Helvetica) and ALL CAPS for every label + balance
   across the whole header, so RIP / CRAFT / BUY RIPS / GALLERY read as one set. */
.player-hud .player-bar,
.player-hud .player-bar button,
.player-hud .pb-nav-label, .player-hud .pb-buy-label, .player-hud .pb-name,
.player-hud .pb-cred-amt, .player-hud .pb-cred-amt *, .player-hud .js-crystal-amt {
  font-family: Helvetica, Arial, sans-serif !important;
  text-transform: uppercase !important;
}

/* The FOUR main userbar buttons (RIP · CRAFT · BUY RIPS · GALLERY) are evenly spaced,
   25% each. The admin cog (admins only) is EXTRA and doesn't factor into that spacing. */
.player-hud.hud-docked .player-bar { width: min(600px, 92vw); }
.player-hud.hud-docked #creditsBuyBtn,
.player-hud.hud-docked #craftNavBtn,
.player-hud.hud-docked #buyCtaBtn,
.player-hud.hud-docked #profileBtn {
  flex: 1 1 0 !important; min-width: 0; justify-content: center;
}
.player-hud.hud-docked #adminToggle { flex: 0 0 auto; }

/* ── Header consistency (desktop): EVERY control in the chase header (the label
   tag, the credits pill, the name pill, the icon buttons and the signed-out dev
   pills) shares one uniform 40px pill height, vertical centering and pill radius
   so the bar reads as a single matched set. The credits pill keeps its gold
   styling — it stays the standout, just the same SIZE as its neighbours. (Mobile
   keeps its own tuned compact sizes, set in the max-width:640px block.) */
@media (min-width: 641px) {
  .chase-head { align-items: center; }
  .chase-head .section-tag,
  .chase-head .dev-pill,
  .chase-head-right .player-hud.hud-docked .pb-credits-btn,
  .chase-head-right .player-hud.hud-docked .pb-name-btn,
  .chase-head-right .player-hud.hud-docked .pb-view-btn,
  .chase-head-right .player-hud.hud-docked .pb-icon {
    height: 40px; box-sizing: border-box; border-radius: 7px;
    display: inline-flex; align-items: center;
  }
  .chase-head .section-tag { padding: 0 14px; margin-bottom: 0; }
}

/* Gallery + admin header rows host the docked userbar (right-aligned), matching
   the rip view so it never reads as a hovering element. */
.view-head-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; max-width: 1040px; margin: 0 auto; }
.view-head-row .gallery-title { flex: 0 1 auto; }
/* Admin top bar: CENTER the userbar so it sits where it does on the Rip/Craft pages
   (navigating into Admin shouldn't shuffle the header), with the live player count
   tucked beside it. A 3-column grid keeps the bar dead-centre regardless of the
   count's width; the pill hugs the bar's left edge. */
.adm-live-head { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 10px; margin-bottom: 8px; }
.adm-live-head .adm-online-pill { grid-column: 1; justify-self: end; }
.adm-live-head #adminUserbarSlot { grid-column: 2; justify-self: center; }
.adm-online-pill { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 700; color: #c7d2e2; white-space: nowrap; }
@media (max-width: 760px) {
  /* Phones: the docked userbar is full-width, so stack the count above it (centred). */
  .adm-live-head { grid-template-columns: 1fr; justify-items: center; gap: 8px; }
  .adm-live-head .adm-online-pill { grid-column: 1; justify-self: center; }
  .adm-live-head #adminUserbarSlot { grid-column: 1; justify-self: stretch; width: 100%; }
}
/* Dev switcher = the signed-out userbar: standalone pills, no container/DEV tag. */
.dev-switch { display: flex; align-items: stretch; gap: 7px; }
.dev-switch-fixed { position: fixed; left: 10px; bottom: 10px; z-index: 10000; }
.dev-pill {
  -webkit-appearance: none; appearance: none; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  height: 34px; padding: 0 13px; white-space: nowrap;
  color: #dbe3ef; font: 700 11.5px/1 system-ui, sans-serif;
  background: linear-gradient(180deg, rgba(20,25,36,.85), rgba(12,15,23,.85));
  border: 1px solid rgba(0,229,255,.32); border-radius: 7px;
  box-shadow: 0 6px 18px rgba(0,0,0,.42);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  transition: background .14s, box-shadow .14s;
}
.dev-pill:hover { background: linear-gradient(180deg, rgba(40,52,72,.9), rgba(22,28,40,.9)); }
/* Admin dev user is marked by the crown (see .dev-pill-admin::before), not a tint. */
.dev-pill.dev-live {       /* in session → green */
  color: #06351f; border-color: #9bf6cf;
  background: linear-gradient(180deg, #46f0a6, #16c47f);
  box-shadow: 0 0 12px rgba(72,213,151,.7);
}
/* In session on another tab → can't sign in as them again. */
.dev-pill:disabled { cursor: not-allowed; opacity: .9; }

/* Admin demo-mode toggle: ON reads green so its state is obvious at a glance. */
#adm-demo-toggle.is-on {
  color: #06351f; border-color: #9bf6cf;
  background: linear-gradient(180deg, #46f0a6, #16c47f);
  box-shadow: 0 0 12px rgba(72,213,151,.6);
}
/* On mobile the DEV label just wastes space — drop it, and compact the userbar
   so the Chase Cards header + userbar/dev buttons stay on ONE row at the top. */
@media (max-width: 640px) {
  .dev-switch { gap: 5px; }
  .dev-pill { height: 30px; padding: 0 10px; font-size: 10.5px; }
  /* Userbar spans the FULL mobile width; the wallet fills the middle. */
  /* Everything must fit on ONE row within the screen (no right bleed): tight gap,
     no wrap, and every control compacted below. */
  .player-hud.hud-docked .player-bar { width: 100%; max-width: 100vw; height: 30px; gap: 4px; flex-wrap: nowrap; overflow: hidden; }
  .player-hud.hud-docked .pb-credits-btn { flex: 0 1 auto; min-width: 0; }
  /* RIP / CRAFT split buttons on mobile: natural width (don't stretch), compact,
     and drop the icon — the label already says which is which — so "RIP 0" /
     "CRAFT 0" both fit alongside Buy Rips + the icon buttons on one row. */
  .player-hud.hud-docked .pb-nav-btn { flex: 0 0 auto !important; padding: 0 7px !important; gap: 3px !important; font-size: 10.5px !important; }
  .pb-nav-btn .rip-logo, .pb-nav-btn .ico-crystal { display: none; }
  .player-hud.hud-docked .pb-credits-btn,
  .player-hud.hud-docked .pb-name-btn,
  .player-hud.hud-docked .pb-buy-btn,
  .player-hud.hud-docked .pb-crystals,
  .player-hud.hud-docked .pb-icon { height: 30px; }
  .player-hud.hud-docked .pb-buy-btn { padding: 0 7px; gap: 4px; }
  .player-hud.hud-docked .pb-buy-label { font-size: 10.5px; }
  .player-hud.hud-docked .pb-buy-btn .buy-cta-rip { width: 15px; height: 15px; }
  .chase-head { gap: 6px; margin-bottom: 6px; justify-content: center; max-width: 100%; padding: 0 8px; }  /* full-width userbar */
  .chase-head .section-tag { padding: 4px 8px; }
  .chase-head .section-tag > span { font-size: 9px; letter-spacing: .1em; }
  .player-bar .pb-credits-btn { padding: 0 10px; gap: 4px; font-size: 12px; }
  .player-bar .pb-credits-btn .gem-diamond { width: 19px; height: 17px; }
  .player-hud.hud-docked .pb-name-btn { padding: 0 7px; max-width: none; font-size: 10.5px; }
  .player-hud.hud-docked .pb-name-btn .pb-gallery-ic { width: 13px; height: 13px; margin-right: 5px; }
  .player-hud.hud-docked .pb-icon { width: 26px; }
}

/* ================================================================
   CARD REVEAL: front + back far left, PSA stats more centred-right
   ================================================================ */
.hero-stage .reveal-inner {
  display: flex;
  align-items: stretch;          /* cards fill full stage height */
  justify-content: flex-start;
  gap: 12px;
  width: 100%;
  height: 100%;
  padding: 10px 12px 10px 10px;  /* tight left padding */
  box-sizing: border-box;
  overflow: hidden;
}
.hero-stage .card-flip-wrap { flex-shrink: 0; }
.hero-stage .reveal-psa-panel {
  flex: 1;
  max-width: 300px;
  margin: 0 auto;
  padding-left: 12px;
}
.hero-stage .reveal-zone.expanded .reveal-psa-panel {
  width: auto;
  opacity: 1;
}

/* Stage credits pill — shown at top of carousel display */
.stage-credits-pill {
  display: flex;
  align-items: center;
  gap: 8px;
  background: rgba(9,12,18,0.7);
  border: 1px solid rgba(255,209,92,0.25);
  border-radius: 999px;
  padding: 6px 16px;
  margin-bottom: 20px;
  backdrop-filter: blur(8px);
}
.scp-label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.4);
}
.scp-amount {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 700;
  color: var(--gold);
  letter-spacing: 0.04em;
}

/* ================================================================
   CREDITS DEPLETION BAR — top of hero stage
   ================================================================ */
/* The standalone credits-depletion bar was merged into the header credits pill
   (.cb-fill) to reclaim vertical space in the game view. */

/* Right panel inside reveal: PSA info on top, buttons at bottom */
.reveal-right-panel {
  display: flex;
  flex-direction: column;
  flex: 1;
  max-width: 300px;
  min-width: 180px;
  height: 100%;
  min-height: 0;
  overflow: hidden;
}
/* Holographic cert-card sheen — defined globally so the mobile cert card (below)
   can use it too (the desktop copy lives inside a min-width media query). */
@keyframes certSheen { 0% { background-position: 140% 0; } 55%, 100% { background-position: -70% 0; } }
/* PSA info takes available space, scrolls if needed. Centre its content
   vertically + horizontally so the generated PSA "card" lines up with the
   front/back card faces beside it (same centering as the other two cards). */
.hero-stage .reveal-psa-panel {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  scrollbar-width: none;
  display: flex;
  flex-direction: column;
  justify-content: center;       /* vertically centre the PSA content */
  align-items: center;
}
.hero-stage .reveal-psa-panel::-webkit-scrollbar { display: none; }
/* CLAIM now lives BELOW the card trio (not inside the PSA card), so the PSA panel
   is a pure card the victory rules size identically to the front/back faces.
   Desktop: pin the actions to the bottom-centre of the stage so the trio can stay
   vertically centred above it. (Mobile keeps them in the stacked flow.) */
@media (min-width: 641px) {
  /* CLAIM flows directly BELOW the card trio, centred — reveal-zone.active is a
     centred flex column, so the banner + cards + CLAIM read as one centred group
     (no absolute pin that left a big gap under the cards). */
  .hero-stage .reveal-in-actions,
  .hero-stage .reveal-zone.expanded .reveal-in-actions {
    /* position:relative (not static) so the z-index actually applies — keeps the
       same centred flow layout but guarantees CLAIM stacks above every in-stage
       sibling (card glow, flash-pulse, cert sheen, tier banner). */
    position: relative; margin: 18px auto 0; width: auto; min-width: 220px;
    display: flex; flex-direction: column; align-items: center; gap: 8px;
    text-align: center; border-top: none !important; padding-top: 0; z-index: 30;
  }
}
/* Actions always pinned at bottom of right panel */
.reveal-in-actions {
  flex-shrink: 0 !important;
  padding-top: 12px;
  margin-top: 8px;
  border-top: 1px solid rgba(200,168,75,0.1);
  /* Keep CLAIM above any reveal sibling (cert sheen, card glow, etc.) so it's
     always the tap target. */
  position: relative; z-index: 20;
}
.reveal-in-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 16px;
  border-top: 1px solid rgba(200,168,75,0.1);
  margin-top: 8px;
}
.reveal-hint-text {
  font-family: var(--font-mono);
  font-size: 10px;
  color: rgba(255,255,255,0.32);
  margin: 0 0 4px;
  text-align: center;
}

/* ================================================================
   ENHANCED RIP ANIMATION — shockwave rings + flash burst
   ================================================================ */
@keyframes shockwaveRing {
  0%   { transform: translate(-50%,-50%) scale(0); opacity: 0.9; }
  100% { transform: translate(-50%,-50%) scale(3.5); opacity: 0; }
}
@keyframes packColorFlash {
  0%   { opacity: 0; }
  15%  { opacity: 1; }
  40%  { opacity: 0.6; }
  100% { opacity: 0; }
}
@keyframes stageShake {
  0%,100% { transform: translateX(0) rotate(0deg); }
  10%  { transform: translateX(-10px) rotate(-0.6deg); }
  20%  { transform: translateX(10px) rotate(0.6deg); }
  30%  { transform: translateX(-8px) rotate(-0.3deg); }
  45%  { transform: translateX(8px) rotate(0.3deg); }
  60%  { transform: translateX(-4px); }
  75%  { transform: translateX(4px); }
  90%  { transform: translateX(-2px); }
}
.pack-shockwave {
  position: absolute;
  top: 50%; left: 50%;
  width: 160px; height: 160px;
  border-radius: 50%;
  transform: translate(-50%,-50%) scale(0);
  pointer-events: none;
  z-index: 20;
}
.pack-shockwave-1 {
  border: 4px solid #FFD15C;
  animation: shockwaveRing 0.7s ease-out forwards;
}
.pack-shockwave-2 {
  border: 3px solid #C490FF;
  animation: shockwaveRing 0.9s ease-out 0.12s forwards;
}
.pack-shockwave-3 {
  border: 2px solid #60C8FF;
  animation: shockwaveRing 1.1s ease-out 0.24s forwards;
}
.pack-color-burst {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at center,
    rgba(255,209,92,0.55) 0%,
    rgba(196,144,255,0.35) 30%,
    rgba(96,200,255,0.2) 60%,
    transparent 75%);
  animation: packColorFlash 0.8s ease-out forwards;
  pointer-events: none;
  z-index: 18;
}
.stage-frame.shaking {
  animation: stageShake 0.5s cubic-bezier(.36,.07,.19,.97);
}

/* ================================================================
   GLOBAL: no drag on card images; crisp click targets
   ================================================================ */
.floor-card img,
.sc-car-card img,
.card-face img,
.hoverable-card img {
  user-select: none;
  -webkit-user-drag: none;
  -moz-user-drag: none;
  pointer-events: none;
  draggable: false;
}
/* Also kill any inherited transform on click/active so no visual "drag" */
.hoverable-card:active { transform: none !important; }
.floor-card:active { transform: none !important; }
.floor-card,
.sc-car-card,
.card-flip-wrap {
  user-select: none;
  cursor: pointer;
}

/* ================================================================
   IDLE CAROUSEL: flashier front-card spotlight + faster pulse
   ================================================================ */
/* Amplify the scene glow when idle */
.sc-scene-glow {
  animation: sceneGlowPulse 1.2s ease-in-out infinite !important;
}
@keyframes sceneGlowPulse {
  0%, 100% { opacity: 0.5; transform: scale(1); }
  50%       { opacity: 1;   transform: scale(1.12); }
}
/* Front-facing carousel card gets a gold halo as it spins past */
.sc-car-card {
  transition: filter 0.15s ease, box-shadow 0.15s ease;
}

/* Reveal card faces — same subtle zoom as all other cards */
.hero-stage .card-face {
  transition: transform 0.25s ease-out, filter 0.2s ease;
}
.hero-stage .card-face:hover {
  transform: scale(1.06) !important;
  z-index: 100;
  filter: brightness(1.18);
  cursor: pointer;
}

/* Don't clip the cards during the rip + victory screen — the stage grows and
   users hover-zoom their cards, so the art must never be cropped. #main still
   has overflow-x: clip, so opening these up can't bring back sideways scroll. */
body.rip-reveal .hero-stage,
body.rip-reveal .hero-stage .stage-frame,
body.rip-reveal .reveal-zone,
body.rip-reveal .reveal-inner,
body.rip-reveal .card-flip-wrap,
body.rip-reveal .card-flip-inner,
body.craft-reveal .hero-stage,
body.craft-reveal .hero-stage .stage-frame,
body.craft-reveal .reveal-zone,
body.craft-reveal .reveal-inner,
body.craft-reveal .card-flip-wrap,
body.craft-reveal .card-flip-inner { overflow: visible; }

/* ================================================================
   PULL RATES — clean, balanced layout
   ================================================================ */
.pr-price-hero {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 18px;
  padding-bottom: 12px;
  border-bottom: 1px solid rgba(255,255,255,0.07);
}
.pr-price-num {
  font-family: var(--font-display);
  font-size: 28px;
  font-weight: 800;
  color: var(--gold);
  line-height: 1;
}
.pr-price-label {
  font-family: var(--font-mono);
  font-size: 10px;
  color: rgba(255,255,255,0.35);
  letter-spacing: 0.06em;
  padding-top: 2px;
}
/* Each tier: one clean row */
/* Two separate graphs in pull rates */
/* ── Pull Rates section: two stacked graphs ── */
.pull-rates-section {
  justify-content: center;   /* no chip here — center the two graphs */
  gap: 0;
}
/* Center the stat blocks in a constrained column so they line up with the
   centered chase + gallery content (cohesive layout). */
.pull-rates-section .pr-graph-block { width: 100%; max-width: 640px; margin-left: auto; margin-right: auto; }

.pr-graph-block {
  display: flex;
  flex-direction: column;
  gap: 0;
}
.pr-graph-block + .pr-graph-block {
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid rgba(255,255,255,0.07);
}

.pr-sub-label {
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold);
  opacity: 0.7;
  margin: 0 0 8px;
  display: flex;
  align-items: center;
  gap: 6px;
}
.pr-sub-label::before {
  content: '';
  display: inline-block;
  width: 2px;
  height: 10px;
  background: var(--gold);
  border-radius: 1px;
  opacity: 0.7;
}

.pr-tier-row {
  display: grid;
  grid-template-columns: 82px 1fr 40px;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  border-bottom: 1px solid rgba(255,255,255,0.04);
}
.pr-tier-row:last-child { border-bottom: none; }

.pr-tier-name-cell {
  display: flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
}
.pr-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
  box-shadow: 0 0 5px currentColor;
}
.pr-tier-label {
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: rgba(237,232,220,0.65);
}
.pr-tier-bar-track {
  height: 7px;
  background: rgba(255,255,255,0.07);
  border-radius: 10px;
  overflow: hidden;
  box-shadow: inset 0 1px 2px rgba(0,0,0,0.35);
}
.pr-tier-bar-fill {
  height: 100%;
  border-radius: 10px;
  opacity: 0.9;
  transition: width 0.7s cubic-bezier(0.4, 0, 0.2, 1);
  min-width: 0;   /* zero means zero — no phantom pixels */
}
.pr-avail-fill { opacity: 0.55; }
.pr-odds-row .pr-tier-bar-track { height: 11px; }  /* pull odds bars are taller */
.pr-tier-pct {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  color: rgba(237,232,220,0.65);
  text-align: right;
  white-space: nowrap;
}


/* ================================================================
   IDLE CARD TICKER — fast horizontal marquee of chase cards
   ================================================================ */
.sc-ticker {
  position: absolute;
  inset: 0;
  /* Horizontal clip wraps the conveyor; vertical stays visible so card glow
     isn't sliced off at the band edge. */
  overflow-x: clip;
  overflow-y: visible;
  display: flex;
  align-items: stretch;
  pointer-events: none;
}
.sc-ticker-track {
  display: flex;
  gap: 0;              /* NO gap — margin-right on each card keeps -50% math exact */
  height: 100%;
  align-items: center;
  animation: tickerScroll 8s linear infinite;
  will-change: transform;
  pointer-events: auto;
}
/*
  Cards are doubled: [set A][set B]
  We animate from 0 → -50% (exactly one set width)
  At -50% it looks identical to 0% → seamless, no jump
*/
@keyframes tickerScroll {
  0%   { transform: translateX(0); }
  100% { transform: translateX(var(--ticker-end, -50%)); }
}
/* sc-ticker-set = one lap of the conveyor. Width is set inline to the loop
   length (≥ viewport). When the cards don't fill the lap they spread out evenly
   (space-around keeps the gap uniform across the wrap), so a single instance of
   each card is never visible twice — the empty room becomes even spacing. */
.sc-ticker-set {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-around;
  height: 100%;
}
/* sc-ticker-card = spacing-only wrapper; fills the carousel band top→bottom and
   sizes its width to the card's true aspect ratio (set by the image). */
.sc-ticker-card {
  flex-shrink: 0;
  height: 92%;            /* nearly the full carousel height, small breathing room */
  min-width: 70px;        /* reserve space so a not-yet-loaded image won't collapse */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 11px;       /* gap between cards — keep adjacent glows separate (matches hpad in app.js) */
  box-sizing: border-box;
}

/* sc-ticker-inner = visual card sized by image — all effects here */
.sc-ticker-inner {
  height: 100%;
  width: auto;
  border-radius: 10px;
  overflow: hidden;
  cursor: pointer;
  position: relative;
  transition: filter 0.15s ease, transform 0.15s ease;
  flex-shrink: 0;
}
.sc-ticker-inner img {
  height: 100%;
  width: auto;
  display: block;
  object-fit: contain;     /* show the whole card — true aspect, never cropped */
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}
/* Only enlarge on real hover (mouse). On touch, :hover sticks after a drag —
   which left the last-touched card enlarged as it kept rotating. */
@media (hover: hover) {
  .sc-ticker-inner:hover {
    transform: scale(1.08);
    filter: brightness(1.2);
    z-index: 10;
  }
}
/* ── Rip landing reticle: a single thin green line down the centre marking where the
   spin lands the pulled card. Shown only during a rip. */
.sc-reticle {
  position: absolute; left: 50%; top: 2%; bottom: 2%; transform: translateX(-50%);
  width: 2px; z-index: 12; pointer-events: none;
  opacity: 0; transition: opacity .2s ease;
}
.sc-reticle.show { opacity: 1; }
.scr-frame {
  position: absolute; inset: 0;
  background: #36e07a;
  box-shadow: 0 0 6px rgba(54,224,122,.9);
}
.sc-reticle.landed .scr-frame { box-shadow: 0 0 12px rgba(54,224,122,1); }
/* The card the spin lands on lights up (held centred under the line for a beat). */
.sc-ticker-card.is-landed .sc-ticker-inner { transform: scale(1.07); filter: brightness(1.16); z-index: 11; }
.sc-ticker-card.is-landed .sc-ticker-inner::after {
  box-shadow: inset 0 0 0 2px #fff, 0 0 30px 5px var(--tier-glow-color, #8fd4ff) !important;
}
/* The signed-out LOGIN placeholder card pulses cyan when the spin lands on it. */
.sc-ticker-card.login-card.is-landed .sc-ticker-inner {
  animation: loginCardPulse .7s ease-in-out infinite alternate;
}
.sc-ticker-card.login-card.is-landed .sc-ticker-inner::after {
  box-shadow: inset 0 0 0 2px #aef2ff, 0 0 34px 6px #45d4ff !important;
}
@keyframes loginCardPulse { from { transform: scale(1.05); filter: brightness(1.05); } to { transform: scale(1.1); filter: brightness(1.25); } }
@media (prefers-reduced-motion: reduce) { .sc-ticker-card.login-card.is-landed .sc-ticker-inner { animation: none; } }

/* Tier glow inset border on inner card */
.sc-ticker-inner::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 10px;
  box-shadow: inset 0 0 0 2px var(--tier-glow-color, #EEEEEE);
  pointer-events: none;
}

/* ── Mobile: slim down the card outlines. The chunky rarity rings (2px border +
   a 1–2px ring shadow) read as far too heavy on a phone — thin them right down
   while keeping the colored glow that signals the tier. Placed AFTER every base
   .card-face / .floor-card / .sc-ticker-inner rule so it wins the cascade. */
@media (max-width: 640px) {
  /* On phones EVERY card border is exactly 1px regardless of rarity — there isn't
     the screen real-estate for thick rarity rings. The tier colour shows through
     the 1px border + a soft glow (no extra ring spread that would read as a 2nd,
     thicker border). */
  .card-face { border-width: 1px; }
  .card-flip-wrap.tier-uncommon .card-face   { box-shadow: 0 0 14px rgba(183,156,255,.3); }
  .card-flip-wrap.tier-rare .card-face       { box-shadow: 0 0 22px rgba(126,212,255,.35); }
  /* Legendary (foil-chase) pulses its ring every frame via foilChasePulse, which
     overrides any static box-shadow — so swap the ANIMATION for a ringless,
     glow-only variant on phones (a static rule alone can't beat the keyframes). */
  .card-flip-wrap.tier-foil-chase .card-face { animation-name: foilChasePulseSm; }
  .floor-card { border-width: 1px; }
  .admin-deck-item .floor-card { border-width: 1px !important; }
  .sc-ticker-inner::after { box-shadow: inset 0 0 0 1px var(--tier-glow-color, #EEEEEE); }
}
@keyframes foilChasePulseSm {
  0%, 100% { box-shadow: 0 0 24px rgba(255,209,92,.35); }
  50%      { box-shadow: 0 0 46px rgba(255,209,92,.65); }
}

/* old single-image pack-card-reveal removed */

/* ================================================================
   CARD BACK RIP ANIMATION — two-piece tear (replaces pack-shell visual)
   ================================================================ */

/* Visible foil pack for the rip animation — it shakes, then tears open before
   the card reveals. (Previously hidden; restored so the rip is actually shown.) */
.pack-shell {
  filter: drop-shadow(0 12px 30px rgba(0,0,0,.5));
}

/* Card back: large, centered, very visible, high z-index */
.pack-card-reveal {
  position: absolute;
  top: 50%; left: 50%;
  width: auto;
  height: 88%;           /* tall: nearly fills the hero stage */
  aspect-ratio: 5/7;     /* PSA slab proportions — gives explicit width */
  transform: translate(-50%, -50%);
  z-index: 20;           /* above everything except flash (z:30) */
  pointer-events: none;
  border-radius: 12px;
  overflow: hidden;      /* clips the two halves cleanly */
}

/* Two halves: each is 50% height of the card container */
.rip-tear-half {
  position: absolute;
  left: 0; right: 0;
  overflow: hidden;
}
.rip-tear-top {
  top: 0; height: 50%;
}
.rip-tear-bottom {
  bottom: 0; height: 50%;
}

/* Image in each half: full card image — CSS overflow clips to show only the half */
.rip-tear-half img {
  position: absolute;
  left: 0;
  width: 100%;
  height: auto;
  display: block;
  box-shadow: 0 0 40px rgba(0,0,0,0.8);
}
.rip-tear-top    img { top: 0; }
.rip-tear-bottom img { bottom: 0; }

/* ── Shake: card vibrates before tearing ── */
.pack-card-reveal.rip-shaking {
  animation: cardBackShake 0.1s ease-in-out infinite;
}
@keyframes cardBackShake {
  0%,100% { transform: translate(-50%,-50%) rotate(0deg); }
  20%      { transform: translate(-54%,-50%) rotate(-3deg); }
  50%      { transform: translate(-46%,-50%) rotate(3deg); }
  80%      { transform: translate(-54%,-52%) rotate(-2deg); }
}

/* ── Tear: halves fly apart ── */
.rip-tear-top.tearing {
  animation: ripTearTop 0.65s cubic-bezier(.4,0,.6,1) forwards;
}
.rip-tear-bottom.tearing {
  animation: ripTearBottom 0.65s cubic-bezier(.4,0,.6,1) forwards;
}
@keyframes ripTearTop {
  0%   { transform: translateY(0)     rotate(0deg);  opacity: 1; }
  100% { transform: translateY(-150%) rotate(-6deg); opacity: 0; }
}
@keyframes ripTearBottom {
  0%   { transform: translateY(0)    rotate(0deg); opacity: 1; }
  100% { transform: translateY(150%) rotate(6deg); opacity: 0; }
}

/* ================================================================
   FULLSCREEN RIP OVERLAY — rings expand beyond any container
   ================================================================ */
@keyframes ripVpRing {
  0%   { transform: scale(0);   opacity: 1; }
  100% { transform: scale(120); opacity: 0; }
}
@keyframes ripVpBurst {
  0%   { opacity: 1; }
  60%  { opacity: 0.6; }
  100% { opacity: 0; }
}

/* ================================================================
   REVEAL CARD BORDERS — always use tier color, never --line (pink)
   ================================================================ */
.hero-stage .card-flip-wrap.revealed .card-face {
  border: 2px solid var(--tier-glow-color, var(--tier-common));
  box-shadow:
    0 0 0 1px var(--tier-glow-color, var(--tier-common)),
    0 0 20px color-mix(in srgb, var(--tier-glow-color, var(--tier-common)) 40%, transparent);
  border-radius: 10px;
  overflow: hidden;
}
/* Foil Chase gets a more dramatic reveal border */
.hero-stage .card-flip-wrap.tier-foil-chase.revealed .card-face {
  box-shadow:                       /* glow only — thin border matches other tiers */
    0 0 30px rgba(255,209,92,0.6),
    0 0 60px rgba(255,209,92,0.3);
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
}
.card-hover-preview {
  position: fixed;
  z-index: 9000;
  width: 220px;
  background: rgba(20, 10, 44, 0.96);
  border: 1px solid var(--line);
  border-radius: 18px;
  padding: 12px 12px 14px;
  box-shadow: 0 8px 40px rgba(0,0,0,0.6), 0 0 0 1px rgba(200,168,75,0.08);
  pointer-events: none;
  opacity: 0;
  transform: scale(0.92) translateY(6px);
  transition: opacity 0.18s ease, transform 0.18s ease;
  overflow: hidden;
}
.card-hover-preview.visible {
  opacity: 1;
  transform: scale(1) translateY(0);
}
.hp-glow {
  position: absolute;
  inset: -30px;
  background: radial-gradient(circle at 50% 30%, color-mix(in srgb, var(--glow, var(--pink)) 20%, transparent), transparent 70%);
  pointer-events: none;
  z-index: 0;
}
.hp-img {
  position: relative;
  z-index: 1;
  width: 100%;
  height: auto;
  display: block;
  border-radius: 10px;
  border: 1px solid color-mix(in srgb, var(--tier-color, var(--pink)) 40%, transparent);
  margin-bottom: 10px;
  object-fit: contain;
  box-shadow: 0 0 20px color-mix(in srgb, var(--tier-color, var(--pink)) 35%, transparent);
}
.hp-info {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.hp-name {
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 700;
  color: var(--text-light);
  line-height: 1.25;
}
.hp-tier {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-bottom: 3px;
}
.hp-grade {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--gold);
  font-weight: 700;
}
.hp-meta {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--text-dim);
}
.hp-value {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 700;
  color: var(--gold);
  margin-top: 5px;
}

/* hoverable-card hover: see the scale(3) rule above */

/* ================================================================
   CINEMATIC ENTRANCE — RARE & FOIL CHASE
   Four-color palette for Foil Chase: white / blue / purple / gold
   SVG-vectorized background effects + 3D camera-zoom card entry
   ================================================================ */

/* ---------- Foil Chase multi-color glow on the card itself ---------- */
@keyframes foilChasePulse {
  0%,100% { box-shadow: 0 0 0 2px var(--tier-glow-color, #34E27C),
    0 0 30px color-mix(in srgb, var(--tier-glow-color, #34E27C) 40%, transparent),
    0 0 70px color-mix(in srgb, var(--tier-glow-color, #34E27C) 16%, transparent); }
  50% { box-shadow: 0 0 0 2px var(--tier-glow-color, #34E27C),
    0 0 46px color-mix(in srgb, var(--tier-glow-color, #34E27C) 70%, transparent),
    0 0 100px color-mix(in srgb, var(--tier-glow-color, #34E27C) 34%, transparent); }
}
.card-flip-wrap.tier-foil-chase .card-face {
  animation: foilChasePulse 2.2s ease-in-out infinite;
}
.card-flip-wrap.tier-foil-chase .card-face::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    115deg,
    transparent 40%,
    rgba(150,255,205,0.22) 47%,
    rgba(205,255,228,0.42) 50%,
    rgba(150,255,205,0.22) 53%,
    transparent 60%
  );
  background-repeat: no-repeat;
  background-size: 250% 100%;
  /* RARE, SYNCED shine: a soft green catch of light passes ONCE every 30s (~1.2s pass,
     then rest). --foil-delay (set in JS from a shared clock) phase-locks every foil card
     so the whole board catches the light at the same instant. */
  animation: foilShine 30s linear infinite;
  animation-delay: var(--foil-delay, 0s);
  opacity: 0;
  pointer-events: none;
}
/* One soft pass early in the cycle, invisible the rest — a rare glint, not a strobe. */
@keyframes foilShine {
  0%   { background-position: 150% 0; opacity: 0; }
  1%   { background-position: 132% 0; opacity: 1; }
  5%   { background-position: -50% 0; opacity: 1; }
  6%   { background-position: -68% 0; opacity: 0; }
  100% { background-position: 150% 0; opacity: 0; }
}

/* ---------- entrance screen shell ---------- */
.entrance-screen {
  position: fixed;
  inset: 0;
  z-index: 8000;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  overflow: hidden;
  opacity: 0;
  visibility: hidden;
}
.entrance-screen.active  { visibility: visible; }
.entrance-screen.done    { opacity: 0 !important; transition: opacity 0.38s ease; }

/* Vectorized SVG — hidden by default, shown programmatically for Foil Chase */
.entrance-svg {
  display: none;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 1;
  pointer-events: none;
}
.entrance-screen.foil-chase .entrance-svg { display: block; }

/* Vectorized SVG beams layer */
.entrance-beams {
  /* Big centred SQUARE so the rotating conic rays always overfill the viewport at
     every angle (a viewport-shaped box would show its edge spinning on mobile).
     Centred with margins because the *Beams keyframes own the transform. */
  position: absolute;
  inset: auto;
  top: 50%; left: 50%;
  width: 220vmax; height: 220vmax;
  margin: -110vmax 0 0 -110vmax;
  opacity: 0;
  will-change: transform, opacity;
}
.entrance-flash { position: absolute; inset: 0; opacity: 0; }

/* ---------- the card — displayed LARGE so pixels show at full size ---------- */
.entrance-card {
  position: relative;
  z-index: 2;
  /* Shrink-wrap the image so the rounded frame + glow hug the card exactly,
     and never let it exceed the viewport (no more cropping tall cards). */
  width: auto;
  max-width: 90vw;
  max-height: 84vh;
  border-radius: 16px;
  overflow: hidden;
  opacity: 0;
  will-change: transform, filter, opacity;
}
.entrance-card img {
  display: block;
  /* Fit fully inside BOTH a max width and the viewport height, preserving the
     card's aspect ratio — so the whole card is always in frame, any shape. */
  width: auto;
  height: auto;
  max-width: min(460px, 86vw);
  max-height: 84vh;
  border-radius: 16px;
  image-rendering: high-quality;
}
/* Per-tier sizing now lives on the image (the frame hugs it). */
.entrance-screen.rare .entrance-card img           { max-width: min(440px, 72vw); }
.entrance-screen.foil-chase-old .entrance-card img { max-width: min(520px, 78vw); }
@media (max-width: 640px) {
  /* Phones: lean on width, and cap height so the zoom-punch never clips. */
  .entrance-card img { max-height: 66vh; }
  .entrance-screen.rare .entrance-card img           { max-width: 82vw; }
  .entrance-screen.foil-chase-old .entrance-card img { max-width: 88vw; }
}

/* ==================================================================
   RARE entrance — lateral blast from off-screen
   ================================================================== */
.entrance-screen.rare {
  background: linear-gradient(135deg, rgba(8,16,42,0.97), rgba(4,8,26,0.99));
  animation: rareScreenIn 1.1s ease forwards;
}
.entrance-screen.rare .entrance-beams {
  background: conic-gradient(from 0deg at 50% 50%,
    transparent 0deg, rgba(126,212,255,0.14) 5deg, transparent 10deg,
    transparent 28deg, rgba(126,212,255,0.09) 33deg, transparent 38deg,
    transparent 56deg, rgba(126,212,255,0.14) 61deg, transparent 66deg,
    transparent 84deg, rgba(126,212,255,0.09) 89deg, transparent 94deg,
    transparent 112deg, rgba(126,212,255,0.14) 117deg, transparent 122deg,
    transparent 140deg, rgba(126,212,255,0.09) 145deg, transparent 150deg,
    transparent 168deg, rgba(126,212,255,0.14) 173deg, transparent 178deg
  );
  animation: rareBeams 1.1s ease-out forwards;
}
.entrance-screen.rare .entrance-flash {
  background: #6AACED;
  animation: rareFlash 1.1s ease forwards;
}
.entrance-screen.rare .entrance-card {
  box-shadow: 0 0 60px #6AACED, 0 0 120px #6AACED44, -20px 0 80px #6AACED33;
  animation: rareCardEntry 1.1s cubic-bezier(.22,1,.36,1) forwards;
}
@keyframes rareScreenIn {
  0% { opacity: 0; } 5% { opacity: 1; } 100% { opacity: 1; }
}
@keyframes rareBeams {
  0%   { opacity: 0; transform: scale(0.5) rotate(-40deg); }
  30%  { opacity: 1; transform: scale(1.1) rotate(0deg); }
  100% { opacity: 0.6; transform: scale(1.4) rotate(9deg); }
}
@keyframes rareFlash {
  0% { opacity: 0; } 8% { opacity: 0.6; } 20% { opacity: 0; } 100% { opacity: 0; }
}
@keyframes rareCardEntry {
  0%   { opacity: 0; transform: translateX(-160vw) rotate(-20deg) scale(0.65); filter: blur(16px) brightness(2.5); }
  30%  { opacity: 1; filter: blur(4px) brightness(1.8); }
  58%  { transform: translateX(14px) rotate(2.5deg) scale(1.07); filter: blur(0) brightness(1.3); }
  75%  { transform: translateX(-6px) rotate(-1deg) scale(0.97); }
  88%  { transform: translateX(3px) rotate(0.5deg) scale(1.01); }
  100% { transform: translateX(0) rotate(0deg) scale(1); opacity: 1; filter: blur(0) brightness(1); }
}

/* ==================================================================
   FOIL CHASE entrance — 3D camera zoom from pixel-level distance
   Uses perspective + translateZ for true depth, not just scale()
   Multi-color light show cycling white → blue → purple → gold
   ================================================================== */
.entrance-screen.foil-chase {
  perspective: 600px;
  background: radial-gradient(ellipse at center, rgba(12,4,28,0.99) 0%, rgba(2,0,8,1) 100%);
  animation: foilScreenIn 2.4s ease forwards;
}
/* Multi-color radial beam rings */
.entrance-screen.foil-chase .entrance-beams {
  animation: foilBeams 2.4s ease-out forwards;
  /* Four-color conic gradient — one color per quadrant */
  background: conic-gradient(from 0deg at 50% 50%,
    rgba(240,248,255,0.0)  0deg,
    rgba(240,248,255,0.18) 4deg,   /* white */
    rgba(240,248,255,0.0)  9deg,
    rgba(240,248,255,0.0)  19deg,
    rgba(96,200,255,0.14)  23deg,  /* blue */
    rgba(96,200,255,0.0)   28deg,
    rgba(96,200,255,0.0)   38deg,
    rgba(196,144,255,0.14) 42deg,  /* purple */
    rgba(196,144,255,0.0)  47deg,
    rgba(196,144,255,0.0)  57deg,
    rgba(255,209,92,0.18)  61deg,  /* gold */
    rgba(255,209,92,0.0)   66deg,
    rgba(255,209,92,0.0)   76deg,
    rgba(240,248,255,0.14) 80deg,
    rgba(240,248,255,0.0)  85deg,
    rgba(240,248,255,0.0)  95deg,
    rgba(96,200,255,0.18)  99deg,
    rgba(96,200,255,0.0)   104deg,
    rgba(96,200,255,0.0)   114deg,
    rgba(196,144,255,0.14) 118deg,
    rgba(196,144,255,0.0)  123deg,
    rgba(196,144,255,0.0)  133deg,
    rgba(255,209,92,0.14)  137deg,
    rgba(255,209,92,0.0)   142deg,
    rgba(255,209,92,0.0)   152deg,
    rgba(240,248,255,0.18) 156deg,
    rgba(240,248,255,0.0)  161deg,
    rgba(240,248,255,0.0)  171deg,
    rgba(96,200,255,0.14)  175deg,
    rgba(96,200,255,0.0)   180deg,
    rgba(196,144,255,0.18) 184deg,
    rgba(196,144,255,0.0)  189deg,
    rgba(255,209,92,0.14)  193deg,
    rgba(255,209,92,0.0)   198deg,
    rgba(240,248,255,0.14) 202deg,
    rgba(240,248,255,0.0)  207deg,
    rgba(96,200,255,0.18)  211deg,
    rgba(96,200,255,0.0)   216deg,
    rgba(196,144,255,0.14) 220deg,
    rgba(196,144,255,0.0)  225deg,
    rgba(255,209,92,0.18)  229deg,
    rgba(255,209,92,0.0)   234deg,
    rgba(240,248,255,0.14) 238deg,
    rgba(240,248,255,0.0)  243deg,
    rgba(96,200,255,0.14)  247deg,
    rgba(96,200,255,0.0)   252deg,
    rgba(196,144,255,0.18) 256deg,
    rgba(196,144,255,0.0)  261deg,
    rgba(255,209,92,0.14)  265deg,
    rgba(255,209,92,0.0)   270deg,
    rgba(240,248,255,0.18) 274deg,
    rgba(240,248,255,0.0)  279deg,
    rgba(96,200,255,0.14)  283deg,
    rgba(96,200,255,0.0)   288deg,
    rgba(196,144,255,0.14) 292deg,
    rgba(196,144,255,0.0)  297deg,
    rgba(255,209,92,0.18)  301deg,
    rgba(255,209,92,0.0)   306deg,
    rgba(240,248,255,0.14) 310deg,
    rgba(240,248,255,0.0)  315deg,
    rgba(96,200,255,0.18)  319deg,
    rgba(96,200,255,0.0)   324deg,
    rgba(196,144,255,0.14) 328deg,
    rgba(196,144,255,0.0)  333deg,
    rgba(255,209,92,0.14)  337deg,
    rgba(255,209,92,0.0)   342deg,
    rgba(240,248,255,0.18) 346deg,
    rgba(240,248,255,0.0)  351deg,
    rgba(96,200,255,0.14)  355deg,
    rgba(96,200,255,0.0)   360deg
  );
}
/* Multi-flash: white → blue → purple → gold in sequence */
.entrance-screen.foil-chase .entrance-flash {
  animation: foilFlash 2.4s ease forwards;
}
/* The card — extra wide so you really see pixel detail at scale(1) */
.entrance-screen.foil-chase .entrance-card {
  width: min(600px, 80vw);
  box-shadow:
    0 0 80px #FFD15C,
    0 0 0 2px #F0F8FF,
    0 0 200px #60C8FF33,
    0 0 320px #C490FF22;
  animation: foilCardEntry 2.4s cubic-bezier(.12,.98,.35,1) forwards;
}

@keyframes foilScreenIn {
  0% { opacity: 0; } 3% { opacity: 1; } 100% { opacity: 1; }
}
@keyframes foilBeams {
  0%   { opacity: 0; transform: scale(0.08) rotate(0deg); }
  15%  { opacity: 0.6; transform: scale(0.6) rotate(60deg); }
  40%  { opacity: 1; transform: scale(1.1) rotate(140deg); }
  70%  { opacity: 0.9; transform: scale(1.4) rotate(200deg); }
  100% { opacity: 0.7; transform: scale(1.8) rotate(260deg); }
}
@keyframes foilFlash {
  /* Four distinct color flashes cycling through the palette */
  0%   { opacity: 0; background: #F0F8FF; }
  12%  { opacity: 0; }
  15%  { opacity: 0.9; background: #F0F8FF; }  /* white burst */
  22%  { opacity: 0; background: #F0F8FF; }
  34%  { opacity: 0.8; background: #60C8FF; }  /* blue burst */
  40%  { opacity: 0; background: #60C8FF; }
  52%  { opacity: 0; background: #C490FF; }
  55%  { opacity: 0.75; background: #C490FF; } /* purple burst */
  62%  { opacity: 0; background: #C490FF; }
  74%  { opacity: 0; background: #FFD15C; }
  77%  { opacity: 0.85; background: #FFD15C; } /* gold burst */
  85%  { opacity: 0; }
  100% { opacity: 0; }
}

/* 3D camera-zoom keyframes — true perspective depth not just scale() */
@keyframes foilCardEntry {
  /*
   * Starts as a genuine pixel-level dot in 3D space.
   * perspective: 600px means translateZ(-120000) = 200x further than
   * the screen plane → the card is 1/200th of its display size = ~3px
   * at 600px width. Racing toward the camera at full resolution.
   */
  0%   {
    opacity: 0;
    transform: translateZ(-120000px) rotateY(720deg) rotateX(45deg);
    filter: blur(24px) brightness(6) saturate(2.5) hue-rotate(60deg);
  }
  8%   {
    opacity: 0.3;
    transform: translateZ(-60000px) rotateY(480deg) rotateX(28deg);
    filter: blur(18px) brightness(5) saturate(2) hue-rotate(40deg);
  }
  20%  {
    opacity: 0.7;
    transform: translateZ(-15000px) rotateY(200deg) rotateX(12deg);
    filter: blur(12px) brightness(3.5) saturate(1.5) hue-rotate(20deg);
  }
  38%  {
    opacity: 1;
    transform: translateZ(-1800px) rotateY(30deg) rotateX(4deg);
    filter: blur(5px) brightness(2.2) saturate(1.3);
  }
  55%  {
    transform: translateZ(120px) rotateY(-8deg) rotateX(-2deg);
    filter: blur(0px) brightness(1.5) saturate(1.15);
  }
  68%  {
    transform: translateZ(-40px) rotateY(4deg) rotateX(1deg);
    filter: blur(0) brightness(1.2);
  }
  80%  {
    transform: translateZ(18px) rotateY(-2deg) rotateX(-0.5deg);
    filter: blur(0) brightness(1.08);
  }
  90%  { transform: translateZ(-6px) rotateY(1deg); }
  96%  { transform: translateZ(3px) rotateY(-0.3deg); }
  100% {
    transform: translateZ(0) rotateY(0deg) rotateX(0deg);
    opacity: 1;
    filter: blur(0) brightness(1) saturate(1);
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
}


/* ═══════════════════════════════════════════════════════════════
   ADMIN PANEL — merged from admin.css
   ═══════════════════════════════════════════════════════════════ */

/* Admin overlay — full-screen, same page as game */
#adminOverlay {
  position: fixed;
  inset: 0;
  z-index: 12000;
  /* Same neon-casino gradient as the Rip/Craft pages (the <html> base) so Admin
     reads as the same room instead of a flat panel. */
  background-color: #04050d;
  background-image:
    radial-gradient(ellipse 130% 58% at 50% 102%, rgba(255,45,155,0.20), transparent 62%),
    radial-gradient(circle at 12% -6%, rgba(0,229,255,0.14), transparent 55%),
    radial-gradient(circle at 88% 6%, rgba(170,85,255,0.15), transparent 55%),
    linear-gradient(180deg, #04050d 0%, #080719 46%, #120a30 100%);
  background-repeat: no-repeat;
  background-size: cover;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
/* Ambient FX layer sits behind the admin content (which is raised above it). */
#adminOverlay > .admin-fx { position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
#adminOverlay > .admin-scroll-area { position: relative; z-index: 1; }
/* The casino edge-light marquee, shown on Admin too (gold theme), matching Rip/Craft. */
body.admin-active .admin-fx .casino-lights {
  display: block; position: fixed; z-index: 0; pointer-events: none;
  filter: drop-shadow(0 0 4px rgba(255,209,92,.7)); opacity: .72;
  --bulb1: #ffd15c; --bulb2: #C8A84B;
}
body.admin-active .admin-fx .casino-lights.cl-top,
body.admin-active .admin-fx .casino-lights.cl-bot {
  left: 0; right: 0; height: 16px;
  background-image:
    radial-gradient(circle 3px at 14px 8px, var(--bulb1) 0 3px, transparent 4px),
    radial-gradient(circle 3px at 42px 8px, var(--bulb2) 0 3px, transparent 4px);
  background-size: 56px 16px; background-repeat: repeat-x;
  animation: marqueeChaseX 4.5s linear infinite, marqueeTwinkle 2.8s ease-in-out infinite;
}
body.admin-active .admin-fx .casino-lights.cl-left,
body.admin-active .admin-fx .casino-lights.cl-right {
  top: 0; bottom: 0; width: 16px;
  background-image:
    radial-gradient(circle 3px at 8px 14px, var(--bulb1) 0 3px, transparent 4px),
    radial-gradient(circle 3px at 8px 42px, var(--bulb2) 0 3px, transparent 4px);
  background-size: 16px 56px; background-repeat: repeat-y;
  animation: marqueeChaseY 4.5s linear infinite, marqueeTwinkle 2.8s ease-in-out .9s infinite;
}
.admin-fx .casino-lights.cl-top { top: 0; } .admin-fx .casino-lights.cl-bot { bottom: 0; }
.admin-fx .casino-lights.cl-left { left: 0; } .admin-fx .casino-lights.cl-right { right: 0; }
/* Float the top bar (the centred userbar + online count) directly over the gradient,
   like the Rip/Craft header — no opaque panel/border behind it. */
#adminOverlay #adm-live-panel { background: transparent !important; border-bottom: none !important; }
/* While Admin is open its own FX renders over the opaque overlay; the page's own
   body-level FX would just animate hidden BEHIND it, so pause them to save work. */
body.admin-active > .casino-lights,
body.admin-active > .fx-scene { display: none !important; }

/* Controls bar */
.admin-controls-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 16px;
  border-bottom: 1px solid var(--line);
  flex-shrink: 0;
  flex-wrap: wrap;
  min-height: 48px;
}
.acb-divider { width:1px; height:24px; background:var(--line); flex-shrink:0; }
.acb-label { font-family:var(--font-mono); font-size:10px; color:var(--text-dim); letter-spacing:0.06em; text-transform:uppercase; white-space:nowrap; }
.acb-status { font-family:var(--font-mono); font-size:11px; color:var(--gold); }
#adm-psa-section { display:flex; align-items:center; gap:8px; min-width:0; }
#adm-psa-section input.cert-input { width:260px; max-width:100%; min-width:0; }
/* Look Up sits INSIDE the cert field (right edge) to save a whole row of space. */
.cert-input-wrap { position:relative; display:inline-flex; align-items:center; min-width:0; }
.cert-input-wrap .cert-input { width:260px; padding-right:84px; }
.cert-lookup-btn {
  position:absolute; right:4px; top:4px; bottom:4px;
  padding:0 12px; font-size:11px; border-radius:6px; line-height:1;
  display:inline-flex; align-items:center; white-space:nowrap;
}
/* Mobile: the cert field + its inline Look Up span the full width on one row;
   the +Generic Card / Restock All buttons drop to the next row. Desktop is
   unchanged (it already fits on one line). */
@media (max-width: 640px) {
  .deck-top-row #adm-psa-section { flex:1 0 100%; }
  #adm-has-key, #adm-no-key { width:100%; }
  #adm-psa-section .cert-input-wrap { flex:1 1 auto; width:100%; }
  #adm-psa-section .cert-input-wrap .cert-input,
  #adm-psa-section input.cert-input { width:100%; }
}

/* Deck area */
.admin-deck-area { flex:0 0 auto; display:flex; flex-direction:column; padding:8px 12px 12px; }
/* Everything groups to the LEFT (no stretching/pushing to the far right). */
.deck-top-row { display:flex; align-items:center; gap:10px; margin-bottom:8px; flex-shrink:0; flex-wrap:wrap; }
/* +Generic Card and Restock All are the same kind of action — give them one
   identical, squared (not pill) appearance, matching the Look Up button. */
.deck-top-row #adm-restock-all,
.deck-top-row #adm-generic-card {
  flex-shrink: 0;
  border-radius: 6px;
  padding: 7px 14px;
  font-size: 12px;
  font-weight: 700;
}
#adm-card-library { flex:0 0 auto; overflow:visible; }
.card-library {
  display: grid;
  /* At most 10 columns: each column is at least 140px, but never narrower than
     a tenth of the row — so a wide screen caps at 10, narrow screens show fewer. */
  grid-template-columns: repeat(auto-fill, minmax(max(140px, (100% - 9 * 12px) / 10), 1fr));
  gap: 12px;
  align-items: start;
  padding-bottom: 16px;
}
@media (max-width: 640px) {
  /* 4 cards across on mobile — consistent with the crafting/collection gallery. */
  .card-library { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 7px; }
}

/* Deck designation tags: which cards are featured (Chase) or in the carousel
   rotation, on the deck thumbnail. */
.adm-card-tag {
  position: absolute; top: 5px; left: 5px; z-index: 4;
  font: 800 8.5px/1 var(--font-mono, ui-monospace); letter-spacing: .04em;
  padding: 3px 6px; border-radius: 5px; white-space: nowrap; pointer-events: none;
  box-shadow: 0 1px 4px rgba(0,0,0,.5);
}
.adm-card-tag.tag-chase    { background: linear-gradient(135deg,#ffe293,#f3b63c); color:#2b2107; border:1px solid #ffd15c; }
.adm-card-tag.tag-rotation { background: rgba(12,22,36,.92); color:#8fd2ff; border:1px solid #6cc6ff; }
@media (max-width: 640px) { .adm-card-tag { font-size: 7.5px; padding: 2px 4px; top: 3px; left: 3px; } }

/* PSA result card */
.psa-result { display:flex; flex-direction:column; gap:20px; }
.psa-card-result {
  display:grid; grid-template-columns:auto 1fr; gap:22px; align-items:start;
  border:1px solid var(--line); border-radius:16px; padding:20px;
  background:rgba(255,255,255,0.025);
}
.psa-card-result.psa-loading { opacity:0.55; }
.psa-card-imgs { display:flex; flex-direction:column; gap:10px; width:130px; }
.psa-card-img { width:100%; height:auto; display:block; border-radius:8px; border:1px solid var(--line); object-fit:contain; background:rgba(255,255,255,0.03); }
.psa-card-img-placeholder { width:130px; min-height:100px; border:2px dashed var(--line); border-radius:10px; display:flex; align-items:center; justify-content:center; font-family:var(--font-mono); font-size:11px; color:var(--text-dim); text-align:center; padding:8px; }
.psa-card-info { min-width:0; }
.psa-card-name { font-family:var(--font-display); font-size:18px; font-weight:700; color:var(--text-light); margin-bottom:3px; }
.psa-card-sub { font-family:var(--font-mono); font-size:11px; color:var(--text-dim); margin-bottom:12px; }
.psa-in-vault { font-family:var(--font-mono); font-size:11px; color:var(--skyblue); margin-bottom:10px; }
.psa-fields { display:grid; grid-template-columns:140px 1fr; gap:5px 12px; font-family:var(--font-mono); font-size:12px; margin-bottom:14px; }
.psa-field-label { color:var(--text-dim); }
.psa-field-val { color:var(--text-light); word-break:break-all; }
.psa-field-val.grade { color:var(--gold); font-weight:700; font-size:14px; }
.psa-error-card { border:1px solid rgba(255,159,176,0.3); border-radius:12px; padding:14px 18px; font-family:var(--font-mono); font-size:12.5px; color:#FF9FB0; display:flex; gap:12px; align-items:center; flex-wrap:wrap; }

/* Tier import buttons */
.tier-import-row { padding-top:14px; border-top:1px solid var(--line); }
.tier-import-label { font-family:var(--font-mono); font-size:11px; color:var(--text-dim); text-transform:uppercase; letter-spacing:0.07em; display:block; margin-bottom:10px; }
.tier-import-btns { display:flex; gap:8px; flex-wrap:wrap; margin-bottom:10px; }
.tier-btn { font-family:var(--font-display); font-size:13px; font-weight:700; border:none; padding:10px 18px; border-radius:999px; cursor:pointer; letter-spacing:0.05em; transition:filter 0.12s, transform 0.1s; }
.tier-btn:hover:not(:disabled) { filter:brightness(1.12); transform:translateY(-1px); }
.tier-btn:disabled { opacity:0.4; cursor:not-allowed; }
.tier-btn.common    { background:#EEEEEE; color:#07090E; }
.tier-btn.uncommon  { background:#60C8FF; color:#07090E; }
.tier-btn.rare      { background:#C490FF; color:#07090E; }
.tier-btn.foil-chase { background:linear-gradient(135deg,#EDE8DC 0%,#60C8FF 30%,#C490FF 65%,#FFD15C 100%); color:#07090E; font-weight:800; }
.tier-value-row { font-family:var(--font-mono); font-size:12px; color:var(--text-dim); margin-bottom:8px; }
.tier-value-input { background:var(--bg-mid); border:1px solid var(--line); color:var(--text-light); padding:6px 10px; font-family:var(--font-mono); font-size:13px; border-radius:8px; width:110px; }
.tier-value-input.needs-value { border-color:#ff6b6b; box-shadow:0 0 0 2px rgba(255,80,80,.3); animation: needsValueShake .3s ease; }
@keyframes needsValueShake { 0%,100%{transform:translateX(0)} 25%{transform:translateX(-4px)} 75%{transform:translateX(4px)} }

/* Admin shared */
.status-msg { font-family:var(--font-mono); font-size:12px; margin-top:8px; min-height:16px; }
.status-msg.ok  { color:var(--skyblue); }
.status-msg.err { color:#FF9FB0; }
.btn-sm { font-size:12px !important; padding:7px 14px !important; }

/* Library edit panel buttons — were unstyled browser defaults; give them the
   same rounded-pill anime look as the rest of the app for a consistent feel. */
.lep-tier-btn {
  font:700 11px/1 var(--font-display); padding:6px 13px; border-radius:999px;
  border:1px solid color-mix(in srgb, var(--tc,#fff) 55%, transparent);
  color:rgba(255,255,255,.55); background:transparent; cursor:pointer;
  transition:background .12s, color .12s, transform .1s;
}
.lep-tier-btn:hover { color:#fff; transform:translateY(-1px); }
.lep-tier-btn.active { background:color-mix(in srgb, var(--tc,#fff) 18%, transparent); color:var(--tc,#fff); border-color:var(--tc,#fff); }
.lep-save {
  font:800 13px/1 var(--font-display); padding:10px 22px; border-radius:999px;
  border:none; cursor:pointer; color:#07090e;
  background:linear-gradient(180deg, var(--gold), #e89a26);
  transition:filter .14s, transform .1s;
}
.lep-save:hover { filter:brightness(1.08); transform:translateY(-1px); }
.lep-save:active { transform:translateY(1px); }
.lep-save:disabled { opacity:.5; cursor:not-allowed; transform:none; }
.lep-close {
  -webkit-appearance:none; appearance:none; cursor:pointer; line-height:1;
  width:28px; height:28px; border-radius:999px; font-size:14px;
  border:1px solid rgba(255,255,255,.16); background:rgba(255,255,255,.05); color:#cdd6e2;
  transition:background .12s, color .12s;
}
.lep-close:hover { background:rgba(255,90,120,.18); color:#ff9fb0; border-color:rgba(255,120,140,.5); }
.danger:hover { color:#FF9FB0 !important; border-color:#FF9FB0 !important; }
.empty-state { font-family:var(--font-mono); font-size:12px; color:var(--text-dim); text-align:center; padding:24px; border:1px dashed var(--line); border-radius:12px; }
.filter-select { background:var(--bg-mid); border:1px solid var(--line); color:var(--text-light); padding:9px 12px; font-family:var(--font-body); font-size:13px; border-radius:8px; }
.cert-input { background:var(--bg-mid); border:1px solid var(--line); color:var(--text-light); padding:9px 12px; font-family:var(--font-mono); font-size:13px; border-radius:8px; }
.cert-input:focus { outline:2px solid var(--gold); outline-offset:1px; }

/* Admin deck cards */
.admin-item-pulled .floor-card img { filter:grayscale(85%) brightness(0.4); }
.pulled-overlay { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; background:rgba(7,9,14,0.45); z-index:4; pointer-events:none; }
.pulled-badge { font-family:var(--font-display); font-size:11px; font-weight:900; letter-spacing:0.2em; color:rgba(237,232,220,0.8); background:rgba(7,9,14,0.85); border:1px solid rgba(237,232,220,0.3); border-radius:5px; padding:4px 10px; }
.admin-deck-item { display:flex; flex-direction:column; gap:6px; align-self:start; }
.admin-deck-item .floor-card { overflow:hidden; cursor:pointer; position:relative; box-shadow:none !important; border-width:2px !important; border-style:solid !important; border-color:var(--tier-glow-color,#FFFFFF) !important; }
.admin-deck-item .floor-card img { width:100%; height:auto; object-fit:contain; object-position:center top; display:block; }
.admin-deck-item .floor-card.hoverable-card:hover { transform:none !important; filter:brightness(1.1); }
.admin-card-controls { background:rgba(10,13,20,0.97); border:1px solid var(--line); border-radius:8px; padding:8px; display:flex; flex-direction:column; gap:6px; }
.aco-tiers { display:flex; flex-wrap:wrap; gap:4px; }
.aco-tier-btn { font-family:var(--font-mono); font-size:9px; font-weight:700; padding:3px 9px; border-radius:999px; border:1px solid color-mix(in srgb,var(--tc,#FFF) 50%,transparent); color:rgba(255,255,255,0.4); background:transparent; cursor:pointer; letter-spacing:0.04em; transition:background 0.1s,color 0.1s; }
.aco-tier-btn:hover { background:rgba(255,255,255,0.08); color:rgba(255,255,255,0.8); }
.aco-tier-btn.active { background:color-mix(in srgb,var(--tc,#FFF) 15%,transparent); color:var(--tc,#FFF); border-color:var(--tc,#FFF); }
.aco-row { display:flex; align-items:center; gap:6px; }
.aco-value { width:60px; padding:4px 6px; font-size:11px; border-radius:5px; border:1px solid var(--line); background:rgba(255,255,255,0.06); color:var(--text-light); font-family:var(--font-mono); }
.aco-cr-label { font-family:var(--font-mono); font-size:10px; color:rgba(255,255,255,0.35); flex:1; }
.aco-actions-row { display:flex; gap:4px; flex-wrap:nowrap; }
.aco-del { background:transparent; border:1px solid rgba(255,100,100,0.3); color:rgba(255,120,120,0.8); border-radius:5px; padding:4px 8px; font-size:11px; cursor:pointer; flex-shrink:0; white-space:nowrap; transition:background 0.1s; }
.aco-del:hover { background:rgba(255,80,80,0.15); color:#ffaaaa; }
.aco-save { flex:1; min-width:0; }
.aco-restock { background:rgba(96,200,255,0.1); border:1px solid rgba(96,200,255,0.35); color:#60C8FF; border-radius:5px; padding:4px 8px; font-size:10px; font-family:var(--font-mono); cursor:pointer; white-space:nowrap; transition:background 0.1s; flex-shrink:0; }
.aco-restock:hover { background:rgba(96,200,255,0.2); }
.aco-restock-prominent { background:rgba(200,168,75,0.15); border:1px solid rgba(200,168,75,0.6) !important; color:#C8A84B !important; font-weight:700; flex:1; text-align:center; padding:6px 10px; }
.aco-restock-prominent:hover { background:rgba(200,168,75,0.3); }

/* Admin custom card name edit field */
.aco-name {
  width: 100%;
  padding: 5px 8px;
  font-size: 11px;
  border-radius: 5px;
  border: 1px solid var(--line);
  background: rgba(255,255,255,0.06);
  color: var(--text-light);
  font-family: var(--font-body);
  box-sizing: border-box;
  margin-bottom: 2px;
}
.aco-name:focus { outline: 1px solid var(--gold); }

/* Custom card name overlay — shown on placeholder cards in all views */
.floor-card {
  position: relative; /* ensure overlay can anchor */
}
.floor-card img {
  display: block;     /* removes baseline whitespace gap (fixes gallery bottom border) */
}
/* Just-crafted highlight: a pulsing rarity-coloured glow around the card the moment you
   land in the gallery after crafting it, so you see exactly which card just joined the deck. */
.floor-card.cc-just-crafted {
  z-index: 4; border-radius: 10px;
  animation: craftedGlow 1s ease-in-out infinite;
}
@keyframes craftedGlow {
  0%, 100% {
    box-shadow: 0 0 0 2px var(--tier-glow-color, #45b1ff),
                0 0 16px 2px color-mix(in srgb, var(--tier-glow-color, #45b1ff) 55%, transparent);
  }
  50% {
    box-shadow: 0 0 0 3px var(--tier-glow-color, #45b1ff),
                0 0 34px 9px color-mix(in srgb, var(--tier-glow-color, #45b1ff) 85%, transparent);
  }
}
@media (prefers-reduced-motion: reduce) {
  .floor-card.cc-just-crafted { animation: none;
    box-shadow: 0 0 0 3px var(--tier-glow-color, #45b1ff), 0 0 26px 6px color-mix(in srgb, var(--tier-glow-color, #45b1ff) 70%, transparent); }
}
.custom-card-name {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 20px 8px 8px;
  background: linear-gradient(to bottom, transparent, rgba(7,9,14,0.88) 60%);
  font-family: var(--font-display);
  font-size: 11px;
  font-weight: 700;
  color: var(--text-light);
  text-align: center;
  letter-spacing: 0.05em;
  line-height: 1.3;
  pointer-events: none;
  border-radius: 0 0 10px 10px;
}
.hero-stage .custom-card-name {
  font-size: 14px;
  padding: 30px 12px 12px;
}

/* Admin save feedback flash — a quick pop + gold glow ring so a Save is felt. */
@keyframes savedFlash {
  0%   { transform: scale(1);    outline: 2px solid var(--gold); box-shadow: 0 0 0 0 rgba(200,168,75,.0); }
  25%  { transform: scale(1.05); outline: 2px solid var(--gold); box-shadow: 0 0 22px 3px rgba(200,168,75,.85); }
  60%  { transform: scale(0.99); }
  100% { transform: scale(1);    outline: 2px solid transparent;  box-shadow: 0 0 0 0 rgba(200,168,75,.0); }
}
.admin-deck-item.just-saved {
  animation: savedFlash 0.85s cubic-bezier(.34,1.4,.5,1) forwards;
  border-radius: 10px;
  position: relative;
  z-index: 5;
}

/* ═══════════════════════════════════════════════════════════════
   FOIL CHASE EFFECT — applied to all foil-chase tier cards
   everywhere in the app (gallery, chase panel, carousel, admin)
   ═══════════════════════════════════════════════════════════════ */

/* Legendary (foil-chase) border glow — uses the RARITY colour, not a fixed gold. */
.floor-card.tier-foil-chase,
.sc-ticker-inner.tier-foil-chase {
  animation: foilChasePulse 1.8s ease-in-out infinite;
  border-color: var(--tier-glow-color) !important;
}

/* NEON ANIME GREEN shine — a soft green catch of light that passes RARELY (once every
   30s) and SYNCED across every foil card (see foilShine + --foil-delay). */
.floor-card.tier-foil-chase::after,
.sc-ticker-inner.tier-foil-chase::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(
    115deg,
    transparent 40%,
    rgba(150,255,205,0.22) 47%,
    rgba(205,255,228,0.42) 50%,
    rgba(150,255,205,0.22) 53%,
    transparent 60%
  );
  background-repeat: no-repeat;
  background-size: 250% 100%;
  animation: foilShine 30s linear infinite;
  animation-delay: var(--foil-delay, 0s);
  opacity: 0;
  pointer-events: none;
  z-index: 2;
}

/* Make sure floor-card position:relative is set (it already is, but belt+suspenders) */
.floor-card.tier-foil-chase {
  position: relative;
  overflow: hidden;
}

/* CAROUSEL foil-chase: the shared foilChasePulse throws an 80px glow, which both
   bleeds into the neighbouring card (looking like ONE combined border glow) and
   spills past the band's top/bottom where it gets clipped. In the conveyor we
   use a TIGHT glow that hugs each card — individual, and small enough to sit
   inside the band's breathing room so it's never cropped. */
@keyframes scFoilPulse {
  0%   { box-shadow: 0 0 0 2px #FFD15C, 0 0 9px #FFD15Caa; }
  20%  { box-shadow: 0 0 0 2px #F0F8FF, 0 0 9px #F0F8FFaa; }
  45%  { box-shadow: 0 0 0 2px #60C8FF, 0 0 10px #60C8FFbb; }
  70%  { box-shadow: 0 0 0 2px #C490FF, 0 0 9px #C490FFaa; }
  100% { box-shadow: 0 0 0 2px #FFD15C, 0 0 9px #FFD15Caa; }
}
.sc-ticker-inner.tier-foil-chase { animation: scFoilPulse 1.8s ease-in-out infinite; }

/* ── Foil glow is STATIC everywhere ────────────────────────────────────────────
   The pulsing box-shadow animation repaints a large blurred area every frame on
   every visible foil card, which makes the carousel spin feel heavy. Replace it
   with a painted-once static glow on all foil cards (carousel, chase grid,
   gallery, reveal). The !important overrides every (duplicated) pulse rule. */
.floor-card.tier-foil-chase,
.sc-ticker-inner.tier-foil-chase {
  animation: none !important;
  /* GLOW ONLY — no thick ring. VIVID rarity-colour glow (green for Legendary) with a tight
     bright core, matching the border + the fast green shine streaks. */
  box-shadow: 0 0 7px color-mix(in srgb, var(--tier-glow-color) 95%, transparent),
              0 0 20px color-mix(in srgb, var(--tier-glow-color) 75%, transparent) !important;
}
.card-flip-wrap.tier-foil-chase .card-face {
  animation: none !important;
  box-shadow: 0 0 10px color-mix(in srgb, var(--tier-glow-color) 95%, transparent),
              0 0 30px color-mix(in srgb, var(--tier-glow-color) 70%, transparent) !important;
}

/* Continuous shine across a RUN of adjacent foil-chase cards: one highlight that
   flows THROUGH the whole run rather than each card shimmering independently.
   JS sets --foil-i (position in run) and --foil-run-len (run length). The sweep
   period spans the entire run and each card is phase-offset by one card, so the
   bright band appears to travel from one card into the next. */
.floor-card.tier-foil-chase.foil-run::after,
.sc-ticker-inner.tier-foil-chase.foil-run::after {
  /* Runs no longer travel card-to-card at their own tempo (that read as cards shimmering
     out of step). Every foil card — in a run or not — uses the one synced rare shine. */
  animation: foilShine 30s linear infinite;
  animation-delay: var(--foil-delay, 0s);
}

/* Admin deck card: click image to toggle pulled state */
.admin-deck-item .floor-card {
  cursor: pointer;
}
.admin-deck-item .floor-card:hover::before {
  content: attr(data-toggle-hint);
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: rgba(7,9,14,0.82);
  color: var(--text-light);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.12em;
  padding: 5px 10px;
  border-radius: 5px;
  border: 1px solid rgba(255,255,255,0.18);
  white-space: nowrap;
  z-index: 8;
  pointer-events: none;
}

/* Admin pulled state: controls greyed out, only Restock is live */
.aco-pulled-state {
  opacity: 0.38;
  pointer-events: none;
}
.aco-pulled-state .aco-restock-active {
  opacity: 1;
  pointer-events: auto;
}
/* Disabled buttons look muted */
.admin-card-controls button:disabled,
.admin-card-controls input:disabled {
  cursor: not-allowed;
}

/* PSA result grid — prominent card grid, not a tiny scrollable list */
#adm-psa-result {
  display: grid;
  /* Cap at 10 columns (like the deck), staying responsive on narrower screens. */
  grid-template-columns: repeat(auto-fill, minmax(max(220px, (100% - 9 * 16px) / 10), 1fr));
  gap: 16px;
  padding: 16px;
  align-items: start;
  grid-auto-rows: auto;
  border-top: 2px solid var(--gold);
  background: var(--bg-mid);
  margin-top: 8px;
}
#adm-psa-result:empty { display: none; }
/* Section label above PSA results */
#adm-psa-result::before {
  content: 'PSA LOOKUP RESULTS';
  grid-column: 1 / -1;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: var(--gold);
  padding-bottom: 8px;
  border-bottom: 1px solid rgba(200,168,75,0.25);
}

/* Compact PSA grid card */
.psa-gc {
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.psa-gc.psa-loading { opacity: 0.5; }
.psa-gc-img {
  width: 100%;
  aspect-ratio: 5/7;
  background: var(--bg-mid);
  position: relative;
  overflow: hidden;
}
.psa-gc-img img {
  width: 100%; height: 100%;
  object-fit: contain;
  background: var(--bg-mid);
  display: block;
}
.psa-gc-img-placeholder {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%;
  font-family: var(--font-mono); font-size: 10px; color: var(--text-dim);
  text-align: center; padding: 8px; box-sizing: border-box;
}
.psa-gc-grade-badge {
  position: absolute; bottom: 6px; left: 50%; transform: translateX(-50%);
  background: rgba(7,9,14,0.85); color: var(--gold);
  font-family: var(--font-mono); font-size: 11px; font-weight: 700;
  padding: 2px 8px; border-radius: 4px; white-space: nowrap;
}
.psa-gc-body {
  padding: 8px;
  display: flex; flex-direction: column; gap: 5px;
  flex: 1;
}
.psa-gc-name {
  font-family: var(--font-display); font-size: 11px; font-weight: 700;
  color: var(--text-light); line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.psa-gc-meta {
  font-family: var(--font-mono); font-size: 9px; color: var(--text-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.psa-gc-owned {
  font-family: var(--font-mono); font-size: 9px; color: var(--skyblue);
}
.psa-gc-tiers {
  display: grid; grid-template-columns: 1fr 1fr; gap: 3px; margin-top: 2px;
}
.psa-gc-tiers .tier-btn {
  font-size: 9px !important; padding: 4px 2px !important;
  border-radius: 5px; letter-spacing: 0;
}
.psa-gc-value {
  display: flex; align-items: center; gap: 5px;
}
.psa-gc-value input {
  width: 60px; padding: 3px 6px; font-size: 11px;
  border-radius: 5px; border: 1px solid var(--line);
  background: rgba(255,255,255,0.06); color: var(--text-light);
  font-family: var(--font-mono);
}
.psa-gc-value span { font-family: var(--font-mono); font-size: 10px; color: var(--text-dim); }
.psa-gc-status {
  font-family: var(--font-mono); font-size: 9px; min-height: 12px;
}

/* PSA card field grid in compact column */
.psa-gc-fields {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 2px 8px;
  font-family: var(--font-mono);
  font-size: 9.5px;
  margin: 4px 0;
}
.psa-gc-fl { color: var(--text-dim); white-space: nowrap; }
.psa-gc-fv { color: var(--text-light); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }


.psa-gc.psa-gc-importing {
  animation: cardHopUp 0.6s cubic-bezier(0.4,0,0.2,1) forwards;
  pointer-events: none;
}

/* Admin: single scrollable area containing deck + PSA results */
.admin-scroll-area {
  flex: 1;
  overflow-y: auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* Admin deck: PSA info shown below card image for PSA cards */
.admin-card-psa-info {
  padding: 6px 6px 2px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.aci-name {
  font-family: var(--font-display);
  font-size: 11px;
  font-weight: 700;
  color: var(--text-light);
  line-height: 1.3;
  word-break: break-word;
}
.aci-grade {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 800;
  color: var(--gold);
}
.aci-meta {
  font-family: var(--font-mono);
  font-size: 9.5px;
  color: var(--text-dim);
  line-height: 1.4;
}
.aci-cert {
  font-family: var(--font-mono);
  font-size: 9px;
  color: rgba(200,168,75,0.5);
}


/* Gallery tab rainbow flash after CLAIM */
@keyframes galleryRainbow {
  0%   { background-color: hsla(0,  100%,58%,0.55); box-shadow: 0 0 18px hsla(0,  100%,58%,0.9), inset 0 0 8px rgba(255,255,255,0.3); }
  17%  { background-color: hsla(60, 100%,52%,0.55); box-shadow: 0 0 18px hsla(60, 100%,52%,0.9), inset 0 0 8px rgba(255,255,255,0.3); }
  33%  { background-color: hsla(120,100%,42%,0.55); box-shadow: 0 0 18px hsla(120,100%,42%,0.9), inset 0 0 8px rgba(255,255,255,0.3); }
  50%  { background-color: hsla(200,100%,55%,0.55); box-shadow: 0 0 18px hsla(200,100%,55%,0.9), inset 0 0 8px rgba(255,255,255,0.3); }
  66%  { background-color: hsla(270,100%,60%,0.55); box-shadow: 0 0 18px hsla(270,100%,60%,0.9), inset 0 0 8px rgba(255,255,255,0.3); }
  83%  { background-color: hsla(320,100%,58%,0.55); box-shadow: 0 0 18px hsla(320,100%,58%,0.9), inset 0 0 8px rgba(255,255,255,0.3); }
  100% { background-color: hsla(360,100%,58%,0.55); box-shadow: 0 0 18px hsla(360,100%,58%,0.9), inset 0 0 8px rgba(255,255,255,0.3); }
}
.gallery-tab-rainbow {
  animation: galleryRainbow 0.45s linear infinite !important;
  color: #ffffff !important;
  position: relative;
}

/* Gallery button flash — pulses the "Your gallery" button when a freshly
   ripped/crafted card lands in your collection, so the eye is drawn to its home. */
/* Glows in the pulled card's RARITY colour via --flash-rgb (set in flashGalleryBtn);
   falls back to cyan if unset. */
@keyframes galleryBtnFlash {
  0%   { box-shadow: 0 0 0 0 rgba(var(--flash-rgb,0,229,255),0);                                          border-color: rgba(var(--flash-rgb,0,229,255),.32); transform: scale(1); }
  18%  { box-shadow: 0 0 26px 6px rgba(var(--flash-rgb,0,229,255),.95), inset 0 0 10px rgba(255,255,255,.55); border-color: rgba(var(--flash-rgb,0,229,255),.95); transform: scale(1.07); }
  50%  { box-shadow: 0 0 14px 3px rgba(var(--flash-rgb,0,229,255),.7),  inset 0 0 6px rgba(255,255,255,.3);   border-color: rgba(var(--flash-rgb,0,229,255),.6);  transform: scale(1.02); }
  100% { box-shadow: 0 0 0 0 rgba(var(--flash-rgb,0,229,255),0);                                          border-color: rgba(var(--flash-rgb,0,229,255),.32); transform: scale(1); }
}
.pb-name-btn.gallery-flash {
  /* !important so it beats the flat-button rework's `animation: none !important`. */
  animation: galleryBtnFlash 1s ease-out 3 !important;   /* ~3s — glows in the pulled card's rarity colour */
  z-index: 1; position: relative;
}
.pb-name-btn.gallery-flash .pb-name { color: #fff !important; }

/* Shockwave rings during rip */
@keyframes shockwaveExpand {
  0%   { transform: scale(0.1); opacity: 0.9; }
  100% { transform: scale(3);   opacity: 0;   }
}
.shockwave-ring {
  position: absolute;
  left: 50%; top: 50%;
  width: 120px; height: 120px;
  margin-left: -60px; margin-top: -60px;
  border-radius: 50%;
  border: 2px solid var(--gold);
  animation: shockwaveExpand 0.7s ease-out forwards;
  pointer-events: none;
  z-index: 10;
}

/* ── Reveal PSA panel (grade badge + card details) ─────────────────────────
   The reveal JS emits these classes; without styles the panel was raw text. */
/* Authentic PSA holder label: a red "PSA" brand block butted against the grade,
   like the real slab — [ PSA | GEM MT 10 ]. Compact so it doesn't eat scroll. */
.reveal-psa-panel .psa-slab-label {
  display: inline-flex;
  align-items: stretch;
  margin: 0 auto clamp(5px, 5cqi, 12px);
  border-radius: 9px;
  overflow: hidden;
  max-width: 100%;
  border: 1px solid rgba(255, 255, 255, 0.16);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.5);
  font-family: var(--font-display, sans-serif);
}
.reveal-psa-panel .psa-slab-brand {
  position: relative; overflow: hidden;
  display: flex;
  align-items: center;
  padding: 0 clamp(5px, 5.5cqi, 12px);
  background: linear-gradient(180deg, #ff3a44 0%, #e1232f 45%, #9c0e19 100%);
  color: #fff;
  font-weight: 900;
  font-size: clamp(9px, 9.5cqi, 18px);
  letter-spacing: 0.05em;
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.45);
}
/* Glossy top highlight on the red PSA block. */
.reveal-psa-panel .psa-slab-brand::after {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 45%;
  background: linear-gradient(180deg, rgba(255,255,255,.35), transparent);
  pointer-events: none;
}
.reveal-psa-panel .psa-slab-grade {
  display: flex;
  align-items: center;
  gap: clamp(3px, 4cqi, 9px);
  padding: clamp(3px, 4.5cqi, 7px) clamp(5px, 7cqi, 14px);
  background: linear-gradient(180deg, #1b2233, #0c0f17);
}
.reveal-psa-panel .psa-slab-gradetxt {
  font-size: clamp(6px, 5.8cqi, 11px);
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #c6d2e4;
  white-space: nowrap;
}
.reveal-psa-panel .psa-slab-gradenum {
  font-size: clamp(15px, 16cqi, 32px);
  font-weight: 900;
  line-height: 1;
  color: var(--gold);
  text-shadow: 0 0 18px currentColor;
}
/* A perfect 10 — glassy, prismatic "diamond" finish that shimmers across the
   number. */
.reveal-psa-panel .psa-slab-10 .psa-slab-gradenum {
  font-size: 36px;
  color: transparent !important;
  background: linear-gradient(110deg,
    #eafdff 0%, #b6f0ff 16%, #ffffff 34%, #dcc8ff 50%,
    #bfe9ff 66%, #ffffff 82%, #eafdff 100%);
  background-size: 280% 100%;
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
  text-shadow: none;
  filter: drop-shadow(0 0 7px rgba(170,235,255,.85)) drop-shadow(0 0 2px rgba(255,255,255,.9));
  animation: diamond10 2.6s linear infinite;
}
.reveal-psa-panel .psa-slab-10 {
  border-color: rgba(190,240,255,.7);
  box-shadow: 0 6px 22px rgba(0,0,0,.5), 0 0 18px rgba(150,225,255,.5);
}
.reveal-psa-panel .psa-slab-10 .psa-slab-brand {
  background: linear-gradient(180deg, #2aa6d8, #1268a8);  /* icy blue instead of red for a 10 */
}
@keyframes diamond10 {
  0%   { background-position: 0% 0; }
  100% { background-position: 280% 0; }
}
/* The cert card is a SIZE CONTAINER so all its text scales to the card's actual
   width on any screen — it shrinks to fit instead of wrapping. */
.reveal-right-panel { container-type: inline-size; }
.reveal-psa-panel .psa-card-info {
  display: flex;
  flex-direction: column;
  gap: 4px;
  text-align: center;
  min-width: 0;
  width: 100%;
}
.reveal-psa-panel .psa-card-name {
  font-family: var(--font-display, sans-serif);
  font-size: clamp(9px, 9cqi, 17px);
  font-weight: 800;
  letter-spacing: .03em; text-transform: uppercase;
  color: #fff8e6;
  text-shadow: 0 0 12px rgba(255,224,150,.35);
  line-height: 1.2;
  margin: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
/* All the metadata on two tidy lines instead of one-per-row. */
.reveal-psa-panel .psa-meta-line {
  font-size: 12px;
  color: var(--text-dim, #aeb6c2);
  line-height: 1.3;
}
.reveal-psa-panel .psa-meta-sub {
  font-family: var(--font-mono, monospace);
  font-size: 10.5px;
  letter-spacing: 0.03em;
  color: rgba(255, 255, 255, 0.45);
}
.reveal-psa-panel .psa-value {
  margin-top: 6px;
  font-family: var(--font-display, sans-serif);
  font-size: 22px;
  font-weight: 800;
}
/* Full detail rows — a clean label/value list, sized to the card width (cqi). */
.reveal-psa-panel .psa-detail-list {
  display: flex;
  flex-direction: column;
  width: 100%;
  margin: clamp(5px, 5cqi, 10px) 0 4px;
  border-top: 1px solid rgba(231,205,134,.35);   /* gold hairline */
}
.reveal-psa-panel .psa-drow {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: clamp(5px, 5cqi, 12px);
  padding: clamp(3px, 3cqi, 6px) 2px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.07);
}
.reveal-psa-panel .psa-dk {
  font-family: var(--font-mono, monospace);
  font-size: clamp(7px, 5.4cqi, 10.5px);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #d8b96a;          /* warm gold labels */
  white-space: nowrap; flex: 0 0 auto;
}
.reveal-psa-panel .psa-dv {
  font-size: clamp(8px, 6.6cqi, 13px);
  font-weight: 700;
  color: var(--text-light, #fff);
  text-align: right;
  min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;   /* never wrap */
}
/* ── Signed-out LOGIN prize: the cert-card slot becomes a sign-in pitch, sized to the
   card width via container units (matches the PSA cert text). ─────────────────────── */
.reveal-psa-panel .login-pitch {
  display: flex; flex-direction: column; justify-content: center; gap: clamp(5px, 5cqi, 13px);
  height: 100%; text-align: center; padding: clamp(6px, 6cqi, 16px) clamp(4px, 4cqi, 12px);
}
.login-pitch .lp-title {
  font: 900 clamp(13px, 12.5cqi, 26px)/1.08 var(--font-display, sans-serif); letter-spacing: .03em;
  color: #aef2ff; text-shadow: 0 0 14px rgba(70,212,255,.55);
}
.login-pitch .lp-sub { font-size: clamp(8px, 6.6cqi, 13px); line-height: 1.35; color: #c2d2e6; }
.login-pitch .lp-cta { font-size: clamp(8px, 6.6cqi, 13px); color: #9fe9ff; }
.login-pitch .lp-cta b { color: #fff; }
/* The CLAIM button reads LOG IN and goes cyan for the signed-out prize. */
body.login-victory .reveal-in-actions .rip-again-btn {
  background: linear-gradient(180deg, #6ee6ff, #2b9fd6) !important; color: #052230 !important;
  box-shadow: 0 6px 18px rgba(0,0,0,.4), 0 0 20px rgba(70,212,255,.6) !important;
}

/* ── Desktop: the PSA panel + CLAIM become a professional "cert card" that sits
   beside the pull. Its width is set in JS to the card's exact width, height
   fills the stage → identical aspect ratio to the card on the left. ────────── */
@media (min-width: 641px) {
  /* Center the card + cert-card pair as ONE tight group: the row shrinks to fit
     its content and centres via auto margins, so the cert panel can never drift to
     the right edge. The legacy translateX(-10%) "slide left" transform is cancelled
     so the cards don't pull away from the panel. */
  .hero-stage .reveal-zone.expanded .reveal-inner {
    justify-content: center;
    width: -webkit-max-content; width: max-content;
    max-width: 96vw; margin: 0 auto; gap: 16px;
  }
  .hero-stage .reveal-zone.expanded .card-flip-wrap { transform: none; }
  .hero-stage .reveal-zone.expanded .reveal-right-panel {
    position: relative;
    height: 100%;
    flex: 0 0 auto;        /* keep the JS-set width (card width), don't grow */
    margin: 0;
    max-width: none;
    box-sizing: border-box;
    padding: 18px 18px 16px;
    /* Premium cert card: dark face + a gold/silver FOIL border (padding-box face,
       border-box gradient frame) for an authentic graded-slab feel. */
    background:
      radial-gradient(130% 70% at 50% -8%, rgba(255,224,150,.12), transparent 55%)        padding-box,
      linear-gradient(180deg, #1a2233 0%, #0a0e16 100%)                                    padding-box,
      linear-gradient(135deg, #e7cd86, #fff7e0 26%, #a9c3df 52%, #ffffff 72%, #d8b96a)     border-box;
    border: 2px solid transparent;
    border-radius: 15px;
    box-shadow:
      0 24px 64px rgba(0,0,0,.6),
      inset 0 1px 0 rgba(255,255,255,.10),
      0 0 22px rgba(231,205,134,.22);
    overflow: hidden;
  }
  /* Holographic sheen that sweeps across the whole cert card — flashy + premium. */
  .hero-stage .reveal-zone.expanded .reveal-right-panel::after {
    content: ""; position: absolute; inset: 0; z-index: 5; pointer-events: none;
    background: linear-gradient(115deg,
      transparent 40%, rgba(255,255,255,.20) 47%, rgba(180,220,255,.16) 50%,
      rgba(255,225,170,.16) 53%, transparent 60%);
    background-size: 250% 100%;
    mix-blend-mode: screen;
    animation: certSheen 5s ease-in-out infinite;
  }
  @keyframes certSheen { 0% { background-position: 140% 0; } 55%, 100% { background-position: -70% 0; } }
  .hero-stage .reveal-zone.expanded .reveal-psa-panel { position: relative; z-index: 2; }
  /* The info area scrolls only if a card has an unusually long subject. */
  .hero-stage .reveal-zone.expanded .reveal-psa-panel {
    width: auto;
    align-self: stretch;
    text-align: center;
  }
  .hero-stage .reveal-zone.expanded .reveal-in-actions {
    border-top: 1px solid rgba(255, 255, 255, 0.1);
  }
  /* Cert-card content: centred header, roomy detail rows, full-width CLAIM pinned
     at the bottom — a clean professional cert card. */
  .hero-stage .reveal-zone.expanded .reveal-psa-panel {
    display: flex; flex-direction: column; align-items: stretch; gap: 3px;
  }
  .hero-stage .reveal-zone.expanded .psa-slab-label { align-self: center; }
  .hero-stage .reveal-zone.expanded .psa-card-info { display: flex; flex-direction: column; }
  .hero-stage .reveal-zone.expanded .psa-card-name { text-align: center; font-size: 17px; margin-top: 5px; }
  .hero-stage .reveal-zone.expanded .psa-detail-list { margin-top: 9px; }
  .hero-stage .reveal-zone.expanded .psa-drow { padding: 7px 2px; }
  .hero-stage .reveal-zone.expanded .reveal-in-actions .rip-again-btn { width: 100%; padding: 11px 16px; font-size: 14px; }
}

/* Reveal card faces had height:100% + width:auto with a width:100% <img>, which
   collapses to ZERO width (no intrinsic width source) — the cards rendered as
   invisible 0-wide boxes. Give each face a real width from a card aspect ratio
   (with a min-width floor) so the front/back always show, image or placeholder. */
.hero-stage .card-flip-wrap .card-face {
  height: 100%;
  width: auto;
  aspect-ratio: auto;      /* let the image define the shape — no forced ratio */
  min-width: 70px;
}
.hero-stage .card-flip-wrap .card-face img {
  height: 100%;
  width: auto;             /* natural aspect ratio, full image, no letterbox bars */
  max-width: none;
  object-fit: contain;
  background: none;
  display: block;
}

/* ═══════════════════════════════════════════════════════════════════════════
   AUTH UI — header control + modal
   ═══════════════════════════════════════════════════════════════════════════ */

.auth-control { display: flex; align-items: center; }
.auth-signed-in {
  display: flex; align-items: center; gap: 10px;
}
.auth-user-email {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-light);
  max-width: 180px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.auth-signout-btn {
  font-family: var(--font-body);
  font-size: 12px; font-weight: 600;
  color: var(--text-dim);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 5px 12px;
  cursor: pointer;
  transition: color .2s, border-color .2s;
}
.auth-signout-btn:hover { color: var(--gold-bright); border-color: var(--gold); }

/* The "really special" Sign Up / Log In CTA */
.auth-cta-btn {
  position: relative;
  overflow: hidden;
  font-family: var(--font-display);
  font-size: 13px; font-weight: 800; letter-spacing: .03em;
  color: #1a1305;
  border: none;
  border-radius: 999px;
  padding: 9px 20px;
  cursor: pointer;
  background: linear-gradient(120deg, var(--lavender) 0%, var(--gold-bright) 45%, var(--gold) 100%);
  box-shadow:
    0 0 0 1px rgba(255,255,255,.18) inset,
    0 4px 18px rgba(200,168,75,.45),
    0 0 26px rgba(226,196,106,.40);
  animation: authCtaPulse 2.6s ease-in-out infinite;
  transition: transform .15s ease, box-shadow .25s ease;
}
.auth-cta-btn:hover {
  transform: translateY(-1px) scale(1.03);
  box-shadow:
    0 0 0 1px rgba(255,255,255,.28) inset,
    0 6px 24px rgba(200,168,75,.6),
    0 0 40px rgba(226,196,106,.6);
}
.auth-cta-label { position: relative; z-index: 2; }
.auth-cta-shine {
  position: absolute; top: 0; left: -60%;
  width: 50%; height: 100%; z-index: 1;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,.65), transparent);
  transform: skewX(-20deg);
  animation: authCtaShimmer 3.2s ease-in-out infinite;
}
@keyframes authCtaPulse {
  0%, 100% { box-shadow: 0 0 0 1px rgba(255,255,255,.18) inset, 0 4px 18px rgba(200,168,75,.40), 0 0 22px rgba(226,196,106,.34); }
  50%      { box-shadow: 0 0 0 1px rgba(255,255,255,.26) inset, 0 6px 24px rgba(200,168,75,.62), 0 0 44px rgba(226,196,106,.66); }
}
@keyframes authCtaShimmer {
  0%   { left: -60%; }
  55%  { left: 130%; }
  100% { left: 130%; }
}

/* ── Modal ── */
.auth-modal {
  position: fixed; inset: 0; z-index: 10000;
  display: flex; align-items: center; justify-content: center;
}
.auth-backdrop {
  position: absolute; inset: 0;
  background: rgba(4, 6, 11, .72);
  backdrop-filter: blur(6px);
}
.auth-card {
  position: relative; z-index: 1; overflow: hidden;
  width: min(94vw, 420px);
  background:
    radial-gradient(120% 80% at 50% -10%, rgba(255,150,210,.10), transparent 60%),
    linear-gradient(180deg, rgba(255,255,255,.05), transparent 30%),
    linear-gradient(180deg, #161320, #0e0b15);
  border: 1px solid rgba(255,170,220,.18);
  border-radius: 22px;
  padding: 30px 28px 26px;
  box-shadow: 0 26px 80px rgba(0,0,0,.62), 0 0 50px rgba(255,140,200,.10), inset 0 1px 0 rgba(255,255,255,.06);
  animation: authCardIn .3s cubic-bezier(.2,.9,.3,1.1);
}
/* Soft welcoming glow band across the top — warm + non-stressful. */
.auth-card::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: linear-gradient(90deg, #ff9ad1, #ffd27a, #8fd4ff);
  opacity: .9;
}
@keyframes authCardIn {
  from { opacity: 0; transform: translateY(14px) scale(.97); }
  to   { opacity: 1; transform: none; }
}
.auth-close {
  position: absolute; top: 14px; right: 15px;
  background: rgba(255,255,255,.05); border: none; border-radius: 8px;
  color: var(--text-dim); font-size: 15px; cursor: pointer;
  line-height: 1; width: 28px; height: 28px;
  transition: color .2s, background .2s;
}
.auth-close:hover { color: #fff; background: rgba(255,255,255,.12); }
.auth-brand {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  margin-bottom: 4px;
}
.auth-brand .brand-icon { color: #ffd27a; font-size: 20px; filter: drop-shadow(0 0 8px rgba(255,210,122,.6)); }
.auth-brand-name {
  font-family: var(--font-display); font-weight: 800; font-size: 21px;
  color: #fff; letter-spacing: .02em;
}
.auth-welcome {
  text-align: center; margin: 0 0 20px; padding: 0 6px;
  font: 500 13.5px/1.5 var(--font-body), system-ui, sans-serif; color: #d7c4e6;
}
.auth-tabs {
  display: flex; gap: 4px;
  background: rgba(0,0,0,.3);
  border: 1px solid rgba(255,255,255,.08);
  border-radius: 13px; padding: 4px;
  margin-bottom: 20px;
}
.auth-tab-btn {
  flex: 1;
  font-family: var(--font-body); font-weight: 700; font-size: 13.5px;
  color: #b6a8c6;
  background: transparent; border: none; border-radius: 10px;
  padding: 10px 0; cursor: pointer;
  transition: background .2s, color .2s;
}
.auth-tab-btn.active {
  color: #2a1320;
  background: linear-gradient(120deg, #ffd6ec, #ffb0d6);
  box-shadow: 0 3px 12px rgba(255,150,200,.4);
}
.auth-form { display: flex; flex-direction: column; }
.auth-label {
  font-family: var(--font-body); font-size: 12px; font-weight: 600; letter-spacing: .02em;
  text-transform: none; color: #c9bcda;
  margin: 8px 0 6px;
}
.auth-input {
  font-family: var(--font-body); font-size: 15px;
  color: #fff;
  background: rgba(255,255,255,.04);
  border: 1px solid rgba(255,255,255,.12);
  border-radius: 12px;
  padding: 13px 15px;
  margin-bottom: 4px;
  outline: none;
  transition: border-color .18s, box-shadow .18s, background .18s;
}
.auth-input::placeholder { color: #8b7f9c; }
.auth-input:focus {
  border-color: #ff9ad1; background: rgba(255,255,255,.06);
  box-shadow: 0 0 0 3px rgba(255,154,209,.18);
}
.auth-code-input {
  text-align: center; letter-spacing: .5em;
  font-family: var(--font-mono); font-size: 20px; font-weight: 600;
}
.auth-hint {
  font-size: 11.5px; color: #9a8caa;
  margin: 6px 0 8px; line-height: 1.45;
}
.auth-verify-lead {
  font-size: 13px; color: var(--text-light); line-height: 1.5;
  margin: 0 0 12px;
}
.auth-verify-lead strong { color: var(--gold-bright); }
.auth-status {
  font-size: 12px; min-height: 16px; margin: 2px 0 8px; line-height: 1.4;
}
.auth-status.err { color: #FF7A7A; }
.auth-status.ok  { color: #7BE0A0; }
.auth-submit-btn {
  font-family: var(--font-display); font-weight: 800; font-size: 15px;
  color: #2a1320;
  background: linear-gradient(120deg, #ffd6ec, #ff9ad1 55%, #ffb98a);
  border: none; border-radius: 13px;
  padding: 14px 0; margin-top: 12px;
  cursor: pointer;
  box-shadow: 0 6px 20px rgba(255,150,200,.42);
  transition: transform .15s, box-shadow .2s, filter .15s;
}
.auth-submit-btn:hover { transform: translateY(-1px); filter: brightness(1.04); box-shadow: 0 9px 28px rgba(255,150,200,.55); }
.auth-link-btn {
  background: transparent; border: none;
  color: #8fd4ff; font-size: 12.5px; font-weight: 600;
  cursor: pointer; margin-top: 14px; padding: 4px;
  align-self: center;
}
.auth-link-btn:hover { color: #bfe6ff; text-decoration: underline; }

/* Locked gallery tab affordance */
.tab-btn.locked { opacity: .55; cursor: pointer; }
.tab-btn.locked::after { content: ' 🔒'; font-size: 11px; }

/* ── Buy Credits ─────────────────────────────────────────────────────────── */
.buy-credits-btn {
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  color: #04210f;
  background: linear-gradient(180deg, #7df0bb, #38d18a);
  border: 1px solid #2bbf78;
  border-radius: 7px;
  padding: 5px 12px;
  white-space: nowrap;
  transition: filter .14s, transform .05s;
}
.buy-credits-btn:hover { filter: brightness(1.08); }
.buy-credits-btn:active { transform: translateY(1px); }

/* Rip purchase stepper — −  N rips  +  then a big Buy button. Dead simple. */
/* Condensed buy form — compact stepper + card field for a fast, clean checkout. */
.rip-buy { display: flex; flex-direction: column; align-items: center; gap: 8px; max-width: 340px; margin: 0 auto; }
.rip-qty { display: flex; align-items: center; gap: 12px; }
/* Buy sheet header + trust signals — a clean, professional checkout so buyers feel
   safe paying real money. */
/* Header row: title BUY RIPS on the left, the ✕ (absolute, top-right of the panel) sits
   on the SAME row to conserve space — so leave room on the right for it. The subtitle
   tucks under the title. Everything is pulled to the very top of the panel. */
/* Title + subtitle share ONE baseline row (wraps only if truly narrow) to save vertical
   space; the ✕ sits top-right, so leave room on the right. */
/* (Buy Rips title + "1 rip = 1 spin" subtitle removed for a compact panel — the qty
   stepper and deals sit right at the top now.) */
.rip-trust { margin-top: 12px; display: flex; flex-direction: column; align-items: center; gap: 9px; }
.rip-trust-line { display: block; max-width: 300px; text-align: center;
  font: 600 11px/1.5 system-ui, sans-serif; color: #9fb0c6; }
.rip-trust-line b { color: #cfe0f2; }
.rip-lock { color: #4fc98a; display: inline; vertical-align: -2px; margin-right: 4px; }
.rip-cardmarks { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; opacity: .95; }
.rip-cm { font: 800 9.5px/1 system-ui, sans-serif; letter-spacing: .02em; padding: 4px 7px; border-radius: 4px;
  color: #fff; background: #1a1c22; border: 1px solid rgba(255,255,255,.14); box-shadow: 0 1px 2px rgba(0,0,0,.4); }
.rip-cm.cm-visa { color: #1a1f71; background: #fff; border-color: rgba(0,0,0,.15); }
.rip-cm.cm-mc   { color: #fff; background: linear-gradient(90deg,#eb001b 46%,#f79e1b 54%); border-color: transparent; }
.rip-cm.cm-amex { color: #fff; background: #2e77bc; border-color: transparent; }
.rip-cm.cm-disc { color: #fff; background: #f27712; border-color: transparent; }
/* Stepper − +  = clean, understated ROUNDED-SQUARE keys (NOT red circles) so they
   never fight the price + card art. The RIPS themselves are the red hero: a 🎴 with the
   count and a bold red "rip / rips" label. */
.rip-qty { gap: 14px; }
.rip-step {
  width: 40px; height: 40px; border-radius: 11px; cursor: pointer; flex: 0 0 auto;
  border: 1px solid rgba(255,255,255,.16); color: #eef2f8;
  background: linear-gradient(180deg, #2c2e35 0%, #17181d 100%);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.14), inset 0 -3px 6px rgba(0,0,0,.4), 0 2px 6px rgba(0,0,0,.42);
  font: 700 22px/1 system-ui, sans-serif;
  display: flex; align-items: center; justify-content: center;
  transition: filter .12s, transform .06s, box-shadow .1s;
}
.rip-step:hover:not(:disabled) { filter: brightness(1.22); }
.rip-step:active:not(:disabled) { transform: translateY(2px); box-shadow: inset 0 2px 5px rgba(0,0,0,.5); }
.rip-step:disabled { opacity: .3; cursor: default; }
/* The quantity is the hero: 🎴 + big count + a bold RED rip/rips label. */
.rip-qty-val { min-width: 92px; text-align: center; color: #fff; font: 800 14px/1 system-ui, sans-serif;
  display: inline-flex; align-items: baseline; justify-content: center; gap: 3px; }
.rip-qty-emoji { line-height: 1; align-self: center; display: inline-flex; }
/* On-brand torn-pack glyph, tinted the RIP page's neon green (replaces the 🎴 emoji). */
.rip-glyph { width: 22px; height: 22px; color: #3af07c;
  filter: drop-shadow(0 0 5px rgba(46,229,124,.6)) drop-shadow(0 1px 2px rgba(0,0,0,.6)); vertical-align: -4px; }
.rip-qty-val b { font: 900 30px/1 system-ui, sans-serif; color: #fff; text-shadow: 0 2px 4px rgba(0,0,0,.7); }
.rip-qty-label { font-weight: 900; font-size: 15px; letter-spacing: .02em; text-transform: uppercase;
  color: #3af07c; text-shadow: 0 0 12px rgba(46,229,124,.6), 0 1px 2px rgba(0,0,0,.6); }
/* Special value bundle deals (e.g. 5 rips for $200) — a red-themed premium offer with a
   gold DEAL badge and green savings; the picked deal lights up with a red ring. */
/* Deals sit SIDE BY SIDE as compact chips (they used to stack full-width, eating a lot
   of vertical room) — so they conserve space and the whole panel stays short. */
.rip-deals { display: flex; flex-direction: row; flex-wrap: wrap; justify-content: center; gap: 8px; margin: 2px auto 0; max-width: 340px; width: 100%; }
.rip-deal {
  position: relative; cursor: pointer; flex: 1 1 132px; text-align: center;
  padding: 9px 10px 8px; border-radius: 11px; color: #fff;
  border: 1.5px solid rgba(46,229,124,.5);
  background: linear-gradient(180deg, #0e2417 0%, #07160f 100%);
  box-shadow: inset 0 1px 0 rgba(200,255,222,.1), 0 3px 10px rgba(0,0,0,.42);
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  transition: filter .12s, transform .06s, box-shadow .12s, border-color .12s;
}
.rip-deal:hover { filter: brightness(1.12); border-color: rgba(90,255,150,.85); }
.rip-deal:active { transform: translateY(1px); }
/* DEAL badge = entry-plug amber hazard tab (matches the cockpit panels). */
.rip-deal-badge {
  position: absolute; top: -9px; left: 50%; transform: translateX(-50%);
  font: 900 9px/1 system-ui, sans-serif; letter-spacing: .11em; text-transform: uppercase;
  color: #2a1500; background: linear-gradient(180deg,#ffd97a,#f0a52a); padding: 3px 10px; border-radius: 999px;
  box-shadow: 0 2px 6px rgba(0,0,0,.5), inset 0 1px 0 rgba(255,255,255,.6);
}
/* Chip content stacks: "⧉ N RIPS" over the price, so it fits a narrow side-by-side chip
   without wrapping the price onto its own orphaned line. */
.rip-deal-main { font: 800 14px/1.1 system-ui, sans-serif; color: #daffe9; display: flex; flex-direction: column; align-items: center; gap: 1px; }
.rip-deal-main .rdm-top { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.rip-deal-main .rdm-price { color: #fff; font-size: 15.5px; }
.rip-deal-main .rip-glyph { width: 17px; height: 17px; }
.rip-deal-save { font: 800 11px/1 system-ui, sans-serif; color: #6fe39a; letter-spacing: .02em; }
.rip-deal.rip-deal-on { border-color: #2ee57c; box-shadow: 0 0 0 1px #2ee57c, 0 0 18px rgba(46,229,124,.5), inset 0 1px 0 rgba(200,255,222,.12); }
.rip-pay-element { width: 100%; max-width: 340px; margin: 2px auto; text-align: left; }
.rip-summary { font: 600 12px/1 system-ui, sans-serif; color: #8c9bb0; letter-spacing: .02em; }
.rip-summary .rip-credits b { color: #eaf7ff; }
/* Primary Buy = the RIP page's neon-green lacquer key (matches the RIP nav button), with
   a glossy sweep so it reads as THE buy-rips action, not a generic checkout button. */
.rip-buy-btn {
  width: 100%; max-width: 320px; padding: 11px 18px; border: 1px solid rgba(0,0,0,.42); border-radius: 8px; cursor: pointer;
  font: 900 15px/1 system-ui, sans-serif; color: #08210f; text-shadow: 0 1px 0 rgba(200,255,222,.6);
  background:
    linear-gradient(105deg, transparent 40%, rgba(255,255,255,.45) 49%, transparent 58%),
    linear-gradient(180deg, #4cf58f 0%, #1fbf63 46%, #0b7e40 100%);
  box-shadow: inset 0 2px 0 rgba(200,255,222,.7), inset 0 -6px 8px rgba(0,0,0,.22), 0 5px 14px rgba(0,0,0,.42), 0 0 18px rgba(46,229,124,.4);
  transition: filter .12s, transform .08s, box-shadow .1s;
}
.rip-buy-btn:hover:not(:disabled) { filter: brightness(1.06); }
.rip-buy-btn:active:not(:disabled) { transform: translateY(2px); box-shadow: inset 0 3px 5px rgba(0,0,0,.16), 0 1px 3px rgba(0,0,0,.3); }
.rip-buy-btn:disabled { opacity: .55; cursor: not-allowed; filter: grayscale(.3); }

/* Each pack is a WHITE ivory piano key on the black panel — matches the BUY RIPS
   key, so the whole component reads as one piano set. */
.buy-pack {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  cursor: pointer;
  text-align: left;
  color: #17181c;
  border: 1px solid rgba(0,0,0,.42);
  border-radius: 7px;
  padding: 12px 16px;
  background: linear-gradient(180deg, #ffffff 0%, #f1f1ea 52%, #d7d7cc 100%);
  box-shadow: inset 0 2px 0 rgba(255,255,255,.92), inset 0 -5px 7px rgba(0,0,0,.12), 0 3px 8px rgba(0,0,0,.36);
  text-shadow: 0 1px 0 rgba(255,255,255,.7);
  transition: filter .12s ease, transform .06s ease, box-shadow .1s ease;
}
.buy-pack:hover { filter: brightness(1.06); }
.buy-pack:active { transform: translateY(2px); box-shadow: inset 0 3px 5px rgba(0,0,0,.18), 0 1px 3px rgba(0,0,0,.3); }
.buy-pack:disabled { opacity: .5; cursor: default; }
.buy-pack .bp-credits { font: 800 17px/1.1 system-ui, sans-serif; color: #17181c; }
.buy-pack .bp-name { font-size: 11px; opacity: .58; margin-top: 2px; color: #2a2b31; }
.buy-pack .bp-price { font: 800 16px/1 system-ui, sans-serif; color: #1b1c21; white-space: nowrap; }

/* ── Combined Credits + Buy pill ─────────────────────────────────────────── */
.credits-buy-btn {
  position: relative;
  overflow: hidden;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font: 800 14px/1 system-ui, sans-serif;
  color: #1a1205;
  background: linear-gradient(135deg, #ffe08a, #ffc34d 45%, #ffd86b);
  border: 1px solid #e0a93a;
  border-radius: 999px;
  padding: 7px 11px;
  box-shadow: 0 2px 10px rgba(255,193,77,.4), inset 0 1px 0 rgba(255,255,255,.5);
  transition: transform .05s, box-shadow .15s, filter .15s;
  -webkit-appearance: none; appearance: none;
}
.credits-buy-btn:hover { filter: brightness(1.05); box-shadow: 0 4px 18px rgba(255,193,77,.6); }
.credits-buy-btn:active { transform: translateY(1px); }
.credits-buy-btn .cb-coin { font-size: 14px; }
.credits-buy-btn .cb-amount { letter-spacing: .3px; font-variant-numeric: tabular-nums; }
.credits-buy-btn .cb-sep { font-size: 10px; font-weight: 700; opacity: .6; margin-left: -3px; }
.credits-buy-btn .cb-plus {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; margin-left: 1px; border-radius: 50%;
  background: rgba(26,18,5,.85); color: #ffd86b; font-size: 12px; font-weight: 900; line-height: 1;
}
.credits-buy-btn .cb-shine {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(115deg, transparent 32%, rgba(255,255,255,.55) 48%, transparent 62%);
  background-size: 250% 100%;
  animation: cbShine 3.4s linear infinite;
}
@keyframes cbShine { 0% { background-position: 160% 0; } 100% { background-position: -160% 0; } }
/* Depletion fill folded into the pill: a thin cool-toned underline that shows
   how much of your balance remains (full pill = full bar, spend to deplete). */
.credits-buy-btn .cb-fill {
  position: absolute; left: 0; bottom: 0; height: 3px; width: 100%;
  background: linear-gradient(90deg, #7b3ff2, #19b7ff);
  box-shadow: 0 0 6px rgba(120,200,255,.6);
  border-bottom-left-radius: 999px; border-bottom-right-radius: 999px;
  transition: width .6s ease; z-index: 1; pointer-events: none;
}
/* Keep the pill's text/icons above both the shine sweep and the fill. */
.credits-buy-btn .cb-coin,
.credits-buy-btn .cb-amount,
.credits-buy-btn .cb-sep,
.credits-buy-btn .cb-plus { position: relative; z-index: 2; }

/* ── Profile chip: tap name → gallery; tap power icon → sign out ──────────── */
.user-chip {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  background: rgba(255,255,255,.05);
  border: 1px solid rgba(255,255,255,.14);
  border-radius: 999px;
  padding: 2px 3px;
  max-width: 230px;
}
.user-chip-profile, .user-chip-power {
  -webkit-appearance: none; appearance: none;
  cursor: pointer;
  background: transparent;
  border: none;
  color: #cdd6e2;
  font: 700 12px/1 var(--font-mono);
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 9px;
  transition: background .14s, color .14s;
}
.user-chip-profile { max-width: 160px; }
.user-chip-profile .user-chip-name {
  max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.user-chip-profile:hover, .user-chip-profile:focus-visible {
  background: rgba(120,180,255,.16); color: #fff;
}
.user-chip-profile.in-gallery { color: #ffd98a; }   /* in the gallery — tap to go back */
.user-chip-power { font-size: 14px; color: #9aa3b2; padding: 5px 10px; }
.user-chip-power:hover, .user-chip-power:focus-visible {
  background: rgba(255,80,80,.16); color: #ffb4b4;
}

/* ── Single header view-toggle button (replaces brand + Rip/Gallery tabs) ──── */
.view-toggle-btn {
  -webkit-appearance: none; appearance: none;
  cursor: pointer;
  font: 800 13px/1 system-ui, sans-serif;
  letter-spacing: .03em;
  color: #e7ecf5;
  background: rgba(255,255,255,.06);
  border: 1px solid rgba(255,255,255,.16);
  border-radius: 999px;
  padding: 8px 16px;
  transition: background .14s, border-color .14s, color .14s, transform .05s;
}
.view-toggle-btn:hover  { background: rgba(120,180,255,.16); border-color: rgba(120,180,255,.5); color: #fff; }
.view-toggle-btn:active { transform: translateY(1px); }

/* ── Mobile: reclaim header space ────────────────────────────────────────── */
@media (max-width: 640px) {
  .app-version { display: none; }
  .header-right { gap: 8px !important; }
  .credits-buy-btn { padding: 6px 9px; font-size: 13px; }
  .user-chip { max-width: 160px; }
  .user-chip-profile { max-width: 104px; padding: 5px 7px; }
  .user-chip-profile .user-chip-name { max-width: 80px; }
  .user-chip-profile .user-chip-icon { display: none; }
}

/* ── App toasts + confirm dialogs (custom alert/confirm) ─────────────────── */
.ui-toast-host {
  position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%);
  z-index: 13500; display: flex; flex-direction: column; align-items: center;
  gap: 8px; pointer-events: none; width: max-content; max-width: 92vw;
}
.ui-toast {
  pointer-events: auto; cursor: pointer; display: flex; align-items: center; gap: 9px;
  min-width: 220px; max-width: 90vw; padding: 11px 16px;
  font: 600 13.5px/1.35 system-ui, sans-serif; color: #eef2f7;
  background: rgba(14,17,24,.97); border: 1px solid rgba(255,255,255,.14);
  border-left: 4px solid #6cc6ff; border-radius: 11px;
  box-shadow: 0 12px 40px rgba(0,0,0,.5);
  transform: translateY(14px); opacity: 0; transition: transform .26s cubic-bezier(.2,1,.3,1), opacity .26s;
}
.ui-toast.show { transform: translateY(0); opacity: 1; }
.ui-toast-ico { font-size: 15px; }
.ui-toast-success { border-left-color: #48d597; }
.ui-toast-error   { border-left-color: #ff6b6b; }
.ui-toast-warn    { border-left-color: #ffb454; }
.ui-toast-info    { border-left-color: #6cc6ff; }

.ui-confirm-backdrop {
  position: fixed; inset: 0; z-index: 13600; display: flex; align-items: center; justify-content: center;
  background: rgba(4,6,10,.62); backdrop-filter: blur(2px); padding: 20px;
  opacity: 0; transition: opacity .18s;
}
.ui-confirm-backdrop.show { opacity: 1; }
.ui-confirm-card {
  width: 100%; max-width: 380px; background: linear-gradient(180deg,#161a23,#0e1117);
  border: 1px solid rgba(255,255,255,.14); border-radius: 16px; padding: 22px 22px 18px;
  box-shadow: 0 24px 70px rgba(0,0,0,.6);
  transform: translateY(10px) scale(.98); transition: transform .2s cubic-bezier(.2,1,.3,1);
}
.ui-confirm-backdrop.show .ui-confirm-card { transform: translateY(0) scale(1); }
.ui-confirm-title { font: 800 16px/1.2 system-ui, sans-serif; color: #fff; margin-bottom: 6px; }
.ui-confirm-msg { font: 500 13.5px/1.5 system-ui, sans-serif; color: #b9c4d2; margin-bottom: 18px; }
.ui-confirm-actions { display: flex; justify-content: flex-end; gap: 10px; }
.ui-btn {
  cursor: pointer; font: 700 13px/1 system-ui, sans-serif; border-radius: 9px; padding: 9px 16px;
  border: 1px solid transparent; transition: filter .12s, background .12s, border-color .12s;
}
.ui-btn-ghost { background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.16); color: #cdd6e2; }
.ui-btn-ghost:hover { background: rgba(255,255,255,.12); }
.ui-btn-primary { background: linear-gradient(180deg,#7df0bb,#38d18a); color: #04210f; }
.ui-btn-primary:hover { filter: brightness(1.06); }
.ui-btn-danger { background: linear-gradient(180deg,#ff8a8a,#ef4d4d); color: #2b0707; }
.ui-btn-danger:hover { filter: brightness(1.06); }

/* ── Mobile header: maximum real estate ──────────────────────────────────── */
@media (max-width: 640px) {
  .ledger-bar { padding-left: 8px !important; padding-right: 8px !important; gap: 8px; }
  .ledger-bar .brand h1 { display: none; }            /* show just the ✦ icon */
  .ledger-bar .brand .brand-icon { font-size: 20px; }
  .main-tabs { gap: 4px; }
  .main-tabs .tab-btn { padding: 6px 10px; font-size: 12px; }
  .admin-corner-btn { padding: 4px 9px; font-size: 10.5px; }
}

/* ── Rip animation shows the actual card BACK being torn in half ──────────── */
.pack-shell.card-rip .pack-half {
  background-color: #0b0a14;
  background-repeat: no-repeat;
  background-size: 100% 200%;          /* each 50%-tall half shows half the image */
  border: 1px solid rgba(0,0,0,.55) !important;
  box-shadow: 0 10px 34px rgba(0,0,0,.55);
}
/* The pack and its torn halves are PURELY decorative — the rip is triggered by the
   wheel/RIP button, never by clicking the pack. After the tear animation the bottom
   half is left spread over the centre of the stage, RIGHT on top of the CLAIM button,
   and (being pointer-events:auto by default) it swallowed every click AND hover on
   CLAIM. Force the whole pack non-interactive so it can never block the reveal UI. */
.pack-shell, .pack-half, .pack-seal { pointer-events: none !important; }
.pack-shell.card-rip .pack-half.top    { background-position: center top;    border-radius: 12px 12px 0 0; border-bottom: none !important; }
.pack-shell.card-rip .pack-half.bottom { background-position: center bottom; border-radius: 0 0 12px 12px; border-top: none !important; }
/* The foil "RIP" seal doesn't belong on a card-back rip. */
.pack-shell.card-rip .pack-seal { display: none !important; }
/* A torn edge highlight along the split for a paper-rip feel. */
.pack-shell.card-rip .pack-half.top::after,
.pack-shell.card-rip .pack-half.bottom::after {
  content: ''; position: absolute; left: -1px; right: -1px; height: 2px;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.5), transparent);
  pointer-events: none;
}
.pack-shell.card-rip .pack-half.top::after    { bottom: 0; }
.pack-shell.card-rip .pack-half.bottom::after { top: 0; }

/* ── Mobile game/reveal layout: front card → PSA → chase → graphs ─────────── */
@media (max-width: 640px) {
  /* Give the stage room and let the reveal stack + scroll vertically. */
  /* Idle band height is set by the carousel block (.hero-stage:not(.revealing));
     during a reveal the stage grows in normal flow (see .hero-stage.revealing). */
  .hero-stage .stage-frame { overflow-y: auto; -webkit-overflow-scrolling: touch; }

  /* Card + PSA cert sit SIDE BY SIDE on mobile (card left, cert right) so the prize
     is short and the CLAIM button stays on screen — no scrolling to claim. */
  .hero-stage .reveal-inner {
    flex-direction: row !important;
    align-items: center !important;
    justify-content: center !important;
    gap: 8px;
    height: auto !important;
    min-height: 0 !important;
    padding: 10px 10px 12px !important;
    overflow: visible !important;
  }

  /* Card: FRONT ONLY (hide the back/"pack"), on the LEFT. */
  .hero-stage .card-flip-inner { flex-direction: column !important; height: auto !important; align-items: center; }
  .hero-stage .card-face.back  { display: none !important; }
  .hero-stage .card-flip-wrap  { height: auto !important; width: 42% !important; max-width: 190px; flex: 0 0 auto; align-self: center !important; }
  .hero-stage .card-flip-wrap .card-face     { height: auto !important; width: 100% !important; }
  .hero-stage .card-flip-wrap .card-face img { height: auto !important; width: 100% !important; object-fit: contain; }

  /* PSA cert: on the RIGHT, filling the remaining width beside the card. */
  .hero-stage .reveal-psa-panel,
  .hero-stage .reveal-zone.expanded .reveal-psa-panel {
    width: auto !important; max-width: none; height: auto !important;
    opacity: 1; overflow: visible !important;
  }
  .reveal-right-panel { width: auto !important; flex: 1 1 0; min-width: 0; align-items: stretch; }

  /* Below the stage: single column (the chase-above-game order is set globally). */
  .sidebar-section { min-height: 0; }
}

/* ── Mobile carousel: shorter band → thinner cards, so more fit on screen ── */
@media (max-width: 640px) {
  /* Idle carousel band height (cards fill ~92% of this). Capped by 36dvh so the
     cards shrink when Safari's address bar is showing (dvh tracks the visible
     viewport) instead of running off the bottom of the screen. */
  .hero-stage:not(.revealing) { min-height: min(220px, 36dvh); }
  .sc-ticker-card { padding: 0 9px; }   /* matches hpad in app.js (mobile) */
  .sc-ticker-inner { border-radius: 8px; }
  /* Stop the SHAKING on mobile — the per-rarity glow/shine/holo box-shadow pulses
     (they rasterize poorly on mobile and the faster ones on rarer cards jiggle
     most) and the touch :hover lift (flickers as a finger scrolls past). The water
     wave stays animating (it slides sideways, so it never shook the card). */
  .shop-craft .craft-tier,
  .shop-craft .craft-tier .ct-shine,
  .shop-craft .craft-tier .ct-name { animation: none !important; }
  .shop-craft .craft-tier:hover:not(:disabled),
  .craft-tier:hover:not(:disabled) { transform: none; }
}

/* Calm the center scene glow: the old pulse blinked opacity 0.5->1 and scaled
   1->1.12 every 1.2s, which repaint-thrashed and glitched the stage (worse on
   mobile). Gentle, slow opacity-only fade — no scale, no blink. */
.sc-scene-glow { animation: sceneGlowSoft 6s ease-in-out infinite !important; }
@keyframes sceneGlowSoft { 0%, 100% { opacity: .55; transform: none; } 50% { opacity: .8; transform: none; } }

/* Cards take their TRUE aspect ratio from the image and fill the carousel
   vertically. The inner shrink-wraps the image width; the image fills height. */
.sc-ticker-inner { width: auto !important; height: 100% !important; aspect-ratio: auto !important; }
.sc-ticker-inner img { width: auto !important; height: 100% !important; object-fit: contain !important; }

/* ── Fullscreen card viewer ──────────────────────────────────────────────────
   Click any card → its full-size front fills the screen; click/Esc closes. */
.card-lightbox {
  position: fixed; inset: 0; z-index: 20000;
  display: flex; align-items: center; justify-content: center;
  padding: 4vh 4vw; box-sizing: border-box;
  background: rgba(4,6,12,.92); backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  cursor: zoom-out; animation: clbFade .18s ease both;
}
.card-lightbox-img {
  max-width: 92vw; max-height: 92vh; width: auto; height: auto;
  border-radius: 14px; object-fit: contain;
  box-shadow: 0 30px 90px rgba(0,0,0,.7), 0 0 0 1px rgba(255,255,255,.08);
  animation: clbPop .26s cubic-bezier(.2,1.1,.3,1) both;
}
@keyframes clbFade { from { opacity: 0 } to { opacity: 1 } }
@keyframes clbPop  { from { opacity: 0; transform: scale(.86) } to { opacity: 1; transform: scale(1) } }
/* Carousel + vault cards are tappable — hint it. */
.sc-ticker-inner, .floor-card { cursor: pointer; }

/* ── Hide the header entirely when signed out (desktop + mobile) ──────────────
   Logged out there's nowhere to navigate; the RIP button is the login CTA. */
body.signed-out .ledger-bar { display: none !important; }

/* ── Mobile reveal: card → PSA (card-sized) → CLAIM, all in normal flow ───────
   Dropping the absolute layout during the reveal lets the stage grow so the PSA
   panel and CLAIM button are always reachable by scrolling, and the next thing
   below is the Chase Cards section. */
@media (max-width: 640px) {
  .hero-stage.revealing { overflow: visible; min-height: 0; height: auto; }
  .hero-stage.revealing .stage-frame {
    position: relative; inset: auto; height: auto; overflow: visible;
  }
  .hero-stage.revealing .reveal-zone.active {
    position: relative; inset: auto; height: auto;
  }
  .hero-stage.revealing .reveal-inner { height: auto !important; min-height: 0 !important; }

  /* Cert card BESIDE the card (right) — the SAME premium look as desktop: a gold/
     silver foil frame, dark face and holographic sheen (not a plain grey box). */
  .hero-stage.revealing .reveal-right-panel {
    position: relative; flex: 1 1 0; min-width: 0; align-self: stretch;
    padding: 11px 11px 11px; border: 2px solid transparent; border-radius: 14px; overflow: hidden;
    background:
      radial-gradient(130% 70% at 50% -8%, rgba(255,224,150,.12), transparent 55%)    padding-box,
      linear-gradient(180deg, #1a2233 0%, #0a0e16 100%)                               padding-box,
      linear-gradient(135deg, #e7cd86, #fff7e0 26%, #a9c3df 52%, #ffffff 72%, #d8b96a) border-box;
    box-shadow: 0 18px 48px rgba(0,0,0,.6), inset 0 1px 0 rgba(255,255,255,.10), 0 0 18px rgba(231,205,134,.22);
  }
  .hero-stage.revealing .reveal-right-panel::after {
    content: ""; position: absolute; inset: 0; z-index: 5; pointer-events: none;
    background: linear-gradient(115deg, transparent 40%, rgba(255,255,255,.20) 47%, rgba(180,220,255,.16) 50%, rgba(255,225,170,.16) 53%, transparent 60%);
    background-size: 250% 100%; mix-blend-mode: screen; animation: certSheen 5s ease-in-out infinite;
  }
  /* The panel inside the frame is just content now — no second box. Width 100% so its
     text is constrained to the card (shrinks to fit via cqi, never overflows). */
  .hero-stage.revealing .reveal-psa-panel {
    display: flex; flex-direction: column; justify-content: center; align-items: stretch;
    width: 100% !important; max-width: 100% !important; min-width: 0 !important; position: relative; z-index: 2;
    background: transparent; border: 0; border-radius: 0; padding: 0; box-sizing: border-box;
  }
  /* Only the PSA cert card + the FRONT image show — same dimensions as the card image.
     A fixed-height row both cards share; the cert card takes the card's aspect ratio. */
  .hero-stage.revealing .reveal-zone.expanded .reveal-inner {
    display: flex !important; flex-direction: row !important; align-items: stretch !important;
    justify-content: center !important; gap: 10px !important;
    /* Height capped so the two card-shaped items fit side-by-side within the screen. */
    height: min(44vh, 56vw) !important; width: -webkit-max-content !important; width: max-content !important;
    max-width: 96vw !important; margin: 0 auto !important;
  }
  /* Card is HEIGHT-driven (fills the shared row height, width follows the image's
     aspect) so it ends up the SAME dimensions as the cert card beside it. Without
     this the earlier `width: 42%` rule fought the row height and the two mismatched. */
  .hero-stage.revealing .reveal-zone.expanded .card-flip-wrap {
    flex: 0 0 auto !important; height: 100% !important; width: auto !important; max-width: none !important;
  }
  .hero-stage.revealing .reveal-zone.expanded .card-flip-inner { height: 100% !important; }
  .hero-stage.revealing .reveal-zone.expanded .card-flip-wrap .card-face,
  .hero-stage.revealing .reveal-zone.expanded .card-flip-wrap .card-face img {
    height: 100% !important; width: auto !important;
  }
  .hero-stage.revealing .reveal-zone.expanded .reveal-right-panel {
    flex: 0 0 auto !important; height: 100% !important; width: auto !important;
    aspect-ratio: var(--card-ar, 0.716) !important;
  }
  /* The cert text scales to the card width via container units (cqi) — fits the narrow
     mobile card WITHOUT wrapping. */
  .hero-stage.revealing .psa-slab-label { align-self: center; margin-bottom: 2px; }
  .hero-stage.revealing .psa-card-name { text-align: center; }
  .hero-stage.revealing .psa-detail-list { margin: clamp(4px, 4cqi, 7px) 0 1px; }
  .hero-stage.revealing .psa-drow { padding: clamp(2px, 2.6cqi, 4px) 1px; }
  /* CLAIM button: prominent, full-width, just below the card+cert row. */
  .hero-stage.revealing .reveal-in-actions {
    display: flex; flex-direction: column; align-items: center; gap: 8px;
    width: 100%; max-width: 360px; margin: 6px auto 0; padding-top: 6px;
  }
  .hero-stage.revealing .reveal-in-actions .rip-again-btn { width: 100%; }
}

/* ── Mobile: game viewer topmost, full-bleed carousel, centered content ────── */
@media (max-width: 640px) {
  /* Full-bleed so the carousel reaches both edges and sits flush at the top. */
  main { padding-left: 0; padding-right: 0; max-width: 100%; }
  #galleryView { padding: 24px 12px 0; }
  /* With the userbar PINNED (mobile, signed in) the bar is fixed and #main already
     clears it, so drop the gallery's extra top padding/margin — the rarity cards
     then sit right under the userbar like the chase cards do on the rip page. */
  body.hud-pinned #galleryView { padding-top: 0; }
  body.hud-pinned #shopPanel { margin-top: 0 !important; }
  /* The vault's default 28px top margin + 20px panel padding pushed the gallery
     marquee far below the bar (a ~53px gap vs the rip page's ~16px). Drop the
     margin and trim the top padding when pinned so the gallery keeps the same
     gap-under-userbar as every other page on mobile. */
  body.hud-pinned .gallery-vault { margin-top: 0; padding-top: 20px; }
  /* Stacked, centered sections with the chip on top. */
  .below-stage-row .sidebar-section { padding: 12px 12px 16px; }
  #ripView .chase-section { padding-top: 6px; }  /* header/userbar close to the top */
  .chase-section .top-hits-grid { gap: 8px; }
  /* The card art fills the whole tile (the card takes the image's own aspect
     ratio) — no letterbox / black space. The shaking is handled by the static
     glows above, so we don't need to force a fixed slot height here. */
  .chase-section .floor-card img { width: 100%; height: auto; object-fit: contain; object-position: center; }
  /* Center the "Cards in Play" / "Pull Rates" graph labels on mobile. */
  .pull-rates-section .pr-sub-label { text-align: center; }
}

/* ════════════════════════════════════════════════════════════════════════════
   PLAYER BAR — compact rectangular control (name + credits) over the game
   ════════════════════════════════════════════════════════════════════════════ */
html, body { overflow-x: clip; }              /* full-bleed carousel must not scroll sideways */
[hidden] { display: none !important; }

/* Never allow horizontal drag/scroll — keep everything in frame. `clip` is
   stricter than `hidden` (it forbids ALL sideways scrolling, including the
   touch panning that `hidden` still allowed on mobile), and pinning the width +
   killing horizontal overscroll stops the whole page from sliding left/right. */
html, body {
  overflow-x: clip;
  max-width: 100%;
  overscroll-behavior-x: none;
  /* Stop scroll-anchoring from nudging the page up/down as the carousel animates
     and card art decodes above the fold (the mobile "shaking" chase section). */
  overflow-anchor: none;
}
#main {
  overflow-x: clip;
  max-width: 100%;
  /* Vertical scroll + pinch-zoom only — the browser may never pan #main
     sideways (the carousel owns its own horizontal drag via pointer events). */
  touch-action: pan-y pinch-zoom;
}

.player-hud { position: fixed; top: 12px; right: 12px; z-index: 12100; } /* above the admin overlay too */
/* Pinned to the very top on both modes (desktop) — keeps the compact docked pill
   sizing but stays fixed so it never moves when switching Rip ⇄ Craft. */
.player-hud.hud-docked.hud-fixed-top { position: fixed !important; inset: auto; top: 12px; left: 50%; right: auto; transform: translateX(-50%); z-index: 12100; }
/* Mobile: a full-width fixed top bar, so the userbar never moves between modes. */
@media (max-width: 640px) {
  .player-hud.hud-docked.hud-fixed-top {
    top: 0 !important; left: 0 !important; right: 0 !important; transform: none !important;
    padding: 6px 8px 8px; box-sizing: border-box;
    background: linear-gradient(180deg, rgba(5,7,12,.94) 60%, rgba(5,7,12,0));
  }
  /* Gallery (signed-in) clears the fixed bar; the RIP view reserves its OWN top
     zone (auth-independent, below) so the spacing matches signed in or out. */
  body.hud-pinned.gallery-active #main { padding-top: 46px; }
  body.hud-pinned .gallery-header { display: none; }
}
body.signed-out .player-hud { display: none; } /* signed out → no control; RIP is the CTA */

/* ── Identical top spacing on the RIP view, signed in OR out ──────────────────
   Reserve the same top "bar zone" regardless of auth, and FLOAT the header (which
   holds the signed-out dev switcher) so it never pushes the content down. The
   userbar (signed-in) already floats; now the signed-out header floats too, so the
   chase content starts at exactly the same place either way, desktop + mobile. */
#ripView { padding-top: 54px; }
#ripView .chase-section .chase-head { display: none; }   /* signed-in: the userbar floats over this zone */
body.signed-out #ripView .chase-section .chase-head {
  display: flex; position: fixed; top: 12px; left: 50%; transform: translateX(-50%);
  margin: 0; width: auto; max-width: 640px; z-index: 12090;
}
/* No reserved gap during the fullscreen spin/reveal (the bar slides away then). */
body.spin-cine #ripView, body.rip-reveal #ripView, body.craft-reveal #ripView { padding-top: 0; }
@media (max-width: 640px) {
  #ripView { padding-top: 46px; }
  body.signed-out #ripView .chase-section .chase-head {
    top: 0; left: 0; right: 0; transform: none; width: 100%; max-width: 100%;
    padding: 6px 8px 8px; justify-content: center;
    background: linear-gradient(180deg, rgba(5,7,12,.94) 60%, rgba(5,7,12,0));
  }
}
/* The HUD floats over the (empty) top-right; on mobile drop the content below it. */

/* Three distinct rounded pills: gold credits CTA, the name, and sign-out. */
.player-bar {
  display: flex; align-items: stretch; gap: 7px;
  background: transparent; border: none; box-shadow: none; overflow: visible;
}

/* ── Wallet = navigation ──────────────────────────────────────────────────────
   Two clickable halves: RIPS (rip icon + count → ripping mode) | MANA (potion +
   count → crafting mode). The active mode's half is underlined in its colour. */
/* The red/blue boundary inside the wallet animates back and forth — red and blue
   "fighting for control". No spinning border, no glow. */
@property --wfight { syntax: '<percentage>'; initial-value: 50%; inherits: false; }
/* ── RIP / CRAFT: two SEPARATE nav buttons (replaced the old toggle wallet). RIP is
   red (fire), CRAFT is blue (water); each shows its balance and jumps straight to
   its page. They keep .pb-credits-btn for the userbar height/layout match, but
   override its gold arcade look. ─────────────────────────────────────────────── */
.pb-nav-btn {
  isolation: auto !important; overflow: hidden;
  gap: 7px !important; padding: 0 14px !important;
  color: #fff !important; text-shadow: 0 1px 2px rgba(0,0,0,.5) !important;
  border: 1px solid rgba(0,0,0,.5) !important;
  box-shadow: 0 6px 18px rgba(0,0,0,.4) !important;
  animation: none !important;
  font: 900 13px/1 system-ui, sans-serif !important; letter-spacing: .02em !important;
}
.pb-nav-btn::before, .pb-nav-btn::after { display: none !important; }   /* no gold sheen / ring */
.pb-nav-btn .pb-nav-label { letter-spacing: .08em; }
.pb-nav-btn:hover { filter: brightness(1.12); }
.pb-nav-rip   { background: linear-gradient(180deg, #23c96a, #0c6f3c) !important; }
.pb-nav-craft { background: linear-gradient(180deg, #9a37ec, #571c9a) !important; }
.pb-nav-rip  .rip-logo { width: 18px; height: 18px; }
.pb-nav-rip  #balanceAmount, .pb-nav-rip .pb-cred-amt { color: #fff !important; text-shadow: 0 1px 2px rgba(0,0,0,.55) !important; }
.pb-nav-craft .ico-crystal { width: 20px; height: 20px; filter: drop-shadow(0 0 2px rgba(0,0,0,.6)); }
.pb-nav-craft .js-crystal-amt { color: #eaf6ff !important; text-shadow: 0 1px 2px rgba(0,0,0,.5) !important; }
/* Active page → its button pops with a bright inset ring + themed glow. */
body.rip-active     #creditsBuyBtn.pb-nav-rip   { box-shadow: inset 0 0 0 1.6px rgba(206,255,225,.85), 0 0 16px rgba(60,255,140,.6), 0 6px 18px rgba(0,0,0,.4) !important; filter: brightness(1.08); }
body.gallery-active #craftNavBtn.pb-nav-craft   { box-shadow: inset 0 0 0 1.6px rgba(232,208,255,.85), 0 0 16px rgba(180,90,255,.6), 0 6px 18px rgba(0,0,0,.4) !important; filter: brightness(1.08); }

/* Credits = THE most attractive item in the game: a solid, premium "gold coin"
   pill with an embossed face, a counter-rotating jewelled gold border ring, a
   diagonal specular sheen that sweeps across, a pulsing halo and a gentle
   breathing scale. The animated conic ring uses @property so it spins in place
   (graceful static-gold fallback where @property is unsupported). */
@property --credSpin { syntax: '<angle>'; initial-value: 0deg; inherits: false; }
.pb-credits-btn {
  -webkit-appearance: none; appearance: none; cursor: pointer;
  position: relative; overflow: hidden; isolation: isolate;
  display: flex; align-items: center; gap: 6px;
  /* SAME size as the other header pills — matched padding + font, and NO scale
     "breathe" (which made it physically grow). It earns attention from its glow +
     spinning ring + sheen instead of from being bigger. */
  padding: 0 13px;
  background:
    linear-gradient(180deg, rgba(255,247,214,.32), rgba(255,200,70,.05) 55%, rgba(180,110,12,.18)),
    linear-gradient(180deg, #ffe9a6 0%, #f8c84f 44%, #e89a26 100%);
  color: #4a2d00;
  border: none;
  border-radius: 7px;            /* arcade/slot-cabinet button — less rounded */
  font: 900 12px/1 system-ui, sans-serif; letter-spacing: .01em;
  text-shadow: 0 1px 0 rgba(255,248,214,.7);
  box-shadow:
    0 5px 18px rgba(0,0,0,.45),
    0 0 26px rgba(255,205,80,.6),
    inset 0 1px 0 rgba(255,255,255,.8),
    inset 0 -2px 7px rgba(165,98,8,.45);
  animation: credPulse 1.9s ease-in-out infinite;
}
.pb-credits-btn::before { /* sweeping specular sheen */
  content: ''; position: absolute; top: -25%; bottom: -25%; left: -60%; width: 42%;
  background: linear-gradient(105deg, transparent, rgba(255,255,255,.85), transparent);
  transform: skewX(-18deg); pointer-events: none; z-index: 2; mix-blend-mode: screen;
  animation: credSheen 2.3s ease-in-out infinite;
}
.pb-credits-btn::after { /* spinning jewelled gold border ring */
  content: ''; position: absolute; inset: 0; border-radius: inherit; padding: 1.7px;
  background: conic-gradient(from var(--credSpin),
    #fff7d2, #ffe48a, #ff9a3c, #ffd15c, #fffbe9, #ffb24c, #ffe9a0, #fff7d2);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  animation: credRing 3.2s linear infinite;
  pointer-events: none; z-index: 1;
}
.pb-credits-btn > * { position: relative; z-index: 3; }
.pb-credits-btn:hover, .pb-credits-btn:focus-visible {
  background:
    linear-gradient(180deg, rgba(255,250,224,.4), rgba(255,210,80,.08) 55%, rgba(190,118,14,.2)),
    linear-gradient(180deg, #fff0bc 0%, #ffd166 44%, #f0a52e 100%);
}
@keyframes credPulse {
  0%, 100% { box-shadow: 0 5px 18px rgba(0,0,0,.45), 0 0 22px rgba(255,205,80,.55), inset 0 1px 0 rgba(255,255,255,.8), inset 0 -2px 7px rgba(165,98,8,.45); }
  50%      { box-shadow: 0 5px 18px rgba(0,0,0,.45), 0 0 48px rgba(255,226,124,.98), 0 0 76px rgba(255,196,70,.55), inset 0 1px 0 rgba(255,255,255,.92), inset 0 -2px 7px rgba(165,98,8,.45); }
}
@keyframes credRing  { to { --credSpin: 360deg; } }
@keyframes credBreathe { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.045); } }
@keyframes credSheen {
  0%       { left: -60%; opacity: 0; }
  30%      { opacity: 1; }
  55%      { left: 120%; opacity: 0; }
  100%     { left: 120%; opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .pb-credits-btn { animation: none; }
  .pb-credits-btn::before, .pb-credits-btn::after { animation: none; }
  .pb-credits-btn::before { opacity: 0; }
}
/* Shiny animated diamond = the credits symbol (replaces the gold bars). */
.gem-diamond {
  width: 20px; height: 18px; flex-shrink: 0; overflow: visible; display: inline-block; vertical-align: middle;
  filter: drop-shadow(0 1px 1px rgba(0,0,0,.4)) drop-shadow(0 0 4px rgba(120,210,255,.85));
  animation: gemGlow 2.4s ease-in-out infinite;
}
@keyframes gemGlow {
  0%, 100% { filter: drop-shadow(0 1px 1px rgba(0,0,0,.4)) drop-shadow(0 0 4px rgba(120,210,255,.7)); }
  45%      { filter: drop-shadow(0 1px 1px rgba(0,0,0,.4)) drop-shadow(0 0 9px rgba(160,230,255,1)); }
}
.gem-diamond-lg { width: 30px; height: 28px; }
@media (prefers-reduced-motion: reduce) { .gem-diamond { animation: none; } }
.pb-cred-amt { font-variant-numeric: tabular-nums; letter-spacing: .01em; text-shadow: 0 1px 0 rgba(255,248,214,.7); }
.pb-cred-amt #balanceAmount { font-variant-numeric: tabular-nums; }
/* Combined wallet pill: CR | ◈ crystals together. Thin divider + dark, readable
   crystal count on the gold face. */
.pb-credits-btn .pb-wallet-sep { width: 1px; align-self: stretch; margin: 3px 1px; background: rgba(74,45,0,.35); }
.pb-credits-btn .crystal-gem { width: 16px; height: 16px; flex-shrink: 0; }
.pb-credits-btn .rip-logo { width: 18px; height: 18px; flex-shrink: 0; }
.pb-unit { font-weight: 800; opacity: .8; font-size: .8em; }
/* Mana logo — animated blue glow/pulse (Crystals are "Mana" now). */
.crystal-gem, .crystal-ico, .ico-crystal { animation: manaPulse 2.1s ease-in-out infinite; transform-origin: center; display: inline-block; }
@keyframes manaPulse {
  0%, 100% { filter: drop-shadow(0 0 3px rgba(69,177,255,.6)); transform: scale(1); }
  50%      { filter: drop-shadow(0 0 9px rgba(143,227,255,1)); transform: scale(1.14); }
}
@media (prefers-reduced-motion: reduce) { .crystal-gem, .crystal-ico, .ico-crystal { animation: none; } }
.pb-credits-btn .js-crystal-amt { font: 900 12px/1 system-ui, sans-serif; color: #4a2d00; font-variant-numeric: tabular-nums; text-shadow: 0 1px 0 rgba(255,248,214,.7); }
/* Gallery icon beside the name, so it's clear the name button opens your gallery. */
.pb-name-btn .pb-gallery-ic { width: 15px; height: 15px; flex: 0 0 auto; margin-right: 7px; opacity: .82; } /* icon on the LEFT now */
.pb-name-btn:hover .pb-gallery-ic { opacity: 1; }
/* Standalone log-out (power) button. */
.pb-power:hover { color: #ff9fb0; }

/* Player name — its own separate rounded pill. Taps to your gallery / admin. */
/* User pill — NEUTRAL grey (the name + log-out). Shows the FULL username. */
.pb-name-btn {
  -webkit-appearance: none; appearance: none; cursor: pointer;
  display: flex; align-items: center; flex: 0 0 auto;
  color: #d4d9e2; font: 700 12px/1 system-ui, sans-serif;
  padding: 0 14px; max-width: none;
  background: linear-gradient(180deg, rgba(36,38,44,.9), rgba(22,23,28,.9));
  border: 1px solid rgba(255,255,255,.18); border-radius: 7px;
  box-shadow: 0 6px 18px rgba(0,0,0,.42);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  transition: background .14s;
}
.pb-name-btn:hover, .pb-name-btn:focus-visible { background: linear-gradient(180deg, rgba(50,52,60,.92), rgba(30,32,38,.92)); }
.pb-name { display: block; white-space: nowrap; }   /* full name — no ellipsis */
.pb-name-btn.in-gallery .pb-name, .pb-name-btn:hover .pb-name { color: #fff; }
/* (The players-online readout now lives in the big USERS ONLINE arcade cabinet on the
   rip page — see #onlineMeter / .jm-online-dot. The old userbar/bottom-right pill was
   removed.) */

/* ── Admin marker: a little king's crown perched on a top corner of the user
   box — shown wherever an admin's box appears (their own userbar pill, the dev
   switcher, the connected-users list). Replaces the old gold/cyan styling. ── */
:root {
  --admin-crown: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 18'%3E%3Cpath d='M2.5 15 L4.5 6 L9 11 L12 3.5 L15 11 L19.5 6 L21.5 15 Z' fill='%23ffd15c' stroke='%23916a12' stroke-width='1.1' stroke-linejoin='round'/%3E%3Crect x='2.5' y='13.6' width='19' height='2.8' rx='1.1' fill='%23ffd15c' stroke='%23916a12' stroke-width='1.1'/%3E%3Ccircle cx='12' cy='3.4' r='1.5' fill='%23fff3c4'/%3E%3C/svg%3E");
}
.pb-name-btn.is-admin, .dev-pill.dev-pill-admin { overflow: visible; position: relative; }
.pb-name-btn.is-admin::before,
.dev-pill.dev-pill-admin::before {
  content: '';
  position: absolute;
  top: -10px; right: -7px;
  width: 20px; height: 15px;
  background: var(--admin-crown) center / contain no-repeat;
  transform: rotate(16deg);
  filter: drop-shadow(0 1px 1.5px rgba(0,0,0,.55));
  pointer-events: none;
  z-index: 4;
}
/* Inline crown used in the admin connected-users list. */
.adm-crown-ic {
  display: inline-block; width: 17px; height: 13px; vertical-align: -1px;
  margin-right: 1px;
  background: var(--admin-crown) center / contain no-repeat;
  filter: drop-shadow(0 1px 1px rgba(0,0,0,.4));
}
/* Connected users = a compact grid of small cells, not one long column. */
/* Live admin tools moved up top: broadcast chat + special animations, side by
   side (stack when narrow). */
/* Live tools condensed onto ONE toolbar row: connected users · say · animate. */
.admin-top-tools {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px;
  padding: 9px 16px; border-bottom: 1px solid var(--line); background: var(--bg-panel);
}
.adm-tool { display: flex; align-items: center; gap: 7px; min-width: 0; }
.adm-tool-label { flex: 0 0 auto; font-size: 15px; cursor: help; opacity: .85; line-height: 1; }
/* Connected users: inline username chips that flow in the remaining space. */
.adm-tool-live { flex: 1 1 260px; min-width: 0; }
.adm-tool-live #adm-live-list { min-width: 0; flex: 1 1 auto; }
.adm-live-list-inline .adm-chat-line { gap: 5px 6px; }
.adm-live-list-inline > div { font-size: 11.5px; opacity: .6; }   /* disabled/empty note */
/* Say to everyone: input grows, button + status inline. */
.adm-tool-say { flex: 2 1 320px; min-width: 0; }
.adm-tool-say #adm-say-input {
  flex: 1 1 auto; min-width: 130px; padding: 8px 11px; border-radius: 9px;
  border: 1px solid var(--line); background: rgba(255,255,255,.06); color: #fff;
  font: 600 13px system-ui, sans-serif;
}
.adm-tool-say .status-msg { flex: 0 0 auto; max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.adm-tool-say .status-msg:empty { display: none; }
/* Say scope toggle: green = broadcast to everyone, amber = local (just me). */
.adm-say-scope { flex: 0 0 auto; white-space: nowrap; }
.adm-say-scope.scope-self { background: rgba(255,183,60,.16); border-color: rgba(255,183,60,.5); color: #ffd27a; }
.adm-tool-anim { flex: 0 0 auto; }
/* Chatroom-style presence: one flowing line of just-the-username chips. Tap a name
   to grant +3 rips; press-and-hold to ban. */
.adm-chat-line { display: flex; flex-wrap: wrap; gap: 6px 8px; align-items: center; }
.adm-chat-user {
  cursor: pointer; user-select: none; -webkit-user-select: none; touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  font: 700 12.5px/1.35 system-ui, sans-serif; color: #cfe0ff; white-space: nowrap;
  padding: 2px 9px; border-radius: 999px;
  background: rgba(120,200,255,.08); border: 1px solid rgba(120,200,255,.24);
  transition: background .12s ease, transform .07s ease, border-color .12s ease;
}
.adm-chat-user:hover { background: rgba(120,200,255,.18); border-color: rgba(120,200,255,.5); }
.adm-chat-user:active { transform: scale(.95); }
.adm-chat-user.is-admin { color: #ffe08a; background: rgba(255,209,92,.10); border-color: rgba(255,209,92,.42); }
.adm-chat-user.is-banned { color: #ff8f8f; text-decoration: line-through; background: rgba(255,107,107,.10); border-color: rgba(255,107,107,.42); }
.adm-chat-user.is-idle { opacity: .5; }
.adm-chat-user.is-anon { cursor: default; opacity: .6; }
.adm-chat-user.is-anon:hover { background: rgba(120,200,255,.08); border-color: rgba(120,200,255,.24); }
.adm-chat-user.granting { opacity: .55; }
.adm-chat-user.granted { color: #7df0bb; background: #13241c; border-color: #48d597; transform: scale(1.04); }
/* Tiny usage hint beside the section title. */
.adm-chat-hint { font: 600 10.5px/1 system-ui, sans-serif; letter-spacing: normal; text-transform: none; color: var(--text-dim); }
/* Compact one-line hint under the "Say it" field (replaces the old paragraph). */
.adm-mini-hint { margin: 6px 0 0; font-size: 11px; line-height: 1.35; color: var(--text-dim); }
.adm-mini-hint code { font-size: 10.5px; }
/* Connected users — compact collapsible strip (no longer top-heavy). */
.adm-live-wrap { padding: 8px 16px; border-bottom: 1px solid var(--line); flex-shrink: 0; }
.adm-live-wrap > summary {
  cursor: pointer; list-style: none; padding: 4px 0;
  font: 700 12px/1 system-ui, sans-serif; letter-spacing: .04em; text-transform: uppercase; color: #9fb0c6;
}
.adm-live-wrap > summary::-webkit-details-marker { display: none; }
.adm-live-list-compact { max-height: 150px; overflow-y: auto; margin-top: 8px; }
.adm-live-list-compact .adm-live-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 6px; }
.adm-live-list-compact .adm-user-cell { padding: 5px 7px; }

.adm-live-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(185px, 1fr));
  gap: 8px;
}
.adm-user-cell {
  display: flex; flex-direction: column; gap: 5px; min-width: 0;
  border: 1px solid var(--line); border-radius: 8px;
  padding: 7px 9px; background: rgba(255,255,255,.025);
}
.adm-user-cell.auc-idle { opacity: .6; }
.auc-top { display: flex; align-items: center; gap: 5px; min-width: 0; font-size: 13px; }
.auc-actions { display: flex; gap: 6px; }
.auc-meta { font-size: 11px; opacity: .55; font-family: var(--font-mono, monospace); }

@media (max-width: 640px) {
  /* ── Kill the vertical "shaking" ───────────────────────────────────────────
     Animating box-shadow + backdrop-filter repaints a large blurred region
     every frame, which judders the userbar pills on mobile. (The foil glow is
     already static globally — see above.) */
  .pb-credits-btn { animation: none !important; transform: none !important; }
  .pb-credits-btn::before { animation: none !important; opacity: 0 !important; }
  .pb-credits-btn::after { animation: none !important; }   /* keep static gold ring, no repaint shake */
  .pb-credits-btn, .pb-name-btn {
    backdrop-filter: none !important; -webkit-backdrop-filter: none !important;
  }

}

/* ── SPIN = split: chase cards TOP, wheel/game BOTTOM. The play area is BIGGER
   than the chase (≈38 / 62) and the wheel is scaled UP to fill it. Header +
   userbar pushed out; pull-rates hide. Applies until the rip animation starts. */
body.spin-cine:not(.rip-reveal) #ripView { height: 100vh; height: 100dvh; overflow: hidden; }
/* Full-bleed during the spin/rip/victory: drop main's side + bottom padding so
   #ripView fills the whole viewport and its overflow:hidden can't clip the
   background into visible left/right edges. */
body.spin-cine:not(.rip-reveal) main,
body.rip-reveal main,
body.craft-reveal main {
  padding-bottom: 0 !important;
  padding-left: 0 !important;
  padding-right: 0 !important;
}
body.spin-cine:not(.rip-reveal) #ripView .chase-section {
  order: 1; flex: 0 0 auto; height: 38vh; height: 38dvh;
  overflow: visible; margin: 0; padding: 0;
  display: flex; flex-direction: column; justify-content: center;
}
body.spin-cine:not(.rip-reveal) #ripView #heroStage {
  order: 2; flex: 1 1 auto; height: 62vh; height: 62dvh; min-height: 0 !important;
  overflow: hidden;
}
body.spin-cine:not(.rip-reveal) #ripView .pull-rates-section { display: none !important; }
body.spin-cine:not(.rip-reveal) #ripView .chase-head { display: none !important; }

/* USERS ONLINE cabinet belongs to the IDLE rip page only. Fade it out the moment a
   spin, rip or victory takes over so it never hovers over the animation. (rip-reveal /
   victory already drop the whole chase-section; this also covers the spin phase, where
   the chase cards keep flying but the section stays put.) Opacity-only (not display) so
   the orbiting chase cards keep their anchor and don't jump. */
#onlineMeter { transition: opacity .22s ease; }
body.spin-cine #onlineMeter,
body.rip-reveal #onlineMeter,
body.victory #onlineMeter {
  opacity: 0 !important;
  pointer-events: none !important;
}

/* ── RIP ANIMATION → REVEAL/CLAIM = FULLSCREEN over ALL cards. The pack tear,
   particles, flash and reveal fill the whole screen — never cropped to the
   bottom half. (rip-reveal covers the pack, reveal and victory.) ───────────── */
body.rip-reveal #ripView,
body.craft-reveal #ripView { height: 100vh; height: 100dvh; overflow: hidden; }
body.rip-reveal #ripView .chase-section,
body.rip-reveal #ripView .pull-rates-section { display: none !important; }
/* A CRAFT reveal shows only the wheel + pack — never the chase cards (which
   the normal rip slides into the cinematic split). Keep them hidden the whole
   craft animation, including the wheel-whirl (spin-cine) phase. */
body.craft-reveal #ripView .chase-section,
body.craft-reveal #ripView .pull-rates-section { display: none !important; }
body.rip-reveal #ripView #heroStage,
body.craft-reveal #ripView #heroStage {
  order: 2; flex: 1 1 auto; height: 100vh; height: 100dvh; min-height: 0 !important;
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  /* Hide the scrollbars while keeping scroll: overflow-y:auto forces overflow-x
     to compute to auto as well, so the overflow:visible reveal panels were
     spawning BOTH an H and V scrollbar at the viewport edges. Keep the panel
     reachable (it can still scroll) but never paint the bars. */
  scrollbar-width: none; -ms-overflow-style: none;
}
body.rip-reveal #ripView #heroStage::-webkit-scrollbar,
body.craft-reveal #ripView #heroStage::-webkit-scrollbar { width: 0; height: 0; display: none; }
/* Desktop reveal: the card+cert row was height:100% (the whole 100dvh stage), which
   shoved the CLAIM button off the bottom of the screen — and the scrollbar is hidden,
   so it was unreachable. Cap the row's height to leave room for CLAIM below it. */
@media (min-width: 641px) {
  body.rip-reveal #ripView .reveal-inner,
  body.craft-reveal #ripView .reveal-inner { height: 72dvh !important; }
  body.rip-reveal #ripView .reveal-in-actions,
  body.craft-reveal #ripView .reveal-in-actions { padding-bottom: 10px; }
}
/* Belt-and-braces: lock the root scroll containers in CSS too (not just the JS
   MutationObserver) so the full-screen rip never shows page edge scrollbars even
   if the observer hasn't fired yet. */
body.spin-cine, body.rip-reveal, body.craft-reveal,
html:has(body.spin-cine), html:has(body.rip-reveal), html:has(body.craft-reveal) { overflow: hidden !important; }

/* Phones: chase cards full-bleed 3-across; spin wheel scaled up to fill the
   bigger bottom area (uniform scale → cards + gaps grow together, no overlap). */
@media (max-width: 640px) {
  /* Snap the chase row + its cards straight into the centred spin layout with NO
     transition, so they can't be seen sliding/creeping left as the section
     animates into place — they just appear, centred. */
  body.spin-cine:not(.rip-reveal) #ripView .chase-section,
  body.spin-cine:not(.rip-reveal) #ripView .chase-section .top-hits-grid,
  body.spin-cine:not(.rip-reveal) #ripView .chase-section .floor-card,
  body.spin-cine:not(.rip-reveal) #ripView .chase-section .floor-card img {
    transition: none !important;
  }
  body.spin-cine:not(.rip-reveal) #ripView .chase-section .top-hits-grid {
    max-width: none !important;
    /* width:100% + justify-content:center keeps the cards CENTRED no matter how the
       chase-section's padding animates — a calc(50%-50vw) breakout shifted with the
       container width, which is what made them creep left during the spin. */
    width: 100%; margin: 0 !important;
    grid-template-columns: none;
    grid-auto-flow: column; grid-auto-columns: max-content;
    justify-content: center !important; justify-items: center; place-content: center;
    align-content: center; align-items: center;
    gap: 8px; padding: 0; box-sizing: border-box;
  }
  body.spin-cine:not(.rip-reveal) #ripView .chase-section .floor-card { width: auto; height: auto; margin: 0; }
  body.spin-cine:not(.rip-reveal) #ripView .chase-section .floor-card img {
    width: auto; height: auto; max-height: 32dvh; max-width: 31vw; object-fit: contain;
  }
  /* A modest enlargement (was scale 1.7, which blew the cards up and cropped them
     out of frame). Keeps the whole card visible with several across the wheel. */
  body.spin-cine:not(.rip-reveal) #ripView #heroStage #scTicker {
    height: 230px; top: 50%; bottom: auto; transform: translateY(-50%) scale(1.15);
  }
  /* Crafting: keep the wheel at its natural size (no rip blow-up). */
  body.spin-cine.craft-reveal:not(.rip-reveal) #ripView #heroStage #scTicker {
    height: 210px; transform: translateY(-50%) scale(1.0);
  }
}

/* Desktop: 3 chase cards fill the (smaller) top by height; spin wheel scaled up
   to fill the bigger bottom; victory reverts to the flowing card+panel reveal. */
@media (min-width: 641px) {
  body.spin-cine:not(.rip-reveal) #ripView .chase-section .top-hits-grid {
    max-width: none !important; width: auto; height: auto; margin: 0 auto;
    grid-template-columns: repeat(3, auto);
    justify-content: center; align-content: center; gap: 16px;
  }
  body.spin-cine:not(.rip-reveal) #ripView .chase-section .floor-card { width: auto; height: auto; }
  body.spin-cine:not(.rip-reveal) #ripView .chase-section .floor-card img {
    width: auto; height: auto; max-height: 33vh; max-width: 24vw; object-fit: contain;
  }
  body.spin-cine:not(.rip-reveal) #ripView #heroStage #scTicker {
    height: 360px; top: 50%; bottom: auto; transform: translateY(-50%) scale(1.5);
  }
  /* Crafting: don't blow the wheel up as much as a rip — the big 1.5× felt too large. */
  body.spin-cine.craft-reveal:not(.rip-reveal) #ripView #heroStage #scTicker {
    height: 300px; transform: translateY(-50%) scale(1.12);
  }
  /* Centre the reveal in the viewport (#ripView is a flex column → just give it
     the full height and centre its single child). */
  body.victory #ripView {
    height: 100vh !important; height: 100dvh !important; overflow: hidden !important;
    justify-content: center !important;
  }
  body.victory #ripView #heroStage {
    height: auto !important; min-height: 62vh !important;
    flex: 0 0 auto !important; overflow: visible !important;
  }
  /* Keep the WHOLE prize (both card faces + the cert card) inside the viewport so
     the cert card — and the CLAIM button pinned to its bottom — never slips off
     the right edge (where #ripView's overflow:hidden would clip it dead, killing
     the click). Cap the card heights so their derived widths stay small, and pull
     the cert card in tight against the cards. */
  body.victory .hero-stage .reveal-zone.expanded .reveal-inner {
    display: flex !important; flex-direction: row !important;
    justify-content: center !important;
    /* STRETCH (not center): the row owns the height and every child fills it, so
       the two card faces and the cert card are guaranteed the exact same height,
       top and bottom — no per-child height that can diverge. */
    align-items: stretch !important;
    gap: 16px !important;
    height: min(60vh, 27vw) !important; max-height: 540px !important; min-height: 300px !important;
    /* Shrink the row to EXACTLY fit [cards + gap + panel] and centre it as one unit.
       With no free space inside the container, the panel physically cannot drift to
       the right — it sits glued next to the cards. */
    width: -webkit-max-content !important; width: max-content !important;
    max-width: 96vw !important;
    padding: 0 !important;
    margin: 0 auto !important;
    overflow: visible !important;
  }
  /* KILL the legacy "slide the card left on expand" transform — that shift (plus a
     grow-able panel) is what opened the big empty gap between the cards and the PSA
     cert panel. Now both items are fixed-size and the row centres tightly. */
  body.victory .hero-stage .reveal-zone.expanded .card-flip-wrap {
    transform: none !important;
    flex: 0 0 auto !important;
  }
  body.victory .hero-stage .reveal-zone.expanded .reveal-right-panel {
    flex: 0 0 auto !important;
    margin: 0 !important;
    order: -1 !important;   /* PSA info on the LEFT → front card centred, back card right */
  }
  /* All three — front card, back card, PSA cert card — share ONE height and a
     card-like shape, so they read as an equal-sized trio sitting flush together.
     The cert card's width comes from a card aspect-ratio (no JS measuring), giving
     the info room to breathe and keeping it glued to the cards. */
  body.victory .hero-stage .card-flip-wrap,
  body.victory .hero-stage .reveal-zone.expanded .reveal-right-panel {
    height: 100% !important;          /* = the row height set on .reveal-inner */
    max-height: none !important;
    min-height: 0 !important;
    align-self: stretch !important;
  }
  body.victory .hero-stage .card-flip-inner { gap: 12px !important; }
  body.victory .hero-stage .reveal-zone.expanded .reveal-right-panel {
    width: auto !important;
    aspect-ratio: var(--card-ar, 0.716) !important;   /* EXACT card image proportions (JS) */
    max-width: none !important;
  }
  /* Only the PSA cert card + the FRONT image show on victory — hide the card BACK. */
  body.victory .hero-stage .card-face.back { display: none !important; }
  body.victory .hero-stage .reveal-zone.expanded .reveal-psa-panel { width: 100% !important; box-sizing: border-box !important; min-width: 0 !important; }
  /* The two card faces fill the SAME height as the cert card, vertically CENTRED,
     but keep their NATURAL aspect ratio (no forced 0.62 box). Forcing 0.62 +
     object-fit:contain letterboxed the ~0.71 card art SHORTER than its box, so the
     art looked smaller than the full-height PSA card next to it. With natural
     aspect, the art reaches full height — all three tops & bottoms line up exactly,
     same height, same Y. */
  body.victory .hero-stage .reveal-zone.expanded .card-flip-inner {
    align-items: center !important; height: 100% !important;
  }
  body.victory .hero-stage .reveal-zone.expanded .card-face {
    align-self: center !important;
    height: 100% !important; width: auto !important; aspect-ratio: auto !important;
  }
  body.victory .hero-stage .reveal-zone.expanded .card-face img {
    width: auto !important; height: 100% !important;
    object-fit: contain !important; object-position: center center !important;
  }
}

/* Same background top + bottom during the spin/rip: drop the game stage's solid
   panel so the one shared page gradient shows through both halves. */
body.spin-cine .hero-stage .stage-frame,
body.rip-reveal .hero-stage .stage-frame { background: transparent !important; }

/* ── Gold bar = the credits symbol. Solid gold ingot with a sweeping specular
   shine (SMIL inside the SVG) and a soft gold glow that pulses. ───────────── */
.gold-bar { width: 17px; height: 13px; overflow: visible; vertical-align: middle; display: inline-block;
  filter: drop-shadow(0 1px 1px rgba(0,0,0,.45)) drop-shadow(0 0 3px rgba(255,200,70,.65));
  animation: goldGlow 2.6s ease-in-out infinite; }
.gold-bar-lg { width: 30px; height: 22px; }
@keyframes goldGlow {
  0%, 100% { filter: drop-shadow(0 1px 1px rgba(0,0,0,.45)) drop-shadow(0 0 3px rgba(255,200,70,.55)); }
  45%      { filter: drop-shadow(0 1px 1px rgba(0,0,0,.45)) drop-shadow(0 0 7px rgba(255,224,120,.95)); }
}
@media (prefers-reduced-motion: reduce) { .gold-bar { animation: none; } }

/* ── Cinematic spin: while the wheel is ripping, enlarge the game and clear the
   stage — the game rises to the top (chase presses out the top, pull rates
   collapses), lean into the wheel with a zoom + vignette, and dim the HUD. ─── */
.chase-section, .pull-rates-section {
  max-height: 1200px;
  transition: opacity .45s ease, transform .55s cubic-bezier(.5,0,.2,1),
              max-height .5s ease, margin .45s ease, padding .45s ease;
}
#scTicker { transition: transform .5s cubic-bezier(.3,0,.2,1); }
#heroStage { transition: min-height .5s cubic-bezier(.3,0,.2,1); }
.player-hud { transition: opacity .4s ease, transform .4s ease; }

/* Enlarge the game view for the pack/reveal (added once the carousel is hidden,
   so the pinned-width cards never distort). min-height is a floor — the reveal
   can still grow beyond it. */
body.rip-reveal #heroStage,
body.craft-reveal #heroStage { min-height: 62vh; }
body.spin-cine #scTicker { transform: scale(1.08); }
body.spin-cine #scScene { filter: brightness(1.12) saturate(1.12); }
/* No dark vignette + no centre spotlight during a rip — those box the game off
   from the chase cards. The two halves share one flat, continuous background. */
body.spin-cine .hero-stage::before,
body.rip-reveal .hero-stage::before { opacity: 0 !important; }
/* The whole top bar (userbar / HUD) lifts up out of the way for the ENTIRE rip —
   spin, pack, and claim — and slides back down only once we return to idle. */
body.spin-cine .player-hud,
body.rip-reveal .player-hud,
body.craft-reveal .player-hud {
  opacity: 0 !important; pointer-events: none !important;
  transform: translateY(-140%) !important;
}

/* Keep the chase cards during the spin (they stay prominent up top while the
   wheel spins below) — only the pull-rates collapse away. */
body.spin-cine .pull-rates-section {
  overflow: hidden;
  max-height: 0 !important; opacity: 0 !important;
  margin-top: 0 !important; margin-bottom: 0 !important;
  padding-top: 0 !important; padding-bottom: 0 !important;
  border: 0 !important; pointer-events: none;
  transform: translateY(20%);
}

@media (prefers-reduced-motion: reduce) {
  body.spin-cine #scTicker { transform: none; }
  body.rip-reveal #heroStage,
  body.craft-reveal #heroStage { min-height: 360px; }
}
.pb-profile.in-gallery .pb-name { color: #ffd98a; }

/* Icon actions: buy / admin / sign out. */
/* Sign-out (and any pb-icon) — its own separate rounded pill. */
.pb-icon {
  -webkit-appearance: none; appearance: none; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; color: #cfd8e6;
  background: linear-gradient(180deg, rgba(20,25,36,.85), rgba(12,15,23,.85));
  border: 1px solid rgba(0,229,255,.32); border-radius: 7px;
  box-shadow: 0 6px 18px rgba(0,0,0,.42);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  transition: background .14s, color .14s;
}
.pb-icon svg { width: 18px; height: 18px; }
.pb-buy { color: #79e9b3; }
.pb-buy .cb-plus { font: 900 20px/1 system-ui, sans-serif; }
.pb-buy:hover   { background: rgba(60,230,140,.16); color: #b6ffd8; }
.pb-admin:hover { background: rgba(168,111,224,.20); color: #e6c6ff; }
.pb-power:hover { background: rgba(255,80,80,.16); color: #ffb4b4; }

/* ── Widescreen carousel: let the hero stage break out to the full viewport ── */
@media (min-width: 641px) {
  .hero-stage { width: 100vw; max-width: 100vw; margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw); }
}

@media (max-width: 640px) {
  .player-hud { top: 10px; right: 10px; }
  .pb-icon { width: 34px; }
}

/* ── Grab-to-spin affordance on the carousel (desktop + touch) ─────────────── */
.sc-ticker-track { cursor: grab; }
.sc-ticker-card .sc-ticker-inner { cursor: grab; }
.hero-stage.sc-grabbing, .hero-stage.sc-grabbing * { cursor: grabbing !important; }
/* While dragging, DROP the rare lightning ::before — it uses mix-blend-mode +
   stacked drop-shadow filters, which force the whole moving track to re-composite
   every frame (the main source of mobile drag jank). Pause the rest. So the GPU
   only slides a plain image layer → liquid drag. */
.hero-stage.sc-grabbing .sc-ticker-inner::before { display: none !important; }
.hero-stage.sc-grabbing .sc-ticker-inner::after,
.hero-stage.sc-grabbing .sc-scene,
.hero-stage.sc-grabbing .sc-scene-glow { animation-play-state: paused !important; }
.hero-stage.sc-grabbing .sc-ticker-track { will-change: transform; }

/* ── Spin perf gate ───────────────────────────────────────────────────────────────────
   .sc-spinning is toggled on the track (by app.js) whenever the wheel is genuinely MOVING
   FAST — a thrown coast, a rip glide, or the craft-wheel land — for BOTH the rip and craft
   carousels. While it's set we freeze every per-card animated effect (rare lightning, foil
   chase pulse, holo/foil sweeps) so the compositor only slides a plain image layer instead
   of repainting ~10 cards each frame. That's what kills the tearing/jank on a fast spin.
   The instant the wheel settles the class drops and every effect resumes exactly as before. */
.sc-ticker-track.sc-spinning { will-change: transform; }
/* Spin = PLAIN cards. Fully REMOVE every rarity effect while the wheel moves: the holo
   shine + rare lightning (::before), the foil gloss sweep, any tier pulse animation, AND
   the tier-colour border ring/glow (::after box-shadow) on ALL cards — no borders glowing
   during the spin, for a smoother read. Everything returns the instant the wheel settles. */
/* Hide the holo shine + rare lightning (::before). */
.sc-ticker-track.sc-spinning .sc-ticker-inner::before { display: none !important; }
/* Drop the tier-colour border RING + glow entirely while the wheel is moving — every card
   is a plain, calm image for a smoother spin (the ::after carries the rarity ring/glow for
   all tiers, foil included). Every ring/glow returns the instant the wheel settles. */
.sc-ticker-track.sc-spinning .sc-ticker-inner::after {
  box-shadow: none !important; background: none !important; animation: none !important;
}
/* Kill any per-card pulse/filter AND the outer glow itself — foil-chase carries a soft gold
   box-shadow halo (0 0 15px) on the element, so drop box-shadow too. With this + the ::after
   ring removed + the ::before hidden, every spinning card is a truly plain image (no border,
   no glow). Everything returns the instant the wheel settles. */
.sc-ticker-track.sc-spinning .sc-ticker-inner,
.sc-ticker-track.sc-spinning .sc-ticker-inner.tier-foil-chase,
.sc-ticker-track.sc-spinning .sc-ticker-inner.foil-run,
.sc-ticker-track.sc-spinning .sc-ticker-inner.tier-rare { animation: none !important; filter: none !important; box-shadow: none !important; }

/* ── Spin hint: ONE bold, glossy anime arrow that swipes left↔right to clearly
   demonstrate the grab-and-spin gesture — points right while it slides right,
   flips and points left while it slides left, with trailing speed lines. Simple,
   obvious and pachinko-intuitive. Vanishes the instant the user grabs. ─────── */
.spin-hints { position: absolute; inset: 0; z-index: 19; pointer-events: none; transition: opacity .25s ease; }
.spin-hints.hidden { opacity: 0; }
.spin-swipe {
  position: absolute; top: 50%; left: 50%;
  will-change: transform, opacity;
  filter: drop-shadow(0 5px 12px rgba(0,0,0,.5)) drop-shadow(0 0 24px rgba(255,70,190,.95));
  animation: spinSwipe 2.6s cubic-bezier(.5,0,.2,1) infinite;
}
.spin-swipe svg { width: 220px; height: auto; display: block; }
/* Instructive anime label sitting above the swiping arrow — bold gradient text
   with a thick white outline + pink glow, matching the arrow's styling. It's
   pinned to centre (the arrow translates, the label stays put and pulses). */
.spin-label {
  position: absolute; left: 50%; top: calc(50% - 92px);
  transform: translateX(-50%);
  font: 900 26px/1 system-ui, sans-serif; letter-spacing: .06em; white-space: nowrap;
  color: #fff;
  background: linear-gradient(180deg, #fff0fb, #ff5bd0 55%, #c23cff);
  -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
  -webkit-text-stroke: 2.5px #fff;
  paint-order: stroke fill;
  filter: drop-shadow(0 2px 0 #3a0f5f) drop-shadow(0 0 14px rgba(255,70,190,.9));
  animation: spinLabelPulse 1.6s ease-in-out infinite;
}
@keyframes spinLabelPulse {
  0%, 100% { transform: translateX(-50%) scale(1);    opacity: .9; }
  50%      { transform: translateX(-50%) scale(1.08); opacity: 1; }
}
.spin-streaks { animation: spinStreak 2.6s ease-in-out infinite; }
/* The arrow's sparkle stars now twinkle exactly like the background ones — each
   scales + rotates around its OWN centre (fill-box) and pulses opacity, the two
   stars out of phase. */
.spin-sparkle path { transform-box: fill-box; transform-origin: center; animation: spinSparkleTwinkle 1.8s ease-in-out infinite; }
.spin-sparkle path:nth-child(2) { animation-delay: .7s; }
@keyframes spinStreak { 0%, 100% { opacity: .2; } 45% { opacity: .95; } }
@keyframes spinSparkleTwinkle {
  0%, 100% { opacity: .3; transform: scale(.55) rotate(0deg); }
  50%      { opacity: 1;  transform: scale(1.12) rotate(22deg); }
}
/* Slide right pointing right → fade → flip (hidden) → slide left pointing left. */
@keyframes spinSwipe {
  0%,  6%  { transform: translate(calc(-50% - 36px), -50%) scaleX(1)  scale(.9);   opacity: 0; }
  16%      { transform: translate(calc(-50% - 36px), -50%) scaleX(1)  scale(1);    opacity: 1; }
  40%      { transform: translate(calc(-50% + 74px), -50%) scaleX(1)  scale(1.08); opacity: 1; }
  47%      { transform: translate(calc(-50% + 96px), -50%) scaleX(1)  scale(1);    opacity: 0; }
  50%      { transform: translate(calc(-50% + 36px), -50%) scaleX(-1) scale(.9);   opacity: 0; }
  60%      { transform: translate(calc(-50% + 36px), -50%) scaleX(-1) scale(1);    opacity: 1; }
  84%      { transform: translate(calc(-50% - 74px), -50%) scaleX(-1) scale(1.08); opacity: 1; }
  91%      { transform: translate(calc(-50% - 96px), -50%) scaleX(-1) scale(1);    opacity: 0; }
  100%     { transform: translate(calc(-50% - 36px), -50%) scaleX(1)  scale(.9);   opacity: 0; }
}
@media (max-width: 640px) {
  .spin-swipe svg { width: 172px; height: auto; }
  .spin-label { font-size: 20px; top: calc(50% - 74px); -webkit-text-stroke-width: 2px; }
}
@media (prefers-reduced-motion: reduce) {
  .spin-swipe { animation: none !important; opacity: .85; transform: translate(-50%,-50%); }
  .spin-streaks, .spin-sparkle path, .spin-label { animation: none; }
}

/* ── RIP emblem: drop the big letters, "CR" price and rotating rings. The center
   is just an invisible tap/click target; the pink sweep arrows are the only cue. */
.rip-emblem::before, .rip-emblem::after { display: none !important; }
.rip-emblem .rip-emblem-text, .rip-emblem .rip-emblem-price { display: none !important; }
.rip-emblem { padding: 44px 80px; }            /* keep a comfortable tap area */
.rip-emblem:disabled { opacity: 0 !important; }

/* ── RARE cards get the SAME polished catch-of-light shine as the legendaries, but
   PURPLE — a soft glossy sheen that passes rarely (once every 30s). It's phase-offset
   half a cycle (+15s) from the legendary shine so rares and legendaries never glint at
   the same moment, keeping the board from feeling busy. ──────────────────────────── */
.floor-card.tier-rare, .sc-ticker-inner.tier-rare { position: relative; overflow: hidden; }
.floor-card.tier-rare::before,
.sc-ticker-inner.tier-rare::before {
  content: ''; position: absolute; inset: 0; border-radius: inherit; z-index: 3; pointer-events: none;
  background: linear-gradient(
    115deg,
    transparent 40%,
    rgba(196,144,255,0.22) 47%,
    rgba(230,210,255,0.44) 50%,
    rgba(196,144,255,0.22) 53%,
    transparent 60%
  );
  background-repeat: no-repeat;
  background-size: 250% 100%;
  animation: foilShine 30s linear infinite;
  animation-delay: calc(var(--foil-delay, 0s) + 15s);
  opacity: 0;
}

/* ── Boot gate overlay: shown until the carousel + chase images are decoded ── */
.boot-overlay {
  position: fixed; inset: 0; z-index: 2147483600;
  display: flex; align-items: center; justify-content: center;
  background: radial-gradient(120% 120% at 50% 30%, #11131c 0%, #07080d 70%, #04050a 100%);
  opacity: 1; transition: opacity .45s ease;
}
.boot-overlay.boot-hidden { opacity: 0; pointer-events: none; }
.boot-core { display: flex; flex-direction: column; align-items: center; gap: 16px; }
.boot-ring {
  width: 54px; height: 54px; border-radius: 50%;
  border: 3px solid rgba(120,200,255,.18);
  border-top-color: #6cc6ff; border-right-color: #9a7bff;
  animation: bootSpin .8s linear infinite;
  box-shadow: 0 0 24px rgba(90,170,255,.35);
}
@keyframes bootSpin { to { transform: rotate(360deg); } }
.boot-title {
  font: 900 22px/1 system-ui, sans-serif; letter-spacing: .18em;
  background: linear-gradient(90deg, #8fd2ff, #c9b3ff); -webkit-background-clip: text;
  background-clip: text; color: transparent; text-shadow: 0 0 30px rgba(120,180,255,.25);
}
.boot-sub { font: 600 12.5px/1 system-ui, sans-serif; letter-spacing: .06em; color: rgba(200,215,240,.6); }
@media (prefers-reduced-motion: reduce) { .boot-ring { animation: none; } }
/* Crafting veil — same loader, witch's-lair blue/teal so it matches the
   apothecary background it's covering. */
.craft-loader {
  z-index: 2147483400;
  background: radial-gradient(120% 120% at 50% 116%, #06231f 0%, #061224 55%, #04060f 100%);
}
.craft-loader .boot-title { background: linear-gradient(90deg, #58e6cf, #6aa0ff); -webkit-background-clip: text; background-clip: text; }
.craft-loader .boot-ring { border-top-color: #58e6cf; border-right-color: #6aa0ff; box-shadow: 0 0 24px rgba(60,230,200,.35); }
@media (prefers-reduced-motion: reduce) {
  .floor-card.tier-rare::before, .sc-ticker-inner.tier-rare::before { animation: none; }
}

/* ── Admin PSA failure debug panel (request sent + response received) ──────── */
.psa-debug-panel {
  margin: 8px 12px; padding: 12px 14px;
  background: rgba(255,80,80,.06); border: 1px solid rgba(255,120,120,.4);
  border-radius: 10px; font-family: var(--font-mono); font-size: 11.5px; color: #e7ecf5;
}
.pdbg-head { display:flex; align-items:center; justify-content:space-between; font-weight:800; color:#ff9b9b; margin-bottom:6px; }
.pdbg-x { cursor:pointer; background:transparent; border:none; color:#ff9b9b; font-size:14px; line-height:1; }
.pdbg-err { color:#ffc2c2; margin-bottom:10px; }
.pdbg-cols { display:grid; grid-template-columns:1fr 1fr; gap:14px; }
.pdbg-cols > div > div { word-break: break-all; margin: 2px 0; }
.pdbg-col-title { font-weight:800; color:var(--gold); letter-spacing:.06em; text-transform:uppercase; font-size:10px; margin-bottom:4px; }
.pdbg-k { color:#7fb2ff; font-weight:700; margin-right:4px; }
.pdbg-pre {
  white-space: pre-wrap; word-break: break-word; max-height: 220px; overflow:auto;
  background: rgba(0,0,0,.35); border:1px solid rgba(255,255,255,.1); border-radius:6px;
  padding: 8px; margin: 4px 0 0; font-size: 11px; color:#cdd6e2;
}
@media (max-width: 640px) { .pdbg-cols { grid-template-columns: 1fr; } }

/* ════════════════════════════════════════════════════════════════════════════
   FLASHY PACHINKO / CASINO DRESSING (idle attract only — hidden during a rip)
   ════════════════════════════════════════════════════════════════════════════ */
/* ── Casino "searchlight" beams crossing in the DEEP BACKGROUND. #stageFx spans
   the WHOLE rip view (game + chase + graphs) behind all content (z -1 inside
   #ripView's isolated stacking context), so the casino lighting is ONE continuous
   background with no visible seam between the sections. TWO counter-rotating conic
   fans (::before sweeps one way, ::after the other) cross and scissor like real
   searchlights; an ambient gold/violet glow sits under them. Cards always on top. */
/* ── UNIFIED ATMOSPHERE ───────────────────────────────────────────────────────
   Both the rip stage (#stageFx) and the craft stage (#craftFx) share ONE system:
   the same fixed full-viewport scene, the same layer set (floor glow, drifting
   fog, counter-rotating searchlight beams, anime concentration rays, 4-point
   sparkle stars, rising particles) and the same animations. Only the COLOUR
   varies — driven by per-scene custom properties (--fx-a/b/c). RIP runs hot red
   casino energy; CRAFT runs cool blue/teal apothecary. Same approach in both. */
.fx-scene {
  /* FIXED, full-viewport background at BODY level (not inside a view) so NO
     ancestor's overflow:hidden can clip it on mobile during rip/victory. */
  display: none;
  position: fixed; inset: 0;
  z-index: -1;
  pointer-events: none; overflow: hidden;
  transition: opacity .3s ease;
  /* Punchy neon saturation/contrast (without a white wash). */
  filter: saturate(1.75) contrast(1.07) brightness(1.12);
}
/* Clear the CENTRE where the conic beams + rays converge so they never
   screen-blend into a bright white core on small viewports — applied to the
   beam/ray layers only (NOT the scene base, so the page backdrop stays whole). */
.fx-scene::before, .fx-scene::after, .sfx-rays {
  -webkit-mask: radial-gradient(circle at 50% 48%, transparent 0, transparent 13vmin, #000 34vmin);
          mask: radial-gradient(circle at 50% 48%, transparent 0, transparent 13vmin, #000 34vmin);
}
/* RIP theme — crimson / ember / amber. */
.stage-fx {
  --fx-a: 255,55,40;     /* primary hot */
  --fx-b: 255,110,40;    /* secondary */
  --fx-c: 255,180,110;   /* accent */
  /* Same horizon-glow structure as the root casino bg, themed hot: a hot floor
     glow, two upper accents, a soft centre wash, over a near-black ember base. */
  background:
    radial-gradient(ellipse 130% 56% at 50% 102%, rgba(var(--fx-a),0.20), transparent 60%),
    radial-gradient(circle at 14% -6%, rgba(var(--fx-b),0.12), transparent 55%),
    radial-gradient(circle at 86% 4%, rgba(var(--fx-c),0.12), transparent 55%),
    radial-gradient(ellipse 75% 60% at 50% 38%, rgba(var(--fx-a),0.08), transparent 74%),
    linear-gradient(180deg, #07040a 0%, #0e050b 50%, #170810 100%);
}
/* CRAFT theme — teal / blue / violet apothecary (same structure, cool). */
.craft-fx {
  --fx-a: 60,230,200;
  --fx-b: 70,140,255;
  --fx-c: 150,110,255;
  background:
    radial-gradient(ellipse 130% 56% at 50% 102%, rgba(var(--fx-a),0.18), transparent 60%),
    radial-gradient(circle at 14% -6%, rgba(var(--fx-b),0.14), transparent 55%),
    radial-gradient(circle at 86% -4%, rgba(var(--fx-c),0.22), transparent 55%),
    radial-gradient(ellipse 75% 60% at 50% 38%, rgba(var(--fx-a),0.07), transparent 74%),
    linear-gradient(180deg, #04060f 0%, #050a18 55%, #04101a 100%);
}
body.rip-active .stage-fx { display: block; }
body.gallery-active .craft-fx { display: block; }
/* A CRAFT reveal (the rip/pull animation + victory screen reached by crafting)
   plays on the rip view, but should keep the CRAFT water atmosphere — not the rip
   page's fire. Swap the scenes while body.craft-reveal is set. */
body.craft-reveal .stage-fx { display: none !important; }
body.craft-reveal .craft-fx { display: block !important; }
/* The searchlight fans are VIEWPORT-SIZED (inset:0) and we spin the conic
   gradient's ANGLE via an @property custom property instead of rotating a giant
   oversized element. iOS Safari caps composited layer size and crops an oversized
   element down to a tile SMALLER than the screen — so its cut edges became visible
   sweeping around (the "cropped rotating rays" on iPhone). Animating the gradient
   angle inside a fixed, screen-sized box can never expose an edge and renders
   identically on iPhone and Android. */
.fx-scene::before, .fx-scene::after {
  content: '';
  position: absolute; left: 50%; top: 50%;
  /* 130vmax: past the viewport diagonal (a rotating square never shows an edge)
     yet under iOS Safari's composited-layer cap. Rotation runs on the compositor
     thread, so the lights spin smoothly without stealing the wheel's paint time. */
  width: 130vmax; height: 130vmax;
  transform: translate(-50%, -50%);
  pointer-events: none;
  mix-blend-mode: screen;
  will-change: transform, opacity;
}
/* Searchlight fan A — themed by --fx-a/b/c on the scene. */
.fx-scene::before {
  background: conic-gradient(from 0deg,
    rgba(var(--fx-a),.5) 0deg, rgba(var(--fx-c),.10) 8deg, transparent 22deg, transparent 86deg,
    rgba(var(--fx-b),.5) 95deg, rgba(var(--fx-c),.10) 103deg, transparent 118deg, transparent 176deg,
    rgba(var(--fx-a),.5) 185deg, rgba(var(--fx-c),.10) 193deg, transparent 208deg, transparent 266deg,
    rgba(var(--fx-b),.5) 275deg, rgba(var(--fx-c),.10) 283deg, transparent 298deg, transparent 360deg);
  animation: beamSpinCW 6s linear infinite, beamPulse 2.4s ease-in-out infinite;
}
/* Searchlight fan B — counter-rotating, offset phase. */
.fx-scene::after {
  background: conic-gradient(from 45deg,
    transparent 0deg, transparent 58deg,
    rgba(var(--fx-a),.46) 70deg, rgba(var(--fx-c),.08) 79deg, transparent 96deg, transparent 156deg,
    rgba(var(--fx-b),.44) 166deg, rgba(var(--fx-c),.08) 175deg, transparent 192deg, transparent 252deg,
    rgba(var(--fx-a),.48) 262deg, rgba(var(--fx-c),.08) 271deg, transparent 288deg, transparent 360deg);
  animation: beamSpinCCW 8.5s linear infinite, beamPulse 2.4s ease-in-out .9s infinite;
}
@keyframes beamSpinCW  { to { transform: translate(-50%, -50%) rotate(360deg); } }
@keyframes beamSpinCCW { to { transform: translate(-50%, -50%) rotate(-360deg); } }
@keyframes beamPulse { 0%,100% { opacity: .72; } 50% { opacity: 1; } }
/* Hard, strobing flash for the feverish "party" state. */
@keyframes beamFever { 0%, 100% { opacity: .35; } 45% { opacity: 1; } 70% { opacity: .6; } }

/* ── Extra layers (all behind the cards, additive/screen) ──── */
.fx-scene > div { position: absolute; pointer-events: none; mix-blend-mode: screen; }

/* Rotating sunburst — bold anime "concentration lines" radiating from centre.
   Coloured from the scene's --fx-a/b so it themes per page automatically. */
.sfx-rays {
  left: 50%; top: 50%; width: 130vmax; height: 130vmax; transform: translate(-50%, -50%);
  background: repeating-conic-gradient(from 0deg,
    rgba(var(--fx-a),.22) 0deg 3deg, transparent 3deg 12deg);
  opacity: .92;
  animation: sfxRaysSpin 26s linear infinite;
}
.sfx-rays2 {
  width: 130vmax; height: 130vmax;
  background: repeating-conic-gradient(from 2deg,
    rgba(var(--fx-b),.18) 0deg 1.8deg, transparent 1.8deg 9deg);
  opacity: .78;
  animation: sfxRaysSpinR 38s linear infinite;
}
@keyframes sfxRaysSpin  { to { transform: translate(-50%, -50%) rotate(360deg); } }
@keyframes sfxRaysSpinR { from { transform: translate(-50%, -50%) rotate(0); } to { transform: translate(-50%, -50%) rotate(-360deg); } }

/* ── RIP SCENE = RED RISING LIGHT SHAFTS ──────────────────────────────────────
   The EXACT inverse of the craft water shafts: RED instead of blue, and rising
   from the FLOOR upward (mask brightest at the bottom, fading to the top) instead
   of cascading down from the surface — and swaying FASTER. The old flame wall is
   retired. */
.sfx-fire, #ripFire { display: none !important; }            /* retire the flame wall */
.stage-fx .sfx-rays { display: none !important; }            /* no central sunburst */
.stage-fx::before, .stage-fx::after {
  display: block !important;
  left: 0 !important; top: 0 !important; width: 100% !important; height: 110% !important;
  transform: none; will-change: transform; mix-blend-mode: screen;
  /* Brightest at the FLOOR (bottom), gone toward the top — light rising from below. */
  -webkit-mask: linear-gradient(0deg, #000 0%, rgba(0,0,0,.7) 38%, transparent 84%);
          mask: linear-gradient(0deg, #000 0%, rgba(0,0,0,.7) 38%, transparent 84%);
}
.stage-fx::before {
  background: repeating-linear-gradient(98deg,
    transparent 0 4.5%, rgba(255,120,60,.13) 5.2% 6.6%, rgba(255,60,40,.06) 7.2% 8.6%, transparent 9.4% 15%);
  animation: riseShaft1 5s ease-in-out infinite alternate;
}
.stage-fx::after {
  background: repeating-linear-gradient(84deg,
    transparent 0 6%, rgba(255,90,50,.1) 6.8% 8.4%, transparent 9.2% 17%);
  animation: riseShaft2 7s ease-in-out infinite alternate;
}
/* Sway like the craft shafts but FASTER, and with an upward drift so the light
   reads as rising from the floor (vs the craft shafts' downward feel). */
@keyframes riseShaft1 { 0% { transform: translate(-4%, 5%) skewX(-3.5deg); } 100% { transform: translate(4%, -5%) skewX(3.5deg); } }
@keyframes riseShaft2 { 0% { transform: translate(3%, 4%)  skewX(2.5deg); } 100% { transform: translate(-5%, -4%) skewX(-2.5deg); } }
/* The bottom glow stays as the hot floor the red light rises from. */
.stage-fx .cfx-glow { bottom: -12vh; height: 64vh; filter: blur(36px); animation: cfxPulse 2.4s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .stage-fx::before, .stage-fx::after { animation: none !important; } }

/* ════════════════════════════════════════════════════════════════════════════
   SLEEK RE-THEME  ·  piano-lacquer pages
   Retire the arcade decorations (marquee bulbs, spinning searchlights, rising
   light shafts, heavy particles, neon over-saturation) for a clean, premium
   look — a deep near-black lacquer base with ONE soft themed glow: sleek RED for
   ripping, cool BLUE for crafting (and the gallery). Same polished vibe as the
   piano-key buttons. This block is late in the file so it overrides the old FX.
   ════════════════════════════════════════════════════════════════════════════ */
html {
  background-color: #05060a;
  background-image:
    radial-gradient(120% 70% at 50% -12%, rgba(255,255,255,.05), transparent 58%), /* soft top sheen */
    linear-gradient(180deg, #0a0b11 0%, #06070c 58%, #030307 100%);                /* lacquer vignette */
}
/* Kill the marquee edge bulbs + every spinning/rising/ray/particle FX layer. */
.casino-lights { display: none !important; }
.fx-scene::before, .fx-scene::after,
.stage-fx::before, .stage-fx::after,
.craft-fx::before, .craft-fx::after,
.sfx-rays, .sfx-rays2, .sfx-fire, #ripFire,
.stage-fx .cfx-glow, .craft-fx .cfx-glow, .fx-scene > div { display: none !important; }
.particle-field { opacity: .18 !important; }
/* Drop the neon over-saturation — a sleek surface, not a nightclub. */
.fx-scene { filter: saturate(1.05) brightness(1.02) !important; }
/* The themed glow: a single clean wash (floor glow + top glow + vignette). */
.stage-fx {   /* RIP — EVA neon GREEN */
  background:
    radial-gradient(135% 85% at 50% 116%, rgba(48,235,120,.42), transparent 60%),   /* toxic floor */
    radial-gradient(100% 64% at 50% -14%, rgba(30,200,110,.30), transparent 58%),    /* top wash */
    radial-gradient(120% 120% at 50% 50%, transparent 40%, rgba(4,44,24,.5) 100%),   /* colour vignette */
    linear-gradient(180deg, rgba(6,26,16,.55) 0%, rgba(4,16,11,.32) 48%, rgba(5,22,14,.72) 100%) !important;
}
.craft-fx {   /* CRAFT / gallery — EVA neon PURPLE */
  background:
    radial-gradient(135% 85% at 50% 116%, rgba(158,74,240,.44), transparent 60%),
    radial-gradient(100% 64% at 50% -14%, rgba(122,44,200,.32), transparent 58%),
    radial-gradient(120% 120% at 50% 50%, transparent 40%, rgba(30,8,54,.5) 100%),
    linear-gradient(180deg, rgba(22,8,40,.55) 0%, rgba(13,5,26,.32) 48%, rgba(18,7,34,.72) 100%) !important;
}

/* ── CRAFT SCENE = UNDERWATER LIGHT SHAFTS ────────────────────────────────────
   Instead of beams spinning in the middle, sunlight cascades DOWN from the
   surface — angled god-rays that fade as they sink into the deep, swaying gently
   like light filtering through deep water. */
.craft-fx .sfx-rays { display: none !important; }            /* no central sunburst */
.craft-fx::before, .craft-fx::after {
  left: 0 !important; top: 0 !important; width: 100% !important; height: 110% !important;
  transform: none; will-change: transform; mix-blend-mode: screen;
  /* Brightest at the surface (top), gone in the depths (bottom). */
  -webkit-mask: linear-gradient(180deg, #000 0%, rgba(0,0,0,.7) 38%, transparent 84%);
          mask: linear-gradient(180deg, #000 0%, rgba(0,0,0,.7) 38%, transparent 84%);
}
.craft-fx::before {
  background: repeating-linear-gradient(98deg,
    transparent 0 4.5%, rgba(226,196,255,.11) 5.2% 6.6%, rgba(180,120,255,.05) 7.2% 8.6%, transparent 9.4% 15%);
  animation: shaftSway1 12s ease-in-out infinite alternate;
}
.craft-fx::after {
  background: repeating-linear-gradient(84deg,
    transparent 0 6%, rgba(206,160,255,.08) 6.8% 8.4%, transparent 9.2% 17%);
  animation: shaftSway2 16s ease-in-out infinite alternate;
}
@keyframes shaftSway1 { 0% { transform: translateX(-4%) skewX(-3.5deg); } 100% { transform: translateX(4%) skewX(3.5deg); } }
@keyframes shaftSway2 { 0% { transform: translateX(3%)  skewX(2.5deg); } 100% { transform: translateX(-5%) skewX(-2.5deg); } }
@media (prefers-reduced-motion: reduce) { .craft-fx::before, .craft-fx::after { animation: none !important; } }

/* ── CRAFTING-MODE atmosphere — spooky blue witch's apothecary ─────────────────
   A fixed, full-viewport backdrop shown only on the crafting page. Deep blue/teal
   gloom, a pulsing cauldron glow, drifting fog and rising potion bubbles. */
/* Floor glow rising from the bottom (RIP = ember bed, CRAFT = cauldron) —
   full-width, breathes slowly. Themed from the scene's --fx-a/b. */
.cfx-glow {
  position: absolute; left: 0; right: 0; bottom: -18vh; height: 78vh;
  filter: blur(42px);
  background: radial-gradient(ellipse 92% 70% at 50% 100%, rgba(var(--fx-a),.32), rgba(var(--fx-b),.15) 50%, transparent 78%);
  animation: cfxPulse 5.5s ease-in-out infinite;
}
@keyframes cfxPulse { 0%,100% { opacity: .55; transform: scaleY(1); } 50% { opacity: .95; transform: scaleY(1.05); } }
/* Drifting haze — large blobs that reach the screen edges. Themed per scene. */
.cfx-fog { position: absolute; width: 90vw; height: 90vw; border-radius: 50%; filter: blur(66px); mix-blend-mode: screen; opacity: .42; }
.cfx-fog1 { top: -22%; left: -24%; background: radial-gradient(circle, rgba(var(--fx-b),.5), transparent 62%); animation: cfxDrift1 28s ease-in-out infinite; }
.cfx-fog2 { bottom: -26%; right: -24%; background: radial-gradient(circle, rgba(var(--fx-c),.45), transparent 62%); animation: cfxDrift2 34s ease-in-out infinite; }
@keyframes cfxDrift1 { 0%,100% { transform: translate(0,0); } 50% { transform: translate(18vw,10vh); } }
@keyframes cfxDrift2 { 0%,100% { transform: translate(0,0); } 50% { transform: translate(-16vw,-8vh); } }
/* Rising particles (RIP = embers, CRAFT = potion bubbles) — generated in JS with
   per-particle --dur/--delay/--drift. Colour comes from the scene's --fx-a/b. */
.cfx-bubbles { position: absolute; inset: 0; }
.cfx-bubble {
  position: absolute; bottom: -24px; border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, rgba(255,255,255,.9), rgba(var(--fx-a),.5) 60%, rgba(var(--fx-b),.12) 100%);
  box-shadow: 0 0 9px rgba(var(--fx-a),.55);
  opacity: 0; animation: cfxRise var(--dur, 9s) linear infinite; animation-delay: var(--delay, 0s);
  will-change: transform, opacity;
}
@keyframes cfxRise {
  0%   { transform: translate(0,0) scale(.55); opacity: 0; }
  12%  { opacity: .9; }
  82%  { opacity: .8; }
  100% { transform: translate(var(--drift, 0px), -104vh) scale(1); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .cfx-glow, .cfx-fog, .cfx-bubble { animation: none !important; }
}

/* ── CASINO MARQUEE — chasing neon bulbs framing ALL FOUR edges of the screen,
   like a slot-machine / pachinko cabinet. Rows of alternating cyan/magenta bulbs
   that march around the frame (theatre-marquee chase). On every device (desktop +
   mobile), shown only on the rip view. ── */
/* Shown on BOTH pages now (same frame of marching bulbs), themed via --bulb1/2:
   RIP = warm gold + ember, CRAFT = teal + blue. */
.casino-lights { display: none; }
body.rip-active .casino-lights,
body.gallery-active .casino-lights {
  display: block;
  position: fixed; z-index: 6; pointer-events: none;
  filter: drop-shadow(0 0 4px rgba(var(--bulb-glow, 255,209,92), .7));
  opacity: .72;
}
body.rip-active    .casino-lights { --bulb1: #ffd15c; --bulb2: #ff5a3c; --bulb-glow: 255,209,92; }
body.gallery-active .casino-lights { --bulb1: #58e6cf; --bulb2: #6aa0ff; --bulb-glow: 88,230,207; }
/* Craft reveal keeps the water palette on the edge marquee too. */
body.craft-reveal .casino-lights { --bulb1: #58e6cf; --bulb2: #6aa0ff; --bulb-glow: 88,230,207; }
/* Top + bottom: a row of round bulbs, widely spaced, drifting slowly. */
body.rip-active .casino-lights.cl-top,    body.gallery-active .casino-lights.cl-top,
body.rip-active .casino-lights.cl-bot,    body.gallery-active .casino-lights.cl-bot {
  left: 0; right: 0; height: 16px;
  background-image:
    radial-gradient(circle 3px at 14px 8px, var(--bulb1) 0 3px, transparent 4px),
    radial-gradient(circle 3px at 42px 8px, var(--bulb2) 0 3px, transparent 4px);
  background-size: 56px 16px;
  background-repeat: repeat-x;
  animation: marqueeChaseX 4.5s linear infinite, marqueeTwinkle 2.8s ease-in-out infinite;
}
.casino-lights.cl-top { top: 0; }
.casino-lights.cl-bot { bottom: 0; }
/* Left + right: vertical bulb columns, same calm drift. */
body.rip-active .casino-lights.cl-left,   body.gallery-active .casino-lights.cl-left,
body.rip-active .casino-lights.cl-right,  body.gallery-active .casino-lights.cl-right {
  top: 0; bottom: 0; width: 16px;
  background-image:
    radial-gradient(circle 3px at 8px 14px, var(--bulb1) 0 3px, transparent 4px),
    radial-gradient(circle 3px at 8px 42px, var(--bulb2) 0 3px, transparent 4px);
  background-size: 16px 56px;
  background-repeat: repeat-y;
  animation: marqueeChaseY 4.5s linear infinite, marqueeTwinkle 2.8s ease-in-out .9s infinite;
}
.casino-lights.cl-left  { left: 0; }
.casino-lights.cl-right { right: 0; }
/* Slow drift + a gentle brightness twinkle instead of a fast strobe march. */
@keyframes marqueeChaseX { to { background-position: -56px 0, -56px 0; } }
@keyframes marqueeChaseY { to { background-position: 0 -56px, 0 -56px; } }
@keyframes marqueeTwinkle { 0%, 100% { opacity: .55; } 50% { opacity: .85; } }
@media (prefers-reduced-motion: reduce) { .casino-lights { animation: none !important; } }

/* ── COMMUNITY JACKPOT — an arcade / pinball LED scoreboard at the top of the
   stage. A chrome-gold cabinet houses a dark recessed LED screen with glowing
   seven-segment-style digits (each over an unlit "8" ghost). Every pull feeds it;
   a Foil Chase wins the whole thing. ── */
/* The jackpot doubles as the divider between the chase cards and the carousel:
   a full-width band with glowing rule-lines that meet at the centred LED
   scoreboard. */
.jackpot-divider {
  width: 100%; max-width: 720px; display: flex; align-items: center; justify-content: center; gap: 16px;
  /* Centre the jackpot in the gap between the chase cards (above) and the carousel
     (below): more top margin pushes it down, the trimmed chase-section padding
     (below) pulls the carousel up, so the two gaps match. */
  margin: 22px auto 4px; padding: 0 18px; pointer-events: none;
}
.jackpot-divider::before, .jackpot-divider::after {
  content: ''; flex: 1 1 0; height: 2px; max-width: 38%; border-radius: 2px;
  background: linear-gradient(90deg, transparent, rgba(255,180,70,.55) 70%, rgba(255,210,120,.85));
  box-shadow: 0 0 8px rgba(255,150,50,.5);
}
.jackpot-divider::after { background: linear-gradient(270deg, transparent, rgba(255,180,70,.55) 70%, rgba(255,210,120,.85)); }
/* During the card reveal, HIDE it so it can't steal height from the fullscreen
   reveal (in flow it shrank the 100vh hero stage and cropped the card). */
body.rip-reveal .jackpot-divider { display: none; }
/* During the spin, OVERLAY it (fixed) at the chase/wheel seam so it stays visible
   without consuming the 38/62 split height. */
body.spin-cine:not(.rip-reveal) .jackpot-divider {
  position: fixed; top: 38vh; left: 50%; transform: translate(-50%, -50%);
  margin: 0; padding: 0; z-index: 50; max-width: 92vw;
}
body.spin-cine:not(.rip-reveal) .jackpot-divider::before,
body.spin-cine:not(.rip-reveal) .jackpot-divider::after { display: none; }

/* The chase cards sit in their row above the (normal-size) jackpot. */
.jackpot-showcase { position: relative; width: 100%; }
/* Trim the chase-section's bottom padding so the carousel below isn't pushed an
   extra notch away from the jackpot (keeps the jackpot centred between them). */
#ripView .chase-section { padding-bottom: 3px; }

/* Blue-mana WATER GUSH when the jackpot is won — a full-screen blue wash, plus
   gooey streams that pour down the whole screen. The SVG #manaGoo filter on
   .mana-liquid fuses the falling drops into continuous, smooth bodies of water
   (metaballs). Spawned in JS (spawnManaBurst). */
.mana-burst { position: fixed; inset: 0; z-index: 11400; pointer-events: none; overflow: hidden; }
.mana-goo-svg { position: absolute; width: 0; height: 0; }
.mana-flash {
  position: absolute; inset: 0;
  background: radial-gradient(circle at 50% 34%, rgba(150,225,255,.5), rgba(60,140,255,.32) 45%, rgba(30,70,200,.2) 72%, transparent 92%);
}
/* The fused liquid layer — the goo filter melts adjacent drops together. */
.mana-liquid { position: absolute; inset: 0; filter: url(#manaGoo); }
.mana-blob {
  position: absolute; top: 0; border-radius: 50%; will-change: transform;
  /* Solid-to-the-edge water gradient so the goo threshold links neighbours; the
     light→deep blue shading reads as glassy water once blurred together. */
  background: radial-gradient(circle at 42% 32%, #e2f7ff 0%, #74bdff 44%, #2f7cf2 78%, #1b56d8 100%);
}
/* The "waterfall into your balance" stream uses its own (tighter) goo filter. */
.mana-liquid2 { filter: url(#manaGoo2); }
@media (prefers-reduced-motion: reduce) { .mana-burst { display: none; } }

/* The mana wallet glows + the count pulses while the won mana pours into it. */
/* mana-filling now sits ON the CRAFT button itself (which is .pb-nav-craft), so
   target it directly — and !important to beat .pb-nav-btn's animation:none. */
.pb-nav-craft.mana-filling, .mana-filling .pb-nav-craft { animation: manaFill 1.7s ease-out !important; }
@keyframes manaFill {
  0%, 100% { text-shadow: none; transform: scale(1); }
  22%      { text-shadow: 0 0 10px #8fcbff, 0 0 22px #4aa0ff; transform: scale(1.16); }
  60%      { text-shadow: 0 0 8px #8fcbff; transform: scale(1.04); }
}

/* ── TIP THE DEALER/HOST ──────────────────────────────────────────────────────
   A glowing tip jar by the mascot; tap to open the amount picker. */
.tip-host { position: fixed; left: 14px; bottom: 14px; z-index: 6200; }
@media (max-width: 640px) { .tip-host { left: 10px; bottom: 10px; } }
/* Out of the way during the fullscreen spin/reveal. */
body.spin-cine .tip-host, body.rip-reveal .tip-host { display: none !important; }
.tip-btn {
  position: relative; display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  padding: 7px 13px 7px 11px; border-radius: 999px;
  font: 800 13px/1 system-ui, sans-serif; color: #2a1a00;
  background: linear-gradient(180deg, #ffe9a6, #f3b63c 60%, #e0992a);
  border: 1.5px solid #fff3c4;
  box-shadow: 0 6px 18px rgba(0,0,0,.5), 0 0 18px rgba(255,200,80,.5), inset 0 1px 0 rgba(255,255,255,.6);
  -webkit-tap-highlight-color: transparent;
  animation: tipBreathe 3.2s ease-in-out infinite;
}
.tip-btn:hover { filter: brightness(1.06); transform: translateY(-1px); }
.tip-btn:active { transform: translateY(1px); }
.tip-btn .tip-jar { font-size: 16px; filter: drop-shadow(0 1px 1px rgba(0,0,0,.3)); }
.tip-btn .tip-coin { width: 15px; height: 15px; margin-left: -2px; }
.tip-btn .tip-label { letter-spacing: .03em; }
@keyframes tipBreathe {
  0%, 100% { box-shadow: 0 6px 18px rgba(0,0,0,.5), 0 0 14px rgba(255,200,80,.4), inset 0 1px 0 rgba(255,255,255,.6); }
  50%      { box-shadow: 0 6px 18px rgba(0,0,0,.5), 0 0 28px rgba(255,216,110,.85), inset 0 1px 0 rgba(255,255,255,.6); }
}
/* When she reminds players to tip, the jar jumps to draw the eye. */
.tip-host.tip-nudge .tip-btn { animation: tipNudge .7s cubic-bezier(.2,.9,.3,1.5) 2; }
@keyframes tipNudge { 0%,100% { transform: translateY(0) rotate(0); } 30% { transform: translateY(-7px) rotate(-6deg); } 60% { transform: translateY(0) rotate(5deg); } }
/* Amount picker — pops up above the jar. */
.tip-menu {
  position: absolute; left: 0; bottom: calc(100% + 9px);
  width: max-content; min-width: 184px; padding: 11px 12px 10px;
  border-radius: 14px; transform-origin: bottom left;
  background: linear-gradient(180deg, rgba(20,14,30,.97), rgba(10,8,18,.97));
  border: 1px solid rgba(255,205,80,.55);
  box-shadow: 0 16px 40px rgba(0,0,0,.6), 0 0 26px rgba(255,200,80,.25);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  opacity: 0; transform: translateY(8px) scale(.96); pointer-events: none;
  transition: opacity .16s ease, transform .16s cubic-bezier(.2,.9,.3,1.3);
}
.tip-host.tip-open .tip-menu { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
.tip-menu-head {
  font: 800 12px/1.1 system-ui, sans-serif; color: #ffe08a; text-align: center; margin-bottom: 9px;
  display: flex; align-items: center; justify-content: center; gap: 5px;
}
.tip-menu-head .crystal-ico { width: 15px; height: 15px; }
.tip-amts { display: grid; grid-template-columns: repeat(4, 1fr); gap: 7px; }
.tip-amt {
  cursor: pointer; padding: 8px 4px; border-radius: 9px;
  font: 800 14px/1 system-ui, sans-serif; color: #eafaff;
  background: radial-gradient(120% 120% at 50% 0%, rgba(90,170,255,.28), rgba(12,18,30,.92));
  border: 1px solid rgba(120,200,255,.5);
  transition: transform .1s ease, box-shadow .12s ease, background .12s ease;
}
.tip-amt:hover { transform: translateY(-2px); box-shadow: 0 6px 16px rgba(0,0,0,.5), 0 0 16px rgba(90,170,255,.6); }
.tip-amt:active { transform: translateY(0); }
.tip-amt-max { color: #2a1a00; background: linear-gradient(180deg, #bfeaff, #6fbcff); border-color: #d6f4ff; }
.tip-bal { margin-top: 9px; text-align: center; font: 700 11px/1 system-ui, sans-serif; color: #9fb0c3; }
.tip-bal .crystal-ico { width: 12px; height: 12px; vertical-align: -2px; }
.tip-bal b { color: #cfe6ff; }
@media (prefers-reduced-motion: reduce) { .tip-btn, .tip-host.tip-nudge .tip-btn { animation: none; } }
/* Mana coins flying from the jar into the mascot when you tip. */
.tip-coin-fly {
  position: fixed; left: 0; top: 0; z-index: 6300; pointer-events: none;
  font-size: 20px; color: #7fc4ff; will-change: transform, opacity;
  text-shadow: 0 0 10px rgba(90,180,255,.95), 0 0 18px rgba(70,150,255,.7);
}

/* Arcade LED scoreboard — the centrepiece of the divider. */
.jackpot-meter {
  position: relative; pointer-events: none;
  display: inline-flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  padding: 6px 16px 8px; border-radius: 12px;
  /* Brushed-gold cabinet bezel with a 3D rim. */
  background: linear-gradient(180deg, #5a4516 0%, #2b1f08 48%, #4a3712 100%);
  border: 1px solid rgba(255,225,150,.7);
  box-shadow:
    0 6px 16px rgba(0,0,0,.5),
    inset 0 1px 0 rgba(255,240,190,.5), inset 0 -2px 4px rgba(0,0,0,.55),
    0 0 0 1px rgba(0,0,0,.5), 0 0 16px rgba(255,170,50,.3);
  /* Default = a calm, normal-size scoreboard. It only blows up on a WIN (jm-won). */
  transition: transform .45s cubic-bezier(.2,1.4,.3,1), box-shadow .3s ease, opacity .3s ease;
  transform-origin: center;
}
/* A pot climb flashes the cabinet brighter (no resize). */
.jackpot-meter.jm-bump { animation: jackpotBumpFlash .55s ease; }
@keyframes jackpotBumpFlash { 0%, 100% { filter: none; } 40% { filter: brightness(1.45) saturate(1.2); } }
/* Marquee header with two blinking cabinet bulbs. */
.jm-label {
  display: inline-flex; align-items: center; gap: 8px;
  font: 900 9px/1 var(--font-display, system-ui), sans-serif; letter-spacing: .3em; text-indent: .3em;
  background: linear-gradient(90deg, #ffd15c, #fff3c4 50%, #ffd15c);
  -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 1px 0 rgba(0,0,0,.5));
}
.jm-bulb {
  width: 7px; height: 7px; border-radius: 50%; text-indent: 0; flex: 0 0 auto;
  background: radial-gradient(circle at 35% 30%, #fff7d6, #ffb52e 60%, #b9670f 100%);
  box-shadow: 0 0 7px rgba(255,190,70,.95), 0 0 13px rgba(255,150,40,.7);
  animation: jmBulb 1s steps(1) infinite;
}
.jm-label .jm-bulb:last-child { animation-delay: .5s; }
@keyframes jmBulb { 0%,49% { opacity: 1; } 50%,100% { opacity: .25; } }
/* Recessed dark LED screen. */
.jm-board {
  position: relative; display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 12px 3px 10px; border-radius: 7px;
  background: linear-gradient(180deg, #160604, #240a06 50%, #120402);
  border: 1px solid rgba(0,0,0,.85);
  box-shadow: inset 0 2px 7px rgba(0,0,0,.9), inset 0 0 14px rgba(255,90,30,.18), 0 0 14px rgba(255,150,50,.3);
  overflow: hidden;
}
/* CRT scanlines across the screen. */
.jm-board::after {
  content: ''; position: absolute; inset: 0; pointer-events: none; border-radius: 7px;
  background: repeating-linear-gradient(0deg, rgba(0,0,0,.28) 0 1px, transparent 1px 3px);
  mix-blend-mode: multiply;
}
/* Mana potion glyph on the LED screen. */
.jm-cr { font-size: 32px; line-height: 1; filter: drop-shadow(0 0 8px rgba(120,230,200,.7)); }
/* Live "online" pulse dot on the LED screen (replaces the glyph on the USERS ONLINE
   cabinet) — a green heartbeat so the board reads as a live readout. */
.jm-online-dot {
  width: 13px; height: 13px; border-radius: 50%; background: #35ff90; align-self: center;
  box-shadow: 0 0 7px #35ff90, 0 0 15px rgba(53,255,144,.7), inset 0 0 3px rgba(255,255,255,.5);
  animation: jmOnlinePulse 2s ease-in-out infinite;
}
@keyframes jmOnlinePulse { 0%,100% { opacity: 1; transform: scale(1); } 50% { opacity: .5; transform: scale(.72); } }
@media (max-width: 640px) { .jm-online-dot { width: 10px; height: 10px; } }
@media (prefers-reduced-motion: reduce) { .jm-online-dot { animation: none; } }
/* The digit reels. */
.jm-digits {
  display: inline-flex; align-items: baseline; gap: 1px;
  font: 900 24px/1 "DS-Digital", "Courier New", ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
}
/* Each seven-segment cell: a lit numeral over a faint unlit "8" ghost. */
.jm-digit { position: relative; display: inline-block; width: .66em; text-align: center; }
.jm-digit::before {
  content: '8'; position: absolute; inset: 0; z-index: 0;
  color: rgba(255,70,30,.14);   /* unlit segments */
}
.jm-d {
  position: relative; z-index: 1;
  color: #ffc23a;               /* lit segments — amber LED */
  text-shadow: 0 0 5px rgba(255,170,40,.95), 0 0 12px rgba(255,120,20,.8), 0 0 22px rgba(255,90,10,.5);
  animation: jmFlicker 4s steps(1) infinite;
}
.jm-sep { position: relative; z-index: 1; color: #ffb02e; opacity: .9; padding: 0 .02em;
  text-shadow: 0 0 6px rgba(255,150,30,.8); }
@keyframes jmFlicker { 0%,96%,100% { opacity: 1; } 97% { opacity: .82; } 98% { opacity: 1; } 99% { opacity: .9; } }
/* WON — only NOW does it blow up huge: the cabinet inflates and floods with blue
   mana light (the prize is mana). It springs back to normal size when jm-won
   clears (~3.2s later) via the .jackpot-meter transform transition. */
.jackpot-meter.jm-won {
  transform: scale(2); z-index: 40;
  border-color: #cde9ff; box-shadow: 0 0 60px rgba(120,200,255,1), 0 0 110px rgba(70,150,255,.7) !important;
}
@media (max-width: 640px) { .jackpot-meter.jm-won { transform: scale(1.7); } }
.jackpot-meter.jm-won .jm-board { box-shadow: inset 0 0 18px rgba(180,235,255,.65), 0 0 24px rgba(90,175,255,.85); }
.jackpot-meter.jm-won .jm-d { color: #eafaff; text-shadow: 0 0 8px #bfeaff, 0 0 18px #66b6ff, 0 0 30px #2f7aff; }
.jackpot-meter.jm-won .jm-cr { filter: drop-shadow(0 0 12px rgba(120,210,255,1)); }
/* Tuck it away during a spin/reveal so it doesn't clash with the prize. */
@media (max-width: 640px) {
  .jackpot-divider { gap: 10px; padding: 0 10px; margin: 18px 0 4px; }
  .jackpot-divider::before, .jackpot-divider::after { max-width: 32%; }
  .jackpot-meter { padding: 5px 12px 6px; border-radius: 10px; }
  .jm-digits { font-size: 19px; }
  .jm-cr { font-size: 26px; }
  .jm-label { font-size: 8px; letter-spacing: .24em; gap: 6px; }
  .jm-bulb { width: 6px; height: 6px; }
}
@media (prefers-reduced-motion: reduce) {
  .jm-bulb, .jm-d { animation: none !important; }
}

/* The big shared "someone hit the JACKPOT" banner. */
.jackpot-won-banner {
  position: fixed; top: 60px; left: 50%; transform: translate(-50%,0);
  z-index: 11500; pointer-events: none; white-space: nowrap;
  padding: 12px 26px; border-radius: 12px;
  font: 800 18px/1 var(--font-display, system-ui), sans-serif; letter-spacing: .03em;
  color: #2b1c00;
  background: linear-gradient(180deg, #fff3c4, #ffd15c 55%, #e89a26);
  border: 2px solid #fff7d2;
  box-shadow: 0 14px 50px rgba(0,0,0,.5), 0 0 50px rgba(255,209,92,.9);
  text-shadow: 0 1px 0 rgba(255,255,255,.5);
}
.jackpot-won-banner b { color: #1c1200; }
@media (max-width: 640px) { .jackpot-won-banner { font-size: 14px; padding: 10px 18px; top: 54px; } }

/* Big anime 4-point SPARKLE STARS (the concave "✦" sparkle on the spin arrow),
   scattered large and sparse — NOT a field of tiny dots. Two layers (bright white
   + pink) twinkle out of phase like a shoujo-manga shimmer. SVG data-URIs so the
   star points stay crisp at any size. */
.sfx-sparkles {
  inset: 0;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpath d='M50 3 C55 38 62 45 97 50 C62 55 55 62 50 97 C45 62 38 55 3 50 C38 45 45 38 50 3 Z' fill='%2300e5ff'/%3E%3C/svg%3E"),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpath d='M50 8 C54 40 60 46 92 50 C60 54 54 60 50 92 C46 60 40 54 8 50 C40 46 46 40 50 8 Z' fill='%23ff2d9b'/%3E%3C/svg%3E");
  background-repeat: no-repeat, no-repeat;
  background-size: 46px 46px, 30px 30px;
  background-position: 16% 24%, 80% 66%;
  filter: drop-shadow(0 0 8px rgba(0,229,255,.7));
  opacity: .9;
  animation: sfxSparkle 2.6s ease-in-out infinite;
}
/* A second sparkle pair on the opposite phase via the shared .sfx-rays box? No —
   use the ::before/::after of the sparkles layer for two more stars elsewhere. */
.sfx-sparkles::before, .sfx-sparkles::after {
  content: ''; position: absolute; pointer-events: none;
  background-repeat: no-repeat; background-position: center;
  filter: drop-shadow(0 0 9px rgba(255,45,155,.7));
}
.sfx-sparkles::before {
  width: 54px; height: 54px; left: 68%; top: 18%;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpath d='M50 3 C55 38 62 45 97 50 C62 55 55 62 50 97 C45 62 38 55 3 50 C38 45 45 38 50 3 Z' fill='%23ffffff'/%3E%3C/svg%3E");
  background-size: contain;
  animation: sfxSparkle 3.1s ease-in-out .6s infinite;
}
.sfx-sparkles::after {
  width: 34px; height: 34px; left: 26%; top: 76%;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpath d='M50 8 C54 40 60 46 92 50 C60 54 54 60 50 92 C46 60 40 54 8 50 C40 46 46 40 50 8 Z' fill='%2300e5ff'/%3E%3C/svg%3E");
  background-size: contain;
  animation: sfxSparkle 2.3s ease-in-out 1.1s infinite;
}
@keyframes sfxSparkle {
  0%, 100% { opacity: .12; transform: scale(.7) rotate(0deg); }
  50%      { opacity: .8;  transform: scale(1.05) rotate(12deg); }
}

@media (prefers-reduced-motion: reduce) {
  .sfx-rays, .sfx-rays2, .sfx-sparkles,
  .sfx-sparkles::before, .sfx-sparkles::after { animation: none !important; }
  .sfx-sparkles { opacity: .7; }
}
/* Mobile: keep the cheap transform/opacity layers (rays/stars/bokeh) but they
   already avoid per-frame filters, so they stay smooth. */

/* ── FEVERISH "CRAZY PARTY" lighting WHILE A PULL IS HAPPENING ───────────────
   During the spin / rip / reveal the beams don't hide — they go wild: spin much
   faster, strobe, and cycle hue rapidly, with a brightness/saturation boost. The
   beams stay BEHIND the cards (z-index -1) so the prize is still front-and-centre.
   Higher specificity + !important so it also overrides the mobile "static" rule
   (the page is fixed-fullscreen during a pull, so there's no scroll to jank). */
body.spin-cine .stage-fx,
body.rip-reveal .stage-fx {
  opacity: 1 !important;
  filter: brightness(1.32) saturate(1.7) !important;
}
/* During a pull the red shafts RISE faster (they no longer spin — the rip scene is
   rising light shafts now, not rotating searchlights). */
body.spin-cine .stage-fx::before,
body.rip-reveal .stage-fx::before {
  animation: riseShaft1 1.8s ease-in-out infinite alternate !important;
}
body.spin-cine .stage-fx::after,
body.rip-reveal .stage-fx::after {
  animation: riseShaft2 2.4s ease-in-out infinite alternate !important;
}
/* The concentration rays + anime sparkles crank up too during a pull. */
body.spin-cine .sfx-rays,  body.rip-reveal .sfx-rays  { animation: sfxRaysSpin 7s linear infinite !important; }
body.spin-cine .sfx-rays2, body.rip-reveal .sfx-rays2 { animation: sfxRaysSpinR 11s linear infinite !important; }
body.spin-cine .sfx-sparkles, body.rip-reveal .sfx-sparkles,
body.spin-cine .sfx-sparkles::before, body.rip-reveal .sfx-sparkles::before,
body.spin-cine .sfx-sparkles::after, body.rip-reveal .sfx-sparkles::after { animation-duration: 1s !important; }

/* (The background keeps fully animating during the drag and the spin — the wheel
   smoothness comes from painting its transform once per vsync frame, not from
   stopping the lights.) */

/* ── FOIL CHASE (Legendary) JACKPOT — the whole game screen reacts ───────────
   The persistent searchlight background floods GOLD and intensifies, and a
   screen-wide shockwave + golden sunburst ripples out from the prize. */
body.foilchase-hit .stage-fx {
  filter: sepia(.55) saturate(2.7) brightness(1.28) hue-rotate(-18deg) !important;
}
body.foilchase-hit .sfx-rays  { animation: sfxRaysSpin 5s linear infinite !important; opacity: 1 !important; }
body.foilchase-hit .sfx-rays2 { animation: sfxRaysSpinR 8s linear infinite !important; }

.foil-screen-burst { position: fixed; inset: 0; z-index: 40; pointer-events: none; overflow: hidden; }
/* expanding shockwave rings */
.foil-screen-burst .fsb-ring {
  position: absolute; left: 50%; top: 46%; width: 34vmin; height: 34vmin;
  margin: -17vmin 0 0 -17vmin; border-radius: 50%;
  border: 4px solid rgba(255,213,108,.95);
  box-shadow: 0 0 46px rgba(255,209,92,.85), inset 0 0 34px rgba(255,209,92,.5);
  transform: scale(0); opacity: 0;
  animation: fsbRing 1.5s cubic-bezier(.2,.6,.2,1) forwards;
}
.foil-screen-burst .fsb-ring2 { border-color: rgba(255,244,200,.9); animation-delay: .22s; }
@keyframes fsbRing {
  0%   { transform: scale(0);   opacity: 0; }
  12%  { opacity: 1; }
  100% { transform: scale(7.5); opacity: 0; }
}
/* one-shot golden sunburst that radiates and fades — viewport-sized box + angle
   spin (no giant element, so it can't crop on iPhone either). The mask gives it a
   ring-band shape; the expanding shockwave rings supply the "burst out" feel. */
@property --fsbA { syntax: '<angle>'; inherits: false; initial-value: 0deg; }
.foil-screen-burst .fsb-rays {
  position: absolute; inset: 0; mix-blend-mode: screen;
  background: repeating-conic-gradient(from var(--fsbA, 0deg) at 50% 46%, rgba(255,216,120,.55) 0deg 2deg, transparent 2deg 9deg);
  -webkit-mask: radial-gradient(circle at 50% 46%, transparent 0 7%, #000 24%, #000 56%, transparent 80%);
          mask: radial-gradient(circle at 50% 46%, transparent 0 7%, #000 24%, #000 56%, transparent 80%);
  opacity: 0;
  animation: fsbRays 2.4s ease-out forwards;
}
@keyframes fsbRays {
  0%   { --fsbA: 0deg;  opacity: 0; }
  15%  { opacity: .9; }
  100% { --fsbA: 55deg; opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .foil-screen-burst .fsb-ring, .foil-screen-burst .fsb-rays { animation-duration: .6s; }
  body.foilchase-hit .sfx-rays, body.foilchase-hit .sfx-rays2 { animation: none !important; }
}

/* ── Spectator badge — "👀 user1 is ripping a Foil Chase…" shown to a SIGNED-OUT
   watcher for the whole attract-mode replay so they always know whose pull they're
   seeing. Anime pill: gradient, tier-coloured glow, blinking eye, slides in from
   the top. ── */
.spectator-banner {
  position: fixed; top: 14px; left: 50%; z-index: 12050;
  transform: translate(-50%, -150%);
  display: flex; align-items: center; gap: 9px;
  padding: 9px 18px; border-radius: 999px; white-space: nowrap; max-width: 92vw;
  font: 800 14px/1 system-ui, sans-serif; color: #fff;
  background: linear-gradient(180deg, rgba(24,16,40,.93), rgba(12,8,22,.93));
  border: 1.5px solid var(--sb-color, #ffd15c);
  box-shadow: 0 8px 28px rgba(0,0,0,.5), 0 0 22px color-mix(in srgb, var(--sb-color, #ffd15c) 60%, transparent);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  opacity: 0; pointer-events: none; overflow: hidden; text-overflow: ellipsis;
  transition: transform .42s cubic-bezier(.2,1.1,.3,1), opacity .3s ease;
}
.spectator-banner.show { transform: translate(-50%, 0); opacity: 1; }
.spectator-banner .sb-eye { font-size: 16px; animation: sbEye 1.4s ease-in-out infinite; }
@keyframes sbEye { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.2); } }
.spectator-banner .sb-text b { color: var(--sb-color, #ffd15c); }
@media (max-width: 640px) {
  .spectator-banner { font-size: 12px; padding: 7px 13px; top: 10px; gap: 7px; }
}
@media (prefers-reduced-motion: reduce) {
  .spectator-banner { transition: opacity .3s ease; }
  .spectator-banner.show { transform: translate(-50%, 0); }
  .spectator-banner .sb-eye { animation: none; }
}

@media (prefers-reduced-motion: reduce) {
  .stage-fx::before, .stage-fx::after,
  body.spin-cine .stage-fx::before, body.rip-reveal .stage-fx::before,
  body.spin-cine .stage-fx::after, body.rip-reveal .stage-fx::after { animation: none !important; }
}
/* Mobile uses the SAME animation set as desktop (incl. the hue cycling) so the
   background reads identically on both — no flat, static, over-bright variant. */

/* ════════════════════════════════════════════════════════════════════════════
   GLASS INTRO — a thin frosted sheet over the whole app; tap anywhere to shatter
   (also the first gesture that unlocks audio). Same on mobile + desktop.
   ════════════════════════════════════════════════════════════════════════════ */
.glass-intro {
  position: fixed; inset: 0; z-index: 2147483601;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; overflow: hidden;
  /* A real sheet of FROZEN ICE over the app: cold blue-white frost, crystalline
     facets cut across it, and a deep-freeze tint behind the blur. */
  -webkit-backdrop-filter: blur(10px) saturate(1.18) brightness(1.06);
  backdrop-filter: blur(10px) saturate(1.18) brightness(1.06);
  background:
    /* cold gleam, top-left light source */
    radial-gradient(135% 100% at 22% 10%, rgba(228,246,255,.24), transparent 55%),
    /* lower icy reflection */
    radial-gradient(120% 90% at 82% 94%, rgba(150,200,255,.16), transparent 60%),
    /* crystalline facets — two crossed sets of icy striations */
    repeating-linear-gradient(57deg, rgba(255,255,255,.055) 0 2px, transparent 2px 27px),
    repeating-linear-gradient(-43deg, rgba(185,222,255,.05) 0 2px, transparent 2px 33px),
    /* the frozen body tint */
    linear-gradient(135deg, rgba(212,236,255,.14) 0%, rgba(140,182,255,.07) 45%, rgba(190,222,255,.045) 70%, rgba(118,158,235,.11) 100%),
    rgba(7,15,30,.34);
  box-shadow:
    inset 0 0 0 1px rgba(222,242,255,.18),
    inset 0 0 150px rgba(120,180,255,.12),
    inset 0 0 70px rgba(255,255,255,.06);
  transition: opacity .35s ease;
}
.glass-intro.gone { display: none; }
/* Frost creeping in from every corner (frozen-over edges), gently breathing. */
.glass-intro::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(62% 62% at 0% 0%,    rgba(255,255,255,.18), transparent 70%),
    radial-gradient(62% 62% at 100% 0%,  rgba(214,238,255,.15), transparent 70%),
    radial-gradient(62% 62% at 0% 100%,  rgba(214,238,255,.15), transparent 70%),
    radial-gradient(62% 62% at 100% 100%, rgba(255,255,255,.16), transparent 70%);
  mix-blend-mode: screen;
  animation: iceBreathe 6s ease-in-out infinite;
}
@keyframes iceBreathe { 0%, 100% { opacity: .65; } 50% { opacity: 1; } }
/* Hairline fracture lines etched across the sheet (the stress in the ice). */
.glass-intro::after {
  content: ''; position: absolute; inset: 0; pointer-events: none; opacity: .55;
  background:
    linear-gradient(62deg,  transparent 49.5%, rgba(255,255,255,.24) 49.8%, rgba(255,255,255,.24) 50.1%, transparent 50.4%),
    linear-gradient(113deg, transparent 38.6%, rgba(205,232,255,.16) 39.9%, transparent 40.2%),
    linear-gradient(27deg,  transparent 70.6%, rgba(205,232,255,.14) 71.9%, transparent 72.2%),
    linear-gradient(149deg, transparent 60.7%, rgba(225,242,255,.14) 61.0%, transparent 61.3%);
  mix-blend-mode: screen;
}
/* ── DEMO MODE ice: the same frozen sheet, tinted DANGER RED — so at a glance
   (before you even smash it) you know this build is in demo mode, not normal. ── */
body.demo-mode .glass-intro {
  background:
    radial-gradient(135% 100% at 22% 10%, rgba(255,226,226,.24), transparent 55%),
    radial-gradient(120% 90% at 82% 94%, rgba(255,150,150,.18), transparent 60%),
    repeating-linear-gradient(57deg, rgba(255,255,255,.05) 0 2px, transparent 2px 27px),
    repeating-linear-gradient(-43deg, rgba(255,190,190,.06) 0 2px, transparent 2px 33px),
    linear-gradient(135deg, rgba(255,208,208,.15) 0%, rgba(255,110,110,.10) 45%, rgba(255,170,170,.05) 70%, rgba(214,58,58,.15) 100%),
    rgba(34,7,9,.36);
  box-shadow:
    inset 0 0 0 1px rgba(255,220,220,.2),
    inset 0 0 150px rgba(255,86,86,.17),
    inset 0 0 70px rgba(255,120,120,.07);
}
body.demo-mode .glass-intro::before {
  background:
    radial-gradient(62% 62% at 0% 0%,     rgba(255,236,236,.18), transparent 70%),
    radial-gradient(62% 62% at 100% 0%,   rgba(255,200,200,.16), transparent 70%),
    radial-gradient(62% 62% at 0% 100%,   rgba(255,200,200,.16), transparent 70%),
    radial-gradient(62% 62% at 100% 100%, rgba(255,236,236,.16), transparent 70%);
}
body.demo-mode .glass-intro::after {
  background:
    linear-gradient(62deg,  transparent 49.5%, rgba(255,230,230,.24) 49.8%, rgba(255,230,230,.24) 50.1%, transparent 50.4%),
    linear-gradient(113deg, transparent 38.6%, rgba(255,192,192,.18) 39.9%, transparent 40.2%),
    linear-gradient(27deg,  transparent 70.6%, rgba(255,192,192,.16) 71.9%, transparent 72.2%),
    linear-gradient(149deg, transparent 60.7%, rgba(255,212,212,.16) 61.0%, transparent 61.3%);
}
body.demo-mode .glass-sheen { background: linear-gradient(105deg, transparent, rgba(255,226,226,.26), rgba(255,255,255,.12), transparent); }
body.demo-mode .glass-title { color: #ffd9d9; text-shadow: 0 0 18px rgba(255,90,90,.5); }

/* A slow diagonal cold-light sheen sweeping across the ice. */
.glass-sheen {
  position: absolute; top: -60%; bottom: -60%; left: -40%; width: 35%;
  background: linear-gradient(105deg, transparent, rgba(226,244,255,.26), rgba(255,255,255,.12), transparent);
  transform: skewX(-18deg); pointer-events: none;
  animation: glassSheen 5.5s ease-in-out infinite;
}
@keyframes glassSheen { 0% { left: -40%; } 55%, 100% { left: 130%; } }
.glass-core { text-align: center; pointer-events: none; user-select: none; padding: 20px; }
/* ── Ice screen: content rides UP TOP (near where USERS ONLINE sits on the rip page) and the
   cabinet is re-skinned frosted ICE-BLUE. The whole sheet stays tappable to shatter/enter. ── */
.glass-intro { align-items: flex-start; }
.glass-core  { margin-top: 15vh; display: inline-flex; flex-direction: column; align-items: center; gap: 14px; }
.ice-meter   { margin: 0 auto; }
/* TRANSLUCENT frosted glass — not opaque. The frosted cabinet sits over the real USERS ONLINE
   cabinet beneath the ice, so it reads as that button frozen under glass (and reveals cleanly
   when the ice shatters). */
.jackpot-meter.ice-meter {
  background: linear-gradient(180deg, rgba(224,242,255,.32), rgba(150,196,240,.18) 48%, rgba(200,228,255,.28)) !important;
  border: 1px solid rgba(235,248,255,.5) !important;
  -webkit-backdrop-filter: blur(3px) brightness(1.12) saturate(1.08); backdrop-filter: blur(3px) brightness(1.12) saturate(1.08);
  box-shadow: 0 8px 22px rgba(10,30,60,.28), inset 0 1px 0 rgba(255,255,255,.55),
    inset 0 0 22px rgba(180,222,255,.26), 0 0 24px rgba(150,210,255,.3) !important;
}
.ice-meter .jm-label {
  background: linear-gradient(90deg,#0a2e52,#1c5891 50%,#0a2e52) !important;
  -webkit-background-clip: text !important; background-clip: text !important; -webkit-text-fill-color: transparent !important;
  filter: drop-shadow(0 1px 0 rgba(255,255,255,.6)) !important;
}
.ice-meter .jm-bulb {
  background: radial-gradient(circle at 35% 30%, #ffffff, #a9dbff 55%, #3f8fd6 100%) !important;
  box-shadow: 0 0 8px rgba(160,220,255,.98), 0 0 16px rgba(90,180,255,.75) !important;
}
.ice-meter .jm-board {
  background: linear-gradient(180deg, rgba(8,22,40,.42), rgba(12,38,62,.36) 50%, rgba(8,20,36,.42)) !important;
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  border: 1px solid rgba(180,222,255,.32) !important;
  box-shadow: inset 0 1px 5px rgba(0,0,0,.5), inset 0 0 14px rgba(90,190,255,.2), 0 0 14px rgba(120,205,255,.35) !important;
}
.ice-meter .jm-d { color: #cdeeff !important; text-shadow: 0 0 6px rgba(150,220,255,.98), 0 0 14px rgba(90,180,255,.85), 0 0 26px rgba(60,150,255,.55) !important; }
.ice-meter .jm-digit::before { color: rgba(90,170,255,.16) !important; }
.ice-meter .jm-sep { color: #a9dbff !important; text-shadow: 0 0 6px rgba(120,200,255,.85) !important; }
.ice-meter .jm-online-dot { background: #86d2ff !important; box-shadow: 0 0 9px rgba(150,220,255,1), 0 0 16px rgba(90,180,255,.7) !important; }
.glass-title {
  font: 900 clamp(30px, 9vw, 64px)/1 system-ui, sans-serif; letter-spacing: .14em;
  background: linear-gradient(180deg, #ffffff, #d6ecff 45%, #9cc4ff 75%, #7fb0ff);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  text-shadow: 0 2px 34px rgba(150,200,255,.45);
  filter: drop-shadow(0 1px 0 rgba(255,255,255,.35)) drop-shadow(0 0 18px rgba(150,205,255,.35));
}
/* Certified brand seal above the wordmark — reads as an official, authenticated mark. */
.glass-emblem {
  display: block; width: clamp(58px, 15vw, 80px); height: auto; margin: 0 auto 14px;
  filter: drop-shadow(0 2px 10px rgba(120,185,255,.4)) drop-shadow(0 0 2px rgba(255,255,255,.35));
  animation: glassEmblemGlow 4.5s ease-in-out infinite;
}
@keyframes glassEmblemGlow {
  0%, 100% { filter: drop-shadow(0 2px 10px rgba(120,185,255,.32)) drop-shadow(0 0 2px rgba(255,255,255,.3)); }
  50% { filter: drop-shadow(0 2px 16px rgba(150,205,255,.6)) drop-shadow(0 0 4px rgba(255,255,255,.5)); }
}
/* Authoritative tagline under the wordmark. */
.glass-tag {
  margin-top: 12px; font: 600 clamp(10.5px, 2.6vw, 12.5px)/1.5 system-ui, sans-serif;
  letter-spacing: .14em; text-transform: uppercase; color: rgba(206,228,255,.82);
  text-shadow: 0 0 12px rgba(120,180,255,.28);
}
.glass-hint {
  margin-top: 16px; font: 700 13px/1 system-ui, sans-serif; letter-spacing: .26em;
  text-transform: uppercase; color: rgba(220,232,255,.78);
  animation: glassHintPulse 1.8s ease-in-out infinite;
}
@keyframes glassHintPulse { 0%, 100% { opacity: .5; } 50% { opacity: 1; } }
/* Certifications / trust strip — frosted credential chips that make the house read
   as legitimate and in control before any money moves. */
.glass-trust {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
  gap: 8px 9px; margin: 22px auto 0; max-width: 460px;
}
.gt-badge {
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
  padding: 6px 12px; border-radius: 999px;
  font: 800 10.5px/1 system-ui, sans-serif; letter-spacing: .08em; text-transform: uppercase;
  color: rgba(224,240,255,.9);
  border: 1px solid rgba(200,228,255,.28);
  background: linear-gradient(180deg, rgba(226,244,255,.12), rgba(120,170,240,.06));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.14), 0 2px 10px rgba(30,70,140,.22);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
}
.gt-ic {
  width: 14px; height: 14px; flex: none;
  fill: none; stroke: #bfe0ff; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round;
  filter: drop-shadow(0 0 4px rgba(140,200,255,.5));
}
/* Demo-mode: warm the credential chips to match the red ice. */
body.demo-mode .glass-tag { color: rgba(255,214,214,.85); }
body.demo-mode .gt-badge {
  color: rgba(255,226,226,.92); border-color: rgba(255,196,196,.3);
  background: linear-gradient(180deg, rgba(255,224,224,.12), rgba(240,120,120,.06));
}
body.demo-mode .gt-ic { stroke: #ffc4c4; filter: drop-shadow(0 0 4px rgba(255,150,150,.5)); }
@media (max-width: 520px) {
  .glass-trust { max-width: 320px; gap: 7px; }
  .gt-badge { padding: 5px 10px; font-size: 9.5px; }
}
/* Deploy stamp etched into the ice — quiet, frosty, below the CTA. */
.glass-build {
  margin-top: 18px; font: 600 10.5px/1.4 ui-monospace, "SF Mono", Menlo, monospace;
  letter-spacing: .12em; text-transform: uppercase;
  color: rgba(170,205,240,.62); text-shadow: 0 0 8px rgba(120,180,255,.25);
}
.glass-build:empty { display: none; }
/* Until the vault's artwork is loaded the glass can't be broken yet — show a
   quiet "loading…" instead of the pulsing CTA, and a wait cursor. */
.glass-intro:not(.glass-ready) { cursor: progress; }
.glass-intro:not(.glass-ready) .glass-hint { animation: none; opacity: .55; letter-spacing: .18em; }
/* When shattering, the base pane fades while the JS-spawned shards fly. Pointer-
   events stay AUTO here so the breaking gesture's own click is consumed by the glass
   (it must NOT click through to a button underneath); the JS then drops the glass to
   pointer-events:none right after, so the NEXT click hits buttons immediately. */
.glass-intro.shattering { background: transparent !important; -webkit-backdrop-filter: none; backdrop-filter: none; box-shadow: none; }
.glass-intro.shattering::before, .glass-intro.shattering::after { opacity: 0 !important; transition: opacity .12s ease; }
.glass-intro.shattering .glass-core, .glass-intro.shattering .glass-sheen { opacity: 0; transition: opacity .12s ease; }
/* Individual flying shards (created in JS). Each is a frosted glass triangle. */
.glass-shard {
  position: fixed; pointer-events: none; will-change: transform, opacity;
  -webkit-backdrop-filter: blur(10px) saturate(1.18) brightness(1.06);
  backdrop-filter: blur(10px) saturate(1.18) brightness(1.06);
  background:
    linear-gradient(135deg, rgba(238,250,255,.26), rgba(160,205,255,.09) 50%, rgba(255,255,255,.05)),
    rgba(10,18,32,.30);
  box-shadow: inset 0 0 0 1px rgba(222,242,255,.22), 0 6px 18px rgba(40,80,150,.25);
}
/* The flash of impact at the exact tap point — a cold white-blue crack-burst. */
.glass-impact {
  position: fixed; width: 200px; height: 200px; border-radius: 50%;
  pointer-events: none; z-index: 3; will-change: transform, opacity;
  background: radial-gradient(circle, rgba(255,255,255,.98) 0%, rgba(216,244,255,.6) 22%, rgba(150,200,255,.2) 44%, transparent 62%);
  box-shadow: 0 0 60px rgba(210,240,255,.85);
}
/* Jagged crystalline fracture lines that snap out from the press point. */
.glass-cracks .crack-line {
  fill: none;
  stroke: rgba(234,247,255,.95);
  stroke-width: 1.7px;
  stroke-linecap: round; stroke-linejoin: round;
  filter: drop-shadow(0 0 3px rgba(150,210,255,.95)) drop-shadow(0 0 8px rgba(120,180,255,.5));
}
@media (prefers-reduced-motion: reduce) {
  .glass-sheen, .glass-hint, .glass-intro::before { animation: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Transmute & Craft — gallery crafting loop
   ═══════════════════════════════════════════════════════════════════════════ */
/* Mana is BLUE — a glowing blue potion. */
:root { --crystal: #45b1ff; --crystal-2: #8fe3ff; }
/* The ◈ glyph spans render the potion icon instead (glyph hidden, potion drawn). */
.crystal-ico {
  display: inline-block; width: 1.05em; height: 1.18em; vertical-align: -.24em; overflow: hidden;
  color: transparent; text-shadow: none; font-style: normal;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Crect x='9.6' y='1.6' width='4.8' height='2.6' rx='.8' fill='%23caa15c' stroke='%237a5a22' stroke-width='.8'/%3E%3Cpath d='M10 4.2 L10 8.6 A6 6 0 1 0 14 8.6 L14 4.2 Z' fill='%2345b1ff' stroke='%231c5f96' stroke-width='1.1' stroke-linejoin='round'/%3E%3Cpath d='M7.1 13.2 C9 12.4 10.4 13.8 12 13.4 13.6 13 15 12.3 16.9 13.1' fill='none' stroke='%23cfeaff' stroke-width='1' opacity='.7'/%3E%3Ccircle cx='10.2' cy='16.4' r='1' fill='%23dff1ff' opacity='.85'/%3E%3Ccircle cx='13.4' cy='17.6' r='.7' fill='%23dff1ff' opacity='.7'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* HUD crystals pill — sits beside the CR credits pill so BOTH balances show
   wherever the player HUD docks (rip, gallery, admin). */
.pb-crystals {
  /* Match the other player-bar pills (pb-name-btn / pb-credits-btn): same shape,
     size, font, radius and shadow — just a purple accent so it still reads as
     Crystals, not CR. */
  display: inline-flex; align-items: center; gap: 5px;
  color: #e6f4ff; font: 800 12px/1 system-ui, sans-serif; white-space: nowrap;
  padding: 0 13px; border-radius: 7px;
  background: linear-gradient(180deg, rgba(18,32,48,.85), rgba(10,18,28,.85));
  border: 1px solid rgba(69,177,255,.5);
  box-shadow: 0 6px 18px rgba(0,0,0,.42);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
}
.pb-crystals .crystal-gem { width: 17px; height: 17px; display: block; filter: drop-shadow(0 0 4px rgba(69,177,255,.65)); }

/* Small inline currency icons (distinct shapes + colors) for balances anywhere.
   CR = blue rhombus diamond; Crystal = purple faceted shard. */
.ico-cr, .ico-crystal {
  display: inline-block; width: 1.05em; height: 1.05em; vertical-align: -.18em;
  background-position: center; background-repeat: no-repeat; background-size: contain;
}
.ico-cr {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2 L22 10 L12 22 L2 10 Z' fill='%2386d6ff' stroke='%232f86cf' stroke-width='1.2' stroke-linejoin='round'/%3E%3Cpath d='M2 10 H22 M12 2 V22' stroke='%232f86cf' stroke-width='.7' opacity='.5'/%3E%3C/svg%3E");
}
.ico-crystal {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Crect x='9.6' y='1.6' width='4.8' height='2.6' rx='.8' fill='%23caa15c' stroke='%237a5a22' stroke-width='.8'/%3E%3Cpath d='M10 4.2 L10 8.6 A6 6 0 1 0 14 8.6 L14 4.2 Z' fill='%2345b1ff' stroke='%231c5f96' stroke-width='1.1' stroke-linejoin='round'/%3E%3Cpath d='M7.1 13.2 C9 12.4 10.4 13.8 12 13.4 13.6 13 15 12.3 16.9 13.1' fill='none' stroke='%23cfeaff' stroke-width='1' opacity='.7'/%3E%3Ccircle cx='10.2' cy='16.4' r='1' fill='%23dff1ff' opacity='.85'/%3E%3Ccircle cx='13.4' cy='17.6' r='.7' fill='%23dff1ff' opacity='.7'/%3E%3C/svg%3E");
}
.craft-balance .cb-money { display: inline-flex; align-items: center; gap: 5px; margin: 0 10px; font-size: 17px; }
.craft-balance .cb-money b { color: #fff; }
.gallery-actions-spacer { flex: 1 1 auto; }

.gallery-actions {
  display: flex; align-items: center; justify-content: center; gap: 12px; flex-wrap: wrap;
  margin-top: 12px;   /* sits right below the userbar — easy to reach on mobile + desktop */
}
/* Crafting + Buy Credits panels, side by side (stack only when truly narrow). */
.gallery-panels {
  display: flex; flex-wrap: wrap; gap: 16px; justify-content: center; align-items: flex-start;
  max-width: 1040px; margin: 18px auto 0; padding: 0 14px;
}
.gallery-panels .gallery-panel { flex: 1 1 320px; max-width: 480px; margin: 0; text-align: center; }
.gallery-panels .craft-tier-list { justify-items: center; }
.gallery-panels .buy-packs { margin: 0 auto; }
@media (max-width: 560px) { .gallery-panels { flex-direction: column; align-items: stretch; } }

/* Buy Credits = the primary, attention-leading action point (this is how the app
   earns). Wider, gold, glowing — clearly dominant over the secondary craft panel. */
.gallery-panel-primary {
  flex: 1.5 1 360px !important; max-width: 560px !important;
  position: relative; overflow: visible;
  border: 1px solid rgba(255,205,80,.55) !important;
  background: radial-gradient(120% 90% at 50% -10%, rgba(255,210,90,.16), rgba(14,11,7,.97)) !important;
  box-shadow: 0 16px 54px rgba(0,0,0,.5), 0 0 30px rgba(255,205,80,.28), inset 0 0 36px rgba(255,205,80,.06) !important;
  animation: buyPanelGlow 2.8s ease-in-out infinite;
}
@keyframes buyPanelGlow {
  0%,100% { box-shadow: 0 16px 54px rgba(0,0,0,.5), 0 0 26px rgba(255,205,80,.24), inset 0 0 36px rgba(255,205,80,.06); }
  50%     { box-shadow: 0 16px 54px rgba(0,0,0,.5), 0 0 46px rgba(255,224,124,.5), inset 0 0 36px rgba(255,205,80,.1); }
}
.buy-title { color: #f2f4f8 !important; font-size: 24px !important; text-shadow: 0 1px 3px rgba(0,0,0,.6); }
.buy-badge {
  position: absolute; top: -11px; left: 50%; transform: translateX(-50%);
  background: linear-gradient(180deg,#ffffff,#d7d7cc); color: #17181c;
  font: 900 11px/1 system-ui, sans-serif; letter-spacing: .08em; text-transform: uppercase;
  padding: 5px 12px; border-radius: 999px; box-shadow: 0 4px 14px rgba(0,0,0,.45), inset 0 1px 0 rgba(255,255,255,.9);
}
.buy-test-note { opacity: .6; font-size: 11.5px; }
@media (prefers-reduced-motion: reduce) { .gallery-panel-primary { animation: none; } }

/* Slick section headers that divide the crafting page into clear bands:
   Crafting → Your Collection → Crafting Wheel. A short centred title with a
   thin gold rule on either side. */
.gallery-section-head {
  width: min(960px, 96vw); margin: 30px auto 4px; text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
}
.gallery-section-title {
  position: relative; margin: 0; padding: 0 18px;
  font: 800 13px/1 system-ui, sans-serif; letter-spacing: .18em; text-transform: uppercase;
  color: #ffe08a; text-shadow: 0 0 14px rgba(255,205,80,.35);
}
.gallery-section-title::before, .gallery-section-title::after {
  content: ""; position: absolute; top: 50%; width: 46px; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(255,205,80,.55));
}
.gallery-section-title::before { right: 100%; transform: scaleX(-1); }
.gallery-section-title::after  { left: 100%; }
.gallery-section-sub { margin: 0; font: 500 12px/1.3 system-ui, sans-serif; color: #9fb0c6; }
@media (max-width: 560px) { .gallery-section-title::before, .gallery-section-title::after { width: 26px; } }

/* Combined shop: Buy Rips + Crafting side by side in one wide panel, sitting just
   above the full-width craft wheel. */
/* ── Buy Rips pill (in the userbar) + drop-down purchase popover ──────────────── */
.pb-buy-btn {
  -webkit-appearance: none; appearance: none; cursor: pointer; flex: 0 0 auto;
  position: relative; overflow: hidden; isolation: isolate;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 0 13px; border: none; border-radius: 7px;
  font: 900 12px/1 system-ui, sans-serif; color: #2a1300; white-space: nowrap;
  text-shadow: 0 1px 0 rgba(255,248,214,.6);
  background: linear-gradient(180deg, #ffe9a6 0%, #f8c84f 48%, #e89a26 100%);
  box-shadow: 0 5px 16px rgba(0,0,0,.42), 0 0 18px rgba(255,205,80,.5), inset 0 1px 0 rgba(255,255,255,.7);
  animation: buyCtaPulse 2.4s ease-in-out infinite;
}
.pb-buy-btn .buy-cta-rip { width: 18px; height: 18px; flex: 0 0 auto; }
.pb-buy-label { white-space: nowrap; }
.pb-buy-btn::before {   /* sweeping specular sheen */
  content: ''; position: absolute; top: -25%; bottom: -25%; left: -60%; width: 42%; z-index: -1;
  background: linear-gradient(105deg, transparent, rgba(255,255,255,.75), transparent);
  transform: skewX(-18deg); pointer-events: none; mix-blend-mode: screen;
  animation: buyCtaSheen 3.2s ease-in-out infinite;
}
@keyframes buyCtaPulse {
  0%,100% { box-shadow: 0 5px 16px rgba(0,0,0,.42), 0 0 16px rgba(255,205,80,.45), inset 0 1px 0 rgba(255,255,255,.7); }
  50%     { box-shadow: 0 5px 16px rgba(0,0,0,.42), 0 0 30px rgba(255,224,124,.9), inset 0 1px 0 rgba(255,255,255,.85); }
}
@keyframes buyCtaSheen { 0% { left: -60%; } 42%,100% { left: 150%; } }

/* The purchase form drops down from the bar as a fixed popover (works on any view). */
/* While the buy sheet is open, freeze the page behind it so it can't scroll up/down. */
body.buy-locked { overflow: hidden; overscroll-behavior: none; }
.buy-dropdown {
  position: fixed; top: 58px; left: 50%; z-index: 12090;
  width: min(420px, 94vw);
  opacity: 0; pointer-events: none;
  /* Fast, smooth GPU transform (drops the old max-height:0→680px transition, which
     eased toward a height far past the content and felt slow/janky at the end). */
  transform: translateX(-50%) translateY(-10px) scale(.97);
  transform-origin: top center;
  transition: opacity .13s ease, transform .17s cubic-bezier(.2,.9,.25,1.15);
  will-change: transform, opacity;
}
.buy-dropdown.open {
  opacity: 1; pointer-events: auto;
  transform: translateX(-50%) translateY(0) scale(1);
}
.buy-dropdown-inner {
  padding: 12px 14px 14px; border-radius: 12px; position: relative;
  /* RIP-page buy terminal — a dark armoured hull with a neon-green rim + glow, matching
     the EVA cockpit panels so the buy sheet reads as part of the same machine. */
  background:
    linear-gradient(180deg, rgba(46,229,124,.08), transparent 30%),
    linear-gradient(180deg, #10161a 0%, #0a1013 52%, #06090b 100%);
  border: 1px solid rgba(46,229,124,.55);
  box-shadow: 0 20px 52px rgba(0,0,0,.72), inset 0 1px 0 rgba(200,255,222,.16), inset 0 -8px 12px rgba(0,0,0,.55), 0 0 20px rgba(46,229,124,.28);
}
/* Close ✕ — a small white key, top-right of the panel (essential on the full-screen
   mobile sheet where there's no "outside" to tap). */
.buy-close {
  position: absolute; top: 12px; right: 12px; z-index: 5;
  width: 32px; height: 32px; border-radius: 50%; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid rgba(0,0,0,.42); color: #17181c; font: 800 15px/1 system-ui, sans-serif;
  background: linear-gradient(180deg, #ffffff 0%, #f1f1ea 52%, #d7d7cc 100%);
  box-shadow: inset 0 2px 0 rgba(255,255,255,.9), 0 2px 5px rgba(0,0,0,.35);
}
.buy-close:active { transform: translateY(1px); }
/* Mobile: the BUY RIPS sheet takes the WHOLE screen and snaps open fast. */
@media (max-width: 640px) {
  /* A CONTAINED card inset from every edge (including the notch / home-indicator safe
     areas) so you can see its rounded edges + green rim, and the close X is never cropped
     by the status bar or a screen corner. It fits within the device and scrolls inside if
     the form is taller than the space. */
  .buy-dropdown {
    top: calc(env(safe-area-inset-top) + 10px);
    left: 10px; right: 10px; bottom: auto; width: auto; max-width: none;
    transform: translateY(8px); transform-origin: top center;
    transition: opacity .1s ease, transform .15s cubic-bezier(.2,.9,.25,1.1);
  }
  .buy-dropdown.open { transform: none; }
  .buy-dropdown-inner {
    /* Never taller than the viewport minus the insets → the whole card (and its X) is
       always on screen; the form scrolls inside. Keeps the base green rim + rounded edges. */
    max-height: calc(100dvh - env(safe-area-inset-top) - env(safe-area-inset-bottom) - 20px);
    border-radius: 16px;
    /* Top-aligned (NOT centered): centering a max-height + overflow:auto flex box makes iOS
       treat it as scrollable even when the content fits, so the panel could be dragged up/down.
       flex-start + the compact content means there's simply nothing to scroll. */
    display: flex; flex-direction: column; gap: 12px; justify-content: flex-start;
    padding: 52px 16px calc(22px + env(safe-area-inset-bottom));
    box-sizing: border-box; overflow-y: auto; overscroll-behavior: none;
  }
  /* X sits INSIDE the card's top-right corner (not under the status bar). */
  .buy-close { top: 10px; right: 10px; width: 34px; height: 34px; font-size: 16px; }
  /* ── Compact the whole purchase panel so it fits WITHOUT scrolling (was overflowing by a
     line or two). No title now, so the top padding only needs to clear the X; everything
     else tightens up. ── */
  .buy-dropdown-inner { padding: 18px 13px calc(12px + env(safe-area-inset-bottom)); gap: 7px; }
  .rip-buy { gap: 7px; }
  .rip-qty { gap: 12px; }
  .rip-step { width: 34px; height: 34px; font-size: 19px; border-radius: 10px; }
  .rip-qty-val { min-width: 80px; }
  .rip-qty-val b { font-size: 25px; }
  .rip-qty-label { font-size: 13px; }
  .rip-glyph { width: 18px; height: 18px; }
  .rip-deals { gap: 7px; margin-top: 0; }
  .rip-deal { flex-basis: 120px; padding: 7px 8px 6px; }
  .rip-deal-main { font-size: 13px; }
  .rip-deal-main .rdm-price { font-size: 14px; }
  .rip-deal-main .rip-glyph { width: 15px; height: 15px; }
  .rip-deal-save { font-size: 10px; }
  .rip-buy-btn { padding: 9px 16px; font-size: 14.5px; }
  .rip-trust { margin-top: 2px; gap: 5px; }
  .rip-trust-line { font-size: 10px; max-width: 280px; line-height: 1.3; }
  .rip-cardmarks { gap: 5px; }
}
/* Short phones: the payment form + trust badges are the last thing to overflow. Drop the
   decorative card-marks row (VISA/MC/… — purely cosmetic; the Stripe line already conveys
   trust) so the sheet always fits on screen with nothing to scroll. */
@media (max-width: 640px) and (max-height: 760px) {
  .rip-cardmarks { display: none; }
  .buy-dropdown-inner { gap: 6px; }
  .rip-buy { gap: 6px; }
}
@media (prefers-reduced-motion: reduce) {
  .pb-buy-btn { animation: none; } .pb-buy-btn::before { display: none; } .buy-dropdown { transition: none; }
}

/* ════════════════════════════════════════════════════════════════════════════
   PIANO-KEY BUTTONS — BUY RIPS (white ivory key) + GALLERY (black ebony key)
   sit side by side in the userbar like two adjacent piano keys. Each has a
   polished-lacquer shine that glides across, a bright key-lip highlight and a
   realistic key-press dip on click. The shine is an animated background layer
   (not a pseudo-element) so the admin crown ::before + gallery-flash still work.
   ════════════════════════════════════════════════════════════════════════════ */
#buyCtaBtn.pb-buy-btn, .pb-name-btn {
  border-radius: 6px !important;
  transition: transform .07s ease, box-shadow .12s ease, filter .15s ease !important;
}
#buyCtaBtn.pb-buy-btn::before { display: none !important; }   /* drop the old gold sheen */
/* BUY RIPS = FLAT white / ivory (textureless — shine sweep removed). */
#buyCtaBtn.pb-buy-btn {
  color: #17181c !important; text-shadow: 0 1px 0 rgba(255,255,255,.85) !important;
  border: 1px solid rgba(0,0,0,.4) !important;
  background: linear-gradient(180deg, #fbfbf6 0%, #ececdf 100%) !important;
  box-shadow: 0 3px 10px rgba(0,0,0,.4), inset 0 1px 0 rgba(255,255,255,.95) !important;
  animation: none !important;
}
/* GALLERY = FLAT fuller-black ebony (textureless — shine sweep removed). */
.pb-name-btn {
  color: #eef0f5 !important;
  border: 1px solid #000 !important;
  background: #08090c !important;
  box-shadow: 0 3px 10px rgba(0,0,0,.6), inset 0 1px 0 rgba(255,255,255,.08) !important;
  backdrop-filter: none !important; -webkit-backdrop-filter: none !important;
  animation: none !important;
}
.pb-name-btn .pb-name { color: #eef0f5 !important; }
@keyframes pianoShineWhite { 0%,14% { background-position: 150% 0, 0 0; } 55%,100% { background-position: -170% 0, 0 0; } }
@keyframes pianoShineBlack { 0%,22% { background-position: 150% 0, 0 0; } 62%,100% { background-position: -170% 0, 0 0; } }
/* Press the key: it dips + the shadow collapses, like a real piano key. */
/* ── Neon "ZAP" button feel in the userbar — replaces the soft/spongy piano-key dip with a
   fast, crisp press and an electric glow spike (per-button accent colour). Snappy timing kills
   the sponginess; the glow makes the press read as an instant zap. ─────────────────────────── */
.pb-nav-rip   { --zap: 46,229,124; }
.pb-nav-craft { --zap: 176,96,255; }
#buyCtaBtn.pb-buy-btn { --zap: 255,205,80; }
.pb-name-btn  { --zap: 150,210,255; }
/* 90s mall light-up panel: NO movement (no mushy key dip), it just LIGHTS UP the instant you
   touch — INSTANT (transition:none) electric flood of the button's neon colour. */
.pb-nav-btn, #buyCtaBtn.pb-buy-btn, .pb-name-btn { transition: none !important; }
.pb-nav-btn:hover, #buyCtaBtn.pb-buy-btn:hover, .pb-name-btn:hover {
  filter: brightness(1.16) saturate(1.25) !important;
  box-shadow: 0 0 0 1px rgba(var(--zap),.95), 0 0 16px rgba(var(--zap),.9), 0 0 34px rgba(var(--zap),.5), 0 6px 18px rgba(0,0,0,.4) !important;
}
.pb-nav-btn:active, #buyCtaBtn.pb-buy-btn:active, .pb-name-btn:active {
  transform: none !important;            /* it lights up — it does NOT move */
  filter: brightness(1.7) saturate(1.7) contrast(1.05) !important;
  box-shadow: 0 0 0 2.5px rgba(var(--zap),1), 0 0 22px rgba(var(--zap),1), 0 0 52px rgba(var(--zap),1),
    0 0 90px rgba(var(--zap),.85), 0 0 150px rgba(var(--zap),.5) !important;
}
/* ── Mall light-up panel: the electricity blooms EXACTLY where your finger/cursor is, and
   FOLLOWS it — the button never moves, it just glows under the touch point. A radial layer
   (::after) is driven by --px/--py (set from the pointer position in app.js). Clipped to the
   button shape; screen-blended so it reads as light, not paint. ─────────────────────────── */
.pb-nav-btn, #buyCtaBtn.pb-buy-btn, .pb-name-btn { position: relative; overflow: hidden; }
.pb-nav-btn::after, #buyCtaBtn.pb-buy-btn::after, .pb-name-btn::after {
  content: ''; display: block !important; position: absolute; inset: 0; z-index: 3;
  border-radius: inherit; pointer-events: none; mix-blend-mode: screen;
  /* RIP/CRAFT also carry .pb-credits-btn, whose ::after is a masked/padded gold ring — reset
     the leftover mask + padding so our bloom fills the whole button instead of a thin edge. */
  -webkit-mask: none !important; mask: none !important; padding: 0 !important;
  /* BIG, bright bloom with a white-hot core — much more noticeable electricity. */
  background: radial-gradient(circle 92px at var(--px, 50%) var(--py, 50%),
    rgba(255,255,255,.95) 0%, rgba(var(--zap), 1) 14%, rgba(var(--zap), .75) 36%,
    rgba(var(--zap), .32) 58%, rgba(var(--zap), .08) 74%, transparent 84%) !important;
  opacity: 0; transition: opacity .16s ease;
}
/* On the WHITE buy button a screen-blend bloom is invisible — paint it normally so the
   yellow electricity actually shows on the pale surface. */
#buyCtaBtn.pb-buy-btn::after {
  mix-blend-mode: normal !important;
  background: radial-gradient(circle 92px at var(--px, 50%) var(--py, 50%),
    rgba(255,240,190,.98) 0%, rgba(var(--zap), 1) 20%, rgba(var(--zap), .7) 44%,
    rgba(var(--zap), .28) 64%, transparent 80%) !important;
}
/* Cursor hovering (desktop): a clear glow trails the pointer. */
.pb-nav-btn.zap-lit::after, #buyCtaBtn.pb-buy-btn.zap-lit::after, .pb-name-btn.zap-lit::after { opacity: .72; }
/* Pressed / touched: the bloom snaps to full at that spot. */
.pb-nav-btn.zapping::after, #buyCtaBtn.pb-buy-btn.zapping::after, .pb-name-btn.zapping::after { opacity: 1; transition: opacity .03s ease; }
/* Hover = a brighter neon rim (flat surface, so no gradient swap). */
#buyCtaBtn.pb-buy-btn:hover { filter: brightness(1.03) !important; }
.pb-name-btn:hover, .pb-name-btn:focus-visible { filter: brightness(1.15) !important; }
@media (prefers-reduced-motion: reduce) {
  #buyCtaBtn.pb-buy-btn, .pb-name-btn { animation: none !important; background-position: -170% 0, 0 0 !important; }
}

/* ── RIP + CRAFT: FLAT, TEXTURELESS matte-black panels (fuller blacks) — no glossy
   gradient/shine, so the electric touch-feedback reads loud against the dark. Identity is
   the neon accent only: a coloured rim + a coloured label glow (--zap), and on touch the
   whole panel floods with that colour (see the ::after bloom + :active flood below). ──── */
#creditsBuyBtn.pb-nav-rip, #craftNavBtn.pb-nav-craft {
  border-radius: 6px !important;
  background: #0a0b0f !important;          /* flat, textureless, fuller black */
  border: 1.5px solid rgba(var(--zap), .6) !important;
  color: #fff !important;
  text-shadow: 0 0 9px rgba(var(--zap), .85), 0 1px 2px rgba(0,0,0,.8) !important;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.05), 0 3px 10px rgba(0,0,0,.55), 0 0 10px rgba(var(--zap), .22) !important;
  animation: none !important;
  transition: none !important;
}
/* Tint the label glyphs so RIP reads green / CRAFT reads purple even at rest. */
#creditsBuyBtn.pb-nav-rip .pb-nav-label, #craftNavBtn.pb-nav-craft .pb-nav-label { color: #fff !important; }
/* NO key-press dip — these are mall light-up panels, not keys. On press they DON'T move;
   they flood with their neon colour (the localized bloom rides the touch point via ::after). */
#creditsBuyBtn.pb-nav-rip:active, #craftNavBtn.pb-nav-craft:active {
  transform: none !important;
  background: #14161d !important;   /* surface lifts toward the neon on press */
  filter: brightness(1.7) saturate(1.7) contrast(1.05) !important;
  box-shadow: 0 0 0 2.5px rgba(var(--zap),1), 0 0 22px rgba(var(--zap),1), 0 0 52px rgba(var(--zap),1),
    0 0 90px rgba(var(--zap),.85), 0 0 150px rgba(var(--zap),.5) !important;
}
#creditsBuyBtn.pb-nav-rip:hover, #craftNavBtn.pb-nav-craft:hover {
  border-color: rgba(var(--zap),.95) !important;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.05), 0 3px 10px rgba(0,0,0,.55), 0 0 16px rgba(var(--zap),.5) !important;
}
@media (prefers-reduced-motion: reduce) {
  #creditsBuyBtn.pb-nav-rip, #craftNavBtn.pb-nav-craft { animation: none !important; background-position: -170% 0, 0 0 !important; }
}

/* The craft page is crafting-only now (the gallery is its own page): a simple centred
   column — rarity cards, then the wheel below. */
#shopPanel {
  display: flex !important; flex-direction: column; align-items: stretch;
  width: min(960px, 96vw) !important; max-width: 960px !important; margin: 36px auto 0 !important;
}
#shopPanel > .shop-cols { min-width: 0; }
#shopPanel > #craftCarousel { margin-top: 4px; }
/* On desktop nudge the craft potions down so the bottle tops sit the same distance
   below the pinned userbar as the rip page's chase-card tops (~22px gap). */
@media (min-width: 641px) {
  #shopPanel { margin-top: 60px !important; }
}
/* ── Gallery page: the full collection, full-width, with the cabinet marquee on top. */
/* Extra top gap so the GALLERY SELECTION cabinet clears the fixed userbar pill and isn't
   touching it (the bar sits at top:12px, ~40px tall). */
.gallery-vault { width: min(1100px, 96vw); margin: 52px auto 0; }
/* Desktop: the vault's top MARGIN was collapsing through #galleryView, so the gallery
   panel sat flush against the pinned userbar (no gap) — unlike RIP/CRAFT. Move the top
   spacing to PADDING on the view (padding never collapses) so it clears the bar by the
   same ~14px gap the craft page gets from #shopPanel's 60px. */
@media (min-width: 641px) {
  #galleryView { padding-top: 40px; }
  .gallery-vault { margin-top: 0; }
}
.gallery-marquee { margin-top: 4px; }
.gallery-vault .crafting-collection { border-top: none !important; margin: 0 !important; padding-top: 0 !important; }
/* On its own page the gallery always shows everything — no collapse, no toggle — and
   fits as many cards per row as the width allows (not just 3). */
.crafting-collection.cc-page #inventoryGrid {
  max-height: none !important; overflow: visible !important;
  -webkit-mask-image: none !important; mask-image: none !important;
  /* A fixed 4-up grid. --gal-cols (set in JS) drops to the tile count when there are
     fewer than 4, so a short gallery is CENTRED instead of clumped on the left. */
  grid-template-columns: repeat(var(--gal-cols, 4), minmax(0, 190px)); justify-content: center; max-width: 1100px;
}
.crafting-collection.cc-page .cc-gallery-divider { display: none !important; }
.crafting-collection.cc-page .gallery-action-card { display: flex !important; }
/* Phones: a FIXED 4-up grid so a card is the SAME (small) size no matter how many
   you own. With the count-based --gal-cols + minmax(0,190px), one card ballooned to
   190px (~half the screen, pushing everything down); pinning 4 equal columns keeps a
   single card at ~1/4 width, left in one short row, so more of the screen shows. */
@media (max-width: 640px) {
  .crafting-collection.cc-page #inventoryGrid {
    /* Each card stays the SAME small ~1/4 width (4-up size), but only --gal-cols of them
       are laid out and the whole row is CENTRED — so 1-3 cards sit centred in the row
       instead of clumped on the left, without any card ballooning. */
    grid-template-columns: repeat(var(--gal-cols, 4), var(--gal-cw, calc((100% - 21px) / 4)));
    justify-content: center; gap: 7px;
  }
}
/* Whose gallery this is (username) + their shipments button — both moved out of the
   top userbar to declutter it. Sits under the GALLERY marquee, above the cards. */
.gallery-identity {
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 12px;
  margin: 2px auto 12px; max-width: 1100px;
}
.gallery-username {
  font: 800 15px/1 system-ui, sans-serif; letter-spacing: .04em;
  color: #ffd98a; text-shadow: 0 0 12px rgba(255,190,90,.35), 0 1px 2px rgba(0,0,0,.6);
}
.gallery-ship-btn {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  padding: 7px 14px; border-radius: 8px;
  color: #cfe0f2; font: 700 12.5px/1 system-ui, sans-serif; letter-spacing: .03em;
  background: rgba(20,26,38,.6); border: 1px solid rgba(120,200,255,.3);
  transition: background .14s, color .14s, border-color .14s;
}
.gallery-ship-btn:hover { color: #fff; background: rgba(30,50,72,.75); border-color: rgba(120,200,255,.55); }

/* Sign out lives quietly at the bottom of your gallery (the userbar has no power
   button anymore) — understated so it never competes with the collection. */
.gallery-signout-row { display: flex; justify-content: center; margin: 20px 0 8px; }
.gallery-signout-btn {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  padding: 8px 16px; border-radius: 8px;
  color: #b9c4d6; font: 700 12.5px/1 system-ui, sans-serif; letter-spacing: .04em;
  background: rgba(20,26,38,.6); border: 1px solid rgba(255,255,255,.12);
  transition: background .14s, color .14s, border-color .14s;
}
.gallery-signout-btn:hover { color: #fff; background: rgba(48,28,32,.7); border-color: rgba(255,120,120,.45); }
.gallery-signout-btn svg { width: 15px; height: 15px; }
/* The collection shows a COMPACT preview by default so a big gallery never pushes
   the craft wheel out of place (on desktop the collection shares the top row with
   the controls; letting it grow shoved the wheel far down). Capping the height needs
   overflow:hidden, which would re-crop the hover-zoomed cards — so WHILE COLLAPSED we
   also suppress the hover lift/glow (tap still selects). The "Show full gallery"
   button expands to the whole grid with hover-zoom restored (overflow back to visible
   → no cropping). The .cc-collapsible class is only added (by JS) when the collection
   actually overflows the cap, so a small collection is never faded or capped. */
.crafting-collection.cc-collapsible:not(.cc-expanded) #inventoryGrid {
  max-height: var(--cc-cap, 332px);   /* one measured row + a peek (set in JS) */
  overflow: hidden;
  -webkit-mask-image: linear-gradient(#000 78%, transparent 100%);
          mask-image: linear-gradient(#000 78%, transparent 100%);
}
/* While collapsed, hide the SELECT/SHIP/DISENCHANT action card entirely so SHOW
   GALLERY is what reveals it (it lives at the end of the grid). */
.crafting-collection.cc-collapsible:not(.cc-expanded) #inventoryGrid .gallery-action-card { display: none; }
@media (hover: hover) {
  .crafting-collection.cc-collapsible:not(.cc-expanded) #inventoryGrid .floor-card:hover {
    transform: none !important; filter: none !important;
    box-shadow: none !important; z-index: auto !important;
  }
}
.crafting-collection.cc-expanded #inventoryGrid {
  max-height: none; overflow: visible;
  -webkit-mask-image: none; mask-image: none;
}
/* The action card is the LAST tile in the gallery grid: a full-card SELECT CARDS
   prompt that splits into SHIP (top) / DISENCHANT (bottom) once cards are picked. It
   matches the card tiles in size (5:7 portrait) so it reads as the final card. */
.crafting-collection .gallery-action-card {
  position: relative; align-self: start; aspect-ratio: 5 / 7;
  display: flex; flex-direction: column; overflow: hidden; border-radius: 10px;
  border: 1.5px dashed color-mix(in srgb, var(--crystal, #45b1ff) 55%, transparent);
  background: linear-gradient(165deg, rgba(69,177,255,.12), rgba(69,177,255,.04));
}
.gallery-action-card .gac-prompt {
  flex: 1; display: flex; align-items: center; justify-content: center; text-align: center;
  font: 900 16px/1.25 system-ui, sans-serif; letter-spacing: .08em; color: #d6ecff;
  text-shadow: 0 1px 4px rgba(0,0,0,.45);
}
.gallery-action-card .gac-half {
  display: none; flex: 1 1 50%; align-items: center; justify-content: center; gap: 5px;
  border: 0; cursor: pointer; color: #fff; font: 900 14px/1.1 system-ui, sans-serif;
  letter-spacing: .03em; padding: 6px; text-align: center; transition: filter .15s ease;
}
.gallery-action-card .gac-half:hover { filter: brightness(1.13); }
.gallery-action-card .gac-half:active { filter: brightness(.95); }
/* SHIP + DISENCHANT match the SELECT / GALLERY-SELECTION cabinets: the EVA cockpit-panel
   treatment (dark hull + neon rim + hazard stripe + corner brackets) is applied to them
   via the shared cockpit selector group below; here we only set the amber HUD label text
   so it reads on the dark hull like the cabinet board text. */
.gallery-action-card .gac-ship,
.gallery-action-card .gac-dis,
.gac-actions .gac-ship,
.gac-actions .gac-dis {
  position: relative;   /* anchor the cockpit hazard stripe + corner-bracket pseudos */
  color: #ffc23a; text-shadow: 0 0 6px rgba(255,170,40,.55), 0 1px 2px rgba(0,0,0,.6);
}
/* Keep the SHIP/DISENCHANT label + icon above the cockpit hazard/bracket pseudos. */
.gac-actions .gac-ship > *, .gac-actions .gac-dis > * { position: relative; z-index: 3; }
.gallery-action-card.gac-active .gac-prompt { display: none; }
.gallery-action-card.gac-active .gac-half { display: flex; }
.gallery-action-card .crystal-ico { font-size: .92em; }

/* The DECISION area at the BOTTOM of the gallery (counterpart to JACKPOT/CRAFT): an
   arcade CABINET reading SELECT CARDS / the count, with SHIP + DISENCHANT buttons that
   drop in BELOW it once cards are picked. */
.gallery-decision-row { display: flex; justify-content: center; margin: 12px auto 6px; max-width: 1100px;
  /* Sit ABOVE the fixed Live2D mascot (z 6000) so SHIP/DISENCHANT are always visible AND
     tappable — she used to overlap and swallow taps on the bottom-left of this row. #main
     makes no stacking context, so this z-index escapes to the root and beats her. */
  position: relative; z-index: 6200; }
.gallery-decision-row .gallery-action-card {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  aspect-ratio: auto; width: min(460px, 94%); border: 0; background: none; overflow: visible;
}
.gac-cabinet { min-width: 220px; }
.gac-actions { display: none; width: 100%; gap: 10px; }
.gallery-action-card.gac-active .gac-actions { display: flex; }
.gac-actions .gac-half {
  display: flex; flex: 1 1 50%; align-items: center; justify-content: center; gap: 6px;
  padding: 13px 10px; border: 0; border-radius: 10px; cursor: pointer; color: #fff;
  font: 900 14px/1.1 Helvetica, Arial, sans-serif; letter-spacing: .04em; text-transform: uppercase;
  box-shadow: 0 4px 12px rgba(0,0,0,.4); transition: filter .15s ease;
}
.gac-actions .gac-half:hover { filter: brightness(1.13); }
.gac-actions .gac-half:active { filter: brightness(.95); }
.gac-actions .gac-ico { font-size: 1.15em; }
/* Ship's package icon rides AFTER the (count); the mana bottle rides AFTER the
   (amount) on Disenchant and is bumped up a touch so it reads as the mana potion. */
/* Disenchant icon = an enlarged BLUE MANA POTION (SVG), not the old test-tube emoji. */
.gac-actions .gac-ico-mana { font-size: 1em; line-height: 1; display: inline-flex; align-items: center; }
.gac-actions .gac-ico-mana .gac-mana-potion { width: 1.9em; height: 1.9em; display: block; filter: drop-shadow(0 1px 2px rgba(0,0,0,.5)); }
.gac-actions .crystal-ico { font-size: .92em; }

/* Mana POTION fill — plays on DISENCHANT: a bottle (same silhouette as the craft rarity
   potions) whose purple mana liquid rises from your old level to the new one. */
.mana-fill-overlay {
  position: fixed; inset: 0; z-index: 12000; display: flex; align-items: center; justify-content: center;
  pointer-events: none; opacity: 0; transition: opacity .3s ease;
  background: radial-gradient(closest-side at 50% 46%, rgba(60,150,255,.26), transparent 72%);
}
.mana-fill-overlay.show { opacity: 1; }
.mfa-card { display: flex; flex-direction: column; align-items: center; gap: 8px; transform: scale(.9); transition: transform .35s cubic-bezier(.2,1.4,.3,1); }
.mana-fill-overlay.show .mfa-card { transform: scale(1); }
.mfa-plus {
  font: 900 15px/1 Helvetica, Arial, sans-serif; letter-spacing: .1em; text-transform: uppercase;
  color: #bfe6ff; text-shadow: 0 0 12px rgba(69,177,255,.7), 0 1px 3px rgba(0,0,0,.6);
}
.mfa-amt {
  display: inline-flex; align-items: center; gap: 6px;
  font: 900 34px/1 Helvetica, Arial, sans-serif; letter-spacing: .02em;
  color: #eaf6ff; text-shadow: 0 0 18px rgba(69,177,255,.8), 0 2px 5px rgba(0,0,0,.7);
  font-variant-numeric: tabular-nums;
}
.mfa-amt .crystal-ico { width: .9em; height: 1em; }
.mfa-bottle {
  --bottle: polygon(45% 0%,55% 0%,55% 7%,62% 9%,62% 13%,57% 14%,57% 25%,60% 27%,68% 31%,80% 39%,90% 48%,96% 58%,99% 68%,100% 78%,99% 88%,94% 96%,85% 99%,74% 100%,26% 100%,15% 99%,6% 96%,1% 88%,0% 78%,1% 68%,4% 58%,10% 48%,20% 39%,32% 31%,40% 27%,43% 25%,43% 14%,38% 13%,38% 9%,45% 7%);
  --tier-glow-color: #45b1ff;   /* mana = default BLUE */
  position: relative; display: block; width: 158px; aspect-ratio: .62; margin-top: 2px;
  -webkit-clip-path: var(--bottle); clip-path: var(--bottle);
  filter: drop-shadow(0 8px 18px rgba(0,0,0,.55)) drop-shadow(0 0 16px rgba(69,177,255,.65));
}
.mfa-bottle .ct-glass {
  position: absolute; inset: 0; z-index: 1; overflow: hidden; -webkit-clip-path: var(--bottle); clip-path: var(--bottle);
  background: linear-gradient(180deg, rgba(34,44,62,.92), rgba(9,13,21,.96));
}
.mfa-bottle .ct-water {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 1; height: var(--fill, 0%);
  background: linear-gradient(180deg, color-mix(in srgb, var(--tier-glow-color) 80%, #fff 20%), color-mix(in srgb, var(--tier-glow-color) 86%, #000 14%));
  opacity: .96; transition: height 1.4s cubic-bezier(.3,.85,.35,1);
}
.mfa-bottle .ct-wave {
  position: absolute; left: 0; right: 0; top: -9px; height: 16px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 20' preserveAspectRatio='none'%3E%3Cpath d='M0 10 Q 25 0 50 10 T 100 10 V20 H0 Z' fill='%23ffffff' fill-opacity='.5'/%3E%3C/svg%3E") repeat-x;
  background-size: 56px 100%; animation: ctWaveSlide 2.6s linear infinite;
}
.mfa-bottle .ct-wave.ct-wave2 { top: -7px; opacity: .55; background-size: 44px 100%; }
.mfa-bottle .ct-cork {
  position: absolute; top: -1px; left: 45%; width: 10%; height: 9%; z-index: 4;
  background: linear-gradient(180deg, #e7c184, #b8854a 55%, #8a5e2a); border-radius: 4px 4px 2px 2px;
  box-shadow: inset 0 1px 0 rgba(255,245,215,.7), inset 0 -2px 3px rgba(0,0,0,.45);
}
.cc-expand-btn {
  display: block; margin: 12px auto 2px; padding: 7px 20px;
  font: 700 12px/1 system-ui, sans-serif; letter-spacing: .05em;
  color: #cfe0ff; background: rgba(120,200,255,.08);
  border: 1px solid rgba(120,200,255,.30); border-radius: 999px;
  cursor: pointer; transition: background .15s ease, border-color .15s ease;
}
.cc-expand-btn:hover { background: rgba(120,200,255,.16); border-color: rgba(120,200,255,.5); }
/* Crafting page = like the rip page: NO opaque panel on top of the background, so
   the blue apothecary animations show through. Controls + wheel + cards float on it. */
#shopPanel.gallery-panel {
  background: none !important; border: none !important; box-shadow: none !important;
  animation: none !important; padding: 4px 10px 0 !important;
}
.crafting-collection { border-top-color: rgba(120,200,255,.22) !important; }
.shop-cols { display: flex; gap: 24px; align-items: stretch; justify-content: center; }
.shop-col { min-width: 0; text-align: center; position: relative; }
/* Buying now lives in the top CTA; the crafting controls take the full panel.
   Wide enough for the rarity cards to read like the front-page chase cards. */
.shop-col.shop-craft { flex: 1 1 auto; max-width: 920px; margin: 0 auto; }
.shop-col .buy-packs { margin: 0 auto; }
.shop-col .craft-balance { margin-bottom: 12px; }
@media (max-width: 620px) {
  .shop-cols { flex-direction: column; gap: 18px; }
}

/* Crafting header: title left, the player's mana on the right — one clean row. */
.craft-head-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 4px; }
.craft-head-row .craft-title { margin: 0; font-size: 18px; }
.craft-mana {
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
  font: 800 14px/1 system-ui, sans-serif; color: #e6f4ff;
  padding: 5px 11px; border-radius: 999px;
  background: rgba(8,22,38,.6); border: 1px solid rgba(69,177,255,.4);
}
.craft-mana .ico-crystal { width: 20px; height: 20px; }
.craft-mana b { color: #fff; }

/* Rarity options — each is an INFO CARD in its rarity's colour: name + a short
   pitch + estimated PSA + cost. The rarer the tier, the more illustrious it is
   (escalating glow / shine / holographic shimmer) to pull players toward it. */
/* All four rarities on ONE row, same dimensions as the gallery collection cards
   (~168px wide, standard 2.5:3.5 aspect). Capped + centred; 2-up on phones. */
.shop-craft .craft-tier-list { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin: 4px auto 8px; max-width: 720px; justify-content: center; }
/* (The standalone mana potion bar was removed — each rarity card's "YOU" line now
   shows the player's mana, and the userbar shows the count.) */
/* Disenchant payoff: every mana readout (potion bar + userbar pill) gleams + pulses
   distinctly for a few seconds (JS adds .mana-shine, removes it after ~3.4s). */
.mana-shine { position: relative; isolation: isolate; animation: manaShinePulse 1.1s ease-in-out 3; }
.mana-shine::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; z-index: 5;
  background: linear-gradient(115deg, transparent 32%, rgba(220,245,255,.7) 48%, transparent 64%);
  background-size: 260% 100%; animation: manaSheen 1.1s ease-in-out 3;
}
@keyframes manaShinePulse {
  0%, 100% { filter: none; box-shadow: 0 0 0 rgba(69,177,255,0); }
  35%      { filter: brightness(1.28) saturate(1.25); box-shadow: 0 0 20px rgba(120,225,255,.95), 0 0 42px rgba(69,177,255,.6); }
}
@keyframes manaSheen { from { background-position: 170% 0; } to { background-position: -70% 0; } }
@media (prefers-reduced-motion: reduce) { .mana-shine, .mana-shine::after { animation: none !important; } }
/* Each rarity is a MANA POTION BOTTLE (cork + neck + round bulb), not a rectangle.
   The bottle silhouette is a shared clip-path; the coloured glass rim is the button's
   own background showing through a slightly-inset glass layer. The liquid (.ct-water)
   fills the bulb bottom-up to --fill, exactly as before. */
/* The button is a column: the BOTTLE (clipped) on top, the rarity NAME label below it
   (outside the clip, so long names never get cropped by the glass). */
.shop-craft .craft-tier {
  position: relative; display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 0 !important; border: 0; background: transparent; cursor: pointer; isolation: isolate;
  /* No rectangular card glow behind the potion — the old .craft-tier card style set an
     inset box-shadow that filled the button rect (reading as a glowing "card"). We want
     JUST the potion silhouette, so kill the box-shadow and seat it with a drop-shadow
     FILTER (which follows the bottle shape) instead. */
  box-shadow: none;
  filter: drop-shadow(0 5px 9px rgba(0,0,0,.55));
  transition: transform .12s ease, filter .15s ease;
}
/* Classic RPG mana-potion silhouette: a rounded CORK nub, a flared LIP collar, a slim
   NECK, and a big round BULB body. The cork region (very top) is tinted by .ct-cork. */
.shop-craft .craft-tier .ct-bottle {
  --bottle: polygon(
    45% 0%, 55% 0%, 55% 7%,
    62% 9%, 62% 13%,
    57% 14%, 57% 25%,
    60% 27%, 68% 31%, 80% 39%, 90% 48%, 96% 58%, 99% 68%, 100% 78%,
    99% 88%, 94% 96%, 85% 99%, 74% 100%,
    26% 100%, 15% 99%, 6% 96%, 1% 88%, 0% 78%, 1% 68%,
    4% 58%, 10% 48%, 20% 39%, 32% 31%, 40% 27%,
    43% 25%, 43% 14%,
    38% 13%, 38% 9%,
    45% 7%
  );
  position: relative; display: block; width: 100%; aspect-ratio: 0.62;
  -webkit-clip-path: var(--bottle); clip-path: var(--bottle);
}
/* Coloured glass RIM: fills the whole bottle silhouette in the tier colour. */
.shop-craft .craft-tier .ct-bottle::before {
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  -webkit-clip-path: var(--bottle); clip-path: var(--bottle);
  background: linear-gradient(180deg, color-mix(in srgb, var(--tier-glow-color, #888) 90%, #fff 10%), color-mix(in srgb, var(--tier-glow-color, #888) 70%, #000 30%));
}
/* The GLASS interior is INSET a hair inside the silhouette, so the rarity-coloured rim
   (.ct-bottle::before) peeks out all around as a THIN rarity OUTLINE. The glass itself is
   plain dark (the liquid is always blue mana) — the bottle's rarity reads from its outline
   alone. */
.shop-craft .craft-tier .ct-glass {
  position: absolute; inset: 2.5px; z-index: 1; overflow: hidden; pointer-events: none;
  -webkit-clip-path: var(--bottle); clip-path: var(--bottle);
  background: linear-gradient(180deg, rgba(34,44,62,.92), rgba(9,13,21,.96));
  /* Own layer so the clip-path reliably contains the liquid (no square flash). */
  transform: translateZ(0); -webkit-transform: translateZ(0);
}
/* ── Varied bottle SHAPES + SIZES per rarity, on a clean grid ─────────────────────────
   Every bottle shares ONE uniform cell HEIGHT so their bases sit on a single shelf and
   the rarity names line up in a neat row (grid stays as clean as the rip page); only the
   silhouette + width vary by tier — a squat round flask (common) → a classic potion →
   a tall slim bottle → a conical Erlenmeyer (legendary). Width = height × aspect-ratio,
   centred in the 1fr column. Per-lvl rules out-specify the base .ct-bottle above. */
.shop-craft .craft-tier .ct-bottle { width: auto; height: 118px; margin: 0 auto; }
.shop-craft .craft-tier.ct-lvl-1 .ct-bottle {   /* COMMON — squat round flask */
  aspect-ratio: 0.86;
  --bottle: polygon(43% 0%,57% 0%,57% 9%, 63% 11%,63% 16%,57% 18%, 55% 28%,
    69% 32%,83% 41%,93% 53%,99% 67%,100% 79%, 98% 90%,89% 97%,77% 100%,
    23% 100%,11% 97%,2% 90%,0% 79%, 1% 67%,7% 53%,17% 41%,31% 32%,
    45% 28%,45% 18%,37% 16%,37% 11%,43% 9%);
}
.shop-craft .craft-tier.ct-lvl-2 .ct-bottle {   /* UNCOMMON — classic potion (base shape) */
  aspect-ratio: 0.62;
}
.shop-craft .craft-tier.ct-lvl-3 .ct-bottle {   /* RARE rank — tall slim bottle (canonical) */
  aspect-ratio: 0.46;
  --bottle: polygon(44% 0%,56% 0%,56% 6%, 61% 8%,61% 12%,57% 13%, 57% 33%,
    66% 39%,74% 49%,79% 61%,80% 74%,80% 86%, 76% 95%,65% 99%,50% 100%,
    35% 99%,24% 95%,20% 86%,20% 74%,21% 61%,26% 49%,34% 39%,
    43% 33%,43% 13%,39% 12%,39% 8%,44% 6%);
}
.shop-craft .craft-tier.ct-lvl-4 .ct-bottle {   /* FOIL CHASE rank — conical Erlenmeyer (canonical) */
  aspect-ratio: 0.72;
  --bottle: polygon(44% 0%,56% 0%,56% 7%, 60% 9%,60% 13%,56% 14%, 56% 33%,
    58% 37%, 87% 90%,92% 96%,85% 100%, 15% 100%,8% 96%,13% 90%,
    42% 37%,44% 33%, 44% 14%,40% 13%,40% 9%,44% 7%);
}
/* ── Each rarity keeps a DISTINCT bottle shape, targeted by tier NAME so it holds no matter
   which ct-lvl the runtime cost-rank assigns. Four unique silhouettes:
     COMMON = squat round flask · UNCOMMON = classic potion · RARE = tall slim bottle ·
     FOIL CHASE = conical Erlenmeyer. ─────────────────────────────────────────────────── */
.shop-craft .craft-tier.tier-common .ct-bottle {        /* icon 3 — tall slim bottle */
  aspect-ratio: 0.46;
  --bottle: polygon(44% 0%,56% 0%,56% 6%, 61% 8%,61% 12%,57% 13%, 57% 33%,
    66% 39%,74% 49%,79% 61%,80% 74%,80% 86%, 76% 95%,65% 99%,50% 100%,
    35% 99%,24% 95%,20% 86%,20% 74%,21% 61%,26% 49%,34% 39%,
    43% 33%,43% 13%,39% 12%,39% 8%,44% 6%);
}
.shop-craft .craft-tier.tier-uncommon .ct-bottle {      /* icon 4 — conical Erlenmeyer */
  aspect-ratio: 0.72;
  --bottle: polygon(44% 0%,56% 0%,56% 7%, 60% 9%,60% 13%,56% 14%, 56% 33%,
    58% 37%, 87% 90%,92% 96%,85% 100%, 15% 100%,8% 96%,13% 90%,
    42% 37%,44% 33%, 44% 14%,40% 13%,40% 9%,44% 7%);
}
.shop-craft .craft-tier.tier-rare .ct-bottle {          /* icon 2 — classic potion */
  aspect-ratio: 0.62;
  --bottle: polygon(45% 0%, 55% 0%, 55% 7%, 62% 9%, 62% 13%, 57% 14%, 57% 25%,
    60% 27%, 68% 31%, 80% 39%, 90% 48%, 96% 58%, 99% 68%, 100% 78%,
    99% 88%, 94% 96%, 85% 99%, 74% 100%, 26% 100%, 15% 99%, 6% 96%, 1% 88%, 0% 78%, 1% 68%,
    4% 58%, 10% 48%, 20% 39%, 32% 31%, 40% 27%, 43% 25%, 43% 14%, 38% 13%, 38% 9%, 45% 7%);
}
.shop-craft .craft-tier.tier-foil-chase .ct-bottle {    /* icon 1 — squat round flask */
  aspect-ratio: 0.86;
  --bottle: polygon(43% 0%,57% 0%,57% 9%, 63% 11%,63% 16%,57% 18%, 55% 28%,
    69% 32%,83% 41%,93% 53%,99% 67%,100% 79%, 98% 90%,89% 97%,77% 100%,
    23% 100%,11% 97%,2% 90%,0% 79%, 1% 67%,7% 53%,17% 41%,31% 32%,
    45% 28%,45% 18%,37% 16%,37% 11%,43% 9%);
}
/* The narrow-necked shapes (tall slim + Erlenmeyer) now sit on COMMON + UNCOMMON — drop the
   mana number lower into the wide bulb and shrink it so it fits inside. */
.shop-craft .craft-tier.tier-common .ct-cost,
.shop-craft .craft-tier.tier-uncommon .ct-cost { top: 72%; font-size: clamp(12px, 3vw, 18px); }
/* The mana COST sits centred in the bulb; the rarity NAME is the label under the bottle.
   A dark stroke keeps the white text legible over ANY liquid colour (incl. white). */
.shop-craft .craft-tier .ct-cost {
  position: absolute; left: 0; right: 0; top: 62%; z-index: 5;
  display: inline-flex; align-items: center; justify-content: center; gap: 3px;
  font: 900 clamp(14px, 3.6vw, 22px)/1 system-ui, sans-serif; color: #fff;
  white-space: nowrap;   /* the mana amount stays on one line inside the bulb */
  -webkit-text-stroke: .6px rgba(0,0,0,.55);
  text-shadow: 0 1px 3px rgba(0,0,0,.95), 0 0 2px rgba(0,0,0,.85);
}
/* The slim RARE bottle + the tapered top of the conical LEGENDARY are narrow where the
   cost sits — shrink the number (and drop it lower on the flask) so it fits inside. */
/* lvl-3 and lvl-4 now use the uncommon/common bottle shapes, so their mana COST sits at the
   base position (no shape-specific offset). */
.shop-craft .craft-tier .ct-name {
  width: 100%; max-width: 100%;
  font: 900 clamp(11px, 2.9vw, 16px)/1.1 system-ui, sans-serif; text-transform: uppercase; letter-spacing: .03em;
  color: #fff; text-align: center; text-shadow: 0 0 12px var(--tier-glow-color, transparent), 0 1px 3px rgba(0,0,0,.85);
}
/* Glossy glass gleam — a bright curved highlight down the left of the bulb (the
   shorthand "video-game potion" cue). */
.shop-craft .craft-tier .ct-shine {
  position: absolute; left: 14%; top: 34%; width: 18%; height: 44%; z-index: 3; pointer-events: none;
  border-radius: 50% / 60%; transform: rotate(-10deg);
  background: linear-gradient(180deg, rgba(255,255,255,.72), rgba(255,255,255,.05));
  filter: blur(1.5px); opacity: .7;
}
/* Cork stopper — sits on the lip at the very top of the silhouette. */
.shop-craft .craft-tier .ct-cork {
  position: absolute; top: -1px; left: 45%; width: 10%; height: 9%; z-index: 4; pointer-events: none;
  background: linear-gradient(180deg, #e7c184, #b8854a 55%, #8a5e2a);
  box-shadow: inset 0 1px 0 rgba(255,245,215,.7), inset 0 -2px 3px rgba(0,0,0,.45);
  border-radius: 4px 4px 2px 2px;
}
/* ── The LIQUID is always BLUE MANA (the same mana in every potion). Its LEVEL is the
   recipe's craft cost on the shared 0→MANA_MAX scale, so the bottle shows "how much mana
   this recipe takes". The rarity shows only on the OUTLINE (.ct-bottle::before rim), not
   the liquid. ─────────────────────────────────────────────────────────────────────── */
/* The liquid is ONE full-glass element (holds the wavy SVG) TRANSLATED down to the fill line.
   Body + surface move as a single unit — no detached cap, no gap when the level changes.
   translateY(100% − --fill − 7%) puts the wave's ~y7 surface baseline exactly on the fill
   line; the crest rides a few % above, troughs a few % below — a true liquid surface. */
.shop-craft .craft-tier .ct-water,
.craft-mana-potion .cm-water {
  position: absolute; left: -1px; right: -1px; top: 0; bottom: 0; height: auto !important; z-index: 1;
  background: none !important; opacity: .95; pointer-events: none;
  -webkit-mask-image: none !important; mask-image: none !important;
  transform: translateY(calc(100% - var(--fill, 0%) - 7%));
  transition: transform var(--surf-ease, .55s) cubic-bezier(.3,.85,.35,1);
}
.ct-liquid-svg { display: block; width: 100%; height: 100%; overflow: visible; }
.ct-liquid-svg path { fill: #45b1ff; }
/* Empty potion → slide the liquid fully out of view. */
.shop-craft .craft-tier[data-cost="0"] .ct-water,
.craft-mana-potion.cm-empty .cm-water { transform: translateY(105%); }
/* Ready (affordable) potions brighten their liquid. */
.shop-craft .craft-tier.ct-affordable .ct-water,
.craft-mana-potion.cm-afford .cm-water { opacity: 1; filter: brightness(1.1) saturate(1.1); }
@media (prefers-reduced-motion: reduce) { .ct-liquid-svg animate { display: none; } }
/* No wave BAND anymore — it read as a line at the surface. The soft-faded liquid top IS
   the surface treatment now, so nothing draws a horizontal edge at the water level. */
.shop-craft .craft-tier .ct-wave,
.craft-mana-potion .ct-wave { display: none !important; }
.shop-craft .craft-tier .ct-water::after,
.craft-mana-potion .cm-water::after { display: none !important; }
/* ── The CONSUME LINE: measured DOWN from the liquid surface by this rarity's cost. For
   a potion you can afford it marks where your mana will DROP TO after crafting (the .ct-
   drain band above it = what gets spent); when you can't afford it yet it instead marks
   the threshold (= "fill to here to afford"). ──────────────────────────────────────── */
.shop-craft .craft-tier .ct-mark {
  position: absolute; left: 6%; right: 6%; bottom: var(--mark, 0%); z-index: 3; height: 0; pointer-events: none;
  border-top: 2px dashed color-mix(in srgb, var(--tier-glow-color, #fff) 55%, #fff);
  opacity: .55;
  transition: bottom .6s cubic-bezier(.3,.85,.35,1);
}
.shop-craft .craft-tier.ct-affordable .ct-mark {
  opacity: 1; border-top-style: solid; border-top-color: #fff;
  filter: drop-shadow(0 0 5px var(--tier-glow-color, #fff)) drop-shadow(0 0 9px var(--tier-glow-color, #fff));
}
/* The chunk of liquid that will be SPENT on this craft — the band between the consume
   line and the current surface. Hatched + dimmed so it reads as "this empties out",
   leaving the level at the line. Only meaningful for potions you can afford. */
.shop-craft .craft-tier .ct-drain {
  position: absolute; left: 0; right: 0; bottom: var(--mark, 0%); z-index: 2; pointer-events: none; display: none;
  height: max(0px, calc(var(--fill, 0%) - var(--mark, 0%)));
  /* A gentle dim + faint hatching so the consumed zone reads as "spent" WITHOUT hiding
     the rarity colour underneath. */
  background:
    repeating-linear-gradient(135deg, rgba(255,255,255,.07) 0 5px, rgba(8,12,20,.05) 5px 11px),
    linear-gradient(180deg, rgba(6,10,18,.26), rgba(6,10,18,.10));
  transition: bottom .6s cubic-bezier(.3,.85,.35,1), height .6s cubic-bezier(.3,.85,.35,1);
}
.shop-craft .craft-tier.ct-affordable .ct-drain { display: block; }
.shop-craft .craft-tier .ct-cost .crystal-ico { width: 1em; height: 1em; color: var(--crystal-2, #8fe3ff); }
/* ── READY (mana over the cost line): keep the rarity colour but make the whole potion
   glow brightly + the liquid brighten + the cost line light up — no green, so each
   potion always reads as its own rarity. ──────────────────────────────────────────── */
/* Affordability is cued by the BRIGHTER water + rim only — NOT a glow halo. The
   tier-coloured glow is reserved for the SELECTED bottle (see .ct-selected / the
   per-rarity selected pulse below), so an affordable-but-unpicked bottle stays calm. */
.shop-craft .craft-tier.ct-affordable .ct-water { opacity: 1; filter: brightness(1.12) saturate(1.12); }
.shop-craft .craft-tier.ct-affordable .ct-bottle::before { filter: brightness(1.2) saturate(1.1); }
/* ── Unlock burst: the instant the potion tops off. ────────────────────────────── */
.shop-craft .craft-tier.ct-unlock .ct-water { animation: ctWaterBurst .7s ease-out; }
@keyframes ctWaterBurst { 0% { filter: brightness(1); } 30% { filter: brightness(2) saturate(1.4); } 100% { filter: brightness(1); } }
/* The surface line: an SVG wave poking just above the fill. The wave TILES
   seamlessly (its left/right edges meet at the same height + slope), and we scroll
   the BACKGROUND POSITION by exactly one tile width per loop — so the repeat is
   perfectly continuous with no jump/skip (the element itself never moves, so there
   are no edges to hitch). Two layers at different speeds/directions = a lively,
   splashy surface. */
.shop-craft .craft-tier .ct-wave {
  position: absolute; left: 0; right: 0; top: -5px; height: 8px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 20' preserveAspectRatio='none'%3E%3Cpath d='M0 10 Q 25 0 50 10 T 100 10 V20 H0 Z' fill='%23ffffff' fill-opacity='.5'/%3E%3C/svg%3E") repeat-x;
  background-size: 56px 100%;
  /* NO will-change — a promoted wave layer escapes the bottle clip-path (square flash). */
  animation: ctWaveSlide 2.6s linear infinite;
}
.shop-craft .craft-tier .ct-wave.ct-wave2 {
  top: -3px; opacity: .55;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 20' preserveAspectRatio='none'%3E%3Cpath d='M0 10 Q 25 0 50 10 T 100 10 V20 H0 Z' fill='%23ffffff' fill-opacity='.35'/%3E%3C/svg%3E");
  background-size: 44px 100%;
  animation: ctWaveSlide2 3.4s linear infinite;
}
/* Scroll by exactly one tile (= background-size width) → the end frame is pixel-
   identical to the start, so the loop is seamless. */
@keyframes ctWaveSlide  { from { background-position: 0 0; } to { background-position: -56px 0; } }
@keyframes ctWaveSlide2 { from { background-position: 0 0; } to { background-position: 44px 0; } }
@media (prefers-reduced-motion: reduce) {
  .shop-craft .craft-tier .ct-water,
  .shop-craft .craft-tier .ct-wave { animation: none !important; }
}
/* Mobile: drop the animated water surface entirely. Its will-change layer gets
   promoted on mobile GPUs and escapes the bottle's clip-path — the liquid's
   rectangular bounds then show as a faint "card" behind the potion, and the
   scrolling wave reads as a flickering horizontal line. A flat, stable liquid top
   is clean and never breaks the silhouette. (Also drop will-change on the liquid so
   nothing inside the clip is composited out of it.) */
@media (max-width: 640px) {
  .shop-craft .craft-tier .ct-wave,
  .craft-mana-potion .ct-wave,
  .mfa-bottle .ct-wave { display: none !important; }
  .shop-craft .craft-tier .ct-water,
  .craft-mana-potion .cm-water,
  .mfa-bottle .ct-water { will-change: auto; }
}
.shop-craft .craft-tier:hover:not(:disabled) {
  transform: translateY(-3px);
  box-shadow: none;   /* no rectangular hover halo — the drop-shadow glow follows the bottle */
  filter: drop-shadow(0 10px 16px rgba(0,0,0,.55)) drop-shadow(0 0 14px color-mix(in srgb, var(--tier-glow-color, #888) 80%, transparent));
}
.shop-craft .craft-tier:disabled { opacity: .55; filter: grayscale(.55) brightness(.82); cursor: not-allowed; }
/* Selected: no rectangular outline (the bottle is clipped) — brighten the glass rim and
   wrap the silhouette in a strong tier-coloured glow instead. */
.shop-craft .craft-tier.ct-selected {
  transform: translateY(-2px) scale(1.04);
  filter: drop-shadow(0 6px 12px rgba(0,0,0,.55)) drop-shadow(0 0 16px var(--tier-glow-color, #fff)) drop-shadow(0 0 7px var(--tier-glow-color, #fff));
}
.shop-craft .craft-tier.ct-selected .ct-bottle::before { filter: brightness(1.22); }
.shop-craft .craft-tier.ct-selected .ct-name { text-shadow: 0 0 14px var(--tier-glow-color, #fff), 0 1px 3px rgba(0,0,0,.85); }
.shop-craft .craft-tier .ct-sold {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%) rotate(-12deg); z-index: 6;
  padding: 3px 10px; border-radius: 4px;
  background: rgba(180,30,30,.92); color: #fff; font: 900 11px/1 system-ui, sans-serif;
  letter-spacing: .08em; box-shadow: 0 2px 8px rgba(0,0,0,.6);
}

/* ── Glow ONLY when SELECTED — escalating by rarity rank ─────────────────────────
   Unselected bottles show just the neutral drop-shadow (base .craft-tier filter) — NO
   tier glow at rest. The moment a bottle is selected it lights up with a pulsing
   tier-coloured glow whose strength escalates with rarity. box-shadow is clipped by the
   bottle silhouette, so the glow is a drop-shadow FILTER (which follows the shape).
   lvl 1 (common) has no pulse — it relies on the steady .ct-selected glow above. */
.shop-craft .craft-tier.ct-lvl-2.ct-selected { animation: ctGlowSoft 3.2s ease-in-out infinite; }
@keyframes ctGlowSoft {
  0%,100% { filter: drop-shadow(0 5px 9px rgba(0,0,0,.55)) drop-shadow(0 0 10px color-mix(in srgb, var(--tier-glow-color,#888) 70%, transparent)); }
  50%     { filter: drop-shadow(0 5px 9px rgba(0,0,0,.55)) drop-shadow(0 0 18px color-mix(in srgb, var(--tier-glow-color,#888) 100%, transparent)); }
}
.shop-craft .craft-tier.ct-lvl-3.ct-selected { animation: ctGlowMed 2.6s ease-in-out infinite; }
@keyframes ctGlowMed {
  0%,100% { filter: drop-shadow(0 5px 9px rgba(0,0,0,.55)) drop-shadow(0 0 12px color-mix(in srgb, var(--tier-glow-color,#888) 80%, transparent)); }
  50%     { filter: drop-shadow(0 5px 9px rgba(0,0,0,.55)) drop-shadow(0 0 22px color-mix(in srgb, var(--tier-glow-color,#888) 100%, transparent)); }
}
.shop-craft .craft-tier.ct-lvl-4.ct-selected { animation: ctGlowStrong 2.1s ease-in-out infinite; }
@keyframes ctGlowStrong {
  0%,100% { filter: drop-shadow(0 6px 11px rgba(0,0,0,.6)) drop-shadow(0 0 14px color-mix(in srgb, var(--tier-glow-color,#fff) 85%, transparent)); }
  50%     { filter: drop-shadow(0 6px 11px rgba(0,0,0,.6)) drop-shadow(0 0 26px color-mix(in srgb, var(--tier-glow-color,#fff) 100%, transparent)); }
}
.shop-craft .craft-tier.ct-lvl-4.ct-selected .ct-name { animation: ctNamePulse 2.1s ease-in-out infinite; }
@keyframes ctNamePulse { 0%,100% { text-shadow: 0 0 12px var(--tier-glow-color), 0 1px 2px rgba(0,0,0,.7); } 50% { text-shadow: 0 0 22px var(--tier-glow-color), 0 0 6px #fff, 0 1px 2px rgba(0,0,0,.7); } }
@media (prefers-reduced-motion: reduce) {
  .shop-craft .craft-tier, .shop-craft .craft-tier .ct-shine, .shop-craft .craft-tier .ct-name { animation: none !important; }
}
/* Mobile: keep all 4 rarities in ONE horizontal row (tighter gap + padding so they
   fit) so the craft wheel below stays visible without scrolling. */
@media (max-width: 560px) {
  .shop-craft .craft-tier-list { grid-template-columns: repeat(4, 1fr); gap: 7px; max-width: 100%; }
  /* The NAME label sits UNDER the bottle now (outside the clip) so it can stay readable;
     the COST is the hero centred in the bulb. */
  .shop-craft .craft-tier { gap: 4px; }
  .shop-craft .craft-tier .ct-name { font-size: 9.5px; letter-spacing: 0; }
  /* Smaller so the mana amount (no commas) fits inside the narrow bulb without
     overflowing when all 4 bottles share one row. */
  .shop-craft .craft-tier .ct-cost { font-size: 12px; gap: 2px; }
}

/* ════════════════════════════════════════════════════════════════════════════
   CRAFT STAGE — the SAME full-bleed two-column grid the rip page uses. LEFT
   column: the four rarity RECIPE potions in ONE row, centred over the CRAFT
   cabinet (exactly like the chase cards over the JACKPOT). RIGHT column: your
   OWN big standalone mana potion — how much mana you have vs. what's required.
   ════════════════════════════════════════════════════════════════════════════ */
/* Base (narrow / mobile): a simple centred stack, with the WHEEL right under the
   recipes (order 2) so it sits at the SAME spot as the rip wheel — the big YOUR-MANA
   potion drops BELOW the wheel (order 3) instead of shoving the wheel off-screen. */
.craft-stage { display: flex; flex-direction: column; align-items: center; gap: 14px; }
.craft-recipes-col { order: 1; width: 100%; display: flex; flex-direction: column; align-items: center; }
.craft-stage > #craftCarousel { order: 2; margin-top: 74px; }   /* drop it to the rip wheel's Y */
.craft-mana-col { order: 3; }
.craft-recipes-col .craft-tier-list { grid-template-columns: repeat(4, 1fr); gap: 12px; margin: 4px auto 0; max-width: 560px; width: 100%; }
.craft-mana-col { display: flex; justify-content: center; align-items: flex-start; }
/* Recipes are static rarity potions — the mana level line + drain band belong only
   to the YOUR-MANA potion, so hide them on the recipe bottles. */
.craft-recipes-col .craft-tier .ct-mark,
.craft-recipes-col .craft-tier .ct-drain { display: none !important; }
/* Drop the rarity NAME under each recipe bottle — the bottle shape + cost already read the
   rarity, and hiding the label frees vertical room so the bottles can stand taller. */
.craft-recipes-col .craft-tier .ct-name { display: none !important; }
/* ── PINNED wheel — the wheel ALWAYS lands at a fixed Y (never pushed by the mana potion) ──
   Row 1 reserves --craft-above-h for the recipes + YOUR-MANA potion (they SHARE that fixed
   space and overflow it rather than growing it); row 2 is the wheel, so it sits at exactly
   --craft-above-h on every breakpoint — matching the rip wheel. JS (alignCraftWheel) refines
   --craft-above-h to the rip wheel's measured Y; until then the per-breakpoint CSS default
   below holds, so the wheel is pinned even before any measurement (no mana-push fallback). */
/* Three rows now: (1) the rarity potions + YOUR-MANA potion share the reserved zone MINUS
   the cabinet row, (2) the SELECT/CRAFT cabinet on its own full-width bar row, (3) the
   wheel. Rows 1+2 still sum to --craft-above-h, so the wheel stays pinned at the rip-wheel Y. */
/* SAME LAYOUT AS THE RIP PAGE, measured live: JS (alignCraftWheel) sets --craft-cab-top to
   the USERS ONLINE cabinet's exact Y and --craft-above-h to the rip-wheel Y, both relative to
   the craft stage. So row 1 (potions, where the chase cards sit) runs to the cabinet, row 2
   holds the CRAFT cabinet AT the USERS-ONLINE spot (top-aligned) with the same gap down to the
   wheel, and row 3 (the wheel) lands on the rip wheel. Nothing to hand-tune — it just matches. */
.craft-stage { --craft-cab-top: 150px;
  grid-template-rows: var(--craft-cab-top, 150px) minmax(63px, calc(var(--craft-above-h, 280px) - var(--craft-cab-top, 150px))) auto !important;
  row-gap: 0 !important; align-items: stretch !important; }
/* Recipes col fills the reserved row and pushes the CRAFT-SELECT cabinet to the BOTTOM
   (right above the wheel) — exactly like the JACKPOT sits just above the rip wheel. */
.craft-stage .craft-recipes-col { grid-row: 1 !important; min-height: 0; align-self: stretch !important; justify-content: flex-end !important; gap: 10px;
  /* Reserve the zone for layout, but DON'T clip: the potions are sized to fit it, so the
     only thing that would ever cross the edge is a selected potion's glow/border — and
     clipping cropped exactly that. Overflow visible lets the glow + selection ring show in
     full. (The bottles fit within --craft-above-h, so nothing solid reaches the userbar.) */
  max-height: var(--craft-cab-top, 150px); overflow: visible; }
/* Mana col fills the reserved row; its bottle is capped to that height so it can't grow
   the row / overlap the wheel, and sits low (near the wheel) to mirror the recipes side. */
.craft-stage .craft-mana-col { grid-row: 1 !important; min-height: 0; align-self: stretch !important; justify-content: flex-end !important; max-height: var(--craft-cab-top, 150px); overflow: visible; }
/* The SELECT/CRAFT cabinet's own full-width bar row (row 2): button hugs the LEFT, the
   glowing purple ::after bar grows from its middle to the right screen edge (rip-page look). */
.craft-stage > .craft-divider { grid-row: 2 !important; grid-column: 1 / -1; align-self: start !important; justify-self: center; width: 100%; max-width: 720px; margin: 0 auto !important; z-index: 3;
  /* JS (alignCraftWheel) nudges this so the cabinet's center-X matches the USERS ONLINE cabinet exactly. */
  transform: translateX(var(--craft-cab-dx, 0px)); }

/* ══ Seamless view morph (View Transitions API) ═══════════════════════════════════════
   The shared arcade CABINET gets a stable name, so when the view swaps the browser MORPHS
   it between its RIP / CRAFT / GALLERY positions (they're already pinned to the same spot, so
   it reads as one element that stays put + recolors). The rest of the page HARD-CUTS instead
   of cross-fading: a cross-fade briefly shows BOTH the old and new page at partial opacity,
   which reads as a flicker/double-image on rapid RIP↔CRAFT toggles (and re-decodes card art).
   A hard cut swaps instantly with no ghosting. The mascot is NO LONGER snapshotted (it's a
   fixed live WebGL layer in the same spot on every view — capturing + cross-fading it was a
   flicker source); it simply stays put across the swap. GPU-composited; costs nothing at rest. */
#onlineMeter, #craftSpinBtn, .gallery-cabinet { view-transition-name: game-cabinet; }
::view-transition-group(game-cabinet) { animation-duration: .11s; animation-timing-function: cubic-bezier(.2,.85,.2,1); }
/* Hard cut — no cross-fade, so no double-image flicker while rapidly switching. */
::view-transition-old(root) { animation: none !important; opacity: 0 !important; }
::view-transition-new(root) { animation: none !important; opacity: 1 !important; }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*), ::view-transition-image-pair(*),
  ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
}
/* Height = the rip wheel's measured band height (set by alignCraftWheel) so the two
   wheels are the SAME size; falls back to the old formula until JS measures. */
.craft-stage > #craftCarousel { grid-row: 3 !important; margin-top: 0 !important; align-self: start;
  height: var(--craft-wheel-h, max(320px, calc(100dvh - 483px))) !important; }
/* Desktop: the SAME 1180px-max, centred, two-column grid the rip page's .below-stage-row
   uses — recipes + CRAFT cabinet in the LEFT column (so the cabinet lands at the EXACT X
   as the rip page's JACKPOT), the big YOUR-MANA potion in the RIGHT column, hugging its
   left edge so it sits right beside the recipes. Keeps rip ↔ craft aligned when toggling. */
@media (min-width: 861px) {
  .craft-stage {
    --craft-above-h: 236px;   /* default reserved height until JS measures the rip wheel */
    width: 100vw; margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw);
    display: grid; grid-template-columns: 1fr 1fr; column-gap: 28px; box-sizing: border-box;
    align-items: center; padding-inline: max(24px, calc(50vw - 590px));
  }
  /* Potions ride at the TOP of the reserved zone (like the rip page's chase cards), so
     raising the cabinet doesn't drag them up into the userbar — the cabinet lifts into the
     gap that opens below them. */
  .craft-stage .craft-recipes-col { grid-column: 1; grid-row: 1; justify-content: flex-start !important; align-self: start !important; }
  /* Mana potion in the RIGHT column, hugging its LEFT edge, TOP-aligned to match the potions.
     align-self STRETCH (fill row 1, a DEFINITE height) so the base .cm-bottle max-height:100%
     cap holds and the bottle can't spill below its column. */
  .craft-stage .craft-mana-col { grid-column: 2; grid-row: 1; align-self: stretch !important; align-items: center; justify-content: flex-start !important; margin-left: -30px; }
  /* Wheel drops BELOW the columns AND the cabinet bar row (full width, row 3). */
  .craft-stage > #craftCarousel { grid-column: 1 / -1; grid-row: 3; margin-top: -24px; }
  /* Four rarity potions as a 2×2 grid (not a single row), centred in the left column, filling
     the chase-cards zone (row 1). Each bottle is ~half the zone height so the 2×2 always fits. */
  .craft-recipes-col .craft-tier-list { grid-template-columns: repeat(2, 1fr) !important; max-width: 300px; margin: 0 auto; gap: 10px; }
  .shop-craft .craft-tier .ct-bottle { height: min(calc((var(--craft-cab-top, 150px) - 16px) / 2), 128px) !important; }
  /* CRAFT cabinet lives in the LEFT column (under the potions), centred within that half
     with its flanking bars — exactly where the rip page's USERS ONLINE meter sits inside
     the chase-cards column. The 32px max-width trim insets it the same 16px the jackpot
     divider sits at inside its own column, so the bars land in the identical spot. */
  .craft-stage > .craft-divider { grid-column: 1; max-width: calc(100% - 32px); margin: 0 auto !important; }
}
/* Mobile/tablet: mirror the desktop split — the four rarity potions as a 2×2 grid on
   the LEFT, and your big mana potion standing in the space on the RIGHT (instead of a
   long single column). The wheel drops below both. */
@media (max-width: 860px) {
  .craft-stage {
    --craft-above-h: 200px;   /* fallback until JS measures the rip wheel Y */
    --craft-cab-top: 120px;   /* fallback until JS measures the USERS ONLINE cabinet Y */
    display: grid; grid-template-columns: 1fr 0.86fr; gap: 6px 10px;
    align-items: start; justify-items: center;     /* content rides at the TOP of its cell */
    /* Break the stage out to the full viewport width so the potions get more room AND the
       cabinet's purple bar can run to the right screen edge (like the rip page). */
    width: 100vw; margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw);
    padding-inline: 14px; box-sizing: border-box;
    /* Same grid model as the base rule (uses --craft-cab-top / --craft-above-h) — the cabinet
       lands at the measured USERS-ONLINE spot and the wheel at the measured rip-wheel Y. */
  }
  /* Potions + mana ride at the TOP of the reserved zone (align-self start), capped to it. */
  .craft-stage .craft-recipes-col { justify-content: flex-start !important; align-self: start !important;
    max-height: var(--craft-cab-top, 120px) !important; overflow: visible; }
  .craft-recipes-col { grid-column: 1; grid-row: 1; width: 100%; min-width: 0; }
  /* Mana column: horizontally centred, TOP-aligned (never vertically centred — that let the
     tall bottle spill off the top of the screen). align-self start hugs the top of row 1. */
  .craft-stage .craft-mana-col { grid-column: 2; grid-row: 1; min-width: 0; width: 100%;
    align-self: start !important; justify-content: center !important; align-items: flex-start !important; overflow: visible; }
  .craft-stage > #craftCarousel { grid-column: 1 / -1; grid-row: 3; margin-top: 0; }
  /* Four rarity potions stack 2×2, centred in the top-left. */
  .shop-craft .craft-recipes-col .craft-tier-list {
    grid-template-columns: repeat(2, 1fr); gap: 7px; max-width: 208px; margin: 0 auto;
  }
  /* Potions FILL the reserved zone (2 rows) instead of a tiny fixed size — each bottle is
     ~half the zone height, so they grow to use whatever room the rip-wheel Y leaves (capped
     at 82px so they never get comically large). No more tiny potions. */
  .shop-craft .craft-tier .ct-bottle {
    height: min(calc((var(--craft-cab-top, 120px) - 12px) / 2), 82px) !important; }
  /* Gentler select pop on mobile so a selected potion never scales past the clip edge. */
  .shop-craft .craft-tier.ct-selected { transform: translateY(-1px) scale(1.02); }
  /* YOUR MANA potion: drive it by HEIGHT (not width) so it can NEVER be cut off — the bottle
     height is capped to the reserved zone (minus a hair), width follows the 0.62 aspect. Also
     hard-capped at 116px so it stays "a lot smaller". */
  .craft-mana-potion { width: auto; max-width: 100%; }
  .shop-craft .craft-mana-potion .cm-bottle {
    height: clamp(88px, calc(var(--craft-cab-top, 120px) - 6px), 116px) !important;
    width: auto !important; max-width: 100% !important; margin: 0 auto; }
  .craft-mana-potion .cm-label { font-size: 11px; letter-spacing: .05em; text-align: center; }
}
/* Phones: SHORTEN the whole recipes+mana block so the craft carousel rides up into
   view. Two levers: (1) square rarity selectors instead of tall bottles, and (2) fold
   the YOUR-MANA label + now→after readout INSIDE the bottle (absolute overlay) so the
   mana column is just the bottle — no text stacked beneath it. */
@media (max-width: 640px) {
  /* Shorter bottles on phones so the 2×2 shelf stays compact (per-lvl aspect-ratios keep
     each shape; height drives the size now, not aspect-ratio). Sized so two rows + the
     CRAFT cabinet fit inside the reserved zone above the wheel — no overflow into the
     userbar (the recipes col also hard-clips as a backstop). */
  .shop-craft .craft-tier .ct-cost { top: 58%; }
  /* The number stays centred in the widest part of the bulb; the "YOUR MANA" label sits
     below the bottle. */
  .shop-craft .craft-mana-potion .cm-label { font-size: 10px; letter-spacing: .05em; }
  .shop-craft .craft-mana-potion .cm-now   { font-size: clamp(15px, 5vw, 22px); }
  /* Compact the recipes column so the CRAFT button + card wheel ride HIGHER — the
     wheel lands at the same Y as the rip wheel. Cap the 2×2 narrower than the column
     (so the potions actually shrink) and tighten the CRAFT button gap + wheel offset. */
  .shop-craft .craft-recipes-col .craft-tier-list { max-width: 176px !important; }
  .craft-stage .craft-divider { margin: 0 auto !important; }
  .craft-stage { --craft-above-h: 186px; }
}
/* Right: YOUR MANA potion — same silhouette as the recipes, blue liquid, but LARGE
   and standing on its own (no card frame). */
.craft-mana-potion {
  /* Just the potion (showing your mana number) with a "YOUR MANA" label BELOW it.
     Height-capped to the reserved row via the mana-col so the wheel stays at the rip-wheel Y. */
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  position: relative; max-height: 100%; width: auto; max-width: 100%;
}
.craft-mana-potion .cm-body {
  display: flex; align-items: center; justify-content: center;
  min-height: 0; flex: 1 1 auto;
}
.craft-mana-potion .cm-bottle { max-height: 100%; }
/* ── Thin vertical mana meter beside the bottle ─────────────────────────────────
   The FULL track height = your MAXIMUM mana capacity. The bar fills from the bottom
   to your CURRENT mana; the red band at the TOP of the fill is exactly the slice this
   craft spends, so the meter reads "you're here now → it drops to the after line".
   Two tick labels ride the "now" (top of the whole fill) and "after" levels. */
.craft-mana-potion .cm-meter {
  position: relative; align-self: stretch; display: flex; align-items: stretch;
  padding-right: 42px;   /* room for the tick value labels hanging off the right */
}
.craft-mana-potion .cm-meter-track {
  position: relative; width: 8px; align-self: stretch; border-radius: 5px; overflow: visible;
  background: linear-gradient(180deg, rgba(10,16,26,.92), rgba(4,7,12,.96));
  box-shadow: inset 0 0 0 1px rgba(120,180,255,.30), inset 0 2px 6px rgba(0,0,0,.7),
    0 0 10px rgba(69,177,255,.18);
}
/* A faint cap tick at the very top = the "MAX capacity" line the fill is measured against. */
.craft-mana-potion .cm-meter-track::before {
  content: ''; position: absolute; left: -3px; right: -3px; top: 0; height: 2px; border-radius: 1px;
  background: rgba(150,200,255,.4);
}
.craft-mana-potion .cm-meter-fill {
  position: absolute; left: 1.5px; right: 1.5px; bottom: 1.5px; height: 0; border-radius: 4px;
  background: linear-gradient(180deg, #8fe0ff, #45b1ff 42%, #2b7fe0);
  box-shadow: 0 0 9px rgba(69,177,255,.85), inset 0 1px 0 rgba(255,255,255,.55);
  transition: height .5s cubic-bezier(.3,.85,.35,1);
}
/* The slice that gets spent — sits at the TOP of the fill (from the after line up to now). */
.craft-mana-potion .cm-meter-drain {
  position: absolute; left: 1.5px; right: 1.5px; bottom: 0; height: 0; border-radius: 4px; z-index: 2;
  background: linear-gradient(180deg, #ff6b6b, #ff4d4d 60%, #e23a3a);
  box-shadow: 0 0 8px rgba(255,80,80,.7), inset 0 1px 0 rgba(255,190,190,.5);
  opacity: 0; transition: bottom .5s cubic-bezier(.3,.85,.35,1), height .5s cubic-bezier(.3,.85,.35,1), opacity .25s ease;
}
/* The "after" line — a crisp marker across the track where the mana lands post-craft. */
.craft-mana-potion .cm-meter-after-line {
  position: absolute; left: -3px; right: -3px; height: 2px; border-radius: 1px; bottom: 0; z-index: 4;
  background: #ffffff; box-shadow: 0 0 6px rgba(255,120,120,.9); opacity: 0;
  transition: bottom .5s cubic-bezier(.3,.85,.35,1), opacity .25s ease;
}
.craft-mana-potion .cm-meter.cm-meter-spending .cm-meter-drain,
.craft-mana-potion .cm-meter.cm-meter-spending .cm-meter-after-line { opacity: 1; }
/* Tick labels hang off the RIGHT of the track at the now/after levels — a compact
   value-over-caption stack so they stay narrow. */
.craft-mana-potion .cm-meter-tick {
  position: absolute; left: 100%; bottom: 0; transform: translateY(50%); white-space: nowrap;
  display: flex; align-items: center; gap: 5px; z-index: 3; pointer-events: none;
  transition: bottom .5s cubic-bezier(.3,.85,.35,1);
}
.craft-mana-potion .cm-meter-tick::before {   /* connector notch to the track */
  content: ''; flex: 0 0 auto; width: 7px; height: 2px; border-radius: 1px; background: currentColor; opacity: .85;
}
/* value stacked over its caption so the whole tick stays as narrow as the number */
.craft-mana-potion .cm-tick-col { display: flex; flex-direction: column; align-items: flex-start; line-height: 1; }
.craft-mana-potion .cm-meter-tick b {
  display: block; font: 900 15px/1 system-ui, sans-serif; -webkit-text-stroke: .5px rgba(0,0,0,.5);
  text-shadow: 0 1px 3px rgba(0,0,0,.85);
}
.craft-mana-potion .cm-meter-tick .cm-tick-lbl {
  display: block; font: 800 8px/1.2 system-ui, sans-serif; letter-spacing: .12em; text-transform: uppercase; opacity: .7;
}
.craft-mana-potion .cm-tick-now   { color: #cfeaff; }
.craft-mana-potion .cm-tick-after { color: #ff9a9a; opacity: 0; transition: bottom .5s cubic-bezier(.3,.85,.35,1), opacity .25s ease; }
/* Only reveal the "after" tick when a craftable recipe is actually spending mana. */
.craft-mana-potion .cm-meter.cm-meter-spending .cm-tick-after { opacity: 1; }
.craft-mana-potion .cm-meter.cm-meter-spending .cm-tick-now b { color: #9fd2ff; }
.craft-mana-potion .cm-bottle {
  --bottle: polygon(
    45% 0%, 55% 0%, 55% 7%, 62% 9%, 62% 13%, 57% 14%, 57% 25%,
    60% 27%, 68% 31%, 80% 39%, 90% 48%, 96% 58%, 99% 68%, 100% 78%,
    99% 88%, 94% 96%, 85% 99%, 74% 100%, 26% 100%, 15% 99%, 6% 96%,
    1% 88%, 0% 78%, 1% 68%, 4% 58%, 10% 48%, 20% 39%, 32% 31%, 40% 27%,
    43% 25%, 43% 14%, 38% 13%, 38% 9%, 45% 7%);
  /* Driven by HEIGHT, width follows the 0.62 aspect. A clamp() FLOOR (never below 96px) means
     the bottle can never collapse to nothing when --craft-cab-top measures small — it just
     overflows a little into the gap below (the "YOUR MANA" label rides under it). */
  position: relative; display: block; aspect-ratio: 0.62;
  height: clamp(96px, calc(var(--craft-cab-top, 150px) - 8px), 140px); width: auto; flex: 0 0 auto;
  -webkit-clip-path: var(--bottle); clip-path: var(--bottle);
  filter: drop-shadow(0 5px 10px rgba(0,0,0,.6)) drop-shadow(0 0 14px rgba(69,177,255,.5));
}
.craft-mana-potion .cm-bottle::before {   /* coloured glass rim */
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  -webkit-clip-path: var(--bottle); clip-path: var(--bottle);
  background: linear-gradient(180deg, color-mix(in srgb, var(--tier-glow-color,#45b1ff) 90%, #fff 10%), color-mix(in srgb, var(--tier-glow-color,#45b1ff) 70%, #000 30%));
}
.craft-mana-potion .ct-glass {
  position: absolute; inset: 0; z-index: 1; overflow: hidden; pointer-events: none;
  -webkit-clip-path: var(--bottle); clip-path: var(--bottle);
  background: linear-gradient(180deg, rgba(34,44,62,.92), rgba(9,13,21,.96));
  /* Promote the CLIPPING element to its own compositing layer so its clip-path is
     authoritative. Otherwise, on mobile GPUs the animating water (height transition on
     select) escapes the clip and flashes a square. translateZ(0) bakes the clip into
     the layer raster. */
  transform: translateZ(0); -webkit-transform: translateZ(0);
}
/* (The mana potion liquid now uses the shared unified `.ct-water, .cm-water` rule above —
   one translated full-glass SVG. No separate height/mask/fill here.) */
.craft-mana-potion .ct-wave {
  position: absolute; left: 0; right: 0; top: -5px; height: 8px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 20' preserveAspectRatio='none'%3E%3Cpath d='M0 10 Q 25 0 50 10 T 100 10 V20 H0 Z' fill='%23ffffff' fill-opacity='.5'/%3E%3C/svg%3E") repeat-x;
  /* NO will-change: promoting the wave to its own layer makes it escape the bottle's
     clip-path during the water-height transition on select — flashing a square. */
  background-size: 56px 100%; animation: ctWaveSlide 2.6s linear infinite;
}
.craft-mana-potion .ct-wave.ct-wave2 {
  top: -3px; opacity: .55; background-size: 44px 100%; animation: ctWaveSlide2 3.4s linear infinite;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 20' preserveAspectRatio='none'%3E%3Cpath d='M0 10 Q 25 0 50 10 T 100 10 V20 H0 Z' fill='%23ffffff' fill-opacity='.35'/%3E%3C/svg%3E");
}
/* The band that DRAINS on the picked craft (between your current level + the after-line). */
.craft-mana-potion .cm-drain {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; height: 0; pointer-events: none;
  background:
    repeating-linear-gradient(135deg, rgba(255,120,120,.10) 0 5px, rgba(8,12,20,.05) 5px 11px),
    linear-gradient(180deg, rgba(255,70,70,.22), rgba(255,70,70,.10));
  transition: bottom .5s cubic-bezier(.3,.85,.35,1), height .5s cubic-bezier(.3,.85,.35,1);
}
/* The AFTER line — where your mana lands post-craft (or the threshold when short). */
.craft-mana-potion .cm-mark {
  position: absolute; left: 6%; right: 6%; bottom: 0; z-index: 3; height: 0; pointer-events: none;
  border-top: 2px dashed rgba(200,230,255,.5); opacity: 0;
  transition: bottom .5s cubic-bezier(.3,.85,.35,1), opacity .2s ease;
}
.craft-mana-potion.cm-afford .cm-mark { opacity: 1; border-top-style: solid; border-top-color: #fff; filter: drop-shadow(0 0 5px #45b1ff); }
.craft-mana-potion.cm-short  .cm-mark { opacity: 0; }
/* The REQUIRED line — how much mana the picked recipe needs (measured from the
   bottom). A little "REQ" flag hangs off it so you can read the graphic at a glance. */
.craft-mana-potion .cm-req {
  position: absolute; left: 4%; right: 4%; bottom: 0; z-index: 4; height: 0; pointer-events: none;
  border-top: 2px dashed rgba(255,214,140,.9); opacity: 0;
  transition: bottom .5s cubic-bezier(.3,.85,.35,1), opacity .2s ease;
}
.craft-mana-potion .cm-req::after {
  content: 'REQ'; position: absolute; right: -2px; top: -8px;
  font: 900 8px/1 system-ui, sans-serif; letter-spacing: .06em; color: #241400;
  background: #ffd27a; padding: 2px 3px; border-radius: 3px; box-shadow: 0 1px 3px rgba(0,0,0,.5);
}
.craft-mana-potion.cm-afford .cm-req,
.craft-mana-potion.cm-short  .cm-req { opacity: 1; }
.craft-mana-potion.cm-short  .cm-req { border-top-color: #ff9a9a; }
.craft-mana-potion.cm-short  .cm-req::after { background: #ff9a9a; content: 'NEED'; }
/* The animated liquid SURFACE (wave) already denotes the level, and the potion drains its
   surface to your after-craft level, so the extra HARD dashed marker lines are redundant —
   hide them for a cleaner potion. The drain band + the "now → after" text still carry it. */
.craft-mana-potion .cm-mark,
.craft-mana-potion .cm-req { display: none !important; }
.craft-mana-potion .ct-shine {
  position: absolute; left: 14%; top: 34%; width: 18%; height: 44%; z-index: 3; pointer-events: none;
  border-radius: 50% / 60%; transform: rotate(-10deg);
  background: linear-gradient(180deg, rgba(255,255,255,.72), rgba(255,255,255,.05));
  filter: blur(1.5px); opacity: .7;
}
.craft-mana-potion .ct-cork {
  position: absolute; top: -1px; left: 45%; width: 10%; height: 9%; z-index: 4; pointer-events: none;
  background: linear-gradient(180deg, #e7c184, #b8854a 55%, #8a5e2a);
  box-shadow: inset 0 1px 0 rgba(255,245,215,.7), inset 0 -2px 3px rgba(0,0,0,.45);
  border-radius: 4px 4px 2px 2px;
}
.craft-mana-potion .cm-amt {
  position: absolute; left: 0; right: 0; top: 72%; transform: translateY(-50%); z-index: 5; text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  color: #fff; white-space: nowrap;
  -webkit-text-stroke: .7px rgba(0,0,0,.5); text-shadow: 0 2px 5px rgba(0,0,0,.95), 0 0 10px rgba(69,177,255,.8);
}
.craft-mana-potion .cm-amt-row { display: inline-flex; align-items: center; gap: 4px; }
/* Just the number now (no "MANA" unit inside — the label lives below). Kept small enough
   that a 4-digit balance fits inside the bulb silhouette (which clips its overflow). */
.craft-mana-potion .cm-now { font: 900 clamp(16px,2.6vw,24px)/1 system-ui, sans-serif; }
.craft-mana-potion .cm-unit { display: none; }
/* Blue ▾ next to the new mana — only when a craftable recipe is picked — showing the
   count will DROP to that value if you craft it. */
.craft-mana-potion .cm-arrow {
  display: none; color: #45b1ff; font: 900 24px/1 system-ui, sans-serif; -webkit-text-stroke: 0;
  text-shadow: 0 0 10px rgba(69,177,255,.95), 0 1px 2px rgba(0,0,0,.8);
}
.craft-mana-potion.cm-afford .cm-arrow { display: inline-block; }
/* "YOUR MANA" label sits BELOW the bottle (the bottle itself shows only the number). */
.craft-mana-potion .cm-label {
  flex: 0 0 auto; z-index: 6; text-align: center;
  font: 900 13px/1.1 system-ui, sans-serif; text-transform: uppercase; letter-spacing: .12em;
  color: #cfe6ff; text-shadow: 0 1px 4px rgba(0,0,0,.9), 0 0 10px rgba(69,177,255,.4);
}
/* Old inline "now → after" readout is retired in favour of the meter — keep it out of flow. */
.craft-mana-potion .cm-preview { display: none !important; }
.cm-preview .cm-pv-hint { color: rgba(200,220,255,.55); font-weight: 700; font-size: 12px; letter-spacing: .02em; }
.cm-preview b { font-weight: 900; }
.cm-preview .cm-pv-have { color: #bfe6ff; }
.cm-preview .cm-pv-req  { color: #ffd27a; }
.cm-preview .cm-pv-left { color: #fff; text-shadow: 0 0 8px rgba(120,225,255,.7); }
.cm-preview .cm-pv-short { color: #ff9a9a; }
.cm-preview .cm-pv-dot { color: rgba(200,220,255,.4); }
@media (prefers-reduced-motion: reduce) {
  .craft-mana-potion .ct-wave { animation: none !important; }
}
/* Mobile: recipes (4-across) over the cabinet, then the big YOUR MANA potion below. */
@media (max-width: 560px) {
  .craft-recipes-col .craft-tier-list { grid-template-columns: repeat(4, 1fr); max-width: 100%; gap: 7px; width: 100%; }
  .craft-mana-potion { width: min(180px, 60vw); }
}

/* Your Collection lives INSIDE the crafting panel, under the buy/craft controls. */
.crafting-collection { margin-top: 22px; padding-top: 18px; border-top: 1px solid rgba(255,205,80,.18); }
.cc-coll-head { text-align: center; margin-bottom: 8px; }
.cc-coll-title {
  margin: 0; font: 800 13px/1 system-ui, sans-serif; letter-spacing: .14em; text-transform: uppercase;
  color: #ffe08a; text-shadow: 0 0 12px rgba(255,205,80,.3);
}
.cc-coll-sub { display: block; margin-top: 4px; font: 500 12px/1.3 system-ui, sans-serif; color: #9fb0c6; }
/* The card grid fills the panel width (no extra centering band inside the panel). */
.crafting-collection .gallery-grid { max-width: 100%; padding: 0; margin: 6px 0 0; }

/* Disenchant: a clear instruction banner while selecting. */
.disenchant-hint {
  display: none; max-width: 640px; margin: 0 auto 12px; padding: 10px 16px; border-radius: 12px;
  background: linear-gradient(180deg, rgba(106,215,255,.12), rgba(179,136,255,.08));
  border: 1px solid rgba(106,215,255,.35); color: #cfe7f5;
  font: 600 13px/1.4 system-ui, sans-serif; text-align: center;
}
body.gallery-select .disenchant-hint { display: block; }

/* Low on credits → the wallet pill pulses harder + shows a "＋" so the path to buy
   is unmissable. */
.pb-credits-btn.pb-low { animation: credPulse 1.9s ease-in-out infinite, pbLowFlash 1.1s ease-in-out infinite; }
/* The RIP button is a .pb-nav-btn (animation:none) — keep the out-of-rips flash by
   re-asserting just the flash (no gold credPulse) with !important. */
.pb-nav-btn.pb-low { animation: pbLowFlash 1.1s ease-in-out infinite !important; }
@keyframes pbLowFlash { 0%,100% { filter: none; } 50% { filter: brightness(1.18) saturate(1.15); } }
.pb-credits-btn.pb-low::before { opacity: 1; }
.crystal-balance {
  display: inline-flex; align-items: center; gap: 5px;
  font-weight: 800; font-size: 15px; letter-spacing: .3px;
  padding: 5px 12px; border-radius: 9px;
  color: #eaf7ff;
  background: linear-gradient(180deg, rgba(106,215,255,.16), rgba(179,136,255,.13));
  border: 1px solid rgba(106,215,255,.4);
  box-shadow: inset 0 0 12px rgba(106,215,255,.18), 0 0 10px rgba(106,215,255,.12);
}
.crystal-balance b { color: #fff; }
.gallery-act-btn.active { box-shadow: 0 0 0 2px rgba(255,120,120,.7), 0 0 14px rgba(255,120,120,.4); }

/* Selectable gallery cards while in transmute mode */
body.gallery-select #inventoryGrid .floor-card { cursor: pointer; position: relative; }
/* Selection ring via OUTLINE (not ::after) so it never collides with the foil/holo
   ::after overlay on chase cards. */
#inventoryGrid .floor-card.tx-selected {
  position: relative;
  outline: 3px solid var(--crystal); outline-offset: -2px;
  box-shadow: 0 0 0 2px rgba(106,215,255,.4), 0 0 20px rgba(106,215,255,.55) !important;
}
/* Checkmark is a real CHILD element (.tx-badge), immune to the rare ::before
   lightning + foil ::after sweep that the pseudo-element versions inherited. */
#inventoryGrid .floor-card .tx-badge {
  position: absolute; top: 5px; right: 5px; z-index: 30;
  width: 22px; height: 22px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font: 900 14px/1 system-ui, sans-serif; color: #07212e;
  background: var(--crystal); box-shadow: 0 0 10px rgba(106,215,255,.9);
  pointer-events: none;
}

/* Sticky transmute action bar */
.transmute-bar {
  position: sticky; bottom: 12px; z-index: 30;
  margin: 14px auto 6px; max-width: 560px;
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 11px 16px; border-radius: 14px;
  background: linear-gradient(180deg, rgba(16,22,38,.96), rgba(10,14,26,.96));
  border: 1px solid rgba(106,215,255,.45);
  box-shadow: 0 8px 30px rgba(0,0,0,.5), inset 0 0 18px rgba(106,215,255,.12);
  backdrop-filter: blur(6px);
}
.tb-info { font-size: 14px; color: #cfe7f5; }
.tb-info b { color: #fff; }
.tb-reward { color: var(--crystal); margin-left: 4px; }
.tb-reward b { color: #eaf7ff; }
.tb-actions { display: flex; gap: 8px; flex-shrink: 0; }

/* Craft overlay */
/* Inline gallery panels — Crafting + Buy Credits live on the gallery page now. */
.gallery-panel {
  width: min(640px, 94vw); margin: 22px auto; padding: 20px 18px 22px; border-radius: 16px;
  background: radial-gradient(120% 90% at 50% -10%, rgba(40,30,70,.5), rgba(10,12,24,.96));
  border: 1px solid rgba(179,136,255,.4);
  box-shadow: 0 14px 50px rgba(0,0,0,.45), inset 0 0 36px rgba(106,215,255,.05);
}
.buy-packs { display: flex; flex-direction: column; gap: 10px; max-width: 420px; margin: 0 auto; }

.craft-overlay {
  position: fixed; inset: 0; z-index: 200;
  display: none; align-items: center; justify-content: center;
  background: rgba(4,7,16,.74); backdrop-filter: blur(4px); padding: 18px;
}
.craft-modal {
  position: relative; width: min(560px, 96vw); max-height: 88vh; overflow-y: auto;
  padding: 22px 20px 24px; border-radius: 18px;
  background: radial-gradient(120% 90% at 50% -10%, rgba(40,30,70,.6), rgba(10,12,24,.98));
  border: 1px solid rgba(179,136,255,.45);
  box-shadow: 0 20px 70px rgba(0,0,0,.6), inset 0 0 40px rgba(106,215,255,.07);
}
.craft-close {
  position: absolute; top: 10px; right: 12px; width: 32px; height: 32px;
  border: 0; border-radius: 8px; cursor: pointer;
  background: rgba(255,255,255,.08); color: #cdd6e6; font-size: 20px; line-height: 1;
}
.craft-close:hover { background: rgba(255,255,255,.16); color: #fff; }
.craft-title { margin: 0 0 4px; font-size: 21px; color: #fff; text-align: center; }
.craft-sub { margin: 0 0 12px; font-size: 13px; color: #aeb9cc; text-align: center; }
.craft-balance {
  text-align: center; font-size: 15px; color: #eaf7ff; margin-bottom: 16px;
}
.craft-balance b { color: #fff; font-size: 18px; }
.craft-tier-list { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.craft-tier {
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  padding: 12px 14px; border-radius: 12px; cursor: pointer; text-align: left;
  background: linear-gradient(180deg, rgba(255,255,255,.05), rgba(255,255,255,.02));
  border: 1px solid var(--tier-glow-color, rgba(255,255,255,.25));
  box-shadow: inset 0 0 16px color-mix(in srgb, var(--tier-glow-color, #fff) 14%, transparent);
  color: #fff; transition: transform .1s ease, box-shadow .15s ease;
}
.craft-tier:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: 0 6px 20px color-mix(in srgb, var(--tier-glow-color, #fff) 35%, transparent),
              inset 0 0 18px color-mix(in srgb, var(--tier-glow-color, #fff) 22%, transparent);
}
.craft-tier:disabled { opacity: .45; cursor: not-allowed; }
.ct-name { font-weight: 800; font-size: 15px; }
.ct-stock { font-size: 11.5px; color: #9fb0c6; }
.ct-cost { margin-top: 3px; font-weight: 800; font-size: 14px; color: var(--crystal); }
/* The forge reveal is a CENTERED overlay (not a strip at the bottom) so the crafted
   card is shown in place, right where you're looking — no fullscreen view switch. */
.craft-result {
  position: fixed; inset: 0; z-index: 12000; display: none;
  flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  margin: 0; padding: 24px; text-align: center; cursor: pointer;
  background: radial-gradient(circle at 50% 42%, rgba(12,18,34,.72), rgba(4,7,14,.93));
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
}
.craft-result-label { font-size: 18px; color: #eaf7ff; margin-bottom: 6px; text-shadow: 0 2px 8px rgba(0,0,0,.7); }
/* Card + PSA info shown side-by-side (card left, slab info right). Wraps to a
   column on narrow screens so the PSA panel drops beneath the card. */
.craft-result-row { display: flex; align-items: center; justify-content: center; gap: 20px; flex-wrap: wrap; }
/* overflow visible so the hover-expand (scale 1.06 + glow) isn't cropped. */
.craft-result-card { display: flex; justify-content: center; position: relative; overflow: visible; border-radius: 12px; }
.craft-result-card .floor-card { width: min(300px, 74vw); max-width: none; }
/* Clips ONLY the forge sheen sweep to the card bounds, without clipping the card itself. */
.crc-sheen-clip { position: absolute; inset: 0; overflow: hidden; border-radius: 12px; pointer-events: none; }
/* The PSA panel reuses the reveal-psa-panel scoped styles, but that base rule
   hides the panel (width:0/opacity:0) for its animated reveal. Force it visible here. */
.craft-result-psa.reveal-psa-panel { width: auto !important; max-width: 280px; opacity: 1 !important; overflow: visible; transition: none; text-align: left; }
.craft-result-dismiss { margin-top: 14px; font: 800 12px/1 system-ui, sans-serif; letter-spacing: .08em; color: rgba(220,235,255,.65); text-transform: uppercase; }
/* Forge animation: crystals spiral in, collapse to a flash, card materializes. */
.craft-forge { position: relative; height: 210px; display: flex; align-items: center; justify-content: center; }
.forge-crystal {
  position: absolute; font-size: 22px; color: var(--crystal);
  text-shadow: 0 0 10px var(--crystal), 0 0 20px var(--crystal-2);
  will-change: transform, opacity; pointer-events: none;
}
.forge-ring {
  position: absolute; width: 116px; height: 116px; border-radius: 50%;
  border: 3px solid var(--crystal);
  box-shadow: 0 0 28px var(--crystal), inset 0 0 22px var(--crystal); pointer-events: none;
}
.forge-flash {
  position: absolute; width: 230px; height: 230px; border-radius: 50%; opacity: 0;
  background: radial-gradient(circle, #fff, rgba(69,177,255,.6) 40%, transparent 70%); pointer-events: none;
}
.forge-sheen {
  position: absolute; inset: 0; pointer-events: none; transform: translateX(-130%);
  background: linear-gradient(115deg, transparent 36%, rgba(255,255,255,.75) 50%, transparent 64%);
  animation: forgeSheen 1s ease .32s 1 forwards;
}
@keyframes forgeSheen { to { transform: translateX(130%); } }
@keyframes cr-pop { 0% { transform: scale(.7); opacity: 0; } 60% { transform: scale(1.06); } 100% { transform: scale(1); opacity: 1; } }
.craft-result.cr-pop .craft-result-card { animation: cr-pop .42s cubic-bezier(.2,.8,.3,1.2) both; }
@media (max-width: 420px) { .craft-tier-list { grid-template-columns: 1fr; } }
/* Selected rarity (ready to spin-craft) + the Spin-to-Craft button.
   NO rectangular outline/box-shadow here — the bottle is a clipped silhouette, so a
   rectangular border reads as a glowing "card" around it. The selected glow lives on
   the .shop-craft .craft-tier.ct-selected drop-shadow FILTER (follows the bottle shape). */
.craft-tier.ct-selected {
  outline: none;
  box-shadow: none;
}
/* Craft carousel — a marquee of the selected rarity's in-stock cards (like the
   front-page wheel); spins fast on craft. */
.craft-carousel {
  position: relative; overflow: hidden; width: 100%; max-width: 480px; margin: 16px auto 2px;
  height: 132px; border-radius: 14px; border: 1px solid rgba(255,255,255,.12);
  background: radial-gradient(120% 120% at 50% 0%, rgba(106,215,255,.1), rgba(0,0,0,.35));
  -webkit-mask: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
          mask: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.craft-carousel.cc-ready { cursor: grab; border-color: var(--tier-glow-color, rgba(255,205,80,.5)); box-shadow: 0 0 22px rgba(255,205,80,.25); }
.craft-carousel.cc-grabbing, .craft-carousel.cc-grabbing * { cursor: grabbing !important; }
.craft-carousel-track {
  display: flex; height: 100%; align-items: center; width: max-content;
  animation: ccScroll 12s linear infinite; will-change: transform;
}
/* Each set = the rarity's cards packed at the left, padded (min-width set in JS) to
   at least the carousel width so a card is never on screen twice. Two sets + -50%
   scroll = a seamless loop, the same conveyor the main rip carousel uses. */
/* Card spacing matches the front-page carousel (~22px between cards). */
/* justify-content: space-around — distribute the available cards EVENLY across the
   set width (like the rip carousel's .sc-ticker-set), so few cards spread out with
   uniform gaps instead of bunching at the left with one long blank space. The 22px
   gap is the minimum spacing for the packed (many-cards) case. */
.cc-set { display: flex; gap: 22px; height: 100%; align-items: center; justify-content: space-around; flex: 0 0 auto; padding: 0 11px; }
@keyframes ccScroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.cc-card {
  height: 116px; flex: 0 0 auto; border-radius: 10px; overflow: hidden;
  box-shadow: 0 0 0 1px var(--tier-glow-color, rgba(255,255,255,.2)), 0 0 12px color-mix(in srgb, var(--tier-glow-color, #888) 50%, transparent);
}
.cc-card img { height: 100%; width: auto; display: block; }
/* While the craft wheel is spinning fast, drop the per-card ring + color-mix glow entirely
   so the cards are plain images — smoother, calmer spin. The full ring/glow returns at rest. */
.craft-carousel-track.sc-spinning { will-change: transform; }
.craft-carousel-track.sc-spinning .cc-card { box-shadow: none !important; }
/* The card the craft wheel lands on pops + rings (held under the green line for a beat). */
.cc-card.cc-landed { position: relative; z-index: 4; transform: scale(1.08); filter: brightness(1.14); transition: transform .28s cubic-bezier(.2,.9,.3,1.3), filter .28s ease; }
.cc-card.cc-landed { box-shadow: 0 0 0 2px #fff, 0 0 26px 5px var(--tier-glow-color, #8fd4ff); }
.craft-carousel-hint {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 3; pointer-events: none;
  text-align: center; padding: 5px 8px; font: 800 12px/1.2 system-ui, sans-serif; color: #fff;
  background: linear-gradient(0deg, rgba(0,0,0,.7), transparent); text-shadow: 0 1px 3px rgba(0,0,0,.8);
}
.craft-carousel.cc-spinning .craft-carousel-track { animation-duration: .3s !important; }
.craft-carousel.cc-spinning .craft-carousel-hint { opacity: 0; }
@media (prefers-reduced-motion: reduce) { .craft-carousel-track { animation: none; } }

/* The MAIN craft wheel — sits in the MIDDLE of the crafting panel (between the
   buy/craft controls and the collection) so it's thumb-reachable on mobile, like
   the front-page rip carousel. Same band height + card size as the front page. */
.craft-carousel.craft-wheel {
  /* Full-bleed: break out of the (centred) crafting panel to span the WHOLE
     viewport width, like the front-page rip carousel. */
  width: 100vw; max-width: 100vw;
  margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw);
  margin-top: 16px; margin-bottom: 16px;
  /* Same band as the front-page rip carousel — the craft gallery is just as big as the
     pull gallery (flex-fills the viewport below the controls: ≈ 100dvh − 483px, floored
     so it never collapses). */
  height: max(320px, calc(100dvh - 483px));
  /* No frame — bare cards on the page background, like the rip carousel (no border,
     no panel gradient, no glow box). */
  border: none; border-radius: 0; background: transparent; box-shadow: none;
  /* Drop the edge-fade MASK on the spinning wheel — masking a full-bleed element every
     frame is expensive and made the spin stutter. Use a plain rectangular clip like the
     rip wheel (which is smooth) instead. */
  -webkit-mask: none !important; mask: none !important; overflow: clip;
}
/* Keep it frameless in every state (ready/disabled). */
.craft-carousel.craft-wheel.cc-ready,
.craft-carousel.craft-wheel.cc-disabled { border: none !important; box-shadow: none !important; background: transparent; }
/* Green centre line while the craft wheel whips to a stop on the forged card (same as
   the rip wheel's landing line) — shown IN PLACE, no fullscreen. */
.craft-carousel.craft-wheel.cc-landing::after {
  content: ''; position: absolute; left: 50%; top: 4%; bottom: 4%; transform: translateX(-50%);
  width: 2px; z-index: 6; pointer-events: none; border-radius: 2px;
  background: #36e07a; box-shadow: 0 0 6px rgba(54,224,122,.9), 0 0 16px rgba(54,224,122,.6);
}
/* When the wheel lands, the winning card pops (scale 1.08) + rings on top. The band's
   overflow:clip was cropping that pop and its glow. Give the clip box room DURING the
   landing beat only (spin still clips tight) so the popped card shows in full. */
.craft-carousel.craft-wheel.cc-landing { overflow-clip-margin: 46px; }
.craft-carousel.craft-wheel .cc-card { height: 92%; }   /* 92% of the band, like the rip carousel */
/* The hint is always empty now — never render its (empty) bar. */
.craft-carousel-hint:empty { display: none; }
.craft-carousel.craft-wheel .craft-carousel-hint { font-size: 13px; padding: 7px 8px; }
@media (max-width: 640px) {
  /* Same band as the mobile rip carousel (capped by 36dvh for Safari's bar). */
  .craft-carousel.craft-wheel { height: min(220px, 36dvh); }
}
/* DISABLED: greyed out + not tappable until a crafting rarity is selected. */
.craft-carousel.cc-disabled {
  cursor: not-allowed; pointer-events: none;
  filter: grayscale(.85) brightness(.55); opacity: .7;
  border-color: rgba(255,255,255,.1); box-shadow: none;
}
.craft-carousel.cc-disabled .craft-carousel-track { animation-play-state: paused; }
.craft-carousel.cc-disabled .craft-carousel-hint {
  background: rgba(0,0,0,.55); color: #cdd6e6; opacity: 1;
}

/* Craft action divider — mirrors the rip page's .jackpot-divider exactly: a centred
   pill flanked by the same glowing gold lines, in the same spot between the cards
   and the wheel, with the same margins so the wheel lands in the same place. */
.craft-divider {
  /* CENTRED cabinet flanked by a glowing bar on BOTH sides — exactly like the rip page's
     USERS ONLINE meter, sitting below all the crafting UI and above the wheel. */
  width: 100%; max-width: 720px; display: flex; align-items: center; justify-content: center; gap: 16px;
  margin: 14px auto 6px; padding: 0 18px;
}
/* Desktop: break the cabinet row out of the (960px) craft panel and lay it on the SAME
   1180px two-column grid the rip page uses for its JACKPOT (page padding 24px, 28px gap),
   with the cabinet in the LEFT column — so the CRAFT and JACKPOT cabinets land at the
   identical X and don't jump when you toggle between the screens. */
@media (min-width: 861px) {
  .craft-divider-row {
    width: 100vw; margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw);
    display: grid; grid-template-columns: 1fr 1fr; gap: 28px; box-sizing: border-box;
    padding-inline: max(24px, calc(50vw - 590px));   /* → content = min(1180px, 100vw − 48px) */
  }
  .craft-divider-row .craft-divider { grid-column: 1; margin: 22px 0 4px; max-width: 100%; }
}
.craft-divider::before, .craft-divider::after {
  content: ''; flex: 1 1 0; height: 2px; max-width: 38%; border-radius: 2px;
  background: linear-gradient(90deg, transparent, rgba(255,180,70,.55) 70%, rgba(255,210,120,.85));
  box-shadow: 0 0 8px rgba(255,150,50,.5);
}
.craft-divider::after { background: linear-gradient(270deg, transparent, rgba(255,180,70,.55) 70%, rgba(255,210,120,.85)); }
/* ── Arcade CABINET — one shared look for the rip JACKPOT, the craft button, and the
   gallery marquee, so the three screens read as the same machine. Brushed-gold bezel
   with a 3D rim, a marquee header flanked by blinking bulbs, and a recessed LED board.
   (Modelled on .jackpot-meter / .cc-gallery-toggle.) ─────────────────────────────── */
.cabinet-btn, .cabinet-display {
  position: relative; display: inline-flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 3px; flex: 0 0 auto; width: fit-content; margin: 0;
  padding: 6px 16px 8px; border-radius: 12px;
  background: linear-gradient(180deg, #5a4516 0%, #2b1f08 48%, #4a3712 100%);
  border: 1px solid rgba(255,225,150,.7);
  box-shadow:
    0 6px 16px rgba(0,0,0,.5),
    inset 0 1px 0 rgba(255,240,190,.5), inset 0 -2px 4px rgba(0,0,0,.55),
    0 0 0 1px rgba(0,0,0,.5), 0 0 16px rgba(255,170,50,.3);
}
.cabinet-btn { cursor: pointer; transition: transform .2s cubic-bezier(.2,1.4,.3,1), box-shadow .2s ease, filter .15s ease; }
.cabinet-btn:not(:disabled):hover { transform: translateY(-1px) scale(1.02); box-shadow:
  0 8px 20px rgba(0,0,0,.55),
  inset 0 1px 0 rgba(255,240,190,.6), inset 0 -2px 4px rgba(0,0,0,.55),
  0 0 0 1px rgba(0,0,0,.5), 0 0 24px rgba(255,180,60,.5); }
.cabinet-btn:not(:disabled):active { transform: translateY(0) scale(.99); }
.cabinet-btn:disabled { cursor: not-allowed; filter: grayscale(.45) brightness(.78); opacity: .9; }
/* ── Seamless rip ↔ craft: the CRAFT cabinet is pixel-identical to the USERS ONLINE cabinet
   (same explicit width + padding/radius at each breakpoint) so toggling the two screens
   shows no size jump, and its flanking bars land in the same place. ── */
.jackpot-meter, .shop-craft .craft-cabinet { box-sizing: border-box; width: 172px; }
@media (max-width: 640px) {
  .jackpot-meter, .shop-craft .craft-cabinet { width: 148px; }
  .shop-craft .craft-cabinet { padding: 5px 12px 6px; border-radius: 10px; }
  .craft-stage > .craft-divider { gap: 10px; padding: 0 10px; }
}
/* Marquee header with two blinking cabinet bulbs (matches .jm-label / .cgt-label). */
.cab-label {
  display: inline-flex; align-items: center; gap: 8px;
  font: 900 9px/1 var(--font-display, system-ui), sans-serif; letter-spacing: .3em; text-indent: .3em;
  background: linear-gradient(90deg, #ffd15c, #fff3c4 50%, #ffd15c);
  -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 1px 0 rgba(0,0,0,.5));
}
.cab-bulb {
  width: 7px; height: 7px; border-radius: 50%; text-indent: 0; flex: 0 0 auto;
  background: radial-gradient(circle at 35% 30%, #fff7d6, #ffb52e 60%, #b9670f 100%);
  box-shadow: 0 0 7px rgba(255,190,70,.95), 0 0 13px rgba(255,150,40,.7);
  animation: jmBulb 1s steps(1) infinite;
}
.cab-label .cab-bulb:last-child { animation-delay: .5s; }
/* Recessed dark LED screen (matches .jm-board / .cgt-board). */
.cab-board {
  position: relative; display: inline-flex; align-items: center; gap: 8px;
  padding: 4px 14px; border-radius: 7px;
  background: linear-gradient(180deg, #160604, #240a06 50%, #120402);
  border: 1px solid rgba(0,0,0,.85);
  box-shadow: inset 0 2px 7px rgba(0,0,0,.9), inset 0 0 14px rgba(255,90,30,.18), 0 0 14px rgba(255,150,50,.3);
  overflow: hidden;
}
.cab-board::after {
  content: ''; position: absolute; inset: 0; pointer-events: none; border-radius: 7px;
  background: repeating-linear-gradient(0deg, rgba(0,0,0,.28) 0 1px, transparent 1px 3px);
  mix-blend-mode: multiply;
}
.cab-emoji { font-size: 22px; line-height: 1; filter: drop-shadow(0 0 8px rgba(255,200,120,.7)); }
.cab-text {
  position: relative; z-index: 1; white-space: nowrap;
  font: 900 15px/1 "DS-Digital", "Courier New", ui-monospace, monospace; letter-spacing: .06em;
  color: #ffc23a; text-shadow: 0 0 5px rgba(255,170,40,.95), 0 0 12px rgba(255,120,20,.8), 0 0 22px rgba(255,90,10,.5);
}
@media (prefers-reduced-motion: reduce) { .cab-bulb { animation: none !important; } }
/* CLAIM button wears the SAME arcade cabinet as USERS ONLINE / CRAFT. Center it in the
   reveal action column (it's fit-content now, not a full-width solid button) and beat the
   higher-specificity mobile/revealing rules that stretched the old .rip-again-btn edge-to-edge. */
.reveal-in-actions .rip-again-btn.cabinet-btn { align-self: center; }
.hero-stage .reveal-zone.expanded .reveal-in-actions .rip-again-btn.cabinet-btn,
.hero-stage.revealing .reveal-in-actions .rip-again-btn.cabinet-btn {
  width: fit-content; padding: 6px 16px 8px; margin: 0 auto; font-size: inherit;
}
/* Make the CLAIM word read big on the LED board (the action, front and centre). */
.claim-cabinet .cab-text { font-size: 20px; letter-spacing: .12em; }
.claim-cabinet .cab-board { padding: 5px 18px; }
/* Empty gallery: an arcade cabinet (same family as JACKPOT) reading CARDS WILL
   APPEAR HERE, centred across the whole grid instead of a plain line of text. */
.empty-gallery { grid-column: 1 / -1; display: flex; justify-content: center; padding: 26px 0; }
/* Gallery marquee sits where the page title was, flanked by the SAME glowing gold
   rule-lines as the rip page's jackpot divider. */
.cabinet-divider {
  display: flex; align-items: center; justify-content: center; gap: 16px;
  width: 100%; max-width: 560px; margin: 0 auto 18px; padding: 0 18px;
}
.cabinet-divider::before, .cabinet-divider::after {
  content: ''; flex: 1 1 0; height: 2px; max-width: 38%; border-radius: 2px;
  background: linear-gradient(90deg, transparent, rgba(255,180,70,.55) 70%, rgba(255,210,120,.85));
  box-shadow: 0 0 8px rgba(255,150,50,.5);
}
.cabinet-divider::after { background: linear-gradient(270deg, transparent, rgba(255,180,70,.55) 70%, rgba(255,210,120,.85)); }
/* Userbar shipments icon — same footprint as the admin cog. */
.pb-ship { font-size: 16px; line-height: 1; }
/* SHOW GALLERY toggle — the collection is hidden behind it; clicking drops the cards
   (and their select/ship/disenchant actions) down. */
/* Mirrors the rip page's .jackpot-meter arcade cabinet: brushed-gold bezel with a
   3D rim, a marquee header flanked by blinking bulbs, and a recessed LED board. */
/* Flank the gallery button with the SAME glowing gold rule-lines as the rip page's
   jackpot divider, so the two read as the same component. */
.cc-gallery-divider {
  display: flex; align-items: center; justify-content: center; gap: 16px;
  width: 100%; max-width: 560px; margin: 14px auto 0; padding: 0 18px;
}
.cc-gallery-divider::before, .cc-gallery-divider::after {
  content: ''; flex: 1 1 0; height: 2px; max-width: 38%; border-radius: 2px;
  background: linear-gradient(90deg, transparent, rgba(255,180,70,.55) 70%, rgba(255,210,120,.85));
  box-shadow: 0 0 8px rgba(255,150,50,.5);
}
.cc-gallery-divider::after { background: linear-gradient(270deg, transparent, rgba(255,180,70,.55) 70%, rgba(255,210,120,.85)); }
.cc-gallery-toggle {
  position: relative; display: flex; width: fit-content; flex-direction: column; align-items: center;
  justify-content: center; gap: 3px; margin: 0; flex: 0 0 auto; padding: 6px 16px 8px;
  border-radius: 12px; cursor: pointer;
  background: linear-gradient(180deg, #5a4516 0%, #2b1f08 48%, #4a3712 100%);
  border: 1px solid rgba(255,225,150,.7);
  box-shadow:
    0 6px 16px rgba(0,0,0,.5),
    inset 0 1px 0 rgba(255,240,190,.5), inset 0 -2px 4px rgba(0,0,0,.55),
    0 0 0 1px rgba(0,0,0,.5), 0 0 16px rgba(255,170,50,.3);
  transition: transform .2s cubic-bezier(.2,1.4,.3,1), box-shadow .2s ease;
}
.cc-gallery-toggle:hover { transform: translateY(-1px) scale(1.02); box-shadow:
  0 8px 20px rgba(0,0,0,.55),
  inset 0 1px 0 rgba(255,240,190,.6), inset 0 -2px 4px rgba(0,0,0,.55),
  0 0 0 1px rgba(0,0,0,.5), 0 0 24px rgba(255,180,60,.5); }
.cc-gallery-toggle:active { transform: translateY(0) scale(.99); }
/* Marquee header with two blinking cabinet bulbs. */
.cgt-label {
  display: inline-flex; align-items: center; gap: 8px;
  font: 900 9px/1 var(--font-display, system-ui), sans-serif; letter-spacing: .3em; text-indent: .3em;
  background: linear-gradient(90deg, #ffd15c, #fff3c4 50%, #ffd15c);
  -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 1px 0 rgba(0,0,0,.5));
}
.cgt-bulb {
  width: 7px; height: 7px; border-radius: 50%; text-indent: 0; flex: 0 0 auto;
  background: radial-gradient(circle at 35% 30%, #fff7d6, #ffb52e 60%, #b9670f 100%);
  box-shadow: 0 0 7px rgba(255,190,70,.95), 0 0 13px rgba(255,150,40,.7);
  animation: jmBulb 1s steps(1) infinite;
}
.cgt-label .cgt-bulb:last-child { animation-delay: .5s; }
/* Recessed dark LED screen (matches .jm-board). */
.cgt-board {
  position: relative; display: inline-flex; align-items: center; gap: 8px;
  padding: 4px 14px; border-radius: 7px;
  background: linear-gradient(180deg, #160604, #240a06 50%, #120402);
  border: 1px solid rgba(0,0,0,.85);
  box-shadow: inset 0 2px 7px rgba(0,0,0,.9), inset 0 0 14px rgba(255,90,30,.18), 0 0 14px rgba(255,150,50,.3);
  overflow: hidden;
}
.cgt-board::after {
  content: ''; position: absolute; inset: 0; pointer-events: none; border-radius: 7px;
  background: repeating-linear-gradient(0deg, rgba(0,0,0,.28) 0 1px, transparent 1px 3px);
  mix-blend-mode: multiply;
}
.cgt-emoji { font-size: 22px; line-height: 1; filter: drop-shadow(0 0 8px rgba(255,200,120,.7)); }
.cgt-text {
  position: relative; z-index: 1;
  font: 900 14px/1 "DS-Digital", "Courier New", ui-monospace, monospace; letter-spacing: .06em;
  color: #ffc23a; text-shadow: 0 0 5px rgba(255,170,40,.95), 0 0 12px rgba(255,120,20,.8), 0 0 22px rgba(255,90,10,.5);
}
.cc-gallery-toggle.is-open { box-shadow:
  0 6px 16px rgba(0,0,0,.5),
  inset 0 1px 0 rgba(255,240,190,.5), inset 0 -2px 4px rgba(0,0,0,.55),
  0 0 0 1px rgba(0,0,0,.5), 0 0 22px rgba(255,180,60,.45); }
@media (prefers-reduced-motion: reduce) { .cgt-bulb { animation: none !important; } }

/* ══ EVA ENTRY-PLUG COCKPIT PANELS ═════════════════════════════════════════════════════
   Re-skin the arcade-cabinet family (rip JACKPOT scoreboard + the CRAFT-SELECT / SHOW-
   GALLERY cabinets) as NERV mecha status readouts — the warning panels inside an Eva
   pilot's entry plug: a dark armoured hull with a neon theme rim, a scrolling black/amber
   hazard stripe, corner targeting brackets, diamond alert diodes and an amber CRT board.
   --eva-rim carries the page theme (green on the rip page, purple on craft/gallery);
   --eva-warn is the classic entry-plug amber. Placed AFTER the originals so it wins. */
.jackpot-meter,
.cabinet-btn, .cabinet-display,
.cc-gallery-toggle,
.gac-actions .gac-ship, .gac-actions .gac-dis {
  --eva-rim: 154,55,236;          /* craft / gallery → neon purple */
  --eva-warn: 255,176,46;         /* entry-plug amber */
  padding-top: 9px !important;    /* headroom for the hazard stripe above the marquee */
  border: 1px solid rgba(var(--eva-rim),.85) !important;
  border-radius: 5px !important;
  background:
    linear-gradient(180deg, rgba(var(--eva-rim),.12), transparent 36%),
    linear-gradient(180deg, #10151b 0%, #070a0e 52%, #0c1219 100%) !important;
  box-shadow:
    0 6px 18px rgba(0,0,0,.6),
    inset 0 1px 0 rgba(var(--eva-rim),.35),
    inset 0 0 0 1px rgba(0,0,0,.6),
    inset 0 0 10px rgba(var(--eva-warn),.10),
    0 0 16px rgba(var(--eva-rim),.4) !important;
}
.jackpot-meter { --eva-rim: 46,229,124; }   /* rip page → neon green */
/* Scrolling hazard stripe across the top — the signature Eva warning bar. */
.jackpot-meter::before,
.cabinet-btn::before, .cabinet-display::before,
.cc-gallery-toggle::before,
.gac-actions .gac-ship::before, .gac-actions .gac-dis::before {
  content: ''; position: absolute; left: 7px; right: 7px; top: 3px; height: 3px; z-index: 2;
  border-radius: 2px; pointer-events: none; opacity: .82;
  background: repeating-linear-gradient(115deg,
    rgba(var(--eva-warn),.95) 0 7px, rgba(8,7,4,.95) 7px 14px);
  background-size: 28px 100%;
  animation: evaHazard 1.1s linear infinite;
}
@keyframes evaHazard { to { background-position: 28px 0; } }
/* Corner targeting brackets (HUD reticle) drawn in the rim colour. */
.jackpot-meter::after,
.cabinet-btn::after, .cabinet-display::after,
.cc-gallery-toggle::after,
.gac-actions .gac-ship::after, .gac-actions .gac-dis::after {
  content: ''; position: absolute; inset: 4px; z-index: 2; pointer-events: none;
  background:
    linear-gradient(rgba(var(--eva-rim),.9),rgba(var(--eva-rim),.9)) left  top   /9px 2px no-repeat,
    linear-gradient(rgba(var(--eva-rim),.9),rgba(var(--eva-rim),.9)) left  top   /2px 9px no-repeat,
    linear-gradient(rgba(var(--eva-rim),.9),rgba(var(--eva-rim),.9)) right top   /9px 2px no-repeat,
    linear-gradient(rgba(var(--eva-rim),.9),rgba(var(--eva-rim),.9)) right top   /2px 9px no-repeat,
    linear-gradient(rgba(var(--eva-rim),.9),rgba(var(--eva-rim),.9)) left  bottom/9px 2px no-repeat,
    linear-gradient(rgba(var(--eva-rim),.9),rgba(var(--eva-rim),.9)) left  bottom/2px 9px no-repeat,
    linear-gradient(rgba(var(--eva-rim),.9),rgba(var(--eva-rim),.9)) right bottom/9px 2px no-repeat,
    linear-gradient(rgba(var(--eva-rim),.9),rgba(var(--eva-rim),.9)) right bottom/2px 9px no-repeat;
}
/* Hover / open states keep the cockpit rim glow (not the old gold). */
.cabinet-btn:not(:disabled):hover,
.cc-gallery-toggle:hover,
.gac-actions .gac-ship:not(:disabled):hover, .gac-actions .gac-dis:not(:disabled):hover {
  box-shadow:
    0 8px 22px rgba(0,0,0,.62),
    inset 0 1px 0 rgba(var(--eva-rim),.42),
    inset 0 0 0 1px rgba(0,0,0,.6),
    inset 0 0 12px rgba(var(--eva-warn),.14),
    0 0 26px rgba(var(--eva-rim),.62) !important;
}
.cc-gallery-toggle.is-open {
  box-shadow:
    0 6px 16px rgba(0,0,0,.6),
    inset 0 1px 0 rgba(var(--eva-rim),.35),
    inset 0 0 0 1px rgba(0,0,0,.6),
    0 0 20px rgba(var(--eva-rim),.5) !important;
}
/* Marquee bulbs → square amber hazard diodes (blink kept). */
.jm-bulb, .cab-bulb, .cgt-bulb {
  border-radius: 1px !important;
  background: rgba(var(--eva-warn),1) !important;
  box-shadow: 0 0 6px rgba(var(--eva-warn),.95), 0 0 12px rgba(var(--eva-warn),.55) !important;
}
/* Marquee labels → amber HUD legend. */
.jm-label, .cab-label, .cgt-label {
  letter-spacing: .34em !important;
  background: linear-gradient(90deg, rgba(var(--eva-warn),1), #fff0c8 50%, rgba(var(--eva-warn),1)) !important;
  -webkit-background-clip: text !important; background-clip: text !important;
  -webkit-text-fill-color: transparent !important;
}
/* Recessed board → cool-dark hull screen with an amber inner wash + rim halo (scanlines
   from the originals still ride on top via .*-board::after). */
.jm-board, .cab-board, .cgt-board {
  background: linear-gradient(180deg, #0a0f0d, #05080a 50%, #080d0b) !important;
  box-shadow: inset 0 2px 7px rgba(0,0,0,.9), inset 0 0 14px rgba(var(--eva-warn),.16), 0 0 12px rgba(var(--eva-rim),.3) !important;
}
/* Flanking rule-lines adopt the panel's rim colour so each unit reads as one instrument. */
.jackpot-divider::before { background: linear-gradient(90deg, transparent, rgba(46,229,124,.5) 70%, rgba(46,229,124,.85)) !important; box-shadow: 0 0 8px rgba(46,229,124,.5) !important; }
.jackpot-divider::after  { background: linear-gradient(270deg, transparent, rgba(46,229,124,.5) 70%, rgba(46,229,124,.85)) !important; box-shadow: 0 0 8px rgba(46,229,124,.5) !important; }
.craft-divider::before, .cabinet-divider::before, .cc-gallery-divider::before { background: linear-gradient(90deg, transparent, rgba(154,55,236,.5) 70%, rgba(154,55,236,.85)) !important; box-shadow: 0 0 8px rgba(154,55,236,.5) !important; }
.craft-divider::after, .cabinet-divider::after, .cc-gallery-divider::after { background: linear-gradient(270deg, transparent, rgba(154,55,236,.5) 70%, rgba(154,55,236,.85)) !important; box-shadow: 0 0 8px rgba(154,55,236,.5) !important; }
@media (prefers-reduced-motion: reduce) {
  .jackpot-meter::before, .cabinet-btn::before, .cabinet-display::before, .cc-gallery-toggle::before { animation: none !important; }
}

.cc-gallery-drop { margin-top: 12px; }
.cc-gallery-drop[hidden] { display: none; }
/* 3-up gallery grid that fills the collection COLUMN (so it lines up the same on
   desktop and mobile, where the column is just half the panel). */
.crafting-collection #inventoryGrid {
  --gal-gap: clamp(5px, 1.6vw, 12px);
  grid-template-columns: 1fr 1fr 1fr;
  gap: var(--gal-gap);
  width: 100%; max-width: 640px; margin: 0 auto;
}
/* Only 1–2 cards → centre them, but at the SAME width a 3-up card would be (one third
   of the row minus the gaps), so a lone card isn't bigger than when there are several. */
.crafting-collection.cc-few:not(.cc-page) #inventoryGrid {
  grid-template-columns: repeat(var(--cc-n, 1), calc((100% - 2 * var(--gal-gap)) / 3));
  justify-content: center;
}
@media (max-width: 640px) {
  .craft-divider { max-width: 366px; margin: 12px auto 5px; }
  .cabinet-btn, .cabinet-display { padding: 5px 13px 7px; }
  .cab-text { font-size: 13px; }
  .cab-emoji { font-size: 19px; }
  .cab-label { font-size: 8px; letter-spacing: .24em; gap: 6px; }
  .cab-bulb { width: 6px; height: 6px; }
  .cabinet-divider { gap: 10px; max-width: 420px; }
  .cabinet-divider::before, .cabinet-divider::after { max-width: 32%; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Mascot "Rippy" — side-by-side anime reaction helper
   ═══════════════════════════════════════════════════════════════════════════ */
.mascot {
  position: fixed; left: 0; bottom: 12px; z-index: 60;
  width: 132px; pointer-events: none; display: none;
  filter: drop-shadow(0 6px 14px rgba(0,0,0,.45));
}
/* Shown on the rip view (where pulls happen) and in the gallery/crafting view,
   so she stays with the player; never over the admin panel. */
body.rip-active .mascot,
body.gallery-active .mascot { display: block; }
.mascot-svg { width: 100%; height: auto; display: block; animation: mascotFloat 3.6s ease-in-out infinite; transform-origin: 50% 100%; }
@keyframes mascotFloat { 0%,100% { transform: translateY(0) rotate(-1deg); } 50% { transform: translateY(-7px) rotate(1deg); } }
/* Real-image mascot as a circular neon portrait (clips arms/white edges cleanly). */
.mascot { width: 128px; }
.mascot-img {
  width: 128px; height: 128px; border-radius: 50%; object-fit: cover; object-position: 50% 45%;
  background: #11182c;
  border: 3px solid #ffb260;
  box-shadow: 0 0 0 2px rgba(8,10,20,.6), 0 6px 18px rgba(0,0,0,.5),
              0 0 18px rgba(255,150,60,.55), inset 0 0 14px rgba(255,150,60,.25);
}
.mascot[data-mood="excited"] .mascot-img,
.mascot[data-mood="starstruck"] .mascot-img { border-color: #ffd24a; box-shadow: 0 0 0 2px rgba(8,10,20,.6), 0 6px 18px rgba(0,0,0,.5), 0 0 26px rgba(255,210,74,.8); }
/* Sparkle FX overlay — shown when she's excited / starstruck. */
.mascot-fx { position: absolute; inset: -10px; pointer-events: none; opacity: 0; transition: opacity .2s; z-index: 2; }
.mascot[data-mood="excited"] .mascot-fx, .mascot[data-mood="starstruck"] .mascot-fx { opacity: 1; }
.mascot-fx span { position: absolute; font-size: 17px; filter: drop-shadow(0 0 6px #ffe9a8); animation: fxTwinkle 1.1s infinite; }
.mascot-fx span:nth-child(1) { top: 3%; left: -9%; }
.mascot-fx span:nth-child(2) { top: 20%; right: -7%; font-size: 13px; animation-delay: .35s; }
.mascot-fx span:nth-child(3) { top: 50%; left: -11%; font-size: 14px; animation-delay: .7s; }
@keyframes fxTwinkle { 0%,100% { transform: scale(.5); opacity: .3; } 50% { transform: scale(1.12); opacity: 1; } }

/* Expression sets: show only the active mood's group. */
.mascot .face-set { display: none; }
.mascot[data-mood="idle"]       .f-idle,
.mascot[data-mood="happy"]      .f-happy,
.mascot[data-mood="excited"]    .f-excited,
.mascot[data-mood="starstruck"] .f-starstruck { display: block; }

/* Idle blink */
.mascot[data-mood="idle"] .m-blink { animation: mascotBlink 4.2s infinite; transform-origin: center; }
@keyframes mascotBlink { 0%,93%,100% { transform: scaleY(1); } 96% { transform: scaleY(.1); } }

/* Reaction bounce + a little overshoot when a mood is set */
.mascot.react .mascot-svg { animation: mascotReact .6s cubic-bezier(.2,.8,.3,1.3); }
@keyframes mascotReact { 0% { transform: scale(.86) translateY(6px); } 45% { transform: scale(1.14) translateY(-10px) rotate(2deg); } 100% { transform: scale(1) translateY(0); } }
.mascot[data-mood="excited"] .mascot-svg,
.mascot[data-mood="starstruck"] .mascot-svg { animation: mascotBounce .5s ease-in-out infinite alternate; }
@keyframes mascotBounce { from { transform: translateY(0) rotate(-2deg); } to { transform: translateY(-6px) rotate(2deg); } }

/* Star eyes pulse + sparkles twinkle on the big hit */
.mascot .m-stareye { animation: starEye .7s ease-in-out infinite alternate; transform-origin: center; }
@keyframes starEye { from { transform: scale(.9); } to { transform: scale(1.12); } }
.mascot .m-sparkles { animation: sparkleTwinkle .9s linear infinite; transform-origin: center; }
@keyframes sparkleTwinkle { 0%,100% { opacity: .5; } 50% { opacity: 1; } }
.mascot .m-crystal { animation: sparkleTwinkle 1.6s ease-in-out infinite; transform-origin: 180px 197px; }

/* Speech bubble */
.mascot-bubble {
  position: absolute; left: 96px; bottom: 118px; min-width: 86px; max-width: 190px;
  padding: 8px 12px; border-radius: 13px 13px 13px 3px;
  background: linear-gradient(180deg, #ffffff, #eaf6ff);
  color: #16314a; font: 800 13px/1.25 system-ui, sans-serif; text-align: center;
  box-shadow: 0 6px 16px rgba(0,0,0,.35), 0 0 0 2px rgba(106,215,255,.6);
  opacity: 0; transform: translateY(6px) scale(.8); transform-origin: 0 100%;
  transition: opacity .18s ease, transform .22s cubic-bezier(.2,.8,.3,1.4);
  pointer-events: none;
}
.mascot-bubble.show { opacity: 1; transform: translateY(0) scale(1); }
.mascot-bubble::after {
  content: ''; position: absolute; left: 6px; bottom: -7px;
  border: 7px solid transparent; border-top-color: #eaf6ff; border-bottom: 0;
}

@media (max-width: 640px) {
  .mascot { width: 88px; left: 0; bottom: 8px; }
  .mascot-bubble { left: 64px; bottom: 82px; font-size: 11px; min-width: 64px; padding: 6px 9px; }
  /* In the GALLERY on a phone she'd otherwise be ~60vh wide (min(500px,60vh)) and eat the
     whole bottom-left, covering the SHIP/DISENCHANT row. Keep her a tidier corner presence
     here (same ~0.855 ratio) so she reads as standing behind the edge, not blocking the UI.
     The rip view keeps her full size — that's where she's the star. */
  body.gallery-active .mascot.has-live2d { width: min(300px, 56vw); height: min(351px, 66vw); }
}
@media (prefers-reduced-motion: reduce) {
  .mascot-svg, .mascot.react .mascot-svg, .mascot .m-stareye, .mascot .m-sparkles,
  .mascot[data-mood="excited"] .mascot-svg, .mascot[data-mood="starstruck"] .mascot-svg { animation: none; }
}

/* ── Admin mascot expression installer ─────────────────────────────────────── */
.admin-mascot { padding: 14px 16px 22px; border-top: 1px solid var(--line); }
.adm-mascot-head h3 { margin: 0 0 4px; font-size: 16px; color: #fff; }
.adm-mascot-head p { margin: 0 0 12px; font-size: 12.5px; color: #9fb0c6; max-width: 640px; line-height: 1.45; }
.adm-mascot-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; }
.amx-tile { display: flex; flex-direction: column; gap: 4px; }
.amx-drop {
  position: relative; aspect-ratio: 1; border-radius: 12px; cursor: pointer; overflow: hidden;
  background: rgba(255,255,255,.04);
  border: 2px dashed rgba(208,123,255,.45);
  display: flex; align-items: center; justify-content: center;
  transition: border-color .15s ease, background .15s ease;
}
.amx-tile.has-img .amx-drop { border-style: solid; border-color: rgba(208,123,255,.6); }
.amx-tile.amx-over .amx-drop { border-color: #d07bff; background: rgba(208,123,255,.16); }
.amx-thumb { width: 100%; height: 100%; object-fit: cover; display: block; }
.amx-empty { text-align: center; font: 700 13px/1.5 system-ui, sans-serif; color: #b48fd0; letter-spacing: .02em; }
.amx-del {
  position: absolute; top: 5px; right: 5px; width: 24px; height: 24px; border: 0; border-radius: 7px;
  background: rgba(10,12,22,.7); color: #fff; font-size: 17px; line-height: 1; cursor: pointer;
}
.amx-del:hover { background: #c0392b; }
.amx-label { font-weight: 800; font-size: 13px; color: #eaf0fa; }
.amx-hint { font-size: 11px; color: #8595ab; line-height: 1.3; }

/* ── Live2D rig: when a model is installed it replaces the image portrait ───── */
/* Until the mascot state is known, hide the portrait so the old art never
   flashes before a Live2D rig (if installed) takes over. */
.mascot.mascot-pending .mascot-img { display: none; }
/* Sized relative to viewport height (vh) so she always caps to a fraction of the
   screen — stays small on short/landscape phones — and anchored to bottom:0 so her
   crop sits at the screen edge (she looks like she stands behind it, not amputated). */
/* Keep the SAME width:height ratio (~0.855) on every device so her framing is
   consistent; only the size changes per screen. left:0 anchors her bottom-left. */
.mascot.has-live2d { width: min(500px, 60vh); height: min(585px, 70vh); left: 0;
  /* -1px so subpixel rounding on mobile never leaves a 1px gap under her feet. */
  bottom: -1px; overflow: hidden;
  /* Her IMAGE fills the whole box, but only the .mascot-hit region below is
     clickable — so her interactable footprint stays tight around her, not the
     wide transparent canvas around her. */
  pointer-events: none; cursor: pointer; touch-action: none; z-index: 6000;
  /* Transition on the BASE element so BOTH mute and unmute animate the filter —
     the animating filter forces the composited WebGL layer to re-rasterise, which
     is what un-sticks the stale grayscale raster on iOS Safari. */
  transition: filter .3s ease; }
body.mascot-preview-on .mascot.has-live2d { cursor: grab; }
/* Muted ("shut up" via long-press): greyed-out, lifeless. The rig also freezes in
   a hurt/crying wince (Live2DMascot.setHurt) and all game audio is muted. */
.mascot.mascot-muted { filter: grayscale(1) brightness(.62) contrast(.92); transition: filter .3s ease; }
.mascot.has-live2d .l2d-canvas { pointer-events: none; }
/* Hit-area: the only interactive part of the mascot. Defaults to the full box
   (e.g. the small SVG fallback), but in Live2D mode it's a tight column over her
   body so clicks in the empty canvas around her pass straight through. */
.mascot-hit { position: absolute; inset: 0; z-index: 4; pointer-events: auto; cursor: inherit; }
/* Live2D: her WHOLE layer is pointer-events:none, so every tap routes natively to the
   wheel/buttons behind her. A window capture-phase listener (see app.js _pointOnMascot)
   re-adds HER interactivity ONLY where a real rendered pixel is hit — the pixel-perfect
   alpha test (hitPixel) is her true clickable silhouette. So the wide transparent canvas
   around her no longer eats clicks. */
.mascot.has-live2d .mascot-hit { inset: 0; pointer-events: none; }
/* During the reveal/victory the mascot (a fixed layer at z-index 6000 in Live2D
   mode) sits ABOVE the reveal content, so ANYTHING in her subtree that is
   pointer-events:auto could swallow taps meant for the CLAIM button. The hit-area
   alone isn't enough — the Live2D <canvas> (and anything PIXI injects) lives here
   too. Force the WHOLE mascot subtree non-interactive for the duration so CLAIM is
   always the hit target, no matter what's overlapping it. */
body.rip-reveal .mascot,
body.rip-reveal .mascot *,
body.craft-reveal .mascot,
body.craft-reveal .mascot *,
body.rip-reveal .mascot-hit,
body.craft-reveal .mascot-hit { pointer-events: none !important; }
/* Force her visible + above the admin panel while the admin previews her
   (admin view isn't rip-active and sits above the mascot's normal z-index). */
body.mascot-preview-on .mascot.has-live2d { display: block; z-index: 12050; }
/* Click pop: she briefly scales up, then settles. Grows from her base. */
.mascot.mascot-poke { animation: mascotPoke .55s cubic-bezier(.2,.9,.3,1.4); transform-origin: 50% 100%; }
@keyframes mascotPoke {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.16); }
  60%  { transform: scale(1.06); }
  100% { transform: scale(1); }
}
/* Spammed too fast → annoyed head/body shake (no scale-up). */
.mascot.mascot-annoyed { animation: mascotShake .5s ease; transform-origin: 50% 100%; }
@keyframes mascotShake {
  0%, 100% { transform: translateX(0) rotate(0); }
  20% { transform: translateX(-7px) rotate(-2.5deg); }
  40% { transform: translateX(7px) rotate(2.5deg); }
  60% { transform: translateX(-5px) rotate(-1.5deg); }
  80% { transform: translateX(4px) rotate(1deg); }
}
.mascot.has-live2d .mascot-img { display: none; }
.mascot .l2d-canvas { display: none; }
.mascot.has-live2d .l2d-canvas { display: block; width: 100%; height: 100%; }
.mascot.has-live2d .mascot-fx { inset: 0; }
/* Word balloon floats over her head (top-centre) with a downward tail. */
.mascot.has-live2d .mascot-bubble {
  /* Sit just above her HEAD (which is framed in the upper area of the canvas),
     not floating at the very top of the tall container. */
  left: 50%; top: 11%; bottom: auto; right: auto;
  transform: translate(-50%, -8px) scale(.85); transform-origin: 50% 100%;
  border-radius: 13px; z-index: 5;
}
.mascot.has-live2d .mascot-bubble.show { transform: translate(-50%, 0) scale(1); }
.mascot.has-live2d .mascot-bubble::after {
  left: 50%; right: auto; bottom: -7px; transform: translateX(-50%);
}
/* Mobile: a compact head-shot in the bottom-LEFT corner so she stays out of the
   way of the play area (the rig itself is zoomed tighter + pushed left in fit()). */
@media (max-width: 640px) { .mascot.has-live2d { width: min(230px, 56vw); height: min(300px, 42vh); left: 0; } }

/* ── Admin Live2D installer ────────────────────────────────────────────────── */
.adm-l2d { margin-top: 18px; padding-top: 14px; border-top: 1px dashed var(--line); }
.adm-l2d-install > summary { cursor: pointer; font-size: 13px; font-weight: 700; color: #9fb0c6; list-style: none; padding: 4px 0; }
.adm-l2d-install > summary::-webkit-details-marker { display: none; }
.adm-l2d-install > summary::before { content: '▸ '; color: #6ad7ff; }
.adm-l2d-install[open] > summary::before { content: '▾ '; }
.adm-l2d-install[open] > summary { margin-bottom: 8px; }
.adm-l2d h4 { margin: 0 0 4px; font-size: 14px; color: #fff; }
.adm-l2d p { margin: 0 0 10px; font-size: 12px; color: #9fb0c6; line-height: 1.45; max-width: 640px; }
.adm-l2d-drop {
  border: 2px dashed rgba(106,215,255,.45); border-radius: 12px; padding: 18px; text-align: center;
  cursor: pointer; color: #bcd6e8; font-size: 13px; background: rgba(106,215,255,.05);
  transition: border-color .15s ease, background .15s ease;
}
.adm-l2d-drop.amx-over { border-color: #6ad7ff; background: rgba(106,215,255,.14); }
.adm-l2d-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 10px; }
.adm-l2d-row .btn-ghost { color: #ff8d8d; }
.adm-l2d-tree {
  margin: 12px 0 0; padding: 10px 12px; border-radius: 10px; max-height: 240px; overflow: auto;
  background: rgba(4,7,16,.6); border: 1px solid var(--line);
  font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  color: #b9c6da; white-space: pre; -webkit-overflow-scrolling: touch;
}

/* "Updating" overlay — shown while a new build deploys; covers the ice screen so
   it can't be broken into a stale build, and clears on reload to the new build. */
#updateOverlay {
  position: fixed; inset: 0; z-index: 2147483646; display: none;
  align-items: center; justify-content: center; padding: 24px; text-align: center;
  color: #eaf3ff;
  background: radial-gradient(circle at 50% 38%, rgba(22,44,78,.9), rgba(4,7,16,.97));
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
}
#updateOverlay .upd-card { max-width: 420px; }
#updateOverlay h2 { margin: 16px 0 8px; font: 800 22px/1.2 system-ui, sans-serif; }
#updateOverlay p { margin: 0; color: #a9c2e0; font-size: 14px; line-height: 1.5; }
.upd-spin { width: 46px; height: 46px; margin: 0 auto; border-radius: 50%;
  border: 4px solid rgba(120,200,255,.22); border-top-color: #6ad7ff;
  animation: updSpin .9s linear infinite; }
@keyframes updSpin { to { transform: rotate(360deg); } }

/* Animation lab — grid of one-shot preview buttons */
.adm-anim-grid { display: flex; flex-wrap: wrap; gap: 8px; }
.adm-anim-grid .adm-anim-btn { flex: 0 0 auto; }

/* Default idle animation picker */
.adm-idle { margin-top: 18px; padding-top: 14px; border-top: 1px dashed var(--line); }
.adm-idle h4 { margin: 0 0 4px; font-size: 14px; color: #fff; }
.adm-idle p { margin: 0 0 10px; font-size: 12px; color: #9fb0c6; line-height: 1.45; max-width: 640px; }
.adm-idle-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 10px; }
.adm-idle-tile {
  display: flex; flex-direction: column; gap: 4px; text-align: left; cursor: pointer;
  padding: 10px 12px; border-radius: 11px; background: rgba(255,255,255,.04);
  border: 2px solid rgba(208,123,255,.28); color: #eaf0fa;
  transition: border-color .15s ease, background .15s ease, transform .1s ease;
}
.adm-idle-tile:hover { border-color: rgba(208,123,255,.55); background: rgba(208,123,255,.10); }
.adm-idle-tile.sel { border-color: #d07bff; background: rgba(208,123,255,.18); }
.adm-idle-name { font-weight: 800; font-size: 13.5px; }
.adm-idle-desc { font-size: 11.5px; color: #b9a7cf; line-height: 1.35; }

/* ═══════════════════════════════════════════════════════════════════════════
   Ship cards home (UPS) — address modal + shipment list
   ═══════════════════════════════════════════════════════════════════════════ */
.cc-ship-link { margin-left: auto; }
.ship-overlay {
  position: fixed; inset: 0; z-index: 12000; display: flex; align-items: center; justify-content: center;
  background: rgba(4, 8, 14, .72); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); padding: 16px;
}
.ship-modal {
  width: 100%; max-width: 460px; max-height: 90vh; overflow: auto;
  background: linear-gradient(180deg, #141b27, #0b1019); color: #eaf1fb;
  border: 1px solid rgba(120, 170, 230, .28); border-radius: 16px;
  box-shadow: 0 20px 60px rgba(0,0,0,.6), 0 0 28px rgba(69,177,255,.16);
}
.ship-head { display: flex; align-items: center; justify-content: space-between; padding: 16px 18px 10px; }
.ship-head h3 { margin: 0; font: 900 17px/1.2 system-ui, sans-serif; }
.ship-x { background: none; border: none; color: #9fb0c3; font-size: 24px; line-height: 1; cursor: pointer; padding: 0 4px; }
.ship-x:hover { color: #fff; }
.ship-body { padding: 4px 18px 8px; }
.ship-note { font: 500 12.5px/1.45 system-ui, sans-serif; color: #aebccd; margin: 2px 0 12px; }
.ship-ok { font: 700 14px/1.4 system-ui, sans-serif; color: #cfe8ff; margin: 6px 0 14px; }
.ship-form { display: flex; flex-direction: column; gap: 9px; }
.ship-row { display: flex; gap: 9px; }
.ship-row .ship-f { flex: 1 1 0; min-width: 0; }
.ship-f { display: flex; flex-direction: column; gap: 4px; }
.ship-f > span { font: 700 11px/1 system-ui, sans-serif; color: #9db0c6; letter-spacing: .02em; }
.ship-f input {
  width: 100%; box-sizing: border-box; padding: 10px 11px; border-radius: 9px;
  background: rgba(8, 13, 21, .9); border: 1px solid rgba(120,150,190,.32); color: #fff;
  font: 600 14px/1.2 system-ui, sans-serif;
}
.ship-f input:focus { outline: none; border-color: #45b1ff; box-shadow: 0 0 0 2px rgba(69,177,255,.25); }
.ship-status { font: 700 12px/1.4 system-ui, sans-serif; margin-top: 10px; min-height: 1px; color: #9fb0c3; }
.ship-status.err { color: #ff6b6b; }
.ship-status.ok { color: #3af07c; }
.ship-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 12px 18px 16px; }
.ship-track {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  background: rgba(69,177,255,.1); border: 1px solid rgba(69,177,255,.4); border-radius: 11px; padding: 11px 14px; margin: 4px 0 12px;
}
.ship-track-label { font: 800 11px/1 system-ui, sans-serif; color: #8fc8ff; text-transform: uppercase; letter-spacing: .05em; }
.ship-track-num { font: 800 15px/1 ui-monospace, monospace; color: #fff; text-decoration: none; word-break: break-all; }
.ship-track-num:hover { text-decoration: underline; }
.ship-label-btn { display: inline-block; text-decoration: none; }
/* Shipment list */
.ship-item { border: 1px solid rgba(120,150,190,.22); border-radius: 12px; padding: 11px 13px; margin-bottom: 10px; background: rgba(10,16,25,.6); }
.ship-item-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
.ship-when { font: 600 11px/1 system-ui, sans-serif; color: #8a9bb0; }
.ship-item-cards { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 7px; font: 600 13px/1.35 system-ui, sans-serif; color: #dce7f4; }
/* Card thumbnails inside a player's shipment order */
.ship-card { width: 56px; display: flex; flex-direction: column; align-items: center; gap: 3px; }
.ship-card img, .ship-card-ph {
  width: 56px; height: 78px; border-radius: 6px; object-fit: cover; display: block;
  background: rgba(8,13,21,.9); border: 1px solid rgba(120,150,190,.3);
}
.ship-card-ph { display: flex; align-items: center; justify-content: center; font: 900 20px/1 system-ui, sans-serif; color: #6b7c91; }
.ship-card-name { font: 600 9.5px/1.2 system-ui, sans-serif; color: #aebccd; text-align: center; max-width: 56px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ship-item-track { font: 700 12px/1.3 system-ui, sans-serif; color: #cfe8ff; }
.ship-item-track.muted { color: #7e8ea2; font-weight: 600; }
.ship-item-track a { color: #6cc0ff; text-decoration: none; }
.ship-item-track a:hover { text-decoration: underline; }
.ship-pill {
  display: inline-block; font: 800 10.5px/1 system-ui, sans-serif; text-transform: uppercase; letter-spacing: .04em;
  padding: 4px 9px; border-radius: 999px; background: rgba(120,150,190,.2); color: #cdd9e8; border: 1px solid rgba(150,180,220,.3);
}
.ship-pill-label_created, .ship-pill-shipped, .ship-pill-in_transit { background: rgba(69,177,255,.18); color: #9fd4ff; border-color: rgba(69,177,255,.5); }
.ship-pill-delivered { background: rgba(72,213,151,.18); color: #8ff0c0; border-color: rgba(72,213,151,.5); }
.ship-pill-cancelled { background: rgba(255,107,107,.16); color: #ffb0b0; border-color: rgba(255,107,107,.45); }
@media (max-width: 480px) { .ship-row { flex-direction: column; gap: 9px; } }

/* ── Gallery SHIPMENT CARDS — each shipment is its own card with a big shipping-box
   icon; tap it to open that shipment's details inline (below the row). ────────── */
.gallery-shipments { margin: 4px auto 18px; width: 100%; }
.gship-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 12px;
  justify-content: center; max-width: 720px; margin: 0 auto;
}
.gship-tile {
  -webkit-appearance: none; appearance: none; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  padding: 16px 10px 12px; border-radius: 14px;
  background: radial-gradient(120% 90% at 50% -10%, rgba(40,58,86,.55), rgba(10,14,24,.96));
  border: 1px solid rgba(120,170,225,.34);
  box-shadow: 0 8px 22px rgba(0,0,0,.4), inset 0 0 24px rgba(69,177,255,.05);
  transition: transform .12s ease, box-shadow .15s ease, border-color .15s ease;
}
.gship-tile:hover { transform: translateY(-3px); border-color: rgba(120,200,255,.6); box-shadow: 0 12px 28px rgba(0,0,0,.5), 0 0 16px rgba(69,177,255,.25); }
.gship-tile.gship-open { border-color: rgba(120,210,255,.9); box-shadow: 0 0 0 1.5px rgba(120,210,255,.5), 0 12px 28px rgba(0,0,0,.5); }
.gship-ico { font-size: 52px; line-height: 1; filter: drop-shadow(0 4px 8px rgba(0,0,0,.5)); }
.gship-status {
  font: 900 12px/1 system-ui, sans-serif; text-transform: uppercase; letter-spacing: .05em; color: #dbebff;
  text-shadow: 0 1px 3px rgba(0,0,0,.7);
}
.gship-meta { font: 700 10.5px/1.2 system-ui, sans-serif; color: #9fb2c8; text-align: center; }
.gship-trk { font: 800 9px/1 system-ui, sans-serif; letter-spacing: .06em; color: #8ff0c0; }
.gship-trk.muted { color: #7e8ea2; font-weight: 700; }
/* Inline detail panel that opens under the row for the tapped shipment. */
.gship-detail {
  max-width: 720px; margin: 14px auto 0; padding: 14px 16px; border-radius: 14px;
  background: radial-gradient(120% 90% at 50% -10%, rgba(40,58,86,.5), rgba(9,12,22,.97));
  border: 1px solid rgba(120,200,255,.4); box-shadow: 0 14px 40px rgba(0,0,0,.5);
}
.gship-detail-head { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
.gship-ico-lg { font-size: 40px; line-height: 1; filter: drop-shadow(0 3px 6px rgba(0,0,0,.5)); }
.gship-d-titles { display: flex; flex-direction: column; gap: 4px; flex: 1 1 auto; }
.gship-d-status { align-self: flex-start; display: inline-block; font: 800 11px/1 system-ui, sans-serif; text-transform: uppercase; letter-spacing: .04em; padding: 4px 9px; border-radius: 999px; }
.gship-d-when { font: 600 12px/1 system-ui, sans-serif; color: #9fb2c8; }
.gship-d-close {
  -webkit-appearance: none; appearance: none; cursor: pointer; flex: 0 0 auto;
  width: 30px; height: 30px; border-radius: 8px; border: 1px solid rgba(150,180,220,.3);
  background: rgba(30,44,64,.7); color: #cdd9e8; font: 700 18px/1 system-ui, sans-serif;
}
.gship-d-close:hover { color: #fff; background: rgba(50,70,96,.8); }
.gship-d-track { font: 700 13px/1.4 system-ui, sans-serif; color: #cfe8ff; margin-bottom: 8px; }
.gship-d-track.muted { color: #8296ac; font-weight: 600; }
.gship-d-track a { color: #6cc0ff; text-decoration: none; } .gship-d-track a:hover { text-decoration: underline; }
.gship-d-to { font: 600 12px/1.4 system-ui, sans-serif; color: #aebccd; margin-bottom: 10px; }
@media (max-width: 480px) {
  .gship-grid { grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 9px; }
  .gship-ico { font-size: 42px; }
}

/* Admin shipments fulfillment panel */
.admin-shipments { padding: 14px 16px; border-top: 1px solid rgba(120,150,190,.18); margin-top: 8px; }
.adm-ship-head { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.adm-ship-head h3 { margin: 0 auto 0 0; font: 900 15px/1.2 system-ui, sans-serif; color: #eaf1fb; }
/* Restock-all: armed (second-tap) confirm state reads as a destructive action. */
.adm-ship-restock.armed { border-color: #ff6b6b !important; color: #ff9b9b !important; background: rgba(255,107,107,.12) !important; }
.adm-ship-list { display: flex; flex-direction: column; gap: 10px; }
.adm-ship-item { border: 1px solid rgba(120,150,190,.22); border-radius: 11px; padding: 11px 13px; background: rgba(10,16,25,.5); }
.adm-ship-to { font: 700 13px/1.3 system-ui, sans-serif; color: #eaf1fb; }
.adm-ship-addr { font: 500 12px/1.4 system-ui, sans-serif; color: #aebccd; margin: 3px 0; }
.adm-ship-cards { font: 600 12px/1.4 system-ui, sans-serif; color: #cfe0f2; margin-bottom: 6px; }
.adm-ship-label { display: inline-block; font: 700 12px/1 system-ui, sans-serif; color: #6cc0ff; text-decoration: none; margin-bottom: 8px; }
.adm-ship-label:hover { text-decoration: underline; }
.adm-ship-controls { display: flex; flex-wrap: wrap; gap: 7px; align-items: center; }
.adm-ship-controls select, .adm-ship-controls input {
  padding: 7px 9px; border-radius: 8px; background: rgba(8,13,21,.9);
  border: 1px solid rgba(120,150,190,.32); color: #fff; font: 600 12.5px/1.2 system-ui, sans-serif;
}
.adm-ship-controls .adm-ship-track { flex: 1 1 150px; min-width: 120px; }
.adm-ship-msg { font: 700 11.5px/1.4 system-ui, sans-serif; margin-top: 6px; min-height: 1px; }
.adm-ship-msg.ok { color: #48d597; }
.adm-ship-msg.err { color: #ff6b6b; }

/* Card thumbnails inside an admin shipment order */
.adm-ship-cards { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 8px; }
.adm-ship-card { width: 60px; display: flex; flex-direction: column; align-items: center; gap: 3px; }
.adm-ship-card img, .adm-ship-card-ph {
  width: 60px; height: 84px; border-radius: 6px; object-fit: cover; display: block;
  background: rgba(8,13,21,.9); border: 1px solid rgba(120,150,190,.3);
}
.adm-ship-card-ph { display: flex; align-items: center; justify-content: center; font: 900 22px/1 system-ui, sans-serif; color: #6b7c91; }
.adm-ship-card-name { font: 600 9.5px/1.2 system-ui, sans-serif; color: #aebccd; text-align: center; max-width: 60px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ═══════════════════════════════════════════════════════════════════════════
   MOBILE BATTERY SAVER
   The full-screen ambient FX (rising shafts, blurred glow/fog, sparkles, edge
   marquee, ember particles) animate 24/7. Animating large blurred + blend layers
   is the single biggest battery/heat cost on phones — and any backdrop-filter in
   front of them re-blurs every frame as they move. While IDLE on mobile we freeze
   that ambient motion (it still plays during the actual spin/reveal/craft) and
   drop backdrop-filters. The carousel + Live2D mascot still move, so the page
   stays alive — it just stops burning the GPU on background eye-candy.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 640px) {
  body:not(.spin-cine):not(.rip-reveal):not(.craft-reveal) .fx-scene::before,
  body:not(.spin-cine):not(.rip-reveal):not(.craft-reveal) .fx-scene::after,
  body:not(.spin-cine):not(.rip-reveal):not(.craft-reveal) .fx-scene .cfx-glow,
  body:not(.spin-cine):not(.rip-reveal):not(.craft-reveal) .fx-scene .cfx-fog,
  body:not(.spin-cine):not(.rip-reveal):not(.craft-reveal) .fx-scene .sfx-rays,
  body:not(.spin-cine):not(.rip-reveal):not(.craft-reveal) .fx-scene .sfx-rays2,
  body:not(.spin-cine):not(.rip-reveal):not(.craft-reveal) .fx-scene .sfx-sparkles,
  body:not(.spin-cine):not(.rip-reveal):not(.craft-reveal) .fx-scene .sfx-sparkles::before,
  body:not(.spin-cine):not(.rip-reveal):not(.craft-reveal) .fx-scene .sfx-sparkles::after,
  body:not(.spin-cine):not(.rip-reveal):not(.craft-reveal) .sc-scene-glow,
  body:not(.spin-cine):not(.rip-reveal):not(.craft-reveal) .casino-lights {
    animation: none !important;
  }
  /* Frozen ember/bubble particles look like stuck dots — hide them while idle
     instead (they return during the spin/reveal). */
  body:not(.spin-cine):not(.rip-reveal):not(.craft-reveal) .fx-scene .cfx-bubbles { display: none; }
  /* backdrop-filter is very expensive on mobile; the panels read fine on their
     own (semi-opaque) dark backgrounds without the live blur. */
  * { backdrop-filter: none !important; -webkit-backdrop-filter: none !important; }
}

/* ══════════════════════════════════════════════════════════════════════════════
   HDR / OLED TRUE-BLACK BASE + PER-PAGE THEME TINT
   The app runs on HDR/OLED screens — the base is now PURE #000 (deepest possible black,
   maximum contrast so neon + cards pop hardest). Each page then lays only a faint HINT of
   its own theme colour over that black: green on RIP, purple on CRAFT, blue on GALLERY.
   Panels keep a small lift (--bg-mid / --bg-panel) so they still read as raised. Appended
   last so it wins the cascade. */
:root {
  --bg-deep:  #000000;   /* was #07090E — now true black (also the on-gold text colour) */
  --bg-mid:   #06080e;   /* faint lift for raised panels over the black */
  --bg-panel: #0b101a;
  /* ── Rarity theme: white commons · black (obsidian) uncommons · purple rares · green
     legendaries (the top "foil chase" tier). Drives every --tier-glow-color. ── */
  --tier-common:     #FFFFFF;   /* white */
  --tier-uncommon:   #6E7482;   /* obsidian / gunmetal "black" (visible on the black bg) */
  --tier-rare:       #C08BFF;   /* purple */
  --tier-foil-chase: #34E27C;   /* legendary green */
}
/* Pure-black floor; the empty canvas + overscroll area go fully dark. */
html {
  background-color: #000 !important;
  background-image: none !important;
}
/* Per-page tint: deepest black with ONLY a hint of that page's theme colour. */
body { background: #000 !important; transition: background .25s ease; }
body.rip-active {
  background:
    radial-gradient(ellipse 120% 62% at 50% -4%, rgba(46,229,124,.13), transparent 58%),
    radial-gradient(ellipse 120% 46% at 50% 104%, rgba(46,229,124,.07), transparent 62%),
    #000 !important;
}
body.craft-active {
  background:
    radial-gradient(ellipse 120% 62% at 50% -4%, rgba(176,96,255,.14), transparent 58%),
    radial-gradient(ellipse 120% 46% at 50% 104%, rgba(176,96,255,.07), transparent 62%),
    #000 !important;
}
body.gallery-active:not(.craft-active) {
  background:
    radial-gradient(ellipse 120% 62% at 50% -4%, rgba(120,180,255,.13), transparent 58%),
    radial-gradient(ellipse 120% 46% at 50% 104%, rgba(120,180,255,.06), transparent 62%),
    #000 !important;
}

/* ── Single-source rarity on the REVEAL card ──────────────────────────────────
   The big flipped reveal card faces used the old palette vars (--lavender/--skyblue/
   --gold) for their rarity border+glow, so they ignored rarity recolors. Point them at
   --tier-glow-color (set by the .tier-* class) so they match every other card. Foil keeps
   its animated pulse (foilChasePulse, now green) — only its static border is set here. */
.card-flip-wrap.tier-common .card-face,
.card-flip-wrap.tier-uncommon .card-face,
.card-flip-wrap.tier-rare .card-face {
  border-color: var(--tier-glow-color) !important;
  box-shadow: 0 0 0 1px var(--tier-glow-color),
    0 0 22px color-mix(in srgb, var(--tier-glow-color) 34%, transparent) !important;
}
.card-flip-wrap.tier-foil-chase .card-face { border-color: var(--tier-glow-color) !important; }

/* ══════════════════════════════════════════════════════════════════════════════
   BRAND HEADER  (teargirl / rdyrip / riprdy — set via window.BRAND)
   A fixed title strip at the very TOP, above the nav bar. The whole document shifts
   down by --brand-h (body padding) and the fixed nav chrome shifts down to match, so
   every page's existing top-spacing is preserved automatically — no per-page retuning.
   In prod (no user buttons) this strip is the app's header. */
:root { --brand-h: 44px; }
@media (max-width: 640px) { :root { --brand-h: 38px; } }
body { padding-top: var(--brand-h); }
/* Push the fixed userbar + the signed-out floating header + the buy sheet BELOW the strip. */
.player-hud { top: calc(12px + var(--brand-h)) !important; }
.player-hud.hud-docked.hud-fixed-top { top: calc(12px + var(--brand-h)) !important; }
body.signed-out #ripView .chase-section .chase-head { top: calc(12px + var(--brand-h)) !important; }
.buy-dropdown { top: calc(58px + var(--brand-h)); }
@media (max-width: 640px) {
  .player-hud.hud-docked.hud-fixed-top { top: var(--brand-h) !important; }
  body.signed-out #ripView .chase-section .chase-head { top: var(--brand-h) !important; }
  .buy-dropdown { top: calc(env(safe-area-inset-top) + 10px + var(--brand-h)); }
}
/* During the fullscreen spin/reveal the chrome slides away — hide the strip too. */
body.spin-cine .brand-bar, body.rip-reveal .brand-bar, body.craft-reveal .brand-bar { opacity: 0; pointer-events: none; }

.brand-bar {
  position: fixed; top: 0; left: 0; right: 0; height: var(--brand-h); z-index: 12080;
  display: flex; align-items: center; justify-content: center; pointer-events: none;
  background: linear-gradient(180deg, rgba(0,0,0,.94) 46%, rgba(0,0,0,.6) 78%, rgba(0,0,0,0));
  transition: opacity .3s ease;
}
/* The wordmark — a bright neon-glass gradient with a soft glow. */
.brand-title {
  font: 900 clamp(19px, 3vw, 27px)/1 'Baloo 2', system-ui, sans-serif;
  letter-spacing: .01em; white-space: nowrap; user-select: none; pointer-events: none;
  /* Neon anime GREEN (legendary) + PURPLE (rare) wordmark, matching the card rarity theme. */
  background: linear-gradient(92deg, #ffffff 0%, #7CFFC3 26%, #34E27C 46%, #C08BFF 72%, #ffffff 100%);
  -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; color: transparent;
  filter: drop-shadow(0 0 9px rgba(52,226,124,.55)) drop-shadow(0 0 20px rgba(192,139,255,.32)) drop-shadow(0 1px 2px rgba(0,0,0,.65));
}
/* Big wordmark hero on the ice-break screen — centred in the clear middle of the sheet,
   below the frozen USERS ONLINE cabinet (which sits up top). Absolute so it never disturbs
   the cabinet's seamless-shatter alignment. */
.brand-title-ice {
  position: absolute; top: 42%; left: 50%; transform: translate(-50%, -50%); z-index: 5;
  margin: 0; font-size: clamp(40px, 12vw, 84px); letter-spacing: 0; text-align: center;
  filter: drop-shadow(0 0 16px rgba(52,226,124,.7)) drop-shadow(0 0 34px rgba(192,139,255,.4)) drop-shadow(0 2px 5px rgba(0,0,0,.75));
}

/* Ice-screen USERS ONLINE cabinet: start HIDDEN and fade in only once JS has pinned it over
   the real cabinet beneath the glass (alignCraftWheel adds .ice-set) — so a fresh load never
   flashes it at its un-aligned default spot. Safety timeout reveals it if measurement fails. */
#iceOnlineMeter { opacity: 0; transition: opacity .28s ease; }
#iceOnlineMeter.ice-set { opacity: 1; }

/* Never show the built-in mascot PLACEHOLDER art (/img/rippy.png). It's a tight face-crop
   that looks nothing like the real Live2D render and reads as creepy when the rig fails to
   load (e.g. some Android browsers) — better to show no mascot than that. A genuinely
   admin-configured expression image (different src) still displays; the Live2D canvas is
   unaffected. */
.mascot-img[src$="/img/rippy.png"] { display: none !important; }
