/* card.css — card elements.
 *
 * Card faces are prerendered bitmaps (see cardFaceRenderer.js), so a card
 * element is just a sized div with a shared background texture. Everything
 * here is about motion and elevation, not paint.
 */

.card {
  position: absolute;
  left: 0;
  top: 0;
  width: 150px;
  height: 210px;
  cursor: grab;
  touch-action: none;
  -webkit-use-highlight-color: transparent;

  /* Cards animate on their own compositor layer (CardView re-asserts this
     for every animation), but a composited layer keeps the raster scale it
     was promoted at — a card rested at hover 1.14 / battleground 1.0 /
     dock 0.6 would be a GPU-resampled texture with soft text. So CardView
     DEMOTES on settle: once every spring rests it rewrites the transform in
     plain 2D form with an inline `will-change: auto`, forcing a re-raster
     at the true on-screen scale, and re-promotes on wake. The settle is
     debounced (~90ms) so mass reflows never thrash promotion. */
  will-change: transform;
}

/* The painted face lives on an inner element at 2× the card's box, scaled
   down by transform. The card's background used to sit on .card itself at
   150×210, so the GPU rotated/scaled that small raster for the fan tilt and
   the text went soft; rasterizing at 2× and scaling to fit supersamples the
   face, keeping hand/table card text crisp (the same sharpness the hover
   preview gets by rendering at its used size). The element spans the card's
   full box, so the rounded-corner clip and every positioned child (A/H pill,
   tuck edges, badges, rings) keep their .card-relative geometry unchanged. */
.card-face {
  position: absolute;
  left: 0;
  top: 0;
  width: 200%;
  height: 200%;
  background-size: 100% 100%;
  background-repeat: no-repeat;
  /* face rx-54 corners (750×1050 viewBox), matching .card's box at 2× */
  border-radius: 7.2% / 5.143%;
  transform: scale(0.5);
  transform-origin: 0 0;
  pointer-events: none;
}

/* Static clones (piles, preview, opponent backs) opt out of absolute
   positioning and spring-driven transforms. */
.card--static {
  position: relative;
  left: auto;
  top: auto;
  transform: none;
  cursor: default;
  will-change: auto;
}

.card--leaving {
  transition: opacity 0.22s ease 0.04s;
  opacity: 0;
}

/* ---------- shadows: removed (the reported under-card slab) ----------
   The old resting contact shadow painted a translucent band PRECISELY under
   the card's bottom edge — most visible when a hand card rose on hover (the
   whole card plus its shadow scaled up) and, at rest, as a weaker strip under
   every battleground card that read as a 3D edge. The user asked for its
   removal everywhere; cards now sit flat on the felt. The ::before/::after
   pseudos REMAIN as empty boxes: every selection ring (game.css) and the
   damage-order badge (order-1..4) reuse them. */
.card::before,
.card::after {
  content: '';
  position: absolute;
  border-radius: 7.2% / 5.143%;
  pointer-events: none;
  transition: opacity 0.18s ease;
  box-shadow: none;
}
.card::before {
  inset: 4px 1px -3px;
  z-index: -1;
}
.card::after {
  inset: 6px -2px -11px;
  opacity: 0;
  z-index: -1;
}
.card--static::before { content: none; }
/* Note: ::after is left live so damage-assignment order badges still paint. */


