/* ==================================================================
   pack.css, le sachet de booster, la boutique en rail, et toute la
   chorégraphie d'ouverture.
   ================================================================== */

/* ================================================================
   1. LE SACHET
   ================================================================ */

.pack {
  --hue: 268;
  --mx: 50%;
  --my: 50%;
  --w: 216px;

  position: relative;
  width: var(--w);
  aspect-ratio: 3 / 4.35;
  perspective: 1200px;
  font-size: calc(var(--w) / 21.6);   /* 10px pour un sachet de 216px */
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.pack--stage { --w: min(320px, 62vw, 40vh); }

/* --- le corps, incliné en 3D --- */
.pack-body {
  --rx: 0deg;
  --ry: 0deg;
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  transform: rotateX(var(--rx)) rotateY(var(--ry));
  transition: transform .35s cubic-bezier(.16,1,.3,1);
  animation: pack-float 5s ease-in-out infinite;
}
@keyframes pack-float {
  0%, 100% { translate: 0 -.7em; }
  50%      { translate: 0 .7em; }
}

.pack-shell {
  position: absolute;
  inset: 0;
  border-radius: .9em;
  overflow: hidden;
  isolation: isolate;
  box-shadow:
    0 4em 6em -3em rgba(0,0,0,.9),
    0 0 0 1px hsl(var(--hue) 70% 70% / .35),
    inset 0 0 0 1px rgba(255,255,255,.14);
}

/* --- couche 1 : la feuille métallisée --- */
.pack-foil {
  position: absolute; inset: 0;
  background:
    linear-gradient(168deg,
      hsl(var(--hue) 72% 44%) 0%,
      hsl(calc(var(--hue) + 24) 66% 24%) 38%,
      hsl(calc(var(--hue) - 16) 74% 15%) 68%,
      hsl(calc(var(--hue) + 42) 60% 26%) 100%);
}

/* --- couche 2 : le prisme holographique.
   Masqué au centre : c'est là que vivent le visuel et le nom, et un
   arc-en-ciel plein cadre rend tout illisible. --- */
.pack-prism {
  position: absolute; inset: -20%;
  background: repeating-linear-gradient(104deg,
    #ff0080 0%, #ff8c00 7%, #ffe600 13%, #00ff9d 20%,
    #00b3ff 27%, #8b00ff 34%, #ff0080 40%);
  background-size: 220% 220%;
  mix-blend-mode: color-dodge;
  opacity: .16;
  filter: blur(.9em) saturate(1.3);
  animation: prism-drift 9s linear infinite;
  mask-image: radial-gradient(75% 60% at 50% 45%, transparent 8%, #000 78%);
  -webkit-mask-image: radial-gradient(75% 60% at 50% 45%, transparent 8%, #000 78%);
}
@keyframes prism-drift {
  0%   { background-position: 0% 50%; }
  50%  { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}

/* --- couche 3 : la trame fine du film plastique --- */
.pack-weave {
  position: absolute; inset: 0;
  background:
    repeating-linear-gradient(72deg, rgba(255,255,255,.07) 0 1px, transparent 1px 4px),
    repeating-linear-gradient(-72deg, rgba(0,0,0,.14) 0 1px, transparent 1px 5px);
  opacity: .7;
}

/* --- couche 4 : la rondeur du sachet (bord gauche clair, droit sombre) --- */
.pack-cylinder {
  position: absolute; inset: 0;
  background: linear-gradient(90deg,
    rgba(0,0,0,.42) 0%,
    rgba(255,255,255,.26) 9%,
    rgba(255,255,255,.05) 20%,
    transparent 44%,
    rgba(0,0,0,.28) 78%,
    rgba(0,0,0,.6) 100%);
}

/* --- couche 4bis : le voile qui garantit la lisibilité du texte --- */
.pack-scrim {
  position: absolute; inset: 0;
  background: linear-gradient(180deg,
    rgba(4,4,10,.3) 0%,
    transparent 20%,
    transparent 46%,
    rgba(4,4,10,.42) 68%,
    rgba(4,4,10,.66) 100%);
}

/* --- couche 5 : le reflet spéculaire qui balaie --- */
.pack-sheen {
  position: absolute; inset: -30% -60%;
  background: linear-gradient(104deg,
    transparent 43%,
    rgba(255,255,255,.5) 48.5%,
    rgba(255,255,255,.12) 52%,
    transparent 58%);
  transform: translateX(-60%);
  animation: sheen-sweep 4.2s cubic-bezier(.5,0,.3,1) infinite;
  mix-blend-mode: screen;
  pointer-events: none;
}
@keyframes sheen-sweep {
  0%, 58% { transform: translateX(-60%); }
  100%    { transform: translateX(60%); }
}

/* --- le sertissage cranté, en haut et en bas --- */
.pack-crimp {
  position: absolute;
  left: 0; right: 0;
  height: 9%;
  background:
    linear-gradient(180deg, rgba(255,255,255,.34), rgba(255,255,255,.04) 55%, rgba(0,0,0,.28)),
    repeating-linear-gradient(90deg,
      rgba(255,255,255,.16) 0 2px,
      rgba(0,0,0,.1) 2px 5px);
  box-shadow: inset 0 0 1em rgba(0,0,0,.35);
  z-index: 3;
}
.pack-crimp.is-top { top: 0; }
.pack-crimp.is-bottom { bottom: 0; }

/* la ligne de perforation : c'est là que ça se déchire */
.pack-crimp i {
  position: absolute;
  left: 0; right: 0;
  height: 2px;
  background: repeating-linear-gradient(90deg,
    rgba(0,0,0,.75) 0 4px, transparent 4px 9px);
}
.pack-crimp.is-top i { bottom: 0; }
.pack-crimp.is-bottom i { top: 0; }

.pack-crimp.is-top::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -.5em;
  height: .5em;
  background: linear-gradient(180deg, rgba(0,0,0,.5), transparent);
}

/* --- la fenêtre découpée, avec le visuel du genre --- */
.pack-window {
  position: absolute;
  left: 50%; top: 20%;
  width: 52%;
  aspect-ratio: 1;
  translate: -50% 0;
  border-radius: 50%;
  overflow: hidden;
  z-index: 2;
  display: grid;
  place-items: center;
  box-shadow:
    0 0 0 .2em rgba(255,255,255,.22),
    0 0 0 .45em rgba(0,0,0,.35),
    0 1.2em 2em -.8em rgba(0,0,0,.8);
}
.pack-window img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: .72;
  filter: saturate(1.35) contrast(1.1) brightness(.85);
}
.win-glow {
  position: absolute; inset: 0;
  background: radial-gradient(circle at 34% 24%,
      hsl(var(--hue) 95% 72% / .5), transparent 58%),
    radial-gradient(circle at 72% 80%, hsl(calc(var(--hue) + 60) 95% 62% / .35), transparent 58%),
    radial-gradient(circle, transparent 42%, rgba(4,4,10,.72) 100%);
}
.win-disc {
  position: absolute; inset: 14%;
  border-radius: 50%;
  background: conic-gradient(from 0deg,
    hsl(var(--hue) 90% 68%), #fff, hsl(calc(var(--hue) + 70) 90% 64%),
    #fff, hsl(var(--hue) 90% 68%));
  opacity: .3;
  filter: blur(.12em);
  animation: win-spin 7s linear infinite;
}
@keyframes win-spin { to { transform: rotate(360deg); } }
.win-ring {
  position: absolute; inset: 8%;
  border: 1px dashed rgba(255,255,255,.4);
  border-radius: 50%;
  animation: win-spin 18s linear infinite reverse;
}
.win-mark {
  position: relative;
  font-size: 2.6em;
  color: #fff;
  text-shadow: 0 0 1.2em rgba(255,255,255,.9), 0 .1em .3em rgba(0,0,0,.6);
}

/* --- l'étiquette du coin : période ou thème ---
   Elle sort du nom de la plaque. Portée à la suite du titre, la période
   allongeait le nom au point de le passer sur deux lignes et de rendre
   quatre volumes du même genre indiscernables au premier coup d'œil. */
.pack-tab {
  position: absolute;
  left: 0;
  top: 13.5%;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .05em;
  padding: .45em 1em .5em .9em;
  border: 1px solid rgba(255,255,255,.26);
  border-left: 0;
  border-radius: 0 .55em .55em 0;
  background:
    linear-gradient(100deg, hsl(var(--hue) 78% 46% / .96), hsl(calc(var(--hue) + 26) 70% 32% / .92));
  box-shadow:
    .18em .18em .5em rgba(0,0,0,.55),
    inset 0 1px 0 rgba(255,255,255,.3);
  text-shadow: 0 .08em .2em rgba(0,0,0,.6);
}
.pack-tab b {
  text-transform: uppercase;
  font-size: .78em;
  font-weight: 800;
  letter-spacing: .22em;
  color: rgba(255,255,255,.72);
}
.pack-tab span {
  font-size: 1.15em;
  font-weight: 800;
  letter-spacing: .02em;
  line-height: 1.05;
  color: #fff;
  font-variant-numeric: tabular-nums;
}

/* --- la plaque gravée --- */
.pack-plate {
  position: absolute;
  left: 6%; right: 6%;
  top: 49%;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .25em;
  text-align: center;
  text-shadow: 0 .15em .5em rgba(0,0,0,.85);
}
.plate-kicker {
  text-transform: uppercase;
  font-size: .95em;
  font-weight: 800;
  letter-spacing: .5em;
  text-indent: .5em;
  color: rgba(255,255,255,.62);
}
.plate-name {
  /* La plaque ne porte plus que le nom de la collection, la période est
     partie dans l'étiquette du coin, donc elle tient sur une ligne et peut
     retrouver sa taille. */
  font-size: 2.05em;
  font-weight: 800;
  letter-spacing: -.02em;
  line-height: 1.12;
  text-wrap: balance;
  color: #fff;
  text-shadow:
    0 .06em .18em rgba(0,0,0,.95),
    0 0 .9em hsl(var(--hue) 95% 70% / .85);
}
.plate-rule {
  width: 45%;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.6), transparent);
}
.plate-tag {
  font-size: 1.05em;
  letter-spacing: .08em;
  color: rgba(255,255,255,.66);
}

/* --- le pied : contenu + pastilles de rareté --- */
.pack-foot {
  position: absolute;
  left: 8%; right: 8%;
  bottom: 13%;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .6em;
}
.foot-count {
  text-transform: uppercase;
  font-size: .95em;
  font-weight: 800;
  letter-spacing: .28em;
  text-indent: .28em;
  color: rgba(255,255,255,.8);
  padding: .3em 1em;
  border: 1px solid rgba(255,255,255,.3);
  border-radius: 999px;
  background: rgba(0,0,0,.3);
  backdrop-filter: blur(4px);
}
.foot-gems { display: flex; gap: .5em; }
.foot-gems i {
  width: .55em; height: .55em;
  border-radius: 50%;
  background: var(--g);
  box-shadow: 0 0 .7em var(--g);
}

/* --- vernis et couture --- */
.pack-gloss {
  position: absolute; inset: 0;
  z-index: 4;
  pointer-events: none;
  background: radial-gradient(60% 50% at var(--mx) var(--my),
    rgba(255,255,255,.3), transparent 60%);
  mix-blend-mode: overlay;
  opacity: 0;
  transition: opacity .3s;
}
.pack:hover .pack-gloss { opacity: 1; }

.pack-seam {
  position: absolute;
  left: 0; right: 0;
  top: 9%;
  height: 2px;
  z-index: 5;
  background: #fff;
  opacity: 0;
  box-shadow: 0 0 1.5em .3em #fff;
}

/* --- halo et ombre --- */
.pack-halo {
  position: absolute;
  /* Le halo déborde de 18 % de chaque côté et montait par-dessus le rail
     des collections : la carte centrée n'était plus cliquable, et le clic
     ouvrait un booster au lieu de changer de collection. Il ne capte donc
     plus le pointeur, comme l'ombre, ce n'est qu'un décor. */
  pointer-events: none;
  inset: -18%;
  border-radius: 50%;
  background: radial-gradient(closest-side,
    hsl(var(--hue) 90% 62% / .55), transparent 72%);
  filter: blur(2.4em);
  opacity: .55;
  z-index: -1;
  transition: opacity .35s;
  animation: halo-breathe 4s ease-in-out infinite;
}
@keyframes halo-breathe { 50% { transform: scale(1.09); } }
.pack:hover .pack-halo { opacity: .95; }

.pack-shadow {
  pointer-events: none;
  position: absolute;
  left: 50%; bottom: -6%;
  width: 62%; height: 5%;
  translate: -50% 0;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(0,0,0,.65), transparent);
  filter: blur(.6em);
  animation: shadow-pulse 5s ease-in-out infinite;
}
@keyframes shadow-pulse {
  0%, 100% { transform: translate(-50%, 0) scale(.88); opacity: .75; }
  50%      { transform: translate(-50%, 0) scale(1.06); opacity: .5; }
}

/* ================================================================
   2. LA BOUTIQUE, un sachet en grand, les voisins en retrait
   ================================================================ */

.shop {
  --hue: 268;
  position: relative;
  /* les sachets voisins débordent volontairement : on les rogne plutôt
     que de laisser apparaître une barre de défilement horizontale */
  overflow-x: clip;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  /* prend toute la hauteur restante de l'écran, et pas un pixel de plus */
  flex: 1 1 auto;
  min-height: 0;
  padding-bottom: 4px;
}

/* La scène : le sachet, ses voisins, les deux flèches et le logo en
   fond. Les flèches vivent ici plutôt que dans `.shop` pour rester à
   hauteur du sachet quel que soit le texte qui suit. */
.shop-scene {
  position: relative;
  width: 100%;
  flex: 1 1 auto;
  min-height: 190px;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Le sachet doit se regler sur la place qui reste, pas sur la hauteur
     de la fenetre : entre les deux il y a la barre du haut, le rail et
     les boutons, et l'ecart suffisait a faire deborder le sachet sur son
     propre titre. En declarant la scene comme conteneur de requete, on
     peut exprimer la largeur du sachet en pourcentage de la scene. */
  container-type: size;
  container-name: scene;
}

/* Le logo, en grand, derrière le présentoir. C'est la seule place où il
   peut s'afficher à cette taille sans voler de hauteur à rien : il
   occupe un espace qui, autrement, resterait vide. */
.shop-logo {
  position: absolute;
  left: 50%; top: 50%;
  translate: -50% -50%;
  width: min(118%, 700px);
  max-height: 104%;
  object-fit: contain;
  /* `screen` fait ressortir les couleurs du logo sur un fond sombre sans
     l'eclaircir : sous un simple voile d'opacite, le trait bleu nuit du
     dessin disparaissait completement. */
  mix-blend-mode: screen;
  opacity: .3;
  filter: saturate(1.2);
  pointer-events: none;
  user-select: none;
  animation: logo-breathe 11s ease-in-out infinite;
}
@keyframes logo-breathe {
  0%, 100% { opacity: .26; scale: 1; }
  50%      { opacity: .36; scale: 1.04; }
}

.shop-stage {
  position: absolute;
  inset: 0;
  perspective: 1400px;
  transform-style: preserve-3d;
  touch-action: pan-y;
}

.shop-slot {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  transition: transform .55s cubic-bezier(.16,1,.3,1), opacity .45s;
  backface-visibility: hidden;
  will-change: transform, opacity;
  cursor: pointer;
}
.shop-slot .pack { --w: max(110px, min(300px, 58cqw, 62cqh)); }
.shop-slot.is-active { cursor: pointer; }
.shop-slot:not(.is-active) .pack-sheen { animation: none; opacity: .25; }

.shop-slot.is-active .pack-body { animation: pack-float 5s ease-in-out infinite; }
.shop-slot.is-active:hover .pack-body { scale: 1.035; }

.shop-nav {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
  z-index: 20;
  width: 48px; height: 48px;
  display: grid; place-items: center;
  border: 1px solid var(--line-2);
  border-radius: 50%;
  background: rgba(12,12,20,.82);
  backdrop-filter: blur(14px);
  color: var(--ink);
  font-size: 15px;
  cursor: pointer;
  transition: background .2s, transform .2s, border-color .2s;
}
.shop-nav:hover {
  background: hsl(var(--hue) 70% 55% / .28);
  border-color: hsl(var(--hue) 80% 65% / .7);
  transform: scale(1.09);
}
.shop-nav.is-prev { left: max(4px, 2vw); }
.shop-nav.is-next { right: max(4px, 2vw); }

.shop-meta {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  text-align: center;
  flex: 0 0 auto;
}
.shop-name {
  font-size: clamp(20px, 3.2vw, 30px);
  letter-spacing: -.02em;
  line-height: 1.15;
  text-shadow: 0 0 1.4em hsl(var(--hue) 90% 65% / .55);
}
.shop-tag { font-size: 13px; color: var(--ink-2); max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.btn-open {
  padding: 13px 30px;
  font-size: 15px;
  background: linear-gradient(135deg, hsl(var(--hue) 75% 58%), hsl(calc(var(--hue) + 30) 70% 42%));
  box-shadow: 0 14px 34px -14px hsl(var(--hue) 85% 55% / .95);
}
.btn-open:hover { box-shadow: 0 20px 44px -16px hsl(var(--hue) 90% 60%); }

.shop-skeleton {
  position: absolute;
  left: 50%; top: 50%;
  translate: -50% -50%;
  width: max(110px, min(300px, 58cqw, 62cqh));
  aspect-ratio: 3 / 4.35;
  border-radius: .9em;
  background: linear-gradient(100deg, var(--panel) 30%, var(--panel-2) 50%, var(--panel) 70%);
  background-size: 300% 100%;
  animation: skel 1.4s linear infinite;
}
@keyframes skel { to { background-position: -300% 0; } }

.load-err { text-align: center; color: var(--ink-2); padding: 40px; width: 100%; }

@media (max-width: 720px) {
  .shop-nav { width: 40px; height: 40px; }
  .shop-slot .pack { --w: max(110px, min(250px, 66cqw, 64cqh)); }
}

/* ================================================================
   3. LA SCÈNE
   ================================================================ */

.stage {
  position: fixed;
  inset: 0;
  z-index: 5000;
  display: grid;
  place-items: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity .4s;
  --aura: #8b95ad;
  --aura-soft: #c8d0e0;
}
.stage.is-open { opacity: 1; pointer-events: auto; }
body.stage-live main,
body.stage-live .topbar,
body.stage-live .tabbar { visibility: hidden; }

.stage-bg {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(70% 60% at 50% 45%,
      color-mix(in srgb, var(--aura) 22%, transparent), transparent 70%),
    radial-gradient(120% 100% at 50% 120%, rgba(8,8,16,.4), #04040a 70%),
    #04040a;
  /* pas de `backdrop-filter` : il floute tout l'écran à chaque image alors
     que le fond est déjà quasi opaque, on ne voyait pas la différence */
  transition: background .6s;
}

/* rayons tournants */
.stage-rays {
  position: absolute;
  /* Borné en pixels : en `vmax` pur, la couche atteignait 2500 px de côté,
     soit ~26 Mo de texture à garder en mémoire pour un simple décor. */
  width: min(140vmax, 1800px);
  height: min(140vmax, 1800px);
  will-change: transform;
  background: repeating-conic-gradient(from 0deg,
    color-mix(in srgb, var(--aura) 22%, transparent) 0deg 5deg,
    transparent 5deg 14deg);
  opacity: 0;
  mask-image: radial-gradient(closest-side, #000 18%, transparent 70%);
  -webkit-mask-image: radial-gradient(closest-side, #000 18%, transparent 70%);
  animation: rays-spin 40s linear infinite;
  transition: opacity .6s;
  pointer-events: none;
}
@keyframes rays-spin { to { transform: rotate(360deg); } }

.stage[data-rarity="rare"]      .stage-rays { opacity: .28; }
.stage[data-rarity="epic"]      .stage-rays { opacity: .45; }
.stage[data-rarity="legendary"] .stage-rays { opacity: .66; }
.stage[data-rarity="mythic"]    .stage-rays { opacity: .85; }

/* onde de choc : anneaux qui s'étendent depuis le centre */
.stage-shock { position: absolute; inset: 0; pointer-events: none; z-index: 40; }
.shock {
  position: absolute;
  left: 50%; top: 50%;
  width: 10px; height: 10px;
  translate: -50% -50%;
  border-radius: 50%;
  border: 3px solid var(--sc, #fff);
  opacity: 0;
  animation: shock-out var(--sd, 900ms) cubic-bezier(.16,1,.3,1) forwards;
}
@keyframes shock-out {
  0%   { transform: scale(.4); opacity: 0; }
  12%  { opacity: .95; }
  100% { transform: scale(var(--ss, 60)); opacity: 0; border-width: 1px; }
}

/* poussée de caméra : tout le contenu recule d'un coup */
.stage-phase { transition: transform .7s cubic-bezier(.16,1,.3,1); }
.stage.is-punched .stage-phase { transform: scale(.93); transition-duration: .12s; }

.stage.is-slowmo .stage-bg { background-color: rgba(2,2,6,.985); }
.stage.is-slowmo .stage-rays { animation-duration: 90s; }

.stage-close {
  position: absolute;
  top: max(16px, env(safe-area-inset-top)); right: 16px;
  z-index: 60;
}
.stage-close:hover { transform: rotate(90deg); }

.stage-phase {
  position: absolute;
  inset: 0;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 26px;
  padding: 24px;
}
.stage-phase.is-on { display: flex; animation: phase-in .45s cubic-bezier(.16,1,.3,1) both; }
@keyframes phase-in { from { opacity: 0; } }

.stage-hint {
  font-size: 13px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-2);
  animation: hint-pulse 2s ease-in-out infinite;
  transition: opacity .3s;
}
.stage-hint.sm { font-size: 11px; letter-spacing: .1em; animation: none; color: var(--ink-3); }
.stage-hint.is-dim { opacity: 0; animation: none; }
@keyframes hint-pulse { 50% { opacity: .42; } }

/* --------------------------- chargement --------------------------- */

.loader-ring { position: relative; width: 84px; height: 84px; }
.loader-ring i {
  position: absolute;
  inset: 0;
  border: 2px solid transparent;
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: load-spin 1.1s linear infinite;
}
.loader-ring i:nth-child(2) { inset: 11px; border-top-color: var(--accent-2); animation-duration: 1.5s; animation-direction: reverse; }
.loader-ring i:nth-child(3) { inset: 22px; border-top-color: #ec4899; animation-duration: .85s; }
@keyframes load-spin { to { transform: rotate(360deg); } }
.loader-txt { color: var(--ink-2); font-size: 14px; letter-spacing: .04em; }

/* ================================================================
   4. PHASE SACHET, la montée en charge et la déchirure
   ================================================================ */

.charge-wrap {
  position: relative;
  display: grid;
  place-items: center;
  --charge: 0;
}

.charge-ring {
  position: absolute;
  width: 168%;
  aspect-ratio: 1;
  transform: rotate(-90deg);
  pointer-events: none;
  overflow: visible;
}
.cr-track, .cr-fill {
  fill: none;
  stroke-width: 1.4;
  stroke-linecap: round;
}
.cr-track { stroke: rgba(255,255,255,.08); }
.cr-fill {
  stroke: var(--aura-soft);
  stroke-dasharray: 345.6;
  stroke-dashoffset: calc(345.6 - 345.6 * var(--charge));
  filter: drop-shadow(0 0 6px var(--aura-soft));
  transition: stroke-dashoffset .08s linear;
}
.charge-wrap.is-charging .cr-track { stroke: rgba(255,255,255,.16); }

/* le sachet se tend, tremble et chauffe pendant la charge */
.pack-slot .pack { cursor: grab; }
/* Tout ce qui bouge ici est composé par la carte graphique : échelle,
   déplacement, opacité. Les vitesses d'animation ne dépendent plus de la
   charge (changer une durée à chaque image faisait sauter l'animation),
   et le tremblement a une amplitude fixe, plus forte passé la moitié. Le
   sachet est promu sur son propre calque : sans ça, le téléphone le
   repeignait à chaque nouvelle taille, d'où les carrés blancs et la
   pochette qui clignotait. */
.charge-wrap.is-charging .pack-body {
  animation: pack-float 5s ease-in-out infinite, pack-strain .11s linear infinite;
  scale: calc(1 + var(--charge) * .1);
  will-change: transform, scale, translate;
}
.charge-wrap.is-charging.is-hot .pack-body {
  animation: pack-float 5s ease-in-out infinite, pack-strain-hot .09s linear infinite;
}
@keyframes pack-strain {
  0%, 100% { translate: 0 0; }
  25%  { translate: -1px .5px; }
  50%  { translate: 1px -.8px; }
  75%  { translate: -.6px 1px; }
}
@keyframes pack-strain-hot {
  0%, 100% { translate: 0 0; }
  25%  { translate: -3px 1.5px; }
  50%  { translate: 2.5px -2px; }
  75%  { translate: -1.5px 2.5px; }
}
.charge-wrap.is-charging .pack-halo { opacity: calc(.5 + var(--charge) * .5); }
.charge-wrap.is-charging .pack-seam {
  opacity: var(--charge);
  height: 5px;
  transform: scaleY(calc(.4 + var(--charge) * .6));
}
.charge-wrap.is-charging .pack-prism {
  opacity: calc(.28 + var(--charge) * .5);
  animation-duration: 3s;
}

/* la colonne de lumière qui jaillit de la déchirure */
.tear-light {
  position: absolute;
  top: 9%;
  left: 50%;
  width: 6px; height: 4px;
  translate: -50% 0;
  border-radius: 999px;
  background: #fff;
  opacity: 0;
  pointer-events: none;
  z-index: 20;
}
.charge-wrap.is-tearing .tear-light { animation: tear-burst 1s cubic-bezier(.16,1,.3,1) forwards; }
@keyframes tear-burst {
  0%   { transform: scale(1, 1);      opacity: 0; box-shadow: 0 0 0 0 #fff; }
  18%  { transform: scale(26, 1.6);   opacity: 1; box-shadow: 0 0 6em 2em var(--aura-soft); }
  46%  { transform: scale(40, 30);    opacity: .95; box-shadow: 0 0 12em 6em var(--aura-soft); }
  100% { transform: scale(70, 120);   opacity: 0;  box-shadow: 0 0 20em 12em transparent; }
}

/* la bande crantée s'arrache et part en vrille */
.charge-wrap.is-tearing .pack-crimp.is-top {
  animation: crimp-rip .85s cubic-bezier(.35,-.2,.5,1.2) forwards;
}
@keyframes crimp-rip {
  0%   { transform: none; opacity: 1; }
  15%  { transform: translate(0, -.6em) rotate(-2deg); }
  100% { transform: translate(7em, -22em) rotate(-42deg) scale(1.15); opacity: 0; }
}

/* le sachet vidé s'effondre */
.charge-wrap.is-tearing .pack-body {
  animation: pack-drop .9s cubic-bezier(.5,0,.75,0) .16s forwards;
}
@keyframes pack-drop {
  0%   { transform: rotateX(0) rotateY(0) scale(1); opacity: 1; }
  25%  { transform: rotateX(-8deg) rotateY(6deg) scale(1.06); opacity: 1; }
  100% { transform: rotateX(46deg) rotateY(-14deg) scale(.82) translateY(26em); opacity: 0; }
}
.charge-wrap.is-tearing { pointer-events: none; }
.charge-wrap.is-tearing .charge-ring { animation: ring-blow .5s ease-out forwards; }
@keyframes ring-blow { to { transform: rotate(-90deg) scale(2.2); opacity: 0; } }

/* ================================================================
   5. PHASE OUVERTURE, pile, tirage, révélation
   ================================================================ */

#phase-open {
  perspective: 1700px;
  /* la largeur de carte pilote toute la scène : la pile et la carte
     révélée font exactement la même taille */
  --cw: 280px;
}

.open-table {
  position: relative;
  width: var(--cw);
  aspect-ratio: 63 / 94;
  transform-style: preserve-3d;
}

/* --- la pile de dos de cartes, posée à gauche de l'emplacement --- */
.deck {
  position: absolute;
  left: 50%; top: 50%;
  translate: -50% -50%;
  /* la pile démarre au centre, puis se range à gauche dès le premier tirage */
  transform: translateX(var(--shift, 0px));
  transition: transform .55s cubic-bezier(.16,1,.3,1);
  transform-style: preserve-3d;
  cursor: pointer;
}
.deck.is-empty { pointer-events: none; }
/* Sur téléphone, la pile recule derrière la carte révélée au lieu de se
   ranger à côté : deux cartes de front ne tiennent pas en portrait. */
.deck.is-behind {
  transform: translateY(-9%) scale(.86);
  opacity: .5;
  pointer-events: none;
}

.deck-card {
  position: absolute;
  left: 50%; top: 50%;
  width: var(--cw);
  aspect-ratio: 63 / 94;
  translate: -50% -50%;
  border-radius: 1.6em;
  font-size: calc(var(--cw) / 30);
  transform: translate3d(var(--dx), var(--dy), var(--dz)) rotate(var(--dr));
  opacity: var(--dop, 1);
  transition: transform .5s cubic-bezier(.16,1,.3,1), opacity .4s;
  background: radial-gradient(120% 90% at 50% 0%, #2b2456, #14122a 55%, #0a0a16);
  border: 1px solid rgba(255,255,255,.14);
  box-shadow: inset 0 0 6em rgba(139,92,246,.22), 0 2em 4.6em -2.2em #000;
  display: grid;
  place-items: center;
  overflow: hidden;
}
.dk-disc {
  position: absolute;
  left: 50%; top: 50%;
  width: 34%; aspect-ratio: 1;
  translate: -50% -50%;
  border-radius: 50%;
  background: conic-gradient(from 0deg, #8b5cf6, #22d3ee, #ec4899, #8b5cf6);
  opacity: .5;
  filter: blur(1px);
  animation: win-spin 9s linear infinite;
}
.dk-ring {
  position: absolute;
  left: 50%; top: 50%;
  width: 62%; aspect-ratio: 1;
  translate: -50% -50%;
  border: 1px solid rgba(255,255,255,.16);
  border-radius: 50%;
}
.dk-ring.r2 {
  width: 46%;
  border-style: dashed;
  border-color: rgba(255,255,255,.14);
  animation: win-spin 22s linear infinite reverse;
}
.dk-logo {
  position: relative;
  width: 30%;
  filter: drop-shadow(0 0 1.2em rgba(139,92,246,.8));
  user-select: none;
}
.dk-word {
  position: absolute;
  bottom: 10%;
  font-size: 1.1em;
  letter-spacing: .42em;
  color: rgba(255,255,255,.45);
  font-weight: 700;
}
.deck-card.is-top { cursor: pointer; }

/* le compteur de cartes restantes, posé sur la tranche de la pile */
.deck-count {
  position: absolute;
  left: 50%; top: 50%;
  translate: -50% -50%;
  transform: translate3d(var(--dx, 0), calc(var(--dy, 0px) - var(--cw) * .62), 40px);
  z-index: 5;
  padding: 5px 13px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .1em;
  white-space: nowrap;
  color: #fff;
  background: rgba(10,10,20,.82);
  border: 1px solid rgba(255,255,255,.2);
  backdrop-filter: blur(8px);
  pointer-events: none;
  transition: transform .5s cubic-bezier(.16,1,.3,1);
}
.deck.is-ready .deck-card.is-top { animation: deck-breathe 2.2s ease-in-out infinite; }
@keyframes deck-breathe {
  50% { transform: translate3d(var(--dx), calc(var(--dy) - 10px), var(--dz)) rotate(var(--dr)) scale(1.03); }
}
.deck:hover .deck-card.is-top { filter: brightness(1.25); }

/* halo sous la pile */
.deck::after {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  width: 150%; aspect-ratio: 1;
  translate: -50% -50%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(139,92,246,.3), transparent 70%);
  filter: blur(30px);
  z-index: -1;
  animation: halo-breathe 3s ease-in-out infinite;
}

/* --- l'emplacement de révélation --- */
.reveal-slot {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  transform-style: preserve-3d;
  z-index: 10;
  /* l'emplacement couvre toute la table : vide, il avalerait les clics
     destinés à la pile posée juste dessous */
  pointer-events: none;
}
.reveal-slot .card { pointer-events: auto; }

/* anneaux de charge avant une carte de haut palier */
.reveal-slot.is-charging::before,
.reveal-slot.is-charging::after {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  width: 150%; aspect-ratio: 1;
  translate: -50% -50%;
  border: 2px solid var(--aura);
  border-radius: 50%;
  opacity: 0;
  animation: charge-ring var(--charge-ms, 800ms) cubic-bezier(.4,0,.2,1) infinite;
}
.reveal-slot.is-charging::after { animation-delay: calc(var(--charge-ms, 800ms) / -2); }
@keyframes charge-ring {
  0%   { transform: scale(1.7); opacity: 0; }
  40%  { opacity: .9; }
  100% { transform: scale(.75); opacity: 0; }
}

.reveal-slot.has-aura .aura {
  position: absolute;
  left: 50%; top: 50%;
  width: 200%; aspect-ratio: 1;
  translate: -50% -50%;
  border-radius: 50%;
  background: radial-gradient(closest-side, color-mix(in srgb, var(--aura) 48%, transparent), transparent 72%);
  filter: blur(34px);
  animation: aura-pulse 2.6s ease-in-out infinite;
  pointer-events: none;
  z-index: -1;
}
@keyframes aura-pulse {
  0%, 100% { transform: translate(-50%,-50%) scale(1); opacity: .55; }
  50%      { transform: translate(-50%,-50%) scale(1.1); opacity: .95; }
}
.reveal-slot.is-mythic .aura { animation: aura-pulse 2.6s ease-in-out infinite, mythic-hue 5s linear infinite; }
.stage[data-rarity="mythic"] .stage-rays { animation: rays-spin 14s linear infinite, mythic-hue 5s linear infinite; }
@keyframes mythic-hue { to { filter: hue-rotate(360deg); } }

/* --- les éclats du dos qui vole en morceaux --- */
.shatter {
  position: absolute;
  left: 50%; top: 50%;
  translate: -50% -50%;
  pointer-events: none;
  z-index: 30;
  perspective: 1100px;
  transform-style: preserve-3d;
}
.shard {
  position: absolute;
  left: 50%; top: 50%;
  width: var(--sw); height: var(--sh);
  margin-left: calc(var(--sw) / -2);
  margin-top: calc(var(--sh) / -2);
  clip-path: var(--clip);
  transform-style: preserve-3d;
  animation: shard-fly var(--sdur) cubic-bezier(.08,.72,.28,1) var(--sdel) forwards;
}

/* la copie du vrai dos : c'est ce qui rend les morceaux reconnaissables */
.shard-face {
  position: absolute;
  inset: 0;
  transform: none !important;
  backface-visibility: visible !important;
  border-radius: 0 !important;
  overflow: hidden;
}
/* l'arête fraîche de la cassure, vive au centre et qui s'éteint au bord */
.shard::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(60% 60% at 50% 50%,
    rgba(255,255,255,.75), rgba(255,255,255,.12) 45%, transparent 72%);
  mix-blend-mode: screen;
  opacity: .9;
  animation: shard-cool var(--sdur) linear var(--sdel) forwards;
}
@keyframes shard-cool { to { opacity: 0; } }

/* `drop-shadow` suit la forme réellement découpée par le clip-path :
   c'est ce qui dessine l'arête vive de chaque morceau et les rend
   lisibles sur un fond noir. */
/* Les morceaux doivent rester lisibles pendant leur vol : ils s'écartent
   vite, gardent leur opacité jusqu'aux deux tiers, et ne s'éteignent
   qu'à la fin. Un fondu trop précoce ne laisse voir que les arêtes. */
@keyframes shard-fly {
  0% {
    transform: translate3d(0,0,0) rotate3d(0,0,1,0deg);
    opacity: 1;
    filter: brightness(3) drop-shadow(0 0 8px rgba(255,255,255,.95));
  }
  10% { filter: brightness(1.5) drop-shadow(0 0 6px rgba(255,255,255,.85)); }
  65% {
    opacity: 1;
    filter: brightness(1.25) drop-shadow(0 0 4px rgba(255,255,255,.6));
  }
  100% {
    transform: translate3d(var(--tx), var(--ty), 240px)
               rotateX(var(--rx)) rotateY(var(--ry)) rotateZ(var(--rz))
               scale(.7);
    opacity: 0;
    filter: brightness(1.1) drop-shadow(0 0 2px rgba(255,255,255,.3));
  }
}

/* --- le tampon de rareté --- */
.rarity-stamp {
  position: absolute;
  left: 50%;
  bottom: 15%;
  translate: -50% 0;
  z-index: 45;
  font-size: clamp(26px, 6vw, 54px);
  font-weight: 900;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: #fff;
  text-shadow:
    0 0 .3em var(--aura),
    0 0 1em var(--aura),
    0 .06em 0 rgba(0,0,0,.6);
  opacity: 0;
  pointer-events: none;
  white-space: nowrap;
}
.rarity-stamp.is-on { animation: stamp-slam .9s cubic-bezier(.16,1,.3,1) forwards; }
@keyframes stamp-slam {
  0%   { opacity: 0; transform: scale(7) rotate(-9deg); filter: blur(12px); }
  22%  { opacity: 1; transform: scale(1) rotate(-2deg); filter: blur(0); }
  30%  { transform: scale(1.09) rotate(1deg); }
  40%  { transform: scale(1) rotate(0); }
  82%  { opacity: 1; }
  100% { opacity: 0; transform: scale(1.04); }
}
.rarity-stamp.is-mythic {
  background: linear-gradient(90deg, #ff3d71, #ffb020, #2ee08a, #3fa9ff, #a855f7, #ff3d71);
  background-size: 300% 100%;
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
}
/* l'animation n'appartient qu'au tampon allumé : posée sur .is-mythic seul,
   elle rejouait « SECRÈTE » à l'ouverture suivante, dès que la scène
   redevenait visible */
.rarity-stamp.is-mythic.is-on {
  animation: stamp-slam .9s cubic-bezier(.16,1,.3,1) forwards, stamp-rainbow 1.4s linear infinite;
}
@keyframes stamp-rainbow { to { background-position: 300% 0; } }

/* --- le compteur de cartes --- */
.reveal-counter { display: flex; gap: 7px; z-index: 20; }
.pip {
  width: 9px; height: 9px;
  border-radius: 50%;
  background: rgba(255,255,255,.16);
  transition: all .35s cubic-bezier(.34,1.56,.64,1);
}
.pip.is-done { background: var(--pip); box-shadow: 0 0 10px var(--pip); }
.pip.is-current { width: 24px; border-radius: 999px; background: #fff; box-shadow: 0 0 14px #fff; }

.btn-next { z-index: 20; }

/* ================================================================
   6. LE RÉCAPITULATIF, voir reel.css
   ================================================================ */

#phase-carousel {
  justify-content: space-between;
  gap: 14px;
  padding: clamp(56px, 8vh, 84px) 0 calc(clamp(18px, 3.5vh, 36px) + var(--safe-b));
}

.carousel-head { text-align: center; z-index: 5; padding: 0 20px; }
.carousel-head h2 {
  font-size: clamp(24px, 4.4vw, 36px);
  letter-spacing: -.02em;
  animation: head-in .6s var(--ease-back) both;
}
@keyframes head-in { from { opacity: 0; transform: translateY(-10px) scale(.96); } }
.carousel-head p {
  margin-top: 10px;
  display: flex; flex-wrap: wrap; gap: 6px; justify-content: center;
}
.sub-chip {
  font-size: 11px; font-weight: 700;
  letter-spacing: .06em;
  padding: 3px 10px;
  border-radius: 999px;
  color: #fff;
  background: color-mix(in srgb, var(--c) 26%, transparent);
  border: 1px solid color-mix(in srgb, var(--c) 55%, transparent);
}
.sub-new { font-size: 11.5px; color: var(--ink-2); align-self: center; margin-left: 4px; }

.carousel-actions {
  display: flex; gap: 10px; flex-wrap: wrap; justify-content: center;
  z-index: 5; padding: 0 16px;
}

/* --- la mosaïque de pochettes dans la fenêtre ---
   Quatre volumes du même genre partageaient un pictogramme et une teinte, et
   ne se distinguaient pas. Les pochettes de ce qu'ils contiennent, si. */
.win-art {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  border-radius: 50%;
  overflow: hidden;
  opacity: .92;
}
.win-art i {
  display: block;
  background-size: cover;
  background-position: center;
  filter: saturate(1.1) contrast(1.05);
}
/* une seule pochette : elle occupe tout le hublot */
.win-art i:only-child { grid-column: 1 / 3; grid-row: 1 / 3; }

.pack-window.has-art .win-disc { opacity: .22; }
.pack-window.has-art .win-mark {
  font-size: 1.7em;
  opacity: .92;
  text-shadow: 0 0 .5em rgba(0,0,0,.95), 0 .1em .3em rgba(0,0,0,.9);
}
.pack-window.has-art::after {
  /* on assombrit le centre pour que la plaque gravée reste lisible */
  content: "";
  position: absolute; inset: 0;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(4,4,10,.62), rgba(4,4,10,.18) 70%, transparent);
  pointer-events: none;
}

/* ================================================================
   7. LE SPECTACLE DE LA RÉVÉLATION

   Des idées de jeu vidéo, toutes en transformation et en opacité :
   l'arrêt sur image avant l'explosion, l'écrasement et l'étirement,
   la caméra qui avance, les bandes de cinéma, le pourtour coloré.
   ================================================================ */

.reveal-slot .card { touch-action: none; }

/* l'arrêt sur image : la carte grossit d'un coup et se fige */
.card.is-hit { scale: 1.12; }
.fx-full .card.is-hit { filter: brightness(1.45); }

/* à chaque palier franchi, la carte encaisse et tangue */
.card.is-lurch { animation: card-lurch .42s var(--ease-back); }
@keyframes card-lurch {
  from { rotate: var(--lurch); scale: .94; }
  to   { rotate: 0deg; scale: 1; }
}

/* une fois révélée, elle flotte */
.card.is-floating { animation: card-float 3.6s ease-in-out infinite; }
@keyframes card-float {
  0%, 100% { translate: 0 0; }
  50%      { translate: 0 -9px; }
}

/* un toucher la fait gigoter */
.card.is-wiggle .card-inner { animation: card-wiggle .55s cubic-bezier(.2,.7,.3,1); }
@keyframes card-wiggle {
  0%   { transform: scale(1); }
  20%  { transform: scale(1.1, .9) rotate(-3deg); }
  40%  { transform: scale(.94, 1.06) rotate(2.5deg); }
  60%  { transform: scale(1.03, .97) rotate(-1deg); }
  100% { transform: scale(1); }
}

/* on la fait tourner du doigt : pendant le glissé, elle suit sans retard,
   et quand elle montre son dos, c'est le vrai dos qu'on voit */
.card.is-spinning .card-inner { transition: none; }
.card.is-backside .card-front { opacity: 0 !important; }
.card.is-backside .card-back  { opacity: 1 !important; }

/* le badge « Nouveau » tombe et rebondit */
.card.is-fresh .card-new { animation: new-drop .8s cubic-bezier(.3,1.6,.5,1) .35s both; }
@keyframes new-drop {
  0%   { opacity: 0; transform: translateY(-70px) scale(1.5) rotate(-12deg); }
  55%  { opacity: 1; transform: translateY(4px) scale(.94) rotate(3deg); }
  75%  { transform: translateY(-6px) scale(1.04) rotate(-2deg); }
  100% { transform: none; }
}

/* la caméra avance pendant la montée d'une légendaire */
.stage.is-dolly .stage-phase {
  transform: scale(1.08);
  transition: transform 1.3s cubic-bezier(.4, 0, .2, 1);
}

/* le pourtour de l'écran, à la couleur du palier */
.stage-vignette {
  position: absolute;
  inset: 0;
  z-index: 35;
  pointer-events: none;
  opacity: 0;
  transition: opacity .35s ease-out;
  background: radial-gradient(ellipse 75% 70% at 50% 50%,
    transparent 40%, color-mix(in srgb, var(--vig, #fff) 55%, transparent) 100%);
}

/* les bandes de cinéma et le titre tapé lettre à lettre */
.cine i {
  position: absolute;
  left: 0; right: 0;
  z-index: 50;
  height: min(9vh, 80px);
  background: #000;
  pointer-events: none;
  transition: transform .55s cubic-bezier(.16, 1, .3, 1);
}
.cine-top { top: 0; transform: translateY(-101%); }
.cine-bot { bottom: 0; transform: translateY(101%); display: grid; place-items: center; }
.cine.is-on i { transform: none; }
.cine-name {
  max-width: 92vw;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-family: var(--font-display, system-ui);
  font-size: clamp(18px, 3.4vw, 30px);
  letter-spacing: .06em;
  color: #fff;
  text-shadow: 0 0 18px var(--aura), 0 2px 0 rgba(0, 0, 0, .9);
}
.cine-name:not(:empty)::after {
  content: "▌";
  margin-left: 2px;
  color: var(--aura);
  animation: caret 1s steps(1) infinite;
}
@keyframes caret { 50% { opacity: 0; } }

/* le tampon se dédouble en rouge et en cyan sur les hauts paliers */
.rarity-stamp.is-chroma {
  text-shadow:
    -3px 0 0 rgba(255, 40, 120, .8),
    3px 0 0 rgba(40, 220, 255, .8),
    0 0 .6em var(--aura);
}

/* la rangée de cases, en haut : de petites cartes qui se remplissent */
.reveal-counter { gap: 9px; }
.pip {
  width: 16px; height: 22px;
  border-radius: 4px;
  border: 2px solid rgba(20, 14, 51, .95);
  background: linear-gradient(180deg, #2b2456, #14122a);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .12);
}
.pip.is-done {
  background: linear-gradient(180deg, color-mix(in srgb, var(--pip) 60%, #fff), var(--pip));
  box-shadow: 0 0 12px -2px var(--pip), inset 0 1px 0 rgba(255,255,255,.5);
}
.pip.is-current {
  width: 20px;
  border-radius: 4px;
  background: linear-gradient(180deg, #fff, #d9ccff);
  box-shadow: 0 0 14px rgba(255, 255, 255, .8);
}
.pip.is-land { animation: pip-land .55s cubic-bezier(.3, 1.8, .5, 1); }
@keyframes pip-land {
  0%   { transform: scale(1.9) rotate(-20deg); }
  100% { transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .card.is-floating, .card.is-lurch, .card.is-fresh .card-new, .pip.is-land { animation: none; }
  .stage.is-dolly .stage-phase { transform: none; }
}

/* Le maintien sur le sachet : le navigateur ne doit rien en faire. Sans
   ça, le moindre glissé du doigt devenait un défilement, le navigateur
   annulait l'appui (pointercancel) et il fallait relever le doigt. */
.charge-wrap, .charge-wrap * {
  touch-action: none;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

/* Les calques de la scène d'ouverture sont permanents. La secousse et le
   « coup de poing » ajoutaient puis retiraient une transformation à
   chaque palier de rareté : le téléphone recréait alors le calque du
   disque, qui disparaissait le temps d'une image avant de revenir. */
.stage, .stage-phase { will-change: transform; }
.reveal-slot .card { will-change: transform, scale, rotate; }
.stage:not(.is-shaking) { transform: translate3d(0, 0, 0); }

/* ------------------------------ le Nyan Cat ------------------------------
   Un passage de quatre secondes, au-dessus de la scène mais sous les
   particules. Tout avance par à-coups (steps) : c'est du pixel art, un
   mouvement fluide le trahirait. */

.nyan-ov {
  position: fixed;
  inset: 0;
  /* la scène est à 9000 : on passe au-dessus (ajouté après elle dans le
     document), sous les particules (#fx-canvas, 9001) */
  z-index: 9000;
  pointer-events: none;
  overflow: hidden;
}
.ny-sky {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 80% at 50% 50%, rgba(18, 52, 132, .5), rgba(6, 14, 52, .72));
  opacity: 0;
  transition: opacity .45s ease-out;
}
.nyan-ov.is-on .ny-sky { opacity: 1; }
.nyan-ov.is-dropped .ny-sky { opacity: 0; transition-duration: 1.4s; }
.nyan-ov.is-out { opacity: 0; transition: opacity .45s; }

.ny-stars { position: absolute; inset: 0; opacity: 0; transition: opacity .5s; }
.nyan-ov.is-on .ny-stars { opacity: 1; }
.nyan-ov.is-dropped .ny-stars { opacity: .35; transition-duration: 1.4s; }
.ny-star {
  position: absolute;
  width: var(--u);
  height: var(--u);
  background: #fff;
  box-shadow:
    0 calc(var(--u) * -2) #fff, 0 calc(var(--u) * 2) #fff,
    calc(var(--u) * -2) 0 #fff, calc(var(--u) * 2) 0 #fff;
  animation:
    ny-star-run var(--d) linear infinite var(--w),
    ny-twinkle .36s steps(1) infinite var(--w);
}
@keyframes ny-star-run { to { translate: -60vw 0; } }
@keyframes ny-twinkle {
  0%   { scale: 1; opacity: 1; }
  50%  { scale: .5; opacity: .6; }
}

.ny-fly {
  position: absolute;
  left: 0;
  will-change: transform;
}
.ny-cat {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 2;
  overflow: visible;
  animation: ny-bob .36s steps(1) infinite;
  filter: drop-shadow(0 calc(var(--u) * 1.5) calc(var(--u) * 2) rgba(0, 0, 0, .45));
}
@keyframes ny-bob { 50% { translate: 0 calc(var(--u) * -1); } }
.ny-legs { animation: ny-legs .18s steps(1) infinite; }
@keyframes ny-legs { 50% { transform: translate(1px, 0); } }
.ny-tail { animation: ny-tail .36s steps(1) infinite; }
@keyframes ny-tail { 50% { transform: translate(0, -2px); } }

.ny-trail {
  position: absolute;
  top: var(--u);
  right: calc(100% - var(--u) * 9);
  height: calc(var(--u) * 17);
  display: flex;
  flex-direction: row-reverse;
  z-index: 1;
}
.ny-trail i {
  flex: 0 0 calc(var(--u) * 8);
  height: 100%;
  background: linear-gradient(to bottom,
    #ff1a1a 0 16.67%, #ff9900 0 33.34%, #ffee00 0 50%,
    #33ff00 0 66.67%, #0099ff 0 83.34%, #6633ff 0);
  animation: ny-wave .36s steps(1) infinite;
}
.ny-trail i:nth-child(even) { animation-delay: -.18s; }
@keyframes ny-wave { 50% { translate: 0 var(--u); } }

/* la carte portée : elle se balance au rythme du chat, auréolée */
.ny-hold {
  position: absolute;
  z-index: 3;
  transform-origin: 50% 0;
  animation: ny-swing .72s ease-in-out infinite alternate;
  filter: drop-shadow(0 0 calc(var(--u) * 2) rgba(255, 255, 255, .7))
          drop-shadow(0 0 calc(var(--u) * 5) rgba(255, 95, 210, .55));
}
@keyframes ny-swing {
  from { rotate: -9deg; }
  to   { rotate: 5deg; }
}
.ny-hold .deck-card {
  left: 50%; top: 50%;
  translate: -50% -50%;
  transform: none;
  transition: none;
  opacity: 1;
}
