/* =====================================================================
   RIP CINEMA v3 — one pack, one light, ten cards, no chrome.

   The dais, the ambient dust, the cut and the glitter are all CANVAS
   (rip_cinema.js). Nothing in this file draws a hard shape: no ellipse
   rims, no light shafts, no rings. CSS owns layout, the pack, the cards
   and the grail film; the canvas owns atmosphere.
   ===================================================================== */

.rcx {
  /* one settle curve for every card move, so nothing feels off-tempo */
  --set: cubic-bezier(.22, .84, .26, 1);
  position: fixed;
  inset: 0;
  z-index: 14000;
  overflow: hidden;
  opacity: 0;
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}
/* the layer goes live the instant the pack copy is painted — the pack is
   never invisible, only the room behind it is still bright */
.rcx.is-mounted { opacity: 1; }
.rcx.is-leaving { opacity: 0; transition: opacity 600ms ease; }

/* the room: fades in UNDER the already-visible pack, so the handoff from
   the stage into the cinema has no blink */
.rcx-veil {
  position: absolute;
  inset: 0;
  z-index: 0;
  opacity: 0;
  /* Fully opaque everywhere. The centre used to sit at 0.985: invisible on a
     desktop, but on Android Chrome a hardware-overlay <video> under a layer
     that is not quite opaque can punch through for a frame -- the stage's
     blue star video flashing behind the cards. */
  background-color: #04050b;
  background-image: radial-gradient(85% 70% at 50% 32%, rgba(13, 16, 32, 1) 0%, rgba(4, 5, 11, 1) 72%);
  transition: opacity 900ms ease;
}
.rcx-veil.is-live { opacity: 1; }
/* The blackout used to transition the veil's BACKGROUND from a
   full-screen radial gradient to flat black. Gradients repaint rather
   than composite, so every frame of that 700ms redrew the viewport.
   A flat black sheet crossfading on top is a compositor-only job. */
.rcx-veil::after {
  content: "";
  position: absolute;
  inset: 0;
  background: #010102;
  opacity: 0;
  transition: opacity 700ms ease;
}
.rcx.is-black .rcx-veil::after { opacity: 1; }

.rcx-cv { position: absolute; inset: 0; pointer-events: none; }
.rcx-cv-back { z-index: 1; }
.rcx-cv-front { z-index: 4; }
/* the near lip of the ring rises BETWEEN the viewer and the card, so it
   needs a layer above the deck (cards sit at 60-80) but under the plate */
.rcx-cv-fore { z-index: 85; }
/* a card that has arrived front and centre has broken through the near
   curtain and sits in front of it; while it is still travelling it does
   not, so you watch it pass through the glitter */
.rcx-card.is-through { z-index: 88 !important; }

.rcx-blink {
  position: absolute;
  inset: 0;
  z-index: 9;
  pointer-events: none;
  background: radial-gradient(66% 56% at 50% 44%,
    rgba(255, 255, 255, 0.72) 0%,
    rgba(255, 250, 235, 0.24) 55%,
    transparent 82%);
  opacity: 0;
}
.rcx-blink.is-flash { animation: rcx-room-flash 380ms cubic-bezier(.04,.7,.16,1) forwards; }
@keyframes rcx-room-flash { 0% { opacity: 1; } 100% { opacity: 0; } }

/* ------------------------------- the pack ------------------------------ */
/* placed + sized inline by JS; the twirl is a Web Animation */
.rcx-pack {
  position: absolute;
  z-index: 3;
  transform-style: preserve-3d;
  will-change: transform;
  /* The base state has to carry the perspective. Every animated state sets
     perspective(1500px), so without it here the moment an animation is
     removed the element falls back to a flat identity transform and the
     translateZ'd depth layers re-project — the pack visibly changes size and
     shape in one frame, right as it starts to dissolve. */
  transform: perspective(1500px);
  /* NO filter here. drop-shadow on a preserve-3d container re-renders the
     whole subtree offscreen every frame and flattens GPU compositing —
     with the depth stack inside, the twirl ran at 9fps. The dais grounds
     the pack instead. */
}
/* the box IS the wrapper: the image is scaled up and offset so its opaque
   region exactly fills this element (--fx/--fy = opaque size as a fraction
   of the file, --ox/--oy = where that region starts) */
.rcx-pack img {
  position: absolute;
  width: calc(100% / var(--fx, 1));
  height: calc(100% / var(--fy, 1));
  left: calc(-1 * var(--ox, 0) * 100% / var(--fx, 1));
  top: calc(-1 * var(--oy, 0) * 100% / var(--fy, 1));
  object-fit: fill;
}
.rcx-pack-depth {
  transform: translateZ(var(--pd, 0px));
  /* opacity, not filter: brightness(). A filter per layer forces its own
     offscreen pass every frame, and with the stack inside a preserve-3d
     parent that is what made the twirl chop. Opacity is composited. */
  opacity: var(--pdl, 0.7);
}
.rcx-pack-body {
  transform: translateZ(7px);
  backface-visibility: hidden;
  /* one shaped shadow on one image, not a filter over the whole stack */
  filter: drop-shadow(0 22px 34px rgba(0, 0, 0, 0.66));
}

.rcx-pack.is-landed { animation: rcx-pack-idle 3.6s ease-in-out infinite; }
@keyframes rcx-pack-idle {
  0%, 100% { transform: perspective(1500px) translateY(0); }
  50% { transform: perspective(1500px) translateY(-0.7%); }
}
/* the charge: it strains, it does not vibrate */
.rcx-pack.is-charging { animation: rcx-pack-strain 1180ms cubic-bezier(.35,0,.6,1) forwards; }
@keyframes rcx-pack-strain {
  0% { transform: perspective(1500px) scale(1) translateY(0); }
  62% { transform: perspective(1500px) scale(1.022, 0.995) translateY(-0.5%); }
  100% { transform: perspective(1500px) scale(1) translateY(0); }
}

/* the top does not slice off — it comes apart where the light cut it.
   The mask lives on a WRAPPER sized to the pack (the <img> inside is much
   bigger than the pack because it is cropped from a padded square, so a
   mask on the image itself never lined up and the top never left). */
