/* ============================================================================
   Component kit — fantasy chunky
   ----------------------------------------------------------------------------
   Every raised surface is built the same way, and that consistency is what
   stops the interface reading as a web page:

     bevel  a light inset on the top edge — where the light hits
     face   a vertical gradient — the object's lit-to-shadow body
     shelf  a hard unblurred band of the object's own darker colour below it
            — this is its THICKNESS, not a shadow
     cast   a soft blurred shadow — what it throws on the scene behind

   A 1px border and a soft drop shadow is a web card. A bevel, a face, a shelf
   and a cast is an object. The difference costs four lines of CSS and is the
   whole brief.

   Depends on tokens.css.
   ========================================================================== */

/* --- panel -----------------------------------------------------------------
   The base surface. Framed panels add a metal edge with corner studs. */

.kit-panel {
  position: relative;
  border-radius: var(--rad-lg);
  padding: var(--s4);
  background: var(--panel-face);
  box-shadow:
    var(--bevel),
    var(--shelf-md) var(--ink-800),
    var(--cast-md);
}

/* Tiling material, laid over the panel's own face and under its contents.
   `overlay` keeps the panel's colour and takes only the grain from the
   texture, so the same stone works on a dark panel and a gold-framed one. */
.has-tex { position: relative; }
.tex-layer {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  border-radius: inherit;
  mix-blend-mode: overlay;
}
/* `position` only, deliberately no `z-index`. The texture sits at z-index 0,
   so a positioned sibling later in the DOM already paints over it — and
   giving these a z-index of its own raised every panel child above the page's
   sticky chrome, which is how "Change formation" ended up drawn on top of the
   Begin Battle bar it is supposed to scroll under. */
.has-tex > *:not(.tex-layer) { position: relative; }

.kit-panel.sunken {
  background: var(--panel-sunken);
  box-shadow: var(--carve);
}

/* A panel the player is meant to treat as treasure gets the gold frame. */
/* The painted frame supplies the edge, the bevel and the corner studs, so the
   CSS versions of all three step aside rather than doubling up. */
.kit-panel.framed.is-painted {
  background: var(--panel-face) padding-box;
  box-shadow: var(--cast-md);
}
.kit-panel.framed.is-painted::before,
.kit-panel.framed.is-painted::after,
.kit-panel.framed.is-painted > .kit-studs { display: none; }

.kit-panel.framed {
  padding: calc(var(--s4) + 2px);
  background:
    linear-gradient(var(--panel-face), var(--panel-face)) padding-box,
    linear-gradient(180deg, var(--gold-200), var(--gold-400) 45%, var(--gold-800)) border-box;
  border: var(--frame-w) solid transparent;
  box-shadow:
    inset 0 2px 0 rgba(255, 255, 255, 0.16),
    var(--shelf-md) var(--gold-800),
    var(--cast-md);
}

/* Corner studs. Four nailheads holding the frame to the wall — cheap to draw
   and the single detail that most says "game" rather than "page". */
.kit-panel.framed::before,
.kit-panel.framed::after {
  content: '';
  position: absolute;
  inset: 5px 5px auto auto;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: radial-gradient(70% 70% at 35% 30%, var(--gold-100), var(--gold-500));
  box-shadow:
    0 1px 1px rgba(0, 0, 0, 0.5),
    calc(-100% + -1 * (100% - 7px)) 0 0 0 transparent;
}
.kit-panel.framed::before { left: 5px; right: auto; }
.kit-panel.framed > .kit-studs { position: absolute; inset: auto 5px 5px 5px; height: 7px; }
.kit-panel.framed > .kit-studs::before,
.kit-panel.framed > .kit-studs::after {
  content: '';
  position: absolute;
  bottom: 0;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: radial-gradient(70% 70% at 35% 30%, var(--gold-100), var(--gold-500));
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.5);
}
.kit-panel.framed > .kit-studs::before { left: 0; }
.kit-panel.framed > .kit-studs::after { right: 0; }

.kit-panel-title {
  font-family: var(--font-display);
  font-size: var(--fz-title);
  letter-spacing: 0.01em;
  color: var(--gold-200);
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.55);
  margin: 0 0 var(--s3);
}

/* --- buttons ---------------------------------------------------------------
   Three tiers. Same geometry, different metal, so they read as one family at
   three volumes. */

.kit-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  min-height: var(--tap);
  min-width: var(--tap);
  /* A button is never narrower than its own label. `min-width: var(--tap)`
     replaces flexbox's automatic minimum, so a text button sitting next to a
     `block` sibling would otherwise be squeezed to 44px and clip itself —
     which is how "Clear" lost its last two letters. Refusing to shrink is
     the fix that holds regardless of what it is placed beside. */
  flex-shrink: 0;
  white-space: nowrap;
  padding: 0 var(--s6);
  border: 2px solid var(--kit-btn-edge, var(--iron-500));
  border-radius: var(--rad-md);
  background: var(--kit-btn-face, linear-gradient(180deg, var(--ink-500), var(--ink-700)));
  color: var(--kit-btn-fg, var(--text-cream));
  font-family: var(--font-display);
  font-size: 1rem;
  letter-spacing: 0.01em;
  cursor: pointer;
  overflow: hidden;
  isolation: isolate;
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.45);
  box-shadow:
    var(--bevel),
    var(--shelf-md) var(--kit-btn-shelf, var(--ink-900)),
    var(--cast-sm);
  /* Position only — pressing moves the button down onto its own shelf. */
  transition:
    transform var(--t-fast) var(--ease-out),
    box-shadow var(--t-fast) var(--ease-out),
    filter var(--t-quick) var(--ease-out);
}

