/* ==========================================================================
   DAGLINGS — card component
   Built to docs/DAGLINGS_CARD_SPEC.md (v1 — LOCKED).
   The foil recipe in §3 is transcribed literally: the 135deg band angle, the
   P=1180px / P*sqrt(2)=1668.77px geometry and the fully-off-card glare
   endpoints each encode a fixed bug. Do not "tidy" those numbers.
   ========================================================================== */

:root {
  /* ==========================================================================
     CARD THICKNESS — ONE DIAL, declared at the root so a dev control (or a
     page) can override it live and every card follows.

     Both are in cqw, so thickness scales with the card: 1cqw = 7.5px at the
     750px reference. --card-thickness drives the 3D box (the front/back
     translateZ split AND all four edge planes) so they cannot drift apart;
     --card-edge-reveal is only how much of it the drawn slab shows at rest.

     THESE ARE DELIBERATELY EXAGGERATED right now — 14px at reference, which
     is card stock about four times too thick — so the system is visible while
     it is being tuned. Dial down once the value looks right.
     ========================================================================== */
  --card-thickness: 1.8667cqw;   /* 14px at the 750px reference */
  --card-edge-reveal: 0.9cqw;    /* ~7px of stock showing under the face */

  --void: #0a0e16;
  --kaspa-teal: #49e0c0;

  /* Type colors (CLAUDE.md) */
  --t-heat: #ff6a3d;
  --t-root: #6fe06a;
  --t-ground: #d9a441;
  --t-surge: #ffd23f;
  --t-coolant: #3fd9ff;
}

/* --------------------------------------------------------------------------
   Card shell — 5:7, full-art, all UI floating over the art as chips (§1)
   -------------------------------------------------------------------------- */

/* CARD CORNERS ARE ROUNDED — 26px radius at the 750x1050 reference, which is
   3.4667cqw so it scales with the card.

   Cards are die-cut physical objects and are rounded like every TCG card ever
   printed. The site's chamfer — buttons, panels, pills, the pack's bay — is
   the INTERFACE's identity and stays exactly as it is. Cards are objects;
   interface is interface.

   The 3D slab forced this as much as taste did: a chamfered corner has no
   edge wall, so side-on you could see straight through the card at all four
   cuts. A radius closes because the ring geometry closes — an inset ring of a
   rounded rect is just the same rect with the radius reduced by the inset,
   where the chamfer needed every inset ring to shrink the cut by 3.41x its
   own width and still left the corners open.

   NOTE: .dc-card is the container, and container query units resolve against
   an ANCESTOR container, never the element's own box — so cqw written here
   would silently resolve against the viewport. Every cqw length therefore
   lives on the child layers below, which do have .dc-card as their container. */
/* Self-contained: the component must not depend on a page-level reset, or its
   stated heights stop including padding and every row grows. */
.dc-card,
.dc-card *,
.dc-card *::before,
.dc-card *::after {
  box-sizing: border-box;
}

/* Inherited text properties are reset at the card root, so a page-level rule
   like `body { text-transform: uppercase }` cannot reach in and restyle the
   card's copy. Each element inside re-declares whatever it actually wants.
   Found the hard way: a stray `text-transform` on a host page rendered every
   name and flavor line in caps, and the earlier `.finish h3` leak cost 42px
   of overflow. Page CSS must not be able to reach inside the component. */
.dc-card {
  text-transform: none;
  font-style: normal;
  font-variant: normal;
  font-weight: 400;
  letter-spacing: normal;
  word-spacing: normal;
  text-indent: 0;
  text-align: left;
  text-shadow: none;
  white-space: normal;
  line-height: 1.6;
}

.dc-card {
  --radius: 3.4667cqw;  /* 26px at the 750px reference — the die-cut corner */

  /* THE PRINTED BORDER — the single biggest "this is a physical card" signal.
     21px at the 750px reference. Nothing on a real card runs to the cut edge
     except the border itself, so art and panel both sit inside it. */
  --border-w: 2.8cqw;
  /* Stock is lifted well clear of the page void. A card whose border matches
     the background has no silhouette — the Pokemon test is that the border
     announces itself on ANY ground, dark or light. Ours stays dark-blue, but
     it is a distinctly lighter blue than the void it sits on. */
  --stock: #202b3d;
  /* And a true hairline of cut edge all the way round, not just top-left. */
  --hair-w: 0.2cqw;
  --hair: #5b6b82;
  --rule-w: 0.5cqw;     /* the printed accent rule inside the border */

  /* Thickness comes from :root (see the dial at the top of this file) so one
     override moves every card. Stock colours are the card's own. */
  --stock-face: #7f92b2; /* the printed skin at the cut edge */
  --stock-core: #0d131f; /* the dark core between the two skins */
  --stock-lit: #cfe0f7;  /* the specular line along the cut */

  /* THE 3D CHAIN. preserve-3d here is not decoration: .dc-card sits between
     .tilt (preserve-3d) and .dc-card__flip (preserve-3d), and while it was
     flat it broke the chain — the perspective declared on .tilt-scene never
     reached the flip, so the whole showcase spin was rendering ORTHOGRAPHIC.
     An orthographic card at 90deg has no foreshortening and no depth cue at
     all; it just gets thinner and vanishes. */
  transform-style: preserve-3d;

  /* Ink colours. On a PANEL these stay fully opaque — printed stock, nothing
     shows through ink. The overlay chips on a full-art card are the deliberate
     exception: they float on the art and let it through. See .dc-chip. */
  --ink-panel: #10151f;  /* printed panel / chip stock */
  --ink-plate: #0b0f18;  /* darker plate, for inset text boxes */
  --ink-edge: #232c3b;   /* printed hairline between inked areas */

  /* Overlay chips: RGB split out from the alpha so the transparency is one
     dial rather than a literal repeated across six rules. */
  --ink-panel-rgb: 16, 21, 31;
  --ink-plate-rgb: 11, 15, 24;
  --overlay-alpha: 0.58;      /* the bottom zones: pills, text, stats, footer */
  --overlay-alpha-top: 0.65;  /* name chip and LV — smaller targets, so firmer */
  --overlay-blur: 2.6cqw;     /* what buys the legibility the alpha gives up */

  container-type: inline-size;
  position: relative;
  aspect-ratio: 5 / 7;
  width: 100%;
  font-family: "IBM Plex Sans", system-ui, sans-serif;
  color: #eef3fb;
  user-select: none;
}

/* ==========================================================================
   Flip holder — front and back as 3D siblings.
   Nothing rotates until the showcase drives --flip; the grid never flips.
   ========================================================================== */