/* ---------- foil sheen (rare cards) ----------
   The MAGICAL RAINBOW of a physical foil — the part that makes it read as
   "shiny", not just "bright". Two surfaces work together:
     - the STATIC prismatic laminate (foilBands.js PNG) paints a seeded
       rainbow + diffraction streaks over the WHOLE face (text zone too —
       the retired carve-out) via the face's background stack (below the
       bronze frame + name/text overlay). The resting card wears the DIM
       variant; the FULL variant is the close-up/showcase reveal;
     - THIS layer carries the MOVING rainbow: a saturated spectral band that
       TRAVELS across the card (the foil-tilt tell — the colors themselves
       shift across the surface) and the 10s flare. It rides ONLY zoomed /
       staged surfaces (.card--static close-ups + the showcase .sg-foil): a
       resting card's laminate never moves (the user's call).

   The static laminate supplies the baseline rainbow texture. The DOM layer
   carries the MOTION on its OWN background (never a pseudo — ::before/::
   after inside a masked element are suppressed under Chromium's exclude/xor
   mask compositing; the element's own background paints reliably): a
   hard-edged DIAGONAL RAINBOW stripe translates across the card (the
   foil-tilt tell — the colors themselves move), and a white-hot FLARE beam
   crosses every 10s. Transform/opacity-only keyframes, GPU-composited;
   background-position is a repaint, so the sweep translates a 300%-wide
   gradient instead. NORMAL alpha compositing (never mix-blend-mode — screen
   read as a washed flood over pale cards and vanished under software
   rasterization); opacities are calibrated so the band reads saturated on
   dark themes and a clean rainbow sheen on pale ones. Reduced-motion parks
   the rainbow mid-card. */
/* The moving sheen rides ONLY zoomed/staged surfaces (the user's call — a
   resting card wears the DIM laminate hint, the FULL laminate + the moving
   rainbow is the close-up/showcase reveal): .card--zoomed is the PreviewPane's
   enlarged close-up, .card--static covers the hall review cells (the hall has
   no hover), and the showcase's .sg-foil is the staged twin. The bands'
   opacity keyframes are inherited from the .card-foil rules below. */
.card--zoomed > .card-foil,
.card--static > .card-foil,
.sg-foil,
.menu__pack-foil {
  position: absolute;
  inset: 0;
  border-radius: 7.2% / 5.143%;
  pointer-events: none;
  overflow: hidden;
  z-index: 1;                  /* over the face, under the pill + badge DOM */
}

/* The motion bands are REAL child divs (not pseudos and NOT the element's own
   background): transform-composited translateX, which repaints per frame in
   every raster mode — background-position keyframes rasterize once and cache
   under headless software-rendering, which made the sweep invisible there.
   The children are oversized so translating them across never shows an edge,
   and their own diagonal gradient supplies the rainbow slant. Both the
   close-up `.card-foil` and the showcase `.sg-foil` host them. */
.card-foil > .foil-sweep,
.card-foil > .foil-flare,
.sg-foil > .foil-sweep,
.sg-foil > .foil-flare,
.menu__pack-foil > .foil-sweep,
.menu__pack-foil > .foil-flare {
  position: absolute;
  top: -28%;
  bottom: -28%;
  left: 0;
  pointer-events: none;
  will-change: transform;
}

/* The traveling rainbow: a hard-edged PRISMATIC stripe (the Saprazzan-Cove
   look) — distinct red→orange→gold→green→cyan→violet bands in one diagonal
   stripe. NORMAL alpha (no blend mode), opacities calibrated to read as an
   unmistakable magical rainbow on dark themes and a luminous rainbow SHEEN on
   pale ones (bright enough to read as shine, not a color flood: keeps the
   foil below a repaint while the band is crossing). */
.card-foil > .foil-sweep,
.sg-foil > .foil-sweep,
.menu__pack-foil > .foil-sweep {
  width: 300%;
  background: linear-gradient(
    105deg,
    rgba(255, 0, 150, 0.00) 26%,
    rgba(255, 60, 60, 0.11) 31%,
    rgba(255, 0, 120, 0.15) 33%,
    rgba(255, 60, 80, 0.22) 36%,
    rgba(255, 176, 46, 0.24) 39.5%,
    rgba(255, 240, 120, 0.26) 43%,
    rgba(120, 255, 130, 0.24) 46.5%,
    rgba(60, 210, 255, 0.25) 50%,
    rgba(120, 90, 255, 0.22) 53.5%,
    rgba(255, 60, 230, 0.16) 57%,
    rgba(255, 60, 80, 0.11) 60%,
    rgba(255, 0, 150, 0.00) 66%);
  transform: translateX(-66%) skewX(-10deg);
  animation: foil-sweep-move 9s linear infinite;
}
@keyframes foil-sweep-move {
  0%    { transform: translateX(-66%) skewX(-10deg); }
  100%  { transform: translateX(33.333%) skewX(-10deg); }
}

