/* ===================================================================
   RiftBonk — Modern board (visual MVP)
   Experimental. Legacy (/) is untouched and remains the default.

   COMPOSITION
   One continuous illustrated stage, not stacked website sections. The
   stage is a fixed 16:9 box (the generated board-table plate is 1672x941),
   so every region can be placed in stable percentage coordinates the way a
   game UI is built. Narrow screens drop to a stacked flow that still shows
   every battlefield.

   ASSET GEOMETRY — measured by alpha-scanning the pack, not eyeballed:
     unit-frame opening  x 9.73%..90.19%   y 10.05%..88.44%
     unit-frame medallion centre (86.0%, 84.1%), diameter ~18%
   Card scans are 744x1039; the square portrait crop is
     x 17%..83%, y 3.85%..51%  ->  img width 151.5%, offset -25.8% / -8.1%

   CSP: style-src 'self', no unsafe-inline. No <style> blocks, no style=""
   attributes; dynamic values come from element.style.setProperty(), which
   CSSOM permits. No external fonts. Art is same-origin under
   /modern/assets/generated-v1/.

   PERFORMANCE: only transform/opacity are animated. No filter on any
   container (it would create a containing block and break the fixed-position
   damage numerals and the absolutely positioned VFX canvas).
   =================================================================== */

:root {
  color-scheme: dark;

  /* Sampled from the art pack itself rather than chosen: the stone in every
     one of those images is hue 210-220 at ~12% saturation, the gold is hue 35
     at 50-67%, and the gems are hue 215 at 83%. The old palette was hue-160
     green stone with hue-43 yellow gold, which is why the board and the cards
     never looked like one object. */
  --stone-0: #0d1114;
  --stone-1: #1b1d1f;
  --stone-2: #2b2f33;
  --slate:   #373b3f;

  --brass:       #7f5f3f;
  --gold:        #bf8a3f;
  --gold-bright: #e9bc61;
  --gold-dim:    #6a4e28;
  --rule:        #3a3226;

  --sapphire:        #1f62bf;
  --sapphire-bright: #4f9bff;
  --sapphire-deep:   #0f1f3f;

  --text:  #f3ead6;
  --muted: #a8a293;
  --faint: #7d7666;

  /* Ownership. Yours is the pack's own sapphire and the enemy stays ember:
     the same pairing Runeterra uses, and the two hues furthest apart on the
     board. Gold is reserved for FURNITURE, so it can never be mistaken for a
     side. */
  --you:    #4f9bff;
  --opp:    #ff6a54;
  --danger: #e8483c;
  --shield: #a8d1ff;
  /* What you may act on. Gold-white, because that is how the art itself
     highlights, and it reads against painted battlefield scenes where both
     ownership colours are already in play. */
  --pick:   #ffe9a8;

  --fury:  #e2493a;
  --calm:  #3fae63;
  --mind:  #4aa3ec;
  --body:  #e08b2a;
  --chaos: #b05fe0;
  --order: #e6c341;
  --any:   #d9cdb4;
  --energy:#4aa3ec;

  --ink: 212 26% 4%;
  --elev-rest:
    0 1px 0 hsl(var(--ink) / .8),
    0 2px 5px hsl(var(--ink) / .55),
    0 8px 16px -6px hsl(var(--ink) / .65);
  --elev-hover:
    0 2px 3px hsl(var(--ink) / .6),
    0 14px 26px -8px hsl(var(--ink) / .72),
    0 30px 50px -18px hsl(var(--ink) / .66);
  --plaque:
    inset 0 1px 0 #e9bc613d,
    inset 0 -1px 0 hsl(var(--ink) / .85),
    0 2px 6px -2px hsl(var(--ink) / .85);

  --ease-lift: cubic-bezier(.2, .75, .28, 1);
  --ease-pop:  cubic-bezier(.18, .9, .26, 1.18);

  --display: "Palatino Linotype", Palatino, Constantia, Georgia, ui-serif, serif;
  --ui: system-ui, "Segoe UI", -apple-system, sans-serif;
  --num: "Bahnschrift", "Segoe UI", system-ui, sans-serif;
  --stat: "Arial Black", "Franklin Gothic Heavy", "Segoe UI", system-ui, sans-serif;

  /* Square portrait crop out of a full 744x1039 card scan, biased to the
     upper-centre where these illustrations put the character's head:
     x 22%..78%, y 4%..44%. Tighter than the full art window on purpose —
     the board needs a recognisable face, not a landscape. */
  --por-w: 178.6%;
  --por-x: -39.3%;
  --por-y: -10%;

  --art: url("/modern/assets/generated-v1/");
}

* { box-sizing: border-box; min-width: 0; }
[hidden] { display: none !important; }
/* `body { height: 100% }` would cap the page at the viewport, so the stacked
   narrow layout would overflow invisibly and scroll nowhere. */
html { height: 100%; }