.dc-card__flip {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
}
.dc-card__front,
.dc-back {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
/* Front and back are half a thickness apart, so the holder is a box rather
   than two coincident planes. */
.dc-card__front { transform: translateZ(calc(var(--card-thickness) / 2)); }
.dc-back {
  transform: rotateY(180deg) translateZ(calc(var(--card-thickness) / 2));
}

/* A card resting face-down. The whole holder is turned, so this is the same
   box seen from behind rather than a second "back card" component — the edge
   planes, the corner walls and the stock all come along.

   The showcase drives .dc-card__flip with the Web Animations API, and a WAAPI
   animation outranks this rule, so a flipped card still spins normally if one
   is ever showcased. Turning a card face-up is removing this class. */
.dc-card--flipped .dc-card__flip { transform: rotateY(180deg); }

/* A card that can be turned over BY HAND, rather than one that is simply
   face-down. The battle board's face-down cards want no transition — they are
   already the wrong way round when they are dealt — so the animation is a
   modifier, not part of `--flipped`.

   Nothing else is needed for this to work: the flip holder is already
   `preserve-3d`, and the front and the back already have backface-visibility
   hidden, which is what keeps the holo foil (inside .dc-card__front) from
   showing through the back. */
.dc-card--flippable .dc-card__flip {
  transition: transform 600ms cubic-bezier(0.2, 0.8, 0.25, 1);
}
@media (prefers-reduced-motion: reduce) {
  .dc-card--flippable .dc-card__flip { transition: none; }
}

/* THE CUT EDGE, as real geometry — all four sides now, not just left and
   right. Each plane is hinged on the card's own edge and swung a quarter
   turn so it stands perpendicular to the face: when the card turns edge-on,
   these are the surfaces pointing at the camera.

   The top and bottom were skipped originally on the reasoning that a Y-spin
   cannot expose them. That holds at a hairline thickness and stops holding
   the moment the card is thick and wobbling on X — which is exactly the
   state it is in now.

   EACH STOPS AT THE CORNER RADIUS, and must. Run corner to corner and their
   sharp ends sit on the flip's SQUARE box, so they protrude past the rounded
   face as a hard right-angle — which is exactly the facet that showed up at
   the corners. Stopping at the radius keeps every layer inside one silhouette.

   What that leaves is a short gap at each corner when the card is dead
   side-on, where the edge wall of a rounded card is really a quarter-cylinder
   and a flat plane cannot follow it. That gap reads as a soft dark notch at
   the extreme ends of the bar, not as a hole through the card — which is what
   the chamfer used to give, because a chamfer is a cut with no wall at all.

   Across the depth the gradient is the stock seen on end: printed skin, dark
   core, printed skin, with a specular line where the cut catches light. A
   flat dark band reads as a shadow, not as an edge. */
.dc-card__side {
  position: absolute;
  background: linear-gradient(
    var(--edge-angle, 90deg),
    var(--stock-face) 0%,
    var(--stock-lit) 7%,
    var(--stock-core) 34%,
    var(--stock-core) 66%,
    var(--stock-lit) 93%,
    var(--stock-face) 100%
  );
}

.dc-card__side--l,
.dc-card__side--r {
  top: var(--radius);
  bottom: var(--radius);
  width: var(--card-thickness);
}
.dc-card__side--r {
  right: 0;
  transform-origin: 100% 50%;
  transform: translateZ(calc(var(--card-thickness) / -2)) rotateY(90deg);
}
.dc-card__side--l {
  left: 0;
  transform-origin: 0 50%;
  transform: translateZ(calc(var(--card-thickness) / -2)) rotateY(-90deg);
}

/* Top and bottom: same construction, hinged on the horizontal edges. The top
   faces up so it catches more light; the bottom is in its own shadow. */
.dc-card__side--t,
.dc-card__side--b {
  left: var(--radius);
  right: var(--radius);
  height: var(--card-thickness);
  --edge-angle: 180deg;
}
.dc-card__side--t {
  top: 0;
  transform-origin: 50% 0;
  transform: translateZ(calc(var(--card-thickness) / -2)) rotateX(90deg);
  filter: brightness(1.25);
}
.dc-card__side--b {
  bottom: 0;
  transform-origin: 50% 100%;
  transform: translateZ(calc(var(--card-thickness) / -2)) rotateX(-90deg);
  filter: brightness(0.7);
}

/* THE CORNERS, where the edge wall is really a quarter-cylinder.

   The four straight planes stop at the radius and must (see above), which
   left the wall open across each corner arc. That was invisible while the
   planes were not drawing at all; the moment they did, dead side-on showed
   a notch at each end with the inside of the card through it.

   A flat plane cannot be a quarter-cylinder, so each arc is approximated by
   TWO inscribed chords — an octagon's worth of corner, within ~2px of the
   true arc at this radius. Two and not one: a single chord across the whole
   90deg is a chamfer, which is precisely what the rounded corner replaced.

   INSCRIBED is the load-bearing word. The chords lie inside the arc, so
   nothing can poke past the rounded silhouette face-on — the failure the
   full-length planes had. And every segment spans the full thickness in z,
   so at 90deg each projects to exactly the width of the straight planes and
   the silhouette closes with no step in it.

   Each chord is a strip one chord long and one thickness deep, hinged at its
   start point: rotateX swings its depth into z, rotateZ aims it along the
   arc's tangent, and the translateZ centres it on the card's midplane the
   same way the straight planes are centred.

   WHERE each one sits — left, top, --chord, --turn and the brightness that
   carries the neighbouring plane's light round the corner — is pure formula
   and is generated in js/card.js (buildCornerWalls), because the number of
   chords per corner is a tuning constant there. This rule is everything that
   does not depend on which chord it is. */
.dc-card__corner {
  --edge-angle: 180deg;   /* the stock reads across the depth, as on t/b */
  width: var(--chord);
  height: var(--card-thickness);
  transform-origin: 0 0;
  transform:
    translateZ(calc(var(--card-thickness) / -2))
    rotateZ(var(--turn))
    rotateX(90deg);
}

/* ==========================================================================
   THE CARD BACK (spec §5)
   One uniform back for every card. It carries NO species, finish or roll
   information — the moment it does, it stops being a back.
   ========================================================================== */

.dc-back__trim {
  position: absolute;
  inset: 0;
  border-radius: var(--radius);
  background: var(--kaspa-teal);
}

/* Thin teal inner chamfer trim: the face is inset, so the teal shows as a
   ring. Same even-ring geometry as the front's trim. */
.dc-back__face {
  --back-trim: 0.5cqw;
  position: absolute;
  inset: var(--back-trim);
  border-radius: var(--radius-back);
  /* A parallel offset of a rounded rect simply loses the inset from its
     radius — the whole of the old even-ring chamfer arithmetic. */
  --radius-back: calc(var(--radius) - var(--back-trim));
  overflow: hidden;
  background:
    linear-gradient(180deg, #0c1322 0%, #080e1b 55%, #050810 100%);
}

/* Blocks gathering toward the bottom, as the art style does. Fixed positions,
   not random — every back is identical. */
.dc-back__blocks { position: absolute; inset: 0; }
.dc-back__blocks i {
  position: absolute;
  background: #1a5f55;
  border: 1px solid #49e0c0;
}

/* The mark sits in the upper-middle, clear of the block field below it. */
.dc-back__mark {
  position: absolute;
  inset: 0 0 26% 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6cqw;
}

/* The emblem: a teal block held in a knot of angular chain links. */
.dc-back__emblem {
  width: 46cqw;
  height: 46cqw;
  color: var(--kaspa-teal);
}

.dc-card .dc-back__word {
  margin: 0;
  font-family: "Chakra Petch", system-ui, sans-serif;
  font-weight: 700;
  font-size: 7.4cqw;
  letter-spacing: 0.28em;
  text-indent: 0.28em; /* balance the tracking on the last letter */
  text-transform: uppercase;
  color: #dff7f1;
}

/* Outer layer = the trim ring + the card's drop shadow.
   clip-path eats any border or box-shadow on the clipped element (the same
   trap as the LV hex badge), so the trim is a background behind an inset
   inner layer, and the shadow is a filter — drop-shadow follows the chamfered
   silhouette, box-shadow would be clipped away with the corners. */
.dc-card__trim {
  position: absolute;
  inset: 0;
  border-radius: var(--radius);
  /* This layer IS the cut edge: a light hairline all the way round, brightest
     along the top-left where light would catch it. The stock sits on top of
     it, inset by the hairline width, so the ring shows. */
  background:
    linear-gradient(
      135deg,
      #93a4bd 0%,
      var(--hair) 26%,
      #3d4a5e 74%,
      #2b3648 100%
    );

}

/* The slab: the same silhouette, sitting a couple of pixels down and to one
   side of the printed face, so a sliver of stock shows along the bottom and
   one edge. That sliver is the whole "this is an object" signal at rest —
   the 3D side planes give nothing here, because a card square to the camera
   has no edge facing it.

   --edge-x / --edge-y come from the pointer (card.js writes them on the
   nearest .dc-card). The offset grows on the side tilting away and can cross
   zero, so pushing the pointer hard left walks the edge round to the left —
   thickness that answers the tilt rather than a border painted on one side.

   It carries the shadows because they belong to the slab's underside, not to
   the printed face. drop-shadow, not box-shadow: the silhouette is clipped,
   and box-shadow would be drawn on the rectangle and cut away with the
   corners. */
.dc-card__slab {
  position: absolute;
  inset: 0;
  border-radius: var(--radius);
  /* THE OFFSET OPPOSES THE POINTER, and it has to.
     .tilt applies rotateY(--ry) with --ry positive when the pointer is right
     of centre. A positive rotateY pushes the RIGHT side away from the viewer
     and lifts the LEFT — so the side you can see under is the one opposite
     the cursor. Same on the other axis: the pointer at the bottom presses the
     bottom away and raises the top.
     These terms were added rather than subtracted, which put the exposed edge
     under the cursor and read as the card's back sliding out from beneath the
     side being pressed. The minus signs are the fix. */
  transform: translate(
    calc(var(--card-edge-reveal) * (0.25 - var(--edge-x, 0) * 1.05)),
    calc(var(--card-edge-reveal) * (0.45 - var(--edge-y, 0) * 0.95))
  );
  transition: transform 0.14s ease-out;
  /* Layered stock seen at a glancing angle: bright skin at the cut, dark
     core below it. Steeper than the face's shading, because this surface is
     turned away from the light. */
  background: linear-gradient(
    196deg,
    var(--stock-face) 0%,
    #33405a 22%,
    #1b2434 58%,
    var(--stock-core) 100%
  );
  filter:
    drop-shadow(0 0.25cqw 0.35cqw rgba(0, 0, 0, 0.85))
    drop-shadow(0 1cqw 1.4cqw rgba(0, 0, 0, 0.6))
    drop-shadow(0 3cqw 4.5cqw rgba(0, 0, 0, 0.45));
}

@media (prefers-reduced-motion: reduce) {
  .dc-card__slab { transition: none; }
}

/* The card body, inset by the hairline so the cut edge reads as a ring. */
.dc-card__stock {
  position: absolute;
  inset: var(--hair-w);
  border-radius: var(--radius-stock);
  --radius-stock: calc(var(--radius) - var(--hair-w));
  /* A gentle top-to-bottom fall across the stock, so the border is not a
     flat swatch — printed card edges pick up a little shading. */
  background: linear-gradient(170deg, #26334a 0%, var(--stock) 46%, #172032 100%);
}

/* The card face, inside the printed border.
   RECTANGULAR on purpose: on a real card the art window is square-cornered
   inside a cut outer edge. It also means no clip-path here, so the accent
   rule can be an actual border. isolation keeps the foil's blend modes in. */
.dc-card__frame {
  position: absolute;
  inset: calc(var(--border-w) - var(--hair-w));
  /* Nested, not square. The face used to be a hard rectangle inside a cut
     outer edge, which reads fine against a chamfer and badly against an arc:
     the printed rule turned a 90deg corner inside a rounded card and that
     corner is the facet you see. r - inset keeps every arc concentric.
     border-radius also keeps the border itself, which clip-path would have
     thrown away — the whole reason this rule could not be a real border
     before. */
  border-radius: calc(var(--radius) - var(--border-w));
  border: var(--rule-w) solid var(--rule-color, var(--kaspa-teal));
  background: var(--frame, #10151f);
  overflow: hidden;
  isolation: isolate;
}

.dc-card__art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover; /* 3:4 art into a 5:7 face => ~5% side trim (§1) */
  display: block;
}

/* ==========================================================================
   WINDOWED layout — plain blue only. Full art is exclusive to holo finishes
   (§1), so a plain card shows a SQUARE crop in a chamfered window with a thin
   type-color trim, over a solid panel carrying the rest of the UI.

   The window is 1:1 and the art in sq/ is 1:1, so object-fit: cover fits it
   exactly — no letterbox, no backfill, none of the seam-hiding the earlier
   3:4-into-landscape attempt needed. That is what square art bought.
   ========================================================================== */

/* The windowed card wears its type colour as the outer ring (set in card.js —
   --frame-trim is written inline, so a rule here would lose to it).

   Panel material. A fine diagonal micro-grid plus a soft vertical gradient,
   so the surface reads as printed stock at arm's length. The grid lines are
   ~2.5% lighter than the base and 1px on a 7px pitch: texture at a glance,
   invisible behind text. Kept in px rather than cqw so the weave stays fine
   at every card size instead of coarsening on big cards. */
.dc-card--windowed .dc-card__frame {
  --frame-pad: 2.2cqw;   /* the window cancels this on three sides */
  display: flex;
  flex-direction: column;
  gap: 1cqw;
  padding: var(--frame-pad);
  background-color: var(--frame, #10151f);
  background-image:
    linear-gradient(
      180deg,
      rgba(255, 255, 255, 0.05) 0%,
      rgba(255, 255, 255, 0) 34%,
      rgba(0, 0, 0, 0.16) 100%
    ),
    repeating-linear-gradient(
      45deg,
      rgba(255, 255, 255, 0.026) 0 1px,
      transparent 1px 7px
    ),
    repeating-linear-gradient(
      -45deg,
      rgba(255, 255, 255, 0.018) 0 1px,
      transparent 1px 7px
    );
}

/* The row budget is DERIVED, not eyeballed, so every zone can carry the same
   type scale as a full-art card and still fit.

   Frame content box, in cqw (the card is 5:7, so its height is 140cqw):
     width  = 100 - 2*0.55 (trim) - 2*2.2 (padding) = 94.5cqw
     height = 140 - 2*0.55       - 2*2.2           = 134.5cqw

   THE ART IS THE TOP OF THE CARD. There is no name bar: the name chip and
   the LV hex float over the art on a .dc-top, the same component and the
   same chip treatment a full-art card uses. The two layouts are siblings —
   identical top, different bottom.

   So the window bleeds past the frame's padding on three sides and its top
   corners take the frame's own radius. Only its sides and bottom still show
   the printed rule, because those are the only edges that still meet
   anything. Its height therefore spends 2.2cqw of the frame's top padding,
   which is why the budget below credits it back.

   Rows under the window (MEASURED in-browser, not estimated — an earlier
   estimate of an older layout was 2cqw light and overflowed at some card
   widths, so these are read back from the renderer):
     art window (--win-h), less the 2.2 it bleeds upward   87.8cqw
     type line                                              7.3cqw
     text zone (takes what is left)                       ~30.8cqw
     footer                                                 5.6cqw
     3 gaps @ 1cqw                                          3.0cqw
                                                        = 134.5cqw

   The window is now 98.9 x 90 — very close to SQUARE, which matters because
   windowed art is natively 1:1. It crops about 9% of the art's height, where
   the earlier landscape window cropped 30%. --art-y aims what little is
   lost; per-species crop_y correction should rarely be needed now.

   The text zone is the only flexible row — everything else has a
   deterministic height, so the arithmetic holds at any rendered width and
   nothing depends on intrinsic text height. */
.dc-window {
  --win-h: 90cqw;
  --win-radius: 1.6cqw;   /* 12px at reference — the window follows suit */
  /* The top corners are the FRAME's corners now. The window sits inside the
     frame's accent-rule border, not on it, so the arc is the frame's radius
     less that border as well — r − inset, concentric, same as every other
     ring on the card. Dropping the --rule-w term bulges the window's corner
     a hair proud of the frame's inner curve. */
  --win-top-radius: calc(var(--radius) - var(--border-w) - var(--rule-w));
  --win-trim: 0.42cqw;
  /* Same even-ring correction as the card's own trim. */
  --win-radius-inner: calc(var(--win-radius) - var(--win-trim));
  position: relative;
  flex: none;
  /* Bleed over the frame's padding on top and both sides. No width: the
     flex column stretches it, and the negative margins widen it from there. */
  margin: calc(var(--frame-pad) * -1) calc(var(--frame-pad) * -1) 0;
  height: var(--win-h);
  border-radius:
    var(--win-top-radius) var(--win-top-radius)
    var(--win-radius) var(--win-radius);
  /* The window's own printed rule, in the type colour. */
  background: var(--type-color, var(--kaspa-teal));
}

/* Square art into a near-square window: only ~9% of the height is lost, so
   the crop sits just above centre rather than hauling the frame upward. */
.dc-card--windowed .dc-card__art {
  object-position: 50% var(--art-y, 45%);
}

/* The name chip and LV hex, floating over the art on the frame's own inset
   so they line up with everything printed below them. */
.dc-card--windowed .dc-window .dc-top {
  position: absolute;
  z-index: 2;
  top: var(--frame-pad);
  left: var(--frame-pad);
  right: var(--frame-pad);
}

/* Flush at the top, inset on the other three: that is what leaves the printed
   rule showing on the sides and bottom and nowhere else. */
.dc-window__inner {
  position: absolute;
  inset: 0 var(--win-trim) var(--win-trim);
  border-radius:
    var(--win-top-radius) var(--win-top-radius)
    var(--win-radius-inner) var(--win-radius-inner);
  overflow: hidden;
  background: #05070c;
}

/* Raised bezel: a 1px highlight along the top lip and an inner shadow pooling
   at the bottom, so the art sits IN the frame instead of floating on it.
   A pseudo-element rather than an inset box-shadow, because the window is
   clip-path'd and a shadow would be cut away at the chamfers — this inherits
   the clip from its parent and follows the cut corners correctly. */
.dc-window__inner::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.18) 0 1px, transparent 1px),
    linear-gradient(0deg, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0) 14%),
    linear-gradient(90deg, rgba(0, 0, 0, 0.28), rgba(0, 0, 0, 0) 6%),
    linear-gradient(270deg, rgba(0, 0, 0, 0.28), rgba(0, 0, 0, 0) 6%);
}

