/* =====================================================================
   CHESTS IN THE SHOP — the lid actually moves
   ===================================================================== */

@keyframes chestIdle {
  0%, 100% { transform: translateY(0) rotate(0deg) scale(1); }
  25%      { transform: translateY(-3px) rotate(-1.6deg) scale(1.02); }
  50%      { transform: translateY(0) rotate(0deg) scale(1); }
  75%      { transform: translateY(-2px) rotate(1.6deg) scale(1.02); }
}

/* A rare, quick shudder — the chest straining against its lid. Most of
   the cycle is still, so a grid of them does not read as noise. */
@keyframes chestShudder {
  0%, 86%, 100% { transform: translateX(0) rotate(0deg); }
  88%  { transform: translateX(-2px) rotate(-2.5deg); }
  90%  { transform: translateX(2px)  rotate(2.5deg); }
  92%  { transform: translateX(-2px) rotate(-2deg); }
  94%  { transform: translateX(1px)  rotate(1.2deg); }
}

.shop-card-icon.is-chest img {
  animation: chestIdle var(--chest-bob, 3.4s) ease-in-out infinite,
             chestShudder var(--chest-shudder, 7s) ease-in-out infinite;
  transform-origin: 50% 85%;
  will-change: transform;
}

/* The two premium chests get a glow that breathes with the bob. */
.shop-card[data-chest="mythic"] .shop-card-icon img,
.shop-card[data-chest="jk1x"] .shop-card-icon img {
  animation: chestIdle 2.8s ease-in-out infinite,
             chestShudder 5.5s ease-in-out infinite,
             chestGlow 2.8s ease-in-out infinite;
}

@keyframes chestGlow {
  0%, 100% { filter: drop-shadow(0 0 6px rgba(255, 184, 0, 0.45)); }
  50%      { filter: drop-shadow(0 0 18px rgba(255, 217, 77, 0.95)); }
}

@media (prefers-reduced-motion: reduce) {
  .shop-card-icon.is-chest img,
  .shop-card[data-chest="mythic"] .shop-card-icon img,
  .shop-card[data-chest="jk1x"] .shop-card-icon img { animation: none; }
}

/* =====================================================================
   THE MASCOT — out of the machine's way
   ---------------------------------------------------------------------
   She stood in the deck with her bubble anchored to the deck's left
   edge, which pointed the tail at the bet controls and put the bubble
   over the bottom of the reels. Both now live in the left rail's own
   bottom corner: she is beside the cabinet rather than under it, and
   the bubble opens sideways into the rail's empty column.
   ===================================================================== */

.rail-left { position: relative; }


/* =====================================================================
   THE CURTAIN — heavier, and it now has a stage behind it
   ===================================================================== */

/* Velvet rather than flat colour: vertical folds, a sheen that catches
   the light, and a gold rope at the leading edge. */
/* Velvet, not flat colour: narrower folds with a genuine sheen, and a
   heavier travel curve so the panels have weight. */