/* Once the plate is painted on, the CSS face has to get out of the way: the
   gradient, the drawn edge, the bevel and the rounding all belong to the art
   now, and leaving them on double-draws every one of them. The shelf stays —
   it is what the button presses down onto, and it is geometry, not surface. */
.kit-btn.is-painted {
  background: none;
  border-radius: 0;
  box-shadow: var(--shelf-md) var(--kit-btn-shelf, var(--ink-900)), var(--cast-sm);
  /* `overflow: hidden` clips a border-image to the padding box, which cuts
     the painted ends off at exactly the point they are supposed to appear. */
  overflow: visible;
}
.kit-btn.is-painted:active:not(:disabled) {
  box-shadow: var(--shelf-sm) var(--kit-btn-shelf, var(--ink-900)), 0 1px 3px rgba(0, 0, 0, 0.5);
}

.kit-btn:hover:not(:disabled) { filter: brightness(1.1); }

/* Press: the button travels the height of its shelf and the shelf collapses,
   so it genuinely looks pushed in rather than shrunk. */
.kit-btn:active:not(:disabled) {
  transform: translateY(3px) scale(0.985);
  box-shadow:
    var(--bevel),
    var(--shelf-sm) var(--kit-btn-shelf, var(--ink-900)),
    0 1px 3px rgba(0, 0, 0, 0.5);
  filter: brightness(0.92);
}

/* Release springs past the resting size and settles. */
.kit-btn.is-released { animation: kit-release 260ms var(--ease-spring); }
@keyframes kit-release {
  0% { transform: translateY(3px) scale(0.985); }
  55% { transform: translateY(-1px) scale(1.04); }
  100% { transform: none; }
}

.kit-btn:disabled { opacity: 0.42; cursor: not-allowed; filter: grayscale(0.5); }

.kit-btn:focus-visible {
  outline: 3px solid var(--gold-200);
  outline-offset: 3px;
}

/* Tier 1 — the one thing on the screen to do. */
.kit-btn.primary {
  --kit-btn-face: linear-gradient(180deg, var(--gold-200) 0%, var(--gold-300) 46%, var(--gold-400) 100%);
  --kit-btn-edge: var(--gold-800);
  --kit-btn-shelf: var(--gold-800);
  --kit-btn-fg: var(--text-ink);
  font-size: 1.08rem;
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.45);
}

/* Tier 2 — real actions that are not the main one. */
.kit-btn.secondary {
  --kit-btn-face: linear-gradient(180deg, #6b7fd6 0%, #4356a8 48%, #2e3b78 100%);
  --kit-btn-edge: #202a5c;
  --kit-btn-shelf: #1b2250;
  --kit-btn-fg: #f1f4ff;
}

/* Tier 3 — icon buttons and anything incidental. Round, so it never competes
   with the rectangular tiers. */
.kit-btn.icon {
  padding: 0;
  width: var(--tap);
  height: var(--tap);
  border-radius: var(--rad-pill);
  --kit-btn-face: linear-gradient(180deg, var(--ink-500), var(--ink-700));
}

.kit-btn.lg { min-height: 58px; font-size: 1.18rem; padding: 0 var(--s8); }
.kit-btn.sm { min-height: var(--tap); font-size: 0.86rem; padding: 0 var(--s3); }
 /* `block` means "take the row". Beside a sibling it has to be allowed to
    SHRINK to do that: `.kit-btn` sets `flex-shrink: 0` so that a text button
    is never squeezed below its own label, and a 100%-wide child that also
    refuses to shrink pushes whatever sits next to it off the edge. That is
    what happened to the gem price beside every Summon button.

    `flex: 1 1 auto` restores shrinking for this one case; `min-width: var(--tap)`
    from the base rule still stops it collapsing past a tap target. */
.kit-btn.block { display: flex; width: 100%; flex: 1 1 auto; }

/* Shine sweep. A light runs across the primary button every few seconds so
   the screen is never completely still. Transform only — it composites. */
.kit-btn.primary::after {
  content: '';
  position: absolute;
  top: -60%;
  left: -40%;
  width: 28%;
  height: 220%;
  z-index: -1;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.58), transparent);
  transform: translateX(-140%) rotate(16deg);
  animation: kit-shine var(--t-idle) var(--ease-out) infinite;
}
@keyframes kit-shine {
  0%, 62% { transform: translateX(-140%) rotate(16deg); }
  86%, 100% { transform: translateX(560%) rotate(16deg); }
}

/* --- badge ----------------------------------------------------------------- */

.kit-badge {
  display: inline-grid;
  place-items: center;
  min-width: 21px;
  height: 21px;
  padding: 0 6px;
  border-radius: var(--rad-pill);
  border: 2px solid #5d1410;
  background: linear-gradient(180deg, #ff6a5e, #c42a20);
  color: #fff;
  font-family: var(--font-num);
  font-size: var(--fz-tiny);
  font-weight: 700;
  font-feature-settings: var(--num-feat);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.4);
  box-shadow: var(--bevel), 0 2px 0 #5d1410, var(--cast-sm);
  animation: kit-nudge 2.6s var(--ease-out) infinite;
}
@keyframes kit-nudge {
  0%, 84%, 100% { transform: translateY(0) scale(1); }
  88% { transform: translateY(-3px) scale(1.09); }
  94% { transform: translateY(0) scale(0.97); }
}