/* The rest/zoom GATE (the user's call): a resting card (table, hand, dock,
   queue mini) carries the DIM laminate hint only — the moving sheen layer
   exists in its DOM but stays hidden; the FULL laminate + the traveling
   rainbow is the reveal of the enlarged close-up (.card--zoomed), the hall
   review cell (.card--static), and the showcase (.sg-foil). */
.card:not(.card--zoomed):not(.card--static) > .card-foil { display: none; }

/* The 10s flare: a tight white-hot beam crossing once per cycle. */
.card-foil > .foil-flare,
.sg-foil > .foil-flare,
.menu__pack-foil > .foil-flare {
  width: 140%;
  background: linear-gradient(
    100deg,
    transparent 26%,
    rgba(255, 255, 255, 0.55) 43%,
    rgba(255, 252, 235, 0.95) 49%,
    rgba(255, 255, 255, 0.55) 55%,
    transparent 72%);
  opacity: 0;
  transform: translateX(-115%) skewX(-10deg);
  animation: foil-flare-move 10s ease-in-out infinite;
}
@keyframes foil-flare-move {
  0%    { transform: translateX(-115%) skewX(-10deg); opacity: 0; }
  4%    { opacity: 1; }
  9%    { opacity: 1; }
  13%   { transform: translateX(168%) skewX(-10deg); opacity: 0; }
  100%  { transform: translateX(168%) skewX(-10deg); opacity: 0; }
}

/* Desync a table of foils (--foil-phase from the card id). The showcase
   .sg-foil picks the same stagger up from its .spell-showcase host. The
   gate's packs carry --pk-phase per slot so the shelf never pulses in
   unison. */
.card--static.card--foil[data-card-id] > .card-foil > .foil-sweep,
.spell-showcase[data-foil-phase] .sg-foil > .foil-sweep { animation-delay: calc(var(--foil-phase, 0) * -1s); }
.card--static.card--foil[data-card-id] > .card-foil > .foil-flare,
.spell-showcase[data-foil-phase] .sg-foil > .foil-flare { animation-delay: calc(var(--foil-phase, 0) * -1.4s); }
.menu__pack[data-pack] > .menu__pack-foil > .foil-sweep { animation-delay: calc(var(--pk-phase, 0) * -1s); }
.menu__pack[data-pack] > .menu__pack-foil > .foil-flare { animation-delay: calc(var(--pk-phase, 0) * -1.4s); }

/* A one-shot landing flare: only zoom/showcase surfaces carry the moving
   sheen, so the flash rides the showcase's staged card (`.spell-showcase--on
   .sg-foil`) — the battleground card itself stays at the dim hint — and the
   hall's zoomed cell (`card--zoomed.card--foil-flash`) re-arms the same
   one-shot for the effect-review "Replay flare" button. */
.spell-showcase.spell-showcase--on .sg-foil > .foil-flare {
  animation: foil-flare-move 10s ease-in-out infinite, foil-flare-once 1.15s ease-out;
  animation-delay: calc(var(--foil-phase, 0) * -1.4s), 0s;
}
.card--zoomed.card--foil-flash > .card-foil > .foil-flare {
  animation: foil-flare-once 1.15s ease-out;
}
@keyframes foil-flare-once {
  0%    { transform: translateX(-115%) skewX(-10deg); opacity: 0; }
  25%   { opacity: 1; }
  100%  { transform: translateX(168%) skewX(-10deg); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  /* Rainbow band parked mid-card (magic visible, still); flare dark. */
  .card--zoomed > .card-foil > .foil-sweep,
  .card--static > .card-foil > .foil-sweep,
  .spell-showcase .sg-foil > .foil-sweep,
  .menu__pack > .menu__pack-foil > .foil-sweep { animation: none; transform: translateX(-20%) skewX(-10deg); }
  .card--zoomed > .card-foil > .foil-flare,
  .card--static > .card-foil > .foil-flare,
  .spell-showcase .sg-foil > .foil-flare,
  .menu__pack > .menu__pack-foil > .foil-flare { animation: none; opacity: 0; }
}