body {
  margin: 0;
  font: 14px/1.45 var(--ui);
  color: var(--text);
  background:
    radial-gradient(120% 90% at 50% 0%, #10151a, #05080a 70%);
  display: grid;
  grid-template-rows: auto auto 1fr auto;
  min-height: 100dvh;
  overflow-x: hidden;
}

button, select { font: inherit; color: inherit; }
button { cursor: pointer; border: 0; background: none; padding: 0; }
h2 { font-size: inherit; font-weight: 600; margin: 0; }
p { margin: 0; }
.vh { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
:focus-visible { outline: 2px solid var(--pick); outline-offset: 2px; }

.num, .might__n, .pile b, .res__n, .hud__points, .hud__xp b {
  font-family: var(--num); font-variant-numeric: tabular-nums lining-nums;
}

/* ---------------------------------------------------- dev chrome ---- */
#devbar {
  display: flex; align-items: center; gap: 12px;
  padding: 5px 14px; font-size: 11px;
  background: #080b0e; border-bottom: 1px solid #1b1f24;
}
.devbar__brand { font-family: var(--display); letter-spacing: 2px; color: var(--gold); font-weight: 700; }
.devbar__brand small { font-size: 9px; color: var(--faint); letter-spacing: 1.5px; }
#devbar select {
  appearance: none; padding: 4px 24px 4px 9px; border-radius: 5px; font-size: 11px;
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9' height='6'%3E%3Cpath d='M1 1l3.5 3.5L8 1' fill='none' stroke='%23aba08a' stroke-width='1.5'/%3E%3C/svg%3E") no-repeat right 8px center,
    #161b20;
  box-shadow: inset 0 0 0 1px #2a3036;
}
.devbar__play {
  min-height: 30px;
  padding: 4px 12px;
  border: 1px solid #c6a95d;
  border-radius: 5px;
  color: #20160a;
  background: linear-gradient(180deg, #f1d88d, #bf9342);
  box-shadow: inset 0 1px 0 #fff3, 0 2px 8px #0007;
  font-size: 12px;
  font-weight: 800;
  white-space: nowrap;
}
/* Temporarily hide the top Play shortcut, including after room transitions. */
#devbar #start-match { display: none !important; }
.devbar__play:hover { filter: brightness(1.08); }
/* Secondary entry: the guided lesson, which deliberately keeps manual
   step-through pacing. Quieter than the primary Start match button. */
.devbar__lesson {
  min-height: 30px;
  padding: 4px 12px;
  border: 1px solid #4a515a;
  border-radius: 5px;
  color: var(--muted);
  background: linear-gradient(180deg, #222930, #161b20);
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}
.devbar__lesson:hover { color: var(--gold-bright); border-color: var(--gold-dim); }
.devbar__lesson:disabled { cursor: wait; opacity: .5; }
.devbar__play:disabled {
  cursor: wait;
  color: #8f8778;
  border-color: #5f5749;
  background: #2a2822;
  box-shadow: none;
}
.devbar__legacy { margin-left: auto; color: var(--muted); text-decoration: none; font-size: 11px; }
.devbar__legacy:hover { color: var(--gold-bright); }

.banner {
  padding: 4px 14px; font-size: 11px; font-weight: 600; color: #1a1206;
  background: linear-gradient(180deg, #d8b866, #b8933f);
}

.orientation-note {
  display: none;
  padding: 8px 14px;
  font-size: 12px;
  line-height: 1.35;
  color: #f2e5c8;
  background: #101418;
  border-bottom: 1px solid #342d20;
}

/* ===================================================================
   THE STAGE — one continuous illustrated board
   =================================================================== */
#stage {
  position: relative;
  /* Hoisted so the font can size off the stage's REAL width. `1.4cqw` here was
     a trap: an element is never its own query container, so cqw on #stage
     resolved against the VIEWPORT, not the stage — 2.17% instead of 1.4% on a
     phone. Children still use cqw correctly; only this element cannot. */
  --chrome: 92px;
  --stage-w: min(
    calc(100vw - env(safe-area-inset-left, 0px) - env(safe-area-inset-right, 0px)),
    calc((100dvh - var(--chrome)) * 16 / 9)
  );
  width: var(--stage-w);
  aspect-ratio: 1672 / 941;
  margin: 6px auto;
  container-type: size;
  container-name: stage;
  isolation: isolate;
  border-radius: 6px;
  overflow: hidden;
  background: #10151a url("/modern/assets/generated-v2/board-table.webp") center/100% 100% no-repeat;
  box-shadow: 0 24px 70px -24px #000, 0 0 0 1px #000;
  /* every child sizes off the stage, so the whole composition scales as one */
  font-size: clamp(12px, calc(var(--stage-w) * 0.0156), 28px);
}

/* generic stone plaque used by HUDs, shelves and resource panels */
.plaque {
  border-radius: .5cqw;
  background: linear-gradient(180deg, #1d2227e0, #0d1014e8);
  box-shadow: var(--plaque);
}

/* ------------------------------------------------- region placement */
/* The composition follows the concept board, not the old dashboard:
     top-left     one block  - rival identity, legend/champion, then resources
     top-centre   rival hand of card backs, and the rival base directly under it
     middle       the battlefields, all of them, side by side
     bottom-centre your base as a band, and your hand fanned below it
     bottom-left  the same one block for you
     right        a single command rail - piles, the round button, turn pips
   Everything is stated left-to-right, top-to-bottom in that order below. */

/* --- top left: one block, identity over resources --- */
.hud--opp   { position: absolute; left: .8%;  top: 1.2%;  width: 31%; }
.res--opp   { position: absolute; left: .8%;  top: 15.5%; width: 31%; right: auto; max-width: none; }

/* --- top centre: the rival's hand, and their base beneath it --- */
.ophand     { position: absolute; left: 35%;  right: 35%; top: 1%;    height: 10%; }
.shelf--opp { position: absolute; left: 32%;  top: 12%;   width: 36%; height: 13%; }

/* --- middle: every battlefield, side by side --- */
.fields     { position: absolute; left: .8%;  right: 19.5%; top: 26%; height: 39%; }

/* --- bottom centre: your base, then your hand --- */
.shelf--you { position: absolute; left: 32%;  top: 66%;   width: 36%; height: 15%; }
.handzone   { position: absolute; left: 30%;  right: 21%; bottom: .5%; height: 18%; }

/* --- bottom left: the same block, mirrored --- */
.hud--you   { position: absolute; left: .8%;  top: 65.5%; width: 31%; }
.res--you   { position: absolute; left: .8%;  top: 80%;   width: 31%; max-width: none; }

/* --- right: one command rail --- */
.piles--opp { position: absolute; right: 1%;  top: 1.2%;   width: 18%; }
.action     { position: absolute; right: 3.2%; top: 32%;    width: 13%; }
.piles--you { position: absolute; right: 1%;  bottom: 1.5%; width: 18%; }

/* The chain has no place in the concept, and floating it over the middle put
   it straight on top of a battlefield. It lives in the rail's own free band
   instead -- between the turn pips and the lower piles -- where it can appear
   and vanish without moving or covering anything. */
.chain      { position: absolute; right: 1%; top: 61%; width: 18%;
              max-height: 21%; overflow: auto; z-index: 40; }
.chain {
  border: .9cqw solid transparent;
  border-image: url("/modern/assets/generated-v2/frame-panel.webp") 150 116 147 119 fill
                / 2.5cqw 1.9cqw 2.4cqw 2cqw / 0 stretch;
}

/* ===================================================================
   Identity HUD — circular portrait, name plaque, points, legend/champion
   =================================================================== */
.hud { display: flex; align-items: flex-start; gap: .7cqw; }

.hud__who { position: relative; width: 21%; flex: 0 0 auto; text-align: center; }
/* The pack's portrait ring, laid over the legend's own artwork. It is a fixed
   circular piece, so it only ever scales uniformly. */
/* The pack's portrait ring. Its interior is an opaque backing disc, not a hole,
   so it goes BEHIND the portrait and the portrait is fitted to its opening --
   measured at radius 112 of the asset's 162, so 69% across. Laid on top it
   simply covered the face. */
/* Points and XP sat straight on the painted board and washed out against it.
   The concept puts them on a plate; so does this. */
.hud__stats {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .1cqw; padding: .2cqw .7cqw .25cqw; border-radius: .35cqw;
  background: linear-gradient(180deg, #212830e8, #0d1114f2);
  box-shadow: inset 0 0 0 .07cqw var(--gold-dim), inset 0 1px 0 #e9bc6130;
}
.hud__portrait {
  position: relative; display: block; width: 100%; aspect-ratio: 1;
  padding: 0; overflow: visible; border-radius: 50%;
  background: url("/modern/assets/generated-v3/hud-portrait.webp") center/contain no-repeat;
  filter: drop-shadow(0 .2cqw .5cqw hsl(var(--ink) / .9));
}
/* Fitted to the ring's opening and cropped to the face by object-position,
   rather than by the --por-* margins, which assume a clipping parent. */
.hud__portrait img {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 69%; height: 69%; margin: 0;
  border-radius: 50%; object-fit: cover; object-position: 50% 24%;
  display: block;
}
.hud__portrait--empty { display: grid; place-items: center; color: var(--faint); font-size: .9em; }
.hud__name {
  display: block; margin-top: -.5cqw; position: relative;
  padding: .12cqw .3cqw; border-radius: .25cqw;
  font-family: var(--display); font-size: .8em; font-weight: 700;
  color: var(--gold-bright);
  width: 132%; margin-inline: -16%;
  background: linear-gradient(180deg, #2a2318, #14100a);
  box-shadow: var(--plaque);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.hud__stats { flex: 0 0 auto; padding-top: .3cqw; }
.hud__points {
  display: flex; align-items: center; gap: .28cqw;
  font-family: var(--num); font-size: 1.25em; font-weight: 700; color: var(--text);
}
.hud__points::before {
  content: ""; width: 1.1em; height: 1.1em; flex: 0 0 auto;
  background: radial-gradient(circle at 36% 30%, #ffd9a0, #e0662a 55%, #7d2a10);
  clip-path: polygon(50% 0, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%);
  box-shadow: 0 0 .4cqw #e0662a66;
}
.hud__xp { font-size: .82em; letter-spacing: .12em; color: var(--muted); text-transform: uppercase; }
.hud__xp b { color: var(--gold-bright); font-size: 1.15em; }

.hud__slots { display: flex; gap: .45cqw; margin-left: auto; align-items: flex-start; }
.slot { width: 4.9cqw; flex: 0 0 auto; text-align: center; }
.hud__slots {
  padding: .38cqw .35cqw .35cqw;
  border-radius: .4cqw;
  background: linear-gradient(180deg, #212830cc, #0d1114dd);
  box-shadow: inset 0 0 0 .07cqw var(--gold-dim), inset 0 1px 0 #e9bc6130,
              0 .2cqw .5cqw -.2cqw hsl(var(--ink) / .9);
}
.slot__label {
  display: block; font-size: .58em; letter-spacing: .1em; text-transform: uppercase;
  color: var(--faint); margin-bottom: .15cqw;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.slot__card {
  display: block; width: 100%; aspect-ratio: 1; padding: 0;
  border-radius: .2cqw; overflow: hidden; background: #0a0d10;
  box-shadow: inset 0 0 0 .1cqw var(--brass), 0 .15cqw .4cqw hsl(var(--ink) / .8);
}
.slot__card img { display: block; width: var(--por-w); margin-left: var(--por-x); margin-top: var(--por-y); }
.slot__card--empty {
  display: grid; place-items: center;
  box-shadow: inset 0 0 0 .1cqw #3c332766;
}
.slot__card--empty::after { content: ""; width: 40%; height: .12cqw; background: #4a4234; }
.slot__card:hover { box-shadow: inset 0 0 0 .1cqw var(--gold), 0 .2cqw .5cqw hsl(var(--ink) / .9); }

/* ===================================================================
   Resources — runes as hex gems, Energy and Power kept distinct
   =================================================================== */
/* The concept gives each resource its own bordered cell -- RUNES with its pips,
   then ENERGY, then POWER -- instead of one long strip of labels. */
.res { display: flex; align-items: stretch; gap: .45cqw; }
/* label above its gems, so long labels never collide with the gem row */
.res__group {
  display: flex; flex-direction: column; align-items: center; gap: .14cqw;
  padding: .22cqw .7cqw .3cqw; border-radius: .35cqw;
  background: linear-gradient(180deg, #212830e8, #0d1114f2);
  box-shadow: inset 0 0 0 .07cqw var(--gold-dim), inset 0 1px 0 #e9bc6130,
              0 .2cqw .5cqw -.2cqw hsl(var(--ink) / .9);
}
.res__label {
  font-size: .6em; letter-spacing: .13em; text-transform: uppercase; color: var(--faint);
  white-space: nowrap;
}
.res__n { font-size: 1.15em; font-weight: 700; color: var(--gold-bright); line-height: 1.1; }
.res:has(.res__group--restricted) { flex-wrap: wrap; }
.res__group--restricted { max-width: 15em; }
.res__balance { font-size: .8em; color: var(--gold-bright); }
.res__restriction { font-size: .7em; line-height: 1.2; text-align: center; }
#hand .hand-drop-before { box-shadow: -4px 0 0 var(--gold-bright); }
#hand .hand-drop-after { box-shadow: 4px 0 0 var(--gold-bright); }

.res__row { display: flex; align-items: center; gap: .18cqw; min-height: 1.7cqw; }

/* rune gems */
.runes { display: flex; gap: .18cqw; }
/* The pack paints a lit rune and an empty socket; the CSS hexagons that stood
   in for them are gone. Sized from the art's own aspect so nothing squashes. */
.rune {
  width: .95cqw; aspect-ratio: 313/326; height: auto;
  background: var(--rune-art) center/contain no-repeat;
  filter: drop-shadow(0 0 .3cqw color-mix(in srgb, var(--gem, var(--any)) 55%, transparent));
  --rune-art: url("/modern/assets/generated-v3/rune-ready.webp");
}
.rune--spent {
  --rune-art: url("/modern/assets/generated-v3/rune-empty.webp");
  filter: none; opacity: .85;
}

/* a single gem carrying a number: energy and each power domain */
/* a single gem carrying a number: energy and each power domain */
.gem {
  display: inline-grid; place-items: center;
  width: 1.75cqw; aspect-ratio: 227/260; height: auto;
  background: var(--gem-art, none) center/contain no-repeat;
  font-family: var(--stat); font-size: .86em; color: #fff6dd;
  text-shadow: 0 .06cqw .12cqw #000, 0 0 .3cqw #000;
}
/* An empty gem keeps its painted medallion and simply loses its light, rather
   than being covered by a grey box. */
.gem--empty {
  filter: grayscale(.85) brightness(.6);
  color: #9aa0a6;
}

/* When the decision tray is open the hand yields room rather than being
   covered — the rightmost cards must stay visible and clickable. */
/* The tray is a decision surface, not a region: it no longer moves the hand. */

/* ===================================================================
   Pile columns
   =================================================================== */
/* The deck is a stack of face-down cards, so it shows one, with two plates
   offset behind it. */
.pile--deck { position: relative; }
.pile__back {
  /* An explicit width, not a percentage: the pile box is auto-width, so a
     percentage resolved against a box that the image itself was sizing, and
     the column came out 263px wide on a phone -- straight through the
     resource strip and the rival's hand. */
  display: block; width: 2.4cqw; aspect-ratio: 744/1039;
  border-radius: 8%;
  box-shadow:
    .09cqw -.09cqw 0 -.02cqw #0c1114, .09cqw -.09cqw 0 0 var(--gold-dim),
    .2cqw -.2cqw 0 -.02cqw #0c1114, .2cqw -.2cqw 0 0 var(--gold-dim);
}
.pile--empty .pile__back { opacity: .25; filter: grayscale(1); }

/* One rail, read as the concept draws it: two labelled chips and a deck card. */
.piles { display: flex; flex-direction: row; align-items: stretch; justify-content: flex-end; gap: .5cqw; }
.pile--chips { display: flex; flex-direction: row; gap: .5cqw; padding: .3cqw; }
.pilechip {
  display: grid; place-items: center; gap: 0;
  min-width: 4.2cqw; padding: .2cqw .35cqw; border-radius: .3cqw;
  font-family: var(--num); color: var(--text);
}
.pilechip .pile__icon { width: 1.5cqw; height: 1.5cqw; opacity: .85; }
.pilechip b { font-size: 1.15em; line-height: 1.1; }
.pilechip small, .pile--deck small {
  font-family: var(--ui); font-size: .58em; letter-spacing: .12em;
  text-transform: uppercase; color: var(--faint);
}
.pilechip:hover { color: var(--gold-bright); }
.pile--deck { display: grid; place-items: center; padding: .35cqw .5cqw; }
.pile--deck .pile__back { width: 3.4cqw; }
.pile {
  display: grid; place-items: center; gap: 0;
  padding: .3cqw .25cqw;
  border-radius: .35cqw;
  background: linear-gradient(180deg, #212830e6, #0c0f12f0);
  box-shadow: inset 0 0 0 .07cqw var(--gold-dim), inset 0 1px 0 #e9bc6130,
              0 .2cqw .5cqw -.2cqw hsl(var(--ink) / .9);
}
/* the deck: a stack of cards with its count struck on it */
.pile--deck { position: relative; padding: .35cqw .3cqw; }
.pile__n {
  position: absolute; left: 50%; top: 46%; transform: translate(-50%, -50%);
  font-family: var(--num); font-size: 1.05em; line-height: 1; color: #fff2cf;
  text-shadow: 0 .1cqw .18cqw #000, 0 0 .4cqw #000;
}
/* trash and banished share one box as chips, not a labelled panel each */
.pile--minor { display: flex; gap: .35cqw; padding: .25cqw .3cqw; }
.pilechip {
  display: flex; align-items: center; gap: .18cqw;
  padding: .1cqw .2cqw; border-radius: .25cqw;
  font-family: var(--num); font-size: .8em; color: var(--muted);
}
.pilechip:hover { color: var(--gold-bright); }
.pilechip .pile__icon { width: 1.6cqw; height: 1.6cqw; }/* the deck: a stack of cards with its count struck on it */
.pile--deck { position: relative; padding: .35cqw .3cqw; }
.pile__n {
  position: absolute; left: 50%; top: 58%; transform: translate(-50%, -50%);
  font-family: var(--num); font-size: 1.05em; line-height: 1; color: #fff2cf;
  text-shadow: 0 .1cqw .18cqw #000, 0 0 .4cqw #000;
}
/* trash and banished share one box as chips, not a labelled panel each */
.pile--minor { display: flex; gap: .35cqw; padding: .25cqw .3cqw; }
.pilechip {
  display: flex; align-items: center; gap: .18cqw;
  padding: .1cqw .2cqw; border-radius: .25cqw;
  font-family: var(--num); font-size: .8em; color: var(--muted);
}
.pilechip:hover { color: var(--gold-bright); }
.pilechip .pile__icon { width: 1.1cqw; height: 1.1cqw; }
.pile__icon { width: 1.5cqw; height: 1.5cqw; opacity: .75; }
.pile b { font-size: 1.15em; font-weight: 700; color: var(--text); line-height: 1.1; }
.pile small { font-size: .6em; letter-spacing: .12em; text-transform: uppercase; color: var(--faint); }
.pile--clickable:hover { box-shadow: var(--plaque), inset 0 0 0 .1cqw var(--gold); }

/* -------------------------------------------------------------- chain */
.chain {
  z-index: 12;
  display: flex; flex-direction: column; gap: .35cqw;
  padding: .35cqw .5cqw;
  /* the frame above draws the edge and the stone; this no longer paints its own */
  filter: drop-shadow(0 .4cqw 1.2cqw hsl(var(--ink) / .8));
}
.chain__head {
  font-family: var(--display); color: var(--gold-bright);
  font-size: .86em; text-transform: uppercase; letter-spacing: .04em;
}
.chain__note { color: var(--faint); font-size: .58em; margin-top: -.3cqw; }
.chain__items { display: flex; flex-direction: column; gap: .32cqw; overflow: auto; padding-right: .1cqw; }
.chain__item {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: .1cqw .45cqw;
  width: 100%; text-align: left; padding: .38cqw .45cqw;
  border-radius: .35cqw; color: var(--text);
  background: linear-gradient(180deg, #242a30e8, #121518f0);
  box-shadow: inset .12cqw 0 0 var(--muted), 0 .2cqw .45cqw #0008;
}
.chain__item--you { box-shadow: inset .12cqw 0 0 var(--you), 0 .2cqw .45cqw #0008; }
.chain__item--opp { box-shadow: inset .12cqw 0 0 var(--opp), 0 .2cqw .45cqw #0008; }
.chain__item--top { background: linear-gradient(180deg, #373c42e8, #171c21f0); }
.chain__name {
  grid-column: 1 / 2; font-family: var(--display); font-size: .72em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chain__detail {
  grid-column: 1 / 2; color: var(--muted); font-size: .56em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chain__owner {
  grid-column: 2; grid-row: 1 / span 2; align-self: center;
  color: var(--gold-bright); font-size: .55em; text-transform: uppercase;
}

/* ===================================================================
   Shelves — the bases. Separate from the hand, with a banner label.
   =================================================================== */
/* Real frames, 9-sliced. Every inset below was measured off its own asset by
   scanning in from the edges for where the ornament stops and the flat stone
   starts, sampled away from the centre so the centre-edge gem could not set
   the inset for a whole side. `fill` paints the asset's own stone interior,
   which is why these rules drop the gradient and the inset shadow they used
   to draw a frame with. */
/* Frames, 9-sliced, with the insets measured off each asset by scanning in
   from its edges for where the ornament stops and the flat stone starts.
   The catch these assets taught: `border-image-width` has to stay PROPORTIONAL
   to the slice or the corner art is squeezed into a smear. A 150px corner
   rendered into a 10px border is not a small frame, it is a ruined one. So the
   ornate frames go only on surfaces big enough to carry them -- the two base
   banners and the chain -- and everything smaller gets a plain stone plaque
   with a gold hairline, which is what Runeterra does with its own small panels
   anyway. `border-image-width` exceeds `border-width` on purpose: the art
   draws at true proportion while the layout only reserves the smaller box. */
.shelf {
  display: flex; flex-direction: column; align-items: center;
  padding: .3cqw 3.1cqw .5cqw;
  border: .95cqw solid transparent;
  border-image: url("/modern/assets/generated-v2/frame-banner.webp") 103 180 69 180 fill
                / 1.85cqw 3.1cqw 1.25cqw 3.1cqw / 0 stretch;
}/* The banner frame already has a top edge to write on, so the label rides it
   instead of taking a row of its own. On a phone that row was the difference
   between a 44px tile and a 5px one: the shelf is 57px tall there, and a label
   plus two frame edges left nothing at all for the units. */
.shelf__label {
  position: absolute; left: 50%; top: 0;
  transform: translate(-50%, -52%);
  /* The banner's top edge carries a gem dead centre, which is exactly where a
     centred label lands: "Rival base - 4 units" lost its count behind it. Open
     a gap the width of the gem and let the name and the count sit either side
     of it. */
  display: flex; align-items: baseline; gap: 3.4cqw;
  font-family: var(--display); font-size: .82em; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--gold-bright); white-space: nowrap;
  text-shadow: 0 .08cqw .16cqw hsl(var(--ink) / .95), 0 0 .3cqw hsl(var(--ink) / .9);
  pointer-events: none;
}
.shelf__label span { color: var(--faint); font-weight: 400; letter-spacing: .08em; }
/* min-height:0 is essential: a flex item defaults to min-height:auto, so
   without it the units box grows to its content and spills out of the
   fixed-height shelf — which is exactly how the hand ended up covering
   the base. */
.shelf .units { flex: 1; min-height: 0; align-content: center; }

/* Too many units to show at a tappable size: keep the size, scroll the row.
   `touch-action: none` on the tiles themselves means a press on a unit still
   starts a drag -- the row is scrolled from the gaps between them. */
.units[data-scroll], .lane[data-scroll] {
  flex-wrap: nowrap;
  justify-content: flex-start;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.units[data-scroll]::-webkit-scrollbar, .lane[data-scroll]::-webkit-scrollbar { display: none; }

/* ===================================================================
   Units — square portrait tiles inside the generated frame
   =================================================================== */
.units {
  display: flex; flex-wrap: wrap; gap: var(--tile-gap, .45cqw);
  justify-content: center; align-content: flex-start;
  width: 100%;
}

.unit {
  position: relative;
  width: var(--tile, 7.4cqw); aspect-ratio: 1;
  flex: 0 0 auto;
  /* --tap-scale compensates a tapped card's rotated bounding box so the
     layout box stays exactly square and nothing overflows its lane. */
  transform: translateY(var(--lift, 0))
             scale(calc(var(--scale, 1) * var(--tap-scale, 1)))
             rotate(var(--tap, 0deg));
  transform-origin: 50% 88%;
  transition: transform 150ms var(--ease-lift);
  contain: layout style;          /* never `paint` — it would clip the medallion */
  z-index: 0;
}
.unit:hover { --lift: -.55cqw; --scale: 1.16; z-index: 30; }

/* Host and equipment are sibling buttons: every card keeps its own hit area. */
.unit-group {
  display: flex; flex: 0 0 auto; align-items: center;
  gap: var(--tile-gap, .45cqw);
}
.unit-group--equipped {
  border-radius: .5cqw;
  background: #c8a96130;
  box-shadow: 0 0 0 2px #c8a96190;
}
.unit__attachment {
  position: absolute; bottom: 3%; left: 3%; right: 3%;
  padding: 2px; border-radius: 3px; background: #10151eef; color: #ffe5a4;
  font-size: max(9px, .7cqw); line-height: 1.15;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  pointer-events: none;
}
/* Keep a group together even when its host has many attachments. */
.units.has-equipment, .lane.has-equipment {
  flex-wrap: nowrap; justify-content: flex-start; overflow-x: auto;
  overscroll-behavior-x: contain;
}

/* the portrait sits exactly in the frame's opening */
.unit__art {
  position: absolute;
  left: 9.73%; top: 10.05%; width: 80.46%; height: 78.39%;
  overflow: hidden; border-radius: 6%;
  background: #0a0d10;
}
.unit__art img {
  display: block; width: var(--por-w); margin-left: var(--por-x); margin-top: var(--por-y);
  /* the concept's tiles read at saturation .48-.64; the raw thumbnails sit near .34 */
  filter: saturate(1.3) contrast(1.06);
}

/* the generated frame, drawn over the portrait */
.unit__frame {
  position: absolute; inset: 0; pointer-events: none;
  background: url("/modern/assets/generated-v1/unit-frame.webp") center/100% 100% no-repeat;
  filter: drop-shadow(0 .12cqw .25cqw hsl(var(--ink) / .85));
}
.unit--mine  .unit__frame { filter: drop-shadow(0 .12cqw .25cqw hsl(var(--ink) / .85)) drop-shadow(0 0 .2cqw #4f9bff55); }
.unit--enemy .unit__frame { filter: drop-shadow(0 .12cqw .25cqw hsl(var(--ink) / .85)) drop-shadow(0 0 .2cqw #ff7a5e55); }

/* Might sits in the medallion baked into the frame at (86%, 84.1%) */
.might {
  position: absolute; left: 86%; top: 84.1%;
  width: 18%; aspect-ratio: 1;
  transform: translate(-50%, -50%);
  display: grid; place-items: center;
  pointer-events: none;
}
/* The frame bakes a dark slate medallion; this lights it so the number reads
   as a struck gold coin at board scale, which is the whole point of it. */
.might::before {
  content: ""; position: absolute; inset: 6%;
  border-radius: 50%;
  background: radial-gradient(circle at 36% 30%, #6a5326, #241b0e 72%);
  box-shadow: inset 0 0 0 .06cqw #d8b86e80, inset 0 .08cqw .12cqw #000;
}
.might__n {
  position: relative;
  font-family: var(--stat); font-size: 1.22em; line-height: 1;
  color: #ffeab5;
  text-shadow: 0 .08cqw .12cqw #000, 0 0 .35cqw #00000099;
}
.unit--mighty .might::before {
  content: ""; position: absolute; inset: -18%;
  border-radius: 50%; box-shadow: 0 0 .5cqw .1cqw #ffd98a;
}

/* damage — a red seal on the opposite corner so it never covers Might */
.dmg {
  position: absolute; left: 13%; top: 15%;
  width: 17%; aspect-ratio: 1; transform: translate(-50%, -50%);
  display: grid; place-items: center; border-radius: 50%;
  background: radial-gradient(circle at 34% 28%, #ff8a72, #8e1f14 62%, #3d0a06);
  box-shadow: 0 0 0 .08cqw #2a0805, 0 .1cqw .2cqw hsl(var(--ink) / .9);
  pointer-events: none;
}
.dmg__n { font-family: var(--stat); font-size: .92em; color: #fff2ee; text-shadow: 0 .06cqw .1cqw #000; }

/* status pips down the left edge, drawn as shapes (no dingbat tofu) */
.flags { position: absolute; left: 2%; bottom: 14%; display: flex; flex-direction: column; gap: .12cqw; }
.flag { width: .82cqw; height: .82cqw; background: currentColor; }
.flag--stunned   { color: #ffd166; clip-path: polygon(60% 0, 20% 56%, 46% 56%, 36% 100%, 82% 40%, 54% 40%); }
.flag--empowered { color: var(--you); clip-path: polygon(50% 4%, 100% 58%, 72% 58%, 72% 96%, 28% 96%, 28% 58%, 0 58%); }
.flag--attacker  { color: var(--danger); clip-path: polygon(50% 0, 68% 38%, 50% 100%, 32% 38%); }
.flag--defender  { color: var(--shield); clip-path: polygon(50% 0, 100% 20%, 100% 60%, 50% 100%, 0 60%, 0 20%); }

.unit--exhausted { --tap: -5deg; --tap-scale: .92; }   /* cos5+sin5 = 1.086; .92 cancels it */
.unit--exhausted .unit__art img { filter: saturate(.4) brightness(.62); }
/* A face-down card shows the card back. The portrait crop that frames real
   artwork would show only the middle of it, so the crop is reset and the whole
   back is fitted to the tile. The gradient that used to stand in for a back
   here was invented art; the real one is in the pack. */
.unit--facedown .unit__art { background: #0a0d10; }
.unit--facedown .unit__art img {
  width: 100%; height: 100%; object-fit: cover;
  margin-left: 0; margin-top: 0;
}
.unit--barrier .unit__frame {
  filter: drop-shadow(0 .12cqw .25cqw hsl(var(--ink) / .85)) drop-shadow(0 0 .35cqw #86d2ff);
}
.unit--gear .unit__art { box-shadow: inset 0 0 0 .1cqw #c8a96159, inset 0 0 .6cqw hsl(var(--ink) / .8); }

/* one-shot pulses: transform/opacity only */
.unit::after {
  content: ""; position: absolute; inset: 4%; border-radius: 12%;
  pointer-events: none; opacity: 0;
}
@keyframes pulse { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(1.28); } }
.fx-hit::after  { box-shadow: 0 0 0 .18cqw #e8483ccc; animation: pulse .5s ease-out; }
.fx-ward::after { box-shadow: 0 0 0 .18cqw #86d2ffcc; animation: pulse .7s ease-out; }
.fx-heal::after { box-shadow: 0 0 0 .18cqw #4f9bffcc; animation: pulse .7s ease-out; }

/* ===================================================================
   Battlefields
   =================================================================== */
.fields {
  display: grid; gap: .7cqw;
  grid-template-columns: repeat(var(--fields, 2), minmax(0, 1fr));
}

.field {
  position: relative; display: flex; flex-direction: column;
  border-radius: .55cqw; overflow: hidden;
  background: #0d1116;
  /* --tint is the battlefield's own domain colour, set from the card in
     board.js. Three panels then read as three places. */
  box-shadow:
    inset 0 0 0 .2cqw var(--tint, #6a4e28),
    inset 0 0 0 .34cqw #10151a,
    inset 0 0 3.2cqw -.5cqw var(--tint, transparent),
    0 .3cqw .9cqw -.2cqw hsl(var(--ink) / .9),
    0 0 2cqw -.6cqw var(--tint, transparent);
}
/* the header carries the same colour, faintly, so the panel reads as one piece */
.field__head { border-bottom: .1cqw solid color-mix(in srgb, var(--tint, #6a4e28) 55%, transparent); }
.field__seam { background: linear-gradient(90deg, transparent, var(--tint, var(--gold)), transparent); }

/* header: info · NAME + score · expand */
.field__head {
  position: relative; z-index: 3; flex: 0 0 auto;
  display: flex; align-items: center; gap: .4cqw;
  padding: .28cqw .5cqw .3cqw;
  /* The concept tints each header to its battlefield, so the three panels read
     as three places from the bar alone. The tint is the one sampled from the
     battlefield's own art. */
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--tint, #353a40) 26%, #171b20) 0%,
      color-mix(in srgb, var(--tint, #181c20) 14%, #12161a) 70%,
      #10141ae6 100%);
  box-shadow: inset 0 1px 0 #e9bc6140, inset 0 -.12cqw 0 color-mix(in srgb, var(--tint, #6a4e28) 60%, transparent);
}
/* a round sapphire disc, the way the concept marks a battlefield's info */
.field__btn {
  width: 1.55cqw; height: 1.55cqw; flex: 0 0 auto;
  display: grid; place-items: center; border-radius: 50%;
  font-size: .78em; font-weight: 700; color: #eaf3ff;
  background: radial-gradient(circle at 38% 32%, #6fb6ff, #1f62bf 62%, #0d2f66);
  box-shadow: inset 0 0 0 .08cqw var(--gold), 0 .1cqw .2cqw hsl(var(--ink) / .8);
}
.field__btn:hover { box-shadow: inset 0 0 0 .08cqw var(--gold-bright), 0 0 .5cqw #4f9bffaa; }
.field__title { flex: 1; text-align: center; min-width: 0; }
/* White, as the concept has it: gold is the furniture's colour, and a
   battlefield's name sitting in gold read as another piece of frame. */
.field__name {
  display: block; font-family: var(--display); font-size: .95em; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: #f4f7ff;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  text-shadow: 0 .08cqw .22cqw #000, 0 0 .5cqw color-mix(in srgb, var(--tint, #6a4e28) 70%, transparent);
}
/* name and emblem on one line, score centred under them */
.field__line { display: flex; align-items: center; justify-content: center; gap: .45cqw; min-width: 0; }
/* The sigil's stand-in: a round crop of the battlefield's own painting. */
.field__emblem {
  flex: 0 0 auto; width: 1.5cqw; height: 1.5cqw; border-radius: 50%;
  background: var(--scene, none) center/cover no-repeat, #10151a;
  box-shadow: 0 0 0 .09cqw var(--tint, var(--gold)), 0 0 .5cqw -.1cqw var(--tint, transparent);
}
.field__score { display: block; font-size: .72em; letter-spacing: .1em; color: var(--faint); }

/* the option filter, shown only when the list is long enough to need it */
.tray__search {
  width: 100%; margin: 0 0 6px; padding: 7px 9px;
  border: 0; border-radius: 6px;
  color: var(--text); background: #0f1419;
  box-shadow: inset 0 0 0 1px #2a3036;
  font: inherit; font-size: 13px;
}
.tray__search::placeholder { color: var(--faint); }

/* Battlefield state, out of the header so it costs the lanes no height. */
.field__state {
  position: absolute; z-index: 4; right: .35cqw; bottom: .35cqw;
  padding: .1cqw .4cqw; border-radius: .2cqw;
  font-size: .62em; letter-spacing: .08em; text-transform: uppercase;
  color: #d8dbcf; background: #0d1116cc;
  box-shadow: inset 0 0 0 .06cqw #6d5c3980;
  pointer-events: none;
}
.field__state--fight { color: #ffd9a8; box-shadow: inset 0 0 0 .08cqw #d89b4e; }
.field__state--spent { color: #979fa8; }
.field__score.is-you { color: var(--you); }
.field__score.is-opp { color: var(--opp); }

/* body: the environment, with the two lanes over it */
.field__body {
  position: relative; flex: 1; display: flex; flex-direction: column;
  min-height: 0;
}
.field__env {
  position: absolute; inset: 0; z-index: 0; overflow: hidden;
}
.field__env::before {
  content: ""; position: absolute; inset: 0;
  background: var(--env, none) center/cover no-repeat;
  opacity: 1;
  filter: saturate(1.6) contrast(1.1) brightness(.85);
}
/* The battlefield card is landscape ~1038x733 with rules text printed at BOTH
   the top (rotated 180deg, to read from the far side of the table) and the
   bottom, and its title overlaid on the lower art. `cover` therefore drags all
   of that into the panel. Instead the scan is scaled so its clean art band —
   card-y 19.8%..58% — exactly fills the panel height, and is centred
   horizontally. 1/0.2698 = 261.7% height; the 19.8% shift is the band's top. */
/* A battlefield IS its scene. Legacy paints the same file edge to edge with
   `background-size: cover` under a light gradient (rgba(8,7,5,.12) to .42,
   style.css:914), and that is what this does -- no crop, no blur, no wash.
   The 261.7% crop that used to live here existed because the source was the
   battlefield CARD, whose rules text is printed at both ends; a scene has no
   rules text, so the crop is not needed and was only ever making the art
   foggy. */
.field--scene .field__scene {
  position: absolute; inset: 0;
  width: 100%; height: 100%; max-width: none;
  object-fit: cover; object-position: center;
  transform: none;
  opacity: 1;
  filter: none;
}
/* restrained contrast: the environment must never fight the portraits */
/* Legacy's scrim, which is only this: a light darkening top to bottom, enough
   to keep a unit tile readable over bright art and no more. */
.field--scene .field__scrim {
  background: linear-gradient(180deg, rgba(8, 7, 5, .12), rgba(8, 7, 5, .42));
}
/* the older generated-environment backdrop keeps its vignette */
.field__scrim {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    radial-gradient(82% 74% at 50% 48%, transparent 45%, hsl(var(--ink) / .3) 100%),
    linear-gradient(180deg, hsl(var(--ink) / .22), hsl(var(--ink) / 0) 24% 74%, hsl(var(--ink) / .2));
}

.lane {
  position: relative; z-index: 2; flex: 1;
  display: flex; flex-wrap: wrap; gap: var(--tile-gap, .45cqw);
  justify-content: center; padding: .35cqw .4cqw;
  min-height: 0;
}
.lane--opp { align-content: flex-end; }   /* press down onto the seam */
.lane--you { align-content: flex-start; } /* press up onto the seam */

/* the seam between the armies */
.field__seam {
  position: relative; z-index: 2; flex: 0 0 auto;
  height: .55cqw; margin: .25cqw 1.6cqw;
  border-radius: .28cqw;
  background: linear-gradient(180deg, #fff3d0 0 18%, #c8a961 32%, #6b5226 62%, #2e2410);
  box-shadow:
    0 0 1cqw -.1cqw #d8c08a8c,
    inset 0 .06cqw 0 #fffbe8a6,
    0 .12cqw .28cqw hsl(var(--ink) / .95);
}
.field__seam::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 1.5cqw; height: 1.5cqw; transform: translate(-50%, -50%) rotate(45deg);
  border-radius: .2cqw;
  background: linear-gradient(135deg, #fff3d0, #c8a961 45%, #5c461f);
  box-shadow: 0 .1cqw .3cqw hsl(var(--ink) / .95), inset 0 0 0 .06cqw #2e2410;
}

.field--combat { box-shadow: inset 0 0 0 .12cqw #ff7a5e, inset 0 0 0 .26cqw #10151a, 0 0 1.2cqw -.2cqw #ff7a5e80; }

/* ===================================================================
   Contextual action button
   =================================================================== */
.action { display: flex; flex-direction: column; align-items: center; gap: .3cqw; }
.bigaction {
  position: relative; width: 100%; aspect-ratio: 1;
  display: grid; place-items: center;
  /* The rail's own medallion, so the primary button is the same object the
     rail behind it is built around, rather than a separate grey disc. */
  background: url("/modern/assets/generated-v2/rail-medallion.webp") center/100% 100% no-repeat;
  filter: drop-shadow(0 .3cqw .6cqw hsl(var(--ink) / .9));
  transition: transform 140ms var(--ease-lift), opacity 140ms linear;
}
/* Live, this is the primary control and it should look like it. It measured
   darker AND less saturated than the board behind it -- the dimmest thing on
   screen -- because the medallion art is grey stone with a sapphire ring. Light
   the gem when there is something to press, and keep the stone for inert. */
.bigaction:not(:disabled):not(.bigaction--inert)::before {
  content: ""; position: absolute; inset: 13%;
  border-radius: 50%;
  background:
    radial-gradient(circle at 38% 30%, #bfe0ff 0 6%, #4f9bff 26%, #1f62bf 58%, #0d2f66 82%, #071b3d 100%);
  box-shadow: inset 0 0 .8cqw #bfe0ff66, inset 0 -.3cqw .6cqw #04122b;
  animation: gemPulse 3.4s ease-in-out infinite;
}
.bigaction:not(:disabled):not(.bigaction--inert) {
  filter: drop-shadow(0 .3cqw .6cqw hsl(var(--ink) / .9)) drop-shadow(0 0 1cqw #2f7fe066);
}
@keyframes gemPulse {
  0%, 100% { filter: brightness(1); }
  50%      { filter: brightness(1.16); }
}
.bigaction__label { position: relative; z-index: 2; }

.bigaction:hover:not(:disabled) {
  transform: scale(1.05);
  filter: drop-shadow(0 .3cqw .7cqw hsl(var(--ink) / .9)) drop-shadow(0 0 .8cqw #4aa3ecaa);
}
.bigaction:active:not(:disabled) { transform: scale(.97); }
.bigaction:disabled { opacity: .62; cursor: not-allowed; }
/* Inert: the decision is not made here (a single-select decision is answered
   by tapping a card in the tray, which submits immediately). Flatten it so it
   stops reading as a lit, pressable gem while the real control is elsewhere. */
.bigaction--inert { filter: grayscale(.9) brightness(.5); opacity: .75; }
.bigaction--inert .bigaction__label { color: #9fb0bd; text-shadow: 0 1px 2px #000; }
.bigaction__label {
  font-family: var(--display); font-size: 1.25em; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; color: #f2f8ff;
  text-shadow: 0 .1cqw .2cqw #062038, 0 0 .5cqw #0a2c4c;
  padding: 0 12%; text-align: center; line-height: 1.05;
}
.bigaction--long .bigaction__label { font-size: .92em; letter-spacing: .04em; }

.turnstate { text-align: center; }
.turnstate__who {
  display: block; font-family: var(--display); font-size: .92em; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--gold-bright);
}
.turnstate__phase { display: block; font-size: .72em; letter-spacing: .16em; text-transform: uppercase; color: var(--muted); }
.turnstate__dots { display: flex; justify-content: center; gap: .25cqw; margin-top: .2cqw; }
.turnstate__dots i { width: .45cqw; height: .45cqw; border-radius: 50%; background: #2f353b; box-shadow: inset 0 0 0 1px #0006; }
.turnstate__dots i.on { background: radial-gradient(circle at 35% 30%, #bfe6ff, #3f8fd0); box-shadow: 0 0 .35cqw #4aa3ec; }

/* ===================================================================
   Rival hand — face-down cards, so the enemy is a presence not a number
   =================================================================== */
.ophand {
  display: flex; flex-direction: column; align-items: center; gap: .2cqw;
  pointer-events: none;
}
.ophand__fan { display: flex; justify-content: center; align-items: flex-start; }
.ophand__label {
  display: flex; align-items: baseline; gap: .6cqw;
  padding: .05cqw 1.1cqw; border-radius: .3cqw;
  background: linear-gradient(180deg, #212830e8, #0d1114f2);
  box-shadow: inset 0 0 0 .07cqw var(--gold-dim), inset 0 1px 0 #e9bc6130;
}
.ophand__label i {
  font-style: normal; font-family: var(--display); font-size: .72em;
  letter-spacing: .16em; text-transform: uppercase; color: var(--muted);
}
.ophand__label b { font-family: var(--num); font-size: .9em; color: var(--gold-bright); }
.opcard {
  flex: 0 0 auto;
  width: 3.9cqw; margin-inline: -1.15cqw;
  border-radius: .25cqw; overflow: hidden;
  transform: rotate(var(--fan, 0deg));
  transform-origin: 50% -40%;     /* hangs from above, mirroring your own fan */
  box-shadow: 0 .18cqw .35cqw hsl(var(--ink) / .55);
}
.opcard img { display: block; width: 100%; aspect-ratio: 744/1039; }
.opcard__count {
  align-self: center;
  margin-left: .7cqw; padding: .1cqw .45cqw;
  border-radius: .8cqw;
  font-family: var(--num); font-size: .8em; color: var(--text);
  background: hsl(var(--ink) / .62);
  box-shadow: inset 0 0 0 .06cqw var(--gold-dim);
}

/* ===================================================================
   Hand — a held fan, separate from the base
   =================================================================== */
.handzone { display: flex; flex-direction: column; align-items: center; justify-content: flex-end; }
.hand { display: flex; justify-content: center; align-items: flex-end; width: 100%; }
.hand .card {
  flex: 0 0 auto; width: 7.6cqw;
  margin-inline: -1.1cqw;
  border-radius: .3cqw; overflow: hidden;
  background: #0c0f13;
  transform: translateY(var(--lift, 0)) rotate(var(--fan, 0deg));
  transform-origin: 50% 150%;
  box-shadow: inset 0 0 0 .08cqw #6d5c3980, var(--elev-rest);
  transition: transform 150ms var(--ease-lift), box-shadow 150ms linear;
  position: relative;
}
.hand .card img { display: block; width: 100%; aspect-ratio: 744/1039; }
.hand .card:hover {
  --lift: -2.6cqw; --fan: 0deg; --scale: 1;
  z-index: 60;
  box-shadow: inset 0 0 0 .08cqw var(--gold), var(--elev-hover);
}
.hand__count {
  font-size: .68em; letter-spacing: .16em; text-transform: uppercase; color: var(--faint);
  margin-top: .15cqw;
}

/* ===================================================================
   Interaction states
   =================================================================== */
.is-legal .unit__frame,
.unit.is-legal .unit__frame {
  filter: drop-shadow(0 .12cqw .25cqw hsl(var(--ink) / .85)) drop-shadow(0 0 .45cqw #4f9bff);
}
.unit.is-legal, .card.is-legal { cursor: pointer; }
.unit.is-legal::before, .card.is-legal::before {
  content: ""; position: absolute; inset: -4%; border-radius: 14%;
  pointer-events: none; box-shadow: 0 0 0 .12cqw #4f9bff, 0 0 .8cqw #4f9bff80;
  animation: breathe 1.9s ease-in-out infinite;
}
@keyframes breathe { 0%, 100% { opacity: .2; } 50% { opacity: .85; } }

.is-selected { z-index: 40 !important; }
.unit.is-selected::before, .card.is-selected::before {
  content: ""; position: absolute; inset: -6%; border-radius: 14%;
  pointer-events: none; animation: none; opacity: 1;
  box-shadow: 0 0 0 .16cqw var(--pick), 0 0 1.2cqw #ffd27a99;
}
/* ===================================================================
   Drag and drop — ported from Legacy's pointer semantics (app.js:996-1150)
   =================================================================== */

/* The effects canvas is decoration: it must never intercept a drop or a tap. */
#vfx-layer, #vfx-layer canvas { pointer-events: none !important; }

/* A card the pointer can pick up must not also scroll the page. */
#hand .card, #stage .unit { touch-action: none; }

.drag-ghost {
  position: fixed; z-index: 120; pointer-events: none;
  transform: translate(-50%, -55%) rotate(3deg);
  border-radius: 8px; overflow: hidden;
  box-shadow: 0 0 0 2px var(--pick), 0 18px 40px -10px #000, 0 0 30px -6px #ffd27a80;
}
.drag-ghost img { display: block; width: 100%; height: auto; }

/* Cast zone for spells, which have no board destination. */
.spell-drop {
  position: absolute; left: 50%; top: 34%; transform: translate(-50%, -50%);
  z-index: 70; display: grid; place-items: center;
  width: 34%; height: 22%; border-radius: var(--r-panel);
  background: linear-gradient(180deg, #2a3a52cc, #0e1520e6);
  box-shadow: inset 0 0 0 2px var(--pick), 0 0 40px -8px #ffd27aa6;
  pointer-events: none;
}
/* While it is showing, it must actually receive the drop -- otherwise the hit
   test looks straight through it to the lane underneath. */
.spell-drop:not([hidden]) { pointer-events: auto; }
.spell-drop__label {
  font-family: var(--display); font-size: 1.1em; font-weight: 700;
  letter-spacing: .06em; color: var(--pick); text-shadow: 0 2px 6px #000;
  text-align: center; padding: 0 8%;
}
.is-dragging { opacity: .35; }

/* LoR lights the whole destination, not a hairline. */
.is-legal-drop {
  box-shadow:
    inset 0 0 0 .16cqw var(--pick),
    inset 0 0 3cqw -.5cqw #ffd27a73 !important;
  background-color: #ffd27a12;
}
.field.is-legal-drop, .shelf.is-legal-drop {
  outline: 2px dashed #ffe2a0b3; outline-offset: -6px;
}
/* the drop chevron, the way LoR marks the live zone */
.shelf.is-legal-drop::after, .field.is-legal-drop::after {
  content: ""; position: absolute; left: 50%; bottom: 6%;
  width: 2.2cqw; height: 1.2cqw; transform: translateX(-50%);
  background: var(--pick);
  clip-path: polygon(50% 0, 100% 100%, 50% 72%, 0 100%);
  opacity: .85; animation: dropPulse 1.1s ease-in-out infinite;
  pointer-events: none;
}
@keyframes dropPulse { 0%, 100% { opacity: .35; transform: translateX(-50%) translateY(3px); }
                       50% { opacity: .95; transform: translateX(-50%) translateY(-3px); } }
/* The chevron above is absolutely positioned, so its host needs to be a
   containing block. `.field` is in normal flow and needs `relative`; the
   shelves are already `position: absolute` (see region placement) and a blanket
   `.shelf { position: relative }` here QUIETLY BEAT that rule by sheet order —
   your base fell ~15% down the stage and ended up underneath the hand, where
   none of its units could be pressed. Only give a shelf a position when the
   stacked layout has taken its absolute one away. */
.field { position: relative; }

/* the zone actually under the pointer reads hotter than the rest */
body.drop-armed .is-legal-drop:hover,
body.is-dragging-card .is-legal-drop:hover {
  background-color: #ffd27a24;
}
.is-dimmed { opacity: .35; }

.floater {
  position: fixed; z-index: 90; pointer-events: none;
  font-family: var(--stat); font-size: 20px;
  text-shadow: 0 2px 7px #000, 0 0 14px currentColor;
  animation: rise 1s ease-out forwards;
}
.floater--damage { color: #ff8a7d; }
.floater--heal { color: var(--you); }
.floater--prevent { color: var(--shield); }
@keyframes rise {
  from { opacity: 1; transform: translate(-50%, 0) scale(.85); }
  to   { opacity: 0; transform: translate(-50%, -42px) scale(1.16); }
}

/* ===================================================================
   Decision tray — only appears when the round button is not enough
   =================================================================== */
/* The concept has no helper box on the board, and it should not have one: the
   tray is a decision surface that appears only when a decision needs more than
   the round button. It floats over the middle of the table instead of owning a
   corner, and it no longer moves the hand. */
.tray {
  position: fixed; left: 50%; transform: translateX(-50%);
  top: 29%; z-index: 80;
  width: min(560px, 44vw); max-height: 40vh; overflow: auto;
  padding: 12px 14px; border-radius: 10px;
  background: linear-gradient(180deg, #262b31f7, #0e1115fb);
  box-shadow:
    0 26px 60px -14px #000,
    inset 0 0 0 1px #8a7443,
    inset 0 0 0 3px #12161a,
    inset 0 1px 0 #e9bc6133;
}
.tray__head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.tray__head { cursor: grab; touch-action: none; user-select: none; }
.tray--dragging .tray__head { cursor: grabbing; }
.tray-restore { position: fixed; left: 18px; top: 110px; z-index: 80; padding: 9px 14px; background: #171c21; color: var(--gold-bright); border: 1px solid #8a7443; }
.tray-restore[hidden] { display: none; }
.tray__prompt { flex: 1; font-family: var(--display); font-size: 14px; font-weight: 700; color: var(--gold-bright); }
.tray__foot { display: flex; gap: 7px; flex-wrap: wrap; margin-top: 8px; align-items: center; }
.affordances { display: flex; flex-wrap: wrap; gap: 6px; }

.act {
  padding: 7px 13px; border-radius: 6px; font-size: 12px; font-weight: 600; color: var(--text);
  background: linear-gradient(180deg, #2c3136, #16191d);
  box-shadow: inset 0 1px 0 #c8a9612e, 0 1px 0 hsl(var(--ink) / .8);
}
.act:hover:not(:disabled) { background: linear-gradient(180deg, #3a4046, #202428); }
.act:disabled { opacity: .45; cursor: not-allowed; }
.act--primary { color: #ddedff; background: linear-gradient(180deg, #2f7a58, #1a4a34); }
.act--pass { color: #ffeec4; background: linear-gradient(180deg, #55401c, #2e2210); }
.ghost { font-size: 11px; color: var(--muted); padding: 5px 9px; border-radius: 5px; box-shadow: inset 0 0 0 1px #3c3327; }
.ghost:hover { color: var(--gold-bright); }
.hint { font-size: 11px; color: var(--muted); }

.options { display: grid; gap: 3px; max-height: 34vh; overflow-y: auto; margin-top: 8px; }
.options__note { font-size: 10px; color: var(--faint); padding-bottom: 3px; }
.option {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 5px 8px; border-radius: 5px; text-align: left; font-size: 12px;
  background: #14171b; box-shadow: inset 0 1px 0 #ffffff0a;
}
.option:hover:not(:disabled) { background: #21262b; }
.option.is-picked { box-shadow: inset 0 0 0 1px var(--pick); background: #2e2715; }
.option__art { width: 26px; aspect-ratio: 1; border-radius: 3px; overflow: hidden; flex: 0 0 auto; background: #0a0d10; }
.option__art img { display: block; width: var(--por-w); margin-left: var(--por-x); margin-top: var(--por-y); }
.option__label { flex: 1; }
.option__rank {
  width: 18px; height: 18px; display: grid; place-items: center; flex: 0 0 auto;
  border-radius: 50%; background: var(--pick); color: #241403; font-size: 10px; font-weight: 800;
}

#statusbar {
  padding: 3px 14px; font-size: 11px; color: var(--faint);
  background: #07090c; border-top: 1px solid #161a1f;
}
#status.is-error { color: #ff9a90; }
#action-error {
  position: fixed; bottom: 32px; left: 50%; transform: translateX(-50%);
  z-index: 1000; width: min(620px, calc(100vw - 32px)); box-sizing: border-box;
  padding: 12px 16px; border: 1px solid #ff9a90; border-radius: 8px;
  background: #301b20; color: #fff; font-size: 14px;
  display: flex; align-items: center; gap: 12px;
}
#action-error[hidden] { display: none; }
#action-error-dismiss { flex: 0 0 auto; padding: 8px; }

/* ---------------------------------------------------- inspector ---- */
dialog {
  border: 0; border-radius: 12px; color: var(--text); padding: 16px;
  width: min(780px, 94vw);
  background: linear-gradient(180deg, #1a1e23, #0c0f12);
  box-shadow: 0 40px 90px -30px #000, inset 0 0 0 1px #6d5c3980;
}
dialog::backdrop { background: #03070699; backdrop-filter: blur(3px); }
.close { float: right; }
.inspect { display: grid; grid-template-columns: minmax(0, 300px) minmax(0, 1fr); gap: 22px; clear: both; }
.inspect img { width: 100%; border-radius: 8px; box-shadow: 0 10px 28px -10px #000; }
.inspect__text h2 { font-family: var(--display); font-size: 25px; color: var(--gold-bright); margin-bottom: 4px; }
.inspect__type { font-size: 10px; letter-spacing: 1.6px; text-transform: uppercase; color: var(--faint); margin-bottom: 14px; }
.inspect__rules { white-space: pre-wrap; font-size: 13px; line-height: 1.6; }
.inspect__state { display: grid; grid-template-columns: auto 1fr; gap: 4px 14px; margin: 16px 0 0; font-size: 12px; }
.inspect__state dt { color: var(--faint); }
.inspect__state dd { margin: 0; }
.pileview { clear: both; }
.pileview h2 { font-family: var(--display); font-size: 25px; color: var(--gold-bright); margin-bottom: 2px; }
.pileview__meta { color: var(--muted); font-size: 12px; margin-bottom: 14px; }
.pileview__cards {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 12px; max-height: 64vh; overflow: auto; padding: 4px;
}
.pileview__entry { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.pileview__entry > button { width: 100%; }
.pileview__card {
  display: flex; flex-direction: column; gap: 7px; align-items: stretch;
  color: var(--text); text-align: center; min-height: 0;
}
.pileview__card img {
  width: 100%; aspect-ratio: 744 / 1039; object-fit: contain; border-radius: 6px;
  background: #080e12; box-shadow: 0 8px 18px -8px #000;
}
.pileview__card span { font-size: 12px; line-height: 1.25; overflow-wrap: anywhere; }
.pileview__card:hover img { outline: 2px solid var(--gold); outline-offset: 2px; }
.pileview__empty { color: var(--muted); font-size: 13px; }

/* ===================================================================
   Crowding — tiles shrink so every unit stays visible and clickable
   =================================================================== */
/* board.js measures each lane and shelf and sets --tile inline, which beats
   these rules; they are only the pre-script fallback. Status pips stop being
   legible on a small tile, so they are hidden rather than blurred. */
[data-density="packed"] .flags { opacity: .75; }
[data-density="swarm"] .flags { display: none; }

/* ===================================================================
   Narrow / short screens: stacked flow that still shows every field
   =================================================================== */
/* ===================================================================
   Stacked layout for narrow / short viewports.

   The fixed 16:9 stage cannot survive a phone, so the board becomes a
   vertical scroll. Every battlefield stays present and readable — that is
   the requirement — but the fixed-height regions must become auto-height,
   and `flex: 1; min-height: 0` has to be undone or the unit rows collapse
   to nothing. board.js also stops measuring tiles here (see is-stacked)
   and lets these rules own the size.
   =================================================================== */
/* ===================================================================
   Touch landscape — the PRIMARY target: iPhone Pro Max held sideways
   (932x430, and 956x440 on the 16 Pro Max).

   Before this block existed such a device matched NEITHER the desktop path
   nor the stacked path: it took the fixed 16:9 stage, which the height cap
   shrank to 601x338 inside a 932px-wide screen — 36% of the display thrown
   away — while every touch-sizing rule sat behind a `max-width: 900px`
   query that a 932px phone never matches.

   Here the stage is allowed to fill the screen and every control is pushed
   to the 44px minimum.
   =================================================================== */
@media (pointer: coarse) and (orientation: landscape) {
  /* The dev bar is a REVIEW control, not part of the game, and at 84px it was
     taking a fifth of a phone screen away from the board -- which is what
     forced base units down to 27px. Compact it to one short row and give the
     height back to the stage. */
  #devbar { padding: 2px 8px; gap: 8px; font-size: 10px; overflow-x: auto; }
  #devbar select { padding: 2px 20px 2px 7px; font-size: 10px; }
  .devbar__play { min-height: 26px; padding: 2px 10px; font-size: 11px; }
  .devbar__brand small { display: none; }

  /* body is already `grid-template-rows: auto auto 1fr auto` with the stage in
     the 1fr row, so bound the grid and let the stage take exactly what is left.
     `100dvh - var(--chrome)` was a guess at the dev bar's height, and when the
     bar changed size the stage overhung the screen by 29px. */
  body { height: 100dvh; min-height: 0; }

  #stage {
    /* fill the screen; the 16:9 cap is what wasted 36% of it */
    --stage-w: calc(100vw - env(safe-area-inset-left, 0px) - env(safe-area-inset-right, 0px));
    width: var(--stage-w);
    height: auto;
    min-height: 0;
    aspect-ratio: auto;
    margin: 0 auto;
    margin-inline: env(safe-area-inset-left, 0px) env(safe-area-inset-right, 0px);
  }

  /* 44px minimums on everything a finger has to hit.
     The battlefield's own info/zoom buttons are the exception: at 44px TALL
     they set the height of a header that already carried a two-line name and a
     two-line score, so the header took 77px of a 129px battlefield and the
     lanes underneath collapsed to 21px — tiles of six pixels. They stay 44px
     wide and lose height; the primary targets (units, cards, the round
     button) keep the full 44. */
  /* `#stage` prefixes because refinement.css loads after this sheet and sets
     its own field-header sizes; a media query adds no specificity, so without
     the prefix those rules win and the header stays 77px tall. Same reason the
     tray rules below are written `.tray.tray--cards`. */
  /* Frames get thin here: at 2.17:1 the base rows are ~57px tall and an ornate
     edge competes directly with the tile a finger has to hit. */
  /* 4px, not 7: the base row is 57px tall here and every pixel the frame takes
     comes straight off the tile a thumb has to hit. At this size the ornament
     reads as a gold edge, which is the most a 57px row can carry anyway. */
  /* The shelf is 57px tall here, so the banner's caps are trimmed back hard:
     the ornament still reads as gold ends, and the tile keeps its pixels. */
  #stage .shelf { border-width: 4px; border-image-width: 9px 20px 6px 20px; padding: 0 20px 2px; }
  #stage .chain { border-width: 5px; border-image-width: 14px 11px 14px 11px; padding: 3px 8px; }
  #stage .pile__back { width: 22px; }
  #stage .pilechip .pile__icon { width: 12px; height: 12px; }
  #stage .piles .pile { min-width: 38px; }
  #stage .shelf__label { gap: 22px; }
  #stage .shelf__label { font-size: 9px; letter-spacing: .08em; }
  /* The top band at 2.17:1 was oversubscribed before the rival's hand existed:
     the resource strip ran from 61.5% to 87.5% and the pile strip from 70.8%,
     so they were already sitting on each other. Lay the whole band out once,
     left to right, with no overlaps -- identity, base, resources, hand, piles. */
  .hud--opp   { left: .8%;  top: 1%; width: 24%; }
  .shelf--opp { left: 25%;  width: 31%; }
  .res--opp   { left: 57%; right: auto; top: 1%; width: 20%; max-width: none; }
  .ophand     { left: 77.5%; right: 16.5%; top: 1%; height: 15%; gap: 1px; }
  #stage .ophand__label { padding: 0 4px; }
  /* "Rival hand" does not fit beside the count in the 56px this band can
     spare, and it does not need to: a card back with a number beside it says
     the same thing. The words stay in the region's aria-label. */
  #stage .ophand__label i { display: none; }
  #stage .ophand__label b { font-size: 10px; }
  /* The strip is 184px wide here and its three groups want ~165 with only a
     10px gap between them, so the labels read as one run-on word. Let it take
     two lines instead -- `flex-wrap` was only ever set in the stacked arm, so
     at 2.17:1 it had none. */
  #stage .res { flex-wrap: wrap; row-gap: 2px; column-gap: 12px; padding: 4px 8px; }
  #stage .res__label { font-size: 8px; letter-spacing: .02em; }
  /* `#stage` again: refinement.css sets these and loads later, so the media
     query alone loses. The name box is 62px wide here and "Opponent" wanted
     71 at refinement's 14px. */
  #stage .hud__name { font-size: 10px; width: 150%; margin-inline: -25%; }
  #stage .hud__points { font-size: 17px; }
  .piles--opp { right: 1%; top: 1%; max-height: 30%; }
  /* and the same left-to-right pass along the bottom, where the pile strip was
     sitting on the hand and the chain was sitting on the pile strip */
  .hud--you   { left: .8%; top: 58%; width: 24%; }
  .shelf--you { left: 25%; width: 31%; }
  .chain      { left: 57%; right: 12%; top: 61.5%; max-height: 17%; }
  .piles--you { right: 1%; bottom: 1%; top: auto; max-height: 30%; }
  /* two lines now, so it starts higher or it runs off the bottom edge */
  /* The bottom-left block is identity over resources, and on a 334px stage the
     two together ran off the bottom edge. The resource cells lose their padding
     rather than the block losing a row. */
  .res--you   { left: .8%; top: 78%; width: 28%; max-width: none; }
  #stage .res__group { padding: 1px 5px 2px; gap: 0; }
  #stage .gem { width: 16px; height: 19px; font-size: 10px; }
  #stage .rune { width: 9px; height: 12px; }
  /* Four cells wrap to three rows in the width a phone can spare, and the
     block then runs off the bottom edge. The rune-deck count is the one of the
     four you never act on, so it is the one that goes; its number stays in the
     strip's own tooltip. */
  #stage .res__group--runedeck { display: flex; }
  #stage .res { flex-wrap: nowrap; }
  /* One back and a count, not a fan: a phone has no room for twelve of them,
     and the information is the number. */
  .opcard { width: 26px; margin-inline: 0; }
  .opcard:not(:first-child) { display: none; }
  .opcard__count { font-size: 11px; margin-left: 4px; }

  #stage .field__btn { min-width: 44px; min-height: 30px; height: 30px; }
  /* one line each, so the title stops competing with the lanes */
  #stage .field__head { min-height: 0; padding: 2px 4px; gap: 4px; }
  #stage .field__name {
    font-size: 11px; line-height: 1.3; letter-spacing: .06em;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  #stage .field__score {
    /* "You 26 - Rival 36 - Uncontrolled" needs 152px and has 141; the tracking
       is what does not fit, so drop the tracking rather than the words. */
    font-size: 9px; line-height: 1.3; letter-spacing: 0; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis;
  }
  /* each of these was cut off by a few pixels on a 932px phone */
  #stage .slot__label { letter-spacing: 0; font-size: 8px; }
  #stage .gem { height: 24px; line-height: 1; }
  #stage .bigaction__label { font-size: 13px; line-height: 1.1; }
  #stage .bigaction--long .bigaction__label { font-size: 11px; }
  .pile { min-height: 44px; }
  .option-inspect { min-height: 44px; }
  .slot__card { min-width: 44px; min-height: 44px; }
  .hud__portrait { min-width: 44px; min-height: 44px; }
  .field-nav__button { min-height: 44px; }

  /* Re-allocate the vertical budget for 2.17:1. The percentages above were
     drawn for 16:9, where 38.5% of the stage is 310px and a battlefield lane
     is comfortable; here the same 38.5% is 129px and the lanes could not hold
     a tile a finger can hit. Give the battlefields the room, and take it from
     the two base rows, which hold one row of tiles each. */
  .shelf--opp { top: .5%;  height: 17%; }
  .fields     { top: 18%;  height: 43%; }
  .shelf--you { top: 61.5%; height: 17%; }
  .chain      { top: 61.5%; max-height: 17%; }

  /* the battlefield switcher must be reachable, not hidden until focus */
  .field-nav { display: flex; }

  /* no sticky :hover after a tap */
  .unit:hover { --lift: 0; --scale: 1; }
  .hand .card:hover { --lift: -12px; }

  /* The hand really does have to tuck under the bottom edge here. At 2.17:1
     the hand zone is ~73px tall while a legible card is ~99px, so the cards
     spilled UP over your own base and swallowed every press aimed at a unit
     standing there -- all nine of them in the crowded case. Push the cards
     down by the difference and clip at the zone, so the fan shows its top two
     thirds (name and art) and owns no pixel above its own zone. */
  .handzone { overflow: hidden; }
  .hand .card { margin-bottom: -3.6cqw; }

  /* The tray must never cover the round action button. At MULLIGAN on a 932px
     phone it overlapped it by 87x93px -- 94% of the button -- and
     elementFromPoint at the button's centre returned the option list, so the
     button was enabled and completely untappable. Reserve the action column. */
  /* `.tray.tray--cards` (0,2,0) so this beats refinement.css's `.tray--cards`
     width rule on specificity rather than relying on sheet order. */
  .tray, .tray.tray--cards {
    left: 8px;
    right: calc(11% + 14px);
    width: auto;
    max-width: none;
  }
  .action { z-index: 85; top: 36%; right: 1%; width: 9.5%; }

  /* The right rail is far more crowded at 2.17:1 than at the 16:9 the
     percentage coordinates were designed for: the pile columns landed on top
     of the action button (56x120px), so a tap meant for the button hit a pile.
     On a phone the piles become short horizontal strips pinned to the top and
     bottom corners, leaving the middle of the right edge to the button. */
  .piles--opp, .piles--you {
    flex-direction: row;
    width: auto;
    height: auto;
    right: 1%;
    gap: 6px;
  }
  .piles--opp { top: 1%; bottom: auto; }
  .piles--you { bottom: 1%; top: auto; }
  .piles .pile { min-width: 44px; padding: 2px 6px; }
}

@media (orientation: portrait), (max-width: 700px) {
  #stage {
    width: 100%; aspect-ratio: auto; height: auto;
    /* inline-size, NOT normal: the sheet is written in cqw throughout, and
       switching the container off makes every one of those lengths collapse.
       inline-size keeps cqw resolving against the stage width while allowing
       the auto height this layout needs. */
    container-type: inline-size;
    font-size: 12px;
    display: flex; flex-direction: column; gap: 10px;
    padding: 10px; margin: 0;
    border-radius: 0;
    background: #0f1318 url("/modern/assets/generated-v2/board-table.webp") center top / 100% auto no-repeat fixed;
  }
  #stage > section, #stage > .action, #stage > .handzone {
    position: static; inset: auto; width: auto; max-width: none; height: auto;
  }
  /* static again here, so the drop chevron needs its containing block back */
  #stage > .shelf { position: relative; }

  /* undo every fixed-height / flex-fill assumption from the stage layout */
  .shelf { padding: 6px 8px 8px; }
  .shelf__label { margin-bottom: 7px; padding: 2px 12px; font-size: 11px; }
  .plaque, .res, .pile, .hud__slots, .field { border-radius: 8px; }
  .shelf .units, .units { flex: none; min-height: 0; }
  .lane { flex: none; min-height: 0; padding: 6px 8px; }
  .field { min-height: 0; }
  .field__body { display: block; }
  .field__env { position: absolute; inset: 0; }
  .field__scene { height: 100%; width: 100%; object-fit: cover; transform: none; left: 0; opacity: .28; }

  .unit { width: var(--tile-n, 58px); }
  .units { gap: 5px; }

  .hud { gap: 10px; align-items: center; flex-wrap: wrap; }
  .hud__who { width: 62px; }
  .hud__name { font-size: 10px; width: 152%; margin-inline: -26%; }
  .hud__slots { padding: 5px; gap: 5px; }
  .slot { width: 42px; }
  .slot__label { font-size: 8px; letter-spacing: .04em; }

  .res { gap: 14px; padding: 6px 10px; flex-wrap: wrap; }
  .res__label { font-size: 9px; }
  .res__row { min-height: 20px; }
  .rune { width: 10px; height: 13px; }
  .gem { width: 19px; height: 22px; font-size: 11px; }

  .piles { flex-direction: row; justify-content: center; gap: 8px; }
  .pile { padding: 4px 10px; }
  .pile__icon { width: 15px; height: 15px; }
  .chain { max-height: none; padding: 8px; border-radius: 8px; }
  .chain__head { font-size: 13px; }
  .chain__note { font-size: 10px; margin-top: -5px; }
  .chain__items { gap: 5px; }
  .chain__item { padding: 7px 8px; border-radius: 6px; }
  .chain__name { font-size: 12px; }
  .chain__detail, .chain__owner { font-size: 10px; }

  .fields { grid-template-columns: 1fr; gap: 10px; }
  .field__name { font-size: 13px; }
  .field__score { font-size: 10px; }
  .field__btn { width: 20px; height: 20px; }
  .field__seam { height: 3px; margin: 4px 16px; border-radius: 2px; }
  .field__seam::after { width: 11px; height: 11px; border-radius: 2px; }

  .might__n { font-size: 13px; }
  .dmg__n { font-size: 11px; }
  .flag { width: 8px; height: 8px; }

  .action {
    z-index: 5;
    flex-direction: row; align-items: center; justify-content: center; gap: 14px;
    padding: 8px 10px 10px; border-radius: 8px;
    background: linear-gradient(180deg, #09101100, #091011ee 28%, #06080aff);
    box-shadow: 0 -14px 26px -22px #000, inset 0 1px 0 #c8a96133;
  }
  .bigaction { width: 82px; }
  .bigaction__label { font-size: 13px; }
  .bigaction--long .bigaction__label { font-size: 11px; }
  .turnstate__who { font-size: 12px; }
  .turnstate__dots i { width: 6px; height: 6px; }

  .handzone { height: auto; }
  .hand { justify-content: flex-start; overflow-x: auto; padding: 6px 2px 10px; }
  .hand .card { width: 76px; margin-inline: -8px; }
  .hand .card:hover { --lift: -14px; }
  .hand__count { font-size: 10px; }

  .tray { right: 8px; left: 8px; width: auto; bottom: 26px; max-height: 52vh; }
  #stage[data-tray="1"] .handzone { right: auto; }
  .inspect { grid-template-columns: 1fr; }
  .inspect img { max-height: 42vh; object-fit: contain; }
  .pileview__cards { grid-template-columns: repeat(auto-fill, minmax(92px, 1fr)); max-height: 56vh; }
}

@media (orientation: portrait) and (max-width: 700px) {
  .orientation-note { display: block; }
}

/* very small phones */
@media (max-width: 470px) {
  .unit { width: var(--tile-n, 48px); }
  .hand .card { width: 64px; }
  .slot { width: 36px; }
  .hud__who { width: 52px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}


/* ===================================================================
   End of match — the last thing a player sees, so not a footer line
   =================================================================== */
.over {
  position: fixed; inset: 0; z-index: 200;
  display: grid; place-items: center;
  background: radial-gradient(60% 60% at 50% 45%, #05080ac4, #05080aee 70%);
  animation: overIn 260ms var(--ease-lift, ease-out) both;
}
.over__card {
  min-width: min(380px, 86vw); max-width: 92vw;
  padding: 26px 32px 22px; text-align: center;
  border-radius: 14px;
  background: linear-gradient(180deg, #1d2227f2, #0d1014f8);
  box-shadow: 0 24px 60px -20px #000, inset 0 0 0 1px #6d5c3980;
}
.over__verdict {
  font-family: var(--display); font-size: 30px; font-weight: 800;
  letter-spacing: .14em; text-transform: uppercase; color: var(--gold-bright);
  text-shadow: 0 2px 10px #0009;
}
.over--lose .over__verdict { color: #e2a0a0; }
.over--draw .over__verdict { color: #cfd2d6; }
.over__reason { margin-top: 6px; color: var(--muted); font-size: 14px; }
.over__score {
  display: flex; flex-direction: column; gap: 4px;
  margin-top: 14px; font-family: var(--num); font-size: 17px;
  letter-spacing: .04em; color: var(--text);
}
.over__row { display: flex; align-items: baseline; justify-content: space-between; gap: 18px; }
.over__row b { font-family: var(--ui); font-size: 13px; font-weight: 600; color: var(--muted);
               overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.over__row i { font-style: normal; font-weight: 700; }
.over__acts { display: flex; gap: 10px; justify-content: center; margin-top: 20px; flex-wrap: wrap; }
.over__act {
  min-height: 44px; padding: 8px 18px; border-radius: 8px;
  color: var(--text); background: #161b20;
  box-shadow: inset 0 0 0 1px #2a3036;
  font-size: 14px; font-weight: 600;
}
.over__act--primary {
  color: #20160a; background: linear-gradient(180deg, #f1d88d, #bf9342);
  box-shadow: inset 0 1px 0 #fff3, 0 2px 8px #0007;
}
.over__act:hover { filter: brightness(1.08); }
@keyframes overIn { from { opacity: 0; } to { opacity: 1; } }

/* Tutorial coaching shares the scrollable choice tray, preserving legal controls. */
#tutorial-coach { padding: 12px; margin-bottom: 10px; border: 1px solid #baa36b; border-radius: 8px; background: #18202a; color: #f4ead5; font-size: 14px; line-height: 1.45; }
#tutorial-coach p { margin: 6px 0 10px; }
#tutorial-coach p:last-child { margin-bottom: 0; }

/* Scoring and staged fights remain readable without hover in every layout. */
.field__badge { flex-wrap: wrap; }
.field__state { flex: 1 0 100%; font-size: 11px; line-height: 1.3; color: #fff1c2; white-space: normal; text-align: center; }
.field__state[hidden] { display: none; }