.kit-ribbon {
  position: absolute;
  top: 9px;
  left: -5px;
  padding: 2px 9px 2px 7px;
  font-size: var(--fz-tiny);
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #10210f;
  background: linear-gradient(180deg, var(--success), var(--success-deep));
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.45);
  clip-path: polygon(0 0, 100% 0, calc(100% - 6px) 50%, 100% 100%, 0 100%, 4px 50%);
}

/* --- currency bar ----------------------------------------------------------
   Fixed order everywhere: gold, gems, then whatever the screen is about. Each
   slot is a carved well with the coin sitting proud of it and a + to buy. */

.kit-currencies { display: flex; gap: var(--s2); align-items: center; }

.kit-cur {
  display: flex;
  align-items: center;
  gap: 5px;
  height: 30px;
  padding: 0 4px 0 2px;
  border-radius: var(--rad-pill);
  background: var(--panel-sunken);
  box-shadow: var(--carve), inset 0 -1px 0 rgba(255, 255, 255, 0.07);
}

.kit-cur-icon {
  width: 26px;
  height: 26px;
  flex: none;
  filter: drop-shadow(0 2px 2px rgba(0, 0, 0, 0.55));
}

.kit-cur-amt {
  font-family: var(--font-num);
  font-size: var(--fz-small);
  font-weight: 700;
  font-feature-settings: var(--num-feat);
  color: var(--text-cream);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.6);
  min-width: 2ch;
}

/* The counter bounces when it is credited. */
.kit-cur.is-credited .kit-cur-amt { animation: kit-credit 420ms var(--ease-spring); }
@keyframes kit-credit {
  0% { transform: scale(1); color: var(--text-cream); }
  40% { transform: scale(1.28); color: var(--gold-200); }
  100% { transform: scale(1); color: var(--text-cream); }
}

/* The coin-sized + keeps its 21px look but carries a 48px hit area, because
   a thumb does not aim at what it can see. The pad is a pseudo-element so it
   never affects layout. */
.kit-cur-plus {
  position: relative;
  display: grid;
  place-items: center;
  width: 21px;
  height: 21px;
  border-radius: 50%;
  border: 2px solid var(--gold-800);
  background: linear-gradient(180deg, var(--gold-200), var(--gold-400));
  color: var(--text-ink);
  font-weight: 800;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--bevel), 0 2px 0 var(--gold-800);
}

.kit-cur-plus::after {
  content: '';
  position: absolute;
  inset: calc((48px - 21px) / -2);
}

/* --- hero card -------------------------------------------------------------
   One component, two axes.

   RARITY paints the bed, the pattern light and the glow — what the hero was
   born as, which never changes. AWAKENING paints the frame: the border, the
   painted 9-slice and the corner ornaments — how far you have carried them,
   which is the only one of the two the player can move.

   Putting both on the frame, as this card used to, made a hero you had spent
   twelve copies on look identical to one straight out of the summon. */

.kit-hero {
  --rar: var(--r-r);
  --rar-deep: var(--r-r-deep);
  --rar-bed: var(--r-r-bed);
  --rar-glow: var(--r-r-glow);
  --aw: var(--aw-excellent);
  --aw-glow: var(--aw-excellent-glow);

  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 3 / 4;
  padding: 0;
  /* The frame is the tier. An unowned card has no tier and falls back to its
     rarity's deep stop, which is what `.locked` sets below. */
  border: var(--frame-w) solid var(--aw);
  border-radius: var(--rad-md);
  background: var(--rar-bed);
  overflow: hidden;
  cursor: pointer;
  isolation: isolate;
  /* The card sizes its own type: five across a phone is 78px wide and a name
     like "Bramblewarden Thorne" has to fit without being cut. */
  container-type: inline-size;
  box-shadow:
    inset 0 0 0 1px color-mix(in srgb, var(--rar) 55%, transparent),
    var(--bevel),
    var(--shelf-md) var(--rar-deep),
    var(--cast-md);
  transition: transform var(--t-quick) var(--ease-out), box-shadow var(--t-quick) var(--ease-out);
}

.kit-hero:hover { transform: translateY(-3px); }
.kit-hero:active { transform: translateY(2px) scale(0.985); }

/* Painted frame present: the drawn ornament IS the edge, so the CSS border,
   the inset ring and the radius all have to get out of its way. The shelf and
   the cast stay — those are the card's thickness and the shadow it throws,
   and the ornament does not supply either.

   `overflow: hidden` has to go too: it clips against the padding box, which
   under a border-image sits inside the ornament and would shave the corners
   off the very art we just turned on. The art is masked by the plate and the
   bed instead. */
.kit-hero.is-framed {
  border-radius: 0;
  overflow: visible;
  background: var(--rar-bed) padding-box;
  box-shadow:
    var(--shelf-md) var(--rar-deep),
    var(--cast-md);
}

/* The bed and the art must stay inside the ornament rather than running under
   it, so they are pinned to the padding box and rounded a little to sit in
   the frame's throat. */
.kit-hero.is-framed .kit-hero-pattern,
.kit-hero.is-framed .kit-hero-art {
  border-radius: var(--rad-sm);
}

/* Corner marks would collide with the ornament's own corners. Tuck them in. */
.kit-hero.is-framed .kit-hero-fac { top: 1px; left: 1px; }
.kit-hero.is-framed .kit-hero-role { top: 1px; right: 1px; }
.kit-hero.is-framed .kit-hero-rar { top: 28px; right: 3px; }

