/* ==========================================================================
   DAGLINGS — booster pack
   Metallized film, not matte foil. Reference: docs/pack-reference.png.

   Everything is sized in cqw against the pack itself, so one component serves
   a shop product image, a big centre-stage pack and a small sealed pack in an
   inventory row.

   The lighting model, in the order the layers stack:

     base      the metallic falloff, with HARD specular crests down the
               pillowed edges — deep shadow to near-white in ~3% of the width
     tent      the card block pushing the film outward through the middle
     creases   vertical micro-creases in the foil
     crest     the moving half of the specular, shifted by --gx on tilt
     plate     three clipped rings: lit outer slope, dark inner slope,
               recessed face — an embossed frame pressed INTO the film
     print     art and wordmark, inside the plate
     gloss     the pointer-tracked hotspot, over the print because the print
               is under the film. The ONLY moving light left — see below.
     crimps    corrugated ribs, a fold shadow and a specular run across them
   ========================================================================== */

/* Registered so the specular set interpolates instead of snapping. */
@property --gx { syntax: "<number>"; inherits: true; initial-value: 0; }
/* Resting light position: the upper-left shoulder, not the middle. Must match
   REST_LX / REST_LY in pack.js — these are what a pack with no pointer on it
   (and a pack with tilt disabled entirely) uses. */
@property --lx { syntax: "<number>"; inherits: true; initial-value: 0.2; }
@property --ly { syntax: "<number>"; inherits: true; initial-value: 0.1; }

.pack {
  container-type: inline-size;
  position: relative;
  aspect-ratio: 2 / 3;
  width: 100%;
  font-family: "IBM Plex Sans", system-ui, sans-serif;
  perspective: 900px;
  user-select: none;
  --tilt-x: 0deg;
  --tilt-y: 0deg;
  /* Slow settle back to rest, quick follow while the pointer is on it: the
     highlight should drift off the bay rather than snap off it. */
  transition: --gx 0.5s ease-out, --lx 0.5s ease-out, --ly 0.5s ease-out;
}
.pack.is-tilting {
  transition: --gx 0.16s ease-out, --lx 0.16s ease-out, --ly 0.16s ease-out;
}

.pack__body {
  position: absolute;
  inset: 0;
  /* clip-path (the pillowed silhouette) is set in pack.js */

  /* The base film. The horizontal ramp is the whole 3D read: a pouch is a
     cylinder around its contents, so it runs near-black at the outermost
     edge, spikes to a near-white specular crest a few percent in where the
     surface turns through the light, then falls away to a mid teal across
     the crown. Those crest stops are deliberately tight — a gloss highlight
     lobe is narrow, and widening them is what made earlier passes read as
     matte. */
  background:
    linear-gradient(
      180deg,
      rgba(0, 0, 0, 0.62) 0%,
      rgba(0, 0, 0, 0.12) 13%,
      rgba(0, 0, 0, 0) 26%,
      rgba(0, 0, 0, 0) 70%,
      rgba(0, 0, 0, 0.18) 86%,
      rgba(0, 0, 0, 0.6) 100%
    ),
    linear-gradient(
      90deg,
      #01070b 0%,
      #05202b 1.8%,
      #3cb8ae 3.6%,
      #dcfff8 5.4%,
      #55bdb6 7.2%,
      #10414f 10.5%,
      #072b38 17%,
      #06222e 30%,
      #0a3243 46%,
      #0c3c4d 52%,
      #082936 62%,
      #072b38 70%,
      #10414f 83%,
      #55bdb6 92.8%,
      #dcfff8 94.6%,
      #3cb8ae 96.4%,
      #05202b 98.2%,
      #01070b 100%
    );

  /* Base tip so the seals fold away from the viewer instead of lying in the
     page plane — the top and bottom are the two places a flat pack gives
     itself away. */
  transform: rotateX(calc(5deg + var(--tilt-x))) rotateY(var(--tilt-y));
  transform-origin: 50% 50%;
  transition: transform 0.16s ease-out;
  animation: pack-breathe 7.5s ease-in-out infinite;
  will-change: transform;

  /* An object with thickness: a tight contact shadow plus a wide, spread
     one. drop-shadow, not box-shadow — the silhouette is clipped and
     box-shadow would be drawn on the rectangle and cut away with the teeth. */
  filter:
    drop-shadow(0 0.5cqw 0.7cqw rgba(0, 0, 0, 0.72))
    drop-shadow(0 3.5cqw 5cqw rgba(0, 0, 0, 0.6))
    drop-shadow(1.6cqw 2cqw 4.5cqw rgba(0, 0, 0, 0.34));
}