.curtain {
  background:
    linear-gradient(90deg, rgba(0,0,0,0.5) 0%, transparent 7%, transparent 93%, rgba(0,0,0,0.5) 100%),
    repeating-linear-gradient(90deg,
      #4d060d 0px, #8c1220 15px, #c02234 27px, #8c1220 40px, #4d060d 55px),
    linear-gradient(180deg, #7d0f1c, #2e0407);
  box-shadow: inset 0 0 90px rgba(0, 0, 0, 0.8);
  transition: transform 820ms cubic-bezier(0.76, 0, 0.24, 1);
}

/* A gold rope down the leading edge, so the panels read as hung cloth
   with a weighted hem rather than two sliding rectangles. */
.curtain-left::before,
.curtain-right::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 12px;
  z-index: 2;
  background: linear-gradient(180deg, #ffe9a3, #ffb800 40%, #a86e00);
  box-shadow: 0 0 24px rgba(255, 184, 0, 0.8);
}

.curtain-left::before  { right: 0; }
.curtain-right::before { left: 0; }

/* Sparks running down the leading edge as the curtains travel. */
.curtain-spark {
  position: absolute;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: #fff3c4;
  box-shadow: 0 0 10px 3px rgba(255, 217, 77, 0.9);
  pointer-events: none;
  animation: curtainSpark var(--spark-dur, 1.4s) ease-out infinite;
  animation-delay: var(--spark-delay, 0s);
}

@keyframes curtainSpark {
  0%   { opacity: 0; transform: translateY(-14px) scale(0.5); }
  25%  { opacity: 1; transform: translateY(0) scale(1); }
  100% { opacity: 0; transform: translateY(52px) scale(0.4); }
}

/* A shaft of light that sweeps the gap while the curtains are closed,
   so the moment between scenes is not a flat wall of red. */
.curtain-beam {
  position: fixed;
  inset: 0;
  z-index: 1996;
  pointer-events: none;
  background: radial-gradient(ellipse 40% 120% at 50% -10%,
    rgba(255, 217, 77, 0.35), transparent 70%);
  opacity: 0;
  transition: opacity 320ms ease;
}

.curtain-beam.lit { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .curtain-spark, .curtain-beam { display: none; }
}


/* =====================================================================
   THE CURTAIN IN 3D — the last word on transform and transition
   ---------------------------------------------------------------------
   This block is HERE, at the end of the last sheet that touches the
   curtain, because that is the only place it can work.

   Three files dress these panels — showcase-curtains, then
   marquee-shop-curtains, then this one — and each overrode the last on
   exactly the properties below. Written anywhere earlier, this has no
   effect at all: marquee-shop-curtains sets .curtain-left's transform,
   and the .curtain rule above sets the transition. Both silently win.

   Everything animated here is transform or opacity only. Neither touches
   layout or paint, so each panel is rasterised once and then handed to
   the compositor — which is what holds 60fps rather than merely aiming
   at it. The depth is real 3D, not a drawn-on gradient: .curtain carries
   transform-style: preserve-3d (set in showcase-curtains), so the rope
   and the edge shading below sit at genuine depths and slide against the
   fabric as the panel turns.
   ===================================================================== */

.curtain {
  /* Reads the duration from .curtains rather than restating it. That
     custom property is also what curtain.js reads, so the stylesheet and
     the script cannot disagree about how long a sweep takes — they had
     been out by 180ms in each direction. */
  transition: transform var(--curtain-sweep) var(--curtain-ease),
              filter    var(--curtain-sweep) ease;
}

/* Hinged on the OUTER edge, the way a real curtain turns on its track.
   marquee-shop-curtains sets `transform-origin: top center` on .curtain,
   which would swing them from the pelmet instead — overridden here. */
.curtain-left  { transform-origin: 0%   50%; }
.curtain-right { transform-origin: 100% 50%; }

/* OPEN: drawn to the side, gathered, and turned away from the viewer.
   scaleX squeezes the pleats together as the fabric bunches — that
   compression is what makes the return stroke read as cloth spreading
   rather than a rectangle being stretched. */
.curtain-left {
  transform: translate3d(-101%, 0, 0) rotateY(-32deg) scaleX(0.66);
}
.curtain-right {
  transform: translate3d(101%, 0, 0) rotateY(32deg) scaleX(0.66);
}

/* SHUT: square to the viewer, pleats at full width. */
.curtains.closed .curtain-left,
.curtains.closed .curtain-right {
  transform: translate3d(0, 0, 0) rotateY(0deg) scaleX(1);
}

/* The gold rope stands proud of the fabric it is sewn to. This single
   line is most of the effect: as the panel turns, the rope tracks across
   the velvet behind it, and that parallax is what the eye reads as
   thickness. It costs nothing — a static offset on a layer that was
   already being composited. */
.curtain-left::before,
.curtain-right::before { transform: translateZ(26px); }

/* Edge shading sits between the rope and the cloth. */
.curtain-left::after,
.curtain-right::after { transform: translateZ(13px); }

/* The label belongs in front of the closed curtain, not on it.
   marquee-shop-curtains gives it a translateY + scale; this keeps that
   movement and adds the depth, so it arrives out of the screen rather
   than sliding up the surface. */
.curtain-label { transform: translate3d(0, 14px, 130px) scale(0.94); }
.curtains.closed .curtain-label { transform: translate3d(0, 0, 130px) scale(1); }

/* Reduced motion keeps the transition — an instant cut between scenes is
   more disorienting than a short sweep — but drops the depth entirely.
   No swing, no gather, no perspective: a quick flat wipe. */
@media (prefers-reduced-motion: reduce) {
  .curtains { perspective: none; --curtain-sweep: 200ms; }
  .curtain-left  { transform: translate3d(-101%, 0, 0); }
  .curtain-right { transform: translate3d(101%, 0, 0); }
  .curtains.closed .curtain-left,
  .curtains.closed .curtain-right { transform: translate3d(0, 0, 0); }
  .curtain-left::before, .curtain-right::before,
  .curtain-left::after,  .curtain-right::after { transform: none; }
  .curtain-label,
  .curtains.closed .curtain-label { transform: none; transition-delay: 0s; }
}

/* The valance and the label kept fixed timings from
   marquee-shop-curtains — 620ms for the drop, and a 380ms delay before
   the label appears. Both are right at the default 820ms sweep and both
   break at any other: under reduced motion the sweep is 200ms, so a
   label held back 380ms arrives after the curtains have finished
   opening again. Expressed as fractions of the sweep, they hold at any
   speed. */
.curtain-valance {
  transition: transform calc(var(--curtain-sweep) * 0.76) cubic-bezier(0.34, 1.4, 0.5, 1);
}
.curtain-label {
  transition:
    opacity   calc(var(--curtain-sweep) * 0.37) ease                                   calc(var(--curtain-sweep) * 0.46),
    transform calc(var(--curtain-sweep) * 0.61) cubic-bezier(0.2, 1.3, 0.4, 1)         calc(var(--curtain-sweep) * 0.46);
}

/* =====================================================================
   THE GATHER: VERTICAL FOLDS, AND A WAVE THAT TRAVELS
   ---------------------------------------------------------------------
   The panels swung and squashed, which reads as a rectangle being
   scaled. A curtain does not scale — it GATHERS: the cloth bunches into
   vertical folds that compress toward the track, overlap each other, and
   fall back away from the viewer as they do.

   So the panel now only does the travel and the swing. The gather lives
   in nine fold strips per panel, each carrying three things:

     --u       0 at the hinge, 1 at the leading edge
     --fold-z  its depth, a sine along --u, computed in curtain.js
               because CSS has no sin()
     --fold-dir  which way the gather runs, since the two panels hinge
                 on opposite edges

   The wave TRAVELS because each fold's transition is delayed by its own
   --u. The hinge end moves first and the crest runs out to the leading
   edge, which is the whole difference between cloth and a sheet of card.

   Fold duration plus the longest delay stays UNDER --curtain-sweep on
   purpose: curtain.js waits on the panel's own transform, so anything
   finishing after the panel would be cut off mid-move.
   ===================================================================== */
.curtain-fold {
  position: absolute;
  top: 0;
  bottom: 0;
  pointer-events: none;
  transform-origin: 50% 50%;
  will-change: transform;

  /* The light on one fold of velvet: bright where it turns toward the
     room, black in the crease. This is what makes nine strips read as
     one continuous gathered cloth rather than nine bars. */
  background: linear-gradient(90deg,
    rgba(0, 0, 0, 0.46) 0%,
    rgba(0, 0, 0, 0.10) 22%,
    rgba(255, 214, 170, 0.13) 48%,
    rgba(0, 0, 0, 0.12) 74%,
    rgba(0, 0, 0, 0.50) 100%);

  transition:
    transform calc(var(--curtain-sweep) * 0.74) var(--curtain-ease),
    opacity   calc(var(--curtain-sweep) * 0.5)  ease;
  transition-delay: calc(var(--u) * var(--curtain-sweep) * 0.22);
}

/* OPEN — gathered. Each fold has pulled toward the hinge, turned edge-on
   and fallen back on the wave. The compression is per fold rather than
   per panel, so neighbouring folds slide over one another the way real
   pleats do. */
.curtain-fold {
  transform:
    translate3d(calc(var(--fold-dir) * var(--u) * -46%), 0, var(--fold-z))
    rotateY(calc(var(--fold-dir) * var(--u) * -38deg))
    scaleX(0.52);
  opacity: 0.92;
}

/* SHUT — hung flat, no depth, no gather. */
.curtains.closed .curtain-fold {
  transform: translate3d(0, 0, 0) rotateY(0deg) scaleX(1);
  opacity: 1;
}

/* THE PANEL NO LONGER SQUASHES. scaleX(0.66) here was standing in for
   the gather; the folds do it properly now, and leaving both in place
   compressed the cloth twice. The panel keeps the travel and the swing —
   a longer swing than before, because at 1.5s there is time for it to
   read as a turn rather than a flick. */
.curtain-left {
  transform: translate3d(-101%, 0, -60px) rotateY(-38deg);
}
.curtain-right {
  transform: translate3d(101%, 0, -60px) rotateY(38deg);
}
.curtains.closed .curtain-left,
.curtains.closed .curtain-right {
  transform: translate3d(0, 0, 0) rotateY(0deg) scaleX(1);
}

@media (prefers-reduced-motion: reduce) {
  /* No gather, no wave — the flat wipe the block above already chose. */
  .curtain-fold,
  .curtains.closed .curtain-fold {
    transform: none;
    transition: none;
    transition-delay: 0s;
    opacity: 1;
  }
}
/* Nine extra composited layers per panel is the one thing here worth
   dropping on a machine already struggling. */
.lean-fx .curtain-fold { display: none; }