/* --- dense tile ------------------------------------------------------------
   The management density. Square, no name, five across. Everything that
   survives is something you compare between heroes at a glance: the frame
   says rarity, the emblem says faction, the badge says level. The name is
   dropped because at this size the art identifies the hero faster than any
   label could, and the plate it needed cost a fifth of the tile. */

.kit-hero.dense {
  aspect-ratio: 1 / 1;
}

/* The art is framed tighter and sits higher: a bust rather than a full
   figure, because feet carry no information at 70px. */
.kit-hero.dense .kit-hero-art {
  inset: 0;
  background-size: auto 150%;
  background-position: center 14%;
  mask-image: none;
}

/* A head crop is already framed; it just needs to fill the tile. */
.kit-hero.dense .kit-hero-art.is-bust {
  background-size: cover;
  background-position: center 18%;
}

/* The plate only has to carry two characters, so it starts where the chin
   ends. At 55% it was shading nearly half the face it sits under. */
.kit-hero.dense .kit-hero-plate {
  padding: 10px 3px 2px;
  background: linear-gradient(180deg, transparent, rgba(8, 10, 16, 0.9) 74%);
  text-align: center;
}

.kit-hero.dense .kit-hero-lv {
  display: inline-block;
  padding: 0 4px;
  font-size: 0.56rem;
  background: none;
  color: var(--text-cream);
  text-shadow: var(--stroke-dark);
}

/* The role mark goes: faction and rarity are what a roster is filtered and
   sorted by, and three marks on a 70px tile is one too many. */
.kit-hero.dense .kit-hero-role { display: none; }

/* And so does the rarity pill, which this block already says the FRAME
   carries. It was never excluded, so `.is-framed` parked it 28px down — the
   middle of a 57px tile, directly over the face the tile exists to show. Two
   badges and a plate left about a third of the art visible. */
/* The rarity shield STAYS in dense mode, bottom-right. It was hidden here
   on the grounds that the frame carries rarity — but the frame carries the
   awakening TIER, and with ornate art now reserved for Transcend and
   Eternal there is nothing else on the tile that says whether this is an S
   or a UR. That is the first thing a roster is read for. */
.kit-hero.dense .kit-hero-rar {
  display: grid;
  place-items: center;
  top: auto;
  right: 2px;
  bottom: 2px;
  min-width: 17px;
  height: 15px;
  padding: 0 3px;
  font-size: 0.52rem;
  font-weight: 800;
  letter-spacing: 0.02em;
  /* A shield rather than a pill, so the shape reads before the letter at
     arm's length — and clipped from a rectangle so it costs no asset. */
  clip-path: polygon(0 0, 100% 0, 100% 62%, 50% 100%, 0 62%);
  color: #fff;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--rar) 92%, #fff 8%), var(--rar-deep));
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.75);
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.85);
}

/* No coloured bed per tile. Thirty tinted rectangles read as a quilt and
   the tint competed with the art it sat behind; the rarity is on the
   shield and the tier is on the outline. */
.kit-hero.dense { background: var(--panel-sunken, rgba(10, 13, 21, 0.9)); }

/* The rarity wash goes with it. `.kit-hero-pattern` throws a 30% tint of
   the rarity colour across the whole tile, which at five across turns the
   roster into a quilt and puts a coloured field behind art that is already
   fighting for contrast. On the big 3:4 card it is decoration; here it is
   noise. The rarity is on the shield, the tier is on the outline. */
.kit-hero.dense .kit-hero-pattern { display: none; }

/* The dense plate is a transparent overlay spanning the tile; the level
   now lives in the identity pill, so nothing is positioned against it
   except the shard bar on an unowned hero. */
/* The plate becomes a transparent overlay spanning the whole tile rather
   than a strip at the bottom, because the level badge inside it is now
   positioned against the TILE's corners. Collapsed to the bottom-left, as
   it was, `top: 3px` on the badge measured 3px from the bottom of the
   card — which is where the level ended up. */
.kit-hero.dense .kit-hero-plate {
  inset: 0;
  padding: 0;
  background: none;
  pointer-events: none;
}


/* --- roster badges ---------------------------------------------------------
   Four corners, one meaning each, and never two marks in one corner. */

/* Identity, top-left: the faction emblem and the level in one pill, so
   they cannot drift apart as the tile resizes. */
.kit-hero-id {
  position: absolute;
  z-index: 5;
  top: 3px;
  left: 3px;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  height: 17px;
  padding: 0 5px 0 3px;
  border-radius: var(--rad-pill);
  background: rgba(6, 8, 14, 0.82);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.14);
}
.kit-hero-id .kit-hero-lv {
  font-size: 0.56rem;
  font-weight: 700;
  line-height: 1;
  color: #fff;
  /* A dark stroke, not a shadow: the pill can sit over a pale helmet as
     easily as a dark cloak, and only an outline survives both. */
  text-shadow:
    0 1px 0 rgba(0, 0, 0, 0.9), 0 -1px 0 rgba(0, 0, 0, 0.9),
    1px 0 0 rgba(0, 0, 0, 0.9), -1px 0 0 rgba(0, 0, 0, 0.9);
}

/* The one status slot, top-right. Exactly one of these ever exists. */
.kit-hero-sig {
  position: absolute;
  z-index: 5;
  top: 3px;
  right: 3px;
  display: grid;
  place-items: center;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.65);
}

/* Awakenable: an upward chevron, so it is not a second coloured dot. */
.kit-hero-sig.is-awaken {
  width: 13px;
  height: 13px;
  border-radius: 2px;
  background: var(--success);
  clip-path: polygon(50% 0, 100% 55%, 72% 55%, 72% 100%, 28% 100%, 28% 55%, 0 55%);
  box-shadow: none;
}
/* Levellable, and only for a hero actually on a team — a red dot on all
   thirty-six heroes the moment the player banks some EXP is a dot that
   means "you have EXP", which they can already see in the top bar. */