.rcx-pack-topwrap {
  position: absolute;
  inset: 0;
  transform: translateZ(7px);
  pointer-events: none;
  /* a tight erase edge: the old band was ~108% of the element tall, which
     is why the top faded into mush instead of being taken away */
  /* A DITHERED ramp, not a clean one. A smooth gradient erases the top in a
     single soft line, which reads as a wipe; alternating partial stops break
     that line into grain so the material comes apart as it goes. */
  -webkit-mask-image: linear-gradient(to top,
    transparent 0%, transparent 2.4%,
    rgba(0, 0, 0, 0.10) 3.4%, transparent 4.4%,
    rgba(0, 0, 0, 0.24) 5.4%, transparent 6.4%,
    rgba(0, 0, 0, 0.40) 7.4%, rgba(0, 0, 0, 0.12) 8.4%,
    rgba(0, 0, 0, 0.58) 9.6%, rgba(0, 0, 0, 0.30) 10.6%,
    rgba(0, 0, 0, 0.74) 12.2%, rgba(0, 0, 0, 0.48) 13.2%,
    rgba(0, 0, 0, 0.90) 15.4%, rgba(0, 0, 0, 0.68) 16.4%,
    #000 19.5%, #000 100%);
  mask-image: linear-gradient(to top,
    transparent 0%, transparent 2.4%,
    rgba(0, 0, 0, 0.10) 3.4%, transparent 4.4%,
    rgba(0, 0, 0, 0.24) 5.4%, transparent 6.4%,
    rgba(0, 0, 0, 0.40) 7.4%, rgba(0, 0, 0, 0.12) 8.4%,
    rgba(0, 0, 0, 0.58) 9.6%, rgba(0, 0, 0, 0.30) 10.6%,
    rgba(0, 0, 0, 0.74) 12.2%, rgba(0, 0, 0, 0.48) 13.2%,
    rgba(0, 0, 0, 0.90) 15.4%, rgba(0, 0, 0, 0.68) 16.4%,
    #000 19.5%, #000 100%);
  -webkit-mask-size: 100% 300%;
  mask-size: 100% 300%;
  -webkit-mask-position: 0% 0%;
  mask-position: 0% 0%;
}
.rcx-pack.is-torn .rcx-pack-topwrap {
  animation: rcx-top-dissolve 820ms cubic-bezier(.36, 0, .62, .58) forwards;
}
@keyframes rcx-top-dissolve {
  /* it is CUT, so it separates first — a hair of lift and tilt reads as a
     severed piece; only then does it burn off upward along the mask.
     No filter here: animating brightness() on a masked, translateZ'd layer
     is what Android Chrome flashes. The desktop-only block below adds the
     burn back where it renders cleanly. */
  0% { -webkit-mask-position: 0% 0%; mask-position: 0% 0%;
       transform: translateZ(7px) translate3d(0, 0, 0) rotate(0deg); opacity: 1; }
  18% { transform: translateZ(7px) translate3d(0, -1.6%, 0) rotate(-0.7deg); opacity: 1; }
  70% { opacity: 1; }
  100% { -webkit-mask-position: 0% 100%; mask-position: 0% 100%;
         transform: translateZ(7px) translate3d(0, -11%, 0) rotate(-2.4deg); opacity: 0; }
}
@media (hover: hover) and (pointer: fine) {
  @keyframes rcx-top-dissolve {
    0% { -webkit-mask-position: 0% 0%; mask-position: 0% 0%;
         transform: translateZ(7px) translate3d(0, 0, 0) rotate(0deg);
         filter: brightness(1); opacity: 1; }
    18% { transform: translateZ(7px) translate3d(0, -1.6%, 0) rotate(-0.7deg);
          filter: brightness(1.35); opacity: 1; }
    70% { filter: brightness(1.95) saturate(1.15); opacity: 1; }
    100% { -webkit-mask-position: 0% 100%; mask-position: 0% 100%;
           transform: translateZ(7px) translate3d(0, -11%, 0) rotate(-2.4deg);
           filter: brightness(2.5) saturate(1.2); opacity: 0; }
  }
}

/* the wrapper gives itself up: sinks a breath and fades as the glitter
   carries it away (the motes are drawn on canvas at its position) */
.rcx-pack.is-spent {
  animation: none;
  transform: perspective(1500px) translateY(3%) scale(0.985);
  opacity: 0;
  /* visibility, not just opacity — see the note on the children below. It is
     held for the length of the fade and then snaps the element out of the
     render tree entirely, so nothing can paint it afterwards. */
  visibility: hidden;
  transition:
    transform 900ms cubic-bezier(.4, 0, .6, 1),
    opacity 820ms ease 60ms,
    visibility 0s linear 900ms;
}
@media (hover: hover) and (pointer: fine) {
  .rcx-pack.is-spent { transition: transform 900ms cubic-bezier(.4, 0, .6, 1), opacity 820ms ease 60ms, filter 820ms ease, visibility 0s linear 900ms; filter: brightness(1.25); }
}
/* The pack is a preserve-3d container and every layer inside it sits on its
   own translateZ plane. An ancestor's opacity is NOT reliable over children in
   a 3D rendering context — several engines rasterise those planes directly and
   the pack keeps painting at full strength while its parent reports opacity 0.
   That is the torn pack that kept reappearing behind the cards and sitting
   there after the run. Each layer now fades on its own account. */
.rcx-pack.is-spent .rcx-pack-body,
.rcx-pack.is-spent .rcx-pack-depth,
.rcx-pack.is-spent .rcx-pack-topwrap,
.rcx-pack.is-spent img {
  opacity: 0;
  transition: opacity 820ms ease 60ms;
}

/* ------------------------------- cards --------------------------------- */
/* face-up for life — no backs, no flips */
.rcx-deck { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
.rcx-deck.is-front { z-index: 5; }

.rcx-card {
  position: absolute;
  left: 50%;
  top: 50%;
  width: min(34vh, 68vw);
  aspect-ratio: 0.72;
  margin-left: calc(min(34vh, 68vw) / -2);
  margin-top: calc(min(34vh, 68vw) / 0.72 / -2);
  /* NOT promoted here. Ten cards leave the pack at once and then sit
     stacked, and a blanket will-change kept every one of them on its own
     compositor layer for the whole cutscene — measurably the largest single
     cost of the extraction. Promotion is granted per state below, only to
     the cards that are actually moving. */
  /* slot 1 sits on top of the stack; the grail (last slot) at the back */
  z-index: calc(60 - var(--slot, 0));
  opacity: 0;
  /* born deep inside the pack, hidden behind it */
  transform: translate(var(--mouth-dx, 0px), calc(var(--mouth-dy, 0px) + var(--pack-h, 300px) * 0.34)) scale(0.42);
}
.rcx-front {
  position: absolute;
  inset: 0;
  border-radius: 4.2% / 3%;
  overflow: hidden;
  isolation: isolate;
  background: #0e1b33;
  box-shadow: 0 18px 30px rgba(0, 0, 0, 0.55);
}
/* every layer inside the card carries the card's corners. Without this the
   reveal stack (lit copy, cover, sheet, beam) drew square and you could see
   its corners cut across the rounded card during the torch. */
.rcx-front > * { border-radius: inherit; }
.rcx-front img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* the states that actually move get the layer; the stack does not */
.rcx-card.is-out,
.rcx-card.is-deal,
.rcx-card.is-hero,
.rcx-card.is-toss-l,
.rcx-card.is-toss-r,
.rcx-card.is-drop,
.rcx-card.is-flick-l,
.rcx-card.is-flick-r,
.rcx-card.is-rest { will-change: transform, opacity; }

/* out of the mouth, up into the open */
.rcx-card.is-out {
  opacity: 1;
  transform: translate(calc(var(--mouth-dx, 0px) + var(--sx, 0px)), calc(var(--mouth-dy, 0px) - 22vh))
    rotate(var(--sr, 0deg)) scale(0.88);
  transition: transform 440ms var(--set), opacity 90ms ease;
}
/* and down onto the stack in front of the pack */
.rcx-card.is-stacked {
  opacity: 1;
  transform: translate(calc(var(--slot, 0) * 0.17rem), calc(var(--stack-dy, 8vh) + var(--slot, 0) * 0.1rem))
    rotate(calc(var(--slot, 0) * 0.3deg)) scale(0.82);
  transition: transform 520ms var(--set);
}

/* the deal — the art is already yours to read */
.rcx-card.is-deal {
  opacity: 1;
  transform: translate(0, -3vh) scale(1.1);
  transition: transform 440ms var(--set);
  z-index: 70;
}
/* A THROW, not a fade. It used to start dissolving at 380ms — still on
   screen — so it read as vanishing. Opacity now holds for the whole
   flight and only drops once the card is already past the edge, and the
   curve accelerates out of frame like something flicked from a hand.
   z-index sits above the incoming card so the exit reads first. */
.rcx-card.is-toss-l, .rcx-card.is-toss-r {
  /* NO opacity transition. Swapping is-deal -> is-toss dips through the
     base rule's opacity:0 for an instant; any transition (especially the
     delayed one that used to be here) latches onto that zero and the card
     flies the whole way invisible. Held at 1, it simply leaves frame. */
  opacity: 1;
  transition: transform 580ms cubic-bezier(.5, 0, .88, .42);
  z-index: 78;
}
/* same scale it was dealt at (1.1): shrinking on the way out read as the
   card receding rather than being thrown */
.rcx-card.is-toss-l { transform: translate(-112vw, -13vh) rotate(-62deg) scale(1.1); }
.rcx-card.is-toss-r { transform: translate(112vw, -13vh) rotate(62deg) scale(1.1); }

/* ==================== the denial: a missed EX+ ==========================
   The cold mirror of the ceremony. A card they pulled but never picked
   comes forward in full colour, the room stops celebrating, the art drains
   to ash, and the seal slams across it. Everything here is opacity or
   transform on one card at a time — the filters are static so they
   rasterise once instead of re-filtering the card every frame. */

/* the card's own width drives every size inside the seal, so the stamp
   scales with the card on any viewport instead of guessing at rem */
.rcx-card { --cw: min(34vh, 68vw); }

/* a miss is lifted a touch higher and larger than a common: this one
   mattered, and the extra size is the first hint that it did */
.rcx-card.is-miss.is-deal { transform: translate(0, -3.6vh) scale(1.16); }

/* the drained copy of the art, crossfaded over the live one */
.rcx-ash {
  z-index: 2;
  opacity: 0;
  filter: grayscale(1) brightness(0.62) contrast(1.02);
  transition: opacity 440ms ease;
}
.rcx-card.is-ashen .rcx-ash { opacity: 1; }

/* cold light replaces the pack's colour on the card */
.rcx-front > .rcx-chill {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  opacity: 0;
  background:
    linear-gradient(180deg, rgba(104, 124, 166, 0.22), rgba(7, 9, 18, 0.4)),
    radial-gradient(122% 88% at 50% 10%, rgba(158, 184, 226, 0.24), transparent 64%);
  transition: opacity 440ms ease;
}
.rcx-card.is-ashen .rcx-chill { opacity: 1; }

/* the seal itself */
.rcx-front > .rcx-seal {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 6;
  /* border-box or the padding pushes the rotated corners past the card's
     own edge, where overflow:hidden clips them off */
  box-sizing: border-box;
  width: 88%;
  display: grid;
  justify-items: center;
  gap: calc(var(--cw) * 0.032);
  padding: calc(var(--cw) * 0.052) calc(var(--cw) * 0.042) calc(var(--cw) * 0.056);
  border-radius: calc(var(--cw) * 0.022);
  border: 1px solid rgba(255, 206, 190, 0.46);
  background: linear-gradient(152deg, rgba(78, 11, 20, 0.95), rgba(22, 6, 11, 0.96) 54%, rgba(72, 12, 21, 0.95));
  box-shadow:
    0 calc(var(--cw) * 0.028) calc(var(--cw) * 0.075) rgba(0, 0, 0, 0.6),
    inset 0 0 0 1px rgba(255, 255, 255, 0.08);
  color: #fff;
  text-align: center;
  text-transform: uppercase;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, -50%) rotate(-14deg) scale(1.9);
}
/* it lands hard, overshoots once, and settles */
.rcx-card.is-stamped .rcx-seal { animation: rcx-seal-slam 540ms cubic-bezier(.2, .88, .2, 1) forwards; }
@keyframes rcx-seal-slam {
  0%   { opacity: 0; transform: translate(-50%, -50%) rotate(-14deg) scale(1.9); }
  52%  { opacity: 1; transform: translate(-50%, -50%) rotate(-5.6deg) scale(0.94); }
  76%  { opacity: 1; transform: translate(-50%, -50%) rotate(-7.8deg) scale(1.035); }
  100% { opacity: 1; transform: translate(-50%, -50%) rotate(-7deg) scale(1); }
}
/* the shock off the impact */
.rcx-seal::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  border: 1px solid rgba(255, 196, 176, 0.7);
  opacity: 0;
  pointer-events: none;
}
.rcx-card.is-stamped .rcx-seal::after { animation: rcx-seal-wave 640ms ease-out 70ms forwards; }
@keyframes rcx-seal-wave {
  0%   { opacity: 0.85; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.42); }
}