/* While the pointer is driving it, the idle breathe would fight the tilt. */
.pack.is-tilting .pack__body { animation: none; }

@keyframes pack-breathe {
  0%   { transform: rotateX(5.9deg) rotateY(-1.5deg); }
  50%  { transform: rotateX(4.1deg) rotateY(1.5deg); }
  100% { transform: rotateX(5.9deg) rotateY(-1.5deg); }
}

/* Declared HERE, before the layers that override it. Putting the overlay's
   copy of this rule further down would outrank .pack__art and .pack__print at
   equal specificity and snap them both back to inset: 0. */
.pack__body > *,
.pack__overlay > * { position: absolute; inset: 0; pointer-events: none; }

/* --- the card block inside -----------------------------------------------
   The film is tented outward by the stack. A soft ridge highlight down each
   long edge of the block, and a shadow just outside it where the film drops
   back toward the seam. */
.pack__tent {
  background:
    linear-gradient(90deg,
      transparent 0 7.5%,
      rgba(0, 0, 0, 0.5) 10%,
      rgba(178, 240, 230, 0.16) 12.2%,
      rgba(255, 255, 255, 0.05) 50%,
      rgba(178, 240, 230, 0.16) 87.8%,
      rgba(0, 0, 0, 0.5) 90%,
      transparent 92.5% 100%),
    linear-gradient(180deg,
      transparent 0 10%,
      rgba(255, 255, 255, 0.05) 14%,
      rgba(255, 255, 255, 0.05) 86%,
      transparent 90% 100%);
}

/* --- vertical micro-creases ----------------------------------------------
   Film does not sit perfectly smooth. Each crease is a dark line with a
   bright one beside it, because a crease is a shadow and a highlight. */
.pack__creases {
  background:
    linear-gradient(90deg, transparent 20.6%, rgba(0,0,0,.34) 21.3%, rgba(214,255,246,.2) 22.1%, transparent 22.8%),
    linear-gradient(90deg, transparent 43.2%, rgba(0,0,0,.26) 43.8%, rgba(214,255,246,.15) 44.5%, transparent 45.1%),
    linear-gradient(90deg, transparent 63.9%, rgba(0,0,0,.3) 64.6%, rgba(214,255,246,.17) 65.3%, transparent 66%),
    linear-gradient(90deg, transparent 78.4%, rgba(0,0,0,.24) 79%, rgba(214,255,246,.13) 79.7%, transparent 80.3%);
  opacity: 0.85;
}

/* --- the moving specular crests ------------------------------------------
   The static crests live in the base gradient; these ride on top and slide
   with --gx, so tilting the pack walks the highlights across the pillow the
   way a real one does. Stop positions are computed from a unitless custom
   property, which is why --gx is registered above. */
.pack__crest {
  background: linear-gradient(
    90deg,
    transparent calc((3.2 + var(--gx)) * 1%),
    rgba(244, 255, 253, 0.92) calc((5.4 + var(--gx)) * 1%),
    rgba(140, 236, 222, 0.2) calc((7.6 + var(--gx)) * 1%),
    transparent calc((10.4 + var(--gx)) * 1%),
    transparent calc((89.6 + var(--gx)) * 1%),
    rgba(140, 236, 222, 0.18) calc((92.4 + var(--gx)) * 1%),
    rgba(244, 255, 253, 0.85) calc((94.6 + var(--gx)) * 1%),
    transparent calc((96.8 + var(--gx)) * 1%)
  );
  mix-blend-mode: screen;
}

