/* Triad — Slay-the-Spire-flavor polish pass.
   Landscape mobile. Party row left, enemy row right, facing each other across
   a haunted parchment battlefield. */

* { box-sizing: border-box; margin: 0; padding: 0; -webkit-tap-highlight-color: transparent; }

:root {
  /* === Cross-platform fit-to-screen ===
     The game was hand-tuned for iPhone landscape (~698×393).  We lock
     the stage to a fixed design resolution and scale it visually via
     transform — same internal pixel layout on iOS / Android / desktop,
     just rendered larger or smaller to fit the viewport.  --ui-scale
     is set from JS on resize / orientationchange. */
  --design-w: 720px;
  --design-h: 405px;
  --ui-scale: 1;

  /* === NieR Automata-inspired palette ===
     Bone-on-pitch.  All "gold" tokens now point at desaturated cream tones;
     warm parchment browns flattened to neutral graphite; decorative blood
     and frost colors pulled toward muted near-cream for the sterile,
     instrument-panel feel.  Variable names are kept so the rest of the
     stylesheet doesn't have to be renamed in lockstep. */
  --bg-0: #0a0a0a;          /* near-black */
  --bg-1: #0e0e0c;
  --bg-2: #131311;
  --bg-3: #1a1916;
  --bg-frame: #0f0f0d;
  --parchment: #16151a;     /* deep graphite (was warm parchment) */
  --parchment-deep: #0a0a0c;
  --ink: #d4c8a8;           /* primary bone */
  --ink-dim: #8a8270;
  --ink-faint: #4e4a3e;

  /* "gold" → cream/bone family (NieR cream) */
  --gold: #c9bd9f;          /* base bone */
  --gold-bright: #e6dab2;   /* hot bone (highlights) */
  --gold-pale: #f0e6c8;     /* near-white bone */
  --gold-dim: #6e6650;
  --gold-deep: #2e2a20;

  /* "blood" → muted crimson for danger only.  Less neon. */
  --blood: #8a2828;
  --blood-bright: #c83838;
  --blood-deep: #2e1010;

  /* "frost" → cool muted steel (used very sparingly) */
  --frost: #5a7898;
  --frost-bright: #8aa8c4;

  --shadow: #000;

  /* status colors — desaturated. Identity-via-shape > identity-via-hue. */
  --hp: #b4a880;            /* bone (full HP reads as cream) */
  --hp-low: #c83838;        /* keep red for danger */
  --resolve: #e6dab2;
  --armor: #8898a8;
  --bleed: #b03030;
  --taunt: #b07878;
  --weak: #8e7ea0;
  --heal: #a8c098;          /* slight green tint to stay legible */
  --chain: #b48c5a;
  --chain-full: #d8c898;
  --stagger: #b86838;
  --aoe: #a06880;
  --debuff: #7e6890;

  --card-radius: 0;         /* NieR is square-cornered. */
}

html, body { height: 100%; width: 100%; overflow: hidden; }
body {
  font-family: 'Iowan Old Style', 'Palatino Linotype', Georgia, serif;
  background: #000;
  color: var(--ink);
  user-select: none;
  -webkit-user-select: none;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ============ ROTATE PROMPT ============ */
#rotate-prompt {
  position: fixed; inset: 0; background: var(--bg-0);
  display: none; align-items: center; justify-content: center; z-index: 200;
}
.rotate-inner { text-align: center; color: var(--gold); }
.rotate-icon { font-size: 72px; animation: rotate-anim 2s ease-in-out infinite; }
.rotate-text { font-size: 18px; letter-spacing: 0.15em; text-transform: uppercase; margin-top: 16px; }
.rotate-text span { font-size: 12px; color: var(--ink-dim); letter-spacing: 0.2em; }
@keyframes rotate-anim { 0%,100%{transform:rotate(0)} 50%{transform:rotate(90deg)} }
@media (orientation: portrait) and (max-width: 900px) {
  #rotate-prompt { display: flex; }
  #stage { visibility: hidden; }
}

/* ============ STAGE FRAME — fit-to-screen wrapper ============
   Owns the design-canvas positioning + viewport-fit transform.  #stage
   itself stays plain (no transform) so per-hit screen-shake animations
   can apply transform: translate() freely without clobbering the
   scale/center transform that lets the whole game canvas render at
   the right size on any device. */
#stage-scale {
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--design-w);
  height: var(--design-h);
  transform: translate(-50%, -50%) scale(var(--ui-scale));
  transform-origin: center center;
}
/* Lift the (transform-scaled) stage above #title-screen (z-index: 150)
   whenever the shared #overlay is open — so Credits / Settings overlays
   opened from the title rail actually paint in front.  The transform on
   #stage-scale creates a stacking context that traps #overlay's own
   z-index inside, so without this rule the overlay opens "in front" of
   the stage children but still behind the title-screen sibling. */
body.on-title:has(#overlay:not(.hidden)) #stage-scale {
  z-index: 200;
}

/* ============ STAGE — battlefield backdrop ============ */
#stage {
  /* Fills its #stage-scale wrapper completely; the wrapper handles
     positioning + viewport scale.  position:relative keeps absolutely
     positioned children (overlay, popup-layer, biome-layer, ::before
     vignette, etc.) anchored to #stage itself, not to the wrapper —
     so the layout origin stays exactly where the rest of the
     stylesheet expects it. */
  position: relative;
  width: 100%;
  height: 100%;
  display: grid;
  /* HUD (resolve readout) | ATB strip | battlefield (1fr) | action+commit
     HUD bumped from 22px → 48px so the 44×44 sigil chips and menu
     button have a real tap target.  Previously they overflowed the
     22px row and overflow:hidden clipped them in half — visible but
     un-tappable below the row line. */
  grid-template-rows: 48px 18px minmax(0, 1fr) 92px;
  grid-template-columns: 1fr;
  /* NieR flatter backdrop — neutral graphite radial pools instead of warm
     parchment browns, so the chrome (cream-on-black) reads as the dominant
     palette. */
  background:
    radial-gradient(ellipse 60% 80% at 22% 50%, #16161a 0%, transparent 60%),
    radial-gradient(ellipse 60% 80% at 78% 50%, #14141a 0%, transparent 60%),
    var(--bg-0);
  overflow: hidden;
  /* Safe-area padding is left in for harmless backwards-compat — in
     landscape it resolves to 0 on all known devices, and any notch /
     cutout falls in the letterbox area outside the centered stage. */
  padding-top: env(safe-area-inset-top, 0);
  padding-bottom: env(safe-area-inset-bottom, 0);
  box-sizing: border-box;
}
/* heavy vignette */
#stage::before {
  content: '';
  position: absolute; inset: 0;
  background:
    radial-gradient(ellipse 100% 100% at 50% 50%, transparent 45%, rgba(0,0,0,0.55) 80%, rgba(0,0,0,0.85) 100%);
  pointer-events: none; z-index: 1;
}
/* parchment grain + drifting motes */
#stage::after {
  content: '';
  position: absolute; inset: 0;
  background:
    radial-gradient(circle at 12% 22%, rgba(232,220,196,0.04) 0%, transparent 8%),
    radial-gradient(circle at 88% 78%, rgba(232,220,196,0.03) 0%, transparent 10%),
    radial-gradient(circle at 30% 75%, rgba(200,164,100,0.025) 0%, transparent 6%),
    radial-gradient(circle at 70% 30%, rgba(200,164,100,0.025) 0%, transparent 6%);
  pointer-events: none; z-index: 1;
  animation: motes-drift 24s ease-in-out infinite alternate;
}
@keyframes motes-drift {
  0%   { transform: translate(0, 0); opacity: 0.85; }
  100% { transform: translate(-6px, 4px); opacity: 1; }
}

/* ambient overlays wrapper — defensively spans the whole grid and is out of flow */
#stage > .bg-effects {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 2;
  grid-column: 1 / -1;
  grid-row: 1 / -1;
  overflow: hidden;
}

/* drifting fog layer over the arena (subtle) */
.bg-effects > .fog {
  position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(90deg, transparent 0%, rgba(232,220,196,0.03) 30%, rgba(232,220,196,0.045) 50%, rgba(232,220,196,0.03) 70%, transparent 100%);
  mix-blend-mode: screen;
  animation: fog-drift 32s linear infinite;
}
@keyframes fog-drift {
  0%   { transform: translateX(-14%); opacity: 0.45; }
  50%  { opacity: 0.85; }
  100% { transform: translateX(14%); opacity: 0.45; }
}

/* slow ember motes rising from below */
.bg-effects > .ember-layer {
  position: absolute; inset: 0;
  pointer-events: none;
  overflow: hidden;
}
.ember {
  position: absolute;
  bottom: -6px;
  width: 2px; height: 2px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--gold-pale) 0%, var(--gold) 40%, transparent 100%);
  box-shadow: 0 0 4px rgba(240,212,136,0.6), 0 0 10px rgba(200,164,100,0.3);
  animation: ember-rise 9s linear infinite;
  opacity: 0;
}
.ember:nth-child(1) { left: 8%;  animation-duration: 11s; animation-delay: 0s; }
.ember:nth-child(2) { left: 18%; animation-duration: 13s; animation-delay: -2.5s; }
.ember:nth-child(3) { left: 32%; animation-duration: 10s; animation-delay: -5s; width: 1.5px; height: 1.5px; }
.ember:nth-child(4) { left: 46%; animation-duration: 14s; animation-delay: -7.5s; }
.ember:nth-child(5) { left: 58%; animation-duration: 12s; animation-delay: -1s; width: 1.5px; height: 1.5px; }
.ember:nth-child(6) { left: 72%; animation-duration: 11s; animation-delay: -4s; }
.ember:nth-child(7) { left: 84%; animation-duration: 13s; animation-delay: -6.5s; width: 2.5px; height: 2.5px; }
.ember:nth-child(8) { left: 94%; animation-duration: 10s; animation-delay: -9s; }
@keyframes ember-rise {
  0%   { transform: translate(0, 0)        scale(1);   opacity: 0; }
  10%  {                                    opacity: 0.65; }
  50%  { transform: translate(8px, -50vh)  scale(1.2); opacity: 0.85; }
  90%  {                                    opacity: 0.3; }
  100% { transform: translate(-4px, -100vh) scale(0.6); opacity: 0; }
}

/* ============ HUD — persistent sigil tray (Slay-the-Spire-style).
   Lives on grid row 1 (22px).  Single horizontal strip of chips, each
   colored by sigil category, tap-to-inspect via the shared chip-tooltip. */
#hud {
  display: flex;
  align-items: center;
  /* Run modifier sits on the LEFT; sigil tray + menu button cluster on
     the RIGHT.  Achieved with flex-start + margin-left:auto on the
     sigil tray, so both right-hand items hug the right edge of the
     strip — easier to scan than the previous space-between layout
     where the sigils drifted toward the centre. */
  justify-content: flex-start;
  gap: 8px;
  padding: 0 8px;
  position: relative;
  /* Lifted above the map/recruit/event overlay (z-index 260) so the run
     modifier chip and the sigil tray stay visible on the path screen
     too — the player can read what's active without dismissing the map. */
  z-index: 280;
  /* overflow:visible — was 'hidden', which clipped the chips when the
     row was shorter than their 44px height.  Even with the 48px row,
     visible is safer for the press-and-hold tooltip arrow + glow. */
  overflow: visible;
}
#run-modifier {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
#run-modifier.empty { display: none; }
/* Layer chip — small gold pill always visible alongside the biome
   chip so the player can read which Abyss layer they're climbing. */
.run-layer-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  min-height: 28px;
  padding: 0 8px;
  font-family: inherit;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.16em;
  color: var(--gold-bright);
  background: rgba(8,6,8,0.78);
  border: 1px solid rgba(212,200,168,0.45);
  border-radius: 14px;
  text-shadow: 0 0 8px rgba(240,212,136,0.35);
  flex-shrink: 0;
}
.run-mod-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px 5px 10px;
  font-family: inherit;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--gold-bright);
  background: rgba(20,16,30,0.7);
  border: none;
  cursor: help;
  border-radius: 14px;
  line-height: 1.4;
  min-height: 28px;
  box-shadow: 0 0 12px rgba(154,120,192,0.22), inset 0 0 8px rgba(0,0,0,0.4);
}
.run-mod-icon { color: var(--debuff); font-size: 13px; }
.run-mod-chip:hover { filter: brightness(1.18); }

/* Ascension chip — surfaces the difficulty level the player opted
   into (A1..A5).  Sits next to the layer badge so it reads as
   "this is the climb I'm on."  Hidden at A0. */
.run-asc-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 30px;
  min-height: 28px;
  padding: 0 8px;
  font-family: inherit;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.18em;
  color: var(--blood-bright);
  background: rgba(40,10,10,0.7);
  border: 1px solid rgba(220,80,80,0.5);
  border-radius: 14px;
  text-shadow: 0 0 6px rgba(220,80,80,0.45);
  flex-shrink: 0;
  cursor: help;
}

/* Equipped-charm chip — surfaces the keepsake the player chose
   pre-run so they can read their loadout at a glance.  Sits to the
   right of the biome chip.  Hidden when no charm is equipped. */
.run-charm-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 11px 4px 9px;
  font-family: inherit;
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--gold-bright);
  background: rgba(40,28,14,0.72);
  border: 1px solid rgba(240,212,136,0.45);
  border-radius: 14px;
  min-height: 28px;
  cursor: help;
  box-shadow: 0 0 10px rgba(240,212,136,0.18), inset 0 0 6px rgba(0,0,0,0.35);
  flex-shrink: 0;
}
.run-charm-chip:hover { filter: brightness(1.18); }
.run-charm-icon {
  font-size: 12px;
  color: var(--gold-bright);
  text-shadow: 0 0 6px rgba(240,212,136,0.5);
}
.run-charm-name {
  font-size: 9.5px;
  letter-spacing: 0.12em;
}
/* On narrow screens (sub-720px design canvas, or scaled-down mobile)
   collapse the charm chip to glyph-only so the run-modifier strip
   doesn't overflow into the sigil tray.  Press-and-hold still
   reveals the full name + effect via the chip-tooltip layer. */
@media (max-width: 600px) {
  .run-charm-name { display: none; }
  .run-charm-chip { padding: 4px 8px; }
}

/* NEW dot on title menu buttons — pulses gold to draw the eye to
   newly-unlocked top-level entries (Codex on first L1 clear).
   Currently only used on the Codex button until the player opens
   it for the first time.  Cleared by marking codex-opened. */
.ts-menu-btn.ts-menu-btn-new {
  position: relative;
  color: var(--gold-bright);
  text-shadow: 0 0 12px rgba(240,212,136,0.5);
  animation: ts-menu-btn-new-pulse 1.8s ease-in-out infinite;
}
@keyframes ts-menu-btn-new-pulse {
  0%, 100% { text-shadow: 0 0 10px rgba(240,212,136,0.45); }
  50%      { text-shadow: 0 0 18px rgba(240,212,136,0.85), 0 0 28px rgba(240,212,136,0.5); }
}

/* Bond tier-up fanfare — large central card when a pair crosses
   into a new tier (Companions → Kindred → Sworn → Bonded).  Slides
   down from above, holds ~3s, fades out.  Doesn't block input. */
.bond-fanfare {
  position: fixed;
  top: 64px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 260;
  padding: 12px 22px 14px;
  background: linear-gradient(180deg, rgba(48,32,18,0.97) 0%, rgba(14,10,8,0.98) 100%);
  border-top: 2px solid var(--gold-bright);
  border-bottom: 2px solid var(--gold-bright);
  box-shadow:
    0 12px 36px rgba(0,0,0,0.85),
    0 0 36px rgba(240,212,136,0.5),
    inset 0 1px 0 rgba(232,220,196,0.15);
  text-align: center;
  pointer-events: none;
  animation: bond-fanfare-in 0.4s ease-out;
}
.bond-fanfare.bond-fanfare-out { animation: bond-fanfare-out 0.45s ease-in forwards; }
@keyframes bond-fanfare-in {
  from { opacity: 0; transform: translate(-50%, -16px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}
@keyframes bond-fanfare-out {
  from { opacity: 1; transform: translate(-50%, 0); }
  to   { opacity: 0; transform: translate(-50%, -16px); }
}
.bond-fanfare-eyebrow {
  font-size: 9.5px;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--gold-pale);
  opacity: 0.85;
  margin-bottom: 4px;
}
.bond-fanfare-pair {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: -6px;
  height: 56px;
  margin-bottom: 4px;
}
.bond-fanfare-portrait {
  width: 42px; height: 56px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  overflow: hidden;
}
.bond-fanfare-portrait + .bond-fanfare-portrait { margin-left: -8px; }
.bond-fanfare-portrait svg { width: 100%; height: 100%; }
.bond-fanfare-names {
  font-size: 12.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-pale);
  margin-bottom: 2px;
}
.bond-fanfare-tier {
  font-size: 16px;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--gold-bright);
  text-shadow: 0 0 14px rgba(240,212,136,0.65);
  font-weight: 600;
}
.bond-fanfare-charm {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid rgba(240,212,136,0.25);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold-bright);
  font-style: italic;
}
#hud::after {
  /* Faint hairline at the bottom of the HUD so it reads as a strip */
  content: '';
  position: absolute;
  left: 8%; right: 8%;
  bottom: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent 0%, rgba(212,200,168,0.12) 50%, transparent 100%);
  pointer-events: none;
}
#sigil-tray {
  display: flex;
  align-items: center;
  gap: 3px;
  flex-wrap: nowrap;
  min-width: 0;
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: 1px 2px;
  /* Push the tray (and the menu button that follows it) to the right
     edge of the HUD strip. */
  margin-left: auto;
}
#sigil-tray::-webkit-scrollbar { display: none; }
#sigil-tray.empty { display: none; }
.sigil-tray-empty {
  display: inline-flex;
  align-items: center;
  font-size: 10px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-dim);
  opacity: 0.55;
  padding: 7px 12px;
  border: 1px dashed rgba(201,189,159,0.18);
  border-radius: 4px;
  background: rgba(20,18,16,0.35);
  white-space: nowrap;
  font-family: inherit;
}

.sigil-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Anchor for the tier-dot overlay positioned inside the chip. */
  position: relative;
  /* Bumped from 36×36 to a proper mobile tap target so the chip isn't
     fiddly to press during combat.  The icon scales with the chip. */
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid rgba(212,200,168,0.45);
  background:
    radial-gradient(ellipse at 50% 30%, rgba(34,26,18,0.92) 0%, rgba(12,10,8,0.92) 75%);
  color: var(--ink);
  font-family: inherit;
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
  border-radius: 4px;
  flex-shrink: 0;
  box-shadow: 0 2px 6px rgba(0,0,0,0.55), 0 0 0 1px rgba(0,0,0,0.4) inset;
  transition: border-color 0.15s, background 0.15s, transform 0.12s, box-shadow 0.15s;
}
.sigil-chip:hover,
.sigil-chip:active {
  background: rgba(28,28,30,0.95);
  border-color: var(--gold-bright);
  transform: translateY(-1px);
}
.sigil-chip-icon {
  display: block;
  line-height: 1;
  font-size: 22px;
  text-shadow: 0 1px 0 var(--shadow), 0 0 8px rgba(0,0,0,0.5);
}
/* Tier dots — small gold stars in the chip's bottom-right corner.  One
   dot for level 2, two for level 3 (max).  Visual signal that the
   player has compounded the same sigil, without bloating the chip. */
.sigil-chip-tier {
  position: absolute;
  bottom: 1px;
  right: 2px;
  font-size: 7px;
  letter-spacing: -0.5px;
  color: var(--gold-bright);
  text-shadow: 0 0 4px rgba(240,212,136,0.85), 0 1px 0 var(--shadow);
  line-height: 1;
  pointer-events: none;
}
.sigil-chip.sigil-chip-leveled {
  /* Subtle gold ring + brightness boost on leveled chips so they
     register as "powered up" at a glance. */
  box-shadow: 0 2px 6px rgba(0,0,0,0.55), 0 0 0 1px var(--gold-bright) inset, 0 0 8px rgba(240,212,136,0.18);
  border-color: var(--gold-bright);
}
.sigil-chip:hover { box-shadow: 0 2px 8px rgba(0,0,0,0.6), 0 0 12px rgba(212,180,120,0.18), 0 0 0 1px rgba(0,0,0,0.4) inset; }

/* ===========================================================================
   SIGIL CHIP PULSE STATES
   - .sigil-just-bound: one-shot 2.4s reveal cascade fired when the
     sigil first lands in the tray (bindSigil stashes the id).
   - .sigil-relevant: looping subtle pulse while the player holds an
     action that would trigger this sigil.  Calls out the cause/effect
     link without opening a panel.
   =========================================================================== */
@keyframes sigil-bound-pulse {
  0%   { transform: scale(0.6) rotate(-6deg); opacity: 0; box-shadow: 0 0 0 0 rgba(212,180,120,0); }
  35%  { transform: scale(1.18) rotate(2deg); opacity: 1; box-shadow: 0 0 24px 6px rgba(240,212,136,0.55); }
  60%  { transform: scale(1) rotate(0deg);    opacity: 1; box-shadow: 0 0 18px 2px rgba(240,212,136,0.4); }
  100% { transform: scale(1);                  opacity: 1; box-shadow: 0 2px 6px rgba(0,0,0,0.55), 0 0 0 1px rgba(0,0,0,0.4) inset; }
}
.sigil-chip.sigil-just-bound {
  animation: sigil-bound-pulse 2.4s ease-out;
  border-color: var(--gold-bright) !important;
}
.sigil-chip.sigil-just-bound .sigil-chip-icon {
  text-shadow: 0 0 18px rgba(240,212,136,0.85), 0 0 6px rgba(255,240,200,0.7);
}

@keyframes sigil-relevant-pulse {
  0%, 100% {
    transform: scale(1);
    box-shadow: 0 2px 6px rgba(0,0,0,0.55), 0 0 0 1px rgba(0,0,0,0.4) inset;
    border-color: rgba(240,212,136,0.85);
  }
  50% {
    transform: scale(1.10);
    box-shadow: 0 2px 8px rgba(0,0,0,0.55), 0 0 16px 3px rgba(240,212,136,0.5), 0 0 0 1px rgba(0,0,0,0.4) inset;
    border-color: var(--gold-bright);
  }
}
.sigil-chip.sigil-relevant {
  animation: sigil-relevant-pulse 0.95s ease-in-out infinite;
  border-color: var(--gold-bright);
}
.sigil-chip.sigil-relevant .sigil-chip-icon {
  text-shadow: 0 0 14px rgba(240,212,136,0.6), 0 0 4px rgba(255,240,200,0.5);
}
/* Category coloring — border + icon tint */
.sigil-chip.cat-combat   { border-color: rgba(212,69,69,0.55); }
.sigil-chip.cat-combat   .sigil-chip-icon { color: var(--blood-bright); }
.sigil-chip.cat-defense  { border-color: rgba(136,152,168,0.55); }
.sigil-chip.cat-defense  .sigil-chip-icon { color: var(--armor); }
.sigil-chip.cat-resource { border-color: rgba(212,200,168,0.55); }
.sigil-chip.cat-resource .sigil-chip-icon { color: var(--gold-bright); }
/* Squad-bond chips — earned through party composition.  Cool-blue accent
   (matches the sig-tier Resonance palette) so the player can tell them
   apart from bound sigils at a glance. */
.sigil-chip.sigil-chip-squad {
  border-color: rgba(170,200,255,0.55);
  background: linear-gradient(180deg, rgba(28,36,68,0.6) 0%, rgba(10,14,28,0.7) 100%);
}
.sigil-chip.sigil-chip-squad .sigil-chip-icon {
  color: #cfe1ff;
  text-shadow: 0 0 6px rgba(170,200,255,0.55);
}
/* ============ RESOLVE PIPS — small jewels next to the Fight crest ============ */
#resolve-pips {
  /* Diamonds rotate 45deg via transform, so their visual extent reaches
     ~1.6px past each side of the bounding box.  Gap bumped from 3 → 5
     so adjacent diamonds keep a clean breathing space. */
  display: flex; gap: 5px; align-items: center; justify-content: center;
}
.pip {
  /* Resolve pip is a diamond (rotated square).  Matches the ♦ glyph used
     in tile descs, vignette tags, and tutorial copy so the visual
     vocabulary stays one shape.  Was a circle; that read clean on its
     own but disconnected from every other ♦ reference in the UI. */
  width: 9px; height: 9px;
  border-radius: 1px;
  transform: rotate(45deg);
  border: 1px solid var(--gold-dim);
  background: radial-gradient(circle at 35% 30%, #221812 0%, #0a0604 100%);
  position: relative;
  transition: border-color 0.25s, background 0.25s, box-shadow 0.25s;
}
.pip.filled {
  border-color: var(--gold-bright);
  background: radial-gradient(circle at 35% 30%, #fff0b8 0%, var(--gold-bright) 40%, var(--gold) 75%, #6a4818 100%);
  box-shadow:
    0 0 5px var(--gold-bright),
    0 0 10px rgba(240,212,136,0.4),
    inset 0 0 2px rgba(255,240,184,0.7);
}
/* "reserved" — slotted to be spent on the next Fight commit */
.pip.filled.reserved {
  border-color: var(--gold-dim);
  background: radial-gradient(circle at 35% 30%, #4a3820 0%, #2a1f10 50%, #0a0604 100%);
  box-shadow: none;
  opacity: 0.6;
  filter: saturate(0.4);
}
/* "bonus" pip — the 4th unit that appears when Coin of Memory carried
   the player over the normal cap.  Slightly larger and tinted with a
   brighter gold glow so the buff is visible.  Drops away the moment
   it's spent back below the cap. */
.pip.bonus {
  width: 11px; height: 11px;
  border-color: rgba(255,232,160,0.9);
  box-shadow:
    0 0 6px rgba(255,232,160,0.85),
    0 0 14px rgba(240,212,136,0.55);
}
.pip.bonus.filled {
  background: radial-gradient(circle at 35% 30%, #fff5d0 0%, #ffce5a 35%, #b88820 75%, #6a4818 100%);
}

/* ============ COMMIT ZONE — Resolve pips above the Fight crest ============ */
#commit-zone {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: center;
  gap: 6px;
  height: 100%;
  min-width: 0;
}
#commit-zone .fight-btn { flex: 1 1 auto; }

/* ============ ARENA ============ */
/* ============ BATTLEFIELD — party stacks (card+tiles) | enemy cards ============ */
/* Objective banner — slim chip at the BOTTOM-CENTER of the battlefield
   so it doesn't crowd the HP bars / intent bubbles at the top of each
   figure.  Sits in the empty floor area between party feet and the
   action tray; semi-transparent so it never feels in the way. */
#objective-banner {
  /* Inline HUD chip — was a wide bottom-strip banner, then a top-left
     corner badge that overlapped the leftmost figure.  Now lives in
     the HUD strip alongside the biome chip so it never sits on top of
     anything in the battlefield. */
  position: relative;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 4px 10px;
  min-width: 32px;
  min-height: 28px;
  background: rgba(8,6,8,0.78);
  border: 1px solid rgba(212,200,168,0.32);
  border-radius: 14px;
  white-space: nowrap;
  pointer-events: auto;
  cursor: pointer;
  animation: ob-pulse 2.4s ease-in-out infinite;
  transition: transform 0.12s, border-color 0.15s;
}
#objective-banner:hover { transform: scale(1.04); border-color: var(--gold-bright); }
#objective-banner:active { transform: scale(0.96); }
#objective-banner.hidden { display: none; }
#objective-banner .ob-glyph {
  font-size: 14px;
  color: var(--gold-bright);
  text-shadow: 0 0 8px rgba(240,212,136,0.6);
}
#objective-banner .ob-label {
  /* No longer rendered in the compact chip (label moved to the splash);
     kept for fallback / older callers. */
  display: none;
}
#objective-banner .ob-hint {
  font-size: 9px;
  font-style: italic;
  color: var(--ink-dim);
  border-left: 1px solid rgba(212,200,168,0.18);
  padding-left: 8px;
}
@keyframes ob-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(240,212,136,0); }
  50%      { box-shadow: 0 0 14px 0 rgba(240,212,136,0.32); }
}
/* Ticking variant: red palette + faster pulse + a charge counter chip
   to the right of the label.  Conveys urgency without being shouty. */
#objective-banner.ob-ticking {
  border-color: rgba(220,80,80,0.55);
  animation: ob-pulse-ticking 1.6s ease-in-out infinite;
}
#objective-banner.ob-ticking .ob-glyph {
  color: var(--blood-bright);
  text-shadow: 0 0 10px rgba(220,80,80,0.7);
}
#objective-banner.ob-ticking .ob-label { color: #f6d3c3; }
#objective-banner .ob-counter {
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.04em;
  padding: 0 2px 0 4px;
  color: var(--blood-bright);
  text-shadow: 0 1px 0 rgba(0,0,0,0.95), 0 0 8px rgba(220,80,80,0.65);
}
@keyframes ob-pulse-ticking {
  0%, 100% { box-shadow: 0 0 0 0 rgba(220,80,80,0); }
  50%      { box-shadow: 0 0 16px 0 rgba(220,80,80,0.4); }
}
/* Survive variant: cool steel/blue palette + slow pulse — endurance,
   not urgency.  Counter chip shows turns remaining. */
#objective-banner.ob-survive {
  border-color: rgba(170,200,255,0.5);
  animation: ob-pulse-survive 2.6s ease-in-out infinite;
}
#objective-banner.ob-survive .ob-glyph {
  color: #cfe1ff;
  text-shadow: 0 0 8px rgba(170,200,255,0.6);
}
#objective-banner.ob-survive .ob-label { color: #d8e4f6; }
#objective-banner.ob-survive .ob-counter {
  color: #cfe1ff;
  text-shadow: 0 1px 0 rgba(0,0,0,0.95), 0 0 8px rgba(170,200,255,0.6);
}
@keyframes ob-pulse-survive {
  0%, 100% { box-shadow: 0 0 0 0 rgba(170,200,255,0); }
  50%      { box-shadow: 0 0 14px 0 rgba(170,200,255,0.35); }
}

/* ===========================================================================
   OBJECTIVE SPLASH — centered fight-start banner.  Reads the objective
   name + hint as a moment, then auto-dismisses (or taps away).  Replaces
   the persistent wide banner that used to overlap the character labels.
   =========================================================================== */
#objective-splash {
  position: fixed;
  inset: 0;
  z-index: 320;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: auto;
  opacity: 0;
  transition: opacity 0.24s ease-out;
}
#objective-splash.obs-in  { opacity: 1; }
#objective-splash.obs-out { opacity: 0; pointer-events: none; }
#objective-splash .obs-card {
  background:
    radial-gradient(ellipse at 50% 0%, rgba(36,28,22,0.96) 0%, rgba(12,10,10,0.96) 70%);
  border: 1px solid rgba(212,200,168,0.45);
  padding: 18px 28px 16px;
  max-width: min(520px, 88vw);
  text-align: center;
  border-radius: 4px;
  box-shadow: 0 20px 48px rgba(0,0,0,0.7), inset 0 1px 0 rgba(232,220,196,0.08);
  transform: translateY(-6px);
  animation: obs-in 0.32s ease-out forwards;
}
#objective-splash.obs-out .obs-card { animation: obs-out 0.28s ease-in forwards; }
@keyframes obs-in {
  from { transform: translateY(-14px); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}
@keyframes obs-out {
  to { transform: translateY(-6px); opacity: 0; }
}
#objective-splash .obs-eyebrow {
  font-size: 9px;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 8px;
}
#objective-splash .obs-row {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 6px;
}
#objective-splash .obs-glyph {
  font-size: 24px;
  color: var(--gold-bright);
  text-shadow: 0 0 14px rgba(240,212,136,0.6);
}
#objective-splash .obs-label {
  font-size: 16px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--gold-bright);
  text-shadow: 0 0 10px rgba(240,212,136,0.35);
}
#objective-splash .obs-hint {
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--ink);
  margin-top: 6px;
  max-width: 460px;
  margin-left: auto;
  margin-right: auto;
  font-style: italic;
  letter-spacing: 0.02em;
}
/* Ticking variant — blood-tint glyph + label so the urgency reads at a glance. */
#objective-splash.obs-ticking .obs-glyph,
#objective-splash.obs-ticking .obs-label {
  color: var(--blood-bright);
  text-shadow: 0 0 12px rgba(220,80,80,0.55);
}
#objective-splash.obs-ticking .obs-card { border-color: rgba(220,80,80,0.55); }
/* Survive variant — steel/blue palette to match the corner badge. */
#objective-splash.obs-survive .obs-glyph,
#objective-splash.obs-survive .obs-label {
  color: #cfe1ff;
  text-shadow: 0 0 12px rgba(170,200,255,0.55);
}
#objective-splash.obs-survive .obs-card { border-color: rgba(170,200,255,0.5); }
/* Mobile landscape — shrink padding so the card fits above the fold. */
@media (max-height: 520px) {
  #objective-splash .obs-card { padding: 12px 22px 12px; max-width: min(480px, 92vw); }
  #objective-splash .obs-eyebrow { font-size: 8px; margin-bottom: 4px; }
  #objective-splash .obs-glyph { font-size: 20px; }
  #objective-splash .obs-label { font-size: 13px; letter-spacing: 0.26em; }
  #objective-splash .obs-hint { font-size: 10.5px; margin-top: 4px; }
}

/* ===========================================================================
   FIGURE INSPECTOR — the press-and-hold panel that explains the icons
   above a character's head.  Attaches to the figure itself so it scrolls
   with the battlefield; auto-removes when the inspect gesture ends.
   =========================================================================== */
.figure-inspector {
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translate(-50%, 4px);
  z-index: 25;
  width: max-content;
  max-width: min(280px, 80vw);
  padding: 8px 10px;
  background: rgba(8,6,8,0.96);
  border: 1px solid rgba(212,200,168,0.4);
  border-radius: 3px;
  box-shadow: 0 8px 22px rgba(0,0,0,0.7), 0 0 0 1px rgba(0,0,0,0.5) inset;
  pointer-events: none;
  animation: fi-in 0.14s ease-out;
}
/* Floating variant — panel is appended to #popup-layer instead of the
   figure, and its left/top are stamped inline.  The base top:100% /
   left:50% rules above don't apply when an inline value is set, but
   the transform from those base rules would re-center the box; the JS
   sets transform inline too (translate(-50%, 0) or -100%) to anchor
   the panel correctly below or above the figure. */
.figure-inspector.figure-inspector-floating {
  top: auto;
  left: auto;
}
@keyframes fi-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.figure-inspector .fi-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding-bottom: 6px;
  margin-bottom: 6px;
  border-bottom: 1px solid rgba(212,200,168,0.18);
}
.figure-inspector .fi-name {
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-bright);
  font-weight: 600;
}
.figure-inspector .fi-slot {
  font-size: 8.5px;
  letter-spacing: 0.24em;
  color: var(--ink-dim);
}
.figure-inspector .fi-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.figure-inspector .fi-row {
  display: grid;
  grid-template-columns: 26px 1fr;
  gap: 8px;
  align-items: start;
}
.figure-inspector .fi-icon {
  font-size: 13px;
  color: var(--gold-bright);
  text-shadow: 0 0 6px rgba(240,212,136,0.35);
  text-align: center;
  line-height: 1.2;
}
.figure-inspector .fi-num {
  font-size: 9.5px;
  color: var(--ink);
  font-weight: 600;
}
.figure-inspector .fi-text {
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--ink);
}
.figure-inspector .fi-row-passive .fi-icon { color: var(--gold-pale); }
.figure-inspector .fi-row-passive .fi-text b { color: var(--gold-bright); letter-spacing: 0.06em; }
.figure-inspector .fi-empty {
  font-size: 10.5px;
  color: var(--ink-dim);
  font-style: italic;
  text-align: center;
}
/* Floating-variant flip is decided in JS now (based on the figure's
   bottom Y in design-canvas coords), so this media query only retunes
   the legacy in-figure variant.  The floating variant carries inline
   left/top/transform that always win over these rules. */
@media (max-height: 520px) {
  .figure-inspector:not(.figure-inspector-floating) {
    top: auto;
    bottom: 100%;
    transform: translate(-50%, -4px);
    max-width: min(240px, 78vw);
    padding: 6px 8px;
  }
  .figure-inspector.figure-inspector-floating {
    max-width: min(240px, 78vw);
    padding: 6px 8px;
  }
  .figure-inspector .fi-name { font-size: 10px; }
  .figure-inspector .fi-slot { font-size: 8px; }
  .figure-inspector .fi-text { font-size: 9.5px; }
}

/* Priority Target — gold crown overhead + brighter ring underfoot so the
   marked enemy is unmistakable. */
.figure.priority-target::before {
  content: '◆';
  position: absolute;
  top: -2px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 16px;
  color: var(--gold-bright);
  text-shadow:
    0 1px 0 rgba(0,0,0,0.95),
    0 0 6px rgba(0,0,0,0.85),
    0 0 12px rgba(240,212,136,0.7);
  z-index: 6;
  pointer-events: none;
  animation: priority-pulse 1.6s ease-in-out infinite;
}
.figure.priority-target .figure-shadow {
  box-shadow: 0 0 22px 4px rgba(240,212,136,0.4);
}
@keyframes priority-pulse {
  0%, 100% { transform: translateX(-50%) translateY(0)    scale(1);    opacity: 0.85; }
  50%      { transform: translateX(-50%) translateY(-2px) scale(1.12); opacity: 1; }
}

/* Catalyst (Ticking Threat) — red flame mark overhead carrying the
   remaining charge count, plus a hot floor halo.  Faster pulse so it
   reads as urgent. */
.figure.ticking-target::before {
  content: '✸ ' attr(data-charge-left);
  position: absolute;
  top: -4px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--blood-bright);
  text-shadow:
    0 1px 0 rgba(0,0,0,0.95),
    0 0 6px rgba(0,0,0,0.85),
    0 0 14px rgba(220,80,80,0.85);
  z-index: 6;
  pointer-events: none;
  animation: ticking-pulse 1.0s ease-in-out infinite;
}
.figure.ticking-target .figure-shadow {
  box-shadow: 0 0 24px 4px rgba(220,80,80,0.45);
}
@keyframes ticking-pulse {
  0%, 100% { transform: translateX(-50%) translateY(0)    scale(1);    opacity: 0.85; }
  50%      { transform: translateX(-50%) translateY(-3px) scale(1.16); opacity: 1; }
}

#battlefield {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr;
  /* No gap between halves — the center sigil + glow line do the job of
     dividing party from enemies, and tighter halves let the figures
     fill more of the frame instead of crowding the outer edges. */
  gap: 0;
  padding: 0 2px;
  position: relative; z-index: 2;
  min-height: 0;
  /* overflow:visible so the figure-inspector panel (anchored at top:100%
     of each figure card) can extend into the action-row area; the
     inspector has its own z-index so it sits over the tiles when open. */
  overflow: visible;
}
/* faint glowing arcane sigil between the two halves */
#battlefield::before {
  content: '';
  position: absolute;
  left: 50%; top: 20%; bottom: 20%;
  width: 1px;
  transform: translateX(-50%);
  background: linear-gradient(180deg, transparent, var(--gold-dim) 30%, var(--gold) 50%, var(--gold-dim) 70%, transparent);
  opacity: 0.22;
  pointer-events: none; z-index: 0;
}
#battlefield::after {
  content: '';
  position: absolute;
  left: 50%; top: 50%;
  width: 90px; height: 90px;
  transform: translate(-50%, -50%);
  background:
    radial-gradient(circle, rgba(200,164,100,0.16) 0%, rgba(200,164,100,0.04) 40%, transparent 70%);
  pointer-events: none; z-index: 0;
  animation: sigil-breathe 5s ease-in-out infinite;
}
@keyframes sigil-breathe {
  0%, 100% { opacity: 0.8; transform: translate(-50%, -50%) scale(1); }
  50%      { opacity: 1;   transform: translate(-50%, -50%) scale(1.08); }
}

#party-half, #enemy-half {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: 1fr;
  gap: 2px;
  min-height: 0;
  position: relative; z-index: 2;
  /* stone ground line under the figures */
}
#party-half::after, #enemy-half::after {
  content: '';
  position: absolute;
  left: 6%; right: 6%;
  bottom: 20px;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--gold-deep) 20%, var(--gold-dim) 50%, var(--gold-deep) 80%, transparent);
  opacity: 0.25;
  pointer-events: none;
}

/* ============ FIGURE — standing character on the battlefield (no card frame) ============
   No corner brackets — they made the battlefield feel busy. Selection state
   reads from the portrait halo / scale / dim from elsewhere. */
.figure {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  min-width: 0; min-height: 0;
  padding: 0 0 1px;
  cursor: pointer;
}
.figure.empty { visibility: hidden; }
.figure.downed { opacity: 0.4; filter: grayscale(1) brightness(0.65); }

/* ===========================================================================
   SHADE FIGURES — Necromancer's risen heroes at L9.  Each Shade wraps
   a specific hero's portrait; the kind class tints the visual: fallen
   (your dead party) reads gold/heroic, roadkill (heroes you killed)
   reads red/vengeful, echo (generic fallback) reads cold/neutral.
   =========================================================================== */
.figure-shade .figure-portrait svg {
  /* Base spectral treatment — slightly translucent, soft glow, slow
     flicker.  Kind classes below override the colour cast. */
  filter: grayscale(0.55) brightness(0.62) drop-shadow(0 0 6px rgba(180,200,255,0.5));
  opacity: 0.88;
  animation: shade-flicker 2.6s ease-in-out infinite;
}
.figure-shade.shade-fallen .figure-portrait svg {
  /* Heroic gold — these are YOUR dead, dragged back against you. */
  filter: grayscale(0.4) brightness(0.6) sepia(0.4) hue-rotate(-10deg) saturate(1.2)
          drop-shadow(0 0 10px rgba(240,200,140,0.65));
}
.figure-shade.shade-roadkill .figure-portrait svg {
  /* Vengeful red — heroes whose blood is on your road. */
  filter: grayscale(0.45) brightness(0.55) sepia(0.6) hue-rotate(-30deg) saturate(1.8)
          drop-shadow(0 0 10px rgba(220,80,80,0.7));
}
.figure-shade.shade-echo .figure-portrait svg {
  /* Cold neutral blue — generic echo, "what could have been". */
  filter: grayscale(0.65) brightness(0.55) hue-rotate(200deg) saturate(0.85)
          drop-shadow(0 0 8px rgba(180,200,255,0.55));
}
@keyframes shade-flicker {
  0%, 100% { opacity: 0.88; }
  50%      { opacity: 0.62; }
}
/* Subtle aura halo behind a shade — sells "raised by dark magic" */
.figure-shade .figure-portrait::after {
  content: '';
  position: absolute;
  left: 50%; bottom: 4%;
  width: 70%; height: 14%;
  transform: translateX(-50%);
  background: radial-gradient(ellipse 50% 50% at 50% 50%,
                              rgba(180,140,220,0.45) 0%,
                              rgba(180,140,220,0) 75%);
  pointer-events: none;
  z-index: -1;
  animation: shade-aura 2.6s ease-in-out infinite;
}
.figure-shade.shade-fallen .figure-portrait::after {
  background: radial-gradient(ellipse 50% 50% at 50% 50%,
                              rgba(240,200,140,0.5) 0%,
                              rgba(240,200,140,0) 75%);
}
.figure-shade.shade-roadkill .figure-portrait::after {
  background: radial-gradient(ellipse 50% 50% at 50% 50%,
                              rgba(220,80,80,0.55) 0%,
                              rgba(220,80,80,0) 75%);
}
@keyframes shade-aura {
  0%, 100% { opacity: 0.5; transform: translateX(-50%) scale(1); }
  50%      { opacity: 0.85; transform: translateX(-50%) scale(1.1); }
}

.figure-portrait {
  flex: 1 1 auto;
  width: 100%;
  max-width: 420px;
  min-height: 0;
  position: relative;
  filter:
    drop-shadow(0 8px 10px rgba(0,0,0,0.75))
    drop-shadow(0 2px 4px rgba(0,0,0,0.6));
  display: flex; align-items: flex-end; justify-content: center;
}
.figure-portrait svg {
  width: 100%; height: 100%;
  display: block;
  /* mask: push focal point lower + extend visible radius so the figure
     occupies more of its allotted vertical space and there's less black
     fade between the visible figure and the info chips below it. */
  -webkit-mask-image: radial-gradient(ellipse 98% 118% at 50% 60%, #000 84%, transparent 100%);
          mask-image: radial-gradient(ellipse 98% 118% at 50% 60%, #000 84%, transparent 100%);
  transform-origin: 50% 100%;
  animation: idle-breathe 5.4s ease-in-out infinite;
}
#enemy-half .figure-portrait svg { animation-name: idle-breathe-mirror; transform: scaleX(-1); }

/* soft ground shadow ellipse under each figure */
.figure-shadow {
  width: 70%;
  max-width: 160px;
  height: 4px;
  margin: -1px auto 1px;
  background:
    radial-gradient(ellipse, rgba(0,0,0,0.72) 0%, rgba(0,0,0,0.3) 50%, transparent 80%);
  pointer-events: none;
  flex-shrink: 0;
  filter: blur(0.5px);
}
.figure.downed .figure-shadow { opacity: 0.4; }

.figure-info {
  width: 100%;
  max-width: 200px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
  flex-shrink: 0;
  position: relative;
  /* tuck the info row up under the portrait's faded edge so HP bar +
     affinity chips read as part of the figure, not floating below it */
  margin-top: -12px;
}
/* Name hidden by default — floats below the figure when the figure is being held
   ("inspected"). Absolute-positioned so it doesn't reserve layout space. */
.figure-name {
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  font-size: 9px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ink);
  font-weight: 600;
  text-shadow: 0 1px 0 var(--shadow), 0 0 6px rgba(0,0,0,0.7);
  margin-top: 2px;
  opacity: 0;
  transition: opacity 0.15s ease-out;
  pointer-events: none;
  z-index: 7;
}
.figure.inspecting .figure-name { opacity: 1; }
.figure-name.home { color: var(--gold-bright); text-shadow: 0 0 8px rgba(240,212,136,0.5), 0 1px 0 var(--shadow); }

/* ============ HOLD-TO-INSPECT / MOVE PICKER ============ */
.figure { cursor: pointer; touch-action: none; }

/* "Pick up" feel — figure lifts + scales up + glows while held */
.figure.inspecting {
  transform: scale(1.05) translateY(-3px);
  z-index: 6;
  filter: drop-shadow(0 6px 12px rgba(0,0,0,0.85)) drop-shadow(0 0 18px var(--gold-bright));
  transition: transform 0.14s ease-out, filter 0.14s ease-out;
}

/* Move arrows — only on party figures, only visible while inspecting.
   NieR-flat: thin gold chevron with drop-shadow legibility, no heavy
   gold-coin circle.  Aim state thickens + brightens the glow. */
.move-arrow {
  position: absolute;
  top: 38%;
  width: 56px; height: 56px;
  padding: 0;
  border-radius: 0;
  border: none;
  background: transparent;
  color: var(--gold-bright);
  font-size: 48px;
  line-height: 1;
  display: flex; align-items: center; justify-content: center;
  z-index: 10;
  filter:
    drop-shadow(0 1px 0 rgba(0,0,0,0.95))
    drop-shadow(0 0 5px rgba(0,0,0,0.85))
    drop-shadow(0 0 10px rgba(240,212,136,0.5));
  transform: translateY(-50%) scale(0.4);
  font-family: inherit;
  font-weight: 200;
  opacity: 0;
  pointer-events: none;
  transition: transform 0.18s cubic-bezier(.4,1.4,.6,1), opacity 0.14s, filter 0.12s;
}
.move-arrow-left  { left: -34px; }
.move-arrow-right { right: -34px; }
.figure.inspecting .move-arrow {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(-50%) scale(1);
  animation: move-arrow-pulse 1.4s ease-in-out infinite;
}
.move-arrow.aim {
  color: var(--gold-pale);
  filter:
    drop-shadow(0 1px 0 rgba(0,0,0,0.95))
    drop-shadow(0 0 4px rgba(0,0,0,0.9))
    drop-shadow(0 0 14px rgba(240,212,136,0.85));
  animation: none;
  transform: translateY(-50%) scale(1.18);
}
@keyframes move-arrow-pulse {
  0%, 100% {
    transform: translateY(-50%) scale(1);
    filter:
      drop-shadow(0 1px 0 rgba(0,0,0,0.95))
      drop-shadow(0 0 4px rgba(0,0,0,0.85))
      drop-shadow(0 0 6px rgba(240,212,136,0.35));
  }
  50% {
    transform: translateY(-50%) scale(1.08);
    filter:
      drop-shadow(0 1px 0 rgba(0,0,0,0.95))
      drop-shadow(0 0 4px rgba(0,0,0,0.85))
      drop-shadow(0 0 12px rgba(240,212,136,0.6));
  }
}

/* persistent direction indicator on a figure once a move is queued */
.figure-queued-move {
  position: absolute;
  top: 6%;
  left: 50%;
  transform: translateX(-50%);
  width: 22px; height: 22px;
  border-radius: 0;
  background: transparent;
  color: var(--gold-pale);
  font-size: 20px;
  font-weight: 200;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 9;
  filter:
    drop-shadow(0 1px 0 rgba(0,0,0,0.95))
    drop-shadow(0 0 4px rgba(0,0,0,0.85))
    drop-shadow(0 0 8px rgba(240,212,136,0.55));
  pointer-events: none;
  animation: queued-move-pulse 1.8s ease-in-out infinite;
}
@keyframes queued-move-pulse {
  0%, 100% { opacity: 0.92; }
  50%      { opacity: 1; }
}

/* "·HOLD·" affordance shown beneath each party hero until the player has
   completed their first press-and-hold pickup.  Pure teaching nudge; the
   gold glow + slow blink hint that the card is a long-press target. */
.fig-hold-hint {
  position: absolute;
  bottom: -10px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 7px;
  font-weight: bold;
  letter-spacing: 0.32em;
  color: var(--gold-bright);
  text-shadow:
    0 1px 0 rgba(0,0,0,0.95),
    0 0 6px rgba(240,212,136,0.6);
  pointer-events: none;
  z-index: 6;
  opacity: 0.78;
  animation: fig-hold-blink 2.2s ease-in-out infinite;
  white-space: nowrap;
}
@keyframes fig-hold-blink {
  0%, 100% { opacity: 0.45; transform: translateX(-50%) scale(0.98); }
  50%      { opacity: 0.95; transform: translateX(-50%) scale(1.02); }
}
.figure.inspecting .fig-hold-hint { opacity: 0; }

/* HP bar reuses .hp-bar / .hp-fill / .hp-text from below — keep it the same look but constrained to the info width */
/* Minimal HP readout, anchored just above the figure's head.
   .figure-hp is rendered INSIDE .figure-portrait so absolute positioning is
   relative to the portrait box.  The portrait mask centers visible content
   around 60% from top — the head edge fades in at roughly 8–10% — so
   top: 8% places the bar just above the visible head.
   Layout:  [ ===---- ] 5/8
*/
.figure-hp {
  position: absolute;
  top: 27px;
  left: 50%;
  transform: translateX(-50%);
  width: 44px;
  height: 3px;
  background: rgba(10,10,12,0.72);
  border: none;
  border-radius: 0;
  overflow: visible;
  z-index: 6;
  pointer-events: none;
}
.figure-hp::before,
.figure-hp::after {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  font-family: ui-monospace, 'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace;
  font-size: 10px;
  line-height: 1;
  color: var(--ink-dim);
  pointer-events: none;
}
.figure-hp::before { content: '['; right: calc(100% + 2px); }
.figure-hp::after  { content: ']'; left:  calc(100% + 2px); }
.figure-hp .hp-fill {
  height: 100%;
  background: var(--ink);
  transition: width 0.4s ease-out;
  box-shadow: none;
  position: relative;
  z-index: 1;
}
.figure-hp .hp-fill.low {
  background: var(--hp-low);
  animation: hp-pulse 1.2s ease-in-out infinite;
}
/* HP numeric "5/8" — current/total, pushed past the right bracket.
   Overrides the visually-hidden clip rules earlier in the file. */
.figure-hp .hp-text {
  /* Sits centered directly UNDER the bar so it never clips against the
     viewport edge on the back-slot enemy.  Reads cleanly under the
     [ === ] track. */
  position: absolute;
  top: calc(100% + 3px);
  left: 50%;
  transform: translateX(-50%);
  width: auto;
  height: auto;
  clip: auto;
  overflow: visible;
  white-space: nowrap;
  font-size: 10px;
  line-height: 1;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-shadow: 0 1px 0 var(--shadow), 0 0 3px rgba(0,0,0,0.7);
  pointer-events: none;
}

/* Weakness reveal icon — small school glyph under the HP bar on enemies.
   Renders once weaknessRevealed flips true (first damaging hit or any
   Hask hit via Frostbreak passive). */
.figure .weakness-icon {
  /* Top-right corner of the portrait so it can co-exist with the
     bottom .state-strip label without overlap. */
  position: absolute;
  top: 4px; right: 4px;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 6px;
  background: rgba(8,6,4,0.82);
  border: 1px solid currentColor;
  border-radius: 4px;
  color: var(--gold-pale);
  text-shadow: 0 0 6px currentColor, 0 1px 0 rgba(0,0,0,0.9);
  pointer-events: auto;
  cursor: help;
  opacity: 0.95;
  letter-spacing: 0;
  z-index: 6;
  white-space: nowrap;
  box-shadow: 0 2px 6px rgba(0,0,0,0.5);
}
.figure .weakness-icon .wi-glyph { font-size: 14px; line-height: 1; }
.figure .weakness-icon .wi-text  { font-size: 9.5px; line-height: 1; font-weight: 800; letter-spacing: 0.16em; }
/* One-shot reveal animation — applied via .fresh class for ~1s after the
   first hit lands so the discovery is impossible to miss.  Animation only
   runs on classes added via JS (no infinite loop on subsequent renders). */
.figure .weakness-icon.fresh {
  animation: weakness-reveal 0.9s ease-out 1;
}
@keyframes weakness-reveal {
  0%   { transform: scale(0.3); opacity: 0; filter: brightness(2); }
  35%  { transform: scale(1.35); opacity: 1; filter: brightness(1.6); }
  70%  { transform: scale(0.95); }
  100% { transform: scale(1); opacity: 0.95; filter: brightness(1); }
}
/* Per-school tint so the weakness chip reads the same color as the
   matching action-tile element badge — visual handshake between them. */
.figure .weakness-icon-physical { color: #d8c2a0; }
.figure .weakness-icon-holy     { color: #f4e6b8; }
.figure .weakness-icon-arcane   { color: #c9a8e8; }
.figure .weakness-icon-ranged   { color: #a8c8a0; }
.figure .weakness-icon-stealth  { color: #a8b8d0; }

/* State strip — labeled WEAKENED / STAGGERED banner under the HP bar.
   Wider than the old corner pip and carries the next-step hint so the
   stagger loop reads directly off the enemy figure without having to
   hover a tooltip. */
.figure .state-strip {
  /* Anchored inside the portrait at the bottom edge — used to float in
     negative-margin space below the figure where it read as a footnote. */
  position: absolute;
  bottom: 4px; left: 50%;
  transform: translateX(-50%);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 4px 10px;
  white-space: nowrap;
  pointer-events: auto;
  cursor: help;
  z-index: 7;
  border-radius: 3px;
  font-weight: 700;
  box-shadow: 0 2px 8px rgba(0,0,0,0.55);
}
.figure .state-strip-weakened {
  color: #f4c0c0;
  background: linear-gradient(180deg, rgba(60,18,18,0.92) 0%, rgba(28,8,8,0.92) 100%);
  border: 1px solid rgba(220,80,80,0.55);
  text-shadow: 0 0 6px rgba(220,80,80,0.55), 0 1px 0 rgba(0,0,0,0.9);
  animation: state-strip-weak-pulse 1.6s ease-in-out infinite;
}
.figure .state-strip-staggered {
  color: #fff3c8;
  background: linear-gradient(180deg, rgba(60,40,12,0.94) 0%, rgba(28,18,4,0.94) 100%);
  border: 1px solid var(--gold-bright);
  text-shadow: 0 0 8px rgba(240,212,136,0.7), 0 1px 0 rgba(0,0,0,0.9);
  animation: state-strip-stagger-pulse 0.7s ease-in-out infinite alternate;
}
/* Weakness reveal — shares the bottom info-strip anchor but uses a
   tighter, glyph-only badge.  WEAKENED / STAGGERED state takes the slot
   when active; this is the quiet "you found a weakness" indicator that
   sits under the figure once the player lands a matching-school hit.
   School-tinted glow carries the identity. */
.figure .state-strip-weakness {
  /* Override the wider state-strip pill — this is a compact badge. */
  padding: 2px 7px;
  font-size: 13px;
  letter-spacing: 0;
  text-transform: none;
  border-radius: 10px;
  background: rgba(10,8,6,0.92);
  text-shadow: 0 0 8px currentColor, 0 1px 0 rgba(0,0,0,0.9);
  font-weight: 700;
}
.figure .state-strip-weakness.weakness-physical { color: #f0d8a8; border: 1px solid #d8c2a0; }
.figure .state-strip-weakness.weakness-holy     { color: #fbeec5; border: 1px solid #f4e6b8; }
.figure .state-strip-weakness.weakness-arcane   { color: #e7c5ff; border: 1px solid #c9a8e8; }
.figure .state-strip-weakness.weakness-ranged   { color: #c8e0bb; border: 1px solid #a8c8a0; }
.figure .state-strip-weakness.weakness-stealth  { color: #c8d4ec; border: 1px solid #a8b8d0; }
/* First-reveal scale pulse — one-shot via the .fresh class added by JS
   after weaknessRevealed flips true. */
.figure .state-strip-weakness.fresh {
  animation: state-strip-weakness-reveal 0.85s ease-out 1;
}
@keyframes state-strip-weakness-reveal {
  0%   { transform: translateX(-50%) scale(0.3); opacity: 0; filter: brightness(2); }
  35%  { transform: translateX(-50%) scale(1.18); opacity: 1; filter: brightness(1.6); }
  70%  { transform: translateX(-50%) scale(0.96); }
  100% { transform: translateX(-50%) scale(1);    opacity: 1; filter: brightness(1); }
}
@keyframes state-strip-weak-pulse {
  0%, 100% { filter: brightness(1); }
  50%      { filter: brightness(1.15); }
}
@keyframes state-strip-stagger-pulse {
  from { filter: brightness(1)    drop-shadow(0 0 4px rgba(240,212,136,0.4)); }
  to   { filter: brightness(1.25) drop-shadow(0 0 12px rgba(240,212,136,0.85)); }
}

@media (max-width: 720px) {
  .figure .state-strip   { font-size: 10px; padding: 3px 8px; letter-spacing: 0.12em; }
}

/* Old corner state pips removed — the WEAKENED / STAGGERED state now
   renders as a labeled .state-strip below the HP bar (see above), which
   carries the next-step hint and reads without hover. */

/* Status chips — horizontal row centered above the HP bar, inside the
   portrait so they anchor near the figure's head, not the cell top.
   Offsets sit so the stack OVERLAPS the head a little rather than floating
   in empty space above it (z-index layers them above the portrait SVG). */
.figure-statuses {
  position: absolute;
  /* Sits above the HP bar (which lives at top: 27px).  With the larger 22px
     chips a top: 2px anchor keeps the chip bottom at 24px, clear of the bar.
     Mobile breakpoint above shrinks chips to 19px so the same offset works. */
  top: 2px;
  left: 50%;
  transform: translateX(-50%);
  display: flex; flex-direction: row;
  justify-content: center; align-items: center;
  gap: 4px;
  z-index: 7;
  pointer-events: none;
  /* Side-by-side, never stacked: chips that don't fit overflow horizontally
     past the card edge.  Wrapping caused a second row to crash into the HP
     bar at top: 27px which "broke" the layout.  Card width is wider than
     the portrait so a couple of overflow chips read fine. */
  max-width: none;
  flex-wrap: nowrap;
  white-space: nowrap;
}
.figure-statuses .status-chip { flex-shrink: 0; }

/* ============ INTENT BUBBLE — minimal floating glyph above enemy figure ============ */
/* Intent bubble — rendered inside .figure-portrait so it anchors to the
   portrait box and sits just below the HP bar (which is at top: 8%).
   Floats as glyph + number with drop-shadow legibility — no plate, no
   border, just the symbols against the battlefield.  Threat color comes
   from the glyph + number themselves. */
.intent-bubble {
  position: absolute;
  /* Sits below the HP bar AND the new under-bar HP number.  Bar bottom is
     ~30px, HP-text spans ~33–46px, so 52px leaves clean breathing room. */
  top: 52px;
  left: 50%;
  transform: translateX(-50%);
  color: var(--blood-bright);
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 0;
  background: transparent;
  border: none;
  box-shadow: none;
  z-index: 5;
  white-space: nowrap;
  pointer-events: none;
}
.intent-bubble .intent-icon {
  font-size: 16px;
  line-height: 1;
  color: var(--blood-bright);
  filter:
    drop-shadow(0 1px 0 rgba(0,0,0,0.95))
    drop-shadow(0 0 3px rgba(0,0,0,0.85))
    drop-shadow(0 0 8px rgba(220,80,80,0.45));
}
.intent-bubble .intent-num {
  font-size: 17px;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  color: #f1d9c4;
  text-shadow:
    0 1px 0 rgba(0,0,0,0.95),
    0 0 4px rgba(0,0,0,0.85),
    0 0 10px rgba(220,80,80,0.35);
}
.intent-bubble.intent-aoe .intent-icon {
  color: var(--aoe);
  filter:
    drop-shadow(0 1px 0 rgba(0,0,0,0.95))
    drop-shadow(0 0 3px rgba(0,0,0,0.85))
    drop-shadow(0 0 8px rgba(180,120,220,0.5));
}
.intent-bubble.intent-debuff .intent-icon {
  color: var(--debuff);
  filter:
    drop-shadow(0 1px 0 rgba(0,0,0,0.95))
    drop-shadow(0 0 3px rgba(0,0,0,0.85))
    drop-shadow(0 0 8px rgba(120,140,200,0.5));
}
/* Wind-up bubble — fires while an enemy is charging a heavy attack.
   Bright red hourglass + countdown so it can't be missed. */
.intent-bubble.intent-charging .intent-icon {
  color: var(--blood-bright);
  font-size: 18px;
  filter:
    drop-shadow(0 1px 0 rgba(0,0,0,0.95))
    drop-shadow(0 0 4px rgba(0,0,0,0.85))
    drop-shadow(0 0 14px rgba(220,80,80,0.85));
  animation: intent-charging-pulse 0.85s ease-in-out infinite alternate;
}
.intent-bubble.intent-charging .intent-num {
  color: #ffd2c2;
  font-weight: 800;
  text-shadow: 0 0 8px rgba(220,80,80,0.7);
}
@keyframes intent-charging-pulse {
  from { transform: scale(1)    rotate(-3deg); }
  to   { transform: scale(1.18) rotate(3deg);  }
}

/* Wind-up enemy figure marker — red glow ring underfoot + a small
   "WIND-UP N" strap so the threat is unmistakable.  Uses ::after so it
   doesn't collide with the priority/ticking ::before markers. */
.figure.e-charging {
  filter:
    drop-shadow(0 6px 8px rgba(0,0,0,0.65))
    drop-shadow(0 0 16px rgba(220,80,80,0.55));
}
.figure.e-charging .figure-shadow {
  box-shadow: 0 0 24px 5px rgba(220,80,80,0.55);
}
.figure.e-charging::after {
  content: 'WIND-UP ' attr(data-release-in);
  position: absolute;
  top: -24px; left: 50%;
  transform: translateX(-50%);
  font-size: 7.5px;
  font-weight: 800;
  letter-spacing: 0.24em;
  color: #ffd2c2;
  background: linear-gradient(180deg, rgba(120,30,30,0.95) 0%, rgba(40,10,10,0.95) 100%);
  border: 1px solid var(--blood-bright);
  padding: 2px 7px;
  text-shadow: 0 0 8px rgba(220,80,80,0.7), 0 1px 0 rgba(0,0,0,0.95);
  z-index: 8;
  pointer-events: none;
  animation: e-charging-strap-pulse 0.85s ease-in-out infinite alternate;
  white-space: nowrap;
}
@keyframes e-charging-strap-pulse {
  from { filter: brightness(1);    box-shadow: 0 0 6px rgba(220,80,80,0.4); }
  to   { filter: brightness(1.25); box-shadow: 0 0 14px rgba(220,80,80,0.85); }
}

/* Threat telegraph — party figures targeted by enemy intents glow red.
   No exact damage number; the glow communicates "this one is in trouble"
   while preserving some uncertainty about how bad the hit will be.
   .targeted-lethal pulses brighter + faster for hits that would KO. */
.figure.targeted-by-enemy {
  animation: figure-threat 1.4s ease-in-out infinite;
}
.figure.targeted-by-enemy.targeted-lethal {
  animation: figure-threat-lethal 0.7s ease-in-out infinite;
}
@keyframes figure-threat {
  0%, 100% {
    filter:
      drop-shadow(0 6px 8px rgba(0,0,0,0.65))
      drop-shadow(0 0 10px rgba(212,69,69,0.45));
  }
  50%      {
    filter:
      drop-shadow(0 6px 8px rgba(0,0,0,0.65))
      drop-shadow(0 0 20px rgba(212,69,69,0.85))
      drop-shadow(0 0 32px rgba(212,69,69,0.4));
  }
}
@keyframes figure-threat-lethal {
  0%, 100% {
    filter:
      drop-shadow(0 6px 8px rgba(0,0,0,0.65))
      drop-shadow(0 0 14px rgba(255,80,80,0.75))
      drop-shadow(0 0 26px rgba(212,69,69,0.5));
  }
  50%      {
    filter:
      drop-shadow(0 6px 8px rgba(0,0,0,0.65))
      drop-shadow(0 0 26px rgba(255,80,80,1))
      drop-shadow(0 0 44px rgba(212,69,69,0.7));
  }
}

/* predicted incoming damage chip — top-left of party figure, opposite figure-statuses */
.incoming-chip {
  position: absolute;
  top: 1px; left: 2px;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 1px 4px;
  background: rgba(20, 6, 4, 0.92);
  border: 1px solid var(--blood-bright);
  border-radius: 3px;
  color: var(--blood-bright);
  font-size: 9.5px;
  font-weight: 800;
  letter-spacing: 0.2px;
  line-height: 1;
  text-shadow: 0 1px 0 var(--shadow), 0 0 6px rgba(212,69,69,0.55);
  box-shadow: 0 2px 4px rgba(0,0,0,0.5);
  z-index: 9;
  pointer-events: auto;
  cursor: help;
  font-variant-numeric: tabular-nums;
}
.incoming-chip.lethal {
  background: rgba(50, 8, 6, 0.96);
  border-color: #ff6b6b;
  color: #ffdcd2;
  box-shadow: 0 0 10px rgba(255,107,107,0.85), 0 2px 4px rgba(0,0,0,0.6);
  animation: incoming-lethal 0.55s ease-in-out infinite alternate;
}
@keyframes incoming-lethal {
  from { box-shadow: 0 0 8px rgba(255,107,107,0.6), 0 2px 4px rgba(0,0,0,0.6); }
  to   { box-shadow: 0 0 14px rgba(255,107,107,1), 0 2px 4px rgba(0,0,0,0.6); }
}
.incoming-ko {
  background: rgba(212,69,69,0.45);
  color: #fff5f0;
  padding: 1px 3px;
  border-radius: 2px;
  font-size: 8.5px;
  letter-spacing: 0.6px;
}

/* hit shake on the portrait */
.figure.hit-flash .figure-portrait { animation: portrait-shake 0.4s ease-out; }
.figure.heal-flash { animation: card-heal-glow 0.7s ease-out; }
/* Telegraph the target of an enemy intent — a brief red pulse on the
   party slot(s) about to take a hit so the player traces enemy → target
   before the swing lands. */
.figure.warn-flash .figure-portrait { animation: portrait-warn 0.32s ease-out; }
@keyframes portrait-warn {
  0%   { filter: brightness(1)    drop-shadow(0 0 0 transparent); }
  35%  { filter: brightness(1.18) drop-shadow(0 0 18px rgba(220,80,80,0.75)); }
  100% { filter: brightness(1)    drop-shadow(0 0 0 transparent); }
}

/* preview / target marker */
.figure.target-marker {
  filter: drop-shadow(0 0 18px var(--gold-bright));
}
.figure.target-marker::before {
  content: '✦';
  position: absolute;
  top: -2px; left: 50%;
  transform: translateX(-50%);
  color: var(--gold-bright);
  font-size: 18px;
  text-shadow: 0 0 12px var(--gold-bright);
  z-index: 10;
  animation: target-pulse 0.6s ease-in-out infinite alternate;
  pointer-events: none;
}

/* Weakness-target aura — fires when the player previews an action whose
   element matches the enemy's weakness.  Tinted per school so the player
   can see "this attack would land on this enemy's weakness" instantly.
   Uses ::after so it doesn't override objective ::before markers (priority
   crown, ticking charge) that share the same anchor. */
.figure.weakness-target { filter: drop-shadow(0 0 14px currentColor); }
.figure.weakness-target[data-weakness-school="physical"] { color: #d8c2a0; }
.figure.weakness-target[data-weakness-school="holy"]     { color: #f4e6b8; }
.figure.weakness-target[data-weakness-school="arcane"]   { color: #c9a8e8; }
.figure.weakness-target[data-weakness-school="ranged"]   { color: #a8c8a0; }
.figure.weakness-target[data-weakness-school="stealth"]  { color: #a8b8d0; }
.figure.weakness-target::after {
  content: 'WEAK';
  position: absolute;
  top: -10px; right: -2px;
  font-size: 7.5px;
  font-weight: 800;
  letter-spacing: 0.18em;
  color: currentColor;
  background: rgba(0,0,0,0.7);
  padding: 1px 4px;
  border-radius: 2px;
  text-shadow: 0 0 6px currentColor;
  z-index: 11;
  pointer-events: none;
  animation: weakness-pulse 1.2s ease-in-out infinite alternate;
}
@keyframes weakness-pulse {
  from { opacity: 0.7; transform: scale(0.96); }
  to   { opacity: 1.0; transform: scale(1.04); }
}

/* Staggered enemy under preview — gold lightning so the 2× window can't
   be missed.  Uses .stagger-badge as a real child element so it doesn't
   conflict with the priority/ticking ::before markers. */
.figure.stagger-target { filter: drop-shadow(0 0 16px rgba(255,206,90,0.85)); }
.figure.stagger-target .stagger-badge,
.figure.stagger-target::after {
  /* fallback ::after only fires if no .stagger-badge child exists */
}
.figure.stagger-target:not(:has(.stagger-badge))::after {
  content: '⚡ 2×';
  position: absolute;
  top: -12px; left: 50%;
  transform: translateX(-50%);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.12em;
  color: #fff3b8;
  background: rgba(120,60,0,0.85);
  padding: 1px 5px;
  border-radius: 2px;
  text-shadow: 0 0 6px rgba(255,206,90,0.9);
  z-index: 12;
  pointer-events: none;
  animation: stagger-target-pulse 0.65s ease-in-out infinite alternate;
}
/* When BOTH weakness and stagger highlights apply, suppress the ::after
   fallback (weakness owns ::after already) so the two don't collide. */
.figure.weakness-target.stagger-target::after {
  content: '⚡ WEAK 2×' !important;
  color: #fff3b8 !important;
  background: rgba(120,60,0,0.85) !important;
  text-shadow: 0 0 8px rgba(255,206,90,0.9) !important;
}
@keyframes stagger-target-pulse {
  from { opacity: 0.85; transform: translateX(-50%) scale(0.96); }
  to   { opacity: 1.0;  transform: translateX(-50%) scale(1.06); }
}

/* predicted damage/heal label — sits below the portrait so the figure stays visible */
.target-dmg-label,
.target-heal-label {
  position: absolute;
  /* Lifted from bottom: 24px to top: 38% so the WEAK / RESIST / KO badge
     sits on the enemy's body mid-chest, well clear of the action-tile
     hold-preview tooltip (which floats above the action row in the lower
     middle of the screen and was overlapping when both rendered).
     translateY(-50%) centers the label on that anchor. */
  top: 38%; left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: baseline;
  gap: 4px;
  padding: 3px 8px;
  background: rgba(8, 4, 2, 0.88);
  border: 1.5px solid var(--gold-bright);
  border-radius: 5px;
  box-shadow: 0 0 12px rgba(240,212,136,0.55), 0 2px 6px rgba(0,0,0,0.6);
  color: var(--gold-bright);
  font-weight: 800;
  font-size: 18px;
  line-height: 1;
  letter-spacing: 0.3px;
  text-shadow: 0 1px 0 var(--shadow), 0 0 6px rgba(240,212,136,0.5);
  z-index: 12;
  pointer-events: none;
  white-space: nowrap;
  animation: target-pulse 0.7s ease-in-out infinite alternate;
}
.target-heal-label {
  border-color: var(--heal);
  color: var(--heal);
  box-shadow: 0 0 12px rgba(152,216,120,0.55), 0 2px 6px rgba(0,0,0,0.6);
  text-shadow: 0 1px 0 var(--shadow), 0 0 6px rgba(152,216,120,0.55);
}

/* Movement preview — character identity stays clear:
   - SOURCE (the actor who is moving) gets the pulsing gold halo. THIS figure
     is the subject of the action; reading it as "highlighted = important".
   - DESTINATION (whoever currently stands in the target slot) is dimmed +
     desaturated so it visually steps back. The destination is a *position*,
     not a participant.
   - A glowing floor crescent at the destination slot marks the landing spot.
   - The ▶▶ FRONT tag floats above the floor marker to label the slot. */
.figure.move-src .figure-portrait {
  filter: drop-shadow(0 0 14px var(--gold-bright)) drop-shadow(0 0 22px rgba(240,212,136,0.45));
  animation: move-actor-pulse 0.9s ease-in-out infinite alternate;
}
@keyframes move-actor-pulse {
  from { filter: drop-shadow(0 0 10px var(--gold-bright)) drop-shadow(0 0 18px rgba(240,212,136,0.35)); }
  to   { filter: drop-shadow(0 0 18px var(--gold-bright)) drop-shadow(0 0 28px rgba(240,212,136,0.55)); }
}
/* Move preview — clarity flip.  Previously the destination was dimmed, the
   source was unmarked.  Now the SOURCE fades (you're leaving here) and the
   DESTINATION stays full-bright with the gold halo + tag.  Reads as
   "from → to" without ambiguity. */
.figure.move-src .figure-portrait {
  filter: brightness(0.45) saturate(0.45);
  opacity: 0.55;
  transition: filter 0.18s ease, opacity 0.18s ease;
}
/* Big direction chevron anchored to the source figure during preview.
   Points outward toward the destination slot.  Same drop-shadow palette
   as the inline move-arrow buttons so the spatial vocabulary matches. */
.move-from-arrow {
  position: absolute;
  top: 42%;
  transform: translateY(-50%);
  font-size: 56px;
  line-height: 1;
  font-weight: 200;
  color: var(--gold-bright);
  pointer-events: none;
  z-index: 11;
  filter:
    drop-shadow(0 1px 0 rgba(0,0,0,0.95))
    drop-shadow(0 0 6px rgba(0,0,0,0.9))
    drop-shadow(0 0 14px rgba(240,212,136,0.7));
  animation: move-from-arrow-pulse 1.0s ease-in-out infinite alternate;
}
.move-from-arrow.arrow-right { right: -36px; }
.move-from-arrow.arrow-left  { left:  -36px; }
@keyframes move-from-arrow-pulse {
  from {
    transform: translateY(-50%) translateX(0)    scale(1);
    opacity: 0.85;
  }
  to {
    transform: translateY(-50%) translateX(var(--mfa-x, 6px)) scale(1.08);
    opacity: 1;
  }
}
.move-from-arrow.arrow-left  { --mfa-x: -6px; }
.move-from-arrow.arrow-right { --mfa-x:  6px; }
.figure.move-src::before {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 12px;
  width: 56%;
  height: 10px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(ellipse 50% 60% at 50% 50%,
    rgba(212,200,168,0.32) 0%,
    rgba(212,200,168,0.08) 50%,
    transparent 75%);
  pointer-events: none;
  opacity: 0.85;
  z-index: 6;
}
.figure.move-dest::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 10px;
  width: 78%;
  height: 16px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(ellipse 50% 60% at 50% 50%,
    var(--gold-bright) 0%,
    rgba(240,212,136,0.6) 28%,
    rgba(240,212,136,0.22) 55%,
    transparent 78%);
  pointer-events: none;
  animation: move-landing-pulse 0.85s ease-in-out infinite alternate;
  z-index: 6;
}
@keyframes move-landing-pulse {
  from { opacity: 0.7;  transform: translateX(-50%) scaleX(0.92); }
  to   { opacity: 1;    transform: translateX(-50%) scaleX(1.08); }
}

/* Move tag — NieR instrument label.  Black plate with corner brackets,
   chevron + destination slot name.  Sits above the gold floor halo. */
.target-move-tag {
  position: absolute;
  bottom: 28px; left: 50%;
  transform: translateX(-50%);
  display: inline-flex; align-items: center; gap: 7px;
  padding: 4px 14px;
  background: rgba(8,8,10,0.92);
  border: none;
  border-radius: 0;
  color: var(--gold-bright);
  font-size: 10.5px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  font-weight: 700;
  line-height: 1;
  z-index: 12;
  pointer-events: none;
  white-space: nowrap;
  animation: nier-target-pulse 1.6s ease-in-out infinite;
  text-shadow: 0 1px 0 rgba(0,0,0,0.95), 0 0 6px rgba(240,212,136,0.45);
}
/* Corner brackets — NieR HUD instrument frame.  Top-left + bottom-right
   bookend the label without the heaviness of a full border. */
.target-move-tag::before,
.target-move-tag::after {
  content: '';
  position: absolute;
  width: 6px;
  height: 6px;
  border-color: var(--gold-bright);
  pointer-events: none;
}
.target-move-tag::before {
  top: 0; left: 0;
  border-top: 1px solid;
  border-left: 1px solid;
}
.target-move-tag::after {
  bottom: 0; right: 0;
  border-bottom: 1px solid;
  border-right: 1px solid;
}
@keyframes nier-target-pulse {
  0%, 100% { opacity: 0.88; }
  50%      { opacity: 1; }
}
.target-move-tag .move-glyph {
  font-size: 16px;
  letter-spacing: 0;
  line-height: 1;
  opacity: 0.95;
  font-weight: 400;
  text-shadow: 0 1px 0 rgba(0,0,0,0.95), 0 0 8px rgba(240,212,136,0.55);
}
.target-move-tag .move-text { font-weight: 700; }

.target-dmg-label .dmg-num { font-size: 19px; }
.target-dmg-label .dmg-sub {
  display: block;
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-bright);
  opacity: 0.85;
  margin-top: 1px;
  text-shadow: 0 0 4px rgba(240,212,136,0.55);
}
.target-dmg-label .dmg-badge {
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 0.6px;
  padding: 2px 4px;
  border-radius: 3px;
  background: rgba(240,212,136,0.18);
}
.target-dmg-label.weak {
  border-color: #ffce5a;
  color: #ffe89a;
  box-shadow: 0 0 14px rgba(255,206,90,0.7), 0 2px 6px rgba(0,0,0,0.6);
}
.target-dmg-label.weak .dmg-badge { background: rgba(255,206,90,0.28); color: #fff3b8; }
.target-dmg-label.resist {
  border-color: #6e7a8c;
  color: #c8d2dc;
  box-shadow: 0 0 10px rgba(110,122,140,0.5), 0 2px 6px rgba(0,0,0,0.6);
  text-shadow: 0 1px 0 var(--shadow);
}
.target-dmg-label.resist .dmg-badge { background: rgba(110,122,140,0.32); color: #d6dde6; }
.target-dmg-label.stagger {
  border-color: #ff8a3a;
  color: #ffd0a0;
  box-shadow: 0 0 12px rgba(255,138,58,0.6), 0 2px 6px rgba(0,0,0,0.6);
}
.target-dmg-label.stagger .dmg-badge { background: rgba(255,138,58,0.3); color: #ffe6c8; }
.target-dmg-label.kill {
  border-color: var(--blood-bright);
  color: #ffe1d6;
  background: rgba(36, 8, 6, 0.92);
  box-shadow: 0 0 16px rgba(212,69,69,0.85), 0 2px 6px rgba(0,0,0,0.7);
  animation: kill-pulse 0.55s ease-in-out infinite alternate;
}
.target-dmg-label.kill .dmg-badge {
  background: rgba(212,69,69,0.45);
  color: #fff5f0;
  letter-spacing: 0.9px;
}
@keyframes kill-pulse {
  from { box-shadow: 0 0 14px rgba(212,69,69,0.65), 0 2px 6px rgba(0,0,0,0.7); }
  to   { box-shadow: 0 0 22px rgba(212,69,69,1), 0 2px 6px rgba(0,0,0,0.7); }
}

/* downed character SVG breathing off */
.figure.downed .figure-portrait svg { animation: none; }
.figure.state-staggered .figure-portrait svg { animation-duration: 1.2s; }
.figure.hit-flash .figure-portrait svg { animation: none; }

/* Staggered enemy — warm gold aura (was orange).  Weakened enemy gets
   a subtler red-shifted aura.  Replaces the old chain-meter glow. */
.figure.state-staggered {
  animation: state-stagger-aura 1.2s ease-in-out infinite;
}
.figure.state-weakened {
  animation: state-weak-aura 1.8s ease-in-out infinite;
}
@keyframes state-stagger-aura {
  0%, 100% { filter: drop-shadow(0 6px 8px rgba(0,0,0,0.65)) drop-shadow(0 0 10px rgba(240,212,136,0.4)); }
  50%      { filter: drop-shadow(0 6px 8px rgba(0,0,0,0.65)) drop-shadow(0 0 22px rgba(240,212,136,0.85)); }
}
@keyframes state-weak-aura {
  0%, 100% { filter: drop-shadow(0 6px 8px rgba(0,0,0,0.65)) drop-shadow(0 0 6px rgba(200,80,80,0.25)); }
  50%      { filter: drop-shadow(0 6px 8px rgba(0,0,0,0.65)) drop-shadow(0 0 12px rgba(200,80,80,0.5)); }
}

/* adjacency icons — small row above figure */
.figure-adj {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 4px;
  margin-bottom: 1px;
  z-index: 5;
  flex-shrink: 0;
  width: 100%;
  min-height: 12px;
  height: 12px;
}

/* ============ CARD — ornate gold frame, parchment fill ============ */
.card {
  background:
    linear-gradient(180deg, #2a1c12 0%, #1a120c 50%, #0e0905 100%);
  border: 1px solid var(--gold-dim);
  border-radius: var(--card-radius);
  position: relative;
  cursor: pointer;
  transition: transform 0.18s, box-shadow 0.25s, filter 0.2s;
  display: flex;
  flex-direction: column;
  min-height: 0;
  box-shadow:
    0 2px 8px rgba(0,0,0,0.6),
    0 0 0 1px rgba(0,0,0,0.6) inset,
    0 0 0 2px rgba(200,164,100,0.08) inset;
  overflow: visible;
}
/* gold corner brackets — Slay-the-Spire style decorative corners (4 real elements) */
.cnr {
  position: absolute;
  width: 10px; height: 10px;
  pointer-events: none;
  z-index: 4;
  transition: border-color 0.2s, box-shadow 0.2s;
}
.cnr-tl { top: -1px; left:  -1px; border-top: 2px solid var(--gold); border-left:  2px solid var(--gold); border-top-left-radius:     var(--card-radius); box-shadow: -1px -1px 4px rgba(200,164,100,0.18); }
.cnr-tr { top: -1px; right: -1px; border-top: 2px solid var(--gold); border-right: 2px solid var(--gold); border-top-right-radius:    var(--card-radius); box-shadow:  1px -1px 4px rgba(200,164,100,0.18); }
.cnr-bl { bottom: -1px; left:  -1px; border-bottom: 2px solid var(--gold); border-left:  2px solid var(--gold); border-bottom-left-radius:  var(--card-radius); box-shadow: -1px  1px 4px rgba(200,164,100,0.18); }
.cnr-br { bottom: -1px; right: -1px; border-bottom: 2px solid var(--gold); border-right: 2px solid var(--gold); border-bottom-right-radius: var(--card-radius); box-shadow:  1px  1px 4px rgba(200,164,100,0.18); }

.card.empty { opacity: 0.18; }
.card.empty .cnr { display: none; }

.card.downed { opacity: 0.4; filter: grayscale(1) brightness(0.65); }
.card.downed .cnr { border-color: var(--ink-faint); box-shadow: none; }

/* adjacency outlines on bottom strip */
.card.adjacent-bond .card-bottom { box-shadow: inset 0 -2px 0 var(--hp); }
.card.adjacent-friction .card-bottom { box-shadow: inset 0 -2px 0 var(--blood-bright); }

/* enemy threat — pulsing blood-glow */
.card.targeted-by-enemy {
  animation: pulse-threat 1.6s ease-in-out infinite;
}
@keyframes pulse-threat {
  0%, 100% {
    box-shadow:
      0 2px 8px rgba(0,0,0,0.6),
      0 0 0 1px rgba(0,0,0,0.6) inset,
      0 0 0 2px rgba(200,164,100,0.08) inset,
      0 0 0 rgba(212,69,69,0);
  }
  50% {
    box-shadow:
      0 2px 8px rgba(0,0,0,0.6),
      0 0 0 1px rgba(0,0,0,0.6) inset,
      0 0 0 2px rgba(212,69,69,0.2) inset,
      0 0 18px rgba(212,69,69,0.5);
  }
}

/* hold-to-preview target — bright gold reticle */
.card.target-marker {
  box-shadow:
    0 0 26px var(--gold-bright),
    0 0 8px var(--gold-bright),
    inset 0 0 16px rgba(240,212,136,0.4);
  transform: scale(1.04);
  z-index: 5;
}
.card.target-marker .cnr { border-color: var(--gold-pale); box-shadow: 0 0 6px var(--gold-bright); }
.card.target-marker::before {
  content: '✦';
  position: absolute;
  top: 6px; left: 50%;
  transform: translateX(-50%);
  color: var(--gold-bright);
  font-size: 16px;
  text-shadow: 0 0 10px var(--gold-bright);
  z-index: 10;
  animation: target-pulse 0.6s ease-in-out infinite alternate;
  pointer-events: none;
}

/* hit flash — chromatic ripple */
.card.hit-flash {
  animation: card-hit-shake 0.4s ease-out, card-hit-glow 0.5s ease-out;
}
.card.hit-flash .card-portrait { animation: portrait-shake 0.4s ease-out; }
@keyframes portrait-shake {
  0%   { transform: translateX(-4px); }
  25%  { transform: translateX(4px); }
  50%  { transform: translateX(-3px); }
  75%  { transform: translateX(2px); }
  100% { transform: translateX(0); }
}
@keyframes card-hit-shake {
  0%, 100% { transform: translateX(0); }
  25%      { transform: translateX(-2px); }
  75%      { transform: translateX(2px); }
}
@keyframes card-hit-glow {
  0%   { filter: brightness(1.6) hue-rotate(-10deg); }
  50%  { filter: brightness(1.2) hue-rotate(-5deg); }
  100% { filter: brightness(1) hue-rotate(0); }
}
.card.heal-flash {
  animation: card-heal-glow 0.7s ease-out;
}
@keyframes card-heal-glow {
  0%   { box-shadow: 0 0 28px var(--heal), 0 0 0 2px var(--heal) inset; }
  100% { box-shadow:
    0 2px 8px rgba(0,0,0,0.6),
    0 0 0 1px rgba(0,0,0,0.6) inset,
    0 0 0 2px rgba(200,164,100,0.08) inset; }
}

@keyframes target-pulse {
  /* translate(-50%, -50%) matches the centered anchor for .target-dmg-label
     / .target-heal-label which sit at top: 38% — the Y offset must be
     carried by the animation or the label snaps to the anchor. */
  from { opacity: 0.5; transform: translate(-50%, -50%) scale(0.9); }
  to   { opacity: 1;   transform: translate(-50%, -50%) scale(1.1); }
}

/* ============ SLOT BANNER ============ */
.slot-banner {
  font-size: 8px;
  letter-spacing: 0.22em;
  color: var(--ink-faint);
  text-transform: uppercase;
  text-align: center;
  padding: 3px 0 2px;
  background: linear-gradient(180deg, rgba(200,164,100,0.06) 0%, transparent 100%);
  font-weight: bold;
  position: relative;
  border-bottom: 1px solid rgba(200,164,100,0.1);
}
.slot-banner.home { color: var(--gold-bright); text-shadow: 0 0 6px rgba(240,212,136,0.4); }
.slot-banner::before, .slot-banner::after {
  content: '';
  position: absolute;
  top: 50%; transform: translateY(-50%);
  width: 8px; height: 1px;
  background: var(--gold-dim);
  opacity: 0.6;
}
.slot-banner::before { left: 4px; }
.slot-banner::after  { right: 4px; }
.slot-banner.home::before, .slot-banner.home::after { background: var(--gold); opacity: 0.9; }

/* ============ PORTRAIT ============ */
.card-portrait {
  flex: 1;
  position: relative;
  overflow: hidden;
  min-height: 0;
  background: var(--parchment-deep);
}
.card-portrait svg {
  width: 100%; height: 100%; display: block;
  transform-origin: 50% 100%;
  animation: idle-breathe 5.4s ease-in-out infinite;
  will-change: transform;
}
/* stagger the breath so the three party / three enemy cards don't sync up */
#party-half > .figure:nth-child(1) .figure-portrait svg { animation-duration: 5.8s; animation-delay: -0.2s; }
#party-half > .figure:nth-child(2) .figure-portrait svg { animation-duration: 5.2s; animation-delay: -1.4s; }
#party-half > .figure:nth-child(3) .figure-portrait svg { animation-duration: 5.6s; animation-delay: -2.6s; }
#enemy-half > .figure:nth-child(1) .figure-portrait svg { animation-duration: 6.2s; animation-delay: -0.8s; animation-timing-function: ease-in-out; }
#enemy-half > .figure:nth-child(2) .figure-portrait svg { animation-duration: 5.0s; animation-delay: -1.9s; animation-timing-function: ease-in-out; }
#enemy-half > .figure:nth-child(3) .figure-portrait svg { animation-duration: 5.8s; animation-delay: -3.1s; animation-timing-function: ease-in-out; }
/* enemy portraits are mirrored — preserve the flip while breathing */
#enemy-half .card-portrait svg { animation-name: idle-breathe-mirror; }
@keyframes idle-breathe {
  0%, 100% { transform: scale(1, 1)         translateY(0); }
  45%      { transform: scale(1.018, 1.012) translateY(-0.6px); }
  55%      { transform: scale(1.02, 1.01)   translateY(-0.4px); }
}
@keyframes idle-breathe-mirror {
  0%, 100% { transform: scaleX(-1) scaleY(1)        translateY(0); }
  45%      { transform: scaleX(-1.018) scaleY(1.012) translateY(-0.6px); }
  55%      { transform: scaleX(-1.02) scaleY(1.01)   translateY(-0.4px); }
}
.card.downed .card-portrait svg { animation: none; }
.card.hit-flash .card-portrait svg { animation: none; }
/* deep vignette over the portrait */
.card-portrait::after {
  content: '';
  position: absolute; inset: 0;
  background:
    radial-gradient(ellipse at center, transparent 45%, rgba(0,0,0,0.6) 100%),
    linear-gradient(180deg, transparent 0%, transparent 80%, rgba(0,0,0,0.4) 100%);
  pointer-events: none;
}
/* subtle ink scratches on top edge */
.card-portrait::before {
  content: '';
  position: absolute; top: 0; left: 0; right: 0; height: 10px;
  background: linear-gradient(180deg, rgba(10,6,4,0.5) 0%, transparent 100%);
  pointer-events: none; z-index: 1;
}

/* enemy cards mirror horizontally to face party */
#enemy-half .card-portrait svg { transform: scaleX(-1); }

/* ============ STATUS CHIPS ============ */
.card-statuses {
  position: absolute;
  left: 3px; right: 3px; bottom: 3px;
  display: flex; flex-wrap: wrap; gap: 2px;
  z-index: 4;
}
/* icon-centric status badge: glowing glyph + tiny tabular numeral */
.status-chip {
  /* Larger pill so the glyph + stack count read at a glance without needing
     an always-on text label.  Pairs with .status-icon / .status-num font
     bumps below. */
  display: inline-flex; align-items: center; gap: 2px;
  min-width: 22px; height: 22px;
  padding: 1px 6px;
  border-radius: 6px;
  background: rgba(10,8,7,0.88);
  border: 1.5px solid;
  line-height: 1;
  font-weight: bold;
  letter-spacing: 0;
  box-shadow: 0 1px 4px rgba(0,0,0,0.75);
}
.status-chip .status-icon {
  font-size: 14px;
  line-height: 1;
  filter: drop-shadow(0 0 4px currentColor);
}
.status-chip .status-num {
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  font-weight: 700;
  text-shadow: 0 1px 0 var(--shadow);
}
/* Trio enemy layouts get a touch tighter so three chips fit comfortably
   across a back-slot figure.  Mobile-width breakpoint further scales. */
@media (max-width: 720px) {
  .status-chip { min-width: 19px; height: 19px; padding: 1px 5px; gap: 1px; }
  .status-chip .status-icon { font-size: 12px; }
  .status-chip .status-num  { font-size: 11px; }
}
.status-armor   { color: var(--armor);       border-color: var(--armor); }
.status-bleed   { color: var(--bleed);       border-color: var(--bleed); }
.status-burn    { color: #ff9a4a;            border-color: #ff9a4a;       background: rgba(180,80,30,0.25); }
.status-taunt   { color: var(--taunt);       border-color: var(--taunt); }
.status-dulled  { color: var(--weak);        border-color: var(--weak); }
.status-retal   { color: var(--gold);        border-color: var(--gold); }
.status-vuln    { color: #ff8a8a;            border-color: var(--bleed); background: rgba(196,56,56,0.25); }
.status-pending {
  color: var(--gold-bright); border-color: var(--gold-bright);
  background: rgba(240,212,136,0.22);
  /* Pulse so the player remembers to consume the bonus next turn —
     pending one-shots are easy to forget when the row crowds up. */
  animation: status-pending-pulse 1.6s ease-in-out infinite;
}
@keyframes status-pending-pulse {
  0%, 100% { box-shadow: 0 1px 4px rgba(0,0,0,0.75), 0 0 0 0 rgba(240,212,136,0); }
  50%      { box-shadow: 0 1px 4px rgba(0,0,0,0.75), 0 0 10px 2px rgba(240,212,136,0.55); }
}
/* Overflow chip — collapses extra statuses when more than 4 would render
   on a single figure.  Neutral gold "+N" pill; the title attribute lists
   every collapsed status so press-and-hold reads them all. */
.status-overflow {
  color: var(--ink);
  border-color: rgba(232,220,196,0.45);
  background: rgba(40,30,20,0.78);
}
.status-overflow .status-icon { font-size: 13px; opacity: 0.8; }
.status-veil    { color: #c0d8ff;            border-color: #c0d8ff;       background: rgba(60,90,140,0.28); }
.status-wall    { color: #ffd098;            border-color: #ffd098;       background: rgba(120,80,40,0.28); }
.status-guard   { color: #ffe896;            border-color: #ffe896;       background: rgba(140,110,50,0.28); }
/* Position-derived aura chips — make Garron Sentinel and Cassia Steadfast
   visible so the player can see *why* damage on this card is being softened. */
.status-sentinel,
.status-steadfast { color: var(--armor); border-color: var(--armor); background: rgba(136,152,168,0.18); }
/* Passive-bonus auras — gold tint so the player reads them as "your damage
   is hot right now" instead of confusing them with defensive armor chips. */
.status-bloodlust,
.status-focus,
.status-lone,
.status-bhunt,
.status-evis     { color: var(--gold-bright); border-color: var(--gold-bright); background: rgba(240,212,136,0.18); }
.status-note     { color: #ff8a8a;            border-color: var(--bleed);       background: rgba(196,56,56,0.18); }

/* ============ DESCRIPTION KEYWORD CHIPS ============
   Inline icon+word chips that appear inside tech tooltips and the hero
   codex so common keywords (advance/retreat/bleed/vuln/armor/etc.) read
   consistently at a glance.  Substitution happens at render via formatDesc(). */
.kw {
  font-style: normal;
  white-space: nowrap;
  letter-spacing: 0.02em;
  font-weight: 600;
}
.kw-advance        { color: var(--frost-bright); }
.kw-retreat        { color: var(--frost); }
.kw-bleed          { color: #e07a6a; }
.kw-vuln           { color: #ff8a8a; }
.kw-armor          { color: var(--armor); }
.kw-taunt          { color: var(--taunt); }
.kw-retal          { color: var(--gold-bright); }
.kw-cleanse        { color: var(--gold-pale); }
.kw-resolve        { color: var(--gold-bright); }
.kw-heal           { color: #b0d8a0; }
.kw-dulled         { color: var(--weak); }
.kw-school-physical { color: #d8c2a0; }
.kw-school-holy     { color: #f4e6b8; }
.kw-school-arcane   { color: #c9a8e8; }
.kw-school-ranged   { color: #a8c8a0; }
.kw-school-stealth  { color: #a8b8d0; }

/* ============ CARD BOTTOM — name + HP ============ */
.card-bottom {
  background:
    linear-gradient(180deg, #2a1c12 0%, #14100b 100%);
  padding: 3px 4px 4px;
  border-top: 1px solid rgba(200,164,100,0.18);
}
.card-name {
  font-size: 11px; font-weight: bold;
  color: var(--ink); letter-spacing: 0.04em;
  line-height: 1.1; text-align: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  text-shadow: 0 1px 0 var(--shadow);
  margin-bottom: 2px;
}
.party-card .card-name { color: var(--gold-pale); }

/* HP bar — segmented gold-framed */
.hp-bar {
  width: 100%; height: 9px;
  background: linear-gradient(180deg, #050200 0%, #1a0e08 100%);
  border: 1px solid var(--gold-deep);
  border-radius: 2px; overflow: hidden;
  position: relative;
  box-shadow: inset 0 1px 2px rgba(0,0,0,0.7);
}
.hp-fill {
  height: 100%;
  background:
    linear-gradient(180deg, var(--hp) 0%, #4a8a2c 50%, #2a5818 100%);
  transition: width 0.4s ease-out;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.18);
  position: relative;
}
.hp-fill::after {
  content: '';
  position: absolute; inset: 0;
  background:
    repeating-linear-gradient(90deg,
      transparent 0px, transparent 14px,
      rgba(0,0,0,0.3) 14px, rgba(0,0,0,0.3) 15px);
  pointer-events: none;
}
.hp-fill.low {
  background: linear-gradient(180deg, var(--hp-low) 0%, #8a2418 50%, #501a10 100%);
  animation: hp-pulse 1.2s ease-in-out infinite;
}
@keyframes hp-pulse {
  0%, 100% { filter: brightness(1); }
  50%      { filter: brightness(1.3); box-shadow: inset 0 1px 0 rgba(255,255,255,0.3), 0 0 8px var(--hp-low); }
}
.hp-text {
  position: absolute; inset: 0;
  text-align: center;
  font-size: 9px;
  color: var(--ink);
  text-shadow: 0 1px 1px var(--shadow), 0 0 4px rgba(0,0,0,0.85);
  line-height: 9px;
  font-variant-numeric: tabular-nums;
  font-weight: bold;
  letter-spacing: 0.04em;
}

/* Chain meter + banner + staggered card chrome removed — the new
   WEAKENED → STAGGERED state flow lives on .figure (.state-weakened /
   .state-staggered class + the .state-pip overlay defined above). */

/* ============ INTENT RIBBON (enemies) ============ */
.intent {
  position: absolute;
  top: -1px; left: -1px; right: -1px;
  background:
    linear-gradient(180deg, var(--blood-bright) 0%, var(--blood) 60%, var(--blood-deep) 100%);
  color: var(--ink);
  font-size: 9px;
  font-weight: bold;
  padding: 2px 6px 3px;
  border-radius: var(--card-radius) var(--card-radius) 0 0;
  letter-spacing: 0.08em;
  border-bottom: 1px solid var(--gold-deep);
  z-index: 3;
  display: flex; align-items: center; justify-content: space-between; gap: 4px;
  box-shadow: 0 2px 6px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.15);
  text-shadow: 0 1px 0 var(--shadow);
  text-transform: uppercase;
}
.intent .intent-icon {
  font-size: 11px;
  line-height: 1;
  filter: drop-shadow(0 1px 0 var(--shadow));
}
.intent .intent-tag {
  flex: 1; text-align: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font-size: 9px;
}
.intent .intent-target {
  background: var(--shadow);
  color: var(--gold-bright);
  padding: 0 5px;
  border-radius: 2px;
  font-size: 8px;
  letter-spacing: 0.12em;
  border: 1px solid var(--gold-deep);
  font-weight: bold;
}
.intent.intent-aoe {
  background: linear-gradient(180deg, var(--aoe) 0%, #8a3878 60%, #4a1838 100%);
}
.intent.intent-debuff {
  background: linear-gradient(180deg, var(--debuff) 0%, #5a3a80 60%, #2a1840 100%);
}
.card.empty .intent { display: none; }

/* ============ ADJACENCY CHIPS ============ */
/* Adjacency glyph — small circular icon, no text. Hold/hover for the name. */
.adj-chip {
  position: static;
  display: inline-flex;
  align-items: center; justify-content: center;
  width: 12px; height: 12px;
  font-size: 11px;
  line-height: 1;
  border-radius: 50%;
  pointer-events: auto;
  cursor: help;
}
.adj-chip.bond {
  color: var(--gold-bright);
  text-shadow: 0 0 5px rgba(240,212,136,0.7), 0 0 10px rgba(240,212,136,0.35);
}
.adj-chip.friction {
  color: var(--blood-bright);
  text-shadow: 0 0 5px rgba(224,72,72,0.75), 0 0 10px rgba(224,72,72,0.35);
}

#incoming-banner { display: none; }

/* ============ POPUP LAYER ============ */
#popup-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 50;
  overflow: visible;
}
.popup {
  position: absolute;
  font-size: 18px; font-weight: bold;
  text-shadow: 0 1px 0 var(--shadow), 0 0 10px rgba(0,0,0,0.9);
  animation: popup-rise 1s ease-out forwards;
  pointer-events: none;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.06em;
  filter: drop-shadow(0 2px 4px rgba(0,0,0,0.8));
}
.popup.dmg   { color: var(--blood-bright); text-shadow: 0 1px 0 var(--shadow), 0 0 12px rgba(212,69,69,0.6); }
.popup.heal  { color: var(--heal); text-shadow: 0 1px 0 var(--shadow), 0 0 12px rgba(152,216,120,0.6); }
.popup.armor { color: var(--armor); text-shadow: 0 1px 0 var(--shadow), 0 0 10px rgba(156,170,208,0.5); }
.popup.miss  { color: var(--ink-dim); font-size: 12px; font-style: italic; }
.popup.crit  { color: var(--gold-bright); font-size: 24px; text-shadow: 0 1px 0 var(--shadow), 0 0 16px var(--gold-bright); }
.popup.stagger {
  color: var(--stagger);
  font-size: 26px;
  text-shadow: 0 0 14px var(--stagger), 0 2px 0 var(--shadow);
  letter-spacing: 0.2em;
}
.popup.synergy,
.popup.bond,
.popup.friction {
  font-size: 12px;
  font-weight: 700;
  font-style: italic;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.popup.passive {
  color: var(--frost-bright);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  text-shadow: 0 1px 0 var(--shadow), 0 0 8px rgba(160,200,255,0.5);
}
.popup.synergy {
  color: var(--gold-bright);
  text-shadow: 0 1px 0 var(--shadow), 0 0 10px rgba(240,212,136,0.7);
}
.popup.bond {
  color: var(--heal);
  text-shadow: 0 1px 0 var(--shadow), 0 0 10px rgba(152,216,120,0.7);
}
.popup.friction {
  color: var(--blood-bright);
  text-shadow: 0 1px 0 var(--shadow), 0 0 10px rgba(212,69,69,0.7);
}
/* ===========================================================================
   WORLD MAP — the Abyss layers.  Surfaced after a Wakeling kill so the
   player sees that the run was one layer of a deeper climb.
   =========================================================================== */
#world-map {
  position: fixed;
  inset: 0;
  z-index: 250;
  overflow: hidden;
  animation: wm-fade-in 0.6s ease-out;
}
#world-map.hidden { display: none !important; }
@keyframes wm-fade-in { from { opacity: 0; } to { opacity: 1; } }
#world-map .wm-bg {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 50% 40% at 50% -10%,
                    rgba(80,60,40,0.32) 0%, transparent 75%),
    radial-gradient(ellipse 80% 70% at 50% 60%,
                    rgba(18,10,12,0.96) 0%, rgba(0,0,0,1) 100%),
    #050405;
}
#world-map .wm-bg::after {
  content: '';
  position: absolute; inset: 0;
  background:
    repeating-linear-gradient(180deg,
      transparent 0,
      transparent 36px,
      rgba(232,220,196,0.018) 36px,
      rgba(232,220,196,0.018) 37px);
  mix-blend-mode: screen;
}
#world-map .wm-content {
  position: relative;
  z-index: 2;
  height: 100%;
  max-width: 760px;
  margin: 0 auto;
  padding: 18px 18px 18px;
  display: flex;
  flex-direction: column;
}
.wm-header { text-align: center; margin-bottom: 8px; }
.wm-title {
  font-size: 22px;
  letter-spacing: 0.44em;
  color: var(--gold-pale);
  text-shadow: 0 0 18px rgba(240,212,136,0.35);
  margin: 0;
  font-weight: 400;
}
.wm-subtitle {
  margin: 4px 0 0;
  font-size: 10.5px;
  letter-spacing: 0.18em;
  font-style: italic;
  color: var(--ink-dim);
}
.wm-list {
  flex: 1;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px 4px;
}
.wm-row {
  display: grid;
  grid-template-columns: 56px 1fr;
  gap: 14px;
  align-items: center;
  padding: 8px 12px;
  background: linear-gradient(180deg, rgba(20,16,16,0.55) 0%, rgba(8,6,8,0.7) 100%);
  position: relative;
}
.wm-row .wm-num {
  font-size: 28px;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  text-align: center;
  font-weight: 300;
}
.wm-name {
  font-size: 13px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold-pale);
}
.wm-sub {
  font-size: 10px;
  font-style: italic;
  color: var(--ink-dim);
  margin-top: 1px;
}
.wm-desc {
  font-size: 10.5px;
  color: var(--ink);
  margin-top: 4px;
  letter-spacing: 0.02em;
  line-height: 1.45;
}
.wm-boss {
  font-size: 9px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--blood-bright);
  margin-top: 4px;
}
.wm-locked-tag {
  font-size: 9px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-top: 4px;
}
/* States */
.wm-row.wm-cleared {
  background: linear-gradient(180deg, rgba(24,18,16,0.4) 0%, rgba(6,6,6,0.55) 100%);
  opacity: 0.65;
}
.wm-row.wm-cleared .wm-name { color: var(--gold); }
.wm-row.wm-cleared::after {
  content: '✓';
  position: absolute;
  top: 8px; right: 12px;
  font-size: 14px;
  color: var(--gold-pale);
  text-shadow: 0 0 8px rgba(240,212,136,0.4);
}
.wm-row.wm-unlocked {
  background: linear-gradient(180deg, rgba(48,28,18,0.7) 0%, rgba(10,6,6,0.85) 100%);
  box-shadow: 0 0 18px rgba(232,184,120,0.22), inset 0 0 22px rgba(232,184,120,0.12);
  animation: wm-unlocked-pulse 2.4s ease-in-out infinite;
}
@keyframes wm-unlocked-pulse {
  0%, 100% { box-shadow: 0 0 18px rgba(232,184,120,0.22), inset 0 0 22px rgba(232,184,120,0.12); }
  50%      { box-shadow: 0 0 26px rgba(232,184,120,0.4),  inset 0 0 30px rgba(232,184,120,0.22); }
}
.wm-row.wm-unlocked .wm-num { color: var(--gold-bright); }
.wm-row.wm-locked { opacity: 0.4; }
.wm-row.wm-locked .wm-name { color: var(--ink-faint); letter-spacing: 0.18em; }
.wm-row.wm-locked .wm-sub  { font-style: normal; }
.wm-row.wm-clickable { cursor: pointer; transition: filter 0.18s, transform 0.12s; }
.wm-row.wm-clickable:hover { filter: brightness(1.18); transform: translateY(-1px); }
.wm-row.wm-clickable:active { transform: translateY(0); }
.wm-continue {
  align-self: center;
  margin-top: 8px;
  background: transparent;
  border: none;
  color: var(--ink);
  font-family: inherit;
  font-size: 11.5px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  cursor: pointer;
  padding: 8px 14px;
  position: relative;
  transition: color 0.18s, text-shadow 0.18s;
}
.wm-continue:hover { color: var(--gold-pale); text-shadow: 0 0 12px rgba(240,212,136,0.45); }
.wm-continue::after {
  content: '';
  position: absolute;
  left: 50%; bottom: 4px;
  width: 0; height: 1px;
  background: var(--gold-pale);
  transition: width 0.2s, left 0.2s;
}
.wm-continue:hover::after { width: 100%; left: 0; }

@media (max-height: 480px) {
  .wm-title    { font-size: 18px; letter-spacing: 0.36em; }
  .wm-subtitle { font-size: 9.5px; }
  .wm-row      { padding: 4px 10px; grid-template-columns: 38px 1fr; gap: 10px; }
  .wm-num      { font-size: 20px; }
  .wm-name     { font-size: 11.5px; letter-spacing: 0.18em; }
  .wm-sub      { font-size: 9.5px; }
  .wm-desc     { font-size: 9.5px; }
}

/* ===========================================================================
   STARTER CHOOSER — horizontal lineup of standing silhouettes.
   Short tap selects; press-and-hold reveals a floating detail card.
   =========================================================================== */
#overlay.overlay-starter #overlay-content {
  max-width: min(1000px, 96vw) !important;
  padding: 12px 18px 14px !important;
}
#overlay.overlay-starter #overlay-title {
  border-bottom: none !important;
  font-size: 16px;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 4px;
}
#overlay.overlay-starter #overlay-title::after { display: none !important; }
.starter-flavor {
  text-align: center;
  font-size: 12px;
  font-style: italic;
  letter-spacing: 0.04em;
  color: var(--ink-dim);
  margin: 0 0 10px;
}
#overlay-choices.starter-choices {
  display: block !important;
}
.starter-lineup {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 14px;
  flex-wrap: nowrap;
  padding: 6px 0 10px;
  position: relative;
}
.starter-lineup.recruit-lineup {
  padding-top: 96px;
}
.starter-lineup::after {
  /* Soft warm floor line under the row */
  content: '';
  position: absolute;
  left: 6%; right: 6%;
  bottom: 4px;
  height: 1px;
  background: linear-gradient(90deg, transparent 0%, rgba(232,220,196,0.18) 50%, transparent 100%);
  pointer-events: none;
}
.starter-fig {
  flex: 1 1 0;
  min-width: 0;
  max-width: 140px;
  /* Portrait carries the moment; sized to leave room for the name +
     start button below in the locked 405px canvas. */
  height: clamp(150px, 56%, 220px);
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  position: relative;
  transition: filter 0.18s, transform 0.12s;
}
.starter-fig:hover { filter: brightness(1.16); transform: translateY(-2px); }
.starter-fig:active { transform: translateY(0); }
/* Locked starters — unlocked heroes who aren't solo-viable (Elin, Garron,
   Veyr) render dimmed with a "needs a partner" tag so the player can
   tell at a glance which roles they've recruited but can't open with.
   Suppress hover/active states since the tile isn't actionable. */
.starter-fig.starter-fig-locked {
  cursor: default;
  filter: grayscale(0.65) brightness(0.55);
  opacity: 0.7;
}
.starter-fig.starter-fig-locked:hover {
  filter: grayscale(0.55) brightness(0.65);
  transform: none;
}
.starter-fig.starter-fig-locked:active { transform: none; }
.starter-locked-tag {
  font-size: 8.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--blood-bright);
  margin-top: 3px;
  font-style: italic;
  opacity: 0.85;
}
.starter-portrait {
  flex: 1 1 auto;
  width: 100%;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  position: relative;
  overflow: hidden;
  -webkit-mask-image: radial-gradient(ellipse 80% 100% at 50% 50%, #000 60%, transparent 100%);
          mask-image: radial-gradient(ellipse 80% 100% at 50% 50%, #000 60%, transparent 100%);
}
.starter-portrait::after {
  content: '';
  position: absolute;
  left: 50%; bottom: 0;
  transform: translateX(-50%);
  width: 70%; height: 12px;
  background: radial-gradient(ellipse 60% 100% at 50% 50%,
                              rgba(0,0,0,0.7) 0%, transparent 80%);
  pointer-events: none;
}
.starter-portrait svg {
  height: 100%;
  width: auto;
  max-width: 90%;
  display: block;
  filter: drop-shadow(0 6px 16px rgba(0,0,0,0.7));
}
.starter-name {
  font-size: 11px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--gold-pale);
  margin-top: 4px;
}

#starter-tooltip {
  position: fixed;
  z-index: 320;
  max-width: 280px;
  padding: 10px 14px 9px;
  background: radial-gradient(ellipse 120% 100% at 50% 0%,
                              rgba(36,28,22,0.97) 0%,
                              rgba(12,10,10,0.98) 70%);
  color: var(--ink);
  font-size: 11px;
  letter-spacing: 0.04em;
  pointer-events: none;
  box-shadow: 0 12px 36px rgba(0,0,0,0.75),
              inset 0 1px 0 rgba(232,220,196,0.06),
              0 0 24px rgba(200,164,100,0.14);
  animation: st-tip-in 0.18s ease-out;
}
@keyframes st-tip-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}
#starter-tooltip .st-name {
  font-size: 13px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold-pale);
  margin-bottom: 2px;
}
#starter-tooltip .st-title {
  font-size: 10px;
  font-style: italic;
  color: var(--ink-dim);
  margin-bottom: 6px;
}
#starter-tooltip .st-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 8px;
  margin-bottom: 6px;
}
#starter-tooltip .st-stat {
  font-size: 9.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  padding: 1px 6px;
  background: rgba(0,0,0,0.45);
  color: var(--gold);
}
#starter-tooltip .st-passive {
  font-size: 10.5px;
  font-style: italic;
  color: var(--ink);
}

@media (max-height: 480px) {
  .starter-fig { max-width: 120px; height: clamp(140px, 32vh, 220px); }
  .starter-name { font-size: 9.5px; letter-spacing: 0.24em; }
  .starter-flavor { font-size: 10.5px; }
}

/* ===========================================================================
   OPENING BOON — the wake vignette stashes a pick on state.run._openingBoon
   and resolveOpeningBoon opens the matching sub-screen.  Both sub-screens
   add .boon-sub to #overlay-choices to force a clean 3-in-a-row layout
   regardless of viewport.
   =========================================================================== */
.rumor-choice .rumor-portrait {
  height: clamp(70px, 14vh, 110px);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  margin-bottom: 4px;
  overflow: hidden;
  -webkit-mask-image: radial-gradient(ellipse 80% 100% at 50% 50%, #000 60%, transparent 100%);
          mask-image: radial-gradient(ellipse 80% 100% at 50% 50%, #000 60%, transparent 100%);
}
.rumor-choice .rumor-portrait svg {
  height: 100%;
  width: auto;
  max-width: 90%;
  display: block;
  filter: drop-shadow(0 4px 12px rgba(0,0,0,0.7));
}
/* Force 3 tiles in one row on every viewport.  Overrides the global
   flex-wrap: wrap that kicks in at max-height: 480px and the fixed
   .sigil-choice width: 200px. */
#overlay-choices.boon-sub {
  display: flex !important;
  flex-direction: row !important;
  flex-wrap: nowrap !important;
  gap: 10px;
  justify-content: center;
  align-items: stretch;
  width: 100%;
}
#overlay-choices.boon-sub .encounter-choice {
  flex: 1 1 0;
  min-width: 0;
  max-width: 240px;
  width: auto !important;
  padding: 10px 12px;
}
#overlay-choices.boon-sub .sigil-glyph {
  font-size: 26px;
  padding: 4px 0 2px;
}
#overlay-choices.boon-sub .sigil-desc {
  font-size: 10.5px;
  line-height: 1.3;
}
#overlay-choices.boon-sub .enc-name {
  font-size: 11.5px;
  letter-spacing: 0.14em;
}
#overlay-choices.boon-sub .rumor-portrait {
  height: clamp(54px, 11vh, 90px);
  margin-bottom: 4px;
}

/* Recruit overlay — chat-bubble vignette layout reusing the lineup.
   Bubbles float above each silhouette with their intro line. */
.starter-eyebrow {
  text-align: center;
  margin: 0 0 4px;
  font-size: 11px;
  letter-spacing: 0.36em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.recruit-fig .recruit-bubble {
  position: absolute;
  /* Float above the portrait with clear space, not draped over the head. */
  bottom: calc(100% + 12px);
  max-width: 220px;
  padding: 6px 12px 8px;
  background: linear-gradient(180deg,
                              rgba(36,28,22,0.94) 0%,
                              rgba(12,10,10,0.96) 100%);
  border-radius: 14px 14px 14px 4px;
  box-shadow:
    0 8px 22px rgba(0,0,0,0.7),
    inset 0 1px 0 rgba(232,220,196,0.06);
  text-align: left;
  z-index: 4;
}
.recruit-fig.recruit-side-left .recruit-bubble  { left: 8px; }
.recruit-fig.recruit-side-right .recruit-bubble { right: 8px; border-radius: 14px 14px 4px 14px; }
.recruit-fig .recruit-bubble .vn-who {
  display: block;
  font-size: 9px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--gold-pale);
  margin-bottom: 2px;
}
.recruit-fig.recruit-side-right .recruit-bubble .vn-who { text-align: right; color: var(--frost-bright); }
.recruit-fig .recruit-bubble .vn-text {
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--ink);
  letter-spacing: 0.02em;
}
.recruit-walk-on {
  display: block;
  margin: 14px auto 0;
  background: transparent;
  border: none;
  color: var(--ink-dim);
  font-family: inherit;
  font-size: 11px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  cursor: pointer;
  padding: 6px 14px;
  position: relative;
  transition: color 0.18s, text-shadow 0.18s;
}
.recruit-walk-on:hover { color: var(--gold-pale); text-shadow: 0 0 12px rgba(240,212,136,0.4); }
.recruit-walk-on::after {
  content: '';
  position: absolute;
  left: 50%; bottom: 4px;
  width: 0; height: 1px;
  background: var(--gold-pale);
  transition: width 0.2s, left 0.2s;
}
.recruit-walk-on:hover::after { width: 100%; left: 0; }

@media (max-height: 480px) {
  .recruit-fig .recruit-bubble { max-width: 180px; padding: 4px 10px 6px; }
  .recruit-fig .recruit-bubble .vn-text { font-size: 10.5px; }
}

/* ===========================================================================
   AWARD FANFARE — full-screen dim + centered reveal when a hero earns or
   loses an affinity, or when a sigil is bound.  Click anywhere on the
   backdrop to dismiss early; auto-fades after a hold.
   =========================================================================== */
.qa-backdrop {
  position: fixed;
  inset: 0;
  z-index: 360;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Near-opaque backdrop — the award is meant to halt play, not float
     translucently over the battlefield.  Subtle radial keeps the focal
     point centered but the underlying scene is essentially blacked out. */
  background: radial-gradient(ellipse at center, rgba(0,0,0,0.94) 0%, rgba(0,0,0,0.99) 80%);
  cursor: pointer;
  animation: qa-bk-in 0.28s ease forwards;
  -webkit-tap-highlight-color: transparent;
}
.qa-backdrop.qa-out {
  /* During the 450ms fade-out, the backdrop is still a fixed full-screen
     element.  Without this, fast-clicking through awards intercepts the
     follow-up clicks meant for the next overlay (recruit / vignette / map),
     which reads to the player as the combat scene stalling. */
  pointer-events: none;
  cursor: default;
  animation: qa-bk-out 0.45s ease forwards;
}
@keyframes qa-bk-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes qa-bk-out {
  from { opacity: 1; }
  to   { opacity: 0; }
}
#quirk-award {
  position: relative;
  text-align: center;
  pointer-events: none;
  max-width: 92vw;
  padding: 0 18px;
  animation: qa-pop-in 0.42s cubic-bezier(.18,.89,.32,1.12) 0.05s both;
}
/* Hero vignette block — portrait + chat bubble stacked above the
   eyebrow/name/desc card.  Bubble matches the in-combat .combat-bark
   styling so the moment reads as the same character voice. */
#quirk-award .qa-vignette {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  margin-bottom: 14px;
}
#quirk-award .qa-portrait {
  height: clamp(72px, 14vh, 110px);
  width: auto;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  -webkit-mask-image: radial-gradient(ellipse 80% 100% at 50% 50%, #000 60%, transparent 100%);
          mask-image: radial-gradient(ellipse 80% 100% at 50% 50%, #000 60%, transparent 100%);
}
#quirk-award .qa-portrait svg {
  height: 100%;
  width: auto;
  max-width: 90%;
  display: block;
  filter: drop-shadow(0 6px 16px rgba(0,0,0,0.7));
}
#quirk-award .qa-bubble {
  display: inline-block;
  padding: 5px 12px 6px;
  background: linear-gradient(180deg,
                              rgba(36,28,22,0.92) 0%,
                              rgba(12,10,10,0.94) 100%);
  border-radius: 14px 14px 14px 4px;
  color: var(--ink);
  font-size: 12px;
  font-style: italic;
  letter-spacing: 0.02em;
  max-width: 320px;
  box-shadow:
    0 6px 16px rgba(0,0,0,0.6),
    inset 0 1px 0 rgba(232,220,196,0.06);
}
.qa-backdrop.qa-out #quirk-award {
  animation: qa-pop-out 0.45s ease forwards;
}
@keyframes qa-pop-in {
  from { opacity: 0; transform: scale(0.94); filter: blur(3px); }
  to   { opacity: 1; transform: scale(1);    filter: blur(0); }
}
@keyframes qa-pop-out {
  from { opacity: 1; transform: scale(1);    filter: blur(0); }
  to   { opacity: 0; transform: scale(1.05); filter: blur(2px); }
}
#quirk-award .qa-eyebrow {
  font-size: 12px;
  letter-spacing: 0.36em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 10px;
}
#quirk-award .qa-name {
  font-size: 30px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold-pale);
  text-shadow:
    0 0 22px rgba(240,212,136,0.55),
    0 0 4px rgba(255,255,255,0.18),
    0 2px 0 rgba(0,0,0,0.85);
  margin-bottom: 10px;
  font-weight: 400;
}
#quirk-award.qa-negative .qa-name {
  color: var(--blood-bright);
  text-shadow:
    0 0 22px rgba(220,80,80,0.55),
    0 0 4px rgba(255,255,255,0.12),
    0 2px 0 rgba(0,0,0,0.85);
}
#quirk-award.qa-negative .qa-eyebrow {
  color: var(--blood-bright);
  opacity: 0.85;
}
#quirk-award.qa-sigil .qa-name {
  color: var(--gold-bright);
}
#quirk-award.qa-sigil.cat-combat .qa-name   { color: var(--blood-bright); }
#quirk-award.qa-sigil.cat-defense .qa-name  { color: var(--armor); }
#quirk-award.qa-sigil.cat-resource .qa-name { color: var(--gold-bright); }
#quirk-award .qa-glyph {
  display: inline-block;
  margin-right: 8px;
  font-size: 26px;
  vertical-align: -3px;
  text-shadow: 0 0 14px currentColor;
}
/* Flavor line — the narrative beat between the hero's name and the rules
   footnote.  Italic, larger, soft gold for affinity / dim red for affliction
   / soft cyan for shed.  Hold this for the longest in the cascade. */
#quirk-award .qa-flavor {
  font-size: 14px;
  font-style: italic;
  letter-spacing: 0.04em;
  color: var(--ink);
  text-shadow: 0 0 10px rgba(232,220,196,0.18), 0 1px 4px rgba(0,0,0,0.85);
  max-width: 520px;
  margin: 0 auto 12px;
  line-height: 1.55;
  font-family: 'Iowan Old Style', Georgia, serif;
}
#quirk-award.qa-positive .qa-flavor { color: #f0e6c8; text-shadow: 0 0 12px rgba(240,212,136,0.32), 0 1px 4px rgba(0,0,0,0.85); }
#quirk-award.qa-negative .qa-flavor { color: #e8c8c8; text-shadow: 0 0 12px rgba(212,90,90,0.32),  0 1px 4px rgba(0,0,0,0.85); }
#quirk-award.qa-shed     .qa-flavor { color: #c8e0f0; text-shadow: 0 0 12px rgba(140,200,232,0.32), 0 1px 4px rgba(0,0,0,0.85); }

/* Reason line — narrator caption explaining WHY this hero earned the
   quirk from THIS fight.  Slightly smaller than the flavor line, sits
   above it, in a quiet ink tone so it reads as voice-over. */
#quirk-award .qa-reason {
  font-size: 12.5px;
  font-style: italic;
  letter-spacing: 0.05em;
  color: var(--ink-dim);
  text-shadow: 0 0 10px rgba(0,0,0,0.65), 0 1px 4px rgba(0,0,0,0.85);
  max-width: 540px;
  margin: 4px auto 10px;
  line-height: 1.6;
  font-family: 'Iowan Old Style', Georgia, serif;
  opacity: 0.92;
}
#quirk-award.qa-positive .qa-reason { color: #d8d2b8; }
#quirk-award.qa-negative .qa-reason { color: #d8b8b8; }

/* Mechanical footnote — now a muted, tabular-feeling line so the eye
   reads it as "the rules, for reference" rather than the headline. */
#quirk-award .qa-desc {
  font-size: 11px;
  font-style: normal;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-dim);
  text-shadow: 0 1px 4px rgba(0,0,0,0.85);
  max-width: 460px;
  margin: 0 auto;
  line-height: 1.5;
  opacity: 0.78;
}

/* Polarity aura — soft radial pulse behind the card, tinted to match the
   moment.  Anchored to the centre of the card via absolute + pointer
   events: none so it can't intercept the dismiss tap. */
#quirk-award .qa-aura {
  position: absolute;
  inset: -120px;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
  z-index: -1;
  animation: qa-aura-pulse 2.8s ease-in-out 0.35s infinite;
}
#quirk-award.qa-positive .qa-aura { background: radial-gradient(circle, rgba(240,212,136,0.22) 0%, rgba(240,212,136,0) 60%); }
#quirk-award.qa-negative .qa-aura { background: radial-gradient(circle, rgba(220,80,80,0.22)   0%, rgba(220,80,80,0)   60%); }
#quirk-award.qa-shed     .qa-aura { background: radial-gradient(circle, rgba(140,200,232,0.22) 0%, rgba(140,200,232,0) 60%); }
#quirk-award.qa-sigil    .qa-aura { background: radial-gradient(circle, rgba(232,220,196,0.20) 0%, rgba(232,220,196,0) 60%); }
@keyframes qa-aura-pulse {
  0%, 100% { opacity: 0.4; transform: scale(0.94); }
  50%      { opacity: 0.85; transform: scale(1.04); }
}

/* Shed-state takes a cooler tint — relief, not pride. */
#quirk-award.qa-shed .qa-eyebrow { color: #a8c8e0; opacity: 0.9; }
#quirk-award.qa-shed .qa-name {
  color: #c8e0f0;
  text-shadow:
    0 0 22px rgba(140,200,232,0.55),
    0 0 4px rgba(255,255,255,0.18),
    0 2px 0 rgba(0,0,0,0.85);
}

/* Cascade — each .qa-row fades in 140ms after the previous so the moment
   unfolds (eyebrow → name → flavor → desc) rather than popping all at
   once.  The portrait/bubble keeps the existing pop-in. */
#quirk-award .qa-row {
  opacity: 0;
  animation: qa-row-in 0.42s ease-out forwards;
}
#quirk-award .qa-row:nth-of-type(1) { animation-delay: 0.10s; }
#quirk-award .qa-row:nth-of-type(2) { animation-delay: 0.25s; }
#quirk-award .qa-row:nth-of-type(3) { animation-delay: 0.45s; }
#quirk-award .qa-row:nth-of-type(4) { animation-delay: 0.62s; }
@keyframes qa-row-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.qa-backdrop.qa-out #quirk-award .qa-row { animation: none; opacity: 1; }

/* ===========================================================================
   COMBAT BARKS — short hero quotes that float over the figure.
   =========================================================================== */
.combat-bark {
  position: absolute;
  z-index: 60;
  transform: translate(-50%, 0);
  padding: 4px 10px 5px;
  background: linear-gradient(180deg,
                              rgba(36,28,22,0.92) 0%,
                              rgba(12,10,10,0.94) 100%);
  border-radius: 12px 12px 12px 4px;
  color: var(--ink);
  font-size: 10.5px;
  font-style: italic;
  letter-spacing: 0.02em;
  white-space: nowrap;
  pointer-events: none;
  box-shadow:
    0 6px 16px rgba(0,0,0,0.6),
    inset 0 1px 0 rgba(232,220,196,0.06);
  animation: bark-in 0.22s cubic-bezier(.18,.89,.32,1) forwards,
             bark-out 0.45s ease-out 1.7s forwards;
}
@keyframes bark-in {
  from { opacity: 0; transform: translate(-50%, 8px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}
@keyframes bark-out {
  from { opacity: 1; transform: translate(-50%, 0); }
  to   { opacity: 0; transform: translate(-50%, -6px); }
}

/* ===========================================================================
   HERO CODEX — title-screen lore screen.
   Full-bleed dark backdrop; vertical scrollable list of hero cards.
   Locked entries silhouette + sealed tag.
   =========================================================================== */
#hero-codex {
  position: fixed;
  inset: 0;
  z-index: 240;
  overflow: hidden;
  animation: hc-fade-in 0.4s ease-out;
}
#hero-codex.hidden { display: none !important; }
@keyframes hc-fade-in { from { opacity: 0; } to { opacity: 1; } }

/* ===========================================================================
   FULL CODEX — Bestiary / Sigils / Resonances.  Shares the dark backdrop
   language with the hero codex but uses a tabbed body instead of a
   single accordion list.  Each tab populates a scrollable row stack;
   sealed (unencountered) entries dim out instead of disappearing so the
   player can see what's still left to discover.
   =========================================================================== */
#full-codex {
  position: fixed;
  inset: 0;
  z-index: 240;
  overflow: hidden;
  animation: hc-fade-in 0.4s ease-out;
}
#full-codex.hidden { display: none !important; }

/* ===========================================================================
   EMBERS SCREEN — meta-currency spend.  Same dark backdrop / card
   language as Codex / Hero codex so the meta-progression family of
   screens reads as one set.  Body holds the balance header + a list
   of unlock rows with tier marks and a per-row purchase button.
   =========================================================================== */
#embers-screen {
  position: fixed;
  inset: 0;
  z-index: 240;
  overflow: hidden;
  animation: hc-fade-in 0.4s ease-out;
}
#embers-screen.hidden { display: none !important; }
.embers-bg {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 80% 70% at 50% 50%,
                    rgba(28,14,8,0.96) 0%, rgba(0,0,0,1) 100%),
    #060403;
}
.embers-card {
  position: relative;
  z-index: 2;
  max-width: 600px;
  margin: 0 auto;
  height: 100%;
  padding: 14px 16px 16px;
  display: flex;
  flex-direction: column;
}
.embers-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 8px;
}
.embers-title {
  font-size: 18px;
  letter-spacing: 0.42em;
  color: var(--gold-bright);
  text-shadow: 0 0 18px rgba(240,212,136,0.5);
  margin: 0;
  font-weight: 400;
}
.embers-close {
  background: transparent;
  border: 1px solid var(--gold-dim);
  color: var(--gold-pale);
  padding: 4px 12px;
  font-size: 16px;
  cursor: pointer;
  font-family: inherit;
}
.embers-close:hover { color: var(--gold-bright); border-color: var(--gold-bright); }
.embers-body {
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding-right: 4px;
}
.embers-balance {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 8px;
  padding: 10px 0;
  border-bottom: 1px solid rgba(240,212,136,0.18);
  margin-bottom: 6px;
}
.embers-balance-glyph {
  font-size: 18px;
  color: var(--gold-bright);
  text-shadow: 0 0 10px rgba(240,212,136,0.75);
}
.embers-balance-num {
  font-size: 28px;
  font-weight: bold;
  color: var(--gold-bright);
  text-shadow: 0 0 12px rgba(240,212,136,0.6);
  font-variant-numeric: tabular-nums;
}
.embers-balance-label {
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.embers-flavor {
  font-size: 11px;
  font-style: italic;
  letter-spacing: 0.02em;
  color: var(--ink-dim);
  text-align: center;
  margin: 0 auto 12px;
  max-width: 480px;
  line-height: 1.5;
}
.embers-rows {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
/* Section headers inside the Embers screen — separate the perk
   ladder from the sealed-hero unseal list so the spend surface
   reads as two clear shopping aisles. */
.embers-section-head {
  font-size: 9.5px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--gold-pale);
  padding: 12px 4px 6px;
  border-bottom: 1px solid rgba(240,212,136,0.18);
  margin-bottom: 6px;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}
.embers-section-head:first-child { padding-top: 4px; }
.embers-section-sub {
  font-size: 9px;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  text-transform: none;
  font-style: italic;
}
/* Grid of sealed-hero tiles — portrait + name/school + cost.  Tap
   to unseal.  Greyed when unaffordable.  Layout wraps to 2-3 columns
   depending on viewport. */
.embers-heroes {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 6px;
  margin-bottom: 12px;
}
.embers-hero {
  display: grid;
  grid-template-columns: 36px 1fr auto;
  align-items: center;
  gap: 8px;
  padding: 5px 10px 5px 6px;
  background: linear-gradient(180deg, rgba(20,18,14,0.55) 0%, rgba(8,6,6,0.7) 100%);
  border: 1px solid rgba(240,212,136,0.18);
  color: var(--ink);
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  transition: all 0.15s;
}
.embers-hero:not(.embers-hero-poor):hover {
  filter: brightness(1.18);
  border-color: var(--gold-bright);
  box-shadow: 0 0 12px rgba(240,212,136,0.32);
  transform: translateY(-1px);
}
.embers-hero:not(.embers-hero-poor):active { transform: translateY(0); }
.embers-hero-poor {
  cursor: not-allowed;
  opacity: 0.45;
  filter: saturate(0.4);
}
.embers-hero-portrait {
  width: 36px; height: 48px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  overflow: hidden;
}
.embers-hero-portrait svg { width: 100%; height: 100%; }
.embers-hero-meta {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}
.embers-hero-name {
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-pale);
}
.embers-hero-stat {
  font-size: 8.5px;
  letter-spacing: 0.08em;
  color: var(--ink-faint);
}
.embers-hero-cost {
  font-size: 10.5px;
  letter-spacing: 0.12em;
  color: var(--gold-bright);
  font-weight: bold;
  text-shadow: 0 0 6px rgba(240,212,136,0.45);
  font-variant-numeric: tabular-nums;
}
.embers-row {
  padding: 8px 10px;
  background: linear-gradient(180deg, rgba(20,18,14,0.55) 0%, rgba(8,6,6,0.7) 100%);
  border-left: 2px solid var(--gold-dim);
}
.embers-row-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 2px;
}
.embers-row-name {
  font-size: 12px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-pale);
}
.embers-row-tier {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 10px;
  color: var(--ink-dim);
}
.embers-tier-mark { color: rgba(240,212,136,0.3); font-size: 10px; }
.embers-tier-filled { color: var(--gold-bright); text-shadow: 0 0 6px rgba(240,212,136,0.7); }
.embers-row-tier-count {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.08em;
  margin-left: 4px;
  font-size: 9px;
  color: var(--ink-faint);
}
.embers-row-flavor {
  font-size: 10px;
  font-style: italic;
  color: var(--ink-faint);
  letter-spacing: 0.02em;
  line-height: 1.4;
  margin-bottom: 6px;
}
.embers-buy {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 6px 12px;
  background: linear-gradient(180deg, rgba(60,40,18,0.7) 0%, rgba(20,12,8,0.85) 100%);
  border: 1px solid var(--gold-dim);
  color: var(--ink);
  font-family: inherit;
  cursor: pointer;
  text-align: left;
  transition: all 0.15s;
}
.embers-buy:not(.embers-buy-disabled):hover {
  filter: brightness(1.15);
  border-color: var(--gold-bright);
  box-shadow: 0 0 12px rgba(240,212,136,0.35);
}
.embers-buy-disabled {
  cursor: not-allowed;
  opacity: 0.5;
  filter: saturate(0.4);
}
.embers-buy-cost {
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--gold-bright);
  font-weight: bold;
  text-shadow: 0 0 6px rgba(240,212,136,0.5);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.embers-buy-label {
  font-size: 10.5px;
  letter-spacing: 0.04em;
  color: var(--ink);
}
.embers-buy-maxed {
  text-align: center;
  padding: 6px 12px;
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold);
  font-style: italic;
  background: rgba(20,14,8,0.4);
  border: 1px solid rgba(240,212,136,0.18);
}

/* Active-slot count chip on the balance header — surfaces the "X / N
   active" budget so the player sees what's equippable at a glance. */
.embers-active-count {
  margin-left: auto;
  font-size: 9.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold);
  font-style: italic;
  font-variant-numeric: tabular-nums;
  padding: 3px 10px;
  background: rgba(20,14,8,0.5);
  border: 1px solid rgba(240,212,136,0.22);
}

/* Active perks get a gold glow + accent border so the player can
   see which of their purchased unlocks are currently burning. */
.embers-row.embers-row-active {
  border-left: 2px solid var(--gold-bright);
  box-shadow: inset 0 0 0 1px rgba(240,212,136,0.1), 0 0 14px rgba(240,212,136,0.18);
}

.embers-row-actions {
  display: flex;
  gap: 8px;
  align-items: stretch;
}
.embers-row-actions .embers-buy { flex: 1 1 auto; }

/* Equip toggle button — sits next to the Buy button.  Three visual
   states: unequipped (gold-border outline), equipped (filled gold),
   disabled-but-unowned (no render), disabled-because-full (greyed). */
.embers-equip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 6px 14px;
  background: transparent;
  border: 1px solid var(--gold-dim);
  color: var(--gold-pale);
  font-family: inherit;
  font-size: 9.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  cursor: pointer;
  white-space: nowrap;
  transition: all 0.15s;
  flex: 0 0 auto;
}
.embers-equip:not(.embers-equip-disabled):not(.embers-equip-active):hover {
  border-color: var(--gold-bright);
  color: var(--gold-bright);
  background: rgba(240,212,136,0.08);
}
.embers-equip.embers-equip-active {
  background: linear-gradient(180deg, var(--gold) 0%, var(--gold-deep) 100%);
  color: #1a0e04;
  border-color: var(--gold-bright);
  text-shadow: 0 1px 0 rgba(255,240,184,0.4);
  box-shadow: 0 0 10px rgba(240,212,136,0.5);
}
.embers-equip.embers-equip-disabled {
  cursor: not-allowed;
  opacity: 0.4;
  filter: saturate(0.4);
}

/* ===========================================================================
   ASCENSION PICKER — overlay row list of available difficulty rungs.
   Each row stacks modifier descriptions so the player sees what they
   opt into.  Active row glows gold.  Reached from Settings only.
   =========================================================================== */
.asc-flavor {
  font-size: 11px;
  font-style: italic;
  letter-spacing: 0.02em;
  color: var(--ink-dim);
  text-align: center;
  margin: 0 auto 10px;
  line-height: 1.5;
}
.asc-grid {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 280px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding-right: 4px;
}
.asc-row {
  width: 100%;
  text-align: left;
  background: linear-gradient(180deg, rgba(20,18,14,0.55) 0%, rgba(8,6,6,0.7) 100%);
  border: 1px solid var(--gold-dim);
  color: var(--ink);
  font-family: inherit;
  padding: 8px 12px;
  cursor: pointer;
  transition: all 0.15s;
}
.asc-row:hover {
  filter: brightness(1.15);
  border-color: var(--gold-bright);
  box-shadow: 0 0 10px rgba(240,212,136,0.3);
}
.asc-row.asc-row-active {
  background: linear-gradient(180deg, rgba(60,40,18,0.85) 0%, rgba(14,10,8,0.92) 100%);
  border-color: var(--gold-bright);
  box-shadow: 0 0 14px rgba(240,212,136,0.35), inset 0 0 0 1px rgba(240,212,136,0.15);
}
.asc-row-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 4px;
}
.asc-level {
  font-size: 11px;
  letter-spacing: 0.32em;
  color: var(--gold-bright);
  font-weight: bold;
  font-variant-numeric: tabular-nums;
}
.asc-name {
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-pale);
}
.asc-mult {
  margin-left: auto;
  font-size: 9.5px;
  letter-spacing: 0.1em;
  color: var(--gold);
  font-style: italic;
}
.asc-mods {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.asc-mod {
  font-size: 10px;
  letter-spacing: 0.02em;
  color: var(--ink);
  padding-left: 14px;
  position: relative;
  line-height: 1.4;
}
.asc-mod::before {
  content: '·';
  position: absolute;
  left: 4px;
  color: var(--gold);
}
.asc-mods-empty {
  font-size: 10px;
  font-style: italic;
  color: var(--ink-faint);
  letter-spacing: 0.02em;
}
.asc-foot {
  font-size: 9.5px;
  font-style: italic;
  letter-spacing: 0.02em;
  color: var(--ink-faint);
  text-align: center;
  margin: 10px auto 0;
}

/* ===========================================================================
   BONDS SCREEN — shows every hero pair the player has shared fights
   with, sorted by count.  Tier labels (Companions → Kindred → Sworn →
   Bonded) escalate with shared-fight thresholds (1 / 10 / 25 / 50).
   Layout: portrait-pair on the left, name + tier + progress bar on the
   right, stat line underneath.
   =========================================================================== */
#bonds-screen {
  position: fixed;
  inset: 0;
  z-index: 240;
  overflow: hidden;
  animation: hc-fade-in 0.4s ease-out;
}
#bonds-screen.hidden { display: none !important; }
.bonds-bg {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 80% 70% at 50% 50%,
                    rgba(14,12,18,0.96) 0%, rgba(0,0,0,1) 100%),
    #040305;
}
.bonds-card {
  position: relative;
  z-index: 2;
  max-width: 680px;
  margin: 0 auto;
  height: 100%;
  padding: 14px 16px 16px;
  display: flex;
  flex-direction: column;
}
.bonds-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 8px;
}
.bonds-title {
  font-size: 18px;
  letter-spacing: 0.42em;
  color: var(--gold-pale);
  text-shadow: 0 0 18px rgba(240,212,136,0.35);
  margin: 0;
  font-weight: 400;
}
.bonds-close {
  background: transparent;
  border: 1px solid var(--gold-dim);
  color: var(--gold-pale);
  padding: 4px 12px;
  font-size: 16px;
  cursor: pointer;
  font-family: inherit;
}
.bonds-close:hover { color: var(--gold-bright); border-color: var(--gold-bright); }
.bonds-body {
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding-right: 4px;
}
.bonds-flavor {
  font-size: 11px;
  font-style: italic;
  letter-spacing: 0.02em;
  color: var(--ink-dim);
  text-align: center;
  margin: 0 auto 12px;
  max-width: 520px;
  line-height: 1.5;
}
.bonds-empty {
  font-size: 11px;
  font-style: italic;
  color: var(--ink-faint);
  text-align: center;
  padding: 20px;
}
.bonds-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.bond-row {
  display: grid;
  grid-template-columns: 88px 1fr;
  gap: 12px;
  padding: 8px 10px;
  background: linear-gradient(180deg, rgba(20,18,14,0.55) 0%, rgba(8,6,6,0.7) 100%);
  border-left: 2px solid rgba(232,220,196,0.18);
}
.bond-row.bond-tier-2 { border-left-color: rgba(240,212,136,0.55); }
.bond-row.bond-tier-3 { border-left-color: var(--gold-bright); }
.bond-row.bond-tier-4 {
  border-left-color: var(--gold-bright);
  box-shadow: inset 0 0 0 1px rgba(240,212,136,0.18), 0 0 20px rgba(240,212,136,0.18);
}
.bond-portraits {
  display: flex;
  gap: -8px;
  align-items: flex-end;
  position: relative;
  height: 64px;
}
.bond-portrait {
  width: 44px; height: 64px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  overflow: hidden;
}
.bond-portrait + .bond-portrait { margin-left: -10px; }
.bond-portrait svg { width: 100%; height: 100%; }
.bond-body { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.bond-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
}
.bond-names {
  font-size: 11.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold-pale);
}
.bond-amp { color: var(--ink-faint); margin: 0 4px; }
.bond-tier {
  font-size: 9px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ink-dim);
  padding: 1px 6px;
  background: rgba(0,0,0,0.45);
}
.bond-row.bond-tier-2 .bond-tier { color: var(--gold-pale); background: rgba(60,40,18,0.5); }
.bond-row.bond-tier-3 .bond-tier { color: var(--gold-bright); background: rgba(80,50,20,0.6); text-shadow: 0 0 6px rgba(240,212,136,0.4); }
.bond-row.bond-tier-4 .bond-tier { color: var(--gold-bright); background: rgba(120,72,28,0.7); text-shadow: 0 0 10px rgba(240,212,136,0.7); }
.bond-bar {
  height: 4px;
  background: rgba(232,220,196,0.08);
  border-radius: 2px;
  overflow: hidden;
}
.bond-bar-fill {
  height: 100%;
  background: linear-gradient(90deg, rgba(240,212,136,0.5) 0%, var(--gold-bright) 100%);
  transition: width 0.3s ease-out;
}
.bond-row.bond-tier-4 .bond-bar-fill {
  background: linear-gradient(90deg, var(--gold-bright) 0%, #fff5d0 100%);
  box-shadow: 0 0 8px rgba(240,212,136,0.5);
}
.bond-stat {
  font-size: 9.5px;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
.bond-stat b { color: var(--ink); font-weight: 600; }
@media (max-width: 720px) {
  .bond-row { grid-template-columns: 70px 1fr; gap: 8px; padding: 6px 8px; }
  .bond-portrait { width: 36px; height: 52px; }
  .bond-portraits { height: 52px; }
  .bond-names { font-size: 10.5px; }
  .bond-tier { font-size: 8.5px; }
}

/* ===========================================================================
   SIGIL REROLL — secondary action below the three offer cards on a
   sigil-pick screen.  Renders into #overlay-body (NOT the choices
   grid) so it sits on its own row.  Hidden entirely unless the
   player has purchased the Embers Second Look unlock.  Disabled
   state ("0 left") is shown once the layer's budget is spent.
   =========================================================================== */
.sigil-reroll-bar {
  display: flex;
  justify-content: center;
  margin: 10px auto 4px;
  padding: 0 16px;
}
.sigil-reroll-btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 6px 18px;
  background: linear-gradient(180deg, rgba(40,30,18,0.78) 0%, rgba(14,10,8,0.88) 100%);
  border: 1px solid var(--gold-dim);
  color: var(--ink);
  font-family: inherit;
  font-size: 10.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  cursor: pointer;
  transition: all 0.18s;
  box-shadow:
    0 4px 14px rgba(0,0,0,0.55),
    inset 0 1px 0 rgba(232,220,196,0.08);
}
.sigil-reroll-btn:not(.sigil-reroll-disabled):hover {
  filter: brightness(1.2);
  border-color: var(--gold-bright);
  color: var(--gold-pale);
  box-shadow:
    0 4px 14px rgba(0,0,0,0.55),
    0 0 16px rgba(240,212,136,0.45),
    inset 0 1px 0 rgba(232,220,196,0.12);
  transform: translateY(-1px);
}
.sigil-reroll-btn:not(.sigil-reroll-disabled):active { transform: translateY(0); }
.sigil-reroll-disabled {
  cursor: not-allowed;
  opacity: 0.5;
  filter: saturate(0.4);
}
.sigil-reroll-icon {
  font-size: 15px;
  color: var(--gold-bright);
  text-shadow: 0 0 8px rgba(240,212,136,0.55);
  line-height: 1;
}
.sigil-reroll-disabled .sigil-reroll-icon {
  color: var(--ink-faint);
  text-shadow: none;
}
.sigil-reroll-label {
  letter-spacing: 0.22em;
  color: var(--gold-pale);
  font-weight: 600;
}
.sigil-reroll-disabled .sigil-reroll-label { color: var(--ink-faint); }
.sigil-reroll-count {
  font-size: 9px;
  letter-spacing: 0.12em;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
  border-left: 1px solid rgba(232,220,196,0.2);
  padding-left: 10px;
  text-transform: lowercase;
}

/* ===========================================================================
   ACHIEVEMENT FANFARE — brief top-of-screen card when an achievement
   unlocks.  Slides in from the right, holds ~3s, slides out.  Doesn't
   block input (pointer-events: none on the wrapper) so it never gates
   what the player is doing.
   =========================================================================== */
.achievement-fanfare {
  position: fixed;
  top: 60px;
  right: 16px;
  z-index: 260;
  max-width: 320px;
  padding: 8px 14px;
  background: linear-gradient(180deg, rgba(48,32,18,0.95) 0%, rgba(14,10,8,0.97) 100%);
  border-left: 3px solid var(--gold-bright);
  border-radius: 2px;
  box-shadow:
    0 10px 32px rgba(0,0,0,0.85),
    0 0 32px rgba(240,212,136,0.42),
    inset 0 1px 0 rgba(232,220,196,0.12);
  pointer-events: none;
  animation: ach-in 0.32s ease-out;
}
.achievement-fanfare.ach-out { animation: ach-out 0.42s ease-in forwards; }
@keyframes ach-in {
  from { opacity: 0; transform: translateX(40px); }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes ach-out {
  from { opacity: 1; transform: translateX(0); }
  to   { opacity: 0; transform: translateX(40px); }
}
.achievement-fanfare .ach-eyebrow {
  font-size: 8.5px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--gold-pale);
  margin-bottom: 2px;
  opacity: 0.85;
}
.achievement-fanfare .ach-name {
  font-size: 13px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold-bright);
  text-shadow: 0 0 12px rgba(240,212,136,0.6);
  font-weight: 600;
  margin-bottom: 3px;
}
.achievement-fanfare .ach-desc {
  font-size: 10px;
  font-style: italic;
  color: var(--ink);
  letter-spacing: 0.02em;
  line-height: 1.45;
}
.achievement-fanfare .ach-reward {
  margin-top: 4px;
  font-size: 9.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gold-bright);
  text-shadow: 0 0 6px rgba(240,212,136,0.5);
  font-weight: 600;
}

/* Achievement codex row — shares the codex-row layout but uses a
   single glyph column instead of a portrait.  Earned rows glow gold;
   unearned are sealed (the same dim treatment used elsewhere). */
.codex-row.codex-row-ach {
  grid-template-columns: 26px 1fr;
}
.codex-ach-mark {
  width: 26px; height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  color: var(--gold-bright);
  text-shadow: 0 0 10px rgba(240,212,136,0.6);
}
.codex-row.codex-row-ach.codex-row-sealed .codex-ach-mark {
  color: var(--ink-faint);
  text-shadow: none;
}

/* Run summary highlights — MVP hero + biggest single hit.  Two side-
   by-side tiles between the stats line and the Embers banner.
   Portrait + label + value + subline.  Either tile renders alone if
   the other is unavailable (e.g., player fled before dealing damage). */
.rs-highlights {
  display: flex;
  gap: 10px;
  justify-content: center;
  flex-wrap: wrap;
  margin: 8px auto 4px;
  max-width: 460px;
}
.rs-highlight {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 10px 5px 6px;
  background: linear-gradient(180deg, rgba(28,20,14,0.7) 0%, rgba(10,8,8,0.85) 100%);
  border-left: 2px solid var(--gold-bright);
  min-width: 160px;
}
.rs-highlight-portrait {
  width: 36px; height: 44px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  overflow: hidden;
  flex-shrink: 0;
}
.rs-highlight-portrait svg { width: 100%; height: 100%; }
.rs-highlight-body {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}
.rs-highlight-label {
  font-size: 8.5px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.rs-highlight-value {
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold-bright);
  font-weight: 600;
  text-shadow: 0 0 8px rgba(240,212,136,0.4);
  font-variant-numeric: tabular-nums;
}
.rs-highlight-sub {
  font-size: 9px;
  letter-spacing: 0.04em;
  color: var(--ink-faint);
  font-style: italic;
}

/* Run summary Embers banner — sits between the stats line and the
   memorial/roadkill blocks.  Banked at run end so the count is solid
   by the time this renders. */
.rs-embers {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 8px;
  margin: 8px auto;
  padding: 6px 14px;
  background: linear-gradient(180deg, rgba(60,40,18,0.55) 0%, rgba(20,12,8,0.7) 100%);
  border: 1px solid var(--gold-dim);
  max-width: 360px;
}
.rs-embers-glyph {
  color: var(--gold-bright);
  text-shadow: 0 0 8px rgba(240,212,136,0.6);
  font-size: 14px;
}
.rs-embers b {
  font-size: 16px;
  color: var(--gold-bright);
  font-variant-numeric: tabular-nums;
  text-shadow: 0 0 6px rgba(240,212,136,0.4);
}
.rs-embers em {
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-dim);
  font-style: normal;
}
.rs-embers-total {
  margin-left: auto;
  font-size: 9.5px;
  letter-spacing: 0.08em;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
@media (max-width: 720px) {
  .embers-row-name { font-size: 11px; }
  .embers-row-flavor { font-size: 9.5px; }
  .embers-balance-num { font-size: 24px; }
}
.codex-bg {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 80% 70% at 50% 50%,
                    rgba(20,14,16,0.94) 0%, rgba(0,0,0,1) 100%),
    #050405;
}
.codex-card {
  position: relative;
  z-index: 2;
  max-width: 760px;
  margin: 0 auto;
  height: 100%;
  padding: 14px 16px 16px;
  display: flex;
  flex-direction: column;
}
.codex-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 8px;
}
.codex-title {
  font-size: 18px;
  letter-spacing: 0.42em;
  color: var(--gold-pale);
  text-shadow: 0 0 18px rgba(240,212,136,0.35);
  margin: 0;
  font-weight: 400;
}
.codex-close {
  background: transparent;
  border: 1px solid var(--gold-dim);
  color: var(--gold-pale);
  padding: 4px 12px;
  font-size: 16px;
  cursor: pointer;
  font-family: inherit;
}
.codex-close:hover { color: var(--gold-bright); border-color: var(--gold-bright); }
.codex-body {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
  gap: 8px;
}
.codex-tabs {
  display: flex;
  gap: 4px;
  flex-shrink: 0;
}
.codex-tab {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  padding: 6px 8px;
  background: rgba(20,18,14,0.55);
  border: 1px solid rgba(232,220,196,0.18);
  color: var(--ink-dim);
  font-family: inherit;
  cursor: pointer;
  transition: all 0.15s;
}
.codex-tab:hover {
  background: rgba(40,30,20,0.65);
  color: var(--gold-pale);
}
.codex-tab.codex-tab-active {
  background: linear-gradient(180deg, rgba(50,36,20,0.85) 0%, rgba(20,12,8,0.92) 100%);
  border-color: var(--gold-dim);
  color: var(--gold-bright);
}
.codex-tab-label {
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  font-weight: 600;
}
.codex-tab-count {
  font-size: 9px;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
.codex-tab.codex-tab-active .codex-tab-count { color: var(--gold-pale); }
.codex-list {
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding-right: 4px;
}
.codex-group { margin-bottom: 10px; }
.codex-group-head {
  font-size: 9.5px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-dim);
  padding: 6px 4px 4px;
  border-bottom: 1px solid rgba(232,220,196,0.12);
  margin-bottom: 4px;
}
.codex-row {
  display: grid;
  grid-template-columns: 44px 1fr;
  gap: 10px;
  padding: 5px 8px;
  background: linear-gradient(180deg, rgba(20,18,14,0.45) 0%, rgba(8,6,6,0.6) 100%);
  margin-bottom: 3px;
  align-items: center;
}
.codex-row-sealed {
  opacity: 0.45;
  filter: grayscale(0.7);
}
.codex-portrait {
  width: 44px; height: 56px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  overflow: hidden;
  font-size: 22px;
  color: var(--ink-faint);
}
.codex-portrait svg { width: 100%; height: 100%; }
.codex-row-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.codex-row-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
}
.codex-row-name {
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-pale);
}
.codex-row-stat {
  font-size: 9px;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
.codex-row-meta {
  font-size: 9.5px;
  letter-spacing: 0.04em;
  color: var(--ink-dim);
  display: flex;
  gap: 14px;
}
.codex-row-desc {
  font-size: 10px;
  font-style: italic;
  letter-spacing: 0.02em;
  color: var(--ink);
  line-height: 1.4;
}
.codex-weak {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px; height: 18px;
  border-radius: 50%;
  font-size: 12px;
  background: rgba(0,0,0,0.55);
  border: 1px solid rgba(232,220,196,0.3);
}
.codex-weak.weak-physical { color: #f0d8a8; border-color: #d8c2a0; }
.codex-weak.weak-holy     { color: #fbeec5; border-color: #f4e6b8; }
.codex-weak.weak-arcane   { color: #e7c5ff; border-color: #c9a8e8; }
.codex-weak.weak-ranged   { color: #c8e0bb; border-color: #a8c8a0; }
.codex-weak.weak-stealth  { color: #c8d4ec; border-color: #a8b8d0; }
.codex-weak.codex-weak-unknown {
  color: var(--ink-faint);
  font-style: italic;
}
/* Hero row inside the codex — clickable; opens the full Hero codex
   on tap (preserves the deep dive that used to live as its own menu
   item).  Sealed rows show ◇ until the hero has been recruited or
   unsealed via the Embers price tag inside the row. */
.codex-row.codex-row-hero {
  cursor: default;
  transition: background 0.15s;
}
.codex-row.codex-row-hero:not(.codex-row-sealed):hover {
  background: linear-gradient(180deg, rgba(40,30,18,0.6) 0%, rgba(16,10,8,0.7) 100%);
}
.codex-hero-unseal {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
  padding: 4px 10px;
  background: linear-gradient(180deg, rgba(60,40,18,0.7) 0%, rgba(20,12,8,0.85) 100%);
  border: 1px solid var(--gold-dim);
  color: var(--ink);
  font-family: inherit;
  font-size: 9.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  cursor: pointer;
  align-self: flex-start;
  transition: all 0.15s;
}
.codex-hero-unseal:not(.codex-hero-unseal-poor):hover {
  filter: brightness(1.18);
  border-color: var(--gold-bright);
  box-shadow: 0 0 10px rgba(240,212,136,0.4);
}
.codex-hero-unseal-poor {
  cursor: not-allowed;
  opacity: 0.45;
  filter: saturate(0.4);
}
.codex-hero-unseal-cost {
  color: var(--gold-bright);
  font-weight: bold;
  text-shadow: 0 0 6px rgba(240,212,136,0.5);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.06em;
}
.codex-hero-unseal-label {
  color: var(--ink);
  letter-spacing: 0.22em;
}

/* Equipped-charm banner at the top of the Codex Bonds tab.  Surfaces
   what's locked in for the next run so the player can read their
   loadout at a glance.  Empty state explains how to unlock charms. */
.codex-charm-equipped {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 8px 10px;
  margin-bottom: 8px;
  background: linear-gradient(180deg, rgba(40,30,18,0.72) 0%, rgba(14,10,8,0.85) 100%);
  border: 1px solid var(--gold-dim);
}
.codex-charm-equipped-label {
  font-size: 8.5px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--gold-pale);
}
.codex-charm-equipped-name {
  font-size: 11.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-bright);
  text-shadow: 0 0 8px rgba(240,212,136,0.5);
}
.codex-charm-equipped-effect {
  font-size: 10px;
  font-style: italic;
  color: var(--ink);
  letter-spacing: 0.02em;
}
.codex-charm-equipped-empty {
  font-size: 10px;
  font-style: italic;
  color: var(--ink-faint);
  flex: 1 1 100%;
}
.codex-charm-unequip {
  margin-left: auto;
  background: transparent;
  border: 1px solid var(--gold-dim);
  color: var(--gold-pale);
  font-family: inherit;
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  padding: 3px 10px;
  cursor: pointer;
  transition: all 0.15s;
}
.codex-charm-unequip:hover {
  color: var(--gold-bright);
  border-color: var(--gold-bright);
  background: rgba(240,212,136,0.08);
}

/* Charm row inside each bond entry — shows the pair's keepsake +
   Equip button when unlocked, locked hint until Kindred. */
.codex-bond-charm {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 4px;
  padding: 5px 8px;
  background: rgba(0,0,0,0.32);
  border-left: 2px solid rgba(240,212,136,0.4);
}
.codex-bond-charm.codex-bond-charm-locked {
  border-left-color: rgba(232,220,196,0.15);
  opacity: 0.65;
}
.codex-bond-charm-info {
  display: flex;
  flex-direction: column;
  gap: 1px;
  flex: 1 1 auto;
  min-width: 0;
}
.codex-bond-charm-name {
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold-pale);
}
.codex-bond-charm.codex-bond-charm-locked .codex-bond-charm-name { color: var(--ink-faint); }
.codex-bond-charm-effect {
  font-size: 9px;
  font-style: italic;
  letter-spacing: 0.02em;
  color: var(--ink-dim);
  line-height: 1.4;
}
.codex-bond-charm-btn {
  background: linear-gradient(180deg, rgba(60,40,18,0.7) 0%, rgba(20,12,8,0.85) 100%);
  border: 1px solid var(--gold-dim);
  color: var(--ink);
  font-family: inherit;
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  padding: 3px 10px;
  cursor: pointer;
  transition: all 0.15s;
  white-space: nowrap;
}
.codex-bond-charm-btn:hover {
  filter: brightness(1.18);
  border-color: var(--gold-bright);
  box-shadow: 0 0 8px rgba(240,212,136,0.32);
}
.codex-bond-charm-btn.codex-bond-charm-btn-active {
  background: linear-gradient(180deg, var(--gold) 0%, var(--gold-deep) 100%);
  color: #1a0e04;
  border-color: var(--gold-bright);
  text-shadow: 0 1px 0 rgba(255,240,184,0.4);
  box-shadow: 0 0 10px rgba(240,212,136,0.5);
}
.codex-bond-charm-lock {
  font-size: 8.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-style: italic;
  white-space: nowrap;
}

/* Bond row inside the codex — same as the dedicated Bonds screen but
   one column wider since the codex card is narrower than the bonds
   card.  Portraits stack with a -10px overlap so they read as a pair. */
.codex-row.codex-row-bond { grid-template-columns: 70px 1fr; }
.codex-row.codex-row-bond.bond-tier-2 { border-left: 2px solid rgba(240,212,136,0.55); }
.codex-row.codex-row-bond.bond-tier-3 { border-left: 2px solid var(--gold-bright); }
.codex-row.codex-row-bond.bond-tier-4 {
  border-left: 2px solid var(--gold-bright);
  box-shadow: inset 0 0 0 1px rgba(240,212,136,0.18), 0 0 16px rgba(240,212,136,0.18);
}
.codex-bond-portraits {
  display: flex;
  align-items: flex-end;
  position: relative;
  height: 50px;
}
.codex-bond-portrait {
  width: 36px; height: 50px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  overflow: hidden;
}
.codex-bond-portrait + .codex-bond-portrait { margin-left: -10px; }
.codex-bond-portrait svg { width: 100%; height: 100%; }
.codex-bond-bar {
  height: 3px;
  background: rgba(232,220,196,0.08);
  border-radius: 2px;
  overflow: hidden;
  margin: 2px 0;
}
.codex-bond-bar-fill {
  height: 100%;
  background: linear-gradient(90deg, rgba(240,212,136,0.5) 0%, var(--gold-bright) 100%);
}
.codex-row.codex-row-bond.bond-tier-4 .codex-bond-bar-fill {
  background: linear-gradient(90deg, var(--gold-bright) 0%, #fff5d0 100%);
  box-shadow: 0 0 6px rgba(240,212,136,0.5);
}
.codex-empty {
  font-size: 11px;
  font-style: italic;
  color: var(--ink-faint);
  text-align: center;
  padding: 20px;
}

/* NEW dot on Codex tabs — surfaces on tabs that have just become
   available (unlock criterion met) but the player hasn't opened yet.
   Cleared the moment the tab is activated.  Small gold pulse so it
   draws the eye without being loud. */
.codex-tab-newdot {
  display: inline-block;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--gold-bright);
  margin-left: 6px;
  box-shadow: 0 0 6px var(--gold-bright), 0 0 12px rgba(240,212,136,0.6);
  vertical-align: super;
  animation: codex-newdot-pulse 1.8s ease-in-out infinite;
}
@keyframes codex-newdot-pulse {
  0%, 100% { opacity: 0.6; transform: scale(0.85); }
  50%      { opacity: 1;   transform: scale(1.15); }
}

/* Title text flash — brief gold pulse confirmation that the dev-tools
   long-press gesture registered.  Auto-clears after 500ms. */
.ts-title.ts-title-flash {
  animation: ts-title-flash 0.5s ease-out;
}
@keyframes ts-title-flash {
  0%   { text-shadow: 0 0 30px rgba(240,212,136,0.7), 0 0 60px rgba(240,212,136,0.45); }
  100% { text-shadow: none; }
}

/* 6-tab fitness — bestiary tabs now host 6 entries (Heroes + Bestiary
   + Sigils + Resonances + Bonds + Achievements).  Tighten the per-tab
   label so they still fit the 720px design canvas without wrapping. */
.codex-tabs { gap: 2px; }
.codex-tab { padding: 5px 4px; }
.codex-tab-label { font-size: 9.5px; letter-spacing: 0.14em; }
.codex-tab-count { font-size: 8px; }
@media (max-width: 480px) {
  .codex-tab-label { font-size: 8.5px; letter-spacing: 0.1em; }
  .codex-tab-count { font-size: 7.5px; }
  .codex-tab { padding: 4px 2px; }
}

/* Sigil row — icon-only column instead of a portrait box. */
.codex-row.codex-row-sigil { grid-template-columns: 32px 1fr; }
.codex-sigil-icon {
  width: 32px; height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
  border-radius: 4px;
  background: rgba(0,0,0,0.45);
}
.codex-row-sigil.cat-combat   .codex-sigil-icon { color: var(--blood-bright); border: 1px solid rgba(212,69,69,0.4); }
.codex-row-sigil.cat-defense  .codex-sigil-icon { color: var(--armor);        border: 1px solid rgba(156,170,208,0.4); }
.codex-row-sigil.cat-resource .codex-sigil-icon { color: var(--gold-bright); border: 1px solid rgba(240,212,136,0.4); }
/* Resonance row — no portrait column at all. */
.codex-row.codex-row-combo { grid-template-columns: 1fr; }
.codex-row-combo.tier-triple .codex-row-name { color: var(--gold-bright); }
.codex-row-combo.tier-sig    .codex-row-name { color: #c0d8f4; }
@media (max-width: 720px) {
  .codex-tab-label { font-size: 10px; letter-spacing: 0.18em; }
  .codex-tab-count { font-size: 8.5px; }
  .codex-row { grid-template-columns: 36px 1fr; gap: 6px; padding: 4px 6px; }
  .codex-row.codex-row-sigil { grid-template-columns: 26px 1fr; }
  .codex-sigil-icon { width: 26px; height: 26px; font-size: 15px; }
  .codex-portrait { width: 36px; height: 48px; }
  .codex-row-name { font-size: 10px; }
  .codex-row-desc { font-size: 9.5px; }
}
#hero-codex .hc-bg {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 80% 70% at 50% 50%,
                    rgba(20,14,16,0.94) 0%, rgba(0,0,0,1) 100%),
    #050405;
}
#hero-codex .hc-content {
  position: relative;
  z-index: 2;
  max-width: 720px;
  margin: 0 auto;
  height: 100%;
  padding: 14px 16px 16px;
  display: flex;
  flex-direction: column;
}
.hc-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 8px;
}
.hc-title-h {
  font-size: 18px;
  letter-spacing: 0.42em;
  color: var(--gold-pale);
  text-shadow: 0 0 18px rgba(240,212,136,0.35);
  margin: 0;
  font-weight: 400;
}
.hc-close {
  background: transparent;
  border: none;
  font-family: inherit;
  font-size: 22px;
  line-height: 1;
  color: var(--ink-dim);
  cursor: pointer;
  padding: 2px 8px;
}
.hc-close:hover { color: var(--gold-pale); }
.hc-list {
  flex: 1;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 4px 4px 8px;
}
/* Collapsed row — portrait, name, title.  Tapping the head toggles the
   body.  Single-expand: opening a row closes the rest. */
.hc-row {
  background: linear-gradient(180deg, rgba(22,18,18,0.5) 0%, rgba(8,6,8,0.7) 100%);
  border-left: 2px solid rgba(212,200,168,0.18);
  transition: border-left-color 0.18s, background 0.18s;
}
.hc-row.hc-locked { opacity: 0.55; filter: grayscale(0.7); }
.hc-row.hc-open {
  border-left-color: var(--gold-bright);
  background: linear-gradient(180deg, rgba(28,22,18,0.7) 0%, rgba(10,8,8,0.85) 100%);
}
.hc-row-head {
  width: 100%;
  display: grid;
  grid-template-columns: 44px 1fr auto;
  gap: 10px;
  align-items: center;
  padding: 6px 10px;
  background: transparent;
  border: none;
  color: inherit;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}
.hc-row-head:hover { background: rgba(32,24,20,0.4); }
.hc-portrait {
  width: 44px; height: 58px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  overflow: hidden;
  -webkit-mask-image: radial-gradient(ellipse 80% 100% at 50% 50%, #000 60%, transparent 100%);
          mask-image: radial-gradient(ellipse 80% 100% at 50% 50%, #000 60%, transparent 100%);
}
.hc-portrait svg { height: 100%; width: auto; }
.hc-row-meta {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 2px;
}
.hc-name {
  font-size: 12px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold-pale);
}
.hc-title {
  font-size: 9.5px;
  font-style: italic;
  color: var(--ink-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.hc-chev {
  font-size: 22px;
  font-weight: 200;
  line-height: 1;
  color: var(--ink-dim);
  transition: transform 0.18s ease, color 0.18s ease;
  margin-right: 2px;
}
.hc-row.hc-open .hc-chev {
  transform: rotate(90deg);
  color: var(--gold-bright);
  filter: drop-shadow(0 0 6px rgba(240,212,136,0.55));
}
.hc-row-body {
  padding: 4px 12px 10px 64px;
  border-top: 1px solid rgba(212,200,168,0.08);
  animation: hc-body-in 0.18s ease-out;
}
@keyframes hc-body-in {
  from { opacity: 0; transform: translateY(-2px); }
  to   { opacity: 1; transform: translateY(0); }
}
.hc-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 8px;
  margin-bottom: 6px;
}
.hc-stat {
  font-size: 9.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  padding: 1px 6px;
  background: rgba(0,0,0,0.45);
  color: var(--gold);
}
.hc-passive {
  font-size: 11px;
  font-style: italic;
  color: var(--ink);
  margin-bottom: 6px;
}
.hc-section {
  font-size: 9px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin: 4px 0 4px;
}
.hc-techs {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 4px;
}
.hc-tech-row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 4px 6px;
  background: rgba(28,22,18,0.5);
  border-left: 2px solid rgba(212,200,168,0.35);
}
.hc-tech-row.sig { border-left-color: var(--gold-bright); }
.hc-tech-kind {
  font-size: 9px;
  width: 16px;
  height: 16px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  letter-spacing: 0;
  color: var(--ink-dim);
  border: 1px solid rgba(212,200,168,0.35);
  flex-shrink: 0;
  margin-top: 1px;
}
.hc-tech-row.sig .hc-tech-kind {
  color: var(--gold-bright);
  border-color: var(--gold-bright);
}
.hc-tech-body { flex: 1; min-width: 0; }
.hc-tech-name {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold-pale);
}
.hc-tech-row.sig .hc-tech-name { color: var(--gold-bright); }
.hc-tech-desc {
  font-size: 10px;
  font-style: italic;
  color: var(--ink-dim);
  line-height: 1.4;
  margin-top: 1px;
}

.hc-combos {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 6px;
}
.hc-combo-row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 5px 6px;
  background: rgba(22,18,14,0.55);
  border-left: 2px solid rgba(212,200,168,0.3);
}
.hc-combo-kind {
  font-size: 9px;
  width: 16px;
  height: 16px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  letter-spacing: 0;
  color: var(--ink-dim);
  border: 1px solid rgba(212,200,168,0.3);
  flex-shrink: 0;
  margin-top: 1px;
}
.hc-combo-body { flex: 1; min-width: 0; }
.hc-combo-head {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.hc-combo-name {
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gold-pale);
}
.hc-combo-tier {
  font-size: 8px;
  letter-spacing: 0.24em;
  padding: 1px 4px;
  border-radius: 2px;
  border: 1px solid currentColor;
}
.hc-combo-tier.hc-combo-tier-duo    { color: var(--gold); }
.hc-combo-tier.hc-combo-tier-triple { color: var(--gold-bright); text-shadow: 0 0 4px rgba(240,212,136,0.5); }
.hc-combo-tier.hc-combo-tier-sig    { color: #aacfff; text-shadow: 0 0 4px rgba(170,200,255,0.5); }
.hc-combo-partners {
  font-size: 9.5px;
  color: var(--ink-dim);
  letter-spacing: 0.04em;
  margin-top: 2px;
}
.hc-combo-desc {
  font-size: 10px;
  font-style: italic;
  color: var(--ink-dim);
  line-height: 1.4;
  margin-top: 2px;
}

.hc-quirks {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
/* Override the global micro-chip .hero-quirk size in the codex so chips
   are a real tap target.  Tap or press-and-hold shows the explanation
   via bindChipExplainers. */
.hc-quirks .hero-quirk {
  font-size: 11px;
  padding: 4px 9px;
  letter-spacing: 0.1em;
  cursor: pointer;
}
.hc-locked-tag {
  font-size: 10px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 4px;
}
.hc-locked-hint {
  font-size: 10px;
  font-style: italic;
  color: var(--ink-dim);
}

@media (max-height: 480px) {
  .hc-row-head { grid-template-columns: 36px 1fr auto; gap: 8px; padding: 4px 8px; }
  .hc-portrait { width: 36px; height: 48px; }
  .hc-name { font-size: 10.5px; letter-spacing: 0.16em; }
  .hc-title { font-size: 9px; }
  .hc-row-body { padding: 4px 10px 8px 52px; }
  .hc-passive { font-size: 9.5px; }
  .hc-chev { font-size: 18px; }
}

/* ===========================================================================
   COMBO DISCOVERABILITY — queue items that participate in an available
   Resonance pulse gold in sync with the bubble overhead.
   =========================================================================== */
.queue-slot.filled.combo-matched {
  position: relative;
  animation: queue-combo-pulse 2.4s ease-in-out infinite;
}
/* Active step during turn resolution — pulsing gold border + warm glow
   so the eye can find which queued action is firing right now. */
.queue-slot.queue-slot-active {
  position: relative;
  z-index: 1;
  animation: queue-active-pulse 0.65s ease-in-out infinite alternate;
  box-shadow: 0 0 0 2px rgba(240,212,136,0.85),
              0 0 18px rgba(240,212,136,0.55),
              inset 0 0 14px rgba(240,212,136,0.32);
}
.queue-slot.queue-slot-active::before {
  content: '';
  position: absolute;
  inset: -3px;
  border: 1px solid rgba(240,212,136,0.5);
  pointer-events: none;
  border-radius: inherit;
  animation: queue-active-ring 0.9s ease-out infinite;
}
@keyframes queue-active-pulse {
  from { transform: translateY(-2px) scale(1.03); filter: brightness(1.15); }
  to   { transform: translateY(-2px) scale(1.06); filter: brightness(1.35); }
}
@keyframes queue-active-ring {
  0%   { opacity: 0.85; transform: scale(1); }
  100% { opacity: 0;    transform: scale(1.18); }
}
.queue-slot.filled.combo-matched::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  box-shadow: 0 0 14px rgba(240,212,136,0.45),
              inset 0 0 12px rgba(240,212,136,0.28);
  border-radius: inherit;
}
@keyframes queue-combo-pulse {
  0%, 100% { filter: brightness(1)    saturate(1); }
  50%      { filter: brightness(1.18) saturate(1.15); }
}

/* ===========================================================================
   KILL CONFETTI — small spark burst on the killing blow.  Each spark is
   positioned at the dying card center; --dx/--dy drive the outward fly.
   =========================================================================== */
.kill-spark {
  position: absolute;
  width: 3px; height: 3px;
  background: rgba(255,220,160,0.95);
  border-radius: 50%;
  box-shadow: 0 0 6px rgba(255,200,120,0.8), 0 0 14px rgba(220,80,60,0.4);
  pointer-events: none;
  transform: translate(-50%, -50%);
  animation: kill-spark 0.68s cubic-bezier(.18,.89,.32,1) forwards;
  z-index: 40;
}
@keyframes kill-spark {
  0%   { transform: translate(-50%, -50%) scale(1); opacity: 1; }
  100% { transform: translate(calc(-50% + var(--dx, 30px)),
                              calc(-50% + var(--dy, -30px))) scale(0.4); opacity: 0; }
}

/* ===========================================================================
   PARTY CRITICAL — when any alive hero is at <= 3 HP, paint a faint red
   edge vignette so the danger is felt in peripheral vision.
   =========================================================================== */
body.party-critical::after {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 90;
  background:
    radial-gradient(ellipse 90% 70% at 50% 50%,
                    rgba(180,40,40,0) 55%,
                    rgba(180,40,40,0.22) 85%,
                    rgba(180,40,40,0.42) 100%);
  animation: party-critical-pulse 1.8s ease-in-out infinite;
}
@keyframes party-critical-pulse {
  0%, 100% { opacity: 0.65; }
  50%      { opacity: 1; }
}

/* Manga-style emoji reaction over a character — used for bond / friction /
   kill moments.  Distinct from .popup so it can be larger and have its
   own bounce arc. */
.reaction-bubble {
  position: absolute;
  font-size: 22px;
  line-height: 1;
  transform: translate(-50%, 0);
  animation: reaction-pop 1.1s cubic-bezier(.18,.89,.32,1.28) forwards;
  pointer-events: none;
  filter: drop-shadow(0 2px 4px rgba(0,0,0,0.8));
  z-index: 50;
}
@keyframes reaction-pop {
  0%   { transform: translate(-50%, 6px)   scale(0.4) rotate(-12deg); opacity: 0; }
  18%  { transform: translate(-50%, -8px)  scale(1.4) rotate(8deg);   opacity: 1; }
  42%  { transform: translate(-50%, -16px) scale(1.0) rotate(-3deg);  opacity: 1; }
  80%  { transform: translate(-50%, -28px) scale(1.0) rotate(0);      opacity: 1; }
  100% { transform: translate(-50%, -40px) scale(0.85) rotate(0);     opacity: 0; }
}

@keyframes popup-rise {
  0%   { transform: translate(-50%, 0)    scale(0.6); opacity: 0; }
  15%  { transform: translate(-50%, -10px) scale(1.2); opacity: 1; }
  85%  { transform: translate(-50%, -42px) scale(1); opacity: 1; }
  100% { transform: translate(-50%, -60px) scale(0.9); opacity: 0; }
}

/* ============ ATB ROW (row 2): slim queue strip above battlefield ============ */
#atb-row {
  display: grid;
  grid-template-columns: 38px minmax(0, 1fr) 24px;
  gap: 8px;
  padding: 2px 12px;
  position: relative; z-index: 2;
  background: transparent;
  align-items: center;
  min-height: 0;
}
#atb-row::before {
  content: 'ATB';
  font-size: 9px; letter-spacing: 0.24em; color: var(--ink-faint);
  text-transform: uppercase; font-style: italic;
  align-self: center;
}
#queue-strip {
  display: grid;
  /* cell count derived from data-atb-max so Crown of Quickening and weakness-bonus
     can grow the strip to 4 or 5 cells */
  grid-template-columns: 1fr 1fr 1fr;
  gap: 5px;
  min-width: 0;
  height: 16px;
}
#queue-strip[data-atb-max="4"] { grid-template-columns: 1fr 1fr 1fr 1fr; }
#queue-strip[data-atb-max="5"] { grid-template-columns: 1fr 1fr 1fr 1fr 1fr; }

/* bonus ATB cells (earned via weakness exploitation last turn) glow gold */
.queue-slot.placeholder.bonus {
  background: linear-gradient(180deg, rgba(74,40,24,0.7) 0%, rgba(40,20,8,0.7) 100%);
  border: 1px solid var(--gold-bright);
  box-shadow: 0 0 8px rgba(240,212,136,0.45), inset 0 0 6px rgba(240,212,136,0.15);
  animation: bonus-pulse 1.8s ease-in-out infinite;
}
.queue-slot.placeholder.bonus .qs-name {
  color: var(--gold-bright);
  text-shadow: 0 0 4px rgba(240,212,136,0.6);
  opacity: 1;
  font-size: 14px;
  font-weight: bold;
  font-style: normal;
}
@keyframes bonus-pulse {
  0%, 100% { box-shadow: 0 0 6px rgba(240,212,136,0.35), inset 0 0 4px rgba(240,212,136,0.1); }
  50%      { box-shadow: 0 0 12px rgba(240,212,136,0.6), inset 0 0 8px rgba(240,212,136,0.25); }
}

/* ============ ACTION ROW (row 5): tile-grid + team-special ============ */
#action-row {
  display: grid;
  /* Right column hosts #commit-zone (resolve pips + the labeled FIGHT
     button).  Bumped from 48px to 112px when the button gained its
     'FIGHT' label so it has room without clipping past the viewport. */
  grid-template-columns: minmax(0, 1fr) 112px;
  gap: 6px;
  padding: 2px 8px 4px;
  position: relative; z-index: 2;
  background: transparent;
  min-height: 0;
  align-items: stretch;
  /* visible so the hold-to-reveal tooltip and the Resonance bubble can
     escape upward */
  overflow: visible;
}
#tile-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 4px;
  min-height: 0;
  min-width: 0;
  height: 100%;
}
.char-col {
  display: grid;
  grid-template-rows: auto 1fr 1fr;
  gap: 4px;
  min-height: 0;
  min-width: 0;
  height: 100%;
}
.char-col.downed { opacity: 0.4; pointer-events: none; }
/* Small portrait + name header above each hero's moveset.  Sits above the
   two action tiles in a compact row so the player can read who-owns-what
   without squinting at the tile labels. */
.char-col-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 1px 4px 2px;
  min-height: 0;
  border-bottom: 1px solid rgba(212,200,168,0.08);
}
.char-col-head .cch-portrait {
  width: 26px; height: 26px;
  flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  overflow: hidden;
  border: 1px solid var(--gold-dim);
  background: var(--parchment-deep);
  box-shadow: inset 0 0 3px rgba(0,0,0,0.7);
}
.char-col-head .cch-portrait svg {
  width: 130%; height: 130%;
  display: block;
  transform: translate(-5%, -8%);
}
.char-col-head .cch-meta {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.05;
}
.char-col-head .cch-name {
  font-size: 9.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold-pale);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* School glyph alongside the hero's name on the column header — same
   tint as the weakness-icon under enemy HP, so the player can match
   "this hero is ✦ HOLY" → "that enemy has ✦ HOLY weakness" at a glance. */
.cch-school {
  display: inline-block;
  margin-right: 4px;
  font-size: 11px;
  line-height: 1;
  vertical-align: -1px;
  cursor: help;
  text-shadow: 0 0 4px currentColor, 0 1px 0 rgba(0,0,0,0.9);
}
.cch-school-physical { color: #d8c2a0; }
.cch-school-holy     { color: #f4e6b8; }
.cch-school-arcane   { color: #c9a8e8; }
.cch-school-ranged   { color: #a8c8a0; }
.cch-school-stealth  { color: #a8b8d0; }
.char-col-head .cch-slot {
  font-size: 7.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
@media (max-height: 480px) {
  .char-col-head { gap: 4px; padding: 0 3px 1px; }
  .char-col-head .cch-portrait { width: 22px; height: 22px; }
  .char-col-head .cch-name { font-size: 8.5px; letter-spacing: 0.1em; }
  .char-col-head .cch-slot { font-size: 6.5px; }
}
#ts-area { display: flex; min-height: 0; min-width: 0; }
#ts-area .tile.team-special { width: 100%; height: 100%; min-height: 0; }
.queue-slot {
  background: linear-gradient(180deg, rgba(20,12,8,0.6) 0%, rgba(8,5,3,0.6) 100%);
  border-radius: 8px;
  position: relative;
  display: flex; align-items: center; justify-content: flex-start;
  gap: 4px;
  padding: 0 6px 0 3px;
  font-size: 9.5px;
  color: var(--ink-faint);
  overflow: hidden;
  cursor: pointer;
  transition: box-shadow 0.15s, background 0.15s, transform 0.1s;
  min-width: 0;
  box-shadow: inset 0 1px 2px rgba(0,0,0,0.55);
  height: 100%;
}
.queue-slot.filled:hover { transform: translateY(-1px); }

/* cost pip: small rounded square — slim variant */
.queue-slot .qs-cost {
  position: static;
  transform: none;
  min-width: 13px; height: 13px;
  padding: 0 2px;
  color: var(--gold-pale);
  background:
    linear-gradient(180deg, #4a2818 0%, #2a1408 70%, #150804 100%);
  border: 1px solid var(--gold);
  border-radius: 2px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 9px; font-weight: bold;
  letter-spacing: 0;
  text-shadow: 0 1px 0 var(--shadow);
  font-variant-numeric: tabular-nums;
  box-shadow:
    inset 0 1px 0 rgba(255,200,160,0.25),
    0 1px 3px rgba(0,0,0,0.6);
  flex-shrink: 0;
}

/* circular portrait avatar inside a queued action — slim 20px */
.queue-slot .qs-avatar {
  display: flex; align-items: center; justify-content: center;
  width: 20px; height: 20px;
  border-radius: 50%;
  overflow: hidden;
  border: 1px solid var(--gold-dim);
  background: var(--parchment-deep);
  flex-shrink: 0;
  box-shadow: inset 0 0 3px rgba(0,0,0,0.7);
}
.queue-slot .qs-avatar svg {
  width: 130%; height: 130%;
  display: block;
  transform: translate(-5%, -8%);
}
/* team-special crossed-swords glyph (when there's no character) */
.queue-slot .qs-avatar .qs-team {
  width: 60%; height: 60%;
  transform: none;
  color: var(--blood-bright);
  filter: drop-shadow(0 0 4px rgba(212,69,69,0.5));
}

/* action name to the right of the avatar */
.queue-slot .qs-name {
  font-weight: bold;
  color: var(--gold-pale);
  font-size: 11px;
  letter-spacing: 0.04em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  flex: 1 1 auto;
  text-shadow: 0 1px 0 var(--shadow);
}
/* an item spans (atb cost) grid columns so the strip never resizes other items */
.queue-slot[data-atb="1"] { grid-column: span 1; }
.queue-slot[data-atb="2"] { grid-column: span 2; }
.queue-slot[data-atb="3"] { grid-column: span 3; }
.queue-slot.placeholder {
  background: transparent;
  border: 1px solid rgba(106,85,48,0.3);
  padding: 0;
  justify-content: center;
  cursor: default;
  box-shadow: none;
}
.queue-slot.placeholder .qs-name {
  color: var(--ink-faint);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  opacity: 0.3;
  flex: 0 0 auto;
}
.queue-slot .qs-cost {
  position: absolute; left: 5px; top: 50%;
  transform: translateY(-50%);
  font-size: 9px; font-weight: bold;
  color: var(--gold-bright);
  width: 15px; height: 15px;
  background:
    radial-gradient(circle at 30% 25%, #4a2818 0%, var(--shadow) 80%);
  border: 1px solid var(--gold-dim);
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  text-shadow: 0 0 4px var(--gold-bright);
}
.queue-slot .qs-name { font-weight: bold; color: var(--gold-pale); font-size: 11px; letter-spacing: 0.05em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.queue-slot .qs-desc { color: var(--ink-dim); font-size: 9px; margin-left: 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-style: italic; }
.queue-slot.filled {
  background:
    linear-gradient(180deg, #2a1c12 0%, #14100b 100%);
  color: var(--ink);
  box-shadow:
    inset 0 0 0 1px var(--gold),
    inset 0 1px 0 rgba(240,212,136,0.18),
    0 1px 3px rgba(0,0,0,0.5);
}
.queue-slot.filled:hover { box-shadow: inset 0 0 0 1px var(--blood-bright), 0 0 8px rgba(212,69,69,0.3); cursor: pointer; }
.queue-slot.filled::after {
  /* Tap-to-remove affordance — a small × in the top-right that fades in on hover */
  content: '×';
  position: absolute;
  top: 2px; right: 4px;
  font-size: 11px;
  line-height: 1;
  color: var(--blood-bright);
  opacity: 0;
  transition: opacity 0.16s;
  pointer-events: none;
  text-shadow: 0 0 6px rgba(220,80,80,0.6);
}
.queue-slot.filled:hover::after { opacity: 0.85; }
.queue-slot.filled.kind-combo::after { color: var(--gold-pale); text-shadow: 0 0 6px rgba(240,212,136,0.6); }

/* Resolve-gain popup uses the existing .popup .heal base but at the HUD anchor */
.popup.resolve-pop {
  color: var(--gold-pale);
  font-size: 13px;
  letter-spacing: 0.06em;
  text-shadow: 0 0 8px rgba(240,212,136,0.55), 0 2px 0 rgba(0,0,0,0.85);
  font-weight: 600;
}
.queue-slot.kind-special.filled { box-shadow: inset 0 0 0 1px var(--gold-bright), 0 0 6px rgba(240,212,136,0.25); }
.queue-slot.kind-team.filled    { background: linear-gradient(180deg, #3a1418 0%, #1a0808 100%); box-shadow: inset 0 0 0 1px var(--blood-bright), 0 0 8px rgba(212,69,69,0.35); }
.queue-slot.kind-team.filled .qs-name { color: var(--blood-bright); text-shadow: 0 0 6px rgba(212,69,69,0.4); }
.queue-slot.kind-move.filled    { box-shadow: inset 0 0 0 1px var(--frost), 0 0 6px rgba(90,138,176,0.3); }
.queue-slot.kind-move.filled .qs-name { color: var(--frost-bright); }
.queue-slot.kind-brace.filled   { box-shadow: inset 0 0 0 1px var(--armor), 0 0 6px rgba(156,170,208,0.3); }
.queue-slot.kind-brace.filled .qs-name { color: var(--armor); }

.control-btn {
  background: transparent;
  border: 1px solid rgba(106,85,48,0.4);
  color: var(--ink-dim);
  width: 22px; height: 22px;
  padding: 0;
  border-radius: 50%;
  cursor: pointer;
  font-family: inherit;
  display: inline-flex; align-items: center; justify-content: center;
  transition: all 0.15s;
  font-size: 11px;
  line-height: 1;
}
.control-btn:not(:disabled):hover {
  border-color: var(--blood-bright);
  color: var(--blood-bright);
  box-shadow: 0 0 8px rgba(212,69,69,0.4);
}
.control-btn:active:not(:disabled) { transform: translateY(1px); }
.control-btn:disabled { opacity: 0.25; cursor: not-allowed; }
.ctrl-label { font-weight: bold; }

/* ============ FIGHT — circular crest, icon only ============ */
/* Fight button — NieR commit panel.  Squared (notched corners via
   clip-path), flat cream-on-black, blood-red text-only for the active
   "EXECUTE" state.  No glowing red disc. */
.fight-btn {
  min-width: 96px;
  /* Stretch to fill the commit-zone slot height so the button visually
     sits inside the action row instead of floating in dead space. */
  height: 100%;
  min-height: 56px;
  padding: 0 14px;
  border-radius: 0;
  background: linear-gradient(180deg, rgba(40,28,16,0.95) 0%, rgba(18,12,8,0.95) 100%);
  border: 1px solid var(--gold-bright);
  color: var(--gold-bright);
  cursor: pointer;
  font-family: inherit;
  display: inline-flex; align-items: center; justify-content: center;
  gap: 8px;
  transition: background 0.14s, color 0.14s, border-color 0.14s, transform 0.1s;
  position: relative;
  box-shadow: 0 0 16px rgba(240,212,136,0.22),
              inset 0 0 14px rgba(240,212,136,0.08);
  justify-self: stretch;
  align-self: stretch;
  /* NieR notched-corner clip — two angled cuts at top-left and bottom-right */
  clip-path: polygon(
    10px 0, 100% 0, 100% calc(100% - 10px),
    calc(100% - 10px) 100%, 0 100%, 0 10px
  );
}
.fight-btn .fight-icon {
  width: 24px; height: 24px;
  filter: drop-shadow(0 0 6px rgba(240,212,136,0.45));
}
.fight-btn .fight-label {
  font-size: 14px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  font-weight: 700;
  color: inherit;
  text-shadow: 0 0 10px rgba(240,212,136,0.4), 0 1px 0 rgba(0,0,0,0.85);
}
/* Active state — bright, pulsing, obviously go.  The ::after layer adds
   an outward ring pulse so the button reads as 'commit' at a glance. */
.fight-btn:not(:disabled) {
  animation: fight-pulse 1.6s ease-in-out infinite;
  background: linear-gradient(180deg, rgba(70,46,18,0.95) 0%, rgba(28,16,8,0.95) 100%);
  box-shadow: 0 0 22px rgba(240,212,136,0.45),
              inset 0 0 18px rgba(240,212,136,0.15);
}
.fight-btn:not(:disabled)::after {
  content: '';
  position: absolute;
  inset: -3px;
  border: 1px solid rgba(240,212,136,0.5);
  pointer-events: none;
  animation: fight-ring 1.4s ease-out infinite;
}
@keyframes fight-pulse {
  0%, 100% { filter: brightness(1); }
  50%      { filter: brightness(1.18); }
}
@keyframes fight-ring {
  0%   { opacity: 0.7; transform: scale(1); }
  100% { opacity: 0;   transform: scale(1.12); }
}
.fight-btn:not(:disabled):hover {
  background: var(--gold-bright);
  color: var(--bg-0);
  transform: translateY(-1px);
}
.fight-btn:not(:disabled):hover .fight-icon path { fill: var(--bg-0); }
.fight-btn:active:not(:disabled) { transform: translateY(1px); }
/* Disabled — clearly muted; no glow, no pulse. */
.fight-btn:disabled {
  background: rgba(14,14,16,0.55);
  color: var(--ink-faint);
  border-color: rgba(212,200,168,0.22);
  cursor: not-allowed;
  animation: none;
  box-shadow: none;
}
.fight-btn:disabled .fight-icon { opacity: 0.4; filter: none; }
.fight-btn:disabled .fight-label { color: var(--ink-faint); text-shadow: none; }
/* Landscape mobile — slim the button so it never clips the right edge.
   #action-row's right column is 112px on standard viewports; on small
   heights we tighten to ~80px and shrink the label to match. */
@media (max-height: 480px) {
  #action-row { grid-template-columns: minmax(0, 1fr) 88px !important; }
  .fight-btn {
    min-width: 76px;
    height: 48px;
    padding: 0 10px;
    gap: 6px;
  }
  .fight-btn .fight-icon { width: 20px; height: 20px; }
  .fight-btn .fight-label { font-size: 12px; letter-spacing: 0.22em; }
}

/* ============ TS-AREA (right side of action row) ============ */
#ts-area {
  display: flex;
  align-items: stretch;
  min-height: 0; min-width: 0;
  height: 100%;
}
#ts-area .tile.team-special { width: 100%; min-height: 0; height: 100%; }

/* tile-pair (mid-character split move) stays inside party-stack row */
.tile-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
  min-height: 0;
  min-width: 0;
}
.tile-pair > .tile { min-height: 0; }


/* tile — NieR instrument tile.  Flat background, crisp 1px cream border,
   square corners, no gradient.  Hover/queued state brightens the border. */
.tile {
  background: rgba(18,18,20,0.78);
  border: 1px solid rgba(212,200,168,0.22);
  color: var(--ink);
  padding: 2px 20px 2px 6px;
  border-radius: 0;
  cursor: pointer;
  font-family: inherit;
  display: flex; flex-direction: column; justify-content: center;
  min-height: 0; min-width: 0;
  text-align: left;
  transition: border-color 0.14s, background 0.14s, color 0.14s;
  position: relative;
  overflow: visible;
  gap: 0;
  box-shadow: none;
}
.tile:hover,
.tile.queued {
  border-color: var(--gold-bright);
  background: rgba(28,28,30,0.85);
}
/* corner brackets removed for a cleaner inline-card feel */
.tile:not(:disabled):hover {
  border-color: var(--gold-dim);
  background: linear-gradient(180deg, rgba(48,32,20,0.7) 0%, rgba(24,18,12,0.7) 100%);
  box-shadow: inset 0 1px 0 rgba(232,220,196,0.1), 0 0 10px rgba(200,164,100,0.2);
}
.tile:active:not(:disabled) { transform: translateY(1px); filter: brightness(1.15); }
.tile:disabled { opacity: 0.3; cursor: not-allowed; }
/* Tile is technically clickable but the action would land on an empty
   reach — fade it so the eye lands on real options first. */
.tile.no-effect:not(:disabled) {
  opacity: 0.5;
  filter: saturate(0.6);
}
.tile.no-effect:not(:disabled):hover {
  opacity: 0.85;
  filter: saturate(0.85);
}
/* Tile is disabled because the same action kind is already queued for
   that character this turn.  Shown lightly dimmed with a faint check mark. */
.tile.action-used {
  opacity: 0.55;
  position: relative;
}
.tile.action-used::after {
  content: '✓';
  position: absolute;
  top: 4px; right: 6px;
  font-size: 10px;
  color: var(--gold-pale);
  opacity: 0.7;
}
.tile .tile-name {
  font-size: 10px; font-weight: bold; color: var(--gold-bright);
  line-height: 1.1;
  letter-spacing: 0.02em;
  text-shadow: 0 1px 0 var(--shadow);
  /* wrap to two lines max if the name is long; tooltip carries the full form */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
  word-break: break-word;
}
/* description hidden by default — revealed as a hover/hold tooltip above the tile */
.tile .tile-desc {
  display: none;
  position: absolute;
  left: 50%; bottom: calc(100% + 10px);
  transform: translateX(-50%);
  min-width: 200px;
  max-width: 320px;
  padding: 12px 18px;
  background:
    linear-gradient(180deg, #2a1c12 0%, #14100a 100%);
  border: 1px solid var(--gold-dim);
  border-radius: 4px;
  font-size: 14px; color: var(--ink);
  line-height: 1.45;
  font-style: italic;
  white-space: normal;
  text-align: center;
  z-index: 50;
  box-shadow:
    0 10px 30px rgba(0,0,0,0.85),
    0 0 24px rgba(200,164,100,0.18),
    inset 0 1px 0 rgba(232,220,196,0.08);
  pointer-events: none;
  letter-spacing: 0.02em;
}
.tile .tile-desc::after {
  /* arrow pointing down to the tile */
  content: '';
  position: absolute;
  bottom: -6px; left: 50%;
  transform: translateX(-50%) rotate(45deg);
  width: 10px; height: 10px;
  background: linear-gradient(135deg, transparent 50%, #14100a 50%);
  border-right: 1px solid var(--gold-dim);
  border-bottom: 1px solid var(--gold-dim);
}
.tile.previewing .tile-desc { display: block; }
.tile .tile-badges {
  position: absolute; top: 2px; right: 3px;
  display: flex; gap: 3px;
  font-size: 8px; font-weight: bold;
  letter-spacing: 0.04em;
  line-height: 1;
  z-index: 2;
}
/* ATB cost pip — rounded square with the number (no "ATB" text) */
.tile .tile-atb {
  color: var(--gold-pale);
  background:
    linear-gradient(180deg, #4a2818 0%, #2a1408 70%, #150804 100%);
  min-width: 14px; height: 14px;
  padding: 0 3px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 3px;
  border: 1px solid var(--gold);
  box-shadow:
    inset 0 1px 0 rgba(255,200,160,0.25),
    0 1px 2px rgba(0,0,0,0.6);
  font-size: 10px; font-weight: bold;
  text-shadow: 0 1px 0 var(--shadow);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
}
/* Resolve diamond pip — gold variant */
.tile .tile-cost {
  color: var(--gold-bright);
  background:
    radial-gradient(circle at 35% 30%, rgba(255,240,184,0.35) 0%, rgba(200,164,100,0.2) 60%, var(--shadow) 100%);
  min-width: 14px; height: 14px;
  padding: 0 3px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 3px;
  border: 1px solid var(--gold);
  text-shadow: 0 0 4px var(--gold-bright);
  box-shadow:
    inset 0 1px 0 rgba(255,248,224,0.3),
    0 1px 2px rgba(0,0,0,0.6);
  font-size: 9px; font-weight: bold;
  letter-spacing: 0;
}
.tile .tile-reach {
  position: absolute; bottom: 2px; right: 4px;
  pointer-events: none;
  line-height: 1;
}
.tile .tile-reach .rch-cells {
  display: inline-flex;
  align-items: center;
  gap: 1px;
}
.tile .tile-reach .rch-mark {
  font-size: 9px;
  color: var(--gold-bright);
  margin-right: 2px;
  line-height: 1;
  text-shadow: 0 0 4px rgba(240,212,136,0.7);
}
.tile .tile-reach .rch-cell {
  display: inline-block;
  width: 9px; height: 9px;
  font-size: 7px;
  font-weight: 700;
  line-height: 9px;
  text-align: center;
  border-radius: 1.5px;
  font-family: 'Trebuchet MS', sans-serif;
}
.tile .tile-reach .rch-cell.on {
  background: var(--gold-deep);
  color: #0a0608;
  box-shadow: 0 0 4px rgba(200,164,100,0.55);
}
.tile .tile-reach .rch-cell.off {
  background: rgba(0,0,0,0.4);
  color: rgba(212,200,168,0.28);
  border: 1px solid rgba(212,200,168,0.18);
  line-height: 7px;
}
/* Element glyph — inline prefix on the tile name so the player can spot
   attack-element ↔ enemy-weakness matches at a glance without the icon
   ever getting clipped by the tile's tight vertical padding. */
.tile .tile-element {
  display: inline;
  margin-right: 1px;
  font-size: 11px;
  font-weight: normal;
  vertical-align: baseline;
  pointer-events: none;
  text-shadow: 0 0 4px currentColor, 0 1px 0 rgba(0,0,0,0.9);
}
.tile .tile-element-physical { color: #d8c2a0; }
.tile .tile-element-holy     { color: #f4e6b8; }
.tile .tile-element-arcane   { color: #c9a8e8; }
.tile .tile-element-ranged   { color: #a8c8a0; }
.tile .tile-element-stealth  { color: #a8b8d0; }
.tile.queued .tile-element { opacity: 0.6; }

/* hold-to-preview tile state */
.tile.previewing {
  box-shadow:
    0 0 14px var(--gold-bright),
    inset 0 0 0 1px var(--gold-bright),
    inset 0 0 12px rgba(240,212,136,0.2);
  transform: scale(0.98);
  border-color: var(--gold-bright);
}
.tile.kind-attack { /* default */ }
.tile.kind-special {
  border-color: var(--gold-dim);
  background: linear-gradient(180deg, #2a1c10 0%, #15100a 100%);
}
.tile.kind-special::before, .tile.kind-special::after { border-color: var(--gold-bright); opacity: 0.7; }
.tile.kind-move {
  background: linear-gradient(180deg, #1a2632 0%, #0a1018 100%);
  border-color: rgba(90,138,176,0.5);
}
.tile.kind-move::before, .tile.kind-move::after { border-color: var(--frost); }
.tile.kind-move .tile-name { color: var(--frost-bright); }
.tile.kind-brace {
  background: linear-gradient(180deg, #1c1f28 0%, #0c0e14 100%);
  border-color: var(--armor);
}
.tile.kind-brace::before, .tile.kind-brace::after { border-color: var(--armor); }
.tile.kind-brace .tile-name { color: var(--armor); }

/* Compact Team Special: tall thin pill — formation-aware name on top, cost pips below.
   Description is on hold-tooltip only. */
.tile.team-special {
  background: linear-gradient(180deg, rgba(74,26,28,0.55) 0%, rgba(42,18,24,0.55) 60%, rgba(21,8,8,0.55) 100%);
  border: 1px solid rgba(212,69,69,0.55);
  padding: 6px 8px;
  display: flex; flex-direction: column; justify-content: center; gap: 4px;
  min-height: 0; min-width: 0;
  width: 100%; height: 100%;
  box-shadow: inset 0 1px 0 rgba(255,200,200,0.12), 0 2px 6px rgba(0,0,0,0.6);
  align-items: center;
  text-align: center;
}
.tile.team-special.ready {
  border-color: var(--blood-bright);
  box-shadow: 0 0 12px rgba(212,69,69,0.45), inset 0 1px 0 rgba(255,200,200,0.2);
  animation: ts-pulse 2s ease-in-out infinite;
}
@keyframes ts-pulse {
  0%, 100% { box-shadow: 0 0 10px rgba(212,69,69,0.35), inset 0 1px 0 rgba(255,200,200,0.15); }
  50%      { box-shadow: 0 0 18px rgba(212,69,69,0.65), inset 0 1px 0 rgba(255,200,200,0.25); }
}
.tile.team-special .ts-name {
  font-size: 11px; font-weight: bold; color: var(--blood-bright);
  letter-spacing: 0.04em;
  text-shadow: 0 0 6px rgba(212,69,69,0.5), 0 1px 0 var(--shadow);
  white-space: nowrap;
  max-width: 100%;
}
/* description hidden by default — revealed on hold */
.tile.team-special .ts-desc {
  display: none;
  position: absolute;
  left: 50%; bottom: calc(100% + 10px);
  transform: translateX(-50%);
  min-width: 200px;
  max-width: 320px;
  padding: 12px 16px;
  background: linear-gradient(180deg, #2a1c12 0%, #14100a 100%);
  border: 1px solid var(--blood-bright);
  border-radius: 4px;
  font-size: 13px; color: var(--ink);
  line-height: 1.45; font-style: italic;
  white-space: normal; text-align: center;
  z-index: 50;
  box-shadow: 0 10px 28px rgba(0,0,0,0.85), 0 0 22px rgba(212,69,69,0.35);
  pointer-events: none;
}
.tile.team-special.previewing .ts-desc { display: block; }
.tile.team-special .ts-cost {
  display: inline-flex; gap: 4px; align-items: center;
}
.tile.team-special .ts-cost .tile-atb,
.tile.team-special .ts-cost .tile-cost { min-width: 16px; height: 16px; font-size: 10px; padding: 0 4px; }
.tile.team-special:disabled { opacity: 0.4; }

.tile.queued {
  box-shadow:
    inset 0 0 0 1px var(--gold-bright),
    0 0 10px rgba(240,212,136,0.3);
}
.tile.queued .tile-reach { display: none; }
.tile .q-count {
  position: absolute; bottom: 2px; right: 4px;
  font-size: 10px; color: var(--gold-bright); font-weight: bold;
  text-shadow: 0 0 6px var(--gold-bright);
  letter-spacing: 0.05em;
  z-index: 2;
}

/* ============ OVERLAY ============ */
#overlay {
  position: absolute; inset: 0;
  background:
    radial-gradient(ellipse at center, rgba(15,8,4,0.85) 0%, rgba(5,3,2,0.96) 70%);
  display: flex; align-items: center; justify-content: center;
  /* Bumped above #title-screen (z-index 150) so Credits / Settings
     overlays opened from the title actually appear in front. */
  z-index: 260;
  animation: overlay-fade 0.3s ease-out;
}
@keyframes overlay-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
#overlay.hidden { display: none; }
#overlay-content {
  background:
    radial-gradient(ellipse at 50% 0%, #2c1f14 0%, #18110a 50%, #0c0805 100%);
  border: 1px solid var(--gold);
  /* Tightened padding (was 32px 40px) so encounter / trade / recruit
     overlays fit within the locked 405px design canvas without
     scrolling, leaving more room for portraits and cinematic. */
  padding: 14px 22px;
  border-radius: var(--card-radius);
  text-align: center;
  /* Mobile-fit: never wider than the viewport, never taller than it.
     overflow-y so long content (path map, victory rows) scrolls in place
     instead of clipping out of view.  The 94% / 92% caps clamp against
     the (transform-scaled) stage parent so the overlay can't overflow
     the design canvas on desktop, where 94vw/92vh would otherwise
     resolve to viewport pixels far larger than the canvas. */
  max-width: min(380px, 94vw, 94%);
  max-height: min(92vh, 92%);
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  box-shadow:
    0 0 60px rgba(200,164,100,0.22),
    0 0 0 1px rgba(200,164,100,0.15) inset,
    0 0 0 3px rgba(0,0,0,0.6) inset,
    0 12px 40px rgba(0,0,0,0.8);
  position: relative;
}
#overlay-content::before, #overlay-content::after {
  content: '⚜';
  position: absolute;
  font-size: 18px;
  color: var(--gold);
  opacity: 0.7;
  top: 8px;
}
#overlay-content::before { left: 14px; }
#overlay-content::after  { right: 14px; }
#overlay-title {
  /* Tightened (was 28px, margin 14) — gives the cinematic body more
     room in the 405-tall design canvas. */
  font-size: 18px; color: var(--gold-bright); margin-bottom: 8px;
  letter-spacing: 0.18em; text-transform: uppercase;
  text-shadow: 0 0 18px rgba(240,212,136,0.5), 0 2px 0 var(--shadow);
  position: relative;
}
#overlay-title::after {
  content: '';
  display: block;
  width: 80%; max-width: 200px;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--gold) 50%, transparent);
  margin: 8px auto 0;
}
#overlay-body  {
  font-size: 13px; color: var(--ink); margin-bottom: 22px;
  line-height: 1.55; font-style: italic;
  letter-spacing: 0.02em;
}
#overlay-btn {
  position: relative;
  z-index: 5;
  background:
    linear-gradient(180deg, var(--gold-pale) 0%, var(--gold-bright) 40%, var(--gold) 80%, var(--gold-dim) 100%);
  color: #1a0e04; border: 1px solid var(--gold-deep);
  padding: 11px 32px; font-family: inherit; font-size: 12px; font-weight: bold;
  letter-spacing: 0.22em; text-transform: uppercase;
  border-radius: 3px; cursor: pointer;
  box-shadow:
    0 4px 12px rgba(0,0,0,0.6),
    inset 0 1px 0 rgba(255,248,224,0.6),
    inset 0 -1px 0 rgba(0,0,0,0.3);
  transition: all 0.15s;
  text-shadow: 0 1px 0 rgba(255,255,255,0.2);
}
#overlay-btn:hover { transform: translateY(-1px); filter: brightness(1.1); box-shadow: 0 6px 16px rgba(200,164,100,0.4), inset 0 1px 0 rgba(255,248,224,0.7); }
#overlay-btn:active { transform: translateY(1px); }
#overlay-btn.hidden { display: none; }

/* first-run welcome — overrides the default italic flavor body */
#overlay-body.welcome-body {
  font-style: normal;
  text-align: left;
  margin-bottom: 20px;
}
.welcome-lede {
  color: var(--gold-pale);
  font-style: italic;
  text-align: center;
  margin: 0 0 14px;
  font-size: 13px;
  letter-spacing: 0.04em;
}
.welcome-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.welcome-list li {
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--ink);
  padding-left: 14px;
  position: relative;
}
.welcome-list li::before {
  content: '◆';
  position: absolute;
  left: 0; top: 1px;
  color: var(--gold);
  font-size: 9px;
}
.welcome-key {
  display: inline-block;
  color: var(--gold-bright);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 11px;
  margin-right: 4px;
}

/* post-fight victory summary — overrides the default italic flavor body */
#overlay-body.victory-summary-body {
  font-style: normal;
  text-align: left;
  margin-bottom: 18px;
  min-width: 320px;
}
.vs-subtitle {
  color: var(--gold-pale);
  font-style: italic;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-align: center;
  margin-bottom: 10px;
}
.vs-meta {
  display: flex;
  justify-content: center;
  gap: 14px;
  margin-bottom: 14px;
  font-size: 12px;
  color: var(--ink-dim);
}
.vs-meta-stat b { color: var(--gold-bright); font-weight: 700; }
.vs-rows {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding-bottom: 4px;
}
.vs-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 6px 10px;
  background: rgba(20,12,6,0.55);
  border: 1px solid rgba(200,164,100,0.15);
  border-radius: 3px;
}
/* Tabular variant — name flush left, the three stat columns flush right
   with consistent widths so dealt / healed / taken line up vertically
   across rows. */
.vs-row.vs-row-grid {
  display: grid;
  grid-template-columns: 1fr auto auto auto;
  gap: 14px;
}
.vs-row.vs-row-grid .vs-col {
  text-align: right;
  min-width: 84px;
  font-size: 11px;
  color: var(--ink-dim);
  font-variant-numeric: tabular-nums;
}
.vs-row.vs-row-grid .vs-col b { font-weight: 700; }
.vs-row.vs-row-grid .vs-dealt b  { color: var(--blood-bright); }
.vs-row.vs-row-grid .vs-healed b { color: var(--heal); }
.vs-row.vs-row-grid .vs-taken b  { color: var(--gold-pale); }
.vs-row.vs-row-grid .vs-zero { color: var(--ink-faint); opacity: 0.5; }
.vs-row.vs-downed {
  opacity: 0.55;
  border-color: rgba(212,69,69,0.35);
}
.vs-name {
  color: var(--gold-pale);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.vs-stats {
  display: flex;
  gap: 10px;
  font-size: 11px;
  color: var(--ink-dim);
}
.vs-stat b { font-weight: 700; font-variant-numeric: tabular-nums; }
.vs-dealt b { color: var(--blood-bright); }
.vs-healed b { color: var(--heal); }
.vs-taken b { color: #c8a878; }
.vs-quiet { color: var(--ink-dim); opacity: 0.6; }
.vs-syn {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid rgba(200,164,100,0.18);
  text-align: center;
  font-size: 11px;
  line-height: 1.6;
  color: var(--ink-dim);
}
.vs-syn-label {
  display: block;
  color: var(--gold-pale);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-size: 10px;
  margin-bottom: 4px;
}
.vs-syn-chip {
  display: inline-block;
  margin: 2px 3px;
  padding: 2px 7px;
  border: 1px solid var(--gold-dim);
  border-radius: 9px;
  font-size: 10px;
  color: var(--gold-bright);
  letter-spacing: 0.06em;
  background: rgba(40,28,16,0.6);
}

/* widen overlay-content while summary is showing — capped to viewport */
#overlay-content:has(#overlay-body.victory-summary-body) {
  max-width: min(460px, 94vw);
  padding: 26px 30px 22px;
}

/* chip tap explainer — mobile-friendly tooltip surfaced on tap, since
   the browser-native title attribute never fires on touch devices */
#chip-tooltip {
  position: fixed;
  display: none;
  max-width: 240px;
  padding: 7px 10px;
  background: rgba(10, 6, 3, 0.96);
  border: 1px solid var(--gold);
  border-radius: 4px;
  color: var(--ink);
  font-size: 11px;
  line-height: 1.4;
  letter-spacing: 0.02em;
  box-shadow: 0 0 18px rgba(0,0,0,0.7), 0 0 0 1px rgba(0,0,0,0.5) inset;
  /* Above #overlay (z-index 260) so chip tooltips render in front of
     the Heroes screen, vignettes, etc. */
  z-index: 800;
  pointer-events: auto;
  text-align: left;
  font-style: italic;
}
#chip-tooltip.visible { display: block; animation: chip-tip-in 0.14s ease-out; }
#chip-tooltip::before,
#chip-tooltip::after {
  content: '';
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  width: 0; height: 0;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
}
#chip-tooltip.arrow-down::after {
  bottom: -6px;
  border-top: 6px solid rgba(10,6,3,0.96);
}
#chip-tooltip.arrow-down::before {
  bottom: -7px;
  border-top: 7px solid var(--gold);
}
#chip-tooltip.arrow-up::after {
  top: -6px;
  border-bottom: 6px solid rgba(10,6,3,0.96);
}
#chip-tooltip.arrow-up::before {
  top: -7px;
  border-bottom: 7px solid var(--gold);
}
@keyframes chip-tip-in {
  from { opacity: 0; transform: translateY(2px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* run-summary variant — adds an italic flavor line above the subtitle */
.vs-flavor {
  font-style: italic;
  text-align: center;
  color: var(--ink);
  font-size: 12.5px;
  line-height: 1.5;
  letter-spacing: 0.02em;
  margin: 0 0 12px;
  padding: 0 4px;
}

/* path-choice overlay — viewport-capped */
#overlay-content:has(#overlay-choices:not(.hidden)) {
  max-width: min(580px, 94vw);
  padding: 24px 28px;
}
#overlay-content:has(#overlay-choices.path-map:not(.hidden)) {
  max-width: min(820px, 96vw);
  padding: 20px 22px 16px;
}
/* Party-inspect overlay is wider — three hero columns of dense info */
#overlay-content:has(#overlay-choices.party-inspect:not(.hidden)) {
  max-width: min(820px, 96vw);
  padding: 14px 18px 14px;
}

/* ============ PARTY INSPECT — FFT-style party menu ============
   Horizontal roster strip at the top: each party member is a portrait
   card you tap to promote.  Below: a single detail panel with a banner
   (large portrait + name + class/hp/pos block), passive, affinities, and
   a 3-column ability grid (FRONT / MID / BACK) with the home slot
   marked.  Non-focused strip avatars grey so the eye lands on the
   detail. */
#overlay-choices.party-inspect {
  display: block;
  margin-top: 6px;
  text-align: left;
}
.hero-inspect-party {
  display: grid;
  grid-template-columns: minmax(220px, 320px) 1fr;
  gap: 18px;
  max-height: 60vh;
  overflow: hidden;
  position: relative;
}
.hero-inspect-party::before,
.hero-inspect-party::after {
  content: '';
  position: absolute;
  width: 10px; height: 10px;
  border-color: var(--gold-bright);
  pointer-events: none;
  opacity: 0.7;
}
.hero-inspect-party::before { top: -4px;    left: -4px;  border-top: 1px solid;    border-left: 1px solid; }
.hero-inspect-party::after  { bottom: -4px; right: -4px; border-bottom: 1px solid; border-right: 1px solid; }
.hip-focus-column {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
}
.hip-focus-figure {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 4px 4px 8px;
  flex: 1 1 auto;
  min-height: 0;
  position: relative;
}
.hip-focus-info {
  overflow-y: auto;
  min-height: 0;
  padding-right: 4px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* ---- switcher strip ---- sits in the left column under the focused
   silhouette.  Tappable mini-silhouettes; the focused one lights up. */
.hip-strip {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 10px;
  padding: 8px 2px 2px;
  border-top: 1px solid rgba(212,200,168,0.18);
  position: relative;
  flex: 0 0 auto;
  flex-wrap: nowrap;
}
.hip-strip::before {
  /* subtle floor hairline beneath the row */
  content: '';
  position: absolute;
  left: 12%; right: 12%;
  bottom: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent 0%, rgba(232,220,196,0.16) 50%, transparent 100%);
  pointer-events: none;
}
.hip-avatar {
  flex: 0 1 auto;
  min-width: 0;
  max-width: 90px;
  width: 90px;
  background: transparent;
  border: none;
  padding: 0 3px 4px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  position: relative;
  opacity: 0.5;
  filter: brightness(0.85);
  transition: opacity 0.18s ease, filter 0.18s ease, transform 0.16s ease;
}
.hip-avatar:hover { opacity: 0.85; filter: brightness(1); transform: translateY(-2px); }
.hip-avatar.hip-avatar-focused {
  opacity: 1;
  filter: brightness(1.08);
  transform: translateY(-3px);
}
.hip-avatar.hip-avatar-focused::before {
  /* focus glow underfoot — same gold halo language as the move-dest */
  content: '';
  position: absolute;
  left: 50%; bottom: 6px;
  transform: translateX(-50%);
  width: 70%; height: 12px;
  background: radial-gradient(ellipse 60% 100% at 50% 50%,
              var(--gold-bright) 0%,
              rgba(240,212,136,0.45) 30%,
              transparent 70%);
  pointer-events: none;
  animation: hip-focus-pulse 1.4s ease-in-out infinite alternate;
}
@keyframes hip-focus-pulse {
  from { opacity: 0.7;  transform: translateX(-50%) scaleX(0.9); }
  to   { opacity: 1;    transform: translateX(-50%) scaleX(1.05); }
}
.hip-avatar-downed { filter: grayscale(0.7) brightness(0.5); }
.hip-avatar.hip-avatar-downed.hip-avatar-focused { filter: grayscale(0.4) brightness(0.8); }
.hip-avatar-portrait {
  /* Fixed portrait box so every switcher figure renders at the same
     height and bottom-anchors cleanly to its caption regardless of
     character-specific viewBox content (helmets, plumes, etc). */
  flex: 0 0 auto;
  width: 100%;
  height: 76px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  position: relative;
  overflow: hidden;
  -webkit-mask-image: radial-gradient(ellipse 80% 100% at 50% 50%, #000 60%, transparent 100%);
          mask-image: radial-gradient(ellipse 80% 100% at 50% 50%, #000 60%, transparent 100%);
  border: none;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
}
.hip-avatar-portrait::after {
  content: '';
  position: absolute;
  left: 50%; bottom: 0;
  transform: translateX(-50%);
  width: 70%; height: 10px;
  background: radial-gradient(ellipse 60% 100% at 50% 50%,
                              rgba(0,0,0,0.65) 0%, transparent 80%);
  pointer-events: none;
}
.hip-avatar-portrait svg {
  height: 100%;
  width: auto;
  max-width: 92%;
  display: block;
  transform: none;
  filter: drop-shadow(0 6px 14px rgba(0,0,0,0.7));
}
.hip-avatar-name {
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold-pale);
  margin-top: 4px;
  white-space: nowrap;
}
.hip-avatar-slot {
  font-size: 8px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-top: 2px;
}
.hip-avatar.hip-avatar-focused .hip-avatar-slot { color: var(--gold); }

/* The .hip-focus-figure + .hip-focus-info layout is defined above
   inside .hero-inspect-party.  This block just styles the silhouette
   container, name banner, and stat row. */
.hip-focus-silhouette {
  width: 100%;
  height: clamp(120px, 24vh, 220px);
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  position: relative;
  overflow: hidden;
  -webkit-mask-image: radial-gradient(ellipse 80% 100% at 50% 50%, #000 60%, transparent 100%);
          mask-image: radial-gradient(ellipse 80% 100% at 50% 50%, #000 60%, transparent 100%);
}
.hip-focus-silhouette::after {
  content: '';
  position: absolute;
  left: 50%; bottom: 0;
  transform: translateX(-50%);
  width: 78%; height: 14px;
  background: radial-gradient(ellipse 60% 100% at 50% 50%,
              var(--gold-bright) 0%,
              rgba(240,212,136,0.45) 30%,
              rgba(0,0,0,0.6) 60%,
              transparent 80%);
  pointer-events: none;
  animation: hip-focus-pulse 1.4s ease-in-out infinite alternate;
}
.hip-focus-silhouette svg {
  height: 100%;
  width: auto;
  max-width: 92%;
  display: block;
  filter: drop-shadow(0 6px 16px rgba(0,0,0,0.7));
}
.hip-focus-name {
  font-size: 15px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--gold-pale);
  text-shadow: 0 0 14px rgba(240,212,136,0.45), 0 1px 0 rgba(0,0,0,0.95);
  line-height: 1.1;
  margin-top: 4px;
  text-align: center;
}
.hip-focus-title {
  font-size: 9.5px;
  font-style: italic;
  color: var(--ink-dim);
  text-align: center;
  margin-bottom: 2px;
}
.hip-focus-stats {
  display: flex;
  flex-wrap: nowrap;
  gap: 4px;
  justify-content: center;
  margin-top: 4px;
  width: 100%;
}
.hip-focus-stats .hip-stat {
  flex: 1 1 0;
  min-width: 0;
  justify-content: center;
}

.hip-stat {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 6px;
  background: rgba(0,0,0,0.45);
  border: 1px solid rgba(212,200,168,0.22);
}
.hip-stat .hip-stat-label {
  font-size: 7px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.hip-stat .hip-stat-val {
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gold-pale);
  font-weight: 700;
}
.hip-stat.hip-stat-school.school-physical { border-color: var(--ink-faint); }
.hip-stat.hip-stat-school.school-physical .hip-stat-val { color: var(--ink); }
.hip-stat.hip-stat-school.school-holy     { border-color: var(--gold); }
.hip-stat.hip-stat-school.school-holy     .hip-stat-val { color: var(--gold-bright); }
.hip-stat.hip-stat-school.school-arcane   { border-color: var(--debuff); }
.hip-stat.hip-stat-school.school-arcane   .hip-stat-val { color: var(--debuff); }
.hip-stat.hip-stat-school.school-ranged   { border-color: var(--armor); }
.hip-stat.hip-stat-school.school-ranged   .hip-stat-val { color: var(--armor); }
.hip-stat.hip-stat-school.school-stealth  { border-color: var(--weak); }
.hip-stat.hip-stat-school.school-stealth  .hip-stat-val { color: var(--weak); }

/* Compact formation diagram inside the POS chip — three dots, BACK on
   the left and FRONT on the right.  The hero's current slot fills gold;
   the home slot gets a thin gold outline whether or not they stand on it. */
.hip-form {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.hip-form-dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: transparent;
  border: 1px solid rgba(212,200,168,0.32);
}
.hip-form-dot.hip-form-dot-home { border-color: var(--gold); }
.hip-form-dot.hip-form-dot-on {
  background: var(--gold-bright);
  border-color: var(--gold-bright);
  box-shadow: 0 0 6px rgba(240,212,136,0.55);
}

/* Hide the switcher strip when there's nothing to switch between */
.hip-strip.hidden { display: none; }

/* Sections — bracket-style labels */
.hip-section { display: flex; flex-direction: column; gap: 4px; }
.hip-section-label {
  font-size: 8.5px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--gold);
  padding-bottom: 2px;
  border-bottom: 1px solid rgba(212,200,168,0.1);
}
.hip-passive {
  font-size: 10.5px;
  line-height: 1.45;
  color: var(--ink);
  padding-left: 7px;
  border-left: 2px solid var(--gold-bright);
}
.hip-passive b {
  font-weight: 700;
  color: var(--gold-bright);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-size: 9.5px;
  margin-right: 4px;
}
.hip-affinities {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  padding-left: 7px;
  border-left: 2px solid rgba(212,200,168,0.18);
  min-height: 26px;
}
/* Inspect-screen affinity chips — bumped from the global 9px micro-chip
   so they're a real tap target.  Press or tap to surface name + effect
   via the chip-explainer tooltip. */
.hip-affinities .hero-quirk {
  font-size: 11px;
  padding: 4px 9px;
  letter-spacing: 0.1em;
  cursor: pointer;
}
.hip-affinities .hero-quirks-empty {
  font-size: 9.5px;
  font-style: italic;
  color: var(--ink-faint);
}

/* Ability grid — three columns side by side, home slot highlighted */
.hip-tech-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 8px;
}
.hip-tech-col {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 6px 8px 8px;
  background: linear-gradient(180deg, rgba(22,18,18,0.45) 0%, rgba(8,6,8,0.6) 100%);
  border-top: 1px solid rgba(212,200,168,0.18);
}
.hip-tech-col.home {
  border-top-color: var(--gold-bright);
  box-shadow: inset 0 1px 0 0 rgba(240,212,136,0.18);
}
.hip-tech-pos {
  font-size: 9px;
  letter-spacing: 0.36em;
  text-transform: uppercase;
  color: var(--ink-dim);
  display: flex;
  align-items: center;
  gap: 4px;
}
.hip-tech-col.home .hip-tech-pos { color: var(--gold-bright); }
.hip-tech-pos .hip-home-mark {
  color: var(--gold-bright);
  letter-spacing: 0;
  text-shadow: 0 0 4px rgba(240,212,136,0.6);
}
.hip-tech-row {
  display: grid;
  grid-template-columns: 18px 1fr;
  gap: 6px;
  align-items: start;
}
.hip-tech-kind {
  width: 16px; height: 16px;
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 0;
  text-align: center;
  line-height: 14px;
  border: 1px solid var(--ink-faint);
  color: var(--ink);
}
.hip-tech-kind.sig {
  border-color: var(--gold-bright);
  color: var(--gold-bright);
}
.hip-tech-name {
  font-size: 10.5px;
  letter-spacing: 0.06em;
  color: var(--ink);
  font-weight: 700;
}
.hip-tech-row.sig .hip-tech-name { color: var(--gold-pale); }
.hip-tech-desc {
  font-size: 9.5px;
  font-style: italic;
  color: var(--ink-dim);
  line-height: 1.35;
}
  height: 78px;
  display: flex; align-items: flex-end; justify-content: center;
  overflow: hidden;
  position: relative;
  -webkit-mask-image: radial-gradient(ellipse 95% 110% at 50% 65%, #000 80%, transparent 100%);
          mask-image: radial-gradient(ellipse 95% 110% at 50% 65%, #000 80%, transparent 100%);
}
.hero-portrait svg { width: 100%; height: 100%; display: block; }
.hero-head { text-align: center; line-height: 1.1; }
.hero-name {
  color: var(--gold-bright);
  font-size: 13px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-weight: 700;
}
.hero-title {
  color: var(--ink-dim);
  font-size: 10px;
  font-style: italic;
  letter-spacing: 0.04em;
  margin-top: 2px;
}
.hero-meta {
  display: flex; gap: 5px; justify-content: center; align-items: center;
  font-size: 9px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-family: ui-monospace, 'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace;
  flex-wrap: wrap;
}
.hero-school {
  padding: 1px 5px;
  border: 1px solid var(--ink-faint);
  color: var(--ink);
  font-weight: 700;
}
.hero-school.school-physical { color: var(--ink);          border-color: var(--ink-faint); }
.hero-school.school-holy     { color: var(--gold-bright);  border-color: var(--gold); }
.hero-school.school-arcane   { color: var(--debuff);       border-color: var(--debuff); }
.hero-school.school-ranged   { color: var(--armor);        border-color: var(--armor); }
.hero-school.school-stealth  { color: var(--weak);         border-color: var(--weak); }
.hero-stat {
  color: var(--ink-dim);
  padding: 1px 3px;
}
.hero-home-tag { color: var(--ink); }
.hero-passive {
  font-size: 10.5px;
  line-height: 1.35;
  color: var(--ink);
  font-style: italic;
  padding: 4px 6px;
  background: rgba(0,0,0,0.35);
  border-left: 2px solid var(--gold-dim);
}
.hero-passive b { font-style: normal; color: var(--gold-bright); letter-spacing: 0.08em; text-transform: uppercase; font-size: 9.5px; }

/* Affinity quirks — bracketed chips, green = positive, red = negative.
   Tap a chip to see name + description via the shared chip-tooltip. */
.hero-quirks {
  display: flex;
  flex-wrap: wrap;
  gap: 3px 5px;
  padding: 2px 0 1px;
  margin: 1px 0;
}
.hero-quirks-empty {
  color: var(--ink-faint);
  font-size: 9px;
  font-style: italic;
  letter-spacing: 0.1em;
  padding: 2px 0;
}
.hero-quirk {
  display: inline-flex;
  align-items: center;
  padding: 1px 5px;
  font-size: 9px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 700;
  border: 1px solid;
  background: rgba(8,8,10,0.55);
  white-space: nowrap;
  cursor: help;
  line-height: 1.4;
}
.hero-quirk-positive {
  color: var(--heal);
  border-color: rgba(168,192,152,0.55);
}
.hero-quirk-negative {
  color: var(--blood-bright);
  border-color: rgba(200,56,56,0.55);
}
.hero-techs {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 2px;
}
.hero-tech-section {
  border-top: 1px solid rgba(212,200,168,0.12);
  padding-top: 4px;
}
.hero-tech-section.home .hero-tech-pos { color: var(--gold-bright); }
.hero-tech-pos {
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 3px;
  font-weight: 700;
}
.hero-tech-row {
  display: flex;
  align-items: flex-start;
  gap: 5px;
  padding: 2px 0;
}
.hero-tech-kind {
  flex-shrink: 0;
  width: 14px;
  height: 14px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 9px;
  font-weight: 700;
  border: 1px solid var(--ink-faint);
  color: var(--ink);
  background: rgba(0,0,0,0.4);
  letter-spacing: 0;
}
.hero-tech-kind.sig {
  border-color: var(--gold-bright);
  color: var(--gold-bright);
}
.hero-tech-body { flex: 1; min-width: 0; }
.hero-tech-name {
  font-size: 11px;
  color: var(--ink);
  font-weight: 600;
  letter-spacing: 0.04em;
}
.hero-tech-row.sig .hero-tech-name { color: var(--gold-bright); }
.hero-tech-desc {
  font-size: 9.5px;
  color: var(--ink-dim);
  line-height: 1.3;
  margin-top: 1px;
  font-style: italic;
}

/* Mobile landscape — tighten the FFT party menu so banner + 3-col grid
   still fit, and reserve room for the BACK button below. */
@media (max-height: 600px) {
  #overlay-content:has(#overlay-choices.party-inspect:not(.hidden)) {
    padding: 8px 12px 6px;
    max-height: 96vh;
  }
  .hero-inspect-party { max-height: 56vh; gap: 6px; }
  .hero-inspect-party { grid-template-columns: minmax(170px, 240px) 1fr; gap: 12px; max-height: 64vh; }
  .hip-strip { gap: 8px; padding: 6px 2px 2px; }
  .hip-avatar { max-width: 74px; width: 74px; padding: 0 2px 3px; }
  .hip-avatar-portrait { height: 60px; }
  .hip-avatar-name { font-size: 8.5px; letter-spacing: 0.16em; margin-top: 3px; }
  .hip-avatar-slot { font-size: 7px; letter-spacing: 0.18em; }
  .hip-focus-figure { padding: 2px 2px 4px; gap: 2px; }
  .hip-focus-silhouette { height: clamp(120px, 26vh, 200px); }
  .hip-focus-name { font-size: 14px; letter-spacing: 0.26em; }
  .hip-focus-title { font-size: 9.5px; }
  .hip-stat { padding: 2px 5px; gap: 4px; }
  .hip-stat .hip-stat-label { font-size: 6.5px; letter-spacing: 0.26em; }
  .hip-stat .hip-stat-val { font-size: 9.5px; }
  .hip-section-label { font-size: 8px; letter-spacing: 0.28em; }
  .hip-passive { font-size: 9.5px; padding-left: 6px; }
  .hip-passive b { font-size: 9px; }
  .hip-tech-grid { gap: 6px; }
  .hip-tech-col { padding: 5px 7px 7px; gap: 4px; }
  .hip-tech-pos { font-size: 7.5px; letter-spacing: 0.26em; }
  .hip-tech-kind { width: 13px; height: 13px; font-size: 7.5px; line-height: 11px; }
  .hip-tech-name { font-size: 9.5px; }
  .hip-tech-desc { font-size: 8.5px; }
}
#overlay-choices {
  display: flex; gap: 10px; justify-content: center;
  margin-top: 6px; margin-bottom: 4px;
}
#overlay-choices.hidden { display: none; }

/* ===== branching map: 5 level columns, variable nodes per column =====
   Flex row instead of grid — predictable on every iOS Safari and avoids
   the grid-auto-flow stacking bug we hit on phones.
   Scoped to :not(.hidden) so the .hidden class can still display:none
   when the victory summary takes over the overlay.
   !important on display + flex-direction because some iOS Safari versions
   were respecting source-order over class-specificity here. */
#overlay-choices.path-map:not(.hidden) {
  display: flex !important;
  flex-direction: row !important;
  flex-wrap: nowrap;
  justify-content: space-between;
  align-items: stretch;
  gap: 12px;
  margin-top: 6px;
  position: relative;
  width: 100%;
}
#overlay-choices.path-map.hidden { display: none !important; }
.path-col { flex: 1 1 0; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.path-col-head {
  text-align: center;
  font-size: 9.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold-pale);
  opacity: 0.75;
}
.path-col-slots {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  flex: 1;
}
.path-node {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 10px 8px 8px;
  border: 1px solid var(--gold-dim);
  border-radius: 4px;
  background: linear-gradient(180deg, #1d130a 0%, #110b06 60%, #08050300 100%);
  color: var(--ink);
  font-family: inherit;
  font-size: 11px;
  letter-spacing: 0.04em;
  cursor: default;
  transition: border-color 0.18s, transform 0.15s, box-shadow 0.18s, filter 0.18s;
  min-width: 0;
}
button.path-node { cursor: pointer; }
.path-name {
  color: var(--gold-pale);
  font-size: 11.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-align: center;
}
.path-tag {
  font-size: 9px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 1px 5px;
  border-radius: 2px;
  background: rgba(40,28,16,0.6);
  color: var(--ink-dim);
}
.path-elite-tag { color: #ffb968; background: rgba(60,30,12,0.55); }
.path-boss-tag  { color: #ff7868; background: rgba(60,12,12,0.6); border: 1px solid rgba(255,120,104,0.4); }
.path-enemies {
  display: flex; gap: 4px; justify-content: center;
  margin-top: 2px;
}
.path-enemy {
  width: 28px; height: 28px;
  border-radius: 50%;
  overflow: hidden;
  background: rgba(0,0,0,0.4);
  border: 1px solid rgba(200,164,100,0.25);
  flex-shrink: 0;
}
.path-enemy svg { width: 100%; height: 100%; display: block; }

.path-node.path-current {
  border-color: var(--gold-bright);
  box-shadow: 0 0 14px rgba(240,212,136,0.5), inset 0 0 0 1px rgba(240,212,136,0.18);
  animation: path-current-pulse 1.4s ease-in-out infinite alternate;
}
.path-node.path-current:hover { transform: translateY(-1px); filter: brightness(1.1); }
@keyframes path-current-pulse {
  from { box-shadow: 0 0 10px rgba(240,212,136,0.4), inset 0 0 0 1px rgba(240,212,136,0.18); }
  to   { box-shadow: 0 0 20px rgba(240,212,136,0.7), inset 0 0 0 1px rgba(240,212,136,0.28); }
}

.path-node.path-completed {
  border-color: var(--gold);
  opacity: 0.85;
  background: linear-gradient(180deg, #25190d 0%, #18100a 100%);
}
.path-check {
  position: absolute;
  top: 4px; right: 6px;
  color: var(--gold-bright);
  font-size: 13px;
  text-shadow: 0 0 6px rgba(240,212,136,0.6);
}

.path-node.path-skipped {
  opacity: 0.35;
  filter: grayscale(0.6);
}
.path-node.path-locked {
  opacity: 0.55;
  filter: grayscale(0.25);
  border-color: rgba(200,164,100,0.18);
}

/* ====== Map redesign — branching node states + type variants ====== */
.path-node {
  position: relative;
  z-index: 2;          /* sits above the SVG connector layer (z:0) */
}
.path-type-glyph {
  font-size: 16px;
  line-height: 1;
  color: var(--gold);
  margin-bottom: 1px;
  text-shadow: 0 0 6px rgba(240,212,136,0.45);
}
.path-node.node-elite .path-type-glyph { color: var(--gold-bright); text-shadow: 0 0 8px rgba(240,212,136,0.7); }
.path-node.node-boss  .path-type-glyph { color: var(--blood-bright); text-shadow: 0 0 8px rgba(212,69,69,0.7); font-size: 18px; }

.path-node.node-reachable {
  border-color: var(--gold-bright);
  box-shadow: 0 0 12px rgba(240,212,136,0.45), inset 0 0 0 1px rgba(240,212,136,0.18);
  animation: path-current-pulse 1.4s ease-in-out infinite alternate;
}
.path-node.node-reachable:hover { transform: translateY(-1px); filter: brightness(1.12); }
.path-node.node-completed {
  border-color: var(--gold);
  opacity: 0.85;
  background: linear-gradient(180deg, #25190d 0%, #18100a 100%);
  animation: none;
}
.path-node.node-locked {
  opacity: 0.42;
  filter: grayscale(0.4);
  border-color: rgba(200,164,100,0.16);
  animation: none;
}
.path-node.node-elite { border-color: rgba(255,185,104,0.55); }
.path-node.node-boss  { border-color: rgba(255,120,104,0.65); }
.path-node.node-rest  { border-color: rgba(152,216,120,0.45); }
.path-node.node-rest  .path-type-glyph { color: var(--heal); text-shadow: 0 0 6px rgba(152,216,120,0.55); }
.path-node.node-event { border-color: rgba(154,120,192,0.45); }
.path-node.node-event .path-type-glyph { color: var(--debuff); text-shadow: 0 0 6px rgba(154,120,192,0.55); }
.path-tag.path-rest-tag  { color: var(--heal);    background: rgba(20,40,20,0.6); }
.path-tag.path-event-tag { color: var(--debuff);  background: rgba(28,18,42,0.6); }

/* Event overlay — same wide cinematic frame as vignettes so the party
   silhouettes have room to breathe.  Override the .overlay-cinematic
   max-width (820px) with the wider vignette dimensions. */
#overlay.overlay-event #overlay-content {
  max-width: min(1100px, 100vw) !important;
  padding: 8px 18px 14px !important;
}
#overlay.overlay-event #overlay-title {
  border-bottom: none !important;
  font-size: 16px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 4px;
}
#overlay.overlay-event #overlay-title::after { display: none !important; }

/* Swap overlay — recruit's portrait + stats banner above the picker, so
   the player can see who's asking to join before deciding who leaves.
   Sizes baked in at the previously-compact-only mobile values now that
   the design canvas is locked at 405px tall on every device. */
.swap-newcomer {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin: 0 auto 6px;
  padding: 4px 10px;
  max-width: 560px;
  background: linear-gradient(180deg, rgba(28,22,18,0.65) 0%, rgba(10,8,8,0.7) 100%);
  border-left: 2px solid var(--gold-bright);
  position: relative;
}
.swap-newcomer-portrait {
  /* Portrait stays the largest single element in the banner — it's the
     cinematic moment of meeting a new hero — but no longer dominates
     the whole canvas. */
  height: clamp(78px, 22%, 110px);
  width: auto;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  flex-shrink: 0;
}
.swap-newcomer-portrait svg {
  height: 100%;
  width: auto;
  max-width: 100px;
  display: block;
  filter: drop-shadow(0 6px 18px rgba(0,0,0,0.7));
}
.swap-newcomer-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
}
.swap-newcomer-name {
  font-size: 13px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-pale);
  text-shadow: 0 0 12px rgba(240,212,136,0.45);
}
.swap-newcomer-title {
  font-size: 9.5px;
  font-style: italic;
  color: var(--ink-dim);
  letter-spacing: 0.04em;
}
.swap-newcomer-stats {
  display: flex;
  gap: 6px;
  margin-top: 1px;
}
.swap-newcomer-stat {
  font-size: 8.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 1px 5px;
  background: rgba(0,0,0,0.45);
  color: var(--gold);
}

/* Event / Rest overlays — single italic flavor line under the scene.
   Sized as a caption, not a paragraph, so the silhouettes above stay
   the focal point. */
.event-flavor {
  font-style: italic;
  color: var(--ink-dim);
  font-size: 9.5px;
  line-height: 1.35;
  text-align: center;
  margin: 0 0 3px;
  padding: 0 8px;
}
/* Party silhouettes standing alongside an event — same visual language
   as the vignette VN stage, but no dialogue rail (events use flavor +
   choices instead). */
.event-stage {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  align-items: end;
  justify-items: center;
  /* The cinematic IS the scene — silhouettes take the lion's share of
     the canvas and the choice rail beneath them collapses into slim
     horizontal strips (see .event-choice rules below).  Encounters now
     feel like vignette beats instead of a text panel + button stack. */
  height: clamp(180px, 56%, 250px);
  margin: 0 0 4px;
  position: relative;
}
.event-stage::after {
  /* Soft warm floor line under the party so they don't float. */
  content: '';
  position: absolute;
  left: 6%; right: 6%;
  bottom: 2px;
  height: 1px;
  background: linear-gradient(90deg, transparent 0%, rgba(232,220,196,0.16) 50%, transparent 100%);
  pointer-events: none;
}
/* Soft atmospheric vignette behind the party — adds depth so the
   silhouettes don't sit on a flat black plate.  Two stacked gradients:
   a warm floor pool (footlight) + a tall radial that fades the top to
   pitch, so the scene reads as a beat of held breath, not a panel. */
.event-stage::before {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 90% 60% at 50% 100%,
                    rgba(80,52,32,0.35) 0%,
                    rgba(0,0,0,0) 65%),
    radial-gradient(ellipse 120% 110% at 50% 45%,
                    rgba(20,16,18,0.7) 0%,
                    rgba(6,4,6,0.95) 78%);
  pointer-events: none;
  z-index: -1;
}
.event-actor {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.event-actor svg {
  height: 100%;
  width: auto;
  max-width: 92%;
  display: block;
  filter: drop-shadow(0 8px 22px rgba(0,0,0,0.7));
}
@media (max-height: 480px) {
  .event-stage { height: clamp(100px, 22vh, 160px); }
}

/* ===========================================================================
   WANDERER DUEL — cinematic beat that frames a wanderer fight before
   and after.  Pre-fight (intro): bright portrait + the hero's defiant
   bark + "Begin".  Post-fight (fall): dimmed/desaturated portrait +
   the hero's death line + "Continue".  Reuses the main #overlay shell
   for one-screen continuity; no extra DOM.
   =========================================================================== */
#overlay.overlay-wanderer-duel #overlay-content {
  max-width: min(720px, 100vw) !important;
  padding: 16px 24px 18px !important;
  text-align: center;
}
#overlay.overlay-wanderer-duel #overlay-title {
  font-size: 16px;
  letter-spacing: 0.32em;
  color: var(--gold-bright);
  text-transform: uppercase;
  margin-bottom: 2px;
  text-shadow: 0 0 14px rgba(240,212,136,0.45), 0 2px 0 var(--shadow);
  border-bottom: none !important;
}
#overlay.overlay-wanderer-duel #overlay-title::after { display: none !important; }
.wanderer-duel-portrait {
  /* Portrait stays the cinematic focal point of the duel beat — the
     percentage cap (vs. raw 260px) keeps it from overrunning the
     locked 405px canvas while still leaving the heroic silhouette
     dominant in the frame. */
  height: clamp(120px, 42%, 180px);
  margin: 4px auto 2px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.wanderer-duel-portrait svg {
  height: 100%;
  width: auto;
  max-width: 100%;
  filter: drop-shadow(0 14px 28px rgba(0,0,0,0.8));
}
.wanderer-duel-subtitle {
  font-size: 9.5px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin: 2px 0 6px;
}
.wanderer-duel-bark {
  font-size: 11.5px;
  line-height: 1.4;
  color: var(--ink);
  max-width: 460px;
  margin: 4px auto 8px;
  letter-spacing: 0.03em;
}
.wanderer-duel-bark b { color: var(--gold-bright); letter-spacing: 0.12em; text-transform: uppercase; font-size: 12px; }
/* Fall variant — desaturate the portrait, blood-tint the title strap,
   shift the subtitle into the "lost on the road" colour space so the
   beat reads as a funeral instead of a duel intro. */
#overlay.overlay-wanderer-duel.wanderer-duel-fall #overlay-title {
  color: rgba(232,140,140,0.85);
  text-shadow: 0 0 14px rgba(212,90,90,0.4), 0 2px 0 var(--shadow);
}
.wanderer-duel-portrait-fallen svg {
  filter: grayscale(0.85) brightness(0.55) drop-shadow(0 14px 28px rgba(0,0,0,0.85));
}
#overlay.overlay-wanderer-duel.wanderer-duel-fall .wanderer-duel-subtitle {
  color: rgba(228,180,180,0.7);
}
/* Mobile landscape — tighten the portrait and type so the whole beat
   fits above the fold without scroll. */
@media (max-height: 520px) {
  #overlay.overlay-wanderer-duel #overlay-title { font-size: 16px; letter-spacing: 0.32em; }
  .wanderer-duel-portrait { height: clamp(120px, 28vh, 180px); }
  .wanderer-duel-subtitle { font-size: 9.5px; letter-spacing: 0.24em; margin: 2px 0 4px; }
  .wanderer-duel-bark { font-size: 12px; margin: 4px auto 8px; }
  .wanderer-duel-bark b { font-size: 10.5px; }
}

/* ===========================================================================
   WANDERER MODAL — five-choice meeting on the road.  Reuses the event
   overlay's frame and the slim event-choice row layout so sigil pickers,
   buff pickers, and the main menu all share one mobile-friendly column.
   =========================================================================== */
.wanderer-stage .wanderer-portrait {
  align-items: center;
}
.wanderer-stage .wanderer-portrait svg {
  height: 110%;
  max-width: 100%;
  filter: drop-shadow(0 10px 24px rgba(0,0,0,0.75));
}
/* Disabled choice — greyed and unclickable, but still readable so the
   player understands the gate (no sigils, etc.). */
.encounter-choice.wanderer-disabled,
.encounter-choice.wanderer-disabled:hover {
  opacity: 0.45;
  cursor: not-allowed;
  filter: grayscale(0.6);
}
.encounter-choice.wanderer-disabled .sigil-desc {
  font-style: italic;
}
/* Fight choice — sharper red border so the dangerous option reads at a
   glance.  Walk-past is the quiet exit, dimmed compared to the four
   committal choices. */
.encounter-choice.wanderer-choice-fight {
  border-color: rgba(212,69,69,0.45);
}
.encounter-choice.wanderer-choice-fight .enc-name {
  color: #e89090;
}
.encounter-choice.wanderer-choice-walk-past {
  opacity: 0.72;
  border-color: rgba(180,168,148,0.25);
}
.encounter-choice.wanderer-choice-walk-past:hover {
  opacity: 0.92;
}

/* Sigil row — used inside the trade and help pickers.  Slim full-width
   strip with the glyph inline, so the layout matches the main menu's
   event-choice cards instead of fighting them.  Defeats the previous
   bug where 200px-wide sigil cards stacked vertically and overflowed
   the modal on mobile landscape. */
.encounter-choice.wanderer-sigil-row {
  display: grid;
  grid-template-columns: 32px 1fr;
  align-items: center;
  gap: 10px;
  padding: 6px 10px;
  text-align: left;
  width: 100%;
  min-width: 0;
}
.encounter-choice.wanderer-sigil-row .wanderer-sigil-glyph {
  font-size: 22px;
  line-height: 1;
  color: var(--gold-bright);
  text-shadow:
    0 0 12px rgba(240,212,136,0.55),
    0 0 22px rgba(240,212,136,0.25);
  text-align: center;
}
.encounter-choice.wanderer-sigil-row .wanderer-sigil-body {
  min-width: 0;
}
.encounter-choice.wanderer-sigil-row .enc-name {
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gold-bright);
  text-shadow: 0 0 8px rgba(240,212,136,0.4);
  margin: 0 0 1px;
}
.encounter-choice.wanderer-sigil-row .sigil-desc {
  font-size: 11px;
  line-height: 1.3;
  font-style: italic;
  color: var(--ink);
  text-align: left;
}
/* Tone differentiator: the "give" rows read as something you're letting
   go of (slightly dimmed left border in the rose hue); the "take" rows
   read as something offered to you (gold border, full brightness). */
.encounter-choice.wanderer-sigil-give {
  border-left: 2px solid rgba(200,144,144,0.55);
  opacity: 0.92;
}
.encounter-choice.wanderer-sigil-take {
  border-left: 2px solid var(--gold-bright);
}
.encounter-choice.wanderer-sigil-row:hover {
  border-color: var(--gold-bright);
  transform: translateY(-1px);
}

/* Mobile landscape (height ≤ 520) — tighten the sigil row to match the
   compact event-choice sizing in this viewport. */
@media (max-height: 520px) {
  .encounter-choice.wanderer-sigil-row {
    padding: 6px 10px;
    gap: 10px;
    grid-template-columns: 32px 1fr;
  }
  .encounter-choice.wanderer-sigil-row .wanderer-sigil-glyph { font-size: 20px; }
  .encounter-choice.wanderer-sigil-row .enc-name             { font-size: 11px; letter-spacing: 0.12em; }
  .encounter-choice.wanderer-sigil-row .sigil-desc           { font-size: 9.5px; }
}

/* Sigil-stage overlay — drifting motes evoke the 'sigil flickering into
   reach' moment.  Sits behind the party silhouettes; pure CSS-animated
   golden dots floating upward. */
.sigil-stage .sigil-motes {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: 0;
}
.sigil-stage .sigil-motes span {
  position: absolute;
  bottom: -8px;
  width: 4px; height: 4px;
  border-radius: 50%;
  background: var(--gold-bright);
  box-shadow: 0 0 8px var(--gold-bright), 0 0 16px rgba(240,212,136,0.4);
  opacity: 0;
  animation: sigil-mote-rise 4.2s ease-in infinite;
}
.sigil-stage .sigil-motes span:nth-child(1) { left: 18%; animation-delay: 0s;   width: 3px; height: 3px; }
.sigil-stage .sigil-motes span:nth-child(2) { left: 32%; animation-delay: 0.7s; width: 5px; height: 5px; }
.sigil-stage .sigil-motes span:nth-child(3) { left: 47%; animation-delay: 1.4s; width: 3px; height: 3px; }
.sigil-stage .sigil-motes span:nth-child(4) { left: 58%; animation-delay: 2.1s; width: 4px; height: 4px; }
.sigil-stage .sigil-motes span:nth-child(5) { left: 72%; animation-delay: 2.8s; width: 3px; height: 3px; }
.sigil-stage .sigil-motes span:nth-child(6) { left: 84%; animation-delay: 3.4s; width: 5px; height: 5px; }
@keyframes sigil-mote-rise {
  0%   { transform: translateY(0)      scale(0.6); opacity: 0; }
  15%  { opacity: 1; }
  85%  { opacity: 0.6; }
  100% { transform: translateY(-180px) scale(1.1); opacity: 0; }
}

/* ===========================================================================
   POST-BOSS SPOILS ROW — inline affinity reveal on the sigil-offer /
   heroic-boon overlay.  Replaces the previous "3 separate fanfare
   backdrops then sigil overlay" cascade with a single screen that
   shows party silhouettes earning their post-kill affinities inline,
   right above the sigil/boon picker.
   =========================================================================== */
.spoils-row {
  margin: 4px auto 6px;
  padding: 4px 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  border-top: 1px solid rgba(212,200,168,0.18);
  border-bottom: 1px solid rgba(212,200,168,0.18);
  background: linear-gradient(180deg, rgba(28,22,18,0.55) 0%, rgba(10,8,8,0.7) 100%);
}
.spoils-label {
  font-size: 8.5px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ink-dim);
  font-style: italic;
}
.spoils-grants {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 14px;
  flex-wrap: wrap;
}
.spoils-grant {
  display: flex;
  align-items: center;
  gap: 6px;
}
.spoils-portrait {
  width: 22px;
  height: 28px;
  overflow: hidden;
  -webkit-mask-image: radial-gradient(ellipse 80% 100% at 50% 50%, #000 60%, transparent 100%);
          mask-image: radial-gradient(ellipse 80% 100% at 50% 50%, #000 60%, transparent 100%);
}
.spoils-portrait svg {
  width: 100%;
  height: 100%;
  display: block;
}
.spoils-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
  line-height: 1.1;
}
.spoils-hero {
  font-size: 9px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-pale);
}
.spoils-quirk {
  font-size: 9.5px;
  color: var(--gold-bright);
  font-style: italic;
}
.spoils-row-boon {
  /* Subtle gold shimmer on the megaboss boon variant to mark it as
     the bigger reward beat. */
  border-color: rgba(240,212,136,0.35);
  box-shadow: 0 0 10px rgba(240,212,136,0.12) inset;
}
/* Sigil card category tints — keep the existing per-category border but
   add a faint inner glow on hover/active so the choice feels weightier. */
.encounter-choice.sigil-choice.cat-combat   { box-shadow: inset 0 0 18px rgba(212,69,69,0.12); }
.encounter-choice.sigil-choice.cat-defense  { box-shadow: inset 0 0 18px rgba(136,152,168,0.12); }
.encounter-choice.sigil-choice.cat-resource { box-shadow: inset 0 0 18px rgba(232,220,150,0.12); }
.encounter-choice.sigil-choice:hover  { transform: translateY(-1px); }

.rest-heals {
  margin: 8px auto 4px;
  text-align: center;
  font-size: 11.5px;
  letter-spacing: 0.05em;
  color: var(--heal);
  line-height: 1.5;
}
.rest-heals b { color: var(--gold-bright); }
#overlay-choices.event-choices {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin-top: 2px;
}
/* === Slim event-choice strips ============================================
   .event-choice cards used to render as tall vertical buttons with a
   centered title, gold underline accent, and NieR corner brackets at
   each corner — which made encounter screens read as "buttons" with
   a small cinematic squeezed above them.  Recast as compact horizontal
   strips here so the scene above can dominate the frame.  Title sits
   inline on the left, mechanical tag right-aligned, no chrome.

   Excluded: .wanderer-sigil-row (uses its own grid layout, see the
   forge / trade pickers where glyph + body need explicit columns).
   ========================================================================= */
.event-choice {
  width: 100%;
  min-width: 0;
  text-align: left;
}
.event-choice:not(.wanderer-sigil-row) {
  flex-direction: row;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  padding: 6px 14px;
  min-height: 0;
}
.event-choice:not(.wanderer-sigil-row)::before,
.event-choice:not(.wanderer-sigil-row)::after {
  /* Drop the corner brackets — they made every choice look like a
     framed card and ate the cinematic budget.  The strip's border is
     enough containment. */
  display: none;
}
.event-choice .enc-name {
  letter-spacing: 0.14em;
  font-size: 11px;
  /* Strip the centered title's underline accent and bottom padding —
     the slim row treats the title as inline text, not a heading. */
  text-align: left;
  padding-bottom: 0;
}
.event-choice:not(.wanderer-sigil-row) .enc-name::after {
  display: none;
}
.event-choice:not(.wanderer-sigil-row) .sigil-desc {
  font-size: 9.5px;
  color: var(--ink-dim);
  font-style: italic;
  letter-spacing: 0.02em;
  margin: 0;
  white-space: nowrap;
}

/* Recruit "Refuse" / decline option — visually subtler so the real heroes
   stay primary in the layout. */
.swap-refuse {
  opacity: 0.85;
  font-style: italic;
}
.swap-refuse:hover { opacity: 1; }
.swap-refuse .enc-name { letter-spacing: 0.18em; color: var(--ink-dim); }

/* ============ TITLE SCREEN — KIZUNA | Resonance ============ */
#overlay-body.title-screen-body {
  font-style: normal;
  text-align: center;
  margin-bottom: 14px;
}
.title-subtitle {
  font-size: 18px;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--gold);
  margin: -4px 0 16px;
  text-shadow: 0 0 12px rgba(240,212,136,0.35);
}
.title-flavor {
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--ink);
  font-style: italic;
  letter-spacing: 0.04em;
  margin-top: 6px;
  padding: 0 6px;
}
#overlay-choices.title-choices {
  display: flex;
  flex-direction: column;
  gap: 9px;
  margin-top: 6px;
}
.title-choice {
  width: 100%;
  text-align: center;
}
.title-choice .enc-name {
  letter-spacing: 0.22em;
  font-size: 13px;
}
.title-choice.disabled {
  opacity: 0.4;
  cursor: not-allowed;
  filter: grayscale(0.5);
}

/* ============ PASSWORD GATE ============ */
#password-gate {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: #050505;
  display: flex;
  align-items: center;
  justify-content: center;
}
.password-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 28px 32px;
  border: 1px solid var(--gold-dim);
  background: rgba(18,18,20,0.92);
  min-width: 260px;
  max-width: 90vw;
  text-align: center;
}
.password-title {
  font-size: 26px;
  letter-spacing: 0.28em;
  color: var(--gold-bright);
  text-shadow: 0 0 18px rgba(240,212,136,0.45);
}
.password-subtitle {
  font-size: 12px;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--gold);
  margin-top: -6px;
  margin-bottom: 8px;
}
#password-input {
  background: rgba(8,8,10,0.85);
  border: 1px solid var(--gold-dim);
  color: var(--ink);
  padding: 8px 12px;
  font-family: inherit;
  font-size: 14px;
  letter-spacing: 0.2em;
  text-align: center;
  width: 200px;
  outline: none;
  border-radius: 0;
  transition: border-color 0.18s;
}
#password-input:focus { border-color: var(--gold-bright); }
#password-submit {
  background: rgba(28,28,30,0.85);
  border: 1px solid var(--gold-bright);
  color: var(--gold-bright);
  padding: 6px 22px;
  font-family: inherit;
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  cursor: pointer;
  font-weight: 700;
}
#password-submit:hover { background: var(--gold-bright); color: var(--bg-0); }
#password-error {
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--blood-bright);
  min-height: 12px;
}

/* ============ VIGNETTES — cinematic full-frame cutscene ============
   The portrait dominates the frame; dialogue floats over the lower
   half of the imagery so the eye lands on the speaker first.  Choices
   are a slim text-link strip at the very bottom of the overlay. */
.vignette-stage {
  position: relative;
  width: 100%;
  /* Aspect-driven height — fills the overlay; the dialogue overlay sits
     on top, anchored to the bottom.  The 64% / 56vh pair caps against
     both the canvas height (when transform-scaled on desktop) and the
     actual viewport (for safety on tall portrait emulators). */
  aspect-ratio: 16 / 9;
  max-height: min(56vh, 64%);
  min-height: 150px;
  margin: 2px 0 6px;
  overflow: hidden;
  background: radial-gradient(ellipse 70% 90% at 35% 45%,
                              rgba(28,22,38,0.5) 0%,
                              rgba(8,6,10,0.95) 75%);
}
.vignette-portrait {
  position: absolute;
  inset: 0;
  display: flex; align-items: center; justify-content: center;
  /* Soft vignette mask so the portrait fades into the dark void at the
     edges instead of presenting as a hard rectangle. */
  -webkit-mask-image: radial-gradient(ellipse 80% 100% at 50% 45%,
                                      #000 60%, transparent 100%);
          mask-image: radial-gradient(ellipse 80% 100% at 50% 45%,
                                      #000 60%, transparent 100%);
}
.vignette-portrait.empty {
  -webkit-mask-image: none; mask-image: none;
  background: transparent;
}
.vignette-portrait svg {
  /* SVG is a square aspect; let it fill what it can while staying centered. */
  height: 100%;
  width: auto;
  max-width: 90%;
  display: block;
}
/* Dialogue floats over the bottom 55% of the portrait with a scrim so the
   text stays readable against any portrait color.  Tightened so dialogue
   takes less of the cinematic frame and the portrait reads bigger. */
.vignette-dialogue {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 10px 18px 8px;
  display: flex; flex-direction: column;
  gap: 4px;
  background: linear-gradient(180deg,
                              rgba(8,6,10,0) 0%,
                              rgba(8,6,10,0.72) 55%,
                              rgba(8,6,10,0.95) 100%);
  pointer-events: none; /* text only, click-throughs reach nothing */
}
.vignette-line {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 11.5px;
  line-height: 1.4;
  color: var(--ink);
  text-shadow: 0 1px 4px rgba(0,0,0,0.85);
}
.vignette-who {
  flex-shrink: 0;
  font-size: 9px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold-bright);
  font-weight: 700;
  min-width: 52px;
  align-self: center;
}
.vignette-text { flex: 1; letter-spacing: 0.01em; }
.vignette-line.narration {
  color: var(--ink-dim);
  font-style: italic;
  padding-left: 60px;
}
.vignette-line.speaker .vignette-who { color: var(--gold-pale); }

/* Choice strip — slim, text-driven; one line per choice with a small
   chip carrying the mechanical tag. */
#overlay-choices.vignette-choices {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 4px;
}
.vignette-choice {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px;
  padding: 5px 10px;
  background: linear-gradient(180deg, rgba(20,18,16,0.55) 0%, rgba(10,8,8,0.65) 100%);
  border: none;
  color: var(--ink);
  font-family: inherit;
  cursor: pointer;
  text-align: left;
  letter-spacing: 0.02em;
  font-size: 11px;
  transition: background 0.16s, color 0.16s;
}
.vignette-choice:hover {
  background: linear-gradient(180deg, rgba(48,32,18,0.7) 0%, rgba(16,10,8,0.8) 100%);
  color: var(--gold-pale);
}
.vc-label {
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 600;
  font-size: 10px;
}
.vc-tag {
  font-size: 9px;
  color: var(--ink-dim);
  font-style: italic;
  letter-spacing: 0.02em;
}

/* The vignette overlay deserves more frame width than a normal overlay
   so the portrait can breathe. */
#overlay-content:has(#overlay-choices.vignette-choices:not(.hidden)) {
  max-width: min(760px, 96vw);
  padding: 16px 22px 14px;
}
#overlay-body.vignette-body { margin-bottom: 0; }

@media (max-height: 520px) {
  .vignette-stage { max-height: 52vh; min-height: 180px; margin: 2px 0 6px; }
  .vignette-dialogue { padding: 12px 18px 10px; gap: 4px; }
  .vignette-line { font-size: 12px; line-height: 1.35; gap: 8px; }
  .vignette-who { font-size: 9px; min-width: 50px; }
  .vignette-line.narration { padding-left: 58px; }
  .vignette-choice { padding: 6px 10px; font-size: 11.5px; }
  .vc-label { font-size: 10.5px; }
  .vc-tag   { font-size: 9.5px; }
}
@media (max-height: 400px) {
  .vignette-stage { max-height: 48vh; min-height: 150px; }
  .vignette-dialogue { padding: 8px 14px 8px; }
  .vignette-line { font-size: 11px; }
}

/* SVG connector layer drawn behind nodes */
#overlay-choices.path-map { position: relative; }
/* Ambient drift — dust motes rising slowly behind the map nodes so
   the screen breathes between the connector dashes and the node
   pulses.  CSS-only, no DOM additions, no JS. */
#overlay-choices.path-map::before {
  content: '';
  position: absolute;
  inset: -20px 0;
  pointer-events: none;
  z-index: 0;
  background-image:
    radial-gradient(2px 2px at 12% 88%, rgba(232,220,196,0.40), transparent 60%),
    radial-gradient(2px 2px at 31% 72%, rgba(232,220,196,0.30), transparent 60%),
    radial-gradient(1px 1px at 47% 60%, rgba(232,220,196,0.55), transparent 60%),
    radial-gradient(2px 2px at 64% 78%, rgba(232,220,196,0.35), transparent 60%),
    radial-gradient(1px 1px at 79% 64%, rgba(232,220,196,0.50), transparent 60%),
    radial-gradient(2px 2px at 22% 40%, rgba(232,220,196,0.32), transparent 60%),
    radial-gradient(1px 1px at 58% 32%, rgba(232,220,196,0.45), transparent 60%),
    radial-gradient(2px 2px at 88% 22%, rgba(232,220,196,0.30), transparent 60%);
  background-size: 100% 200%;
  animation: map-drift 18s linear infinite;
  opacity: 0.6;
}
@keyframes map-drift {
  from { background-position: 0% 100%; }
  to   { background-position: 0% 0%; }
}
.map-connectors { overflow: visible; }
.map-connector {
  stroke: rgba(200,164,100,0.25);
  stroke-width: 1.5;
  stroke-dasharray: 4 5;
  fill: none;
}
.map-connector.completed {
  stroke: var(--gold-bright);
  stroke-width: 2;
  stroke-dasharray: none;
  opacity: 0.9;
  filter: drop-shadow(0 0 4px rgba(240,212,136,0.5));
}
.map-connector.reachable {
  stroke: var(--gold-bright);
  stroke-width: 2;
  stroke-dasharray: 5 3;
  opacity: 0.85;
  animation: map-connector-flow 1.6s linear infinite;
}
.map-connector.locked {
  stroke: rgba(200,164,100,0.18);
  stroke-width: 1;
  stroke-dasharray: 3 6;
}
@keyframes map-connector-flow {
  to { stroke-dashoffset: -16; }
}

@media (max-width: 720px) {
  .path-name { font-size: 10.5px; letter-spacing: 0.08em; }
  .path-enemy { width: 22px; height: 22px; }
  .path-tag { font-size: 8.5px; padding: 1px 4px; }
  #overlay-content:has(#overlay-choices.path-map:not(.hidden)) {
    max-width: 92vw;
    padding: 16px 14px 14px;
  }
}

/* Landscape phones — viewport is short (~350–460px tall). Compact every
   overlay variant so welcome / summary / map / sigil pickers fit without
   clipping or scrolling. Triggers at <= 520px viewport height. */
@media (max-height: 520px) {
  /* base overlay shell */
  #overlay-content {
    padding: 14px 22px;
    max-height: 96vh;
  }
  #overlay-content::before, #overlay-content::after {
    font-size: 14px; top: 4px;
  }
  #overlay-title {
    font-size: 20px;
    margin-bottom: 8px;
    letter-spacing: 0.14em;
  }
  #overlay-title::after { margin: 4px auto 0; }
  #overlay-body {
    font-size: 12px;
    margin-bottom: 12px;
    line-height: 1.45;
  }
  #overlay-btn {
    padding: 8px 22px;
    font-size: 11px;
    letter-spacing: 0.18em;
  }

  /* welcome */
  .welcome-lede { font-size: 12px; margin-bottom: 8px; }
  .welcome-list { gap: 5px; }
  .welcome-list li { font-size: 11.5px; line-height: 1.35; }
  .welcome-key { font-size: 10px; }

  /* victory / run summary */
  #overlay-content:has(#overlay-body.victory-summary-body) {
    padding: 14px 18px 12px;
    max-height: 96vh;
  }
  .vs-subtitle { font-size: 10px; margin-bottom: 6px; }
  .vs-meta { font-size: 11px; gap: 10px; margin-bottom: 8px; }
  .vs-rows { gap: 4px; }
  .vs-row { padding: 4px 8px; }
  .vs-name { font-size: 11px; }
  .vs-stats { font-size: 10px; gap: 8px; }
  .vs-syn { margin-top: 8px; padding-top: 6px; font-size: 10px; }
  .vs-syn-label { font-size: 9px; margin-bottom: 2px; }
  .vs-syn-chip { font-size: 9.5px; padding: 1px 5px; }
  .vs-flavor { font-size: 11px; margin-bottom: 8px; }

  /* path / encounter choice overlays */
  #overlay-content:has(#overlay-choices:not(.hidden)) {
    padding: 14px 18px 12px;
    max-height: 96vh;
  }
  #overlay-content:has(#overlay-choices.path-map:not(.hidden)) {
    padding: 12px 16px 10px;
    max-width: 96vw;
  }
  #overlay-choices { gap: 10px; margin-top: 6px; margin-bottom: 4px; flex-wrap: wrap; }
  #overlay-choices.path-map { gap: 8px; flex-wrap: nowrap; }
  .path-col-head { font-size: 8.5px; letter-spacing: 0.18em; }
  .path-col-slots { gap: 6px; }
  .path-node { padding: 6px 6px 5px; font-size: 10px; }
  .path-name { font-size: 10px; letter-spacing: 0.06em; }
  .path-tag { font-size: 8px; }
  .path-enemy { width: 18px; height: 18px; }

  /* encounter / sigil pick cards — shrink to fit */
  .encounter-choice {
    min-width: 0;
    padding: 8px 10px;
    gap: 6px;
  }
  .encounter-choice .enc-name {
    font-size: 11px;
    letter-spacing: 0.14em;
    padding-bottom: 4px;
  }
  .encounter-choice .enc-icon { width: 30px; height: 30px; }
  .sigil-choice {
    width: auto;
    min-width: 0;
    flex: 1 1 0;
    padding: 8px 10px 8px;
  }
  .sigil-glyph { font-size: 26px; padding: 4px 0 2px; }
  .sigil-desc { font-size: 10px; line-height: 1.3; }
}
.encounter-choice {
  background:
    linear-gradient(180deg, #251a10 0%, #160f08 60%, #0a0604 100%);
  border: 1px solid var(--gold-dim);
  border-radius: var(--card-radius);
  /* Tightened padding / gap / min-width so a row of 3 swap or wanderer
     choices + the cinematic banner above them all fit within the
     405px-tall design canvas without scrolling. */
  padding: 8px 12px;
  cursor: pointer;
  color: var(--ink);
  font-family: inherit;
  min-width: 150px;
  transition: border-color 0.2s, transform 0.15s, box-shadow 0.2s, filter 0.15s;
  display: flex; flex-direction: column; gap: 6px;
  position: relative;
  box-shadow:
    inset 0 1px 0 rgba(232,220,196,0.08),
    0 4px 16px rgba(0,0,0,0.6);
}
.encounter-choice::before, .encounter-choice::after {
  content: '';
  position: absolute;
  width: 12px; height: 12px;
  pointer-events: none;
}
.encounter-choice::before {
  top: -1px; left: -1px;
  border-top: 2px solid var(--gold);
  border-left: 2px solid var(--gold);
}
.encounter-choice::after {
  bottom: -1px; right: -1px;
  border-bottom: 2px solid var(--gold);
  border-right: 2px solid var(--gold);
}
.encounter-choice:hover {
  border-color: var(--gold-bright);
  transform: translateY(-3px);
  filter: brightness(1.12);
  box-shadow:
    inset 0 1px 0 rgba(240,212,136,0.18),
    0 8px 28px rgba(200,164,100,0.35),
    0 0 24px rgba(240,212,136,0.25);
}
.encounter-choice:hover::before, .encounter-choice:hover::after {
  border-color: var(--gold-pale);
}
.encounter-choice .enc-name {
  font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--gold-bright); text-align: center;
  text-shadow: 0 0 8px rgba(240,212,136,0.4), 0 1px 0 var(--shadow);
  position: relative;
  padding-bottom: 4px;
}
.encounter-choice .enc-name::after {
  content: '';
  position: absolute;
  bottom: 0; left: 50%; transform: translateX(-50%);
  width: 50%; height: 1px;
  background: linear-gradient(90deg, transparent, var(--gold-dim) 50%, transparent);
}
.encounter-choice .enc-row {
  display: flex; gap: 6px; justify-content: center;
}
.encounter-choice .enc-icon {
  width: 38px; height: 50px; overflow: hidden;
  border-radius: 2px; border: 1px solid var(--gold-deep);
  position: relative;
  background: var(--parchment-deep);
  box-shadow: inset 0 0 8px rgba(0,0,0,0.6);
}
.encounter-choice .enc-icon svg { width: 100%; height: 100%; display: block; }
.encounter-choice .enc-icon-slot {
  position: absolute; bottom: 0; left: 0; right: 0;
  font-size: 7px; text-align: center;
  letter-spacing: 0.15em; text-transform: uppercase;
  background: linear-gradient(180deg, transparent, rgba(0,0,0,0.85));
  color: var(--ink-dim);
  padding: 6px 0 1px;
  font-weight: bold;
}

/* ============ RECRUIT + SWAP CARDS ============ */
.recruit-choice, .swap-choice {
  width: 200px;
  padding: 14px 14px 12px;
  align-items: stretch;
}
.recruit-portrait {
  width: 100%;
  /* Tightened from 130px so a row of three swap-candidate cards
     fits below the newcomer banner inside the 405-tall canvas.  The
     portrait remains the dominant element of each card — meta and
     stats sit underneath as a slim caption. */
  height: 84px;
  border-radius: 3px;
  overflow: hidden;
  background: var(--parchment-deep);
  border: 1px solid var(--gold-deep);
  box-shadow: inset 0 0 8px rgba(0,0,0,0.6);
}
.recruit-portrait svg {
  width: 100%; height: 100%; display: block;
  -webkit-mask-image: radial-gradient(ellipse 95% 110% at 50% 52%, #000 78%, transparent 100%);
          mask-image: radial-gradient(ellipse 95% 110% at 50% 52%, #000 78%, transparent 100%);
}
.recruit-meta {
  display: flex; flex-direction: column;
  gap: 4px; align-items: stretch;
  margin-top: 2px;
}
.recruit-title {
  font-size: 10px; color: var(--ink-dim);
  font-style: italic; letter-spacing: 0.06em;
  text-align: center;
}
.recruit-stats {
  display: flex; gap: 6px; justify-content: center;
  flex-wrap: wrap;
}
.recruit-stat {
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gold);
  background: rgba(20,12,8,0.6);
  border: 1px solid var(--gold-deep);
  border-radius: 2px;
  padding: 2px 5px;
  font-weight: bold;
}
.recruit-passive {
  font-size: 10px;
  color: var(--ink);
  font-style: italic;
  line-height: 1.35;
  text-align: center;
  padding: 4px 6px;
  background: rgba(20,12,8,0.4);
  border-top: 1px solid var(--gold-deep);
  border-bottom: 1px solid var(--gold-deep);
}
.recruit-passive b {
  font-style: normal;
  color: var(--gold-bright);
  letter-spacing: 0.04em;
}
.swap-choice .enc-name { color: var(--ink); }
.swap-downed { opacity: 0.5; filter: grayscale(0.6); }
.swap-downed .enc-name { color: var(--ink-faint); }

/* ============ UPGRADE CARDS ============
   Layout reads top-down:  meta row (avatar + bracketed identity), then
   upgrade title in gold, then a delta block showing the OLD tech dimmed
   and the NEW description bright, separated by a centered chevron.  The
   card itself carries NieR corner brackets via .encounter-choice's existing
   ::before / ::after — we only need the inner structure here. */
.upgrade-choice {
  width: 200px;
  padding: 8px 12px;
  align-items: stretch;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.upgrade-meta-row {
  display: flex;
  align-items: center;
  gap: 10px;
}
.upgrade-avatar {
  width: 32px; height: 44px;
  overflow: hidden;
  flex-shrink: 0;
  -webkit-mask-image: radial-gradient(ellipse 95% 110% at 50% 52%, #000 78%, transparent 100%);
          mask-image: radial-gradient(ellipse 95% 110% at 50% 52%, #000 78%, transparent 100%);
  opacity: 0.85;
}
.upgrade-avatar svg {
  width: 100%; height: 100%; display: block;
}
.upgrade-meta-label {
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold);
  flex: 1;
  text-align: left;
}
.upgrade-title {
  font-size: 14px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--gold-bright);
  text-shadow: 0 0 10px rgba(240,212,136,0.5), 0 1px 0 rgba(0,0,0,0.9);
  text-align: center;
  padding: 4px 0 6px;
  border-bottom: 1px solid rgba(212,200,168,0.18);
  line-height: 1.15;
}
.upgrade-delta {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 10px;
  align-items: center;
}
.upgrade-from {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  text-align: left;
}
.upgrade-from-name {
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-dim);
  font-style: italic;
  text-decoration: line-through;
  text-decoration-color: rgba(212,200,168,0.35);
}
.upgrade-from-desc {
  font-size: 9.5px;
  line-height: 1.35;
  color: var(--ink-faint);
  font-style: italic;
}
.upgrade-chevron {
  font-size: 22px;
  font-weight: 200;
  line-height: 1;
  color: var(--gold-bright);
  text-shadow: 0 0 8px rgba(240,212,136,0.55), 0 1px 0 rgba(0,0,0,0.9);
  text-align: center;
  padding: 0 4px;
}
.upgrade-to {
  display: flex;
  flex-direction: column;
  min-width: 0;
  text-align: left;
}
.upgrade-to-desc {
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--ink);
  font-style: normal;
}

/* ============ SIGIL CARDS ============ */
.sigil-choice {
  width: 200px;
  padding: 14px 14px 12px;
  align-items: center;
}
.sigil-choice .enc-name {
  color: var(--gold-bright);
  text-shadow: 0 0 8px rgba(240,212,136,0.55), 0 1px 0 var(--shadow);
}
.sigil-glyph {
  font-size: 36px;
  color: var(--gold-bright);
  line-height: 1;
  text-shadow:
    0 0 16px rgba(240,212,136,0.65),
    0 0 30px rgba(240,212,136,0.3),
    0 2px 0 var(--shadow);
  padding: 8px 0 6px;
  filter: drop-shadow(0 2px 4px rgba(0,0,0,0.6));
}
.sigil-desc {
  font-size: 11px;
  color: var(--ink);
  line-height: 1.4;
  font-style: italic;
  text-align: center;
}

/* ============ ELITE ENCOUNTER MARKER ============ */
.encounter-choice.encounter-elite {
  border-color: var(--blood-bright);
  box-shadow:
    inset 0 1px 0 rgba(255,200,200,0.18),
    0 4px 16px rgba(0,0,0,0.6),
    0 0 18px rgba(212,69,69,0.35);
}
.encounter-choice.encounter-elite::before,
.encounter-choice.encounter-elite::after { border-color: var(--blood-bright); }
.encounter-choice.encounter-elite:hover {
  border-color: var(--blood-bright);
  filter: brightness(1.15);
  box-shadow:
    inset 0 1px 0 rgba(255,200,200,0.3),
    0 8px 28px rgba(212,69,69,0.45),
    0 0 28px rgba(212,69,69,0.55);
}
.encounter-choice .enc-badge {
  font-size: 9px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--blood-bright);
  background: linear-gradient(180deg, rgba(212,69,69,0.25), rgba(70,16,15,0.25));
  border: 1px solid var(--blood-bright);
  border-radius: 10px;
  padding: 2px 8px;
  align-self: center;
  text-shadow: 0 1px 0 var(--shadow);
  text-align: center;
}

/* ============ ENEMY AFFINITY CHIPS (weakness / resistance) ============
   Minimal style: monospace W:HLY / R:PHY plain text, no brackets, no fill.
   Color carries the W/R distinction. */
.affinity-row {
  display: flex;
  gap: 7px;
  justify-content: center;
  margin-bottom: 2px;
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
}
.affinity-chip {
  display: inline-flex;
  align-items: center; justify-content: center;
  gap: 0;
  height: 12px;
  padding: 0;
  border: none;
  background: transparent;
  box-shadow: none;
  border-radius: 0;
  font-size: 8.5px;
  line-height: 1;
  font-weight: 700;
  letter-spacing: 0.06em;
  cursor: help;
  text-shadow: 0 1px 0 var(--shadow);
  white-space: nowrap;
  font-family: ui-monospace, 'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace;
}
.affinity-chip .aff-tag {
  font-size: 8.5px;
  opacity: 1;
}
.affinity-chip .aff-tag::after {
  content: ':';
  opacity: 0.7;
  margin: 0 1px;
}
.affinity-chip .aff-school {
  font-size: 8.5px;
  letter-spacing: 0.05em;
}
.affinity-chip.weak   { color: var(--blood-bright); }
.affinity-chip.resist { color: var(--armor); opacity: 0.88; }

/* ============ HUD RESOLVE MINI ============ */
.hud-resolve-mini {
  display: inline-flex;
  align-items: baseline;
  gap: 3px;
  margin-left: 10px;
  font-size: 9px;
  color: var(--ink-dim);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.hud-resolve-glyph {
  color: var(--gold-bright);
  font-size: 9px;
  text-shadow: 0 0 6px rgba(240,212,136,0.45);
}
.hud-resolve-num {
  color: var(--gold-bright);
  font-size: 11px;
  font-weight: bold;
  font-variant-numeric: tabular-nums;
  text-shadow: 0 0 6px rgba(240,212,136,0.4), 0 1px 0 var(--shadow);
}
.hud-resolve-max {
  color: var(--ink-faint);
  font-size: 8.5px;
}
.hud-resolve-mini.has-reserved .hud-resolve-num {
  color: var(--gold);
  text-shadow: 0 0 4px rgba(200,164,100,0.3);
}

/* ===========================================================================
   FANTASY MINIMALISM PASS — Code Vein / FFXVI inspiration
   Softens the prior heavy-border / corner-bracket look in favor of shapes
   drawn out of the dark by gradients, subtle inset glow, and typography.
   Kept as an additive block so the underlying styles are still legible and
   trivially revertable if anything reads worse than the original.
   =========================================================================== */

/* Battle cards — drop the framed look in favor of a soft inset glow.
   The figures inside (portraits + HP bars) carry their own weight. */
.card {
  background: linear-gradient(180deg, rgba(24,18,16,0.55) 0%, rgba(10,8,8,0.65) 100%);
  border: 1px solid transparent;
  box-shadow:
    0 6px 18px rgba(0,0,0,0.55),
    inset 0 0 24px rgba(0,0,0,0.55),
    inset 0 1px 0 rgba(232,220,196,0.04);
}
/* The corner brackets were the loudest piece of the old look — hide them
   so cards read as silhouettes against the void instead. */
.card .cnr { display: none; }
.card.targeted-lethal, .card.target-marker, .card.targeted-by-enemy {
  /* keep targeting state, but express it through a soft glow rather than
     brighter brackets */
  box-shadow:
    0 6px 18px rgba(0,0,0,0.55),
    inset 0 0 24px rgba(0,0,0,0.45),
    0 0 22px rgba(200,80,80,0.25);
}

/* Tile actions — the bordered tile-row was the densest band of lines.
   Use a faint background tint + bottom-only hairline for separation; let
   the gold come in on hover/queued only. */
.tile {
  background: linear-gradient(180deg, rgba(20,18,18,0.35) 0%, rgba(10,8,8,0.55) 100%);
  border: 1px solid transparent;
  border-bottom: 1px solid rgba(212,200,168,0.06);
  box-shadow: none;
}
.tile:not(:disabled):hover {
  background: linear-gradient(180deg, rgba(40,28,18,0.55) 0%, rgba(18,12,8,0.65) 100%);
  border-color: transparent;
  border-bottom-color: rgba(240,212,136,0.35);
  box-shadow: 0 0 14px rgba(200,164,100,0.15) inset;
}
.tile.queued {
  background: linear-gradient(180deg, rgba(58,40,24,0.7) 0%, rgba(22,14,8,0.75) 100%);
  border-color: transparent;
  border-bottom-color: var(--gold-bright);
  box-shadow: 0 0 18px rgba(232,220,196,0.18) inset;
}

/* Overlay choice cards — sigils, recruits, upgrades, vignette choices,
   events.  Drop the framed look; let the heading + tag carry hierarchy. */
.encounter-choice {
  background: linear-gradient(180deg, rgba(20,18,16,0.65) 0%, rgba(10,8,8,0.75) 100%);
  border: 1px solid transparent;
  box-shadow: inset 0 0 24px rgba(0,0,0,0.4);
  transition: background 0.18s, box-shadow 0.18s, transform 0.06s;
}
.encounter-choice:not(:disabled):hover {
  background: linear-gradient(180deg, rgba(46,30,20,0.75) 0%, rgba(16,10,8,0.85) 100%);
  box-shadow: inset 0 0 28px rgba(0,0,0,0.3), 0 0 18px rgba(232,220,196,0.12);
}

/* Status pills — let the COLOR carry identity; drop the bordered chip. */
.status-pill, .status-armor, .status-bleed, .status-taunt, .status-dulled,
.status-retal, .status-vuln, .status-sentinel, .status-steadfast,
.status-bloodlust, .status-focus, .status-note, .status-lone,
.status-bhunt, .status-evis {
  border: none !important;
  background: rgba(0,0,0,0.45);
  padding: 0 4px;
  letter-spacing: 0.12em;
  text-shadow: 0 0 6px currentColor;
}

/* Sigil chips (run header) — soften the chip outline; rely on the icon. */
.sigil-chip {
  border-color: transparent !important;
  background: rgba(20,18,16,0.55);
  box-shadow: inset 0 0 12px rgba(0,0,0,0.35);
}

/* Overlay panel — the central modal.  Pull the heavy 1.5px gold frame
   into a soft outer glow so the panel reads as light drawn out of the
   dark, not a placard stamped onto it. */
#overlay-content {
  border: 1px solid transparent !important;
  background: radial-gradient(ellipse 120% 100% at 50% 0%,
                              rgba(36,28,22,0.92) 0%,
                              rgba(12,10,10,0.97) 70%) !important;
  box-shadow:
    0 16px 60px rgba(0,0,0,0.8),
    inset 0 1px 0 rgba(232,220,196,0.06),
    0 0 40px rgba(200,164,100,0.08) !important;
}
/* Title separator: a soft fading line instead of a hard rule. */
#overlay-title::after {
  background: linear-gradient(90deg,
                              transparent 0%,
                              rgba(232,220,196,0.25) 50%,
                              transparent 100%) !important;
  height: 1px !important;
  box-shadow: none !important;
}
/* Hide the small corner brackets on the overlay frame. */
#overlay-content::before, #overlay-content::after { display: none !important; }

/* Map nodes — bordered rectangles → silhouettes with soft halos.
   Reachable nodes glow gently; completed nodes recede into the dark. */
.path-node {
  border: 1px solid transparent !important;
  box-shadow: 0 0 0 1px rgba(212,200,168,0.06), 0 0 14px rgba(0,0,0,0.5);
  background: linear-gradient(180deg, rgba(28,22,18,0.65) 0%, rgba(10,8,8,0.85) 100%) !important;
}
button.path-node:not(:disabled) {
  box-shadow: 0 0 0 1px rgba(232,220,196,0.16), 0 0 18px rgba(232,220,196,0.14);
}
button.path-node:not(:disabled):hover {
  box-shadow: 0 0 0 1px rgba(232,220,196,0.35), 0 0 26px rgba(232,220,196,0.28);
}
.path-node.path-current {
  border: 1px solid transparent !important;
  box-shadow: 0 0 0 1px rgba(232,220,196,0.45), 0 0 28px rgba(232,220,196,0.35) !important;
}
.path-node.path-completed {
  box-shadow: 0 0 0 1px rgba(120,108,84,0.16) !important;
}

/* HUD seams — replace the 1px hairline with a soft gradient fade. */
#hud-bar, #hud {
  border-bottom: none !important;
  box-shadow: 0 8px 16px -8px rgba(0,0,0,0.6);
  background: linear-gradient(180deg, rgba(10,8,8,0.9) 0%, rgba(10,8,8,0.6) 100%);
}

/* Action input row — the "bottom bar" that holds the tile grid.  Soften
   the top boundary so it doesn't feel like a stamp. */
#tile-grid {
  background: linear-gradient(0deg, rgba(10,8,8,0.85) 0%, rgba(10,8,8,0) 100%);
}

/* Title screen / password gate — already minimal, but pull the gate's
   panel border out so the title floats. */
.password-card {
  border: 1px solid transparent;
  background: radial-gradient(ellipse 100% 80% at 50% 0%,
                              rgba(32,24,22,0.95) 0%,
                              rgba(10,8,8,0.95) 80%);
  box-shadow: 0 14px 40px rgba(0,0,0,0.75), inset 0 1px 0 rgba(232,220,196,0.06);
}

/* (vignette portrait styling now lives with the main vignette block;
   this override slot intentionally left empty for future tuning) */

/* Subtler hairlines wherever a hard 1px line was carrying a section break. */
.welcome-list li, .vs-row, .upgrade-row {
  border-bottom: 1px solid rgba(212,200,168,0.06);
}

/* ===========================================================================
   MOBILE FIT — compress overlay/title/path/vignette content so a landscape
   phone never has to scroll vertically to see all options.
   =========================================================================== */
@media (max-height: 480px) {
  /* Reclaim vertical space in the universal overlay frame. */
  #overlay-content { padding: 14px 22px 12px !important; max-height: 100dvh !important; }
  #overlay-title   { font-size: 18px; margin-bottom: 4px !important; padding-bottom: 4px !important; }

  /* Title screen */
  .title-subtitle  { font-size: 13px; margin: -4px 0 10px !important; }
  .title-flavor    { font-size: 11.5px; line-height: 1.45; margin-top: 2px !important; }
  #overlay-choices.title-choices { gap: 6px !important; margin-top: 4px !important; }
  .title-choice    { padding: 6px 10px !important; }
  .title-choice .enc-name { font-size: 11.5px; }
  .title-choice .sigil-desc { font-size: 9.5px; margin-top: 1px; }

  /* Vignette stage */
  /* Vignette mobile sizing now lives in the main vignette block via its
     own @media (max-height: 520px) — kept here only for choice padding. */
  .vignette-choice  { padding: 5px 10px !important; font-size: 11px !important; }

  /* Path map nodes */
  #overlay-content:has(#overlay-choices.path-map:not(.hidden)) {
    padding: 10px 14px 10px !important;
  }
  .path-node { padding: 4px 6px 4px !important; gap: 2px !important; font-size: 9.5px !important; }
  .path-name { font-size: 9.5px !important; letter-spacing: 0.06em !important; }
  .path-cost, .path-rest-flavor, .path-event-tag, .path-boss-tag { font-size: 8.5px !important; }
  .path-icon { width: 16px !important; height: 16px !important; font-size: 12px !important; }
  .path-enemies { gap: 2px !important; }
  .path-enemy-icon { width: 14px !important; height: 14px !important; }

  /* Recruit / upgrade / sigil choice rows shrink too. */
  .encounter-choice { padding: 6px 10px !important; }
  .encounter-choice .enc-name { font-size: 11.5px !important; }
  .encounter-choice .sigil-desc { font-size: 9.5px !important; }

  /* Victory / run summary rows */
  .vs-row { padding: 3px 0 !important; font-size: 10.5px !important; }
  .vs-flavor { font-size: 11px !important; margin-bottom: 6px !important; }
  .vs-subtitle { font-size: 10px !important; margin-bottom: 6px !important; }
}

/* Even tighter for very short viewports (small phones in landscape). */
@media (max-height: 400px) {
  #overlay-content { padding: 8px 16px 8px !important; }
  #overlay-title   { font-size: 16px !important; margin-bottom: 2px !important; }
  .title-flavor    { font-size: 10.5px !important; line-height: 1.35 !important; }
  /* Vignette deep-mobile sizing handled in the main vignette block. */
  .path-node         { padding: 3px 5px 3px !important; }
  .path-name         { font-size: 9px !important; }
}

/* ===========================================================================
   GAME FEEL — shake, hit-flash, mute toggle, tutorial toast
   =========================================================================== */

@keyframes hit-shake {
  0%   { transform: translate(0, 0); }
  20%  { transform: translate(-3px, 1px); }
  40%  { transform: translate(3px, -1px); }
  60%  { transform: translate(-2px, 0); }
  80%  { transform: translate(2px, 0); }
  100% { transform: translate(0, 0); }
}
@keyframes hit-shake-hard {
  0%   { transform: translate(0, 0); }
  15%  { transform: translate(-5px, 2px); }
  30%  { transform: translate(5px, -2px); }
  45%  { transform: translate(-4px, 1px); }
  60%  { transform: translate(4px, -1px); }
  80%  { transform: translate(-2px, 0); }
  100% { transform: translate(0, 0); }
}
.figure.hit-shake      { animation: hit-shake 0.28s cubic-bezier(.36,.07,.19,.97); }
.figure.hit-shake-hard { animation: hit-shake-hard 0.32s cubic-bezier(.36,.07,.19,.97); }

@keyframes stage-shake-soft {
  0%, 100% { transform: translate(0, 0); }
  25%      { transform: translate(-2px, 1px); }
  50%      { transform: translate(2px, -1px); }
  75%      { transform: translate(-1px, 0); }
}
@keyframes stage-shake {
  0%, 100% { transform: translate(0, 0); }
  15%      { transform: translate(-4px, 2px); }
  30%      { transform: translate(4px, -2px); }
  45%      { transform: translate(-3px, 1px); }
  60%      { transform: translate(3px, -1px); }
  80%      { transform: translate(-1px, 0); }
}
@keyframes stage-shake-hard {
  0%, 100% { transform: translate(0, 0); }
  10%      { transform: translate(-7px, 3px); }
  25%      { transform: translate(7px, -3px); }
  40%      { transform: translate(-5px, 2px); }
  55%      { transform: translate(5px, -2px); }
  70%      { transform: translate(-3px, 1px); }
  85%      { transform: translate(3px, 0); }
}
#stage.stage-shake-soft { animation: stage-shake-soft 0.20s ease-out; }
#stage.stage-shake      { animation: stage-shake      0.30s cubic-bezier(.36,.07,.19,.97); }
#stage.stage-shake-hard { animation: stage-shake-hard 0.36s cubic-bezier(.36,.07,.19,.97); }

/* Hit-flash boost — the existing .hit-flash class gets a brighter halo
   so the moment a hit lands has more impact. */
.figure.hit-flash .figure-portrait {
  filter: brightness(1.65) saturate(1.2) drop-shadow(0 0 14px rgba(232,160,140,0.7));
}

/* (#mute-btn removed — music toggle now lives inside the menu opened by
   the top-right ☰ button.  See #game-menu styles below.) */

/* Tutorial toast — corner hint with a Got It button. */
#tutorial-toast {
  position: fixed;
  bottom: 16px; left: 50%;
  transform: translateX(-50%);
  z-index: 220;
  max-width: min(420px, 88vw);
  padding: 10px 16px;
  background: radial-gradient(ellipse 120% 100% at 50% 0%,
                              rgba(36,28,22,0.96) 0%,
                              rgba(10,8,10,0.98) 70%);
  color: var(--ink);
  font-size: 12px;
  line-height: 1.4;
  text-align: center;
  box-shadow:
    0 8px 30px rgba(0,0,0,0.75),
    inset 0 1px 0 rgba(232,220,196,0.08),
    0 0 30px rgba(200,164,100,0.12);
  animation: tut-in 0.32s ease-out;
}
@keyframes tut-in {
  from { opacity: 0; transform: translate(-50%, 10px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}
#tutorial-toast .tut-text {
  display: block;
  letter-spacing: 0.02em;
}
#tutorial-toast .tut-text b { color: var(--gold-bright); letter-spacing: 0.08em; }
#tutorial-toast .tut-ok {
  display: inline-block;
  margin-top: 6px;
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold-pale);
  background: transparent;
  border: none;
  padding: 4px 8px;
  cursor: pointer;
  font-family: inherit;
}
#tutorial-toast .tut-ok:hover { color: var(--gold-bright); }

/* ===========================================================================
   COACHMARKS — contextual first-run hints anchored to specific elements.
   Replaces the sequential tutorial-toast stack: a coachmark appears once
   per device when the relevant moment first happens (weakness reveal,
   first staggered enemy, first map view, first Resonance) then never
   shows again.  See showCoachmark / cm_* triggers in game.js.
   =========================================================================== */
.coachmark {
  position: fixed;
  z-index: 240;
  max-width: min(320px, 78vw);
  padding: 10px 14px;
  background: radial-gradient(ellipse 120% 100% at 50% 0%,
                              rgba(40,30,22,0.97) 0%,
                              rgba(8,6,8,0.98) 70%);
  color: var(--ink);
  font-size: 11.5px;
  line-height: 1.45;
  text-align: center;
  border: 1px solid var(--gold-dim);
  border-radius: 4px;
  box-shadow:
    0 10px 32px rgba(0,0,0,0.85),
    inset 0 1px 0 rgba(232,220,196,0.12),
    0 0 28px rgba(200,164,100,0.22);
  animation: coachmark-in 0.28s ease-out;
  pointer-events: auto;
  cursor: pointer;
}
.coachmark.coachmark-out { animation: coachmark-out 0.24s ease-in forwards; }
@keyframes coachmark-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes coachmark-out {
  from { opacity: 1; transform: translateY(0); }
  to   { opacity: 0; transform: translateY(6px); }
}
.coachmark .coachmark-body {
  letter-spacing: 0.02em;
}
.coachmark .coachmark-body b {
  color: var(--gold-bright);
  letter-spacing: 0.06em;
}
.coachmark .coachmark-dismiss {
  display: inline-block;
  margin-top: 8px;
  font-size: 9.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold-pale);
  background: transparent;
  border: 1px solid var(--gold-dim);
  padding: 4px 12px;
  cursor: pointer;
  font-family: inherit;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.coachmark .coachmark-dismiss:hover {
  color: var(--gold-bright);
  border-color: var(--gold-bright);
  background: rgba(240,212,136,0.08);
}
/* Arrow tail — points from the card edge to the anchor element.  Drawn
   as a CSS triangle by combining a transparent border with a single
   filled side; rotation handles the placement variants. */
.coachmark .coachmark-arrow {
  position: absolute;
  width: 0; height: 0;
  border-left: 7px solid transparent;
  border-right: 7px solid transparent;
}
.coachmark.coachmark-above .coachmark-arrow {
  /* card sits ABOVE anchor — arrow points DOWN from bottom-center */
  bottom: -8px; left: 50%; transform: translateX(-50%);
  border-top: 8px solid rgba(8,6,8,0.98);
}
.coachmark.coachmark-below .coachmark-arrow {
  /* card sits BELOW anchor — arrow points UP from top-center */
  top: -8px; left: 50%; transform: translateX(-50%);
  border-bottom: 8px solid rgba(8,6,8,0.98);
  border-top: none;
}
.coachmark.coachmark-left .coachmark-arrow {
  right: -8px; top: 50%; transform: translateY(-50%);
  border-left: 8px solid rgba(8,6,8,0.98);
  border-top: 7px solid transparent;
  border-bottom: 7px solid transparent;
  border-right: none;
}
.coachmark.coachmark-right .coachmark-arrow {
  left: -8px; top: 50%; transform: translateY(-50%);
  border-right: 8px solid rgba(8,6,8,0.98);
  border-top: 7px solid transparent;
  border-bottom: 7px solid transparent;
  border-left: none;
}

/* Strip the per-portrait card backdrop — every portrait SVG paints a
   background rect (fill="url(#X-bg)") and an atmospheric overlay
   (fill="url(#X-shadow)").  Hiding both lets the silhouettes float
   against the dark stage instead of sitting on a card. */
.figure-portrait svg rect[fill*="-bg"],
.figure-portrait svg rect[fill*="-shadow"],
.recruit-portrait svg rect[fill*="-bg"],
.recruit-portrait svg rect[fill*="-shadow"],
.ts-hero svg rect[fill*="-bg"],
.ts-hero svg rect[fill*="-shadow"],
.vignette-portrait svg rect[fill*="-bg"],
.vignette-portrait svg rect[fill*="-shadow"] {
  display: none;
}

/* ===========================================================================
   TITLE SCREEN — campfire companion
   =========================================================================== */
/* Stage the hero + a campfire side-by-side, anchored to the floor. */
.ts-stage-row {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 20px;
  height: 100%;
  min-height: 160px;
  position: relative;
}
.ts-stage-row::after {
  /* Warm ground-glow centered between the hero and the fire */
  content: '';
  position: absolute;
  left: 50%; bottom: -4px;
  width: 320px; height: 48px;
  transform: translateX(-50%);
  background: radial-gradient(ellipse 60% 100% at 50% 50%,
                              rgba(232,138,68,0.28) 0%,
                              rgba(232,138,68,0) 75%);
  pointer-events: none;
  z-index: 0;
}
/* The hero shrinks so the duo composes together. */
#title-screen .ts-hero {
  max-width: 200px;
  height: 100%;
  position: relative;
  z-index: 1;
}
/* Subtle warm rim-light on the hero from the campfire side (right). */
#title-screen .ts-hero svg {
  filter: drop-shadow(-2px 0 6px rgba(0,0,0,0.7))
          drop-shadow(8px 4px 14px rgba(232,138,68,0.18));
}

/* CAMPFIRE — pure CSS shape: log triangle + three flickering flames. */
/* CAMPFIRE — rebuilt to be robust on iOS Safari:
   - no mix-blend-mode (collapses inside some flex/grid contexts)
   - no filter:blur (some iOS versions render this opaque-black)
   - larger flames, higher saturation, explicit z-index, halo via
     box-shadow on the container so the glow shows even if the flame
     elements somehow fail to paint. */
.ts-fire {
  position: relative;
  width: 100px;
  height: 140px;
  align-self: flex-end;
  flex-shrink: 0;
  z-index: 3;
  border-radius: 50%;
  box-shadow:
    0 -12px 60px 14px rgba(255,150,60,0.22),
    0 -2px  30px 4px  rgba(255,180,90,0.24);
}
.ts-fire-logs {
  position: absolute;
  left: 50%; bottom: 6px;
  transform: translateX(-50%);
  width: 72px; height: 14px;
  z-index: 4;
}
.ts-fire-logs::before,
.ts-fire-logs::after {
  content: '';
  position: absolute;
  bottom: 0;
  width: 42px; height: 10px;
  background: linear-gradient(180deg, #6b4226 0%, #2c1a10 100%);
  border-radius: 5px;
  box-shadow: 0 1px 0 rgba(0,0,0,0.6);
}
.ts-fire-logs::before { left:  0; transform: rotate(-14deg); }
.ts-fire-logs::after  { right: 0; transform: rotate( 14deg); }
.ts-flame {
  position: absolute;
  left: 50%;
  bottom: 14px;
  transform: translateX(-50%);
  width: 40px;
  height: 64px;
  background: radial-gradient(ellipse 70% 110% at 50% 95%,
                              #fff0b8 0%,
                              #ffae54 30%,
                              #e84a1c 65%,
                              rgba(120,20,8,0) 100%);
  border-radius: 50% 50% 50% 50% / 70% 70% 30% 30%;
  animation: ts-flicker 1.4s ease-in-out infinite;
  transform-origin: 50% 100%;
  z-index: 5;
}
.ts-flame.f2 {
  width: 28px;
  height: 46px;
  bottom: 20px;
  opacity: 0.95;
  animation-duration: 1.1s;
  animation-delay: 0.3s;
  background: radial-gradient(ellipse 70% 110% at 50% 95%,
                              #fff5cc 0%,
                              #ffc870 40%,
                              #e6701c 80%,
                              rgba(120,20,8,0) 100%);
}
.ts-flame.f3 {
  width: 18px;
  height: 30px;
  bottom: 28px;
  opacity: 1;
  animation-duration: 0.9s;
  animation-delay: 0.6s;
  background: radial-gradient(ellipse 60% 110% at 50% 95%,
                              #fffadc 0%,
                              #ffd896 50%,
                              #e8881c 90%,
                              rgba(120,20,8,0) 100%);
}
@keyframes ts-flicker {
  0%, 100% { transform: translateX(-50%) scaleY(1)    scaleX(1);    opacity: 1; }
  20%      { transform: translateX(-50%) scaleY(1.08) scaleX(0.92); opacity: 0.95; }
  40%      { transform: translateX(-50%) scaleY(0.95) scaleX(1.05); opacity: 1; }
  60%      { transform: translateX(-50%) scaleY(1.04) scaleX(0.96); opacity: 0.9; }
  80%      { transform: translateX(-50%) scaleY(0.98) scaleX(1.02); opacity: 1; }
}
/* Big ambient glow surrounding the fire */
.ts-fire::before {
  content: '';
  position: absolute;
  left: 50%; bottom: -10px;
  transform: translateX(-50%);
  width: 180px; height: 180px;
  background: radial-gradient(circle at 50% 50%,
                              rgba(255,170,80,0.32) 0%,
                              rgba(255,140,60,0.14) 35%,
                              rgba(255,140,60,0) 70%);
  border-radius: 50%;
  pointer-events: none;
  animation: ts-glow-pulse 3s ease-in-out infinite;
  z-index: -1;
}
@keyframes ts-glow-pulse {
  0%, 100% { opacity: 0.85; transform: translateX(-50%) scale(1); }
  50%      { opacity: 1;    transform: translateX(-50%) scale(1.08); }
}
/* Sparks drifting up from the campfire */
.ts-sparks {
  position: absolute;
  left: 50%; bottom: 40px;
  width: 40px; height: 80px;
  transform: translateX(-50%);
  pointer-events: none;
}
.ts-sparks span {
  position: absolute;
  bottom: 0;
  width: 2px; height: 2px;
  background: rgba(255,200,120,0.95);
  border-radius: 50%;
  box-shadow: 0 0 4px rgba(255,180,90,0.9);
  animation: ts-spark 2.4s linear infinite;
  opacity: 0;
}
.ts-sparks span:nth-child(1) { left: 30%; animation-delay: 0s;    }
.ts-sparks span:nth-child(2) { left: 60%; animation-delay: 0.6s;  }
.ts-sparks span:nth-child(3) { left: 45%; animation-delay: 1.2s;  }
.ts-sparks span:nth-child(4) { left: 70%; animation-delay: 1.8s;  }
@keyframes ts-spark {
  0%   { transform: translateY(0) translateX(0)  scale(1); opacity: 0; }
  15%  { opacity: 1; }
  80%  { opacity: 0.6; }
  100% { transform: translateY(-70px) translateX(8px) scale(0.4); opacity: 0; }
}

/* Mobile fit for the new title staging. */
@media (max-height: 480px) {
  #title-screen .ts-hero { max-width: 150px; }
  .ts-fire { width: 64px; height: 100px; }
  .ts-flame { width: 24px; height: 42px; }
  .ts-flame.f2 { width: 16px; height: 30px; }
  .ts-flame.f3 { width: 10px; height: 20px; }
  .ts-fire::before { width: 140px; height: 140px; }
  .ts-stage-row { gap: 14px; }
}
@media (max-height: 380px) {
  #title-screen .ts-hero { max-width: 120px; }
  .ts-fire { width: 50px; height: 80px; }
  .ts-flame { width: 18px; height: 32px; bottom: 6px; }
  .ts-flame.f2 { width: 12px; height: 22px; bottom: 10px; }
  .ts-flame.f3 { width: 8px;  height: 16px; bottom: 14px; }
}

/* ===========================================================================
   REST SCENE — heroes around a campfire
   =========================================================================== */
.rest-scene {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: end;
  justify-items: center;
  gap: 12px;
  margin: 6px 0 12px;
  min-height: 180px;
  padding-bottom: 6px;
  position: relative;
}
.rest-row {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 6px;
  height: 100%;
}
.rest-hero {
  width: 80px; height: 130px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  position: relative;
}
.rest-hero svg {
  height: 100%;
  width: auto;
  filter: drop-shadow(4px 4px 12px rgba(0,0,0,0.7))
          drop-shadow(0 0 14px rgba(255,150,60,0.18));
}
.rest-hero-right svg { transform: scaleX(-1); }
.rest-hero::after {
  content: '';
  position: absolute;
  left: 50%; bottom: 0;
  transform: translateX(-50%);
  width: 60px; height: 8px;
  background: radial-gradient(ellipse 60% 100% at 50% 50%,
                              rgba(0,0,0,0.65) 0%,
                              transparent 80%);
  pointer-events: none;
}
/* Reuse the title-screen campfire shapes (.ts-flame / .ts-fire-logs)
   but at a slightly smaller size inside the rest scene. */
.rest-fire {
  position: relative;
  width: 84px;
  height: 130px;
  z-index: 3;
  /* No border-radius / no big box-shadow — the previous oval halo was
     leaking past the scene boundary on mobile.  Warmth is provided by
     the flame elements themselves + a tighter inner glow. */
  box-shadow:
    0 -4px 20px 0 rgba(255,150,60,0.28),
    0 -1px 10px 0 rgba(255,180,90,0.22);
}
.rest-fire .ts-fire-logs { left: 50%; bottom: 6px; transform: translateX(-50%); width: 60px; height: 12px; z-index: 4; }
.rest-fire .ts-fire-logs::before, .rest-fire .ts-fire-logs::after { width: 36px; height: 8px; }
.rest-fire .ts-flame    { width: 34px; height: 56px; bottom: 12px; }
.rest-fire .ts-flame.f2 { width: 24px; height: 38px; bottom: 18px; }
.rest-fire .ts-flame.f3 { width: 14px; height: 24px; bottom: 24px; }
.rest-flavor { text-align: center; margin: 4px 0 10px; }
/* Rest-choice categorization — tinted left border on each option so the
   three actions (heal / hone / sigil) read at a glance without needing
   to scan labels. */
.rest-choice-heal  { border-left: 3px solid var(--heal); }
.rest-choice-hone  { border-left: 3px solid var(--gold-bright); }
.rest-choice-sigil { border-left: 3px solid #aacfff; }

@media (max-height: 480px) {
  .rest-scene { min-height: 140px; }
  .rest-hero { width: 60px; height: 100px; }
  .rest-fire { width: 64px; height: 100px; }
  .rest-fire .ts-flame    { width: 26px; height: 42px; }
  .rest-fire .ts-flame.f2 { width: 18px; height: 28px; }
  .rest-fire .ts-flame.f3 { width: 10px; height: 18px; }
}

/* ===========================================================================
   TEAM SPECIAL — bigger banner on trigger + upgraded tile look
   =========================================================================== */
/* End-of-fight callout — fires after the last enemy falls, before the
   victory summary opens.  Subtler than the Resonance banner — the kill
   itself is the loud moment; this just gives it a heading. */
#reach-banner {
  position: fixed;
  top: 36%; left: 50%;
  transform: translate(-50%, -50%);
  z-index: 350;
  display: flex;
  align-items: center;
  gap: 18px;
  pointer-events: none;
  animation: rb-in 0.28s cubic-bezier(.18,.89,.32,1) forwards,
             rb-out 0.45s ease-out 0.65s forwards;
}
@keyframes rb-in {
  from { opacity: 0; transform: translate(-50%, -48%) scale(0.92); filter: blur(2px); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1);    filter: blur(0); }
}
@keyframes rb-out {
  from { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  to   { opacity: 0; transform: translate(-50%, -50%) scale(1.04); filter: blur(2px); }
}
.rb-flank {
  font-size: 18px;
  color: var(--gold);
  text-shadow: 0 0 10px rgba(240,212,136,0.55);
}
.rb-name {
  font-size: 22px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--gold-pale);
  text-shadow:
    0 0 18px rgba(240,212,136,0.55),
    0 2px 0 rgba(0,0,0,0.85);
}

#ts-banner {
  position: fixed;
  top: 30%; left: 50%;
  transform: translate(-50%, -50%);
  z-index: 350;
  display: flex;
  align-items: center;
  gap: 22px;
  pointer-events: none;
  animation: tsb-in 0.32s cubic-bezier(.18,.89,.32,1) forwards,
             tsb-out 0.5s ease-out 0.9s forwards;
}
@keyframes tsb-in {
  from { opacity: 0; transform: translate(-50%, -52%) scale(0.85); filter: blur(3px); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1);    filter: blur(0); }
}
@keyframes tsb-out {
  from { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  to   { opacity: 0; transform: translate(-50%, -50%) scale(1.05); filter: blur(2px); }
}
.tsb-flank {
  font-size: 22px;
  color: var(--gold-bright);
  text-shadow: 0 0 14px rgba(240,212,136,0.65);
}
.tsb-stack {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}
.tsb-tier {
  font-size: 11px;
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--ink-dim);
  padding: 1px 6px;
  border: 1px solid currentColor;
}
.tsb-tier.tsb-tier-duo    { color: var(--gold); }
.tsb-tier.tsb-tier-triple { color: var(--gold-bright); text-shadow: 0 0 8px rgba(240,212,136,0.5); }
.tsb-tier.tsb-tier-sig    { color: #aacfff; text-shadow: 0 0 8px rgba(170,200,255,0.5); }
.tsb-name {
  font-size: 28px;
  letter-spacing: 0.36em;
  text-transform: uppercase;
  color: var(--gold-pale);
  text-shadow:
    0 0 22px rgba(240,212,136,0.55),
    0 0 4px rgba(255,255,255,0.2),
    0 3px 0 rgba(0,0,0,0.85);
}
/* Triple-tier combos burn a touch brighter; sig-tier combos shift cool. */
#ts-banner.tsb-triple .tsb-name {
  color: #fff3c8;
  text-shadow:
    0 0 26px rgba(240,212,136,0.75),
    0 0 6px rgba(255,255,255,0.32),
    0 3px 0 rgba(0,0,0,0.85);
}
#ts-banner.tsb-sig .tsb-name {
  color: #d6e6ff;
  text-shadow:
    0 0 24px rgba(170,200,255,0.65),
    0 0 5px rgba(255,255,255,0.25),
    0 3px 0 rgba(0,0,0,0.85);
}
#ts-banner.tsb-sig .tsb-flank {
  color: #aacfff;
  text-shadow: 0 0 14px rgba(170,200,255,0.7);
}

/* ===========================================================================
   COMBO CINEMATIC SYSTEM — primitives consumed by playComboCinematic.
   Each cinematic primitive injects one .cine-active element; the runtime
   collects them on skip.  All animations are CSS keyframes parameterised
   by --ms so the JS-supplied duration drives the timing.
   =========================================================================== */
body.cine-playing #tile-grid,
body.cine-playing #ts-area,
body.cine-playing #btn-fight,
body.cine-playing #queue-strip { pointer-events: none; }

/* TINT — radial school wash over the whole stage */
.cine-tint {
  position: fixed; inset: 0; pointer-events: none; z-index: 340;
  opacity: 0;
  animation: cine-tint-fade var(--ms, 200ms) ease-out forwards;
}
.cine-tint.school-physical { background: radial-gradient(ellipse at center, rgba(255,220,160,0.40), transparent 65%); }
.cine-tint.school-holy     { background: radial-gradient(ellipse at center, rgba(255,240,180,0.44), transparent 65%); }
.cine-tint.school-stealth  { background: radial-gradient(ellipse at center, rgba(160,200,255,0.42), transparent 65%); }
.cine-tint.school-ranged   { background: radial-gradient(ellipse at center, rgba(220,190,140,0.42), transparent 65%); }
.cine-tint.school-arcane   { background: radial-gradient(ellipse at center, rgba(200,160,240,0.44), transparent 65%); }
@keyframes cine-tint-fade {
  0%   { opacity: 0; }
  30%  { opacity: 1; }
  100% { opacity: 0; }
}

/* BANNER — three sizes, lives at upper-third of screen, slides + fades */
.cine-banner {
  position: fixed; top: 30%; left: 50%;
  transform: translate(-50%, -50%);
  z-index: 360;
  pointer-events: none;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  animation: cine-banner-in 0.30s cubic-bezier(.18,.89,.32,1) forwards,
             cine-banner-out 0.35s ease-out var(--out-delay, 800ms) forwards;
  text-align: center;
}
.cine-banner .cb-name {
  text-transform: uppercase;
  color: var(--gold-pale);
  text-shadow: 0 0 22px rgba(240,212,136,0.55), 0 3px 0 rgba(0,0,0,0.85);
  font-weight: 700;
}
.cine-banner .cb-sub {
  font-size: 11px;
  letter-spacing: 0.5em;
  color: var(--ink-dim);
  text-transform: uppercase;
}
.cine-banner.size-sm .cb-name { font-size: 22px; letter-spacing: 0.30em; }
.cine-banner.size-md .cb-name { font-size: 30px; letter-spacing: 0.36em; }
.cine-banner.size-lg .cb-name { font-size: 40px; letter-spacing: 0.42em; }
.cine-banner.is-sig .cb-name {
  color: #d6e6ff;
  text-shadow: 0 0 28px rgba(170,200,255,0.7), 0 3px 0 rgba(0,0,0,0.85);
}
@keyframes cine-banner-in {
  from { opacity: 0; transform: translate(-50%, -58%) scale(0.85); filter: blur(3px); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1);    filter: blur(0); }
}
@keyframes cine-banner-out {
  from { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  to   { opacity: 0; transform: translate(-50%, -50%) scale(1.05); filter: blur(2px); }
}

/* SLASH — drawn SVG stroke across the enemy half */
.cine-slash svg { width: 100%; height: 100%; overflow: visible; }
.cine-slash .cs-stroke {
  fill: none;
  stroke-width: 5;
  stroke-linecap: round;
  stroke-dasharray: 1600;
  stroke-dashoffset: 1600;
  animation: cine-slash-draw var(--ms, 280ms) cubic-bezier(.7,.0,.3,1) forwards,
             cine-slash-fade calc(var(--ms, 280ms) * 0.6) ease-out calc(var(--ms, 280ms) * 0.55) forwards;
  filter: drop-shadow(0 0 8px currentColor);
}
.cine-slash.school-physical .cs-stroke { stroke: #ffd098; color: #ffd098; }
.cine-slash.school-holy     .cs-stroke { stroke: #ffe896; color: #ffe896; }
.cine-slash.school-stealth  .cs-stroke { stroke: #b0c8ff; color: #b0c8ff; }
.cine-slash.school-ranged   .cs-stroke { stroke: #d8c08c; color: #d8c08c; }
.cine-slash.school-arcane   .cs-stroke { stroke: #c8a0f0; color: #c8a0f0; }
@keyframes cine-slash-draw { to { stroke-dashoffset: 0; } }
@keyframes cine-slash-fade { to { opacity: 0; } }

/* PORTRAIT lift — additive class applied to a party .figure for the duration
   of the step.  Distinct visual per pose so the player can read the moment. */
.figure.cine-portrait-active { z-index: 30; }
.figure.cine-portrait-rise {
  transform: translateY(-8px) scale(1.06);
  transition: transform 0.28s ease-out;
  filter: drop-shadow(0 0 24px rgba(240,212,136,0.65));
}
.figure.cine-portrait-cross {
  transform: translateY(-5px) scale(1.05) rotate(-2deg);
  transition: transform 0.28s ease-out;
  filter: drop-shadow(0 0 20px rgba(176,200,255,0.6));
}
.figure.cine-portrait-guard {
  transform: scale(1.08);
  transition: transform 0.28s ease-out;
  filter: drop-shadow(0 0 26px rgba(170,200,255,0.7));
}
.figure.cine-portrait-whisper {
  transform: translateY(-3px) scale(1.03);
  transition: transform 0.28s ease-out;
  filter: drop-shadow(0 0 16px rgba(176,200,255,0.5));
}

/* FADE — full-screen veil for cuts between phases */
.cine-fade {
  position: fixed; inset: 0;
  pointer-events: none;
  z-index: 355;
  background: #000;
  opacity: 0;
}
.cine-fade.dir-in  { animation: cine-fade-in  var(--ms, 200ms) ease-out forwards; }
.cine-fade.dir-out { animation: cine-fade-out var(--ms, 200ms) ease-out forwards; }
@keyframes cine-fade-in  { from { opacity: 0; }   to { opacity: 0.7; } }
@keyframes cine-fade-out { from { opacity: 0.7; } to { opacity: 0; } }

/* STAGE — full-screen takeover that hides the combat board behind a
   school-tinted radial wallpaper.  Holds hero-big portraits and the
   banner inside its frame, then collapses via the punch transition. */
.cine-stage {
  position: fixed; inset: 0;
  z-index: 350;
  pointer-events: none;
  overflow: hidden;
  /* Center darkness raised from 0.55 → 0.86 so the combat HUD (ATB bars,
     party portraits, enemy stats) doesn't leak through the cinematic.
     Edges stay near-opaque for the framed-stage feel. */
  background: radial-gradient(ellipse at center, rgba(0,0,0,0.86) 0%, rgba(0,0,0,0.99) 75%);
  opacity: 0;
  animation: cine-stage-in 0.30s ease-out forwards;
  display: flex; align-items: center; justify-content: center;
}
.cine-stage::before {
  /* Tighter vignette frame — pulls focus to the center stage and crushes
     any light bleed past the radial backdrop. */
  content: ''; position: absolute; inset: 0;
  background: radial-gradient(ellipse at center, transparent 22%, rgba(0,0,0,0.62) 100%);
  pointer-events: none;
}
.cine-stage-bg {
  position: absolute; inset: 0;
  pointer-events: none;
  opacity: 0.85;
  mix-blend-mode: screen;
}
.cine-stage.school-physical .cine-stage-bg { background: radial-gradient(ellipse at center, rgba(255,200,120,0.55), transparent 60%); }
.cine-stage.school-holy     .cine-stage-bg { background: radial-gradient(ellipse at center, rgba(255,240,170,0.62), transparent 60%); }
.cine-stage.school-stealth  .cine-stage-bg { background: radial-gradient(ellipse at center, rgba(150,190,255,0.55), transparent 60%); }
.cine-stage.school-ranged   .cine-stage-bg { background: radial-gradient(ellipse at center, rgba(220,190,140,0.55), transparent 60%); }
.cine-stage.school-arcane   .cine-stage-bg { background: radial-gradient(ellipse at center, rgba(190,150,240,0.6), transparent 60%); }
.cine-stage-rays {
  /* Persona-style speed lines radiating from center.  Crank up during
     hero-big phase to sell the "synchronizing" beat, then ride the
     punch zoom outward at impact. */
  position: absolute; inset: -10% -10%;
  pointer-events: none;
  background: repeating-conic-gradient(from 0deg at 50% 50%, transparent 0deg 6deg, rgba(255,255,255,0.10) 6deg 9deg);
  animation: cine-rays-spin 6s linear infinite;
  opacity: 0.55;
  mix-blend-mode: screen;
}
/* Punch frame intensifies the rays one more time, then fades with the stage. */
.cine-stage.cine-stage-punch .cine-stage-rays {
  animation: cine-rays-spin 1.4s linear infinite;
  opacity: 0.95;
}

/* Color-flash layer — invisible until the punch frame, then a single
   school-tinted strobe at impact.  Sells the "all heroes fire at once"
   beat without spawning new DOM during the cinematic. */
.cine-stage-flash {
  position: absolute; inset: 0;
  pointer-events: none;
  opacity: 0;
  mix-blend-mode: screen;
  background: radial-gradient(ellipse at center, rgba(255,255,255,0.9) 0%, rgba(255,255,255,0) 60%);
}
.cine-stage.school-holy     .cine-stage-flash { background: radial-gradient(ellipse at center, rgba(255,242,180,0.95) 0%, rgba(255,242,180,0) 60%); }
.cine-stage.school-physical .cine-stage-flash { background: radial-gradient(ellipse at center, rgba(255,210,140,0.95) 0%, rgba(255,210,140,0) 60%); }
.cine-stage.school-stealth  .cine-stage-flash { background: radial-gradient(ellipse at center, rgba(180,210,255,0.95) 0%, rgba(180,210,255,0) 60%); }
.cine-stage.school-ranged   .cine-stage-flash { background: radial-gradient(ellipse at center, rgba(232,196,140,0.95) 0%, rgba(232,196,140,0) 60%); }
.cine-stage.school-arcane   .cine-stage-flash { background: radial-gradient(ellipse at center, rgba(208,168,255,0.95) 0%, rgba(208,168,255,0) 60%); }
.cine-stage.cine-stage-punch .cine-stage-flash {
  animation: cine-flash-strobe 0.42s cubic-bezier(.4,0,.6,1) forwards;
}
.cine-stage-frame {
  position: relative;
  width: 100%; height: 100%;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 22px;
  z-index: 2;
}
.cine-stage-heroes {
  display: flex; align-items: center; justify-content: center;
  gap: clamp(8px, 2vw, 26px);
  width: min(96vw, 1100px);
}
.cine-stage-text {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  text-align: center;
  /* Constrain so long titles ("LITANY OF THE GATE", "AS ABOVE, SO BELOW")
     don't span the full viewport.  text-wrap: balance gives a clean
     two-line split when wrap is unavoidable on narrow screens. */
  max-width: min(94vw, 1000px);
  opacity: 0;
  animation: cine-stage-text-in 0.45s cubic-bezier(.18,.89,.32,1) 0.15s forwards;
}
.cine-stage-text .cb-name {
  text-transform: uppercase;
  color: var(--gold-pale);
  text-shadow: 0 0 30px rgba(240,212,136,0.65), 0 4px 0 rgba(0,0,0,0.9);
  font-weight: 800;
  text-wrap: balance;
  line-height: 1.08;
}
/* Letter-spacing trimmed across all sizes so 17-18 char combo names fit
   on one line at typical viewports.  Was 0.30/0.36/0.42 — too wide for
   long titles, forcing awkward wraps like "LITANY OF THE / GATE". */
.cine-stage-text .cb-name.size-sm { font-size: clamp(24px, 4.6vw, 34px); letter-spacing: 0.22em; }
.cine-stage-text .cb-name.size-md { font-size: clamp(30px, 5.6vw, 46px); letter-spacing: 0.24em; }
.cine-stage-text .cb-name.size-lg { font-size: clamp(40px, 7.4vw, 62px); letter-spacing: 0.28em; }
.cine-stage.is-sig .cine-stage-text .cb-name {
  color: #d6e6ff;
  text-shadow: 0 0 36px rgba(170,200,255,0.78), 0 4px 0 rgba(0,0,0,0.9);
}
.cine-stage-text .cb-sub {
  font-size: clamp(11px, 1.4vw, 14px);
  letter-spacing: 0.55em;
  color: var(--ink-dim);
  text-transform: uppercase;
}
.cine-stage-text .cb-slogan {
  font-size: clamp(13px, 1.6vw, 16px);
  letter-spacing: 0.18em;
  color: var(--gold-pale);
  opacity: 0.78;
  font-style: italic;
  text-shadow: 0 2px 0 rgba(0,0,0,0.85);
}

/* HERO-BIG — large portrait of a participating hero, scaled inside the
   stage frame.  Pulls from the existing inline-SVG PORTRAITS map.
   Heroes converge from off-screen edges so the moment reads as a sync
   attack — leftmost slides in from the left, rightmost from the right,
   any middle hero rises from below.  Each lands with a brief overshoot
   to sell the impact. */
.ch-big {
  width: clamp(160px, 22vw, 280px);
  height: clamp(220px, 30vw, 380px);
  flex: 0 0 auto;
  opacity: 0;
  animation: cine-hero-converge-up 0.52s cubic-bezier(.18,.89,.32,1.18) forwards;
  filter: drop-shadow(0 18px 28px rgba(0,0,0,0.7));
  position: relative;
  transform-origin: center bottom;
}
/* Leftmost hero slides in from the left edge. */
.ch-big:first-child:not(:only-child) { animation-name: cine-hero-converge-left; }
/* Rightmost hero slides in from the right edge. */
.ch-big:last-child:not(:only-child)  { animation-name: cine-hero-converge-right; }
/* Stagger so they don't all arrive on the same frame. */
.ch-big:nth-child(1) { animation-delay: 0.04s; }
.ch-big:nth-child(2) { animation-delay: 0.12s; }
.ch-big:nth-child(3) { animation-delay: 0.20s; }
.ch-big svg, .ch-big > * { width: 100%; height: 100%; display: block; }
/* Pose-specific aura colors layered onto the base drop-shadow.  Transform
   is animation-driven now, so poses set FILTER only — keeps the converge
   keyframe intact and lets the aura color cue each combo's tone. */
.ch-big.ch-pose-rise    { filter: drop-shadow(0 18px 32px rgba(240,212,136,0.65)) drop-shadow(0 4px 6px rgba(0,0,0,0.7)); }
.ch-big.ch-pose-cross   { filter: drop-shadow(0 18px 28px rgba(176,200,255,0.65)) drop-shadow(0 4px 6px rgba(0,0,0,0.7)); }
.ch-big.ch-pose-guard   { filter: drop-shadow(0 14px 30px rgba(255,200,120,0.65)) drop-shadow(0 4px 6px rgba(0,0,0,0.7)); }
.ch-big.ch-pose-whisper { filter: drop-shadow(0 12px 22px rgba(176,200,255,0.65)) drop-shadow(0 4px 6px rgba(0,0,0,0.7)) blur(0.4px); }

/* Punch frame triggers a synchronized flare — every hero brightens and
   scales up together in the half-beat before the stage zooms out, so
   the cinematic reads as "all three fire at once" instead of "stage
   fades away." */
.cine-stage.cine-stage-punch .ch-big {
  animation: cine-hero-flare 0.36s cubic-bezier(.4,.0,.6,1) forwards;
  animation-delay: 0s;
}

/* PUNCH — closes the stage with a camera punch-in zoom + fade + a brief
   screen shake.  Reveals the combat board underneath at the impact
   frame.  The shake + color flash + hero flare all fire on the same
   beat so the moment lands as a single synchronized hit. */
.cine-stage.cine-stage-punch {
  animation: cine-stage-punch 0.5s cubic-bezier(.45,.05,.55,.95) forwards;
}

@keyframes cine-stage-in {
  0%   { opacity: 0; }
  100% { opacity: 1; }
}
/* Title slam — title enters big + blurred and snaps to size with a brief
   overshoot, like a JRPG combo name slamming onto the screen. */
@keyframes cine-stage-text-in {
  0%   { opacity: 0; transform: scale(1.45) translateY(8px); filter: blur(10px); letter-spacing: 0.5em; }
  45%  { opacity: 1; transform: scale(0.96) translateY(0);   filter: blur(0);    }
  70%  { transform: scale(1.04); }
  100% { opacity: 1; transform: scale(1)    translateY(0);   filter: blur(0); }
}
@keyframes cine-hero-converge-left {
  0%   { opacity: 0; transform: translateX(-160px) scale(0.78); filter: blur(5px); }
  55%  { opacity: 1; transform: translateX(0)      scale(1.06); filter: blur(0); }
  80%  { transform: translateX(0) scale(0.98); }
  100% { opacity: 1; transform: translateX(0)      scale(1); }
}
@keyframes cine-hero-converge-right {
  0%   { opacity: 0; transform: translateX(160px)  scale(0.78); filter: blur(5px); }
  55%  { opacity: 1; transform: translateX(0)      scale(1.06); filter: blur(0); }
  80%  { transform: translateX(0) scale(0.98); }
  100% { opacity: 1; transform: translateX(0)      scale(1); }
}
@keyframes cine-hero-converge-up {
  0%   { opacity: 0; transform: translateY(60px)   scale(0.78); filter: blur(5px); }
  55%  { opacity: 1; transform: translateY(0)      scale(1.06); filter: blur(0); }
  80%  { transform: translateY(0) scale(0.98); }
  100% { opacity: 1; transform: translateY(0)      scale(1); }
}
/* Heroes pulse bright + scale up together one beat before the camera
   punches out — the visible "fire" of the synchronized attack. */
@keyframes cine-hero-flare {
  0%   { transform: scale(1);    filter: brightness(1)   drop-shadow(0 0 0 rgba(255,255,255,0)); }
  45%  { transform: scale(1.18); filter: brightness(1.8) drop-shadow(0 0 28px rgba(255,255,255,0.85)); }
  100% { transform: scale(1.32); filter: brightness(1.3) drop-shadow(0 0 18px rgba(255,255,255,0.4)); opacity: 0.9; }
}
/* Punch — quick zoom + dual-axis shake before the fade-out.  The shake
   reads as a screen-wide impact thump even without a camera primitive. */
@keyframes cine-stage-punch {
  0%   { opacity: 1; transform: scale(1)    translate(0, 0);    filter: blur(0); }
  18%  { opacity: 1; transform: scale(1.06) translate(-6px, 4px); }
  28%  { opacity: 1; transform: scale(1.12) translate(7px, -5px); }
  38%  { opacity: 1; transform: scale(1.08) translate(-4px, 3px); }
  50%  { opacity: 1; transform: scale(1.14) translate(0, 0);    filter: blur(0); }
  100% { opacity: 0; transform: scale(1.5)  translate(0, -8px); filter: blur(6px); }
}
/* Single-beat strobe synchronized with the punch.  School color fed in
   via .cine-stage.school-* on the parent. */
@keyframes cine-flash-strobe {
  0%   { opacity: 0; }
  20%  { opacity: 1; }
  60%  { opacity: 0.4; }
  100% { opacity: 0; }
}
@keyframes cine-rays-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* When the takeover stage is open, the combat HUD strip and queue should
   read as suppressed — the cinematic owns the screen.  pointer-events
   already drop via body.cine-playing; this just darkens the rest. */
body.cine-stage-open #stage::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: rgba(0,0,0,0.5); z-index: 200;
  animation: cine-stage-in 0.2s ease forwards;
}

/* Reduced motion — collapse the animation visuals; timing stays
   setTimeout-driven so combat still resolves correctly. */
@media (prefers-reduced-motion: reduce) {
  .cine-tint, .cine-banner, .cine-slash .cs-stroke, .cine-fade,
  .cine-stage, .cine-stage.cine-stage-punch, .ch-big, .cine-stage-text,
  .cine-stage-rays { animation-duration: 0.001ms !important; }
  .figure.cine-portrait-rise, .figure.cine-portrait-cross, .figure.cine-portrait-guard, .figure.cine-portrait-whisper {
    transform: none !important; filter: none !important;
  }
}

/* ===========================================================================
   RESONANCE RAIL — combo "fuse" chips that appear in the team-special slot
   when the queue contains matching ingredients.
   =========================================================================== */
/* Resonance rail floats just above the action row.  Bubbles are wide chat
   shapes with a small tail pointing down at the queue.  When no combos
   match, the rail is hidden (#ts-area.hidden). */
#ts-area {
  position: absolute;
  left: 8px; right: 56px;
  bottom: calc(100% + 4px);
  pointer-events: none; /* the bubbles themselves re-enable pointer events */
}
#ts-area.resonance-rail {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap-reverse;
  justify-content: center;
  align-items: flex-end;
  gap: 6px;
  padding: 0 4px 6px;
}
#ts-area.hidden { display: none; }
.resonance-chip {
  display: inline-flex;
  flex-direction: column;
  gap: 2px;
  min-width: 220px;
  max-width: 420px;
  padding: 8px 14px 9px;
  background: linear-gradient(180deg,
                              rgba(58,38,18,0.92) 0%,
                              rgba(20,12,8,0.96) 100%);
  border: none;
  border-radius: 12px 12px 4px 12px;  /* tail-ish bottom-right corner */
  color: var(--ink);
  font-family: inherit;
  cursor: pointer;
  pointer-events: auto;
  position: relative;
  animation: resonance-pulse 2.4s ease-in-out infinite;
  text-align: left;
  box-shadow:
    inset 0 0 20px rgba(240,212,136,0.2),
    0 6px 18px rgba(0,0,0,0.6),
    0 0 18px rgba(240,212,136,0.22);
}
.resonance-chip::after {
  /* Chat-bubble tail pointing down at the action row */
  content: '';
  position: absolute;
  right: 28px;
  bottom: -8px;
  width: 0; height: 0;
  border-left: 8px solid transparent;
  border-right: 8px solid transparent;
  border-top: 8px solid rgba(20,12,8,0.96);
  filter: drop-shadow(0 1px 0 rgba(240,212,136,0.18));
}
.resonance-chip:hover {
  filter: brightness(1.16);
  box-shadow:
    inset 0 0 26px rgba(240,212,136,0.36),
    0 6px 18px rgba(0,0,0,0.6),
    0 0 28px rgba(240,212,136,0.42);
}
@keyframes resonance-pulse {
  0%, 100% { box-shadow: inset 0 0 20px rgba(240,212,136,0.2),  0 6px 18px rgba(0,0,0,0.6), 0 0 18px rgba(240,212,136,0.22); }
  50%      { box-shadow: inset 0 0 30px rgba(240,212,136,0.42), 0 6px 18px rgba(0,0,0,0.6), 0 0 30px rgba(240,212,136,0.44); }
}
.resonance-chip .rc-label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  font-size: 12.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold-pale);
  text-shadow: 0 0 10px rgba(240,212,136,0.45);
  font-weight: 600;
}
.resonance-chip .rc-tier {
  font-size: 8.5px;
  letter-spacing: 0.28em;
  color: var(--gold);
  padding: 1px 6px;
  background: rgba(0,0,0,0.55);
  font-weight: 400;
}
.resonance-chip .rc-desc {
  font-size: 10.5px;
  font-style: italic;
  letter-spacing: 0.04em;
  color: var(--ink-dim);
  line-height: 1.4;
}
.resonance-chip.resonance-triple {
  background: linear-gradient(180deg,
                              rgba(120,40,30,0.9) 0%,
                              rgba(28,10,8,0.96) 100%);
}
.resonance-chip.resonance-triple::after { border-top-color: rgba(28,10,8,0.96); }
.resonance-chip.resonance-triple .rc-label {
  color: var(--gold-bright);
  text-shadow: 0 0 14px rgba(240,212,136,0.7);
}
.resonance-chip.resonance-triple .rc-tier {
  color: var(--gold-bright);
  background: rgba(80,30,20,0.65);
}

/* Sig-tier combos — built from queued specials, not basics.  Cooler hue +
   tighter glow so they read as a separate, rarer class of resonance. */
.resonance-chip.resonance-sig {
  background: linear-gradient(180deg,
                              rgba(40,60,120,0.92) 0%,
                              rgba(10,16,32,0.96) 100%);
  box-shadow:
    0 0 16px rgba(140,180,255,0.35),
    inset 0 1px 0 rgba(200,220,255,0.18);
}
.resonance-chip.resonance-sig::after { border-top-color: rgba(10,16,32,0.96); }
.resonance-chip.resonance-sig .rc-label {
  color: #cfe1ff;
  text-shadow: 0 0 14px rgba(170,200,255,0.7);
  letter-spacing: 0.14em;
}
.resonance-chip.resonance-sig .rc-tier {
  color: #cfe1ff;
  background: rgba(30,46,90,0.7);
  letter-spacing: 0.22em;
}
.resonance-chip.resonance-sig:hover {
  box-shadow:
    0 0 22px rgba(170,200,255,0.55),
    inset 0 1px 0 rgba(220,232,255,0.28);
}

/* Near-miss chips — combos the queue is one action away from.  Dimmer,
   dashed, non-interactive.  Keep their tier palette so sig partials stay
   visually distinct from attack-tier partials. */
.resonance-chip.resonance-partial {
  opacity: 0.6;
  border: 1px dashed rgba(212,200,168,0.5);
  background: linear-gradient(180deg, rgba(36,28,20,0.6) 0%, rgba(14,10,8,0.7) 100%);
  box-shadow: none;
  cursor: default;
  pointer-events: none;
}
.resonance-chip.resonance-partial::after { display: none; }
.resonance-chip.resonance-partial .rc-label { text-shadow: none; }
.resonance-chip.resonance-partial.resonance-sig {
  border-color: rgba(170,200,255,0.45);
  background: linear-gradient(180deg, rgba(28,36,68,0.6) 0%, rgba(10,14,28,0.7) 100%);
}
.resonance-chip.resonance-partial.resonance-triple {
  border-color: rgba(220,140,100,0.45);
  background: linear-gradient(180deg, rgba(60,30,22,0.6) 0%, rgba(20,10,8,0.7) 100%);
}

/* Active sigils panel — floats below the sigil tray when the player taps
   a chip.  Lists every bound sigil with icon + name + description so
   they can answer "what do I have right now?" with a single tap. */
/* Status tooltip — floating panel that appears on press-and-hold of a
   status chip.  Position is computed at show-time. */
.status-tooltip {
  position: fixed;
  z-index: 600;
  max-width: 220px;
  padding: 6px 9px;
  background: rgba(14,12,10,0.96);
  color: var(--ink);
  border: 1px solid rgba(212,200,168,0.35);
  box-shadow: 0 8px 24px rgba(0,0,0,0.6), 0 0 14px rgba(232,220,196,0.08);
  font-size: 10.5px;
  line-height: 1.45;
  pointer-events: none;
  animation: status-tip-in 0.14s ease-out forwards;
}
.status-tooltip .st-name {
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold-pale);
  margin-bottom: 3px;
}
.status-tooltip .st-name .st-val {
  color: var(--gold-bright);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: none;
}
.status-tooltip .st-text {
  color: var(--ink);
  font-size: 10px;
  line-height: 1.45;
}
@keyframes status-tip-in {
  from { opacity: 0; transform: translateY(2px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Tile combo-ready glow — pressing this tile would complete a fresh
   Resonance with the current queue.  Soft pulse + a small ribbon labels
   which combo lands.  Sig-tier combos use the cool-blue palette. */
.tile.tile-combo-ready {
  animation: tile-combo-pulse 1.6s ease-in-out infinite;
  position: relative;
}
.tile.tile-combo-ready::before {
  content: attr(data-combo);
  position: absolute;
  top: -10px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 7.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  padding: 1px 6px;
  background: rgba(40,28,20,0.95);
  color: var(--gold-bright);
  border: 1px solid rgba(240,212,136,0.45);
  white-space: nowrap;
  pointer-events: none;
  box-shadow: 0 0 10px rgba(240,212,136,0.35);
}
.tile.tile-combo-ready.tile-combo-sig::before {
  color: #cfe1ff;
  border-color: rgba(170,200,255,0.55);
  box-shadow: 0 0 10px rgba(170,200,255,0.4);
}
@keyframes tile-combo-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(240,212,136,0.0); }
  50%      { box-shadow: 0 0 16px 2px rgba(240,212,136,0.4); }
}
.tile.tile-combo-ready.tile-combo-sig {
  animation-name: tile-combo-pulse-sig;
}
@keyframes tile-combo-pulse-sig {
  0%, 100% { box-shadow: 0 0 0 0 rgba(170,200,255,0.0); }
  50%      { box-shadow: 0 0 16px 2px rgba(170,200,255,0.45); }
}

/* Keep the team-special tile styling as legacy fallback for any leftover
   references, but the live UI uses .resonance-chip now. */
.tile.team-special {
  background: linear-gradient(180deg,
                              rgba(58,38,18,0.5) 0%,
                              rgba(20,12,8,0.7) 100%) !important;
  border: none !important;
  border-bottom: 1px solid rgba(240,212,136,0.18) !important;
  position: relative;
}
.tile.team-special.ready {
  border-bottom-color: var(--gold-bright) !important;
  box-shadow:
    inset 0 0 22px rgba(240,212,136,0.18),
    0 0 18px rgba(240,212,136,0.18) !important;
  animation: ts-tile-pulse 2.4s ease-in-out infinite;
}
@keyframes ts-tile-pulse {
  0%, 100% { box-shadow: inset 0 0 22px rgba(240,212,136,0.18), 0 0 18px rgba(240,212,136,0.18); }
  50%      { box-shadow: inset 0 0 28px rgba(240,212,136,0.32), 0 0 26px rgba(240,212,136,0.32); }
}
.tile.team-special .ts-name {
  font-size: 11.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-pale);
}
.tile.team-special .ts-desc { color: var(--ink-dim); font-size: 9.5px; letter-spacing: 0.06em; }
.tile.team-special .ts-cost { gap: 4px; }

/* Commit zone polish — give resolve pips space.  The fight button's own
   styling (top of file) carries the bold/labeled/pulsing treatment; no
   need to override it here. */
#commit-zone {
  gap: 6px;
}

/* ===========================================================================
   BIOME ATMOSPHERE — per-modifier ambient layer behind combat / map.
   Each variant paints a coloured gradient + (sometimes) a particle field.
   Layer is positioned behind everything in #stage (z-index: 0) and never
   intercepts pointer events.
   =========================================================================== */
.biome-layer {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
  opacity: 0;
  transition: opacity 0.6s ease-out;
}
.biome-layer.biome-burning,
.biome-layer.biome-bonetide,
.biome-layer.biome-withered,
.biome-layer.biome-veiled,
.biome-layer.biome-fortified,
.biome-layer.biome-hunger { opacity: 1; }

/* Burning Sky — warm ember rain, orange wash */
.biome-layer.biome-burning {
  background:
    radial-gradient(ellipse 80% 60% at 50% 100%,
                    rgba(255,120,40,0.10) 0%,
                    rgba(255,80,20,0)     70%),
    radial-gradient(ellipse 60% 30% at 50% 0%,
                    rgba(255,180,60,0.06) 0%,
                    transparent 70%);
}
.biome-layer.biome-burning::before {
  /* Animated ember motes */
  content: '';
  position: absolute; inset: 0;
  background-image:
    radial-gradient(circle 1.5px at 12% 80%, rgba(255,180,90,0.7), transparent 60%),
    radial-gradient(circle 1.5px at 28% 90%, rgba(255,150,80,0.6), transparent 60%),
    radial-gradient(circle 2px   at 48% 70%, rgba(255,170,80,0.6), transparent 60%),
    radial-gradient(circle 1.5px at 66% 85%, rgba(255,180,90,0.65), transparent 60%),
    radial-gradient(circle 1.5px at 80% 75%, rgba(255,150,80,0.6), transparent 60%),
    radial-gradient(circle 2px   at 92% 88%, rgba(255,170,80,0.55), transparent 60%);
  animation: biome-ember-rise 14s linear infinite;
}
@keyframes biome-ember-rise {
  from { transform: translateY(0); opacity: 0.85; }
  to   { transform: translateY(-100%); opacity: 0; }
}

/* Bone Tide — low red mist */
.biome-layer.biome-bonetide {
  background:
    radial-gradient(ellipse 100% 50% at 50% 100%,
                    rgba(180,40,40,0.18) 0%,
                    rgba(60,8,8,0.02) 70%);
}
.biome-layer.biome-bonetide::before {
  content: '';
  position: absolute; left: 0; right: 0; bottom: 0; height: 35%;
  background: linear-gradient(0deg,
              rgba(120,20,20,0.22) 0%,
              rgba(120,20,20,0.05) 60%,
              transparent 100%);
  animation: biome-mist-drift 18s ease-in-out infinite;
}
@keyframes biome-mist-drift {
  0%, 100% { transform: translateX(-2%); opacity: 1; }
  50%      { transform: translateX( 2%); opacity: 0.7; }
}

/* Withered Land — dry beige dust */
.biome-layer.biome-withered {
  background:
    radial-gradient(ellipse 100% 70% at 50% 100%,
                    rgba(180,160,120,0.10) 0%,
                    transparent 70%);
}
.biome-layer.biome-withered::before {
  content: '';
  position: absolute; inset: 0;
  background-image:
    radial-gradient(circle 1.5px at 18% 78%, rgba(220,200,160,0.55), transparent 60%),
    radial-gradient(circle 1px   at 42% 88%, rgba(220,200,160,0.45), transparent 60%),
    radial-gradient(circle 1.5px at 70% 80%, rgba(220,200,160,0.55), transparent 60%),
    radial-gradient(circle 1px   at 88% 90%, rgba(220,200,160,0.45), transparent 60%);
  animation: biome-dust-drift 22s linear infinite;
}
@keyframes biome-dust-drift {
  from { transform: translate(0, 0); }
  to   { transform: translate(40px, -40px); }
}

/* Veiled Hour — cool blue ghost-mist */
.biome-layer.biome-veiled {
  background:
    radial-gradient(ellipse 80% 50% at 50% 50%,
                    rgba(120,160,200,0.10) 0%,
                    rgba(40,60,100,0.04)  70%);
}
.biome-layer.biome-veiled::before {
  content: '';
  position: absolute; inset: 0;
  background: radial-gradient(ellipse 60% 30% at 50% 60%,
              rgba(180,200,230,0.08) 0%,
              transparent 75%);
  animation: biome-mist-drift 20s ease-in-out infinite;
}

/* Fortified Bones — sparse stone-grey particles, no motion */
.biome-layer.biome-fortified {
  background:
    radial-gradient(ellipse 80% 100% at 50% 100%,
                    rgba(120,124,132,0.08) 0%,
                    transparent 70%);
}
.biome-layer.biome-fortified::before {
  content: '';
  position: absolute; inset: 0;
  background-image:
    radial-gradient(circle 1px at 22% 60%, rgba(180,182,186,0.4), transparent 60%),
    radial-gradient(circle 1px at 58% 70%, rgba(180,182,186,0.35), transparent 60%),
    radial-gradient(circle 1px at 84% 50%, rgba(180,182,186,0.35), transparent 60%);
}

/* Hollow Hunger — dark vignette + slow pulse */
.biome-layer.biome-hunger {
  background:
    radial-gradient(ellipse 100% 100% at 50% 50%,
                    rgba(40,18,28,0.0)  0%,
                    rgba(20,8,14,0.45) 100%);
  animation: biome-hunger-pulse 5s ease-in-out infinite;
}
@keyframes biome-hunger-pulse {
  0%, 100% { opacity: 1;   }
  50%      { opacity: 0.7; }
}

/* ===========================================================================
   GROUND ANCHORING — bigger shadow + faint horizon line under each side.
   =========================================================================== */
.figure-shadow {
  width: 76%;
  max-width: 180px;
  height: 8px;
  margin: -2px auto 2px;
  background:
    radial-gradient(ellipse, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.5) 40%, transparent 75%);
  filter: blur(2px);
}
#party-half::before, #enemy-half::before {
  content: '';
  position: absolute;
  left: 4%; right: 4%; bottom: 12px;
  height: 1px;
  background: linear-gradient(90deg,
              transparent 0%,
              rgba(232,220,196,0.18) 50%,
              transparent 100%);
  pointer-events: none;
  z-index: 1;
}

/* ===========================================================================
   PATH MAP — compact icon-nodes with pulse + press-hold tooltip.
   Replaces the old text-and-tag rectangles.
   =========================================================================== */
.path-node {
  width: 58px;
  height: 58px;
  padding: 0 !important;
  border: none !important;
  background: transparent !important;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  cursor: default;
  border-radius: 50%;
  transition: transform 0.18s, filter 0.18s;
  font-size: 0; /* prevent gap from inline-block hairs */
  box-shadow: none !important;
}
.path-node .pn-icon {
  position: relative;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  font-size: 17px;
  font-weight: 600;
  color: var(--ink-dim);
  background: radial-gradient(circle at 50% 40%,
              rgba(36,28,22,0.95) 0%,
              rgba(10,8,8,0.95) 75%);
  box-shadow:
    0 0 0 1px rgba(212,200,168,0.10),
    inset 0 1px 0 rgba(232,220,196,0.06),
    0 4px 10px rgba(0,0,0,0.5);
  transition: color 0.18s, transform 0.18s, box-shadow 0.18s;
}
.path-node .pn-pulse {
  position: absolute;
  inset: 10px;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
  z-index: 2;
}
.path-node .pn-check {
  position: absolute;
  top: -2px; right: -2px;
  width: 16px; height: 16px;
  display: flex; align-items: center; justify-content: center;
  font-size: 11px;
  font-weight: 700;
  color: var(--gold-pale);
  background: rgba(8,6,4,0.9);
  border-radius: 50%;
  z-index: 4;
}

/* States */
button.path-node:not(:disabled) { cursor: pointer; }
button.path-node.node-reachable .pn-icon {
  color: var(--gold-pale);
  box-shadow:
    0 0 0 1px rgba(232,220,196,0.32),
    inset 0 1px 0 rgba(232,220,196,0.18),
    0 0 18px rgba(232,220,196,0.25);
}
button.path-node.node-reachable .pn-pulse {
  opacity: 1;
  box-shadow: 0 0 0 1px rgba(232,220,196,0.35);
  animation: pn-pulse 2.2s cubic-bezier(0,0,.2,1) infinite;
}
@keyframes pn-pulse {
  0%   { transform: scale(0.9); opacity: 0.65; box-shadow: 0 0 0 1px rgba(232,220,196,0.6); }
  70%  { transform: scale(1.6); opacity: 0;    box-shadow: 0 0 0 1px rgba(232,220,196,0); }
  100% { transform: scale(1.6); opacity: 0;    box-shadow: 0 0 0 1px rgba(232,220,196,0); }
}
button.path-node:not(:disabled):hover .pn-icon {
  transform: scale(1.08);
  color: var(--gold-bright);
  box-shadow:
    0 0 0 1px rgba(240,224,182,0.55),
    0 0 24px rgba(232,220,196,0.4);
}
.path-node.node-current .pn-icon,
.path-node.node-completed .pn-icon {
  color: var(--ink-faint);
  box-shadow:
    0 0 0 1px rgba(120,108,84,0.18),
    inset 0 1px 0 rgba(232,220,196,0.04);
}
.path-node.node-locked .pn-icon,
.path-node.node-skipped .pn-icon {
  color: var(--ink-faint);
  opacity: 0.55;
}

/* Type-specific tints — ALWAYS on (not just when reachable) so the player
   reads elite/event/regular at a glance, even when the node is locked or
   completed.  Each type colors the glyph and the ring around it. */
.path-node.node-elite    .pn-icon { color: #ffce5a; box-shadow: 0 0 0 1.5px rgba(255,206,90,0.55), inset 0 1px 0 rgba(255,232,160,0.18), 0 4px 10px rgba(0,0,0,0.5); }
.path-node.node-boss     .pn-icon { color: var(--blood-bright); box-shadow: 0 0 0 2px rgba(212,69,69,0.65), inset 0 1px 0 rgba(255,180,180,0.18), 0 4px 14px rgba(0,0,0,0.6); width: 44px; height: 44px; font-size: 19px; text-shadow: 0 0 14px rgba(200,80,80,0.6); }
.path-node.node-rest     .pn-icon { color: var(--heal); box-shadow: 0 0 0 1.5px rgba(152,216,120,0.55), inset 0 1px 0 rgba(220,255,200,0.16), 0 4px 10px rgba(0,0,0,0.5); }
.path-node.node-event    .pn-icon { color: #c9a8e8; box-shadow: 0 0 0 1.5px rgba(154,120,192,0.55), inset 0 1px 0 rgba(220,200,240,0.16), 0 4px 10px rgba(0,0,0,0.5); }
.path-node.node-wanderer .pn-icon { color: #e0a8a0; box-shadow: 0 0 0 1.5px rgba(200,144,144,0.55), inset 0 1px 0 rgba(240,210,200,0.16), 0 4px 10px rgba(0,0,0,0.5); }
.path-node.node-forge    .pn-icon { color: #e89858; box-shadow: 0 0 0 1.5px rgba(232,152,88,0.55), inset 0 1px 0 rgba(255,220,180,0.18), 0 4px 12px rgba(80,30,0,0.5); }
.path-node.node-combat   .pn-icon { color: var(--gold-pale); box-shadow: 0 0 0 1px rgba(212,200,168,0.30), inset 0 1px 0 rgba(232,220,196,0.10), 0 4px 10px rgba(0,0,0,0.5); }

/* On reachable, the type-tint glows brighter so the player's eye is drawn
   to active choices but the type is already legible everywhere. */
.path-node.node-elite.node-reachable .pn-icon  { box-shadow: 0 0 0 1.5px rgba(255,206,90,0.85), 0 0 22px rgba(255,206,90,0.35); }
.path-node.node-boss.node-reachable  .pn-icon  { box-shadow: 0 0 0 2px rgba(212,69,69,0.95), 0 0 26px rgba(200,80,80,0.5); }
/* Boss node always breathes red — the destination should feel alive
   from stretch 1, not just when it becomes reachable. */
.path-node.node-boss .pn-icon {
  animation: boss-node-breathe 3.2s ease-in-out infinite;
}
@keyframes boss-node-breathe {
  0%, 100% { box-shadow: 0 0 0 1.5px rgba(212,69,69,0.55), 0 0 14px rgba(200,80,80,0.25); }
  50%      { box-shadow: 0 0 0 2px   rgba(212,69,69,0.85), 0 0 26px rgba(200,80,80,0.55); }
}
/* Wanderer node — a slower, gentler breathe (less aggressive than the
   boss, but enough that the eye finds these special slots).  Only on
   reachable nodes so completed wanderers stay quiet. */
.path-node.node-wanderer.node-reachable .pn-icon {
  animation: wanderer-node-breathe 4.5s ease-in-out infinite;
}
@keyframes wanderer-node-breathe {
  0%, 100% { box-shadow: 0 0 0 1.5px rgba(200,144,144,0.55), 0 0 14px rgba(200,144,144,0.22); }
  50%      { box-shadow: 0 0 0 1.5px rgba(224,168,160,0.95), 0 0 28px rgba(224,168,160,0.50); }
}
.path-node.node-rest.node-reachable  .pn-icon  { box-shadow: 0 0 0 1.5px rgba(152,216,120,0.85), 0 0 22px rgba(152,216,120,0.35); }
.path-node.node-event.node-reachable .pn-icon  { box-shadow: 0 0 0 1.5px rgba(180,140,220,0.85), 0 0 22px rgba(154,120,192,0.35); }
.path-node.node-wanderer.node-reachable .pn-icon { box-shadow: 0 0 0 1.5px rgba(224,168,160,0.85), 0 0 22px rgba(200,144,144,0.35); }
.path-node.node-combat.node-reachable .pn-icon { box-shadow: 0 0 0 1px rgba(232,220,196,0.55), 0 0 20px rgba(232,220,196,0.30); }

/* Type label strap — small uppercase text under each node so the type
   is unmistakable.  Tinted to match the ring colour. */
.path-node .pn-label {
  position: absolute;
  bottom: -8px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 7.5px;
  font-weight: 800;
  letter-spacing: 0.22em;
  white-space: nowrap;
  color: var(--ink-dim);
  text-shadow: 0 1px 0 rgba(0,0,0,0.85);
  z-index: 5;
  pointer-events: none;
}
.path-node.node-elite  .pn-label { color: #ffce5a; }
.path-node.node-boss   .pn-label { color: var(--blood-bright); text-shadow: 0 0 6px rgba(212,69,69,0.5), 0 1px 0 rgba(0,0,0,0.9); }
.path-node.node-rest   .pn-label { color: var(--heal); }
.path-node.node-event    .pn-label { color: #c9a8e8; }
.path-node.node-wanderer .pn-label { color: #e0a8a0; }
.path-node.node-forge    .pn-label { color: #e89858; }
.path-node.node-forge.node-reachable .pn-icon { box-shadow: 0 0 0 1.5px rgba(232,152,88,0.95), 0 0 24px rgba(232,152,88,0.55); animation: forge-node-breathe 3.0s ease-in-out infinite; }
@keyframes forge-node-breathe {
  0%, 100% { box-shadow: 0 0 0 1.5px rgba(232,152,88,0.55), 0 0 14px rgba(232,152,88,0.25); }
  50%      { box-shadow: 0 0 0 2px rgba(255,200,140,0.95), 0 0 28px rgba(232,152,88,0.6); }
}
.path-node.node-combat   .pn-label { color: var(--gold-pale); opacity: 0.7; }
.path-node.node-locked .pn-label,
.path-node.node-skipped .pn-label { opacity: 0.45; }
.path-node.node-completed .pn-label { opacity: 0.5; }

/* Hide the old text-bearing decorations the prior renderer added; the
   new markup doesn't include .path-name / .path-tag / .path-enemies but
   leaving stale rules disabled defensively. */
.path-node .path-name,
.path-node .path-tag,
.path-node .path-type-glyph,
.path-node .path-enemies { display: none !important; }

/* ===========================================================================
   NODE TOOLTIP — surfaces on press-and-hold of a map node.
   =========================================================================== */
#node-tooltip {
  position: fixed;
  z-index: 300;
  max-width: 260px;
  padding: 10px 12px 8px;
  background: radial-gradient(ellipse 120% 100% at 50% 0%,
                              rgba(36,28,22,0.97) 0%,
                              rgba(12,10,10,0.98) 70%);
  color: var(--ink);
  font-size: 11px;
  letter-spacing: 0.04em;
  line-height: 1.5;
  pointer-events: none;
  animation: nt-in 0.18s ease-out;
  box-shadow:
    0 12px 36px rgba(0,0,0,0.75),
    inset 0 1px 0 rgba(232,220,196,0.06),
    0 0 24px rgba(200,164,100,0.12);
}
@keyframes nt-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}
#node-tooltip .nt-name {
  font-size: 12px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-pale);
  margin-bottom: 2px;
}
#node-tooltip .nt-type {
  font-size: 9.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 4px;
}
#node-tooltip .nt-enemies {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 4px 0 6px;
}
#node-tooltip .nt-enemy {
  font-size: 10px;
  letter-spacing: 0.06em;
  padding: 2px 6px;
  background: rgba(120,40,40,0.35);
  color: var(--ink);
}
#node-tooltip .nt-reward {
  font-size: 10px;
  font-style: italic;
  letter-spacing: 0.02em;
  color: var(--ink);
  opacity: 0.85;
}

/* Mobile node compaction for path-map */
@media (max-height: 480px) {
  #overlay-choices.path-map { gap: 8px; }
  .path-col { gap: 8px; }
  .path-col-slots { gap: 10px; }
  .path-node { width: 48px; height: 48px; }
  .path-node .pn-icon { width: 32px; height: 32px; font-size: 15px; }
}

/* ===========================================================================
   FULL-BLEED OVERLAYS — class-based (works on older Safari without :has).
   #overlay.overlay-full strips the card chrome so content fills the
   viewport.  Specific scenes layer on .overlay-path / .overlay-vignette /
   .overlay-runsummary for size + spacing tweaks.
   =========================================================================== */
#overlay.overlay-full {
  background: radial-gradient(ellipse at center, #0a0606 0%, #000 70%) !important;
}
#overlay.overlay-full #overlay-content {
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
  max-width: min(960px, 96vw) !important;
  width: 100%;
  padding: 18px 22px 18px !important;
}
#overlay.overlay-vignette #overlay-content {
  max-width: min(1100px, 100vw) !important;
  padding: 8px 18px 14px !important;
}
#overlay.overlay-vignette #overlay-title {
  border-bottom: none !important;
  font-size: 16px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 4px;
}
#overlay.overlay-vignette #overlay-title::after { display: none !important; }
#overlay.overlay-full::before, #overlay.overlay-full::after { display: none !important; }

/* Recruit / Upgrade / Sigil — bigger overlay frame for cinematic choices */
/* Shared cinematic overlay treatment.  Recruit / upgrade / sigil all add
   .overlay-cinematic alongside their specific class so this block runs
   once instead of three times. */
#overlay.overlay-cinematic #overlay-content {
  max-width: min(820px, 96vw) !important;
  padding: 16px 22px 14px !important;
}
#overlay.overlay-cinematic #overlay-title {
  border-bottom: none !important;
  font-size: 16px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 8px;
}
#overlay.overlay-cinematic #overlay-title::after { display: none !important; }
/* Pass / Back button — slim text-link */
#overlay.overlay-cinematic #overlay-btn {
  background: transparent !important;
  border: none !important;
  color: var(--ink) !important;
  font-size: 11px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  padding: 6px 16px !important;
  box-shadow: none !important;
}
#overlay.overlay-cinematic #overlay-btn:hover {
  color: var(--gold-pale) !important;
  text-shadow: 0 0 10px rgba(240,224,182,0.35);
}

/* ===========================================================================
   DEFEAT MOMENT — party falls.  Desaturates the stage and washes a quiet
   blood-tinted glow across the screen.  Cleared when the player returns
   to the title from the run summary.
   =========================================================================== */
/* Dim the battlefield + HUD + ambient layers when the party falls,
   but EXCLUDE #overlay from the filter — the run summary lives there
   and shouldn't inherit the desaturation.  Previously a single rule
   on #stage applied the filter to the whole subtree, so the Defeat
   screen rendered with saturate(0.25) brightness(0.55) inherited and
   every word read as faded grey.  Scoping to siblings of #overlay
   keeps the in-fight scene dimmed (the intent) while letting the
   summary card render at full brightness. */
body.party-fallen #stage > *:not(#overlay) {
  filter: saturate(0.25) brightness(0.55);
  transition: filter 1.1s ease-out;
}
body.party-fallen::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 350;
  pointer-events: none;
  background: radial-gradient(ellipse at center,
                              rgba(180,40,40,0.32) 0%,
                              rgba(30,4,4,0.4) 35%,
                              rgba(0,0,0,0) 75%);
  animation: defeat-flash 1.6s ease-out forwards;
}
@keyframes defeat-flash {
  0%   { opacity: 0; }
  20%  { opacity: 1; }
  100% { opacity: 0.85; }
}

/* ===========================================================================
   COMBAT ANIMATION POLISH — slot slide + attack lunge
   =========================================================================== */
@keyframes figure-slide-in {
  0%   { transform: translateX(40px); opacity: 0.65; }
  60%  { transform: translateX(-4px); opacity: 1; }
  100% { transform: translateX(0); opacity: 1; }
}
.figure.figure-sliding { animation: figure-slide-in 0.36s cubic-bezier(.18,.89,.32,1); }

@keyframes figure-lunge {
  0%   { transform: translate(0, 0); }
  35%  { transform: translate(8px, -3px); }
  60%  { transform: translate(6px, -1px); }
  100% { transform: translate(0, 0); }
}
@keyframes figure-lunge-enemy {
  0%   { transform: translate(0, 0); }
  35%  { transform: translate(-8px, -3px); }
  60%  { transform: translate(-6px, -1px); }
  100% { transform: translate(0, 0); }
}
.figure.party-figure.lunging  { animation: figure-lunge       0.32s cubic-bezier(.18,.89,.32,1); }
.figure.enemy-figure.lunging  { animation: figure-lunge-enemy 0.32s cubic-bezier(.18,.89,.32,1); }

/* ===========================================================================
   VIGNETTE — VN-style two-actor stage.
   Bubble rail at the top (left-aligned for left speaker, right-aligned for
   right speaker, centered + italic for thoughts).  Portrait row at the
   bottom (1 or 2 portraits standing on the floor).  Bubbles never overlap
   the figures.
   =========================================================================== */
.vn-stage {
  display: grid;
  grid-template-rows: 1fr auto;
  width: 100%;
  /* Fixed design-pixel sizing.  Percentage heights kept resolving to
     unexpected values on Android Chrome (the parent <p>'s indefinite
     height + nested grid percent heights cascading off the wrong
     ancestor), making the portrait track balloon and the SVG render
     several hundred design pixels tall — head visible, body off-screen.
     With the canvas locked to 720×405 the stage's design budget is
     well-known: title (~26) + choices+btn (~120) leaves ~225 design px
     for the cinematic.  Bumped from 220 → 250 + shrunk portraits from
     140 → 110 so 3-line bossPrep vignettes no longer clip the first
     bubble above the rail's scroll fold. */
  height: 250px;
  max-height: 250px;
  gap: 4px;
  padding: 0;
  position: relative;
  overflow: hidden;
}
/* When .vn-stage rides on top of .vignette-stage (recruit / story
   vignettes), neutralize .vignette-stage's aspect-ratio: 16/9 — the
   grid template alone drives the layout here. */
.vignette-stage.vn-stage {
  aspect-ratio: auto;
  min-height: 0;
}

/* Dialogue rail: flex column.  Each line aligns L/R/center based on who's
   speaking; no line ever sits in the bottom area where portraits live.
   min-height: 0 is required for overflow-y: auto to actually engage when
   the rail is inside a grid 1fr cell — without it the rail grows to fit
   its content and the top bubbles get clipped above the stage. */
.vn-dialogue {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 6px 18px 8px;
  align-items: stretch;
  justify-content: flex-end;
  overflow-y: auto;
  min-height: 0;
}
.vn-line {
  /* Bumped from 56% so longer lines wrap less aggressively and don't push
     earlier bubbles off the top of the rail.  Padding + line-height
     tightened so 3-line vignettes fit the rail without scroll-clipping. */
  max-width: 64%;
  padding: 6px 12px 8px;
  font-size: 13px;
  line-height: 1.4;
  color: var(--ink);
  background: linear-gradient(180deg, rgba(36,28,22,0.92) 0%, rgba(12,10,10,0.94) 100%);
  box-shadow:
    0 8px 22px rgba(0,0,0,0.7),
    inset 0 1px 0 rgba(232,220,196,0.06);
}
.vn-line .vn-who {
  display: block;
  font-size: 9px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  margin-bottom: 4px;
  color: var(--gold-pale);
}
.vn-line .vn-text {
  display: block;
  letter-spacing: 0.02em;
}
.vn-line.bubble.left  {
  align-self: flex-start;
  border-radius: 14px 14px 14px 4px;
}
.vn-line.bubble.right {
  align-self: flex-end;
  border-radius: 14px 14px 4px 14px;
}
.vn-line.bubble.right .vn-who { color: var(--frost-bright); text-align: right; }
.vn-line.thought {
  align-self: center;
  max-width: 78%;
  padding: 4px 10px;
  background: transparent;
  box-shadow: none;
  text-align: center;
  font-style: italic;
  color: var(--ink-dim);
  text-shadow: 0 2px 4px rgba(0,0,0,0.85);
  font-size: 12.5px;
  letter-spacing: 0.06em;
}

/* Portrait row at the bottom.  One portrait centered for solo scenes,
   two side-by-side for conversations. */
.vn-portraits {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: end;
  justify-items: center;
  /* Fixed design-pixel height.  Anything percentage-based here was
     resolving against an unexpected ancestor on Android Chrome and
     blowing the portrait area up several hundred px, pushing the
     character bodies below the visible viewport.  Shrunk from 140
     → 110 so the dialogue rail above gets enough room for 3 bubbles
     without scroll-clipping the first line. */
  height: 110px;
  max-height: 110px;
  padding-bottom: 4px;
  position: relative;
}
.vn-stage.one-actor .vn-portraits {
  grid-template-columns: 1fr;
}
.vn-portrait {
  width: 100%;
  height: 110px;
  max-height: 110px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  position: relative;
  overflow: hidden;
}
.vn-portrait svg {
  /* Hard-bound on both axes so the SVG can't compute a runaway
     intrinsic size on Android (where width:auto + height:100% in a
     percent-sized grid cell was producing 400+ design-px renders). */
  height: 110px;
  max-height: 110px;
  width: auto;
  max-width: 90%;
  display: block;
  filter: drop-shadow(0 8px 24px rgba(0,0,0,0.7));
}
.vn-portrait.vn-portrait-right svg { transform: scaleX(-1); }
/* Soft floor-glow under each portrait so they don't float */
.vn-portrait::before {
  content: '';
  position: absolute;
  left: 50%; bottom: -2px;
  width: 70%; height: 18px;
  transform: translateX(-50%);
  background: radial-gradient(ellipse 60% 100% at 50% 50%,
                              rgba(0,0,0,0.7) 0%,
                              transparent 75%);
  pointer-events: none;
}

/* Compact-mobile tweaks — only adjust font / padding here.  The
   vh-based height clamps that used to live in these blocks were
   the proximate cause of the Android "head-only" portrait bug
   (see .vn-stage / .vn-portraits above); heights are now driven
   by percent of the locked design canvas in all cases. */
@media (max-height: 480px) {
  .vn-line { font-size: 12px; padding: 6px 12px 8px; }
  .vn-line .vn-who { font-size: 8.5px; }
  .vn-line.thought { font-size: 11px; }
}
@media (max-height: 380px) {
  .vn-line { font-size: 11.5px; }
}

/* ===========================================================================
   INFO PANEL (in-combat / on-map run state)
   =========================================================================== */
#info-panel {
  position: fixed;
  inset: 0;
  z-index: 220;
  display: flex;
  align-items: center;
  justify-content: center;
  animation: ip-bg-in 0.22s ease-out;
}
#info-panel.hidden { display: none !important; }
@keyframes ip-bg-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
#info-panel .ip-bg {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at center, rgba(8,4,4,0.82) 0%, rgba(0,0,0,0.95) 70%);
}
#info-panel .ip-card {
  position: relative;
  z-index: 1;
  max-width: min(620px, 96vw);
  max-height: 92vh;
  overflow-y: auto;
  padding: 18px 22px 18px;
  background: radial-gradient(ellipse 120% 100% at 50% 0%,
                              rgba(28,22,18,0.92) 0%,
                              rgba(10,8,8,0.96) 70%);
  box-shadow:
    0 16px 60px rgba(0,0,0,0.85),
    inset 0 1px 0 rgba(232,220,196,0.06),
    0 0 30px rgba(200,164,100,0.1);
  animation: ip-card-in 0.28s cubic-bezier(.18,.89,.32,1);
}
@keyframes ip-card-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
.ip-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 6px;
}
.ip-title {
  font-size: 14px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--gold-pale);
  margin: 0;
}
.ip-close {
  background: transparent;
  border: none;
  font-family: inherit;
  font-size: 20px;
  line-height: 1;
  color: var(--ink-dim);
  cursor: pointer;
  padding: 2px 6px;
}
.ip-close:hover { color: var(--gold-pale); }

/* ===========================================================================
   BOSS FIGHT — body.boss-fight collapses the 3 enemy slots into a single
   imposing figure with a dramatic full-width HP bar.
   =========================================================================== */
body.boss-fight #enemy-half {
  display: flex !important;
  align-items: flex-end;
  justify-content: center;
  position: relative;
}
body.boss-fight #enemy-half .figure {
  flex: 0 0 auto;
  width: 92%;
  max-width: 540px;
  height: 100%;
}
body.boss-fight #enemy-half .figure.empty { display: none; }
body.boss-fight #enemy-half .figure-portrait { max-width: none; }
body.boss-fight #enemy-half .figure-portrait svg {
  transform: scale(1.18);
  transform-origin: 50% 80%;
  filter: drop-shadow(0 8px 24px rgba(0,0,0,0.7))
          drop-shadow(0 0 30px rgba(220,80,80,0.18));
}
/* Boss HP — same shape and styling as the regular hero HP bar (brackets,
   thin bar, ink fill) but stretched dramatically wide across the boss's
   side of the field.  Not centered as a banner anymore. */
body.boss-fight #enemy-half .figure-hp {
  position: absolute !important;
  /* Pushed from 12px → 32px so the "20/30" text label that floats
     -18px above the bar lands at +14px instead of -6px — keeping it
     fully inside #enemy-half instead of clipping into the HUD strip
     above (which sits at z-280 and was painting over the readout). */
  top: 32px !important;
  left: 6% !important;
  right: 6% !important;
  width: auto !important;
  height: 5px !important;
  margin: 0 !important;
  transform: none !important;
  background: rgba(10,10,12,0.78) !important;
  box-shadow: 0 0 18px rgba(220,40,40,0.35);
  z-index: 6;
}
/* Match the bracket caps from the player bar but scaled up + tinted red. */
body.boss-fight #enemy-half .figure-hp::before,
body.boss-fight #enemy-half .figure-hp::after {
  display: block !important;
  content: '[';
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  font-family: ui-monospace, 'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace;
  font-size: 16px;
  line-height: 1;
  color: var(--blood-bright);
  text-shadow: 0 0 6px rgba(220,40,40,0.6);
  pointer-events: none;
}
body.boss-fight #enemy-half .figure-hp::before { content: '['; right: calc(100% + 4px); left: auto; }
body.boss-fight #enemy-half .figure-hp::after  { content: ']'; left:  calc(100% + 4px); right: auto; }
body.boss-fight #enemy-half .figure-hp .hp-fill {
  background: linear-gradient(180deg, #ff5050 0%, #c01818 55%, #5a0808 100%);
  box-shadow: 0 0 16px rgba(220,60,60,0.55);
  animation: boss-hp-pulse 2.2s ease-in-out infinite;
}
@keyframes boss-hp-pulse {
  0%, 100% { filter: brightness(1)   saturate(1); }
  50%      { filter: brightness(1.1) saturate(1.12); }
}
body.boss-fight #enemy-half .figure-hp .hp-text {
  position: absolute !important;
  font-size: 11px !important;
  letter-spacing: 0.22em !important;
  text-shadow: 0 0 8px rgba(0,0,0,0.9), 0 2px 0 rgba(0,0,0,0.8);
  top: -18px !important;
  left: 50% !important;
  transform: translateX(-50%);
  color: var(--ink) !important;
  font-weight: 500;
}
body.boss-fight #enemy-half .figure-name {
  font-size: 14px;
  letter-spacing: 0.36em;
  text-transform: uppercase;
  color: var(--blood-bright);
  text-shadow: 0 0 12px rgba(220,80,80,0.4);
}

/* ===========================================================================
   MULTI-ACTOR BOSS FIGHT — Sundering Choir (Voices), Husk Garden (Blooms),
   Crown of Echoes (Shades).  Restore the normal 3-slot grid so each figure
   shares the enemy half cleanly instead of inheriting the single-boss 92%
   width that was overflowing into the party half.  Keep the wide red HP
   bar treatment ONLY on the actual boss (front slot); minions get
   normal-scale HP bars over their figures.
   =========================================================================== */
body.boss-fight.boss-fight-multi #enemy-half {
  display: grid !important;
  grid-template-columns: 1fr 1fr 1fr !important;
  grid-template-rows: 1fr !important;
  gap: 2px;
}
body.boss-fight.boss-fight-multi #enemy-half .figure {
  flex: initial;
  width: auto !important;
  max-width: none !important;
}
body.boss-fight.boss-fight-multi #enemy-half .figure-portrait svg {
  transform: none;
  filter: drop-shadow(0 8px 22px rgba(0,0,0,0.7));
}
/* Minion HP bars (mid / back slots) — pull them OUT of the wide red
   banner treatment.  They render as normal-shape inline bars over the
   figure, like a regular fight.  Only the front-slot boss keeps the
   dramatic stretched HP bar.  Mirrors the base .figure-hp positioning
   (top: 27px, centered, 44px wide) so the bar lands in the same spot
   it would in any non-boss encounter. */
body.boss-fight.boss-fight-multi #enemy-half .figure[data-slot="mid"]  .figure-hp,
body.boss-fight.boss-fight-multi #enemy-half .figure[data-slot="back"] .figure-hp {
  position: absolute !important;
  top: 27px !important;
  left: 50% !important;
  right: auto !important;
  bottom: auto !important;
  width: 44px !important;
  height: 3px !important;
  margin: 0 !important;
  transform: translateX(-50%) !important;
  background: rgba(10,10,12,0.72) !important;
  box-shadow: none !important;
}
body.boss-fight.boss-fight-multi #enemy-half .figure[data-slot="mid"]  .figure-hp::before,
body.boss-fight.boss-fight-multi #enemy-half .figure[data-slot="mid"]  .figure-hp::after,
body.boss-fight.boss-fight-multi #enemy-half .figure[data-slot="back"] .figure-hp::before,
body.boss-fight.boss-fight-multi #enemy-half .figure[data-slot="back"] .figure-hp::after {
  display: none !important;
}
body.boss-fight.boss-fight-multi #enemy-half .figure[data-slot="mid"]  .figure-hp .hp-fill,
body.boss-fight.boss-fight-multi #enemy-half .figure[data-slot="back"] .figure-hp .hp-fill {
  background: linear-gradient(180deg, var(--hp) 0%, var(--ink-dim) 100%);
  box-shadow: none;
  animation: none;
}
body.boss-fight.boss-fight-multi #enemy-half .figure[data-slot="mid"]  .figure-hp .hp-text,
body.boss-fight.boss-fight-multi #enemy-half .figure[data-slot="back"] .figure-hp .hp-text {
  /* Restore the default in-portrait hp-text positioning — absolute,
     just below the bar, centered.  The boss-fight CSS yanks this to
     top: -18px (banner mode); we undo that for minions. */
  position: absolute !important;
  top: calc(100% + 3px) !important;
  left: 50% !important;
  right: auto !important;
  transform: translateX(-50%) !important;
  width: auto !important;
  height: auto !important;
  font-size: 9.5px !important;
  letter-spacing: 0.06em !important;
  color: var(--ink) !important;
  text-shadow: none !important;
  font-weight: normal !important;
}
body.boss-fight.boss-fight-multi #enemy-half .figure-name {
  font-size: 10.5px;
  letter-spacing: 0.18em;
  color: var(--ink);
  text-shadow: 0 0 6px rgba(0,0,0,0.8);
}

/* Boss intro overlay — full screen darken + name reveal. */
#boss-intro {
  position: fixed;
  inset: 0;
  z-index: 400;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
#boss-intro.hidden { display: none !important; }
#boss-intro .bi-bg {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 80% 60% at 50% 50%,
                    rgba(40,8,8,0.85) 0%,
                    rgba(0,0,0,0.98) 80%);
  animation: bi-bg-in 0.35s ease-out forwards;
}
#boss-intro.out .bi-bg { animation: bi-bg-out 0.5s ease-out forwards; }
@keyframes bi-bg-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes bi-bg-out {
  from { opacity: 1; }
  to   { opacity: 0; }
}
#boss-intro .bi-content {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 6px;
  animation: bi-content-in 0.55s cubic-bezier(.18,.89,.32,1) forwards;
}
#boss-intro.out .bi-content { animation: bi-content-out 0.4s ease-out forwards; }
@keyframes bi-content-in {
  0%   { opacity: 0; transform: translateY(8px); }
  100% { opacity: 1; transform: translateY(0); }
}
@keyframes bi-content-out {
  0%   { opacity: 1; transform: translateY(0); filter: blur(0); }
  100% { opacity: 0; transform: translateY(-4px); filter: blur(2px); }
}
.bi-title-row {
  display: flex;
  align-items: center;
  gap: 12px;
}
.bi-rule {
  width: 80px;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--blood-bright) 50%, transparent);
}
.bi-eyebrow {
  font-size: 11px;
  letter-spacing: 0.5em;
  color: var(--blood-bright);
  text-shadow: 0 0 12px rgba(220,80,80,0.5);
}
.bi-name {
  font-size: 38px;
  letter-spacing: 0.44em;
  color: var(--ink);
  text-shadow:
    0 0 24px rgba(220,80,80,0.6),
    0 0 4px rgba(255,255,255,0.18),
    0 3px 0 rgba(0,0,0,0.8);
  font-weight: 400;
}
.bi-tag {
  font-size: 12px;
  font-style: italic;
  letter-spacing: 0.06em;
  color: var(--ink-dim);
}

/* Boss slow-motion death — drops the page into a tinted, slow-motion
   freeze; CSS animations slow + colours desaturate, then snap back. */
#stage.boss-slowmo .figure-portrait svg { animation-duration: 12s !important; }
body.boss-killed #stage {
  filter: saturate(0.4) brightness(0.65);
  transition: filter 0.6s ease-out;
}
body.boss-killed::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 350;
  pointer-events: none;
  background: radial-gradient(ellipse at center,
                              rgba(255,240,200,0.6) 0%,
                              rgba(255,180,140,0.2) 30%,
                              rgba(0,0,0,0) 70%);
  animation: boss-flash 1.4s ease-out forwards;
}
@keyframes boss-flash {
  0%   { opacity: 0; }
  10%  { opacity: 1; }
  50%  { opacity: 0.6; }
  100% { opacity: 0; }
}

/* Power-spike — stagger consume hit (2× damage on a staggered enemy).
   Triggered by playStaggerHit; the body class lifts off after ~380ms.
   Brief gold flash across the stage so the payoff of the weakness
   loop reads as a MOMENT instead of a numbered popup.  Doesn't slow
   animations — just a visual pulse so it doesn't stall pace. */
#stage.stagger-hit {
  filter: brightness(1.18) saturate(1.15);
  transition: filter 0.18s ease-out;
}
#stage.stagger-hit::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 100;
  pointer-events: none;
  background: radial-gradient(ellipse at center,
                              rgba(240,212,136,0.42) 0%,
                              rgba(180,140,80,0.18) 40%,
                              rgba(0,0,0,0) 70%);
  animation: stagger-hit-flash 0.36s ease-out forwards;
}
@keyframes stagger-hit-flash {
  0%   { opacity: 0; }
  20%  { opacity: 1; }
  100% { opacity: 0; }
}

/* Power-spike — first Resonance of the run.  Subtle gold halo on the
   stage so the moment of discovery feels weighty on top of the combo
   cinematic.  Held ~700ms; cinematic runtime handles the rest. */
#stage.first-resonance::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 99;
  pointer-events: none;
  background: radial-gradient(ellipse at center,
                              rgba(240,212,136,0.30) 0%,
                              rgba(0,0,0,0) 60%);
  animation: first-resonance-pulse 0.7s ease-out forwards;
}
@keyframes first-resonance-pulse {
  0%   { opacity: 0; }
  30%  { opacity: 1; }
  100% { opacity: 0; }
}

/* Regular fight killing-blow hold — lighter slow-mo than the boss kill.
   Gives the final hit a moment to breathe before the victory cascade.
   Body class is removed after ~1.2s by playKillingBlowHold. */
#stage.kill-slowmo .figure-portrait svg { animation-duration: 6s !important; }
body.killing-blow #stage {
  filter: saturate(0.7) brightness(0.78);
  transition: filter 0.45s ease-out;
}
body.killing-blow::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 340;
  pointer-events: none;
  background: radial-gradient(ellipse at center,
                              rgba(232,200,160,0.32) 0%,
                              rgba(180,140,120,0.12) 40%,
                              rgba(0,0,0,0) 70%);
  animation: kill-flash 1.1s ease-out forwards;
}
@keyframes kill-flash {
  0%   { opacity: 0; }
  18%  { opacity: 0.95; }
  60%  { opacity: 0.4; }
  100% { opacity: 0; }
}

/* ===========================================================================
   QUIRK DOTS — small affinity glyphs under each party hero's name.
   =========================================================================== */
.fig-quirks {
  display: flex;
  justify-content: center;
  gap: 4px;
  margin-top: 2px;
}
.fig-quirk {
  font-size: 10px;
  line-height: 1;
  cursor: help;
  user-select: none;
}
.fig-quirk-positive { color: var(--gold-pale); text-shadow: 0 0 8px rgba(240,224,182,0.55); }
.fig-quirk-negative { color: var(--blood-bright); text-shadow: 0 0 8px rgba(220,80,80,0.45); }

/* Mastery crown — small gold mark next to a hero's name once they've
   awakened their Mastery (positive affinities hit cap → HERO_MASTERIES
   grants the upgrade).  Persistent reminder of "this hero is operating
   at their final form" so the achievement reads beyond just the unlock
   cinematic.  Pulses subtly so it draws the eye without being loud. */
.fig-mastery {
  display: inline-block;
  margin-right: 4px;
  font-size: 10px;
  color: var(--gold-bright);
  text-shadow: 0 0 8px rgba(240,212,136,0.7), 0 0 14px rgba(240,212,136,0.45);
  cursor: help;
  animation: fig-mastery-pulse 2.4s ease-in-out infinite;
}
@keyframes fig-mastery-pulse {
  0%, 100% { text-shadow: 0 0 6px rgba(240,212,136,0.55), 0 0 10px rgba(240,212,136,0.3); }
  50%      { text-shadow: 0 0 10px rgba(240,212,136,0.85), 0 0 18px rgba(240,212,136,0.55); }
}

/* Mastery row in the in-run info panel — small gold chip under
   each hero's HP showing which mastery they've awakened.  Tap or
   hold for the full effect text via the chip-tooltip layer. */
.info-hero-mastery {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 4px;
  padding: 2px 8px 2px 6px;
  background: rgba(40,28,14,0.6);
  border: 1px solid rgba(240,212,136,0.45);
  font-size: 9.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold-pale);
  cursor: help;
  white-space: nowrap;
}
.info-hero-mastery-mark {
  color: var(--gold-bright);
  text-shadow: 0 0 6px rgba(240,212,136,0.5);
  font-size: 11px;
}

/* Mastery row inside the Hero codex — surfaces the awakening goal so
   the player sees what each hero's passive becomes at full affinity.
   Sits between passive and home abilities. */
.hc-mastery {
  display: grid;
  grid-template-columns: 22px 1fr;
  gap: 8px;
  align-items: center;
  padding: 6px 10px;
  margin: 6px 0;
  background: linear-gradient(180deg, rgba(40,28,14,0.55) 0%, rgba(12,8,8,0.7) 100%);
  border-left: 2px solid var(--gold-bright);
}
.hc-mastery-mark {
  font-size: 14px;
  color: var(--gold-bright);
  text-shadow: 0 0 8px rgba(240,212,136,0.6);
  text-align: center;
}
.hc-mastery-body { display: flex; flex-direction: column; gap: 1px; }
.hc-mastery-label {
  font-size: 8.5px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.hc-mastery-name {
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-pale);
}
.hc-mastery-desc {
  font-size: 9.5px;
  font-style: italic;
  color: var(--ink);
  letter-spacing: 0.02em;
  line-height: 1.4;
}

/* ===========================================================================
   IN-COMBAT INFO BUTTON + PANEL
   =========================================================================== */
/* Menu button — now lives inside the #hud strip as a flex item so it
   always aligns with the run-modifier chip and the sigil tray, on
   combat and on the map.  Sized to match the sigil chips (44×44 mobile
   tap target).  The HUD strip itself is already z-lifted above the
   overlay (z-index 280), so the button stays reachable on the map. */
#info-btn {
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  border-radius: 4px;
  border: 1px solid rgba(201,189,159,0.42);
  background:
    radial-gradient(ellipse at 50% 30%, rgba(34,26,18,0.92) 0%, rgba(12,10,8,0.92) 75%);
  color: var(--gold);
  font-family: inherit;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  padding: 0;
  box-shadow: 0 2px 6px rgba(0,0,0,0.55), 0 0 0 1px rgba(0,0,0,0.4) inset;
  transition: background 0.15s, border-color 0.15s, color 0.15s, transform 0.12s;
}
#info-btn:hover {
  border-color: var(--gold-bright);
  color: var(--gold-bright);
  background:
    radial-gradient(ellipse at 50% 30%, rgba(52,40,26,0.96) 0%, rgba(20,16,12,0.96) 75%);
}
#info-btn:active { transform: scale(0.96); }

/* ===========================================================================
   GAME MENU — opened from the ☰ button.  Matches the game's overlay
   aesthetic: dark radial card, gold hairline border, serif title,
   uppercase tracking labels.  Centered modal (not corner-pinned) so
   it never clips against the viewport edge on notched devices.
   =========================================================================== */
.game-menu {
  position: fixed; inset: 0;
  z-index: 900;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  pointer-events: auto;
}
.gm-backdrop {
  position: absolute; inset: 0;
  background:
    radial-gradient(ellipse at center, rgba(15,8,4,0.78) 0%, rgba(5,3,2,0.92) 70%);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}
.gm-card {
  position: relative;
  width: 100%;
  max-width: 320px;
  background:
    radial-gradient(ellipse at 50% 0%, #2c1f14 0%, #18110a 50%, #0c0805 100%);
  border: 1px solid var(--gold);
  border-radius: 6px;
  box-shadow: 0 24px 60px rgba(0,0,0,0.7), 0 0 24px rgba(201,189,159,0.12), 0 0 0 1px rgba(0,0,0,0.5) inset;
  padding: 22px 22px 18px;
  animation: gm-in 0.22s ease-out;
}
@keyframes gm-in {
  from { opacity: 0; transform: translateY(-4px) scale(0.985); }
  to   { opacity: 1; transform: translateY(0)    scale(1); }
}
.gm-title {
  font-family: 'Iowan Old Style', Georgia, serif;
  font-size: 14px;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--gold-bright);
  text-align: center;
  margin: 0 0 16px;
  font-weight: 400;
  text-shadow: 0 0 12px rgba(212,180,120,0.3);
  padding-right: 0.42em;  /* visually center the tracked text */
}
.gm-title::after {
  content: '';
  display: block;
  margin: 8px auto 0;
  width: 56px; height: 1px;
  background: linear-gradient(90deg, transparent 0%, var(--gold) 50%, transparent 100%);
}
.gm-rows { display: flex; flex-direction: column; gap: 6px; }
.gm-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 11px 14px;
  background: rgba(14,10,8,0.55);
  border: 1px solid rgba(201,189,159,0.22);
  color: var(--ink);
  font-family: inherit;
  cursor: pointer;
  border-radius: 3px;
  text-align: left;
  transition: background 0.15s, border-color 0.15s, color 0.15s, transform 0.1s;
}
.gm-row:hover:not([disabled]) {
  background: rgba(40,30,18,0.7);
  border-color: var(--gold-bright);
  color: var(--gold-bright);
  transform: translateX(2px);
}
.gm-row[disabled] { opacity: 0.35; cursor: not-allowed; }
.gm-row-label {
  font-size: 13px;
  letter-spacing: 0.06em;
  font-weight: 500;
}
.gm-row-value {
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-dim);
  font-family: inherit;
  white-space: nowrap;
}
.gm-row:hover:not([disabled]) .gm-row-value { color: var(--gold); }
.gm-row-danger { border-color: rgba(212,90,90,0.28); }
.gm-row-danger:hover:not([disabled]) {
  background: rgba(48,18,16,0.7);
  border-color: rgba(220,110,110,0.55);
  color: #f0d0d0;
}
.gm-row-danger .gm-row-label { color: #d8b8b8; }
.gm-row-danger:hover:not([disabled]) .gm-row-value { color: #e8b8b8; }

/* ===========================================================================
   FEEDBACK MODAL — QA submission form opened from the game menu.
   =========================================================================== */
.feedback-modal {
  position: fixed; inset: 0;
  z-index: 9100;
  display: flex; align-items: center; justify-content: center;
  padding: 16px;
}
.fb-backdrop {
  position: absolute; inset: 0;
  background: rgba(8,6,4,0.78);
  backdrop-filter: blur(2px);
}
.fb-card {
  position: relative;
  width: min(440px, 100%);
  max-height: calc(100vh - 32px);
  display: flex; flex-direction: column;
  background: var(--paper, #1a1410);
  border: 1px solid rgba(212,200,168,0.35);
  box-shadow: 0 18px 48px rgba(0,0,0,0.55), 0 0 0 1px rgba(0,0,0,0.6);
  color: var(--ink, #d4c8a8);
}
.fb-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 14px;
  border-bottom: 1px solid rgba(212,200,168,0.18);
}
.fb-title {
  font-size: 12px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--gold-pale, #e0c890);
}
.fb-close {
  background: none; border: none; color: var(--ink-dim, #9a8f78);
  font-size: 24px; line-height: 1;
  min-width: 36px; min-height: 36px;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
}
.fb-close:hover { color: var(--gold-bright, #f0d488); }
.fb-body {
  padding: 12px 14px;
  overflow-y: auto;
  display: flex; flex-direction: column; gap: 10px;
  flex: 1;
}
.fb-field {
  display: flex; flex-direction: column; gap: 4px;
}
.fb-label {
  font-size: 10px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ink-dim, #9a8f78);
}
.fb-optional {
  letter-spacing: 0.04em;
  text-transform: none;
  opacity: 0.7;
}
.fb-input {
  font-family: inherit;
  font-size: 13px;
  color: var(--ink, #d4c8a8);
  background: rgba(28,22,18,0.6);
  border: 1px solid rgba(212,200,168,0.22);
  padding: 8px 10px;
  outline: none;
  border-radius: 0;
}
.fb-input:focus { border-color: var(--gold, #b89860); }
.fb-textarea {
  resize: vertical;
  min-height: 64px;
  line-height: 1.45;
}
.fb-check {
  display: flex; align-items: center; gap: 8px;
  font-size: 11px; color: var(--ink-dim, #9a8f78);
  cursor: pointer;
  padding: 4px 0;
  min-height: 32px;
}
.fb-check input { width: 16px; height: 16px; accent-color: var(--gold, #b89860); }
.fb-preview {
  font-size: 10px;
  color: var(--ink-dim, #9a8f78);
}
.fb-preview summary {
  cursor: pointer;
  padding: 4px 0;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.fb-preview pre {
  margin: 4px 0 0;
  padding: 8px;
  background: rgba(0,0,0,0.35);
  border-left: 2px solid rgba(212,200,168,0.2);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 10px;
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 160px;
  overflow-y: auto;
}
.fb-flash {
  font-size: 11px;
  padding: 6px 8px;
  background: rgba(40,32,22,0.7);
  border-left: 2px solid var(--gold, #b89860);
  color: var(--gold-pale, #e0c890);
}
.fb-flash.fb-flash-error {
  border-left-color: #d05858;
  color: #f0b4b4;
}
.fb-actions {
  display: flex; gap: 8px; justify-content: flex-end;
  padding: 10px 14px;
  border-top: 1px solid rgba(212,200,168,0.18);
}
.fb-btn {
  font-family: inherit;
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  padding: 9px 14px;
  min-height: 36px;
  background: rgba(28,22,18,0.7);
  color: var(--ink, #d4c8a8);
  border: 1px solid rgba(212,200,168,0.3);
  cursor: pointer;
}
.fb-btn:hover { border-color: var(--gold, #b89860); color: var(--gold-pale, #e0c890); }
.fb-btn-primary {
  background: rgba(60,46,22,0.85);
  border-color: var(--gold, #b89860);
  color: var(--gold-bright, #f0d488);
}
.fb-btn-primary:hover {
  background: rgba(80,60,28,0.95);
  border-color: var(--gold-bright, #f0d488);
}
.fb-btn-ghost { background: transparent; }
@media (max-width: 480px) {
  .fb-card { width: 100%; max-height: calc(100vh - 16px); }
  .fb-actions { flex-wrap: wrap; }
  .fb-btn { flex: 1 1 auto; }
}

/* ===========================================================================
   SIGIL INFO CARD — focused popover shown when a sigil chip is tapped.
   Owns its own DOM appended to <body> so it survives strip re-renders.
   =========================================================================== */
.sigil-info-card {
  position: fixed; inset: 0;
  z-index: 880;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.sic-backdrop {
  position: absolute; inset: 0;
  background: rgba(4,3,2,0.6);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}
.sic-body {
  position: relative;
  width: 100%;
  max-width: 360px;
  background:
    radial-gradient(ellipse at 50% 0%, #2c1f14 0%, #18110a 50%, #0c0805 100%);
  border: 1px solid var(--gold);
  border-radius: 6px;
  box-shadow: 0 24px 60px rgba(0,0,0,0.7), 0 0 28px rgba(201,189,159,0.15), 0 0 0 1px rgba(0,0,0,0.5) inset;
  padding: 26px 26px 20px;
  text-align: center;
  animation: gm-in 0.22s ease-out;
}
.sic-icon {
  font-size: 44px;
  line-height: 1;
  margin: 4px auto 12px;
  color: var(--gold-bright);
  text-shadow: 0 0 18px rgba(212,180,120,0.45), 0 0 6px rgba(212,180,120,0.6);
  display: inline-block;
  width: 72px;
  height: 72px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background:
    radial-gradient(circle, rgba(60,42,22,0.7) 0%, rgba(20,14,10,0.6) 60%, transparent 100%);
}
.sic-eyebrow {
  font-size: 10px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 8px;
  font-family: inherit;
}
.sic-name {
  font-family: 'Iowan Old Style', Georgia, serif;
  font-size: 22px;
  letter-spacing: 0.02em;
  font-weight: 400;
  color: var(--gold-bright);
  margin-bottom: 12px;
  text-shadow: 0 0 14px rgba(212,180,120,0.35);
}
.sic-tier {
  margin-left: 10px;
  font-size: 14px;
  letter-spacing: 0.18em;
  color: var(--gold-pale);
  vertical-align: middle;
  opacity: 0.9;
}
.sic-value {
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--ink-dim);
  margin: -10px auto 16px;
  text-align: center;
}
.sic-value b { color: var(--gold-bright); font-weight: 700; }
.sic-desc {
  font-family: 'Iowan Old Style', Georgia, serif;
  font-size: 14px;
  line-height: 1.6;
  color: var(--ink);
  font-style: italic;
  margin: 0 auto 18px;
  max-width: 280px;
}
.sic-close {
  display: inline-block;
  padding: 8px 22px;
  background: rgba(14,10,8,0.7);
  border: 1px solid var(--gold);
  color: var(--gold-bright);
  font-family: inherit;
  font-size: 11px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  border-radius: 3px;
  cursor: pointer;
  transition: background 0.15s, color 0.15s, transform 0.1s;
}
.sic-close:hover {
  background: rgba(40,30,18,0.85);
  color: var(--gold-pale);
  transform: translateY(-1px);
}
.sigil-info-card.sic-combat   .sic-body { border-color: rgba(212,69,69,0.7); }
.sigil-info-card.sic-defense  .sic-body { border-color: rgba(136,152,168,0.7); }
.sigil-info-card.sic-resource .sic-body { border-color: rgba(212,200,168,0.7); }
.sigil-info-card.sic-squad    .sic-body { border-color: rgba(240,212,136,0.7); }

.info-biome {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 6px 12px;
  margin: 2px 0 8px;
  padding: 8px 12px;
  background: linear-gradient(180deg, rgba(28,22,16,0.55) 0%, rgba(8,6,6,0.7) 100%);
}
.info-biome .info-label { color: var(--ink-dim); }
.info-biome-name {
  font-size: 12px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold-pale);
}
.info-biome-desc {
  flex: 1 1 100%;
  font-size: 10.5px;
  font-style: italic;
  color: var(--ink);
  opacity: 0.85;
}
.info-section {
  margin: 8px 0;
}
.info-label {
  font-size: 9.5px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-dim);
  display: block;
  margin-bottom: 4px;
}
.info-sigils {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}
.info-sigils .sigil-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
  font-size: 10.5px;
  letter-spacing: 0.04em;
  padding: 3px 10px;
  background: rgba(28,22,18,0.55);
}
.info-empty {
  font-size: 10.5px;
  font-style: italic;
  color: var(--ink-faint);
}
.info-heroes {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.info-hero {
  display: grid;
  grid-template-columns: 50px 1fr;
  gap: 10px;
  align-items: center;
  padding: 6px 8px;
  background: linear-gradient(180deg, rgba(20,18,14,0.45) 0%, rgba(8,6,6,0.65) 100%);
}
.info-hero.info-hero-downed { opacity: 0.5; }
.info-hero-portrait {
  width: 50px; height: 70px;
  display: flex; align-items: flex-end; justify-content: center;
  overflow: hidden;
}
.info-hero-portrait svg { width: 100%; height: 100%; }
.info-hero-name {
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-pale);
}
.info-hero-hp {
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--ink);
  margin-top: 2px;
}
.info-hero-quirks {
  margin-top: 4px;
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.info-hero-quirks-empty {
  font-size: 9.5px;
  font-style: italic;
  color: var(--ink-faint);
}

/* ===========================================================================
   BONDS — info panel section listing active adjacency synergies with
   their effect glyphs.  Each row reads: glyph + pair name + line label.
   Bond rows have a gold accent; friction rows blood-red.
   =========================================================================== */
.info-bonds {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.info-bond {
  display: grid;
  grid-template-columns: 22px 1fr;
  align-items: center;
  gap: 8px;
  padding: 5px 8px;
  background: linear-gradient(180deg, rgba(20,18,14,0.45) 0%, rgba(8,6,6,0.6) 100%);
  border-left: 2px solid rgba(232,220,196,0.18);
}
.info-bond.info-bond-bond     { border-left-color: var(--gold-bright); }
.info-bond.info-bond-friction { border-left-color: var(--blood-bright); }
.info-bond-glyph {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px; height: 22px;
  font-size: 14px;
  border-radius: 50%;
  background: rgba(10,8,7,0.7);
}
.info-bond.effect-dmg     .info-bond-glyph { color: var(--blood-bright); }
.info-bond.effect-heal    .info-bond-glyph { color: var(--heal); }
.info-bond.effect-armor   .info-bond-glyph { color: var(--armor); }
.info-bond.effect-resolve .info-bond-glyph { color: var(--gold-bright); }
.info-bond.effect-utility .info-bond-glyph { color: var(--ink-dim); }
.info-bond-body { display: flex; flex-direction: column; gap: 1px; }
.info-bond-name {
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink);
}
.info-bond.info-bond-bond     .info-bond-name { color: var(--gold-pale); }
.info-bond.info-bond-friction .info-bond-name { color: #ffb8b8; }
.info-bond-pair {
  font-size: 9.5px;
  letter-spacing: 0.04em;
  color: var(--ink-faint);
}

/* ===========================================================================
   RESONANCES — info panel section listing every combo the current party
   can build.  Each row shows the combo name + tier badge + requirement
   summary + flavor desc.  Sig-tier combos get a cool-blue accent;
   triples get a red accent; duos default gold.
   =========================================================================== */
.info-combos {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.info-combo {
  padding: 5px 8px;
  background: linear-gradient(180deg, rgba(20,18,14,0.45) 0%, rgba(8,6,6,0.6) 100%);
  border-left: 2px solid var(--gold-dim);
}
.info-combo.tier-triple { border-left-color: var(--blood-bright); }
.info-combo.tier-sig    { border-left-color: #88b8e8; }
.info-combo-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
}
.info-combo-name {
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-pale);
}
.info-combo.tier-triple .info-combo-name { color: var(--gold-bright); }
.info-combo.tier-sig    .info-combo-name { color: #c0d8f4; }
.info-combo-tier {
  font-size: 8.5px;
  letter-spacing: 0.28em;
  color: var(--ink-faint);
  padding: 1px 6px;
  background: rgba(0,0,0,0.45);
}
.info-combo-req {
  font-size: 9.5px;
  letter-spacing: 0.04em;
  color: var(--ink-dim);
  margin-top: 2px;
}
.info-combo-desc {
  font-size: 10px;
  font-style: italic;
  color: var(--ink);
  letter-spacing: 0.02em;
  line-height: 1.35;
  margin-top: 2px;
}

/* ===========================================================================
   LOST ON THE ROAD — memorial section in the info panel.  Lists every
   hero the player killed on a wanderer node this climb (persisted across
   layers).  Visual language is intentionally muted — desaturated
   portraits, blood-tinted left border, italic flavor line — so the
   section reads as consequence rather than achievement.
   =========================================================================== */
.info-section.info-section-lost .info-label { color: rgba(212,90,90,0.75); }
.info-lost-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.info-lost-row {
  display: grid;
  grid-template-columns: 38px 1fr;
  gap: 10px;
  align-items: center;
  padding: 4px 8px;
  background: linear-gradient(180deg, rgba(30,16,14,0.55) 0%, rgba(8,4,4,0.7) 100%);
  border-left: 2px solid rgba(180,60,60,0.5);
}
.info-lost-portrait {
  width: 38px; height: 52px;
  display: flex; align-items: flex-end; justify-content: center;
  overflow: hidden;
  filter: grayscale(0.85) brightness(0.7);
}
.info-lost-portrait svg { width: 100%; height: 100%; }
.info-lost-name {
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(232,200,200,0.85);
}
.info-lost-flavor {
  font-size: 9.5px;
  font-style: italic;
  color: var(--ink-faint);
  margin-top: 2px;
}

/* ===========================================================================
   CONFIRM MODAL
   =========================================================================== */
#confirm-modal {
  position: fixed;
  inset: 0;
  /* Above the game-menu (z 900) so the confirm dialog isn't hidden
     behind it when Return-to-Title is tapped. */
  z-index: 950;
  background: radial-gradient(ellipse at center, rgba(15,8,4,0.85) 0%, rgba(0,0,0,0.96) 70%);
  display: flex;
  align-items: center;
  justify-content: center;
  animation: cm-bg-in 0.2s ease-out;
}
@keyframes cm-bg-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
#confirm-modal .cm-card {
  max-width: 360px;
  padding: 18px 22px 16px;
  background: radial-gradient(ellipse 120% 100% at 50% 0%,
                              rgba(36,28,22,0.97) 0%,
                              rgba(12,10,10,0.98) 70%);
  box-shadow:
    0 16px 60px rgba(0,0,0,0.85),
    inset 0 1px 0 rgba(232,220,196,0.06),
    0 0 30px rgba(220,80,80,0.12);
  text-align: center;
  animation: cm-card-in 0.28s cubic-bezier(.18,.89,.32,1);
}
@keyframes cm-card-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
.cm-title {
  font-size: 14px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold-pale);
  margin-bottom: 8px;
}
.cm-body {
  font-size: 11.5px;
  font-style: italic;
  color: var(--ink);
  letter-spacing: 0.04em;
  line-height: 1.5;
  margin-bottom: 16px;
}
.cm-actions {
  display: flex;
  justify-content: center;
  gap: 14px;
}
.cm-actions button {
  background: transparent;
  border: none;
  color: var(--ink);
  font-family: inherit;
  font-size: 11px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  padding: 6px 16px;
  cursor: pointer;
  transition: color 0.16s, text-shadow 0.16s;
}
.cm-actions .cm-cancel:hover { color: var(--gold-pale); text-shadow: 0 0 10px rgba(240,224,182,0.35); }
.cm-actions .cm-confirm {
  color: var(--blood-bright);
}
.cm-actions .cm-confirm:hover { text-shadow: 0 0 12px rgba(220,80,80,0.6); }

/* ===========================================================================
   COMBAT READABILITY — intent threat coloring + bond/friction edges
   =========================================================================== */

/* Intent bubble: threat tier colorizes the number so the player can plan
   in a glance.  Lethal = red glow, heavy = amber, mild = ink-dim. */
.intent-bubble.threat-mild .intent-num {
  color: #d6cab0;
  text-shadow:
    0 1px 0 rgba(0,0,0,0.95),
    0 0 4px rgba(0,0,0,0.85);
}
.intent-bubble.threat-heavy .intent-num {
  color: #ffb86b;
  text-shadow:
    0 1px 0 rgba(0,0,0,0.95),
    0 0 4px rgba(0,0,0,0.85),
    0 0 10px rgba(255,160,80,0.55);
}
.intent-bubble.threat-lethal .intent-num {
  color: var(--blood-bright);
  font-weight: 800;
  text-shadow:
    0 1px 0 rgba(0,0,0,0.95),
    0 0 4px rgba(0,0,0,0.85),
    0 0 12px rgba(220,80,80,0.8);
}
.intent-bubble.threat-lethal {
  animation: intent-lethal-pulse 1.6s ease-in-out infinite;
}
.intent-bubble.threat-lethal .intent-icon {
  filter:
    drop-shadow(0 1px 0 rgba(0,0,0,0.95))
    drop-shadow(0 0 3px rgba(0,0,0,0.85))
    drop-shadow(0 0 10px rgba(220,80,80,0.7));
}
@keyframes intent-lethal-pulse {
  0%, 100% { transform: translateX(-50%) scale(1);    filter: drop-shadow(0 0 0   rgba(220,80,80,0)); }
  50%      { transform: translateX(-50%) scale(1.06); filter: drop-shadow(0 0 8px rgba(220,80,80,0.55)); }
}

/* Bond / friction edge — small connector dot between two adjacent hero
   cards.  #party-half is a 3-col grid in order back / mid / front, so
   the MB pair sits between cols 1 & 2 and FM between 2 & 3. */
.adj-edge {
  position: absolute;
  z-index: 5;
  bottom: 18%;
  width: 22px; height: 22px;
  display: flex; align-items: center; justify-content: center;
  pointer-events: none;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%,
                              rgba(36,28,22,0.85) 0%,
                              rgba(10,8,8,0.4) 70%);
  animation: adj-edge-pulse 2.6s ease-in-out infinite;
}
.adj-edge.adj-edge-mb { left: 33.33%; transform: translateX(-50%); }
.adj-edge.adj-edge-fm { left: 66.66%; transform: translateX(-50%); }
.adj-edge-glyph {
  font-size: 11px;
  letter-spacing: 0;
  line-height: 1;
  text-shadow: 0 0 8px currentColor;
}
.adj-edge.adj-edge-bond     .adj-edge-glyph { color: var(--gold-pale); }
.adj-edge.adj-edge-friction .adj-edge-glyph { color: var(--blood-bright); }
.adj-edge.adj-edge-bond     { box-shadow: 0 0 12px rgba(240,224,182,0.22); }
.adj-edge.adj-edge-friction { box-shadow: 0 0 12px rgba(220,80,80,0.22); }
@keyframes adj-edge-pulse {
  0%, 100% { transform: translateX(-50%) scale(1);    opacity: 0.85; }
  50%      { transform: translateX(-50%) scale(1.12); opacity: 1; }
}

/* ===========================================================================
   HUD POLISH — quieter background, refined spacing.
   =========================================================================== */
#hud {
  padding: 6px 12px !important;
  background: linear-gradient(180deg,
                              rgba(10,8,8,0.55) 0%,
                              rgba(10,8,8,0.2)  100%) !important;
  border-bottom: none !important;
  box-shadow: 0 6px 10px -6px rgba(0,0,0,0.5);
}
/* Hide the HUD strip on welcome / vignette / starter / run-summary
   / world-map overlays.  Those are full-screen narrative moments where
   the biome chip + sigil tray + menu would compete with the title
   text for the top-left area.  The HUD stays visible on map / combat. */
body:has(#overlay.overlay-vignette:not(.hidden)) #hud,
body:has(#overlay.overlay-starter:not(.hidden)) #hud,
body:has(#overlay.overlay-runsummary:not(.hidden)) #hud,
body:has(#overlay.overlay-wanderer-duel:not(.hidden)) #hud,
body:has(#title-screen:not(.hidden)) #hud,
body:has(#world-map:not(.hidden)) #hud {
  visibility: hidden;
}
#run-modifier .run-mod-chip {
  border: none !important;
  background: rgba(20,18,16,0.45);
  letter-spacing: 0.16em;
  /* Match the visual weight of the 44px sigil chips and ☰ button that
     share the strip — was 28px and looked small alongside them. */
  min-height: 36px;
  padding: 8px 14px 8px 12px;
}
#sigil-tray {
  /* Tightened from 6px so chips read as a group instead of spread out
     across the right side of the HUD strip. */
  gap: 3px;
}

/* ===========================================================================
   RUN SUMMARY — cinematic refresh
   =========================================================================== */
#overlay-body.run-summary-body { padding-top: 4px; }

/* Pin the Ascend / Return-to-Title button to the bottom of the
   overlay so it stays visible when the summary content (montage +
   stats + highlights + embers + unlock callouts + memorial) is taller
   than the design canvas.  Was getting pushed below the fold —
   players had to scroll without realizing it.  Layout:
     - overlay-content becomes a flex column
     - the body fills the remaining space and scrolls inside itself
     - the button sits at the bottom, always reachable
   Targets via the .overlay-runsummary class on #overlay (added by
   showRunSummary in JS) instead of :has() so it applies reliably
   across every engine, including older mobile Safari quirks. */
#overlay.overlay-runsummary #overlay-content {
  display: flex !important;
  flex-direction: column !important;
  padding-bottom: 14px !important;
  /* Hard cap on the run-summary card height so it never extends
     behind the iOS home indicator / safe-area bottom.  The
     viewport-fit=cover meta lets content slide under the indicator
     by default; here we pull back so the Ascend button stays in
     view.  88vh / 88% leaves a small margin even on edge-case
     devices that compute 92vh past the safe area. */
  max-height: min(88vh, 88%) !important;
}
#overlay.overlay-runsummary #overlay-content > #overlay-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
#overlay.overlay-runsummary #overlay-content > #overlay-btn {
  flex: 0 0 auto;
  align-self: center;
  margin-top: 10px;
}

.rs-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: stretch;
  text-align: center;
  padding: 6px 4px 0;
}
.rs-flavor {
  font-size: 13px;
  font-style: italic;
  letter-spacing: 0.04em;
  color: var(--ink);
  line-height: 1.55;
  padding: 0 4px;
}
.rs-stats {
  display: flex;
  justify-content: center;
  gap: 18px;
  padding: 6px 0 4px;
  border-top: 1px solid rgba(212,200,168,0.06);
  border-bottom: 1px solid rgba(212,200,168,0.06);
}
.rs-stat {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
}
.rs-stat b {
  font-size: 18px;
  letter-spacing: 0.04em;
  color: var(--gold-pale);
  font-weight: 400;
  text-shadow: 0 0 10px rgba(240,224,182,0.3);
}
.rs-stat em {
  font-style: normal;
  font-size: 9.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.rs-rows {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.rs-rows .vs-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 12px;
  align-items: baseline;
  padding: 4px 6px;
  font-size: 11.5px;
}
.rs-rows .vs-name {
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-size: 10.5px;
  color: var(--ink);
}
.rs-rows .vs-row.vs-downed .vs-name { color: var(--ink-faint); }
.rs-rows .vs-stat { font-size: 10px; letter-spacing: 0.04em; }
.rs-unlocks {
  margin-top: 4px;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px;
  padding-top: 6px;
  border-top: 1px solid rgba(212,200,168,0.06);
}
.rs-unlocks-label {
  width: 100%;
  font-size: 9px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 2px;
}
.rs-unlocks-chip {
  font-size: 10.5px;
  letter-spacing: 0.06em;
  padding: 2px 8px;
  color: var(--gold-pale);
  background: rgba(40,28,20,0.5);
}
.rs-card.rs-boss .rs-flavor    { color: var(--gold-pale); text-shadow: 0 0 12px rgba(240,224,182,0.25); }
.rs-card.rs-defeat .rs-flavor  { color: var(--ink-dim); }

/* Silhouette montage of the party at the top of the run summary. */
.rs-montage {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 10px;
  height: 130px;
  padding-bottom: 2px;
  position: relative;
}
.rs-montage::after {
  /* Faint floor hairline under the silhouettes */
  content: '';
  position: absolute;
  left: 10%; right: 10%;
  bottom: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent 0%, rgba(232,220,196,0.18) 50%, transparent 100%);
}
.rs-portrait {
  width: 70px; height: 110px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  position: relative;
  -webkit-mask-image: radial-gradient(ellipse 80% 100% at 50% 50%, #000 60%, transparent 100%);
          mask-image: radial-gradient(ellipse 80% 100% at 50% 50%, #000 60%, transparent 100%);
}
.rs-portrait svg {
  height: 100%;
  width: auto;
  filter: drop-shadow(0 6px 14px rgba(0,0,0,0.65));
}
.rs-portrait.rs-portrait-downed {
  opacity: 0.4;
  filter: grayscale(0.6);
}
.rs-card.rs-boss .rs-portrait svg   { filter: drop-shadow(0 0 16px rgba(240,224,182,0.3)) drop-shadow(0 6px 14px rgba(0,0,0,0.65)); }
.rs-card.rs-defeat .rs-portrait svg { filter: drop-shadow(0 0 14px rgba(220,80,80,0.25)) drop-shadow(0 6px 14px rgba(0,0,0,0.65)) grayscale(0.4); }

@media (max-height: 480px) {
  .rs-montage { height: 88px; gap: 6px; }
  .rs-portrait { width: 48px; height: 70px; }
}

/* Landscape compaction — the Ascend summary must fit on one mobile/tablet
   landscape screen without scrolling.  We trigger on orientation: landscape
   so iPad-class viewports (e.g. 1024 tall) also compress, and tighten again
   on the shorter phone-class screens. */
@media (orientation: landscape) and (max-height: 1100px) {
  .overlay-runsummary #overlay-body { padding-top: 4px; padding-bottom: 4px; }
  .rs-card {
    display: grid;
    grid-template-columns: minmax(220px, 1fr) minmax(220px, 1fr);
    grid-template-areas:
      "montage  rows"
      "flavor   rows"
      "stats    rows"
      "bonds    memorial"
      "bonds    memorial";
    gap: 6px 18px;
    padding: 0 6px;
    align-items: start;
  }
  .rs-montage  { grid-area: montage;  height: 96px; gap: 6px; padding-bottom: 0; }
  .rs-flavor   { grid-area: flavor;   font-size: 11px; line-height: 1.4; padding: 0; text-align: left; }
  .rs-stats    { grid-area: stats;    justify-content: flex-start; gap: 14px; padding: 2px 0 0; border: none; }
  .rs-rows     { grid-area: rows;     gap: 2px; align-self: start; }
  .vs-syn      { grid-area: bonds;    padding-top: 4px; text-align: left; font-size: 9.5px; margin-top: 0; border-top: none; }
  .rs-memorial { grid-area: memorial; margin-top: 0; padding-top: 4px; gap: 4px; align-items: flex-start; }
  .rs-portrait { width: 56px; height: 84px; }
  .rs-stat b   { font-size: 14px; }
  .rs-stat em  { font-size: 8px; }
  .rs-rows .vs-row { padding: 2px 4px; font-size: 10px; grid-template-columns: 1fr auto; gap: 8px; }
  .rs-rows .vs-name { font-size: 9px; }
  .rs-rows .vs-stat { font-size: 9px; }
  .rs-memorial-label { font-size: 8px; }
  .rs-graves { gap: 8px; }
  .rs-grave-stone { width: 44px; min-height: 54px; padding: 4px 3px 6px; }
  .rs-grave-mark { font-size: 12px; }
  .rs-grave-name { font-size: 7.5px; letter-spacing: 0.14em; }
  .vs-syn-label { font-size: 8px; }
  .vs-syn-chip { font-size: 9px; padding: 1px 6px; }
  /* Title sits above the card — keep it small in landscape so it doesn't
     steal vertical space.  Same for the Ascend button. */
  .overlay-runsummary #overlay-title { font-size: 13px; padding: 2px 0; }
  .overlay-runsummary #overlay-btn { padding: 6px 18px; font-size: 11px; }
}
@media (orientation: landscape) and (max-height: 520px) {
  .rs-card { gap: 4px 16px; padding: 0 4px; }
  .rs-montage { height: 72px; }
  .rs-portrait { width: 44px; height: 60px; }
  .rs-flavor { font-size: 10px; line-height: 1.3; }
  .rs-stat b { font-size: 12px; }
  .rs-rows .vs-row { padding: 1px 4px; font-size: 9px; }
  .rs-rows .vs-name, .rs-rows .vs-stat { font-size: 8.5px; }
  .rs-grave-stone { width: 36px; min-height: 44px; }
  .rs-grave-mark { font-size: 10px; }
  .rs-grave-name { font-size: 7px; }
}

/* ===========================================================================
   ASCENT — glowing stairway behind the survivors on boss victory.  The
   silhouettes sit in front of a perspective-converging path of light steps,
   reading as "the climb continues."
   =========================================================================== */
.rs-montage.rs-with-ascent { position: relative; overflow: visible; }
.rs-montage.rs-with-ascent .rs-portrait { position: relative; z-index: 1; }
.rs-ascent {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
}
.rs-ascent-glow {
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  width: 70%;
  height: 140%;
  background: radial-gradient(ellipse 60% 80% at 50% 100%,
              rgba(240,224,182,0.32) 0%,
              rgba(255,180,90,0.14) 28%,
              rgba(255,140,60,0.06) 55%,
              transparent 80%);
  filter: blur(2px);
  animation: rs-ascent-pulse 4s ease-in-out infinite;
}
@keyframes rs-ascent-pulse {
  0%, 100% { opacity: 0.85; }
  50%      { opacity: 1; }
}
.rs-steps {
  position: absolute;
  left: 50%;
  bottom: 4px;
  transform: translateX(-50%);
  width: 140px; height: 90%;
  display: flex;
  flex-direction: column-reverse;
  align-items: center;
  gap: 5px;
  pointer-events: none;
}
.rs-steps span {
  display: block;
  height: 4px;
  background: linear-gradient(180deg, rgba(240,224,182,0.55) 0%, rgba(240,224,182,0.08) 100%);
  border-top: 1px solid rgba(240,224,182,0.7);
  box-shadow: 0 0 12px rgba(240,224,182,0.35);
}
.rs-steps span:nth-child(1) { width: 100%; }
.rs-steps span:nth-child(2) { width: 78%; }
.rs-steps span:nth-child(3) { width: 56%; }
.rs-steps span:nth-child(4) { width: 36%; }
.rs-steps span:nth-child(5) { width: 18%; opacity: 0.85; }

/* Ascend cinematic — when the player taps Ascend on a boss-win summary
   the surviving silhouettes drift up the stairway, the stats fade away,
   and then we hand off to the world map.  The summary itself becomes
   the carry-over transition. */
.rs-card.rs-ascending .rs-portrait {
  animation: rs-ascend-rise 1.3s cubic-bezier(.32,.08,.4,1) forwards;
}
.rs-card.rs-ascending .rs-portrait.rs-portrait-downed {
  /* Fallen don't ascend — they fade in place. */
  animation: rs-ascend-fade 0.6s ease-out forwards;
}
.rs-card.rs-ascending .rs-ascent-glow {
  animation: rs-ascend-flare 1.3s ease-out forwards;
}
.rs-card.rs-ascending .rs-flavor,
.rs-card.rs-ascending .rs-stats,
.rs-card.rs-ascending .rs-rows,
.rs-card.rs-ascending .vs-syn,
.rs-card.rs-ascending .rs-memorial {
  animation: rs-ascend-fade 0.55s ease-out forwards;
}
@keyframes rs-ascend-rise {
  0%   { transform: translateY(0)     scale(1);    opacity: 1; }
  40%  { transform: translateY(-30px) scale(0.96); opacity: 1; filter: drop-shadow(0 0 18px rgba(240,224,182,0.45)); }
  100% { transform: translateY(-180px) scale(0.7);  opacity: 0; filter: drop-shadow(0 0 26px rgba(240,224,182,0.7)); }
}
@keyframes rs-ascend-fade {
  0%   { opacity: 1; }
  100% { opacity: 0; }
}
@keyframes rs-ascend-flare {
  0%   { opacity: 0.9; transform: translateX(-50%) scale(1); }
  60%  { opacity: 1;   transform: translateX(-50%) scale(1.12); }
  100% { opacity: 0;   transform: translateX(-50%) scale(1.3); }
}

/* ===========================================================================
   META UNLOCK CALLOUT — one-shot moment surfaced on the run summary when
   the player has just opened up a meta-progression system (e.g. oaths).
   =========================================================================== */
.rs-unlock {
  margin: 8px auto 0;
  padding: 6px 12px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: linear-gradient(180deg, rgba(40,28,52,0.7) 0%, rgba(14,10,18,0.85) 100%);
  border: 1px solid rgba(170,140,212,0.45);
  border-radius: 3px;
  animation: rs-unlock-pulse 2s ease-in-out infinite;
}
@keyframes rs-unlock-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(170,140,212,0); }
  50%      { box-shadow: 0 0 14px 0 rgba(170,140,212,0.35); }
}
.rs-unlock-mark {
  font-size: 14px;
  color: #d4b8f0;
  text-shadow: 0 0 8px rgba(170,140,212,0.7);
}
.rs-unlock-label {
  font-size: 10.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold-pale);
  font-weight: 700;
}
.rs-unlock-desc {
  font-size: 9.5px;
  letter-spacing: 0.04em;
  color: var(--ink-dim);
  font-style: italic;
}

/* ===========================================================================
   TOMBSTONE MEMORIAL — fallen heroes from this run.  Replaces the old
   "Companions remembered" chip list with carved-stone markers.
   =========================================================================== */
.rs-memorial {
  margin-top: 8px;
  padding-top: 10px;
  border-top: 1px solid rgba(212,200,168,0.06);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.rs-memorial-label {
  font-size: 9px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.rs-graves {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
}
.rs-grave {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.rs-grave-stone {
  position: relative;
  width: 56px;
  min-height: 70px;
  padding: 6px 4px 8px;
  background: linear-gradient(180deg, rgba(72,64,54,0.85) 0%, rgba(34,28,24,0.95) 100%);
  border-radius: 28px 28px 4px 4px;
  border: 1px solid rgba(212,200,168,0.16);
  box-shadow:
    0 6px 16px rgba(0,0,0,0.65),
    inset 0 1px 0 rgba(232,220,196,0.10),
    inset 0 -10px 18px rgba(0,0,0,0.45);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}
.rs-grave-stone::before {
  content: '';
  position: absolute;
  left: 50%; bottom: -4px;
  transform: translateX(-50%);
  width: 110%;
  height: 6px;
  background: radial-gradient(ellipse 50% 100% at 50% 0%, rgba(0,0,0,0.7) 0%, transparent 80%);
}
.rs-grave-mark {
  font-size: 16px;
  line-height: 1;
  color: rgba(212,200,168,0.4);
  text-shadow: 0 0 6px rgba(212,200,168,0.15);
  margin-top: 2px;
}
.rs-grave-name {
  font-size: 8.5px;
  letter-spacing: 0.18em;
  color: var(--ink-dim);
  text-align: center;
  word-break: break-word;
}

/* "The road keeps" — the wanderer-kill memorial.  Same scaffolding as
   the in-combat fallen memorial, but tinted blood-red and uses an ×
   mark instead of a +.  Reads as deaths the player chose to inflict
   rather than ones that just happened. */
.rs-memorial.rs-roadkill .rs-memorial-label {
  color: rgba(212,90,90,0.78);
}
.rs-grave-roadkill .rs-grave-stone {
  background: linear-gradient(180deg, rgba(60,28,24,0.85) 0%, rgba(28,12,12,0.95) 100%);
  border-color: rgba(180,60,60,0.28);
}
.rs-grave-roadkill .rs-grave-mark {
  color: rgba(232,140,140,0.85);
  text-shadow: 0 0 8px rgba(212,90,90,0.4);
}
.rs-grave-roadkill .rs-grave-name {
  color: rgba(228,192,192,0.7);
}

/* ===========================================================================
   REST — landscape compaction.  On short-height screens, choices flow
   horizontally so the three options fit above the fold instead of
   forcing a scroll.
   =========================================================================== */
@media (max-height: 600px) {
  .overlay-rest #overlay-choices.event-choices {
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: center;
    gap: 8px;
    margin-top: 2px;
  }
  .overlay-rest .event-choice {
    flex: 1 1 0;
    min-width: 0;
    max-width: 240px;
    text-align: center;
  }
  .overlay-rest .rest-scene { min-height: 110px; margin: 2px 0 4px; }
  .overlay-rest .rest-hero { width: 56px; height: 90px; }
  .overlay-rest .rest-fire { width: 60px; height: 90px; }
  .overlay-rest .rest-fire .ts-flame    { width: 22px; height: 36px; }
  .overlay-rest .rest-fire .ts-flame.f2 { width: 16px; height: 24px; }
  .overlay-rest .rest-fire .ts-flame.f3 { width: 9px;  height: 16px; }
  .overlay-rest .rest-flavor { margin: 2px 0 4px; font-size: 11px; }
}

/* ===========================================================================
   SETTINGS PANEL
   =========================================================================== */
.settings-grid {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 4px;
}
.settings-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 10px 14px;
  background: linear-gradient(180deg, rgba(20,18,16,0.55) 0%, rgba(10,8,8,0.7) 100%);
  border: none;
  color: var(--ink);
  font-family: inherit;
  cursor: pointer;
  text-align: left;
  transition: background 0.16s, color 0.16s;
}
.settings-row:hover { background: linear-gradient(180deg, rgba(40,28,18,0.7) 0%, rgba(16,10,8,0.85) 100%); color: var(--gold-pale); }
.settings-label {
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
}
.settings-value {
  font-size: 10.5px;
  font-style: italic;
  letter-spacing: 0.04em;
  color: var(--ink-dim);
}
.settings-row:hover .settings-value { color: var(--gold-pale); }
.settings-row.settings-danger .settings-value { color: var(--blood-bright); opacity: 0.85; }

/* ===========================================================================
   DEV TOOLS PANEL — Settings → Dev tools opens this.  Jump-to-layer chip
   row + playtest-boss column.  Reuses the parent overlay-content shell;
   only the inner section / chip styles live here.
   =========================================================================== */
.dev-section {
  margin: 4px 0 8px;
  text-align: left;
}
.dev-section-label {
  font-size: 9.5px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 4px;
}
.dev-section-hint {
  font-size: 9px;
  font-style: italic;
  color: var(--ink-faint);
  letter-spacing: 0.02em;
  margin-top: 4px;
  line-height: 1.35;
}
.dev-row {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.dev-col {
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.dev-chip {
  flex: 0 0 auto;
  padding: 4px 10px;
  background: linear-gradient(180deg, rgba(28,22,18,0.7) 0%, rgba(10,8,8,0.8) 100%);
  border: 1px solid var(--gold-dim);
  color: var(--gold);
  font-family: inherit;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: pointer;
  border-radius: 2px;
  transition: border-color 0.12s, color 0.12s, background 0.12s;
}
.dev-chip:hover {
  border-color: var(--gold-bright);
  color: var(--gold-pale);
  background: linear-gradient(180deg, rgba(48,36,22,0.85) 0%, rgba(18,12,10,0.92) 100%);
}
.dev-chip-boss {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 6px 12px;
  text-align: left;
}
.dev-chip-boss .dev-chip-tag {
  font-size: 9.5px;
  letter-spacing: 0.22em;
  color: var(--ink-dim);
  flex-shrink: 0;
}
.dev-chip-boss .dev-chip-name {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: none;
  color: var(--gold);
  font-style: italic;
}
.dev-chip-boss:hover .dev-chip-name { color: var(--gold-pale); }
.dev-chip-mega {
  border-color: rgba(170,140,212,0.6);
  box-shadow: 0 0 8px rgba(170,140,212,0.18) inset;
}
.dev-chip-mega .dev-chip-tag { color: #d4b8f0; }
.dev-chip-mega .dev-chip-name { color: #f0d4ff; }
.dev-chip-mega:hover { border-color: #d4b8f0; box-shadow: 0 0 12px rgba(170,140,212,0.32) inset; }

.settings-flash {
  position: fixed;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  z-index: 320;
  padding: 8px 18px;
  background: rgba(10,8,8,0.92);
  color: var(--gold-pale);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  box-shadow: 0 8px 24px rgba(0,0,0,0.7), 0 0 20px rgba(232,220,196,0.15);
  animation: settings-flash 1.1s ease-out forwards;
  pointer-events: none;
}
@keyframes settings-flash {
  0%   { opacity: 0; transform: translate(-50%, -45%); }
  20%  { opacity: 1; transform: translate(-50%, -50%); }
  80%  { opacity: 1; }
  100% { opacity: 0; }
}

/* ===========================================================================
   FULL-BLEED TITLE SCREEN — JRPG / Slay-the-Spire style splash.
   Drawn outside #stage so it covers the entire viewport.  A hero silhouette
   stands in a dark abyss with floating ambient particles; title text floats
   at the top, menu links rest at the bottom.
   =========================================================================== */
#title-screen {
  position: fixed;
  inset: 0;
  z-index: 150;
  overflow: hidden;
  display: flex;
  align-items: stretch;
  justify-content: center;
  animation: ts-fade-in 0.6s ease-out;
}
#title-screen.hidden { display: none !important; }
/* While the title screen is up, suppress the in-game HUD strip — the
   menu button and sigil tray don't belong on the title, which has its
   own NEW GAME / CONTINUE / HEROES / CREDITS / SETTINGS rail. */
body.on-title #hud { display: none !important; }
@keyframes ts-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
/* Background: a deep void with a faint glow at the player's feet so the
   hero figure has somewhere to land. */
.ts-bg {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 60% 30% at 50% 88%,
                    rgba(80,52,40,0.20) 0%,
                    rgba(20,12,12,0.0) 75%),
    radial-gradient(ellipse 100% 80% at 50% 50%,
                    rgba(28,20,24,0.92) 0%,
                    rgba(6,4,6,1) 80%),
    #050405;
}
/* A second pass: subtle vertical streaks (the air of the abyss). */
.ts-bg::after {
  content: '';
  position: absolute; inset: 0;
  background:
    repeating-linear-gradient(180deg,
      transparent 0px,
      transparent 28px,
      rgba(232,220,196,0.012) 28px,
      rgba(232,220,196,0.012) 29px);
  mix-blend-mode: screen;
  pointer-events: none;
}
/* Drifting motes of light (ash, embers). */
.ts-particles {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1;
}
.ts-particles span {
  position: absolute;
  width: 2px; height: 2px;
  background: rgba(232,220,196,0.6);
  border-radius: 50%;
  box-shadow: 0 0 6px rgba(232,220,196,0.55);
  animation: ts-mote 14s linear infinite;
  opacity: 0;
}
.ts-particles span:nth-child(1)  { left: 12%; animation-delay:  0s; animation-duration: 12s; }
.ts-particles span:nth-child(2)  { left: 24%; animation-delay:  3s; animation-duration: 16s; }
.ts-particles span:nth-child(3)  { left: 38%; animation-delay:  6s; animation-duration: 13s; }
.ts-particles span:nth-child(4)  { left: 46%; animation-delay:  9s; animation-duration: 18s; }
.ts-particles span:nth-child(5)  { left: 54%; animation-delay:  1s; animation-duration: 15s; }
.ts-particles span:nth-child(6)  { left: 62%; animation-delay:  4s; animation-duration: 14s; }
.ts-particles span:nth-child(7)  { left: 70%; animation-delay:  7s; animation-duration: 17s; }
.ts-particles span:nth-child(8)  { left: 78%; animation-delay: 10s; animation-duration: 12s; }
.ts-particles span:nth-child(9)  { left: 86%; animation-delay:  2s; animation-duration: 16s; }
.ts-particles span:nth-child(10) { left: 92%; animation-delay:  5s; animation-duration: 13s; }
@keyframes ts-mote {
  0%   { transform: translateY(110vh) scale(0.8); opacity: 0; }
  10%  { opacity: 0.75; }
  90%  { opacity: 0.55; }
  100% { transform: translateY(-10vh)  scale(1.0); opacity: 0; }
}

/* Layout column — everything else stacks here, centered horizontally. */
.ts-content {
  position: relative;
  z-index: 2;
  flex: 1;
  display: grid;
  grid-template-rows: auto 1fr auto;
  align-items: stretch;
  width: 100%;
  max-width: 880px;
  padding: 22px 16px;
}
/* Title at the top — medium-weight cap, not huge. */
.ts-header {
  text-align: center;
}
.ts-title {
  font-size: 38px;
  letter-spacing: 0.42em;
  margin: 0;
  color: var(--gold-pale);
  font-weight: 400;
  text-shadow:
    0 0 22px rgba(240,224,182,0.4),
    0 0 4px  rgba(240,224,182,0.6),
    0 2px 0  rgba(0,0,0,0.8);
}
.ts-subtitle {
  margin: 4px 0 0;
  font-size: 13px;
  letter-spacing: 0.46em;
  text-transform: uppercase;
  color: var(--gold);
  opacity: 0.85;
}

/* Hero — silhouette standing in the abyss.  The SVG portrait scales
   to the available middle row, anchored to the bottom so the feet
   meet the floor-glow. */
.ts-hero {
  position: relative;
  align-self: end;
  justify-self: center;
  width: 100%;
  max-width: 320px;
  height: 100%;
  /* Subtle floor-glow under the figure */
}
.ts-hero::before {
  content: '';
  position: absolute;
  left: 50%; bottom: -2px;
  width: 200px; height: 36px;
  transform: translateX(-50%);
  background: radial-gradient(ellipse 60% 100% at 50% 50%,
                              rgba(240,224,182,0.18) 0%,
                              rgba(240,224,182,0) 75%);
  pointer-events: none;
}
.ts-hero svg {
  display: block;
  height: 100%;
  width: auto;
  max-width: 100%;
  margin: 0 auto;
  filter: drop-shadow(0 8px 28px rgba(0,0,0,0.85));
  animation: ts-hero-breath 4.6s ease-in-out infinite;
}
@keyframes ts-hero-breath {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-3px); }
}

/* Footer — slim menu links + a small unlocks line. */
.ts-footer {
  text-align: center;
}
.ts-menu {
  display: flex;
  justify-content: center;
  gap: 28px;
  flex-wrap: wrap;
}
.ts-menu-btn {
  background: transparent;
  border: none;
  color: var(--ink);
  font-family: inherit;
  font-size: 11.5px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  cursor: pointer;
  padding: 6px 4px;
  position: relative;
  transition: color 0.18s, text-shadow 0.18s;
}
.ts-menu-btn:hover:not(.disabled) {
  color: var(--gold-pale);
  text-shadow: 0 0 12px rgba(240,224,182,0.45);
}
.ts-menu-btn::after {
  content: '';
  position: absolute;
  left: 50%; bottom: 0;
  transform: translateX(-50%);
  width: 0; height: 1px;
  background: var(--gold-pale);
  transition: width 0.2s;
}
.ts-menu-btn:hover:not(.disabled)::after { width: 100%; }
.ts-menu-btn.disabled {
  opacity: 0.35;
  cursor: not-allowed;
}
.ts-meta {
  margin: 10px 0 0;
  font-size: 9.5px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--ink-dim);
  display: flex;
  flex-direction: column;
  gap: 3px;
  align-items: center;
}
.ts-meta .ts-meta-line { display: block; }
.ts-meta b { color: var(--gold-pale); }

/* Mobile fit */
@media (max-height: 480px) {
  .ts-content  { padding: 12px 12px; }
  .ts-title    { font-size: 28px; letter-spacing: 0.38em; }
  .ts-subtitle { font-size: 11px; letter-spacing: 0.42em; }
  .ts-hero     { max-width: 220px; }
  .ts-menu-btn { font-size: 10.5px; letter-spacing: 0.26em; padding: 4px 2px; }
  .ts-menu     { gap: 18px; }
  .ts-meta     { font-size: 9px; letter-spacing: 0.22em; margin-top: 6px; }
}
@media (max-height: 380px) {
  .ts-title    { font-size: 22px; letter-spacing: 0.32em; }
  .ts-subtitle { font-size: 10px; }
  .ts-hero     { max-width: 170px; }
}

/* ===========================================================================
   MOBILE FIT — comprehensive sweep to make sure every overlay holds
   inside a landscape phone (~430px tall) without internal scroll.
   Builds on the earlier per-overlay rules; these are the last-line
   overrides that win on tiny viewports.
   =========================================================================== */
@media (max-height: 480px) {
  /* Use the dynamic viewport so URL-bar collapse gives back real estate. */
  #overlay-content,
  #info-panel .ip-card,
  #confirm-modal .cm-card {
    max-height: 100dvh !important;
    padding: 10px 14px 10px !important;
  }

  /* Rest scene — shrink the campfire + heroes so the 3 choice buttons fit. */
  .rest-scene  { min-height: 110px !important; margin: 2px 0 6px !important; gap: 8px !important; }
  .rest-hero   { width: 52px !important; height: 90px !important; }
  .rest-fire   { width: 56px !important; height: 90px !important; }
  .rest-fire .ts-flame    { width: 22px !important; height: 36px !important; bottom: 8px !important; }
  .rest-fire .ts-flame.f2 { width: 16px !important; height: 26px !important; bottom: 12px !important; }
  .rest-fire .ts-flame.f3 { width: 10px !important; height: 18px !important; bottom: 16px !important; }
  .rest-fire .ts-fire-logs { width: 42px !important; bottom: 4px !important; }
  .rest-flavor { font-size: 11px !important; margin: 4px 0 6px !important; }

  /* Run summary — collapse the rs-card into compact rows. */
  .rs-card { gap: 6px !important; padding: 2px 4px 0 !important; }
  .rs-flavor { font-size: 11px !important; line-height: 1.4 !important; }
  .rs-stats { padding: 3px 0 !important; gap: 12px !important; }
  .rs-stat b { font-size: 14px !important; }
  .rs-stat em { font-size: 8.5px !important; letter-spacing: 0.18em !important; }
  .rs-rows .vs-row { padding: 2px 4px !important; font-size: 10.5px !important; }
  .rs-rows .vs-name { font-size: 9.5px !important; letter-spacing: 0.1em !important; }
  .rs-unlocks-chip { font-size: 9.5px !important; padding: 1px 6px !important; }

  /* Info panel — heroes and sigils tighten so 3+ heroes still fit. */
  #info-panel .ip-card { padding: 10px 14px 12px !important; }
  .ip-title { font-size: 12px !important; letter-spacing: 0.22em !important; }
  .info-biome { margin: 0 0 6px !important; padding: 4px 8px !important; }
  .info-biome-name { font-size: 11px !important; }
  .info-biome-desc { font-size: 9.5px !important; }
  .info-section { margin: 4px 0 !important; }
  .info-label { font-size: 8.5px !important; }
  .info-hero { padding: 3px 6px !important; grid-template-columns: 38px 1fr !important; gap: 8px !important; }
  .info-hero-portrait { width: 38px !important; height: 50px !important; }
  .info-hero-name { font-size: 10px !important; letter-spacing: 0.14em !important; }
  .info-hero-hp { font-size: 9.5px !important; }
  .info-hero-quirks { gap: 2px !important; }
  .info-hero-quirks .hero-quirk { font-size: 9px !important; padding: 1px 4px !important; }

  /* Recruit + Upgrade + Sigil overlay choices — slim cards stacked. */
  #overlay.overlay-recruit #overlay-choices,
  #overlay.overlay-upgrade #overlay-choices,
  #overlay.overlay-sigil   #overlay-choices {
    gap: 4px !important;
  }
  #overlay.overlay-recruit .encounter-choice,
  #overlay.overlay-upgrade .encounter-choice,
  #overlay.overlay-sigil   .encounter-choice {
    padding: 4px 8px !important;
  }
  .recruit-choice .recruit-portrait { height: 50px !important; }
  .recruit-choice .recruit-portrait svg { max-height: 50px !important; }
  .recruit-choice .enc-name { font-size: 11px !important; }
  .recruit-choice .recruit-title { font-size: 9.5px !important; }
  .recruit-choice .recruit-stats { gap: 4px !important; font-size: 9.5px !important; }
  .recruit-choice .recruit-passive { font-size: 9.5px !important; }
  .upgrade-choice { width: 220px !important; padding: 10px 12px !important; gap: 6px !important; }
  .upgrade-choice .upgrade-avatar { width: 26px !important; height: 36px !important; }
  .upgrade-choice .upgrade-meta-label { font-size: 8.5px !important; letter-spacing: 0.18em !important; }
  .upgrade-choice .upgrade-title { font-size: 12px !important; padding: 2px 0 4px !important; }
  .upgrade-choice .upgrade-from-name { font-size: 9px !important; }
  .upgrade-choice .upgrade-from-desc { font-size: 8.5px !important; }
  .upgrade-choice .upgrade-to-desc { font-size: 9.5px !important; }
  .upgrade-choice .upgrade-chevron { font-size: 18px !important; padding: 0 2px !important; }
  .sigil-choice .enc-name { font-size: 11px !important; }
  .sigil-choice .sigil-desc { font-size: 9.5px !important; }
  .sigil-choice .sigil-glyph { font-size: 16px !important; }

  /* Resonance Rail — slimmer chips when combat is tight. */
  .resonance-chip { padding: 4px 8px !important; }
  .rc-label { font-size: 10.5px !important; letter-spacing: 0.14em !important; }
  .rc-desc { font-size: 9px !important; }
  .rc-tier { font-size: 8px !important; padding: 1px 4px !important; }

  /* Boss intro slightly shorter so the reveal beat doesn't overflow. */
  .bi-name { font-size: 28px !important; letter-spacing: 0.34em !important; }
  .bi-eyebrow { font-size: 9.5px !important; letter-spacing: 0.4em !important; }
  .bi-tag { font-size: 10.5px !important; }
}

@media (max-height: 380px) {
  /* Extra-tight phones in landscape: deeper compression on the same panels. */
  #overlay-content,
  #info-panel .ip-card,
  #confirm-modal .cm-card {
    padding: 6px 12px 6px !important;
  }
  .rest-scene  { min-height: 90px !important; }
  .rest-hero   { width: 44px !important; height: 76px !important; }
  .rest-fire   { width: 48px !important; height: 76px !important; }
  .rest-flavor { font-size: 10.5px !important; margin: 2px 0 4px !important; }
  .rs-stat b { font-size: 13px !important; }
  .info-hero { grid-template-columns: 32px 1fr !important; }
  .info-hero-portrait { width: 32px !important; height: 42px !important; }
  .bi-name { font-size: 22px !important; }
}

/* ===========================================================================
   FORGE + OATH MODAL — reuses the wanderer modal frame; only the stage
   glyph + tinting are forge/oath specific.
   =========================================================================== */
.forge-stage {
  height: clamp(70px, 16vh, 110px);
  display: flex; align-items: center; justify-content: center;
  margin: 4px auto 6px;
}
.forge-anvil {
  font-size: clamp(36px, 8vh, 56px);
  color: #e89858;
  text-shadow:
    0 0 18px rgba(232,152,88,0.55),
    0 0 36px rgba(232,152,88,0.25),
    0 2px 0 rgba(0,0,0,0.7);
  filter: drop-shadow(0 6px 14px rgba(80,30,0,0.6));
  animation: forge-anvil-pulse 2.2s ease-in-out infinite;
}
@keyframes forge-anvil-pulse {
  0%, 100% { filter: drop-shadow(0 6px 14px rgba(80,30,0,0.6)); }
  50%      { filter: drop-shadow(0 8px 18px rgba(232,152,88,0.7)); }
}
.oath-stage .forge-anvil {
  color: rgba(212,90,90,0.78);
  text-shadow:
    0 0 18px rgba(220,80,80,0.55),
    0 0 36px rgba(220,80,80,0.25),
    0 2px 0 rgba(0,0,0,0.7);
}

/* ===========================================================================
   HEROIC BOON CINEMATIC STAGE — shown above the three boon choices after
   a mega-boss falls.  The boss's silhouette stands felled in the back of
   the frame, desaturated, while a gold aura + crowned gift glyph float
   in the foreground — the gift the megaboss left behind.
   =========================================================================== */
.boon-stage {
  position: relative;
  width: 100%;
  height: clamp(160px, 50%, 220px);
  margin: 0 0 4px;
  overflow: hidden;
  display: flex;
  align-items: end;
  justify-content: center;
}
.boon-stage-bg {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 90% 60% at 50% 100%,
                    rgba(120,80,32,0.4) 0%,
                    rgba(0,0,0,0) 65%),
    radial-gradient(ellipse 120% 110% at 50% 45%,
                    rgba(20,16,18,0.7) 0%,
                    rgba(6,4,6,0.95) 78%);
  pointer-events: none;
  z-index: 0;
}
/* Soft god-rays fanning down from above — sells "gift descending" */
.boon-stage-rays {
  position: absolute;
  inset: -8% 0 0 0;
  background:
    conic-gradient(from 270deg at 50% 12%,
      transparent 0deg,
      rgba(240,212,136,0) 88deg,
      rgba(240,212,136,0.20) 90deg,
      rgba(240,212,136,0) 92deg,
      transparent 180deg,
      rgba(240,212,136,0) 268deg,
      rgba(240,212,136,0.14) 270deg,
      rgba(240,212,136,0) 272deg,
      transparent 360deg);
  -webkit-mask-image: linear-gradient(180deg, rgba(0,0,0,0.85) 0%, transparent 60%);
          mask-image: linear-gradient(180deg, rgba(0,0,0,0.85) 0%, transparent 60%);
  mix-blend-mode: screen;
  pointer-events: none;
  z-index: 1;
  animation: boon-rays-pulse 3.6s ease-in-out infinite;
}
@keyframes boon-rays-pulse {
  0%, 100% { opacity: 0.55; }
  50%      { opacity: 0.95; }
}
/* Warm aura puddle on the floor where the boss collapsed */
.boon-stage-aura {
  position: absolute;
  left: 50%; bottom: 4%;
  transform: translateX(-50%);
  width: 60%;
  height: 22%;
  background: radial-gradient(ellipse 50% 50% at 50% 50%,
                              rgba(240,212,136,0.45) 0%,
                              rgba(240,212,136,0.18) 40%,
                              rgba(240,212,136,0) 80%);
  pointer-events: none;
  z-index: 2;
  animation: boon-aura-pulse 2.8s ease-in-out infinite;
}
@keyframes boon-aura-pulse {
  0%, 100% { opacity: 0.7; transform: translateX(-50%) scale(1); }
  50%      { opacity: 1;   transform: translateX(-50%) scale(1.06); }
}
/* Felled megaboss silhouette — desaturated and dimmed, leaning back so
   the player reads "defeated" without us having to draw a corpse pose. */
.boon-stage-fallen {
  position: relative;
  z-index: 3;
  height: 78%;
  width: auto;
  display: flex;
  align-items: end;
  justify-content: center;
}
.boon-stage-fallen svg {
  height: 100%;
  width: auto;
  max-width: 100%;
  filter: grayscale(0.85) brightness(0.42) drop-shadow(0 10px 22px rgba(0,0,0,0.85));
  /* slight backward tilt sells "fallen" without changing the figure art */
  transform: rotate(-4deg) translateY(4%);
  opacity: 0.92;
}
.boon-stage-fallen:empty {
  /* Fallback when no portrait is known — surface a hollow silver outline
     so the stage still reads as a scene instead of a blank panel. */
  width: 90px;
  height: 78%;
  background:
    radial-gradient(ellipse 70% 90% at 50% 60%,
                    rgba(120,108,140,0.22) 0%,
                    rgba(40,32,52,0) 75%);
  border-bottom: 1px solid rgba(212,200,168,0.18);
}
/* The gift — a small crowned glyph hovering in front of the felled boss */
.boon-stage-gift {
  position: absolute;
  left: 50%;
  bottom: 30%;
  transform: translateX(-50%);
  width: 44px;
  height: 44px;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
  animation: boon-gift-float 3.4s ease-in-out infinite;
}
@keyframes boon-gift-float {
  0%, 100% { transform: translateX(-50%) translateY(0); }
  50%      { transform: translateX(-50%) translateY(-6px); }
}
.boon-gift-ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 1px solid rgba(240,212,136,0.7);
  box-shadow:
    0 0 14px rgba(240,212,136,0.6),
    inset 0 0 8px rgba(240,212,136,0.35);
  animation: boon-gift-ring-pulse 2s ease-in-out infinite;
}
@keyframes boon-gift-ring-pulse {
  0%, 100% { transform: scale(1);    opacity: 0.85; }
  50%      { transform: scale(1.18); opacity: 0.45; }
}
.boon-gift-core {
  position: relative;
  font-size: 18px;
  color: var(--gold-pale);
  text-shadow:
    0 0 8px rgba(255,240,200,0.95),
    0 0 16px rgba(240,212,136,0.7);
  z-index: 1;
}
/* Subtitle pinned to the floor of the stage — "<Boss> falls." */
.boon-stage-caption {
  position: absolute;
  left: 0; right: 0;
  bottom: 1%;
  text-align: center;
  z-index: 5;
  font-size: 9.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-dim);
  font-style: italic;
  pointer-events: none;
  text-shadow: 0 1px 4px rgba(0,0,0,0.95);
}

/* Heroic Boon overlay — uses overlay-boon class.  Cards are slim event
   rows with a brighter gold tone to signal the rarity of the pick. */
#overlay.overlay-boon .encounter-choice.boon-choice {
  border-color: rgba(232,200,160,0.55);
  box-shadow: 0 0 0 1px rgba(0,0,0,0.4) inset, 0 0 18px rgba(232,200,160,0.18);
}
#overlay.overlay-boon .encounter-choice.boon-choice .enc-name {
  color: var(--gold-bright);
  text-shadow: 0 0 12px rgba(240,212,136,0.45);
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

/* Mega-boss intro variant — red eyebrow, larger name, animated underline
   so the milestone reads as a genuine wall-stop. */
#boss-intro.bi-megaboss .bi-eyebrow {
  color: var(--blood-bright);
  text-shadow: 0 0 14px rgba(220,80,80,0.6);
  letter-spacing: 0.48em;
}
#boss-intro.bi-megaboss .bi-name {
  font-size: 48px;
  text-shadow:
    0 0 28px rgba(220,40,40,0.45),
    0 2px 0 rgba(0,0,0,0.95);
  position: relative;
}
#boss-intro.bi-megaboss .bi-name::after {
  content: '';
  display: block;
  margin: 12px auto 0;
  width: 60%;
  height: 2px;
  background: linear-gradient(90deg, transparent 0%, var(--blood-bright) 50%, transparent 100%);
  animation: bi-megaboss-underline 2.2s ease-in-out infinite;
}
@keyframes bi-megaboss-underline {
  0%, 100% { opacity: 0.45; transform: scaleX(0.85); }
  50%      { opacity: 1;    transform: scaleX(1); }
}

/* Mastery — small gold crown chip in the inspector + figure overlay
   so the player can see at a glance who has awakened. */
.fi-row-mastery .fi-icon { color: var(--gold-bright); text-shadow: 0 0 8px rgba(240,212,136,0.6); }
.fi-row-mastery .fi-text b { color: var(--gold-bright); letter-spacing: 0.08em; }