.kit-hero-sig.is-level {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--alert);
}
/* Fielded: a small check, not a banner across the hero's face. */
.kit-hero-sig.is-team {
  width: 15px;
  height: 15px;
  border-radius: 50%;
  color: #2a1e05;
  background: var(--gold-300);
}

/* Stock, bottom-left. Only ever drawn when it is more than one. */
.kit-hero-copies {
  position: absolute;
  z-index: 5;
  left: 3px;
  bottom: 3px;
  padding: 0 4px;
  border-radius: var(--rad-pill);
  font-size: 0.54rem;
  font-weight: 700;
  color: var(--text-cream);
  background: rgba(6, 8, 14, 0.85);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22);
}

.kit-hero.dense .kit-hero-id .gi { width: 13px; height: 13px; }

.kit-hero.dense .kit-hero-shardnum { font-size: 0.5rem; }
.kit-hero.dense .kit-hero-ready { inset: auto 0 34% 0; font-size: 0.5rem; }

.kit-hero.r-r   { --rar: var(--r-r);   --rar-deep: var(--r-r-deep);   --rar-bed: var(--r-r-bed);   --rar-glow: var(--r-r-glow); }
.kit-hero.r-s   { --rar: var(--r-s);   --rar-deep: var(--r-s-deep);   --rar-bed: var(--r-s-bed);   --rar-glow: var(--r-s-glow); }
.kit-hero.r-sr  { --rar: var(--r-sr);  --rar-deep: var(--r-sr-deep);  --rar-bed: var(--r-sr-bed);  --rar-glow: var(--r-sr-glow); }
.kit-hero.r-ssr { --rar: var(--r-ssr); --rar-deep: var(--r-ssr-deep); --rar-bed: var(--r-ssr-bed); --rar-glow: var(--r-ssr-glow); }
.kit-hero.r-ur  { --rar: var(--r-ur);  --rar-deep: var(--r-ur-deep);  --rar-bed: var(--r-ur-bed);  --rar-glow: var(--r-ur-glow); }

/* The faction's pattern, faint, behind the art. Gives each card a texture
   without needing five painted backdrops. */
.kit-hero-pattern {
  position: absolute;
  inset: 0;
  z-index: 0;
  opacity: 0.3;
  /* Also throws a soft light behind where the figure stands, so a dark
     character does not disappear into a dark bed. */
  box-shadow: inset 0 36px 52px -22px color-mix(in srgb, var(--rar) 38%, transparent);
  background-image:
    radial-gradient(circle at 20% 14%, color-mix(in srgb, var(--f) 60%, transparent) 0 2px, transparent 3px),
    radial-gradient(circle at 70% 36%, color-mix(in srgb, var(--f) 45%, transparent) 0 3px, transparent 4px),
    repeating-linear-gradient(126deg, transparent 0 13px, color-mix(in srgb, var(--f) 22%, transparent) 13px 14px);
}

.kit-hero-art {
  position: absolute;
  inset: 0 0 20% 0;
  z-index: 1;
  background-size: auto 112%;
  background-repeat: no-repeat;
  background-position: center 6%;
  /* Fade the art into the plate rather than cutting it with a hard edge. */
  mask-image: linear-gradient(180deg, #000 68%, transparent 100%);
}

/* SSR and above: the frame itself glows, and the glow pulses. The ring takes
   the tier colour and the bloom takes the rarity's, so a Transcend SSR reads
   as cyan-edged in a red haze rather than as one undifferentiated red. */
.kit-hero.r-ssr, .kit-hero.r-ur {
  box-shadow:
    inset 0 0 0 1px color-mix(in srgb, var(--aw) 70%, transparent),
    inset 0 0 22px var(--rar-glow),
    var(--bevel),
    var(--shelf-md) var(--rar-deep),
    0 0 18px var(--rar-glow),
    var(--cast-md);
  animation: kit-rar-pulse 2.8s var(--ease-out) infinite;
}
@keyframes kit-rar-pulse {
  0%, 100% { filter: brightness(1); }
  50% { filter: brightness(1.11); }
}

/* UR alone throws sparks. Three dots on different orbits, pure transform. */
.kit-hero.r-ur .kit-hero-spark {
  position: absolute;
  z-index: 3;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--gold-100);
  box-shadow: 0 0 7px 2px var(--r-ur-glow);
  animation: kit-spark 3.4s linear infinite;
}
.kit-hero-spark:nth-child(1) { left: 14%; bottom: 26%; animation-delay: 0s; }
.kit-hero-spark:nth-child(2) { left: 52%; bottom: 20%; animation-delay: 1.15s; }
.kit-hero-spark:nth-child(3) { left: 81%; bottom: 31%; animation-delay: 2.3s; }
@keyframes kit-spark {
  0% { transform: translateY(0) scale(0.5); opacity: 0; }
  18% { opacity: 1; }
  100% { transform: translateY(-92px) scale(1); opacity: 0; }
}
.kit-hero:not(.r-ur) .kit-hero-spark { display: none; }

/* Name plate across the foot. Two lines allowed, never clipped. */
.kit-hero-plate {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 2;
  padding: 18px 6px 6px;
  background: linear-gradient(180deg, transparent, rgba(8, 10, 16, 0.92) 42%);
}