/* The drifting foil sheen (135deg bands, color-dodge) and the travelling
   glare both lived here. Both are GONE, deliberately.
   They existed to help the hand-authored CSS body read as film. The baked
   wrapper has its own highlights, and three moving lights on one object is
   noise on top of lighting that already works. What is left is the
   pointer-tracked hotspot below — the one thing a still render cannot do —
   plus the idle breathe. */

/* --- the embossed frame plate ---------------------------------------------
   A ridge is a lit slope next to a dark one. Lit from the top-left, so the
   OUTER slope of the ridge is bright along its top and left runs, and the
   INNER slope — facing the other way — is dark there and bright along the
   bottom and right. Getting that pair the wrong way round is what makes an
   emboss look like a printed outline. */
.pack__plate-rim {
  background: linear-gradient(
    152deg,
    #f0fffc 0%, #9fecdd 7%, #2f8f92 20%, #0a3d4c 40%, #051f28 64%, #03141b 100%
  );
}
.pack__plate-groove {
  background: linear-gradient(
    152deg,
    #01090e 0%, #05202b 22%, #0a3644 48%, #2b8f92 78%, #8ce0d4 93%, #d0fff5 100%
  );
}
/* The recessed face: darkest where the ridge shades it at the top-left, with
   the pouch's own glow welling up from below. */