/* The panel below the window. Rows keep full-art type scale; the panel takes
   the height the window leaves, which the split above guarantees is enough. */
.dc-panel {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 1cqw;
}

/* Long names ellipsize rather than wrap. line-height is loosened from the
   chip's 1.05 because `overflow: hidden` on a line box that tight clips
   descenders (the p in Carapip). */
.dc-card--windowed .dc-top .dc-name__name {
  /* Chakra Petch's natural line box is ~1.27em, so anything tighter clips
     descenders once `overflow: hidden` is on for the ellipsis. */
  line-height: 1.3;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---- windowed zone chips -------------------------------------------------
   On the panel there is no art to see through, so the see-through look is
   dead weight: these are near-opaque plates with a 1px lighter edge, sitting
   slightly proud of the textured surface. The LV hex stays outside the name
   plate — it already carries its own type-coloured ring. */

/* Scoped to the panel ON PURPOSE. The chips floating over the art are the
   full-art layout's, and must keep its treatment untouched — that shared top
   is the whole point of the two layouts being siblings. */
.dc-card--windowed .dc-panel .dc-chip,
.dc-card--windowed .dc-typeline {
  background: var(--ink-panel);
  /* Opaque ink on a printed panel: there is nothing behind these to show
     through, so the blur would cost a composite per chip and buy nothing. */
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  border: 1px solid var(--ink-edge);
  box-shadow:
    0 0.25cqw 0.6cqw rgba(0, 0, 0, 0.38),
    inset 0 1px 0 rgba(255, 255, 255, 0.055);
}

/* The text box is inset like a printed plate: darker than its neighbours,
   with the highlight on the BOTTOM lip instead of the top, which is what
   sells "pressed in" rather than "raised". */
.dc-card--windowed .dc-text {
  background: var(--ink-plate);
  /* Ink on a printed plate does not cast a shadow. The lift exists only for
     type floating over art; here it would read as a printing fault. */
  text-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  box-shadow:
    inset 0 0.3cqw 0.7cqw rgba(0, 0, 0, 0.5),
    inset 0 -1px 0 rgba(255, 255, 255, 0.05);
}
.dc-card--windowed .dc-panel .dc-stat__label,
.dc-card--windowed .dc-panel .dc-stat__num {
  text-shadow: none;
}

/* Pills carry full-opacity fills on the panel, and no blur for the same
   reason the panel chips do not. */
.dc-card--windowed .dc-type,
.dc-card--windowed .dc-band,
.dc-card--windowed .dc-finish-badge {
  background: var(--ink-plate);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  text-shadow: none;
}
.dc-card--windowed .dc-band {
  border: 1px solid var(--ink-edge);
}

/* No font overrides: the windowed panel uses the same scale as a full-art
   card. Only the rows' flex behaviour differs, so nothing can be crushed.
   The text zone is the one row allowed to flex — it absorbs the slack. */
.dc-card--windowed .dc-typeline,
.dc-card--windowed .dc-footer {
  flex: none;
}

/* ---- type line -----------------------------------------------------------
   A narrow bar directly under the art: what the creature IS on the left, the
   type compacted onto the right end of the same bar. The block width is not
   here — it rides in the footer, because this bar cannot hold three things
   at a card's width without one of them ellipsizing. */
.dc-typeline {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.4cqw;
  min-width: 0;
  padding: 0.7cqw 1.1cqw 0.7cqw 2.2cqw;
  border-radius: 1cqw;
}
.dc-card .dc-typeline__text {
  min-width: 0;
  font-family: "Chakra Petch", system-ui, sans-serif;
  font-weight: 600;
  font-size: 2.9cqw;
  line-height: 1.3;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(238, 243, 251, 0.82);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dc-typeline__mark {
  flex: none;
  display: flex;
  align-items: center;
  gap: 0.9cqw;
}
/* The pill shrinks to ride the bar instead of standing on its own row. */
.dc-card .dc-type--compact {
  padding: 0.5cqw 1.5cqw;
  font-size: 2.5cqw;
  border-width: 0.26cqw;
}

/* ---- text box + stat emblems ---------------------------------------------
   The emblems overlap the text box's bottom edge, so the two share a
   positioning context. --emblem-drop is how far they hang below it; the
   overlap is therefore (--emblem-h - --emblem-drop), and the text box pads
   its bottom by that much so no line can run underneath a plate. */
.dc-textzone {
  --emblem-w: 17cqw;
  --emblem-h: 11cqw;
  --emblem-drop: 6cqw;
  position: relative;
  flex: 1;
  min-height: 0;
}
/* `.dc-text.dc-textbox` on purpose: the shared `.dc-card .dc-text` rule sets a
   fixed clamped height and `display: -webkit-box`, and it ties this one on
   specificity while sitting later in the file — so it would win. The extra
   class buys the weight to override both. */
.dc-card--windowed .dc-text.dc-textbox {
  position: absolute;
  inset: 0 0 var(--emblem-drop) 0;
  height: auto;              /* overrides the clamped fixed height */
  display: flex;
  flex-direction: column;
  gap: 1cqw;
  padding: 1.6cqw 2.2cqw calc(var(--emblem-h) - var(--emblem-drop) + 0.6cqw);
  border-radius: 1.2cqw;
}
/* No clamp here — these are flex items and it would be inert on them. Each
   wraps its text in a .dc-text__lines span, which carries it. */
.dc-card .dc-textbox__rules,
.dc-card .dc-textbox__flavor {
  margin: 0;
  overflow: hidden;
}
.dc-card .dc-textbox__rules {
  font-size: 2.75cqw;
  line-height: 1.3;
  color: rgba(238, 243, 251, 0.92);
}
.dc-card .dc-textbox__flavor {
  font-style: italic;
  font-size: 2.65cqw;
  line-height: 1.34;
  color: rgba(238, 243, 251, 0.72);
}
/* Printed cards rule off between what the card DOES and what it is about. */
.dc-textbox__rules + .dc-textbox__flavor {
  padding-top: 1cqw;
  border-top: 1px solid var(--ink-edge);
}

.dc-emblems {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  justify-content: space-between;
  pointer-events: none;
}
/* Chamfered, not rounded — the one place the card nods at the site's UI.
   Two stacked clipped plates rather than a border, because clip-path
   discards borders (the same reason the LV hex is built this way). */
.dc-emblem {
  --chamfer: polygon(16% 0, 100% 0, 100% 84%, 84% 100%, 0 100%, 0 16%);
  width: var(--emblem-w);
  height: var(--emblem-h);
  display: grid;
  clip-path: var(--chamfer);
  background: var(--type-color, var(--kaspa-teal));
  filter: drop-shadow(0 0.3cqw 0.6cqw rgba(0, 0, 0, 0.55));
}
.dc-emblem__inner {
  margin: 0.34cqw;
  clip-path: var(--chamfer);
  background: var(--ink-plate);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  line-height: 1;
}
.dc-card .dc-emblem__label {
  font-size: 2cqw;
  letter-spacing: 0.18em;
  color: rgba(238, 243, 251, 0.58);
}
/* The biggest numbers on the card (§1). */
.dc-card .dc-emblem__num {
  font-family: "Chakra Petch", system-ui, sans-serif;
  font-weight: 700;
  font-size: 6.4cqw;
  color: #eef3fb;
}

/* Red/gold on a species with no red art yet. The card falls back to the blue
   art so it still reads, and says so out loud — this is a known gap in the
   asset set, not something to paper over. Uses chain-red, which is the one
   place red is allowed outside a red/gold finish. */
.dc-redflag {
  position: absolute;
  left: 0;
  right: 0;
  top: 38%;
  z-index: 4;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6cqw;
  padding: 1.6cqw 2cqw;
  background: rgba(24, 6, 8, 0.82);
  border-top: 0.3cqw solid #ff5a5a;
  border-bottom: 0.3cqw solid #ff5a5a;
  text-align: center;
  pointer-events: none;
}
.dc-card .dc-redflag__label {
  font-family: "Chakra Petch", system-ui, sans-serif;
  font-weight: 700;
  font-size: 3.2cqw;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: #ff8c8c;
}
.dc-card .dc-redflag__file {
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: 2.3cqw;
  color: rgba(255, 200, 200, 0.75);
  word-break: break-all;
}
/* In the windowed layout the flag belongs over the art window, not the panel. */
.dc-card--windowed .dc-redflag { top: 30%; }

/* Missing-art placeholder: the gallery still renders, and says what to drop in. */
.dc-card__missing {
  position: absolute;
  inset: 0;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.4cqw;
  padding: 8cqw;
  text-align: center;
  background:
    radial-gradient(120% 90% at 50% 18%, #16202e 0%, var(--void) 70%);
}
.dc-card--noart .dc-card__missing { display: flex; }
.dc-card--noart .dc-card__art { display: none; }

.dc-card__missing-mark {
  width: 9cqw;
  height: 9cqw;
  background: var(--kaspa-teal);
  opacity: 0.85;
}
.dc-card .dc-card__missing-title {
  font-family: "Chakra Petch", system-ui, sans-serif;
  font-weight: 600;
  font-size: 3.4cqw;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #9fb2c9;
}
.dc-card .dc-card__missing-file {
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: 2.7cqw;
  color: var(--kaspa-teal);
  word-break: break-all;
}

/* --------------------------------------------------------------------------
   Gold grade (§2) — NO unique gold art: red art + programmatic grade.
   color-blend #e8b84b @ 0.92 + soft-light #8a5f14 @ 0.45
   -------------------------------------------------------------------------- */

.dc-grade { position: absolute; inset: 0; display: none; }
.dc-card--gold .dc-grade,
.dc-card--purple .dc-grade { display: block; }

.dc-grade__color {
  position: absolute;
  inset: 0;
  background: #e8b84b;
  mix-blend-mode: color;
  opacity: 0.92;
}
.dc-grade__soft {
  position: absolute;
  inset: 0;
  background: #8a5f14;
  mix-blend-mode: soft-light;
  opacity: 0.45;
}

/* Purple grade — the same two-layer construction as gold, violet hues. Only
   the colours change: the blend modes and opacities are what make a grade
   read as a metal rather than a tint, and they are shared on purpose. */
.dc-card--purple .dc-grade__color { background: #9b5de5; }
.dc-card--purple .dc-grade__soft { background: #3a1070; }

/* ==========================================================================
   §3 HOLO — LOCKED recipe ("barberpole"). Two layers between art and UI.
   ========================================================================== */

.dc-foil {
  position: absolute;
  inset: 0;
  display: none;
  overflow: hidden;
  pointer-events: none;
}
.dc-card--foil-rainbow .dc-foil,
.dc-card--foil-gold .dc-foil,
.dc-card--foil-violet .dc-foil { display: block; }

/* --- A. Band layer -------------------------------------------------------
   Angle MUST be 135deg: screen-periodic angles are the only ones that tile
   seamlessly (115deg caused the original seam — structural, unfixable).
   Stops in px, 236px apart, cycle P = 1180px.
   background-size is exactly P*sqrt(2) square (1668.77px) and the loop shifts
   background-position by that same P*sqrt(2) horizontally. At 135deg a
   horizontal shift of P*sqrt(2) advances the gradient axis by exactly P, so
   the tile lands on itself: zero seam and zero loop-pop. Both are required.
   -------------------------------------------------------------------------- */

.dc-foil__bands {
  position: absolute;
  inset: 0;
  background-size: 1668.77px 1668.77px;
  mix-blend-mode: color-dodge;
  animation: dc-bands 4s linear infinite;
}

/* Rainbow spectrum — blue holo and red, alpha 0.55 */
.dc-card--foil-rainbow .dc-foil__bands {
  background-image: repeating-linear-gradient(
    135deg,
    rgba(80, 220, 255, 0.55) 0px,
    rgba(120, 255, 200, 0.55) 236px,
    rgba(255, 230, 120, 0.55) 472px,
    rgba(255, 120, 200, 0.55) 708px,
    rgba(140, 120, 255, 0.55) 944px,
    rgba(80, 220, 255, 0.55) 1180px
  );
}

/* Gold spectrum — gold's exclusive foil, same geometry, alpha 0.6 */
.dc-card--foil-gold .dc-foil__bands {
  background-image: repeating-linear-gradient(
    135deg,
    rgba(255, 215, 110, 0.6) 0px,
    rgba(255, 245, 200, 0.6) 236px,
    rgba(240, 180, 70, 0.6) 472px,
    rgba(255, 230, 150, 0.6) 708px,
    rgba(230, 160, 60, 0.6) 944px,
    rgba(255, 215, 110, 0.6) 1180px
  );
}

/* Violet spectrum — purple's exclusive foil. IDENTICAL geometry to gold's:
   135deg, stops at 236px, cycle 1180px. Those numbers each encode a fixed bug
   (see the band-layer note above) and are not a per-finish choice — only the
   stop COLOURS change, through violet, magenta and indigo. */
.dc-card--foil-violet .dc-foil__bands {
  background-image: repeating-linear-gradient(
    135deg,
    rgba(168, 96, 245, 0.6) 0px,
    rgba(236, 186, 255, 0.6) 236px,
    rgba(120, 58, 220, 0.6) 472px,
    rgba(255, 138, 236, 0.6) 708px,
    rgba(86, 64, 200, 0.6) 944px,
    rgba(168, 96, 245, 0.6) 1180px
  );
}

/* Opacity: 0.30 blue/gold/purple, 0.26 red (§3) */
.dc-card--holo .dc-foil__bands,
.dc-card--gold .dc-foil__bands,
.dc-card--purple .dc-foil__bands { opacity: 0.3; }
.dc-card--red  .dc-foil__bands { opacity: 0.26; }

@keyframes dc-bands {
  from { background-position: 0 0; }
  to   { background-position: 1668.77px 0; }
}

/* --- B. Glare layer ------------------------------------------------------
   White sheen bar, 45% card width, rotated 18deg, crossing every 6s.
   CRITICAL: the keyframe endpoints put the bar FULLY off-card on BOTH sides.
   Rotation widens its footprint, so -120% -> +380% is the working range.
   On-card endpoints cause a periodic flash — the hardest-to-diagnose bug of
   the build. Do not shrink this range to "clean it up".
   -------------------------------------------------------------------------- */

.dc-foil__glare {
  position: absolute;
  top: -50%;
  left: 0;
  width: 45%;
  height: 200%;
  background: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.18) 50%,
    rgba(255, 255, 255, 0) 100%
  );
  mix-blend-mode: overlay;
  will-change: transform;
  animation: dc-glare 6s ease-in-out infinite;
}

@keyframes dc-glare {
  from { transform: rotate(18deg) translateX(-120%); }
  to   { transform: rotate(18deg) translateX(380%); }
}

@media (prefers-reduced-motion: reduce) {
  .dc-foil__bands,
  .dc-foil__glare { animation: none; }
}

/* The band layer animates background-position, which is a paint, not a
   compositor property — that is inherent to the locked recipe and not
   something to "fix" by rewriting it as a transform. What we can do is not
   run it for cards nobody is looking at: a grid of 21 holos would otherwise
   paint 21 foils every frame while you scroll. Toggled by an
   IntersectionObserver; paused animations cost nothing. */
.dc-card--offscreen .dc-foil__bands,
.dc-card--offscreen .dc-foil__glare {
  animation-play-state: paused;
}

/* ==========================================================================
   Silhouette — a species the holder has never mined (Daglabs)
   The card keeps its full anatomy and loses its content: see
   createCardSilhouette() in card.js for why the data is absent from the DOM
   rather than merely hidden.
   ========================================================================== */

/* The SHAPE is the point — the real creature's outline, in its real window,
   is the clue the catalogue is offering. Kept as a filter on the real <img>
   so it cannot drift from the art the card would show once it lights up.
   Nothing is redrawn or hand-traced; there is one piece of art per creature.

   A plain brightness(0) does NOT give a silhouette here, and it is worth
   saying why: the art is creature AND void baked into one opaque PNG, with no
   alpha. Blacking it out blacks out the whole rectangle and the shape goes
   with it. So the subject is separated from its background first:

     grayscale + brightness + contrast   threshold it — the lit creature goes
                                         white, the dark void goes black
     invert                              creature black, void white
     mix-blend-mode: multiply            white drops out, so the window's own
                                         glow shows around a black creature

   The glowing blocks in the void survive the threshold and come through as
   flecks around the shape. That is the right accident: a Dagling still caught
   in the chain is exactly what this card is saying. */
.dc-card--silhouette .dc-card__art {
  filter: grayscale(1) brightness(1.35) contrast(620%) invert(1);
  mix-blend-mode: multiply;
}
/* The window is lit from behind, so the silhouette reads as something held up
   against the void rather than a hole in the card. This is also what the
   multiply blend above composites onto — without a lit backdrop there is
   nothing for the shape to stand against. */
.dc-card--silhouette .dc-window__inner {
  background:
    radial-gradient(ellipse 68% 58% at 50% 42%, rgba(73, 224, 192, 0.3), transparent 70%),
    radial-gradient(ellipse 100% 100% at 50% 50%, #16283a, #080d16 78%);
  isolation: isolate; /* keep the multiply inside the window */
}
.dc-card--silhouette .dc-card__missing { display: none; }

/* The type line and text box keep their boxes and lose their words, so a dark
   entry is the same object as a lit one and the grid does not jump when one
   fills in. */
.dc-typeline--blank {
  min-height: 3.4cqw;
  opacity: 0.5;
}
.dc-textzone--blank {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 4cqw;
  text-align: center;
  font-family: "IBM Plex Sans", system-ui, sans-serif;
  font-size: 3cqw;
  font-style: italic;
  line-height: 1.4;
  color: rgba(238, 243, 251, 0.34);
}
.dc-card--silhouette .dc-footer { opacity: 0.55; }

/* ==========================================================================
   Surfacing — the moment a silhouette becomes a card
   Reuses the pack opening's vocabulary (css/open.css): the same ~600ms
   ease-out, the same teal bloom. One system, two places.
   ========================================================================== */

.dc-card--revealing .dc-card__art {
  animation: dc-surface 900ms cubic-bezier(0.2, 0.8, 0.25, 1) both;
}
@keyframes dc-surface {
  0%   { filter: brightness(0); }
  35%  { filter: brightness(0.25) saturate(0.4); }
  70%  { filter: brightness(1.35) saturate(1.25)
                 drop-shadow(0 0 22px rgba(120, 240, 220, 0.5)); }
  100% { filter: none; }
}
/* The frame catches the same light a beat later, so the card lights from the
   art outward instead of all at once. */
.dc-card--revealing .dc-card__trim {
  animation: dc-surface-trim 900ms ease-out both;
}
@keyframes dc-surface-trim {
  0%   { box-shadow: none; }
  60%  { box-shadow: 0 0 26px rgba(120, 240, 220, 0.45); }
  100% { box-shadow: none; }
}

@media (prefers-reduced-motion: reduce) {
  .dc-card--revealing .dc-card__art,
  .dc-card--revealing .dc-card__trim { animation: none; }
}

/* ==========================================================================
   UI chips — translucent panels floating over the art (§1)
   ========================================================================== */

.dc-ui {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 3.2cqw;
}

/* Printed labels, not smoked glass. Fully opaque ink on stock: nothing behind
   a chip shows through it, because nothing shows through ink. The full-art
   layout keeps these floating in the same places — they are stamped labels
   over the art, the way a full-art Pokemon card stamps its text boxes. */
/* Translucent on purpose, and it is a reversal: these were fully opaque ink
   for a while, on the reasoning that nothing shows through print. They read
   as too heavy over full art, so the art comes back through — but a chip that
   only drops its alpha becomes unreadable over a busy passage, so the blur
   behind it does the work the opacity used to. The alpha and the blur are one
   decision; lowering the first without raising the second is what made the
   original smoked-glass version illegible.

   Panel chips do NOT get this — see the .dc-panel override, which restores
   opaque ink and kills the blur. Translucency over a printed panel buys
   nothing and costs a composite. */
.dc-chip {
  background: rgba(var(--ink-panel-rgb), var(--chip-alpha, var(--overlay-alpha)));
  -webkit-backdrop-filter: blur(var(--overlay-blur)) saturate(0.8);
  backdrop-filter: blur(var(--overlay-blur)) saturate(0.8);
  border: 1px solid var(--ink-edge);
  border-radius: 1.2cqw;
}
/* The top pair sit firmer: they are small, and a name has to survive
   whatever the art is doing directly behind it. */
.dc-top .dc-chip {
  --chip-alpha: var(--overlay-alpha-top);
}

/* --- Top bar: name chip left, LV hex badge right ------------------------- */

.dc-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 2cqw;
}

.dc-name {
  padding: 1.7cqw 2.4cqw;
  min-width: 0;
}
/* Prefixed with .dc-card so page-level element selectors (e.g. `.finish h3`,
   which is more specific than a bare `.dc-name__name`) cannot reach in and
   restyle the component. Everything text-bearing that a page might plausibly
   style carries this guard. */
.dc-card .dc-name__name {
  margin: 0;
  font-family: "Chakra Petch", system-ui, sans-serif;
  font-weight: 700;
  font-size: 5.2cqw;
  line-height: 1.05;
  letter-spacing: 0.01em;
}
.dc-card .dc-name__sub {
  margin: 0.5cqw 0 0;
  font-size: 2.5cqw;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(238, 243, 251, 0.62);
}

/* LV hex-block badge. Level appears once — it IS the cost (§1).
   Two stacked hexagons: the outer is the ring, the inner (inset by its margin)
   is the fill. A border or inset box-shadow cannot be used here — both draw on
   the element's rectangle, and clip-path then cuts their corners off, leaving
   two vertical slivers instead of a hex outline. */
.dc-lv {
  --hex: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
  flex: none;
  width: 13cqw;
  height: 14.6cqw;
  display: grid;
  clip-path: var(--hex);
  background: var(--type-color, var(--kaspa-teal));
}
.dc-lv__inner {
  margin: 0.5cqw;
  clip-path: var(--hex);
  /* Follows the top chips. No backdrop-filter: clip-path already forces this
     into its own pass, and a blur inside a hex clip samples the un-clipped
     backdrop and fringes at the cut. The type-colour ring around it is opaque
     and carries the contrast instead. */
  background: rgba(var(--ink-plate-rgb), var(--overlay-alpha-top));
  display: grid;
  place-content: center;
  text-align: center;
}
.dc-lv__label {
  font-family: "Chakra Petch", system-ui, sans-serif;
  font-size: 2.3cqw;
  font-weight: 600;
  letter-spacing: 0.18em;
  color: rgba(238, 243, 251, 0.7);
  line-height: 1;
}
.dc-lv__num {
  font-family: "Chakra Petch", system-ui, sans-serif;
  font-size: 7cqw;
  font-weight: 700;
  line-height: 1;
  color: var(--type-color, #fff);
}

/* --- Bottom stack -------------------------------------------------------- */

/* Keep this stack inside the bottom ~30% of the face (§1): the art's focal
   point lives in the upper two-thirds and the chips must not climb into it. */
.dc-bottom {
  display: flex;
  flex-direction: column;
  gap: 1.1cqw;
}

/* Type pill (+ finish badge) left, BLOCK WIDTH tag right */
.dc-pills {
  display: flex;
  align-items: center;
  gap: 1.4cqw;
}

.dc-type {
  padding: 1.1cqw 2.8cqw;
  border-radius: 999px;
  font-family: "Chakra Petch", system-ui, sans-serif;
  font-weight: 700;
  font-size: 3.4cqw;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--type-color);
  background: rgba(var(--ink-panel-rgb), var(--chip-alpha, var(--overlay-alpha)));
  -webkit-backdrop-filter: blur(var(--overlay-blur)) saturate(0.8);
  backdrop-filter: blur(var(--overlay-blur)) saturate(0.8);
  border: 0.32cqw solid var(--type-color);
  /* The type name is coloured ink on a now-translucent fill, which is the
     weakest contrast pairing on the card. A tight shadow puts it back. */
  text-shadow: 0 0.12cqw 0.45cqw rgba(0, 0, 0, 0.85);
  white-space: nowrap;
}

.dc-finish-badge {
  padding: 1cqw 2cqw;
  border-radius: 999px;
  font-family: "Chakra Petch", system-ui, sans-serif;
  font-weight: 700;
  font-size: 2.4cqw;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
  background: rgba(var(--ink-panel-rgb), var(--chip-alpha, var(--overlay-alpha)));
  -webkit-backdrop-filter: blur(var(--overlay-blur)) saturate(0.8);
  backdrop-filter: blur(var(--overlay-blur)) saturate(0.8);
  border: 0.28cqw solid currentColor;
  text-shadow: 0 0.12cqw 0.45cqw rgba(0, 0, 0, 0.85);
}
.dc-card--red  .dc-finish-badge { color: #ff5a5a; }
.dc-card--gold .dc-finish-badge { color: #f2c94c; }
.dc-card--purple .dc-finish-badge { color: #b06cf0; }

.dc-band {
  margin-left: auto;
  padding: 1cqw 2cqw;
  border-radius: 1.2cqw;
  font-size: 2.3cqw;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
  color: rgba(238, 243, 251, 0.78);
  background: rgba(var(--ink-panel-rgb), var(--chip-alpha, var(--overlay-alpha)));
  -webkit-backdrop-filter: blur(var(--overlay-blur)) saturate(0.8);
  backdrop-filter: blur(var(--overlay-blur)) saturate(0.8);
  border: 1px solid rgba(255, 255, 255, 0.14);
  text-shadow: 0 0.12cqw 0.45cqw rgba(0, 0, 0, 0.85);
}

/* Text box — ability, or flavor when the card is vanilla.
   SPEC LAW (§1): a hard 2-line clamp with an ellipsis, on every layout. The
   height is stated rather than left to the content, so the card's vertical
   arithmetic is fixed at every rendered size and long flavor can never push
   the stat bar or footer out of the frame. */
.dc-card .dc-text {
  --text-lines: 2;
  padding: 1.5cqw 2.2cqw;
  font-size: 2.75cqw;
  line-height: 1.3;
  color: rgba(238, 243, 251, 0.9);
  /* +0.5cqw of headroom: a clamped line box rounds a little taller than
     lines x line-height, and without it descenders on the last line clip.
     +2px for the 1px edge top and bottom, which box-sizing puts INSIDE this
     stated height and would otherwise eat the headroom straight back. */
  height: calc(var(--text-lines) * 1.3em + 3.5cqw + 2px);
  overflow: hidden;
}

/* THE CLAMP LIVES HERE, ON THE INNER SPAN, and moving it back up to the box
   breaks it silently.

   `-webkit-line-clamp` only does anything on a real `display: -webkit-box`,
   and a FLEX ITEM has its display blockified. `.dc-text` is a child of the
   flex column `.dc-bottom` (and the windowed `__rules`/`__flavor` are
   children of the flex `.dc-textbox`), so a clamp declared on any of them
   computes to `flow-root` and is inert — even when set inline, which is how
   this was finally pinned down. The box was left clipping on its stated
   height alone, which cuts the last line through the middle of its glyphs
   rather than ending it on an ellipsis.

   The span is an ordinary child of a block container, so nothing blockifies
   it and the clamp does what it says. See buildText() in js/card.js. */
.dc-card .dc-text__lines {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: var(--text-lines, 2);
  overflow: hidden;
}
.dc-text--flavor {
  font-style: italic;
  color: rgba(238, 243, 251, 0.78);
}

/* Stat bar — ATK left, DEF right, biggest numbers on the card (§1) */
.dc-stats {
  display: flex;
  align-items: stretch;
  padding: 0;
  overflow: hidden;
}
.dc-stat {
  flex: 1;
  display: flex;
  align-items: baseline;
  gap: 1.4cqw;
  padding: 1cqw 2.8cqw;
}
.dc-stat--def {
  justify-content: flex-end;
  border-left: 1px solid var(--ink-edge);
}
.dc-stat__label {
  font-family: "Chakra Petch", system-ui, sans-serif;
  font-size: 2.6cqw;
  font-weight: 600;
  letter-spacing: 0.2em;
  color: rgba(238, 243, 251, 0.6);
}
.dc-stat__num {
  font-family: "Chakra Petch", system-ui, sans-serif;
  font-size: 8.6cqw;
  font-weight: 700;
  line-height: 0.9;
  /* The biggest numerals on the card, now sitting on a fill you can see the
     art through. Big type is the most fragile over busy art, not the least:
     the strokes are wide enough to contain a whole bright detail. */
  text-shadow: 0 0.15cqw 0.6cqw rgba(0, 0, 0, 0.8);
}
.dc-stat__label,
.dc-card .dc-text,
.dc-card .dc-name__sub {
  text-shadow: 0 0.1cqw 0.4cqw rgba(0, 0, 0, 0.75);
}
.dc-card .dc-name__name {
  text-shadow: 0 0.15cqw 0.7cqw rgba(0, 0, 0, 0.8);
}
.dc-card--gold .dc-stat__num { color: #f2c94c; } /* gold stats (§2) */
.dc-card--purple .dc-stat__num { color: #b06cf0; } /* purple stats */

/* Footer — DAGLINGS · SEASON n · ### · finish | KASPA L1 */
.dc-card .dc-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2cqw;
  padding: 0.9cqw 2.2cqw;
  font-size: 2.1cqw;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(238, 243, 251, 0.6);
}
.dc-footer__right { color: var(--kaspa-teal); white-space: nowrap; }

/* ==========================================================================
   Showcase overlay
   The stage carries the perspective so the 360 reads as depth rather than a
   flat squash. Cards are pointer-interactive inside it; the backdrop closes.
   ========================================================================== */

.dc-card { cursor: pointer; }
.dc-card:focus-visible { outline: 2px solid var(--kaspa-teal); outline-offset: 4px; }

.dc-showcase {
  position: fixed;
  inset: 0;
  /* TOP OF THE PAGE, whatever else is on it. This was 60, which put it under
     the sticky nav (100) on every page and — once the pack opening existed —
     under the opening overlay (200). Both are fixed children of <body> like
     this one, so they stack in the root context by z-index alone: the card
     was being reparented into the showcase correctly and then drawn behind an
     opaque sheet, which reads as the card vanishing.
     A showcased card is the frontmost thing the site ever shows. Anything new
     that overlays the page belongs below this number, not above it. */
  z-index: 300;
  display: grid;
  place-items: center;
  padding: 4vmin;
  background: rgba(3, 5, 9, 0);
  backdrop-filter: blur(0px);
  transition: background 320ms ease, backdrop-filter 320ms ease;
}
.dc-showcase.is-open {
  background: rgba(3, 5, 9, 0.82);
  backdrop-filter: blur(3px);
}

.dc-showcase__stage {
  /* 5:7 card, so height = 1.4 x width. 57vh keeps it inside the viewport. */
  width: min(440px, 86vw, 57vh);
  perspective: 1600px;
}
/* Perspective, not orthographic. The spin needs it twice over: the card has
   to foreshorten as it turns for the flip to read as a turn at all, and the
   pointer tilt composed underneath is invisible without it. Longer than the
   grid's 1100px because the showcased card is much larger and the grid value
   would fisheye it. */
.dc-showcase__stage .tilt-scene { perspective: 1600px; max-width: none; }

/* Holds the grid open while its card is away, so nothing reflows. */
.dc-showcase__hole { flex: none; }

/* TEMPORARY dev thickness dial — see attachThicknessDial() in card.js.
   Delete both together once the value is settled. */
.dc-dial {
  position: fixed;
  left: 18px;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 18px);
  z-index: 10;
  display: grid;
  gap: 8px;
  padding: 14px 16px;
  background: rgba(8, 12, 20, 0.94);
  border: 1px solid rgba(255, 209, 102, 0.45);
  border-left-width: 2px;
  color: #eef3fb;
  font: 400 12px/1.3 "IBM Plex Sans", system-ui, sans-serif;
}
.dc-dial__title {
  margin: 0;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: #ffd166;
}
.dc-dial__row {
  display: grid;
  grid-template-columns: 84px 150px 46px;
  align-items: center;
  gap: 10px;
  cursor: pointer;
}
.dc-dial__name { color: rgba(226, 255, 250, 0.75); }
.dc-dial__val {
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: #9ffbe6;
}
.dc-dial__note {
  margin: 0;
  font-size: 10.5px;
  color: rgba(226, 255, 250, 0.4);
}

@media (prefers-reduced-motion: reduce) {
  .dc-showcase { transition: none; }
}
/* ==========================================================================
   PRINT GRAIN
   One uniform layer of paper tooth across the whole face, at the same scale
   everywhere — it is the card stock, not per-element decoration, so it sits
   above the ink and below nothing. Generated noise rather than an image, so
   it costs no request and never tiles visibly.
   ========================================================================== */
.dc-grain {
  position: absolute;
  inset: 0;
  z-index: 6;
  pointer-events: none;
  opacity: 0.035;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 160px 160px;
}

/* ==========================================================================
   Compact card — what a card shows when it is very small

   The card is a container (`container-type: inline-size`) and every length
   on it is in `cqw`, so it scales perfectly and illegibly: on the battle
   board at 46px wide the name rendered at 2.4px and the class line at 1.1px.
   Measured, on a 390px viewport.

   So below two thresholds the card stops printing things it cannot print.
   This is a CONTAINER query, not a viewport one, which is the whole point —
   it fires on the battle board, in the deck builder's panes, in the name fan
   on a phone and in the graveyard list, without any of them knowing about it
   or passing a variant class. A card that is small is compact, wherever it
   got small.

   What goes, in order of what a reader can least afford to lose: flavor text
   first (it is prose, and prose is the first thing that stops being readable),
   then the printed furniture, then everything but the art, the name and the
   level.
   ========================================================================== */

/* EVERY SELECTOR IN HERE IS PREFIXED `.dc-card`, and that is not tidiness.
   A container query adds NO SPECIFICITY — `@container { .dc-footer { … } }`
   is still a (0,1,0) selector, and the base rules it is trying to override
   are `.dc-card .dc-footer` at (0,2,0), so they win and the compact card
   silently does nothing. Measured: the battle board's name text stayed at
   2.6px, exactly its uncompacted size, until these were prefixed. */

/* --- under ~168px: drop the prose and the small print ------------------- */
@container (max-width: 168px) {
  .dc-card .dc-text,
  .dc-card .dc-textzone,
  .dc-card .dc-footer,
  .dc-card .dc-typeline,
  .dc-card .dc-band,
  .dc-card .dc-name__sub {
    display: none;
  }
  /* The name is the one word that has to survive, so it takes the room the
     class line gave up. */
  .dc-card .dc-name__name { font-size: 7.4cqw; line-height: 1.2; }
  .dc-card .dc-type { font-size: 5.4cqw; }
  .dc-card .dc-stat__label { font-size: 4.4cqw; }
  .dc-card .dc-stat__num { font-size: 11cqw; }
  .dc-card .dc-lv__label { font-size: 4.2cqw; }
  .dc-card .dc-lv__num { font-size: 9cqw; }
  /* The windowed layout's panel is mostly the parts just removed; what is
     left should sit tight under the art rather than float in a gap. */
  .dc-card--windowed .dc-panel { gap: 1cqw; padding-bottom: 1.5cqw; }
}

/* --- under ~120px: art, name, level. Nothing else fits ------------------ */
@container (max-width: 120px) {
  .dc-card .dc-type,
  .dc-card .dc-stats,
  .dc-card .dc-emblems,
  .dc-card .dc-pills {
    display: none;
  }
  .dc-card .dc-name__name { font-size: 11cqw; }
  .dc-card .dc-lv__num { font-size: 13cqw; }
  .dc-card .dc-lv__label { display: none; }
  /* The card back's DAGLINGS wordmark is cqw-scaled print; on a deck pile
     at battle size it renders at 3.4px, which is texture, not a word. */
  .dc-card .dc-back__word { display: none; }
}

/* --- under ~76px: the art IS the identification ------------------------
   The battle board is four slots wide at any viewport, so on a phone a card
   is about 46px and a name rendered at even 11cqw is 5px of mush. Below this
   the name chip goes entirely: the art is what tells two Daglings apart at
   that size, the level is what you need to read, and a long press opens the
   real card. Better an honest silhouette-and-number than unreadable type. */
@container (max-width: 76px) {
  .dc-card .dc-name { display: none; }
  .dc-card .dc-lv__num { font-size: 20cqw; }
}