.kit-hero-name {
  font-family: var(--font-display);
  font-size: 0.8rem;
  line-height: 1.1;
  hyphens: auto;
  color: var(--text-cream);
  text-align: center;
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.75);
  /* Two lines, then ellipsis — a hero name is never cut mid-word. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.kit-hero-meta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  margin-top: 3px;
  flex-wrap: wrap;
}

.kit-hero-stars { display: flex; gap: 1px; }

/* Narrow cards step the plate down rather than truncating. Three lines are
   allowed here because at this size a long name needs them, and a clipped
   hero name is worse than a slightly taller plate. */
@container (max-width: 110px) {
  .kit-hero-name { font-size: 0.64rem; -webkit-line-clamp: 3; }
  .kit-hero-lv { font-size: 0.6rem; padding: 0 4px; }
  .kit-hero-plate { padding: 14px 3px 4px; }
  .kit-hero-fac, .kit-hero-role { width: 20px; height: 20px; }
}

@container (max-width: 86px) {
  .kit-hero-name { font-size: 0.58rem; }
  .kit-hero-meta { gap: 3px; }
}

/* Corner marks: faction top-left, role top-right, rarity letter under the
   role. Same spots on every card on every screen, so the eye learns where to
   look once.

   The rarity letter sits BELOW the role rather than beside it: both are
   right-aligned "what is this hero" marks, and side by side at tile width
   they met in the middle and overlapped. */
.kit-hero-fac,
.kit-hero-role {
  position: absolute;
  z-index: 3;
  display: grid;
  place-items: center;
  width: 25px;
  height: 25px;
  border-radius: 50%;
  background: rgba(10, 13, 20, 0.74);
  box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--rar) 60%, transparent);
}
.kit-hero-fac { top: 5px; left: 5px; color: var(--f); }
.kit-hero-role { top: 5px; right: 5px; color: var(--text-muted); }

.kit-hero-lv {
  padding: 0 6px;
  border-radius: var(--rad-pill);
  background: rgba(255, 255, 255, 0.11);
  font-family: var(--font-num);
  font-size: var(--fz-tiny);
  font-weight: 700;
  font-feature-settings: var(--num-feat);
  color: var(--text-muted);
  white-space: nowrap;
}

/* Not yet owned: the silhouette plus how far off the player is. */
.kit-hero.locked .kit-hero-art { filter: brightness(0.06) saturate(0); }
.kit-hero.locked .kit-hero-name { color: var(--text-dim); }

/* Shard progress lives inside the plate, under the name, so it can never
   end up behind it however long the name is. */
.kit-hero-progress {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 4px;
  margin-top: 4px;
  padding: 0 2px;
}

.kit-hero-shards {
  height: 7px;
  border-radius: var(--rad-pill);
  background: rgba(0, 0, 0, 0.6);
  box-shadow: var(--carve), inset 0 0 0 1px rgba(255, 255, 255, 0.1);
  overflow: hidden;
}

.kit-hero-shardnum {
  font-size: 0.56rem;
  color: var(--text-dim);
  white-space: nowrap;
}
.kit-hero-shards > i {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(180deg, var(--rar), var(--rar-deep));
}

/* Enough shards banked: the card announces itself. */
.kit-hero.summonable {
  animation: kit-rar-pulse 1.6s var(--ease-out) infinite;
}
.kit-hero.summonable .kit-hero-ready {
  position: absolute;
  z-index: 4;
  inset: auto 0 42% 0;
  text-align: center;
  font-size: var(--fz-tiny);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-ink);
  background: linear-gradient(180deg, var(--gold-200), var(--gold-400));
  padding: 2px 0;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
}

/* --- modal -----------------------------------------------------------------
   Scrim darkens, the sheet pops past its size and settles. */

.kit-scrim {
  position: absolute;
  inset: 0;
  z-index: var(--z-overlay);
  display: grid;
  place-items: center;
  padding: var(--s4);
  background: rgba(4, 6, 11, 0.72);
  backdrop-filter: blur(3px);
  animation: kit-fade var(--t-quick) var(--ease-out);
}
@keyframes kit-fade { from { opacity: 0; } to { opacity: 1; } }

/* A sheet never grows past the screen.

   It had `max-height: none`, which was fine while every modal held one short
   list. The roster filter now carries four rows of pickers — faction, role,
   rarity, tier — and measured 1057px against a 953px viewport, so the bottom
   two rows could not be reached at all. The head stays put and the body
   scrolls, which is the same rule the screen templates follow. */
.kit-modal {
  width: min(380px, 100%);
  max-height: min(86vh, 760px);
  overflow-y: auto;
  overscroll-behavior: contain;
  animation: kit-pop var(--t-slow) var(--ease-back);
}
/* The sheet itself scrolls and the head rides along at the top, rather than
   each child getting a scrollbar of its own. The title and the close button
   are the two things that must never scroll out of reach. */
.kit-modal > .kit-modal-head {
  position: sticky;
  top: calc(var(--s4) * -1);
  z-index: 2;
  margin-top: calc(var(--s4) * -1);
  padding-top: var(--s4);
  background: var(--panel-face);
}
@keyframes kit-pop {
  0% { opacity: 0; transform: scale(0.8) translateY(14px); }
  100% { opacity: 1; transform: none; }
}

.kit-modal-head {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin-bottom: var(--s3);
}
.kit-modal-head .kit-panel-title { margin: 0; flex: 1; }

/* --- entrances -------------------------------------------------------------
   Applied to a container; children arrive in sequence rather than together.
   The index is set inline by the builder. */