.pack__plate-face {
  background:
    linear-gradient(158deg, rgba(0, 0, 0, 0.72) 0%, rgba(0, 0, 0, 0.2) 12%, transparent 30%),
    radial-gradient(78% 58% at 50% 104%, #3fe6cd 0%, #10808f 24%, #07405a 50%, #04222f 74%, #030d15 100%);
}

/* --- the featured creature ------------------------------------------------
   Masked to fade out at every edge so it emerges from the film instead of
   sitting on it as a rectangle. Generic: any portrait slots in. */
/* Positioned off the BAKED RENDER, not off the CSS plate: the bay's inner
   face sits at x 23.14-76.86% and y 18.42-74.54% of the pack (run
   `node scripts/cut-pack.mjs --measure` to re-derive). The CSS build's plate
   was moved to match, so one set of positions serves both bodies.

   Everything inside the bay keeps a margin wider than the bay's corner
   chamfer, so nothing runs into a cut corner. */
.pack__art {
  inset: 21% 27.5% auto;
  height: 26%;
  overflow: hidden;
}
.pack__art-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 32%;
  display: block;
  /* Two masks intersecting: an oval falloff plus a hard fade at top and
     bottom, so the art never shows a straight cut anywhere. */
  -webkit-mask-image:
    radial-gradient(70% 64% at 50% 46%, #000 36%, transparent 84%),
    linear-gradient(180deg, transparent 0, #000 12%, #000 76%, transparent 99%);
  mask-image:
    radial-gradient(70% 64% at 50% 46%, #000 36%, transparent 84%),
    linear-gradient(180deg, transparent 0, #000 12%, #000 76%, transparent 99%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}
/* Teal rim-light where the creature meets the film, so it pops off the foil. */
.pack__art-rim {
  background: radial-gradient(
    62% 56% at 50% 46%,
    rgba(73, 224, 192, 0.2) 0%,
    rgba(73, 224, 192, 0.06) 46%,
    transparent 72%
  );
  mix-blend-mode: screen;
}

/* --- printed matter ------------------------------------------------------ */

/* Wordmark and brand mark, in the lower half of the bay. */
.pack__print {
  inset: 50% 27.5% 28%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  text-align: center;
}

.pack__word {
  margin: 0;
  font-family: "Chakra Petch", system-ui, sans-serif;
  font-weight: 700;
  /* Sized to the bay, not to the pack: the bay is 53.7% of the pack's width
     and the wordmark sits inside a further margin, so ~45cqw is all there is.
     Type set to the pack's width overflows the emboss on both sides. */
  font-size: 7cqw;
  letter-spacing: 0.16em;
  text-indent: 0.16em; /* balance the tracking on the last letter */
  text-transform: uppercase;
  color: #f2fffc;
  /* Foil-stamped, so it has its own relief rather than lying flat. */
  text-shadow:
    0 0.14cqw 0 rgba(255, 255, 255, 0.55),
    0 -0.14cqw 0 rgba(0, 0, 0, 0.5),
    0 0.9cqw 1.4cqw rgba(0, 0, 0, 0.55);
}

/* The emblem is a brand mark now the art carries the front. It stays large
   only on the card back. */
.pack__emblem {
  width: 10cqw;
  height: 10cqw;
  color: var(--kaspa-teal, #49e0c0);
  opacity: 0.9;
  filter: drop-shadow(0 0.2cqw 0 rgba(0, 0, 0, 0.55));
}

/* The card count sits BELOW the bay, on the wrapper's black lower zone —
   where a real pack prints it, and where this render leaves room for it. Not
   part of the print stack inside the bay. */
.pack__meta {
  margin: 0;
  inset: 79% 18% auto;
  height: auto;
  text-align: center;
  font-size: 2.8cqw;
  letter-spacing: 0.24em;
  text-indent: 0.24em;
  text-transform: uppercase;
  color: rgba(226, 255, 250, 0.66);
  text-shadow: 0 0.12cqw 0.3cqw rgba(0, 0, 0, 0.8);
}

/* --- glazing --------------------------------------------------------------
   The print is under the film, so the hotspot crosses it. */
/* The pointer-tracked hotspot. Its mask (set in pack.js) is full strength on
   the foil and 16% over the bay: gloss film throws a tight bright highlight,
   recessed matte print does not, so the hotspot dims as it crosses the bay
   instead of sitting on the creature. Physically right and it keeps the art
   readable at every hotspot position. */
.pack__gloss {
  background: radial-gradient(
    42% 32% at calc(var(--lx) * 100%) calc(var(--ly) * 100%),
    rgba(255, 255, 255, 0.5) 0%,
    rgba(198, 255, 246, 0.16) 36%,
    transparent 72%
  );
  mix-blend-mode: screen;
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

/* --- the crimped seals ----------------------------------------------------
   The serration is in the silhouette; this is the pressed band inboard of it.
   Each pleat is its own element with its own crest position and brightness,
   so the seal has individual teeth catching light rather than one gradient
   smeared across the whole band. */

.pack__crimp {
  /* Overshoots the seal at both ends: rotateX foreshortens the band, and
     without the overshoot a sliver of bare body shows through the teeth. The
     silhouette clips the excess away. */
  inset: -0.8% 0 auto;
  height: 9.2%;
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.18) 46%, rgba(0, 0, 0, 0.55));
}
.pack__crimp--top {
  transform-origin: center bottom;
  transform: perspective(320px) rotateX(13deg);
}
/* The bottom seal is the top one mirrored, but scaleY(-1) is applied to the
   CONTENTS, not the band. Mirroring the band itself moves it: the flip is
   about the transform-origin, and any origin that keeps the fold pivot
   correct also walks the band a full height off the bottom of the pack. */
.pack__crimp--bottom {
  inset: auto 0 -0.8%;
  transform-origin: center top;
  transform: perspective(320px) rotateX(-13deg);
}
.pack__crimp--bottom .pack__rib,
.pack__crimp--bottom .pack__crimp-ridges { transform: scaleY(-1); }
.pack__crimp--bottom .pack__fold {
  top: -18%;
  bottom: auto;
  transform: scaleY(-1);
}

/* ONE RIB of the corrugation.

   The gradient is ASYMMETRIC on purpose. Light comes from the upper left, so
   each rib climbs through a lit face to a hot crest and then drops almost to
   black on the face turned away — and the next rib starts climbing again
   right beside that shadow. A symmetric rib (dark, bright, dark) is what the
   seal had before, and however many of them you tile, it reads as a repeating
   texture stamp rather than as corrugation catching one light. */
.pack__rib {
  position: absolute;
  top: 0;
  bottom: 0;
  background: linear-gradient(
    90deg,
    #07222c 0%,
    #1d6d76 20%,
    #6fcfc6 38%,
    rgba(236, 254, 251, var(--hot)) var(--crest),
    #145a68 calc(var(--crest) + 5%),
    #010a0f 72%,
    #051c25 100%
  );
}

/* A specular sweep ACROSS the ribs, tracking the pointer. Each rib already
   has its own hot crest, so screening this over the band flares individual
   teeth rather than brightening the whole seal at once. */
.pack__crimp-run {
  position: absolute;
  inset: 0;
  background: radial-gradient(
    50% 150% at calc(var(--lx) * 100%) 40%,
    rgba(255, 255, 255, 0.6) 0%,
    rgba(214, 255, 250, 0.2) 36%,
    transparent 74%
  );
  mix-blend-mode: screen;
}

/* The crimp bar presses horizontal compression lines across the ribs. */
.pack__crimp-ridges {
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.3) 0 2.2%,
    rgba(0, 0, 0, 0.46) 2.2% 5.6%,
    rgba(255, 255, 255, 0.06) 5.6% 10.5%
  );
  mix-blend-mode: overlay;
  opacity: 0.7;
}

/* THE CREASE. Where the seal meets the pillow, the film folds through nearly
   180 degrees, so this is the darkest line anywhere on the pack — a hard
   near-black rule with a bright specular lip above it where the fold catches
   light, falling off into the body below. Without it the seal reads as a band
   stuck onto the wrapper rather than the same sheet folded over. */
.pack__fold {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -18%;
  height: 26%;
  background: linear-gradient(
    180deg,
    rgba(238, 255, 252, 0.82) 0%,
    rgba(238, 255, 252, 0.82) 6%,
    rgba(0, 0, 0, 0.97) 9%,
    rgba(0, 0, 0, 0.97) 13.5%,
    rgba(0, 0, 0, 0.42) 36%,
    transparent 100%
  );
}

/* --- the tear guide -------------------------------------------------------
   Printed ink on the wrapper, not UI: a fine dashed rule with a small
   scissors on its left end, the way every snack and booster wrapper carries
   one. Flat grey at low opacity with a dark line under it for relief — no
   glow, nothing teal.

   STAGE 2 RIPS HERE. This element's position IS the tear line, which is why
   it is a real element and why the baked wrapper must not print one. */
.pack__tear {
  position: absolute;
  left: 11%;
  right: 11%;
  bottom: auto;
  height: 4.2cqw;
  display: flex;
  align-items: center;
  gap: 1.4cqw;
  color: rgba(206, 232, 228, 0.5);
}
.pack__scissors {
  flex: none;
  width: 3.6cqw;
  height: 3.6cqw;
  /* Printed marks sit IN the film, so this gets the same relief as the
     wordmark: a light edge below, a dark one above. */
  filter:
    drop-shadow(0 0.12cqw 0 rgba(255, 255, 255, 0.28))
    drop-shadow(0 -0.12cqw 0 rgba(0, 0, 0, 0.55));
}
.pack__tear-line {
  flex: 1;
  height: 0.62cqw;
  background:
    repeating-linear-gradient(90deg,
      currentColor 0 1.5cqw, transparent 1.5cqw 3cqw) 0 0 / 100% 0.3cqw no-repeat,
    repeating-linear-gradient(90deg,
      rgba(0, 0, 0, 0.5) 0 1.5cqw, transparent 1.5cqw 3cqw) 0 100% / 100% 0.3cqw no-repeat;
}

/* --- the baked wrapper ----------------------------------------------------
   When the rendered body loads, the CSS foil is not built at all — the image
   carries the silhouette in its alpha (which is what the drop-shadow reads),
   the seals, the emboss and the metal. Everything that moves still runs over
   the top. */
.pack__body--baked { background: none; }
.pack__image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: fill;
  display: block;
}

/* Everything printed and glazed, bounded by the wrapper's own alpha. Without
   this the gloss hotspot is a rectangle hanging
   off the sides of the pack — there is no clip-path on a baked body, because
   the image carries the silhouette. mask-image URL is set in pack.js from
   PACK_IMAGE, so the path lives in one place. */
.pack__overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  mask-mode: alpha;
}

@media (prefers-reduced-motion: reduce) {
  .pack__body { animation: none; }
  .pack__body { transform: rotateX(5deg); transition: none; }
}