/* the tier headline — MISSED GRAIL!!! down to MISSED CHASE! */
.rcx-seal em {
  font-style: normal;
  font-weight: 900;
  font-size: calc(var(--cw) * 0.088);
  line-height: 1.02;
  letter-spacing: 0.01em;
  color: #ffd8c6;
  text-shadow: 0 calc(var(--cw) * 0.006) calc(var(--cw) * 0.03) rgba(0, 0, 0, 0.55);
}
/* a passed-over grail says so louder */
.rcx-seal.is-tier-sir em,
.rcx-seal.is-tier-gold em { font-size: calc(var(--cw) * 0.099); color: #ffe4b0; }

/* which card it was */
.rcx-seal > span {
  max-width: 100%;
  font-weight: 800;
  font-size: calc(var(--cw) * 0.052);
  line-height: 1.16;
  letter-spacing: 0.03em;
  color: rgba(255, 255, 255, 0.9);
  overflow-wrap: anywhere;
}

/* the two verdicts, unmissable and stacked */
.rcx-seal b {
  display: block;
  box-sizing: border-box;
  width: 100%;
  padding: calc(var(--cw) * 0.03) calc(var(--cw) * 0.02) calc(var(--cw) * 0.033);
  border-radius: calc(var(--cw) * 0.012);
  border: 1px solid rgba(255, 138, 118, 0.66);
  background: rgba(255, 78, 62, 0.2);
  color: #ffd0c4;
  font-size: calc(var(--cw) * 0.054);
  font-weight: 900;
  line-height: 1.2;
  letter-spacing: 0.1em;
}
.rcx-seal b span { display: block; }
/* the second line carries the consequence, so it reads brightest */
.rcx-seal b span + span { color: #fff; }

/* said once more in plain words, because this is the part people dispute */
.rcx-seal u {
  max-width: 97%;
  text-decoration: none;
  text-transform: none;
  font-size: calc(var(--cw) * 0.041);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: 0.005em;
  color: rgba(255, 255, 255, 0.64);
}

/* the card takes the hit. This rides .rcx-front, which carries no transform
   of its own — putting it on the card would fight the deal transform. */
.rcx-card.is-stamped .rcx-front { animation: rcx-miss-jolt 460ms cubic-bezier(.2, .8, .3, 1) both; }
@keyframes rcx-miss-jolt {
  0%   { transform: translate(0, 0) scale(1); }
  12%  { transform: translate(-1.5%, 1%) scale(1.015); }
  27%  { transform: translate(1.2%, -0.7%) scale(0.994); }
  44%  { transform: translate(-0.6%, 0.35%) scale(1.005); }
  100% { transform: translate(0, 0) scale(1); }
}

/* it is DROPPED, not flicked. Commons get thrown off the side; this one
   falls out of frame, and the different verb is what sells the loss. */
.rcx-card.is-drop {
  opacity: 0;
  transform: translate(0, 76vh) rotate(9deg) scale(0.96);
  transition: transform 740ms cubic-bezier(.5, .02, .86, .4), opacity 460ms ease 320ms;
  z-index: 76;
}

/* the room cools behind the card — a flat sheet, so this is a compositor
   crossfade rather than a full-viewport repaint */
.rcx-chillroom {
  position: absolute;
  inset: 0;
  z-index: 8;
  pointer-events: none;
  opacity: 0;
  background: rgba(36, 48, 76, 0.42);
  transition: opacity 440ms ease;
}
.rcx.is-miss-stage .rcx-chillroom { opacity: 1; }

/* ------------------- the grail film (party, exact) --------------------- */
.rcx-card.is-sealed .rcx-art { filter: brightness(4.8) saturate(0) contrast(0.08) blur(1.5px); }
.rcx-sheet {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background:
    linear-gradient(115deg,
      transparent 26%, rgba(139, 220, 255, 0.34) 42%,
      rgba(198, 151, 255, 0.3) 52%, rgba(255, 231, 170, 0.32) 62%, transparent 78%),
    linear-gradient(145deg, #fff 0 34%, #e3f8ff 58%, #f0e6ff 78%, #fff7dc 100%);
  background-size: 240% 100%, 100% 100%;
  box-shadow: inset 0 0 20px rgba(255, 255, 255, 0.94);
  opacity: 0;
}
.rcx-card.is-sealed .rcx-sheet {
  opacity: 1;
  animation: rcx-film-drift 3.4s linear infinite, rcx-film-breathe 0.5s ease-in-out infinite alternate;
}
@keyframes rcx-film-drift {
  from { background-position: 120% 0, 0 0; }
  to { background-position: -120% 0, 0 0; }
}
@keyframes rcx-film-breathe {
  from { filter: brightness(1.05) saturate(0.86); }
  to { filter: brightness(1.3) saturate(1.08); }
}
.rcx-card.is-sealed .rcx-front {
  box-shadow:
    0 0 0 2px rgba(255, 255, 255, 0.94),
    0 0 18px rgba(132, 218, 255, 0.72),
    0 0 34px rgba(194, 143, 255, 0.48);
  animation: rcx-seal-pulse 0.48s ease-in-out infinite alternate;
}
@keyframes rcx-seal-pulse {
  from { box-shadow: 0 0 0 2px rgba(255,255,255,.82), 0 0 13px rgba(132,218,255,.56), 0 0 25px rgba(194,143,255,.36); }
  to { box-shadow: 0 0 0 2px rgba(255,255,255,1), 0 0 24px rgba(132,218,255,.86), 0 0 42px rgba(194,143,255,.56); }
}

/* ------------------------- the hit ceremony ---------------------------- */
.rcx-card.is-hero {
  opacity: 1;
  transform: translate(0, -4vh) scale(1.24);
  transition: transform 720ms var(--set);
  z-index: 80;
}
.rcx.is-hit-stage .rcx-card.is-stacked { opacity: 0.07; transition: opacity 520ms ease; }

.rcx-card.is-heartbeat .rcx-sheet {
  animation: rcx-film-drift 3.4s linear infinite, rcx-heartbeat 1300ms cubic-bezier(.45, 0, .55, 1) infinite;
}
@keyframes rcx-heartbeat {
  0%, 100% { filter: brightness(0.8); }
  9% { filter: brightness(1.45); }
  18% { filter: brightness(0.9); }
  30% { filter: brightness(1.7); }
  44% { filter: brightness(0.8); }
}

.rcx-card.is-charge .rcx-sheet {
  animation: rcx-film-drift 0.9s linear infinite, rcx-charge-burn 900ms ease-in forwards;
}
@keyframes rcx-charge-burn {
  0% { filter: brightness(1.05); }
  100% { filter: brightness(1.62) saturate(1.1); }
}
.rcx-card.is-charge .rcx-front { animation-duration: 0.2s; }

/* the landing has WEIGHT: the face takes a short punch as the flare
   fires, so the flash is felt as an impact rather than seen as a light */
.rcx-card.is-flash .rcx-front {
  animation: rcx-flare 360ms cubic-bezier(.1, .8, .2, 1) forwards,
             rcx-punch 400ms cubic-bezier(.16, .9, .3, 1) forwards;
}
@keyframes rcx-punch {
  0% { transform: scale(1.04); }
  100% { transform: scale(1); }
}
@keyframes rcx-flare {
  0% {
    box-shadow:
      0 0 8px 5px rgba(255, 255, 255, 1),
      0 0 120px 48px rgba(255, 255, 255, 1),
      0 0 260px 100px rgba(163, 222, 255, 0.9),
      0 18px 30px rgba(0, 0, 0, 0.55);
  }
  100% {
    box-shadow:
      0 0 0 0 rgba(255, 255, 255, 0),
      0 0 0 0 rgba(255, 255, 255, 0),
      0 0 0 0 rgba(163, 222, 255, 0),
      0 18px 42px rgba(0, 0, 0, 0.8);
  }
}

.rcx-cover {
  position: absolute;
  inset: 0;
  z-index: 3;
  background: #030308;
  opacity: 0;
  transition: opacity 220ms cubic-bezier(.1, .75, .25, 1);
  pointer-events: none;
}
.rcx-card.is-crash .rcx-cover { opacity: 1; }
.rcx-card.is-crash .rcx-front {
  animation: none;
  box-shadow: 0 18px 42px rgba(0, 0, 0, 0.85);
  transition: box-shadow 220ms cubic-bezier(.1, .75, .25, 1);
}

.rcx-lit {
  z-index: 4;
  opacity: 0;
  filter:
    brightness(1.16) saturate(1.1)
    drop-shadow(0 0 11px rgba(255, 239, 193, 0.82))
    drop-shadow(0 0 26px rgba(117, 205, 255, 0.48));
}
.rcx-card.is-sweep .rcx-lit, .rcx-card.is-flood .rcx-lit { opacity: 1; }
.rcx-card.is-sweep .rcx-lit {
  -webkit-mask-image: linear-gradient(to right,
    #000 0%, #000 calc(var(--rx, -30%) - 26%),
    rgba(0, 0, 0, 0.96) calc(var(--rx, -30%) - 12%),
    rgba(0, 0, 0, 0.6) calc(var(--rx, -30%) + 6%),
    transparent calc(var(--rx, -30%) + 28%), transparent 100%);
  mask-image: linear-gradient(to right,
    #000 0%, #000 calc(var(--rx, -30%) - 26%),
    rgba(0, 0, 0, 0.96) calc(var(--rx, -30%) - 12%),
    rgba(0, 0, 0, 0.6) calc(var(--rx, -30%) + 6%),
    transparent calc(var(--rx, -30%) + 28%), transparent 100%);
}
.rcx-beam {
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  background: radial-gradient(
    ellipse 25% 72% at var(--rx, -30%) 50%,
    rgba(255, 252, 232, 0.78) 0%,
    rgba(218, 240, 255, 0.38) 28%,
    rgba(153, 210, 255, 0.14) 52%,
    transparent 76%);
  mix-blend-mode: screen;
  filter: blur(7px);
  opacity: 0;
}
.rcx-card.is-sweep .rcx-beam { opacity: 0.9; }

.rcx-card.is-flood .rcx-lit { animation: rcx-flood 1200ms ease forwards; }
@keyframes rcx-flood {
  0% {
    filter:
      brightness(1.16) saturate(1.1)
      drop-shadow(0 0 11px rgba(255, 239, 193, 0.82))
      drop-shadow(0 0 26px rgba(117, 205, 255, 0.48));
  }
  100% { filter: brightness(1) saturate(1); }
}
.rcx-card.is-flood .rcx-cover, .rcx-card.is-flood .rcx-sheet {
  opacity: 0; animation: none; transition: opacity 420ms ease;
}
.rcx-card.is-flood .rcx-art { filter: none; transition: filter 420ms ease; }
.rcx-card.is-flood .rcx-front {
  animation: none;
  box-shadow:
    0 0 22px rgba(255, 255, 255, 0.26),
    0 0 70px rgba(120, 150, 255, 0.4),
    0 24px 40px rgba(0, 0, 0, 0.6);
  transition: box-shadow 900ms ease;
}

/* the plate reads over ANY background: it is its own panel of dark glass,
   not text floating on whatever happens to be behind it */
.rcx-plate {
  position: absolute;
  left: 50%;
  top: calc(50% + min(34vh, 68vw) / 0.72 / 2 - 1vh);
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  /* a reserved width: the tag appearing (and the counter growing from
     $0.00 to five figures) used to change the panel's width, and a
     centre-anchored panel re-centres on every width change — that was the
     little sideways shove as the plate settled */
  min-width: min(23rem, 80vw);
  padding: 0.72rem 1.35rem 0.8rem;
  border-radius: 16px;
  border: 1px solid rgba(158, 212, 255, 0.16);
  background: linear-gradient(180deg, rgba(12, 16, 30, 0.82), rgba(6, 8, 16, 0.9));
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.06);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 520ms ease, transform 520ms var(--set);
  z-index: 90;
}
.rcx-plate.is-live { opacity: 1; transform: translateX(-50%) translateY(-0.4rem); }
.rcx-plate b {
  color: #f4f7fc;
  font-size: clamp(1rem, 2.3vh, 1.35rem);
  font-weight: 800;
  letter-spacing: 0.04em;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.7);
}
.rcx-plate i {
  display: block; width: 0; height: 1px;
  background: linear-gradient(90deg, transparent, #e9cd97, transparent);
  transition: width 700ms cubic-bezier(.2, .8, .2, 1) 150ms;
}
.rcx-plate.is-live i { width: min(38vw, 240px); }
.rcx-price {
  position: relative;
  color: #e9cd97; font-style: normal;
  font-size: clamp(1.05rem, 2.4vh, 1.45rem); font-weight: 800;
  font-variant-numeric: tabular-nums; letter-spacing: 0.06em;
  transition: text-shadow 400ms ease, transform 400ms cubic-bezier(.2,.9,.3,1);
}
/* while it counts, a light rides the digits; when it lands, it sets */
.rcx-price.is-counting {
  color: #fff8e6;
  text-shadow: 0 0 12px rgba(255, 236, 178, 0.7), 0 1px 2px rgba(0, 0, 0, 0.8);
}
.rcx-price.is-landed {
  transform: scale(1.06);
  text-shadow: 0 0 9px rgba(233, 205, 151, 0.4), 0 1px 2px rgba(0, 0, 0, 0.8);
}

/* the price is theirs to uncover: the button occupies the slot, then hands
   it to the number — one element becoming the other, not a swap */
/* both grades sit on one line, the graded value quieter than the raw */
.rcx-prices {
  grid-area: 1 / 1;
  display: flex;
  /* stacked, not side by side: two prices on one line squeezed both and made
     the PSA figure read as an afterthought hung off the raw one */
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.16rem;
  white-space: nowrap;
}
.rcx-price-psa {
  font-size: 0.82em;
  color: #9ed4ff;
  opacity: 0.92;
}
.rcx-price-psa.is-landed { transform: none; }

.rcx-pricebox {
  position: relative;
  display: grid;
  place-items: center;
  min-height: 2.1rem;
  min-width: 11rem;          /* the counter cannot resize the panel */
}
.rcx-reveal {
  pointer-events: auto;
  cursor: pointer;
  grid-area: 1 / 1;
  padding: 0.42rem 1.05rem;
  border-radius: 999px;
  border: 1px solid rgba(233, 205, 151, 0.55);
  background: linear-gradient(180deg, rgba(233, 205, 151, 0.16), rgba(233, 205, 151, 0.06));
  color: #f0dcb4;
  font: inherit;
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: transform 420ms var(--set), opacity 300ms ease,
              border-color 300ms ease, box-shadow 300ms ease, filter 300ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .rcx-reveal:hover {
    border-color: rgba(233, 205, 151, 0.95);
    box-shadow: 0 0 22px rgba(233, 205, 151, 0.3);
    filter: brightness(1.1);
  }
}
.rcx-reveal:focus-visible { outline: 2px solid #e9cd97; outline-offset: 4px; }
/* the transform: the shell blows out and the value steps through it */
.rcx-pricebox.is-revealed .rcx-reveal {
  opacity: 0;
  transform: scale(1.45);
  border-color: rgba(233, 205, 151, 0);
  box-shadow: 0 0 46px rgba(233, 205, 151, 0.55);
  pointer-events: none;
}
.rcx-price {
  opacity: 0;
  transform: scale(0.7);
  transition: opacity 360ms ease 120ms, transform 520ms var(--set) 120ms, text-shadow 400ms ease;
}
.rcx-pricebox.is-revealed .rcx-price { opacity: 1; transform: scale(1); }
.rcx-pricebox { min-width: 15rem; }

.rcx-tag {
  margin-top: 2px;
  padding: 0.24rem 0.66rem;
  border-radius: 999px;
  font-size: 0.66rem; font-weight: 800;
  letter-spacing: 0.16em; text-transform: uppercase;
  opacity: 0; transform: translateY(4px);
  transition: opacity 420ms ease, transform 420ms cubic-bezier(.2,.9,.3,1);
}
.rcx-tag.is-live { opacity: 1; transform: translateY(0); }
.rcx-tag.is-vault { color: #061a10; background: linear-gradient(180deg, #86f3bd, #3fd291); box-shadow: 0 0 18px rgba(74, 211, 155, 0.45); }
.rcx-tag.is-linked { pointer-events: auto; cursor: pointer; transition: transform 300ms var(--set), box-shadow 300ms ease, filter 300ms ease; }
.rcx-tag.is-linked::after { content: " \203A"; font-weight: 900; }
@media (hover: hover) and (pointer: fine) {
  .rcx-tag.is-linked:hover { transform: translateY(-1px) scale(1.04); box-shadow: 0 0 26px rgba(74, 211, 155, 0.7); filter: brightness(1.06); }
}
.rcx-tag.is-linked:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
.rcx-tag.is-sim { color: #cdd7ea; background: rgba(255,255,255,0.09); border: 1px solid rgba(158, 212, 255, 0.28); }
.rcx-tag.is-nope { color: #ffb3a6; background: rgba(255, 90, 70, 0.12); border: 1px solid rgba(255, 126, 104, 0.45); }

/* ==================== the keep rail =====================================
   Cards kept along the way used to hang at -32vh over the middle of the
   room, on top of the pack, the next card and the page's own chrome. They
   now stow into a low strip under the dais: small, quiet, scrollable, and
   out of the way of everything the animation is doing. */
/* One row, two independent tenants. Bulk lands on every pack; kept cards do
   not, so they show and hide on their own terms — a full-width rail with
   nothing in it but a bulk count was the wrong shape for pack one of a run. */
.rcx-tray {
  position: absolute;
  left: 50%;
  bottom: 2.2vh;
  z-index: 92;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  max-width: 94vw;
  transform: translateX(-50%);
  pointer-events: none;
}
.rcx-tray > * {
  border-radius: 12px;
  border: 1px solid rgba(158, 212, 255, 0.14);
  background: linear-gradient(180deg, rgba(20, 26, 46, 0.78) 0%, rgba(10, 14, 27, 0.86) 100%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 14px 34px rgba(3, 8, 20, 0.5);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  opacity: 0;
  transform: translateY(1.2rem);
  transition: opacity 420ms ease, transform 480ms var(--set);
}
.rcx-tray > .is-live { opacity: 1; transform: translateY(0); pointer-events: auto; }
/* an empty rail takes no room at all */
.rcx-keeps:not(.is-live) { display: none; }

.rcx-keeps {
  display: grid;
  grid-template-columns: auto minmax(0, auto) auto;
  align-items: center;
  gap: 0.4rem;
  /* hugs its tiles rather than reserving a wide empty panel for one card */
  width: auto;
  max-width: min(38rem, 72vw);
  padding: 0.4rem 0.45rem;
}

.rcx-keeps-rail {
  display: flex;
  gap: 0.4rem;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-behavior: smooth;
  scrollbar-width: none;
  -ms-overflow-style: none;
  /* the drag handler owns horizontal panning; let the browser keep vertical */
  touch-action: pan-y;
  cursor: grab;
}
.rcx-keeps-rail::-webkit-scrollbar { display: none; }
.rcx-keeps.is-dragging .rcx-keeps-rail { cursor: grabbing; scroll-behavior: auto; }
/* There is nothing on a phone to say the strip scrolls — no arrows, no bar.
   The edge it can still travel toward fades out, so a half-shown tile reads
   as "there is more this way" instead of as a clipped one. */
.rcx-keeps.can-next .rcx-keeps-rail {
  -webkit-mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 2.1rem), transparent 100%);
  mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 2.1rem), transparent 100%);
}
.rcx-keeps.can-prev.can-next .rcx-keeps-rail {
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 1.6rem, #000 calc(100% - 2.1rem), transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, #000 1.6rem, #000 calc(100% - 2.1rem), transparent 100%);
}
.rcx-keeps.can-prev:not(.can-next) .rcx-keeps-rail {
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 1.6rem, #000 100%);
  mask-image: linear-gradient(to right, transparent 0, #000 1.6rem, #000 100%);
}

.rcx-keep-tile {
  flex: 0 0 auto;
  width: 3.1rem;
  aspect-ratio: 0.72;
  border-radius: 4px;
  overflow: hidden;
  background: #0e1b33;
  box-shadow: 0 0 0 1px rgba(233, 205, 151, 0.55), 0 6px 14px rgba(0, 0, 0, 0.5);
  transition: opacity 260ms ease, transform 260ms var(--set);
}
.rcx-keep-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* it is invisible until the flying card is nearly on top of it, then it
   takes over and settles with a small bounce */
.rcx-keep-tile.is-arriving { opacity: 0; transform: scale(0.72); }
/* a thud, not a pop: it takes the hit, squashes, then rebounds level */
.rcx-keep-tile.is-settled { animation: rcx-tile-settle 520ms cubic-bezier(.18, .9, .24, 1); }
/* the rail scrolls itself as tiles land; on a phone that smooth-scroll ran
   against the card's own transform and both stuttered */
@media (max-width: 720px) {
  .rcx-keeps-rail { scroll-behavior: auto; }
}
@keyframes rcx-tile-settle {
  0%   { transform: translateY(-5px) scale(1.2, 0.86);
         box-shadow: 0 0 0 1px rgba(233, 205, 151, 1), 0 0 22px rgba(233, 205, 151, 0.6); }
  38%  { transform: translateY(1px) scale(0.94, 1.08); }
  68%  { transform: translateY(0) scale(1.03, 0.98); }
  100% { transform: translateY(0) scale(1); }
}

/* arrows, for a mouse with no sideways wheel. They only appear when there
   is somewhere to go. */
.rcx-keeps-nav {
  width: 1.7rem;
  height: 1.7rem;
  display: grid;
  place-items: center;
  border-radius: 999px;
  border: 1px solid rgba(158, 212, 255, 0.2);
  background: rgba(10, 14, 26, 0.72);
  color: #cfe0ff;
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity 220ms ease, border-color 220ms ease;
}
.rcx-keeps.can-prev .rcx-keeps-nav.is-prev,
.rcx-keeps.can-next .rcx-keeps-nav.is-next { opacity: 1; pointer-events: auto; }
.rcx-keeps-nav:hover { border-color: rgba(233, 205, 151, 0.55); }
.rcx-keeps-nav:focus-visible { outline: 2px solid #e9cd97; outline-offset: 2px; }

/* the bulk tally is its own small chip, present from the first pack */
.rcx-bulk {
  display: flex;
  align-items: baseline;
  gap: 0.34rem;
  padding: 0.5rem 0.75rem;
  white-space: nowrap;
}
.rcx-bulk span {
  color: #8fa0bd;
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.rcx-bulk strong {
  color: var(--vb-gold, #e9cd97);
  font-size: 0.92rem;
  font-weight: 900;
  font-variant-numeric: tabular-nums;
  display: inline-block;
}
.rcx-bulk strong.is-bumped { animation: rcx-bulk-bump 460ms cubic-bezier(.2, .9, .25, 1); }
@keyframes rcx-bulk-bump {
  0%   { transform: translateY(0) scale(1); }
  32%  { transform: translateY(-3px) scale(1.22); }
  100% { transform: translateY(0) scale(1); }
}

/* Holo Points: the same chip surface as the tray, at the top of the stage
   between the run HUD (left) and the exit (right), so it never sits on the
   staged-card rail. The line of text hangs directly under it. */
.rcx-points {
  position: absolute;
  top: 3vh;
  left: 50%;
  z-index: 92;
  display: flex;
  align-items: baseline;
  gap: 0.3rem;
  padding: 0.5rem 0.75rem;
  white-space: nowrap;
  border-radius: 12px;
  border: 1px solid rgba(158, 212, 255, 0.14);
  background: linear-gradient(180deg, rgba(20, 26, 46, 0.78) 0%, rgba(10, 14, 27, 0.86) 100%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 14px 34px rgba(3, 8, 20, 0.5);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  opacity: 0;
  transform: translate(-50%, -1rem);
  pointer-events: none;
  transition: opacity 420ms ease, transform 480ms var(--set);
}
.rcx-points.is-live { opacity: 1; transform: translate(-50%, 0); }
.rcx.is-resting-final .rcx-points { opacity: 0; transition: opacity 500ms ease; }
.rcx-points i {
  font-style: normal;
  color: #c4b5fd;
  font-size: 0.72rem;
  filter: drop-shadow(0 0 4px rgba(196, 181, 253, 0.7));
}
.rcx-points strong {
  color: #dccbff;
  font-size: 0.92rem;
  font-weight: 900;
  font-variant-numeric: tabular-nums;
  display: inline-block;
}
.rcx-points.is-bumped strong { animation: rcx-bulk-bump 460ms cubic-bezier(.2, .9, .25, 1); }
.rcx-points.is-bumped i { animation: rcx-points-flare 700ms ease-out; }
@keyframes rcx-points-flare {
  0%   { transform: scale(1); filter: drop-shadow(0 0 4px rgba(196, 181, 253, 0.7)); }
  30%  { transform: scale(1.6) rotate(18deg); filter: drop-shadow(0 0 12px rgba(255, 255, 255, 1)); }
  100% { transform: scale(1); filter: drop-shadow(0 0 4px rgba(196, 181, 253, 0.7)); }
}
/* the gain rises out of the number and fades */
.rcx-points em {
  position: absolute;
  right: 0.6rem;
  top: -0.2rem;
  font-style: normal;
  font-size: 0.8rem;
  font-weight: 900;
  color: #f0abfc;
  text-shadow: 0 0 10px rgba(240, 171, 252, 0.9);
  opacity: 0;
  pointer-events: none;
}
.rcx-points em.is-pop { animation: rcx-points-pop 1100ms cubic-bezier(.2, .7, .3, 1) forwards; }
@keyframes rcx-points-pop {
  0%   { opacity: 0; transform: translateY(6px) scale(0.8); }
  18%  { opacity: 1; transform: translateY(-4px) scale(1.1); }
  70%  { opacity: 1; transform: translateY(-22px) scale(1); }
  100% { opacity: 0; transform: translateY(-34px) scale(0.96); }
}
/* unlocked: the chip turns gold and stays there */
.rcx-points.is-unlock { border-color: rgba(233, 205, 151, 0.7); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08), 0 0 26px rgba(233, 205, 151, 0.35), 0 14px 34px rgba(3, 8, 20, 0.5); }
.rcx-points.is-unlock i, .rcx-points.is-unlock strong { color: #fff3d6; text-shadow: 0 0 12px rgba(233, 205, 151, 0.9); }

/* the line under the chip — iridescent text that drops in, ticks, and goes */
.rcx-points-line {
  position: absolute;
  left: 50%;
  top: calc(3vh + 3rem);
  z-index: 93;
  max-width: 94vw;
  transform: translate(-50%, -0.8rem);
  opacity: 0;
  pointer-events: none;
  white-space: nowrap;
  font-size: 0.78rem;
  font-weight: 900;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  text-align: center;
  background: linear-gradient(100deg, #3ee6ff, #7c5cff 30%, #ff5ce1 55%, #ffd35c 78%, #3ee6ff);
  background-size: 300% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.95)) drop-shadow(0 0 10px rgba(124, 92, 255, 0.35));
  transition: opacity 360ms ease, transform 520ms var(--set);
}
.rcx-points-line.is-live { opacity: 1; transform: translate(-50%, 0); }
.rcx-points-line.is-live span { display: inline-block; animation: rcx-points-line-in 620ms cubic-bezier(.2, .9, .25, 1); }
@keyframes rcx-points-line-in {
  0%   { letter-spacing: 0.5em; opacity: 0; }
  100% { letter-spacing: inherit; opacity: 1; }
}
.rcx-points-line.is-unlock {
  font-size: 0.92rem;
  /* background-image, not the shorthand: `background:` resets background-clip
     to border-box and the line paints as a solid gold bar instead of letters */
  background-image: linear-gradient(100deg, #fff6dc, #ffd35c 45%, #fff3d6 70%, #e9cd97);
  background-size: 300% 100%;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.95)) drop-shadow(0 0 16px rgba(233, 205, 151, 0.7));
}
@media (prefers-reduced-motion: no-preference) {
  .rcx-points-line { animation: rcx-points-holo 7s linear infinite; }
  @keyframes rcx-points-holo { to { background-position: 300% 50%; } }
}
.rcx.is-resting-final .rcx-points-line { opacity: 0; transition: opacity 500ms ease; }

@media (max-width: 720px) {
  /* the tray takes the width of the screen and the rail takes what the bulk
     chip does not — two tiles in a 62vw box was most of the problem */
  .rcx-tray {
    bottom: 1.6vh;
    gap: 0.35rem;
    width: 94vw;
    justify-content: stretch;
  }
  .rcx-keeps {
    flex: 1 1 auto;
    width: auto;
    max-width: none;
    min-width: 0;
    gap: 0.3rem;
    padding: 0.34rem 0.4rem;
    grid-template-columns: minmax(0, 1fr);
  }
  .rcx-bulk { flex: 0 0 auto; padding: 0.42rem 0.6rem; }
  .rcx-points { top: 2.4vh; padding: 0.42rem 0.6rem; }
  .rcx-points-line { font-size: 0.62rem; letter-spacing: 0.12em; top: calc(2.4vh + 2.7rem); }
  .rcx-points-line.is-unlock { font-size: 0.74rem; }
  .rcx-keep-tile { width: 2.35rem; }
  /* a thumb drags; the arrows are dead weight on a phone */
  .rcx-keeps-nav { display: none; }
}

/* the rest: the card settles back onto the stage, the room hands itself
   back to the page, and it waits there until the next rip dismisses it */
.rcx-card.is-rest {
  opacity: 1;
  z-index: 80;
  transition: transform 880ms var(--set);
}
.rcx-card.is-rest .rcx-front {
  box-shadow:
    0 0 0 1px rgba(233, 205, 151, 0.5),
    0 0 30px rgba(120, 150, 255, 0.3),
    0 20px 36px rgba(0, 0, 0, 0.6);
  transition: box-shadow 900ms ease;
}
.rcx.is-resting { cursor: default; }
.rcx.is-resting .rcx-veil { opacity: 0; transition: opacity 900ms ease; }
/* the page is live again underneath — only the card and its plate remain.
   It also drops out of the takeover z-layer: at 14000 the resting hit sat
   over the page's own dialogs (Sell bulk confirm is 12000, the rest are
   10020+), so those opened behind it and could not be read. */
/* inside .page-shell now (see rip_cinema.js): above the stage's tallest layer
   (62), below the nav (70) so its menu still opens, below every dialog */
/* The layer is pinned to a viewport-sized box at the scroll offset it rested
   at, but the card inside it is placed against the stage — which can sit far
   outside that box when the page was scrolled during the rip. overflow:hidden
   then clipped the card away entirely: it went to the right place and
   vanished. So the layer stops clipping vertically. It keeps clipping
   sideways: the spent pack art is wider than a phone screen, and left
   unclipped it widened the page and let it pan off to the right. `clip`
   (not hidden) so this never becomes a scroll container of its own. */
.rcx.is-resting-final { pointer-events: none; z-index: 65; overflow-x: clip; overflow-y: visible; }
.rcx.is-resting-final .rcx-pack { display: none; }
/* the card itself takes clicks back — everything else stays click-through */
.rcx-card.is-rest.is-linked { pointer-events: auto; cursor: pointer; }
.rcx-card.is-rest.is-linked .rcx-front { transition: box-shadow 320ms ease, transform 320ms var(--set); }
@media (hover: hover) and (pointer: fine) {
  .rcx-card.is-rest.is-linked:hover .rcx-front {
    transform: translateY(-4px);
    box-shadow:
      0 0 0 1px rgba(233, 205, 151, 0.9),
      0 0 42px rgba(233, 205, 151, 0.4),
      0 26px 44px rgba(0, 0, 0, 0.66);
  }
}
.rcx-card.is-rest.is-linked:focus-visible { outline: 2px solid #e9cd97; outline-offset: 6px; }

/* (the "view in vault" hint is gone — the Vaulted tag is the door) */
.rcx.is-resting-final .rcx-cv { opacity: 0; transition: opacity 900ms ease; }
/* the run label and the card counter are progress readouts: once the run is
   over and the layer has settled onto the page, they have nothing left to
   report and they sit right where the site's own nav is */
.rcx.is-resting-final .rcx-run { opacity: 0; transition: opacity 500ms ease; }
/* the keep rail goes with them: the page's own hit list takes over once the
   cinema hands control back, so leaving a second copy on screen is clutter */
.rcx.is-resting-final .rcx-tray > * {
  opacity: 0;
  pointer-events: none;
  transition: opacity 500ms ease;
}

.rcx-plate.is-rest,
.rcx-plate.is-rest.is-live {
  transform: translateX(-50%) translate(var(--pdx, 0px), var(--pdy, 0px));
  transition: transform 880ms var(--set), opacity 500ms ease;
}

/* every card leaves the same way: flicked clean off the screen */
.rcx-card.is-flick-l, .rcx-card.is-flick-r {
  opacity: 1 !important;
  transition: transform 620ms cubic-bezier(.5, 0, .88, .42);
  z-index: 90;
}
/* --fs carries the card's live scale so it leaves at the size it was */
.rcx-card.is-flick-l { transform: translate(-112vw, -13vh) rotate(-58deg) scale(var(--fs, 1)) !important; }
.rcx-card.is-flick-r { transform: translate(112vw, -13vh) rotate(58deg) scale(var(--fs, 1)) !important; }

/* in a simulation the way-out controls own the top-right corner, so the
   counter drops beneath them rather than sitting behind Switch to Live */

.rcx-fin {
  position: absolute;
  left: 50%; top: 55%;
  z-index: 10;
  transform: translate(-50%, 1rem);
  text-align: center;
  color: #9fb0c8;
  font-size: 0.9rem; letter-spacing: 0.14em; text-transform: uppercase;
  opacity: 0;
  transition: opacity 620ms ease, transform 620ms cubic-bezier(.2, .8, .2, 1);
}
.rcx-fin.is-live { opacity: 1; transform: translate(-50%, 0); }
.rcx-fin strong { display: block; color: #e8eef7; font-size: 1.05rem; margin-bottom: 0.4rem; letter-spacing: 0.2em; }

/* the phone sits the whole show lower: the deal and the hero both lifted
   the card off centre, which on a tall screen stacked with the higher
   pack anchor and pushed everything toward the top */
@media (max-width: 720px) {
  .rcx-card.is-deal { transform: translate(0, 1.5vh) scale(1.06); }
  .rcx-card.is-hero { transform: translate(0, 0.5vh) scale(1.16); }
  .rcx-card.is-keep { transform: translate(calc(var(--keep-x, 0) * 1px), -28vh) scale(0.46); }
}

@media (prefers-reduced-motion: reduce) {
  .rcx-card.is-flash .rcx-front { animation: rcx-flare 360ms cubic-bezier(.1,.8,.2,1) forwards; }
  .rcx-pack.is-landed, .rcx-pack.is-charging,
  .rcx-card.is-sealed .rcx-sheet, .rcx-card.is-sealed .rcx-front,
  .rcx-card.is-heartbeat .rcx-sheet { animation: none; }
  .rcx-beam { opacity: 0 !important; }
  /* the seal still has to READ — it just arrives instead of slamming */
  .rcx-card.is-stamped .rcx-front { animation: none; }
  .rcx-card.is-stamped .rcx-seal::after { animation: none; }
  .rcx-card.is-stamped .rcx-seal {
    animation: none;
    opacity: 1;
    transform: translate(-50%, -50%) rotate(-7deg) scale(1);
  }
}

/* ---- a run: which pack we are on, and the moment the target is secured -- */
.rcx-run {
  position: absolute;
  top: 3.4vh;
  left: 4vw;
  z-index: 10;
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  color: #8fa0bd;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  opacity: 0;
  transition: opacity 420ms ease;
}
.rcx-run.is-live { opacity: 1; }
.rcx-run strong { color: #e8eef7; font-size: 1.15rem; }
.rcx-run strong::before { content: "#"; opacity: 0.5; margin-right: 1px; }

/* the guarantee landing: it is the one piece of text in the whole sequence
   that reports rather than decorates, so it gets the gold treatment */
.rcx-secured {
  position: absolute;
  left: 50%;
  top: 22%;
  z-index: 92;
  transform: translate(-50%, 0.6rem);
  padding: 0.6rem 1.1rem;
  /* a pill is only right while it is one line; wrapped, it needs a box */
  border-radius: 14px;
  border: 1px solid rgba(233, 205, 151, 0.55);
  background: rgba(8, 10, 20, 0.82);
  color: #f0dcb4;
  font-size: clamp(0.7rem, 1.6vh, 0.9rem);
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  /* it used to be nowrap, so on a phone the whole banner ran off both edges
     of the screen and the message could not be read at all */
  white-space: normal;
  text-wrap: balance;
  text-align: center;
  max-width: min(30rem, 86vw);
  line-height: 1.35;
  box-shadow: 0 0 34px rgba(233, 205, 151, 0.24);
  opacity: 0;
  transition: opacity 420ms ease, transform 520ms var(--set);
  pointer-events: none;
}
.rcx-secured.is-live { opacity: 1; transform: translate(-50%, 0); }

/* ---- the way out ----------------------------------------------------
   A simulation was a one-way door: the layer owns the viewport and eats
   taps, so once a run started there was no way back to the page and no
   way to move to Live without sitting through every pack. */
.rcx-exit {
  position: absolute;
  top: 2.6vh;
  right: 3vw;
  z-index: 96;
  display: flex;
  align-items: center;
  gap: 0.45rem;
  opacity: 0;
  animation: rcx-exit-in 600ms ease 900ms forwards;
}
@keyframes rcx-exit-in { to { opacity: 1; } }

.rcx-exit button {
  border-radius: 999px;
  border: 1px solid rgba(158, 212, 255, 0.22);
  background: rgba(10, 14, 26, 0.74);
  color: #cfe0ff;
  cursor: pointer;
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transition: border-color 200ms ease, color 200ms ease, background 200ms ease;
}
.rcx-exit button:hover { border-color: rgba(233, 205, 151, 0.6); color: #fff; }
.rcx-exit button:focus-visible { outline: 2px solid #e9cd97; outline-offset: 3px; }

.rcx-exit-close {
  width: 2rem;
  height: 2rem;
  display: grid;
  place-items: center;
  font-size: 0.8rem;
  line-height: 1;
}
/* the live switch is the one that costs money, so it reads as a decision */
.rcx-exit-live {
  padding: 0.44rem 0.85rem;
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: #ffd9a8;
  border-color: rgba(233, 205, 151, 0.4);
}
.rcx-exit-live:hover { background: rgba(233, 205, 151, 0.14); }

/* once the hit is resting the layer is click-through, so the controls have
   done their job and would only sit on top of the page */
/* animation: none matters — the fade-in below runs with fill:forwards, and a
   filled animation outranks a plain declaration, so without cancelling it the
   close button stayed lit over the page after the run was over */
.rcx.is-resting-final .rcx-exit {
  animation: none;
  opacity: 0;
  pointer-events: none;
  transition: opacity 400ms ease;
}

@media (max-width: 720px) {
  .rcx-exit { top: 1.8vh; right: 3vw; gap: 0.35rem; }
  .rcx-exit-live { padding: 0.4rem 0.7rem; font-size: 0.62rem; letter-spacing: 0.1em; }
}

/* the pack minimum, counting off beside the run number */
.rcx-run-min {
  font-style: normal;
  font-size: 0.66rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  color: #8fa0bd;
  padding: 0.16rem 0.44rem;
  border-radius: 999px;
  border: 1px solid rgba(158, 212, 255, 0.16);
  transition: color 320ms ease, border-color 320ms ease, background 320ms ease;
}
.rcx-run-min.is-met {
  color: #2a1c05;
  border-color: transparent;
  background: linear-gradient(180deg, #f3dcae, #e9cd97);
  animation: rcx-min-met 520ms cubic-bezier(.2, .9, .25, 1);
}
@keyframes rcx-min-met {
  0%   { transform: scale(1); }
  38%  { transform: scale(1.18); }
  100% { transform: scale(1); }
}

/* ---- the settled card's plate, as a glass pane laid over the card ----
   It used to be a caption beneath the card, and that strip of the stage was
   where most of the card's size was going. Same material as the homepage
   detail sheet, so the two read as one family. */
.rcx-plate.is-overlay {
  /* set in JS from the card's own width, so the pane is part of the card.
     border-box or the padding pushes it back out past the card's edges. */
  box-sizing: border-box;
  width: var(--plate-w, min(21rem, 74vw));
  min-width: 0;
  padding: 0.72rem 0.9rem 0.8rem;
  border-radius: 18px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  /* Real glass, not frosting. The old pane leaned on a 14px blur and a
     rgba(10,14,26,.58) floor to make its type readable, which fogged the
     card underneath. The tint is now light enough to see the art through,
     and legibility comes from the type and the controls instead. */
  background: linear-gradient(158deg,
    rgba(255, 255, 255, 0.13),
    rgba(255, 255, 255, 0.05) 36%,
    rgba(8, 12, 22, 0.3) 78%,
    rgba(255, 255, 255, 0.07));
  backdrop-filter: blur(6px) saturate(1.5) brightness(0.86);
  -webkit-backdrop-filter: blur(6px) saturate(1.5) brightness(0.86);
  box-shadow:
    0 22px 52px rgba(0, 0, 0, 0.5),
    inset 0 1px 0 rgba(255, 255, 255, 0.34),
    inset 0 -1px 0 rgba(255, 255, 255, 0.08);
}
/* one still specular facet across the top-left, exactly as the sheet does */
.rcx-plate.is-overlay::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(118deg, rgba(255, 255, 255, 0.14), rgba(255, 255, 255, 0) 42%);
}
/* With a clearer pane the card's own art is behind every glyph, so the type
   carries its own contrast rather than relying on the panel to supply it. */
.rcx-plate.is-overlay b {
  font-size: 1.02rem;
  color: #fff;
  text-shadow:
    0 1px 2px rgba(0, 0, 0, 0.9),
    0 2px 14px rgba(0, 0, 0, 0.85);
}
.rcx-plate.is-overlay > i { opacity: 0.45; }
.rcx-plate.is-overlay .rcx-price {
  color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.9), 0 2px 12px rgba(0, 0, 0, 0.8);
}
.rcx-plate.is-overlay .rcx-price-psa { color: #cfe8ff; }
/* the two controls stop being translucent — they have to read as buttons
   against whatever the card art happens to be behind them */
.rcx-plate.is-overlay .rcx-reveal {
  background: linear-gradient(180deg, rgba(46, 36, 16, 0.88), rgba(24, 18, 8, 0.9));
  border-color: rgba(233, 205, 151, 0.8);
  color: #ffe6b8;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.12);
}
.rcx-plate.is-overlay .rcx-tag {
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.14);
}
.rcx-plate.is-overlay .rcx-tag.is-sim,
.rcx-plate.is-overlay .rcx-tag.is-nope {
  background: rgba(10, 14, 26, 0.82);
  border: 1px solid rgba(158, 212, 255, 0.28);
}

@media (max-width: 720px) {
  .rcx-plate.is-overlay { padding: 0.55rem 0.6rem 0.62rem; border-radius: 14px; }
  .rcx-plate.is-overlay b { font-size: 0.88rem; }
  .rcx-plate.is-overlay .rcx-reveal { padding: 0.34rem 0.7rem; font-size: 0.64rem; }
  .rcx-plate.is-overlay .rcx-tag { font-size: 0.58rem; }
}