.kit-stagger > * {
  animation: kit-rise var(--t-base) var(--ease-out) backwards;
  animation-delay: calc(var(--i, 0) * var(--stagger));
}
@keyframes kit-rise {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}

/* --- idle ------------------------------------------------------------------
   Static character art, breathing. Scale alone would slide the feet, so the
   origin is the ground and the shadow breathes with it. */

.kit-breathe {
  transform-origin: 50% 100%;
  animation: kit-breathe var(--t-idle) ease-in-out infinite;
}
@keyframes kit-breathe {
  0%, 100% { transform: scaleY(1) translateY(0); }
  50% { transform: scaleY(1.015) translateY(-2px); }
}

.kit-float {
  animation: kit-float calc(var(--t-idle) * 1.4) ease-in-out infinite;
}
@keyframes kit-float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-4px); }
}

/* --- numerals --------------------------------------------------------------
   Anything the player compares is tabular, so columns line up and a changing
   value does not shuffle the layout. */
.kit-num {
  font-family: var(--font-num);
  font-weight: 700;
  font-feature-settings: var(--num-feat);
  font-variant-numeric: tabular-nums lining-nums;
}

/* --- scrollbars -------------------------------------------------------------
   The parts of the page nobody draws still carry the design. Left alone, a
   desktop browser puts a wide grey bar with arrow buttons down the side of
   the game — the single loudest "this is a web page" tell on the screen.

   Both syntaxes, deliberately. Chrome honours `scrollbar-width`/`-color` and
   IGNORES the `::-webkit-scrollbar` pseudo-elements once they are set, so the
   standard properties carry current browsers and the pseudo-elements carry
   the older ones.
   ========================================================================== */

* {
  scrollbar-width: thin;
  scrollbar-color: var(--iron-400) transparent;
}

*::-webkit-scrollbar { width: 7px; height: 7px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  border-radius: var(--rad-pill);
  background: var(--iron-400);
  border: 2px solid transparent;
  background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover { background: var(--iron-300); background-clip: padding-box; }
/* The arrow buttons at each end are a desktop convention from before scroll
   wheels; nothing in a phone game needs them. */
*::-webkit-scrollbar-button { display: none; }
*::-webkit-scrollbar-corner { background: transparent; }

/* Horizontal strips that scroll by swipe show no bar at all. */
.kit-noscroll { scrollbar-width: none; }
.kit-noscroll::-webkit-scrollbar { display: none; }

/* --- other browser surfaces -------------------------------------------------
   Selection, focus and the caret ship with colours that belong to no design
   system. Theme them from the palette. */

::selection { background: color-mix(in srgb, var(--gold-300) 45%, transparent); color: var(--text-cream); }

:focus-visible { outline: 3px solid var(--gold-200); outline-offset: 2px; }

html { caret-color: var(--gold-200); accent-color: var(--gold-300); }

/* --- rarity letter and awakening ornaments ---------------------------------
   The two signals in text form, for everyone the colours do not reach. */

.kit-hero-rar {
  position: absolute;
  z-index: 3;
  top: 33px;
  right: 6px;
  min-width: 17px;
  padding: 1px 3px;
  border-radius: var(--rad-sm);
  background: color-mix(in srgb, var(--rar-deep) 88%, #000);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--rar) 80%, transparent);
  color: var(--rar);
  font-size: 0.56rem;
  font-weight: 800;
  line-height: 1.35;
  letter-spacing: 0.04em;
  text-align: center;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.6);
}
/* The dense tile's rarity shield is defined once, with the rest of the
   dense rules. A second copy lived here and, being later at the same
   specificity, quietly won — which is why the shield stayed pinned to the
   top-right corner under the "can level" dot however many times the rule
   above it was corrected. */

/* The painted chevron, centred on the top edge where the frame's own
   ornament would sit. Dots were the fallback and are kept for the case where
   the art is missing. */
.kit-hero-plus {
  position: absolute;
  z-index: 3;
  top: -2px;
  left: 50%;
  display: flex;
  gap: 2px;
  transform: translateX(-50%);
}
.kit-hero-plus img {
  width: 22px;
  height: auto;
  filter: drop-shadow(0 0 4px var(--aw-glow));
}
.kit-hero.dense .kit-hero-plus img { width: 17px; }
.kit-hero-plus i {
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--aw);
  box-shadow: 0 0 5px 1px var(--aw-glow);
}

/* The tier's name on the plate, where the rarity-derived stars used to be. */
.kit-hero-aw {
  overflow: hidden;
  color: var(--aw);
  font-size: 0.56rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Unowned: no tier, so the frame falls back to the rarity it advertises. */
.kit-hero.locked { --aw: var(--rar-deep); }

/* --- §14.4 counter hints and power bands ---------------------------------- */

/* The team's power against what the stage wants: §5d's three bands. The
   number is the thing a player checks before committing, so it carries the
   verdict rather than sitting next to one. */
.fm-power.is-green { color: var(--good); }
.fm-power.is-amber { color: var(--warn, #e0b341); }
.fm-power.is-red   { color: var(--bad); }

/* What you are walking into, and what beats it. Drawn as a pair with an
   arrow between rather than as a sentence, so it reads at a glance and
   needs no translation. */
.fm-counter {
  display: flex; align-items: center; gap: var(--s2);
  flex-wrap: wrap; padding: var(--s1) var(--s2);
}
.fm-counter-pair { display: inline-flex; align-items: center; gap: 2px; }
.fm-counter-arrow { opacity: 0.55; font-size: 11px; }

/* A hero who counters something on the other side. Deliberately a ring and
   not a tint: the card already uses colour for rarity and for the awakening
   frame, and a third colour meaning would make all three harder to read. */
.kit-hero.is-counter {
  outline: 2px solid var(--good);
  outline-offset: 1px;
}

/* --- §14.6 summon library ------------------------------------------------- */

.lib-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(92px, 1fr));
  gap: var(--s2);
}
.lib-card {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: var(--s1); border-radius: var(--r2);
  background: var(--surface-2, rgba(255, 255, 255, 0.04));
  text-align: center;
}
.lib-art {
  width: 56px; height: 56px; border-radius: var(--r2);
  background: var(--surface-3, rgba(255, 255, 255, 0.06)) center / cover no-repeat;
}
.lib-name { font-size: 11px; line-height: 1.2; }
/* Unowned reads as absent rather than as disabled: §14.6 asks for the
   silhouette treatment, and greying the whole card would make the rate
   unreadable too. */
.lib-card.is-missing .lib-art { filter: grayscale(1) brightness(0.55); }
/* An event-only hero is not dimmed for being unowned — it is dimmed for
   being unobtainable, which is a different fact and needs a different look. */
.lib-card.is-event { opacity: 0.72; }
.lib-card.is-event .lib-art { filter: grayscale(0.4); }


/* ===========================================================================
   Roster icon, Hero Clash pass
   --------------------------------------------------------------------------
   Airy: one readable face, four marks, and nothing outside the frame.
   ========================================================================= */

/* --- the interior ---------------------------------------------------------
   THE RARITY BED, at full strength.
   
   This mixed the rarity hue 12% into near-black and called it a tint,
   which at tile size is not a colour — thirty tiles all read as the same
   dark rectangle and the corner letter was doing all the work. The bed
   token is the colour; the tile just uses it. */
.kit-hero.dense { background: var(--rar-bed); }

/* The art needs to stay readable ON a saturated bed, so it gets its own
   scrim rather than relying on the bed being dark. */
.kit-hero.dense .kit-hero-art::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(4, 6, 11, 0.22), transparent 42%);
}

/* The bottom 30%, darkened, so the shield and the copy count survive a
   pale cloak as easily as a dark one. */
.kit-hero.dense::after {
  content: '';
  position: absolute;
  z-index: 2;
  inset: 70% 0 0 0;
  pointer-events: none;
  background: linear-gradient(180deg, transparent, rgba(5, 7, 12, 0.82));
}

/* --- the frame ------------------------------------------------------------
   Two pixels of the tier's colour. No outer glow below Mythic: a glow on
   every tier is a texture rather than a signal, and thirty glowing tiles
   is the opposite of airy. */
.kit-hero.dense {
  border-width: 2px;
  box-shadow: none;
}

/* Mythic and up earn a light that travels the frame — slow, and once. */
.kit-hero.dense.aw-mythic,
.kit-hero.dense.aw-transcend,
.kit-hero.dense.aw-eternal {
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--aw) 45%, transparent);
}
.kit-hero.dense.aw-mythic::before,
.kit-hero.dense.aw-transcend::before,
.kit-hero.dense.aw-eternal::before {
  content: '';
  position: absolute;
  z-index: 4;
  inset: -2px;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(115deg,
    transparent 38%,
    color-mix(in srgb, var(--aw) 70%, #fff 30%) 48%,
    transparent 58%);
  background-size: 260% 100%;
  animation: kit-hero-run 4.5s linear infinite;
  opacity: 0.5;
}
@keyframes kit-hero-run {
  0% { background-position: 160% 0; }
  100% { background-position: -60% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .kit-hero.dense::before { animation: none; }
}

/* --- unowned --------------------------------------------------------------
   A dark silhouette and a shard bar. It used to be the full-colour art
   dimmed, which reads as a hero you own on a bad screen. */
.kit-hero.dense.locked .kit-hero-art {
  filter: brightness(0.14) grayscale(1);
}
/* An unowned hero keeps its rarity bed, dimmed. Flat grey threw away the
   one thing the card is advertising: what you would be getting. */
.kit-hero.dense.locked { background: var(--rar-bed); filter: saturate(0.55) brightness(0.72); }
.kit-hero.dense.locked .kit-hero-id { opacity: 0.55; }

.kit-hero.dense .kit-hero-progress {
  position: absolute;
  z-index: 5;
  /* Stops short of the rarity shield in the bottom-right corner. Run the
     full width and the shard count sits underneath it — "39/80" reads as
     "39" with the rest behind a badge. */
  /* 30px, not 24: the shield's min-width is 20 but "SSR" plus its
     padding measures wider, so a clearance set from the min-width still
     ran the shard count under it. */
  inset: auto 30px 4px 4px;
  display: grid;
  gap: 2px;
  justify-items: center;
}
.kit-hero.dense .kit-hero-shards {
  display: block;
  width: 100%;
  height: 4px;
  border-radius: var(--rad-pill);
  background: rgba(4, 6, 11, 0.9);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.14);
  overflow: hidden;
}
.kit-hero.dense .kit-hero-shards > i {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, var(--rar), var(--rar-deep));
}
.kit-hero.dense .kit-hero-shardnum {
  font-size: 0.5rem;
  color: var(--text-cream);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.9);
}

/* --- the rarity shield ----------------------------------------------------
   18dp, bottom-right, over the gradient that was put there for it. */
.kit-hero.dense .kit-hero-rar {
  min-width: 20px;
  height: 18px;
  z-index: 5;
  font-size: 0.56rem;
}
