/* Emanation Online — a full-screen table that changes shape between scenes.

   #app[data-mode] is the single switch: lobby, pick, target, lay, resolve,
   results, over. Every region is absolutely positioned against the viewport and
   each mode sets where it sits and how big it is. Because the same elements
   persist across modes, a scene change is a transition rather than a cut.

   What animates between scenes is kept cheap. The hand never changes its
   layout size: it is laid out once at its full (pick) size and scenes move and
   scale it with a transform, which costs no layout and no text reflow. Seats do
   change size, through --seat, but that variable animates on the two seat
   containers only. (It used to animate on #app, where an inherited variable
   restyles every element in the app on every frame of a scene change; on a
   slower laptop that ran at about 20fps.) */

@import url('https://fonts.googleapis.com/css2?family=Crimson+Pro:ital,wght@0,400;0,600;0,700;1,400&family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap');

@property --hand-card { syntax: "<length>"; inherits: true; initial-value: 300px; }
@property --seat { syntax: "<length>"; inherits: true; initial-value: 48px; }
@property --card-h { syntax: "<length>"; inherits: true; initial-value: 240px; }
/* Your seat's height above the floor is one formula in every scene:
   hand height x --me-k + --me-off. Scenes set --me-k-to / --me-off-to on #app
   (a plain jump); .me copies them into the registered pair and animates those,
   so only your seat is restyled while it moves. */
@property --me-k { syntax: "<number>"; inherits: true; initial-value: 1; }
@property --me-off { syntax: "<length>"; inherits: true; initial-value: 58px; }

:root {
  --ink: #0E0B08;
  --ink-2: #17130F;
  --ink-3: #241E17;
  --paper: #F5F1E8;
  --paper-2: #E8E1D2;
  --hairline: #3A3126;
  --gold: #E0B94A;
  --gold-line: #C9931F;
  --gold-dim: #8A6F28;
  --gold-text: #7A5C10;
  --on-ink: #C6BCA8;
  --on-ink-dim: #7E7565;
  --blood: #B4432A;
  --blood-lit: #E0714F;
  --life: #E0B94A;
  --heal: #9FD08A;

  /* Crimson Pro is the brand serif — the launch trailer sets every headline in it. */
  --serif: 'Crimson Pro', Georgia, serif;
  --sans: 'IBM Plex Sans', system-ui, sans-serif;
  --mono: 'IBM Plex Mono', ui-monospace, Menlo, monospace;

  /* Springs from the launch trailer (motion/lib/fx.js), sampled into linear().
     --arrive: stiffness 170, damping 0.62 — overshoots 8% and settles. The
     trailer's rule: "arrivals are springs that settle, so motion always has a
     reason and a destination." --settle is the same idea, gentler, for big
     regions moving between scenes so the whole table never wobbles. */
  --arrive: linear(0, 0.0462, 0.1597, 0.3083, 0.4674, 0.6197, 0.7543, 0.8655, 0.9513, 1.0129, 1.0528, 1.0749, 1.0832, 1.0815, 1.0733, 1.0615, 1.0483, 1.0353, 1.0235, 1.0137, 1.0059, 1.0002, 0.9964, 0.9941, 0.9932, 0.9931, 0.9936, 0.9946, 1);
  --settle: linear(0, 0.0401, 0.1347, 0.2549, 0.3815, 0.5024, 0.6111, 0.7046, 0.782, 0.8441, 0.8926, 0.9294, 0.9566, 0.9759, 0.9892, 0.9979, 1.0033, 1.0062, 1.0075, 1.0077, 1.0073, 1.0065, 1);
  --ease: var(--settle);
  --t: .62s;
  /* One type scale, and nothing set below 13px. Labels are sentence case and
     upright: no tracked capitals, no italics. */
  --fs-cap: 13px;
  --fs-label: 14px;
  --fs-body: 16px;
  --fs-lead: 20px;
  --hud: 58px;
}

* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
  background: var(--ink);
  color: var(--on-ink);
  font: 400 15px/1.55 var(--sans);
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
}
button { font: 500 14px var(--sans); cursor: pointer; }
button:disabled { opacity: .4; cursor: not-allowed; }
:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
[hidden] { display: none !important; }

.btn {
  background: var(--gold-line); border: 1px solid var(--gold-line); color: #17130F;
  border-radius: 6px; padding: 9px 16px; font-weight: 600;
  transition: background .15s, transform .1s;
}
.btn:hover:not(:disabled) { background: var(--gold); }
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn.ghost { background: rgba(0,0,0,.35); color: var(--on-ink); border-color: var(--hairline); }
.btn.ghost:hover:not(:disabled) { background: var(--ink-3); color: var(--paper); }
.btn.big { padding: 13px 22px; font-size: 15px; }

input[type=text], select {
  font: 500 14px var(--sans); color: var(--paper);
  background: rgba(0,0,0,.45); border: 1px solid var(--hairline); border-radius: 6px;
  padding: 11px 13px;
}
input#code { font-family: var(--mono); text-transform: uppercase; letter-spacing: .14em; width: 7.6em; text-align: center; }
input::placeholder { color: var(--on-ink-dim); }

/* ── the room itself ─────────────────────────────────────────────────── */

#app {
  position: fixed; inset: 0; overflow: hidden;
  /* A lit table in a dark room. The pool of light follows the scene: over the
     hand while picking, over the centre while the turn resolves. */
  background:
    radial-gradient(90% 70% at 50% var(--light-y), #2A2117 0%, #16110C 48%, var(--ink) 100%);
  transition: --light-y .9s var(--ease);
}
/* Scene defaults at zero specificity. Set on #app directly they would outrank
   every [data-mode] rule below — an ID beats an attribute selector whatever the
   order — and no scene would ever resize anything. */
:where(#app) {
  --light-y: 72%;
  --hand-card: clamp(220px, 42vh, 400px); /* the hand's one layout size */
  --seat-to: 40px;
  --me-k-to: 0; --me-off-to: 3vh;
}
@property --light-y { syntax: "<percentage>"; inherits: false; initial-value: 72%; }

/* The aether (fx.js) renders here, under every region. If WebGL is missing the
   canvas never appears and the gradient above is the fallback. */
.aether { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; display: block; }

/* Every region shares one motion language. (.seats adds --seat further down.) */
.hud, .seats, .stage, .me, .hand, .handhint, .focus, .results, .over, .lobby, .log {
  position: absolute;
  transition:
    opacity var(--t) var(--ease),
    transform var(--t) var(--ease),
    translate var(--t) var(--ease),
    top var(--t) var(--ease),
    bottom var(--t) var(--ease),
    left var(--t) var(--ease),
    padding var(--t) var(--ease),
    filter var(--t) var(--ease),
    visibility 0s linear var(--t);
}

/* ── lobby: a character select ─────────────────────────────────────────
   Roster across the top, the chosen class filling the screen below it, and
   a slim bar for your name and the room. Everything the dossier says comes
   from src/engine/data/classes.ts. */

.lobby {
  --pad: clamp(16px, 3vw, 44px);
  inset: 0; z-index: 20; overflow-y: auto; overflow-x: hidden;
  display: grid; grid-template-rows: auto 1fr auto;
  padding: 16px var(--pad) 0;
  background: radial-gradient(80% 70% at 30% 50%, transparent 0%, rgba(8,6,4,.6) 100%);
}
/* zero specificity, so any component margin below wins */
:where(.lobby) :is(p, h2, h3) { margin: 0; }

.lobby__top { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 20px; }
.lobby__logo { width: clamp(140px, 14vw, 210px); height: auto; justify-self: start; filter: drop-shadow(0 4px 16px rgba(0,0,0,.7)); }

/* the roster */
.classpick { grid-column: 2; display: flex; gap: clamp(4px, 1.1vw, 16px); }
.classpick button {
  display: grid; justify-items: center; gap: 7px; padding: 4px 2px; border: 0; background: none;
  color: var(--on-ink-dim); cursor: pointer; border-radius: 10px;
}
.classpick img {
  width: clamp(44px, 4.4vw, 62px); aspect-ratio: 1; border-radius: 50%; object-fit: cover; display: block;
  border: 1.5px solid var(--hairline); background: var(--ink-2);
  filter: saturate(.55) brightness(.75);
  transition: filter .25s, transform .5s var(--arrive), border-color .25s, box-shadow .35s;
}
.classpick span { font: 500 var(--fs-cap) var(--sans); transition: color .25s; }
.classpick button:hover img { filter: none; transform: translateY(-2px); }
.classpick button:hover span { color: var(--on-ink); }
.classpick button[aria-checked="true"] img {
  filter: none; border-color: var(--gold); transform: translateY(-3px) scale(1.08);
  box-shadow: 0 0 0 3px rgba(224,185,74,.16), 0 0 28px rgba(224,185,74,.38);
}
.classpick button[aria-checked="true"] span { color: var(--gold); }
/* one Class per player: someone else's is shown with their name, and locked */
.classpick button { position: relative; }
/* under the name, out of the layout, so the roster keeps its height */
.classpick__who {
  position: absolute; top: 100%; left: 50%; translate: -50% -3px;
  font: 500 var(--fs-cap) var(--sans); color: var(--on-ink-dim); max-width: 7em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.classpick button.taken { cursor: not-allowed; }
.classpick button.taken img { filter: grayscale(1) brightness(.45); transform: none; }
.classpick button.taken span { color: var(--on-ink-dim); text-decoration: line-through; text-decoration-color: rgba(198,188,168,.4); }

/* the select: stage and path on the left, dossier on the right */
.select {
  /* no min-height: 0 here — if the class outgrows the screen, the lobby
     scrolls rather than letting it slide under the bar */
  display: grid; width: min(1320px, 100%); margin: 0 auto; padding: 6px 0 18px;
  grid-template-columns: minmax(0, .95fr) minmax(0, 1.05fr);
  grid-template-rows: minmax(0, 1fr) auto;
  grid-template-areas: "stage dossier" "path dossier";
  column-gap: clamp(24px, 4.5vw, 72px); row-gap: 10px; align-items: center;
}

.select__stage { grid-area: stage; position: relative; height: 100%; min-height: clamp(220px, 36vh, 460px); }
/* one full-size cell, so the art's height can be a percentage of the stage */
.select__hero, .select__preview { position: absolute; inset: 0; display: grid; grid-template: 100% / 100%; place-items: center; }
.select__hero { transition: opacity .35s ease, filter .35s ease, scale .5s var(--settle); }
/* a pool of light the figure stands in */
.select__hero::before {
  content: ""; grid-area: 1 / 1; height: 112%; max-width: 100%; aspect-ratio: 1; border-radius: 50%;
  background: radial-gradient(closest-side, rgba(224,185,74,.3), rgba(224,185,74,.09) 52%, transparent 74%);
  animation: breathe 6s ease-in-out infinite;
}
.select__hero img {
  grid-area: 1 / 1; height: 100%; width: 100%; object-fit: contain;
  filter: drop-shadow(0 26px 40px rgba(0,0,0,.7));
  animation: hero-in .9s var(--arrive) both, hover-float 7s ease-in-out .9s infinite;
}
.select__hero img.out { animation: hero-out .45s ease forwards; }
@keyframes hero-in { from { opacity: 0; scale: .84; translate: 0 18px; } }
@keyframes hero-out { to { opacity: 0; scale: 1.08; } }
@keyframes hover-float { 50% { translate: 0 -9px; } }
@keyframes breathe { 50% { scale: 1.06; opacity: .8; } }

/* reading a card from the path: the art steps back, the card steps up */
.select__preview { pointer-events: none; --card-h: min(44vh, 440px); }
.select__preview .card { box-shadow: 0 26px 60px rgba(0,0,0,.85); animation: preview-in .42s var(--arrive) both; }
.select__stage:not([data-preview]) .select__preview { opacity: 0; transition: opacity .15s; }
.select__stage[data-preview] .select__hero { opacity: .14; filter: blur(4px); scale: .96; }
@keyframes preview-in { from { opacity: 0; scale: .9; translate: 0 14px; } }

/* the dossier */
.dossier { grid-area: dossier; align-self: center; display: flex; flex-direction: column; gap: 12px; max-width: 640px; text-align: left; }
.dossier > * { animation: dossier-in .6s var(--arrive) both; }
.dossier > :nth-child(2) { animation-delay: .04s; } .dossier > :nth-child(3) { animation-delay: .08s; }
.dossier > :nth-child(4) { animation-delay: .12s; } .dossier > :nth-child(5) { animation-delay: .16s; }
.dossier > :nth-child(6) { animation-delay: .2s; } .dossier > :nth-child(7) { animation-delay: .24s; }
@keyframes dossier-in { from { opacity: 0; translate: 14px 0; } }

.dossier__epithet { font: 400 16px/1.4 var(--serif); color: var(--on-ink-dim); }
.dossier__name {
  font: 600 clamp(48px, 5.4vw, 82px)/.95 var(--serif); letter-spacing: -.01em; color: var(--paper);
  text-shadow: 0 0 44px rgba(224,185,74,.2), 0 4px 24px rgba(0,0,0,.6); margin-top: -2px;
}
.dossier__tagline { font: 500 clamp(20px, 1.7vw, 24px)/1.3 var(--serif); color: var(--gold); margin-top: -4px; }

.facts { display: flex; margin: 2px 0 0; padding: 10px 0; border-block: 1px solid var(--hairline); }
.facts > div { flex: 1; padding: 0 18px; border-left: 1px solid var(--hairline); min-width: 0; }
.facts > div:first-child { padding-left: 0; border-left: 0; }
.facts dt { font: 500 var(--fs-label) var(--sans); color: var(--gold-dim); }
.facts dd { margin: 6px 0 0; display: flex; align-items: baseline; gap: 7px; font: 400 var(--fs-label) var(--sans); color: var(--on-ink); }
.facts dd b { font: 600 26px/1 var(--mono); color: var(--paper); font-variant-numeric: tabular-nums; }
.facts dd b.word { font: 600 25px/1 var(--serif); }

.dossier__play { font: 400 var(--fs-body)/1.55 var(--sans); color: var(--on-ink); max-width: 62ch; text-wrap: pretty; }
.dossier__mech { font: 400 15px/1.55 var(--sans); color: var(--on-ink); max-width: 62ch; }
.dossier__mech b { font: 600 17px var(--serif); color: var(--gold); margin-right: .35em; }

/* the deck profile: one cell per level-up card */
.profile { --k: 104px; --gap: 12px; margin-top: 2px; }
.profile h3, .path h3 { font: 600 15px var(--sans); color: var(--paper); margin-bottom: 8px; }
.meter { display: grid; grid-template-columns: var(--k) minmax(0, 290px) 2ch; align-items: center; column-gap: var(--gap); height: 23px; }
.meter__k { font: 500 var(--fs-label) var(--sans); color: var(--on-ink); }
.meter__cells { display: grid; grid-template-columns: repeat(10, 1fr); gap: 3px; }
.meter__cells i { height: 8px; border-radius: 2px; background: rgba(245,241,232,.08); }
.meter__cells i.on { background: var(--gold); box-shadow: 0 0 9px rgba(224,185,74,.38); animation: cell-on .45s var(--arrive) both; animation-delay: calc(var(--i) * 26ms + .18s); }
@keyframes cell-on { from { background: rgba(245,241,232,.08); box-shadow: none; scale: 1 .3; } }
.meter b { font: 600 13px var(--mono); color: var(--paper); text-align: right; font-variant-numeric: tabular-nums; }

.mixrow { height: auto; margin-top: 8px; }
.mix i { height: 13px; width: auto; border-radius: 2px; animation: cell-on .45s var(--arrive) both; animation-delay: calc(var(--i) * 26ms + .3s); }
.mix .t, .mix__legend .k-t::before { background: #E6DCC6; }
.mix .m, .mix__legend .k-m::before { background: #97A3D9; }
.mix .p, .mix__legend .k-p::before { background: var(--gold-dim); }
.mix .x { background: rgba(245,241,232,.08); }
.mix__legend { display: flex; flex-wrap: wrap; gap: 2px 16px; margin: 8px 0 0 calc(var(--k) + var(--gap)); font: 500 var(--fs-cap) var(--sans); color: var(--on-ink); }
.mix__legend span::before { content: ""; display: inline-block; width: 8px; height: 8px; border-radius: 2px; margin-right: 7px; }
.mix__legend em { flex-basis: 100%; font: 400 var(--fs-cap) var(--sans); font-style: normal; color: var(--on-ink-dim); }

/* the level path */
.path { grid-area: path; align-self: start; justify-self: center; width: min(580px, 100%); text-align: left; container-type: inline-size; }
.path__list > li, .pc { min-width: 0; }
@container (max-width: 500px) {
  .path__list { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; grid-auto-flow: row; }
}
.path h3 span { font: 400 var(--fs-label) var(--sans); color: var(--on-ink-dim); margin-left: .6em; }
/* the cards you start with, as a sentence of links rather than a row of chips */
.path__core { margin: 0 0 10px; font: 400 var(--fs-label)/1.5 var(--sans); color: var(--on-ink-dim); }
.path__core .pc.inline { display: inline; width: auto; padding: 0; border-radius: 0; font: 500 var(--fs-label) var(--sans); color: var(--paper); text-decoration: underline; text-decoration-color: var(--gold-dim); text-underline-offset: 3px; }
.path__core .pc.inline:hover, .path__core .pc.inline.on { background: none; color: var(--gold); text-decoration-color: var(--gold); }
.path__core > span { font: 500 var(--fs-label) var(--sans); color: var(--on-ink-dim); }
.path__list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: repeat(5, auto); grid-auto-flow: column; column-gap: 16px; }
.pc {
  width: 100%; display: flex; align-items: center; gap: 10px; padding: 5px 8px; border: 0; border-radius: 6px;
  background: none; color: var(--paper); text-align: left; cursor: pointer; font: 500 15px var(--sans);
  transition: background .15s;
}
.pc:hover, .pc.on { background: rgba(224,185,74,.11); }
.pc.sig .pc__n { background: var(--gold-dim); color: var(--ink); }
.path__list .pc { border-radius: 4px; }
.pc__n {
  flex: none; width: 24px; height: 24px; border-radius: 50%; display: grid; place-items: center;
  font: 600 var(--fs-cap) var(--serif); color: var(--gold); background: var(--ink-3); border: 1px solid var(--gold-dim);
}
.pc__name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pc.sig .pc__name { color: var(--gold); }
.pc__lv { font: 500 var(--fs-cap) var(--mono); color: var(--on-ink-dim); }
.d20g { flex: none; width: 15px; height: 15px; fill: none; stroke: var(--gold-dim); stroke-width: 11; stroke-linejoin: round; }
.path__hint { margin-top: 10px; font: 400 var(--fs-cap) var(--sans); color: var(--on-ink-dim); }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* on touch, a card from the path opens over everything */
.cardsheet {
  position: fixed; inset: 0; z-index: 90; --card-h: min(70vh, 124vw);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px;
  background: rgba(8,6,4,.88); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  animation: fade-in .2s ease;
}
.cardsheet .card { animation: preview-in .42s var(--arrive) both; box-shadow: 0 24px 60px rgba(0,0,0,.85); }
.cardsheet p { margin: 0; font: 400 var(--fs-label) var(--sans); color: var(--on-ink-dim); }
.cardsheet.out { opacity: 0; transition: opacity .2s; }

/* the bar: your name, then the room */
.lobby__bar {
  position: sticky; bottom: 0; z-index: 2; margin: 0 calc(var(--pad) * -1);
  padding: 12px var(--pad) calc(12px + env(safe-area-inset-bottom));
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 12px 26px;
  background: linear-gradient(0deg, rgba(10,8,6,.97), rgba(10,8,6,.84));
  border-top: 1px solid var(--hairline);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
.lobby__name { display: flex; align-items: center; gap: 10px; }
.lobby__name span, .lobby__or, .lobby__room span {
  font: 500 var(--fs-label) var(--sans); color: var(--on-ink-dim); white-space: nowrap;
}
.lobby__name input { width: 16em; font-size: 15px; }
.lobby__join, .lobby__wait { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 10px 12px; }
.lobby[data-step="join"] .lobby__wait, .lobby[data-step="wait"] .lobby__join { display: none; }
.lobby__room { display: flex; align-items: baseline; gap: 10px; }
.lobby__room b { font: 600 26px var(--mono); letter-spacing: .16em; color: var(--gold); }
.lobby__seated { display: flex; flex-wrap: wrap; gap: 6px; }
.lobby__seated { gap: 16px; }
.lobby__seated .who { display: flex; align-items: center; gap: 9px; animation: rise .6s var(--arrive) both; }
.lobby__seated img { width: 34px; height: 34px; border-radius: 50%; object-fit: cover; border: 1px solid var(--gold-dim); }
.lobby__seated .who span { display: grid; line-height: 1.2; }
.lobby__seated b { font: 600 var(--fs-label) var(--sans); color: var(--paper); max-width: 9em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lobby__seated small { font: 400 var(--fs-cap) var(--sans); color: var(--gold); }
@keyframes rise { from { opacity: 0; transform: translateY(12px) scale(.94); } }
#newRoom .sm, .hud__btn .sm { display: none; }
.lobby__help { grid-column: 3; justify-self: end; }
.lobby__seated .who.away { opacity: .5; }
.who__x {
  margin-left: 2px; width: 24px; height: 24px; border-radius: 4px; border: 1px solid var(--hairline); background: none;
  color: var(--on-ink-dim); font: 400 18px/1 var(--sans); cursor: pointer;
}
.who__x:hover { color: var(--paper); border-color: var(--blood); background: rgba(180,67,42,.2); }
.lobby[data-practice] #addBot { display: none; }

/* ── HUD ─────────────────────────────────────────────────────────────── */

.hud {
  top: 0; left: 0; right: 0; height: var(--hud); z-index: 30;
  display: flex; align-items: center; gap: 14px; padding: 0 18px;
  background: linear-gradient(180deg, rgba(0,0,0,.6), transparent);
}
.hud__logo { height: 24px; width: auto; }
.hud__btn {
  background: transparent; border: 1px solid var(--hairline); color: var(--on-ink-dim);
  border-radius: 4px; padding: 5px 12px; font: 500 var(--fs-label) var(--sans);
}
.hud__btn[aria-expanded="true"] { color: var(--gold); border-color: var(--gold-dim); }
.hud__scene {
  flex: 1; text-align: center;
  font: 600 15px var(--sans); color: var(--gold);
}
.hud__turn { font: 500 var(--fs-label) var(--sans); color: var(--on-ink-dim); font-variant-numeric: tabular-nums; }

.timer { position: relative; width: 44px; height: 44px; flex: none; transition: opacity var(--t); }
.timer svg { transform: rotate(-90deg); width: 100%; height: 100%; }
.timer__track { fill: none; stroke: rgba(224,185,74,.14); stroke-width: 4; }
.timer__arc { fill: none; stroke: var(--gold); stroke-width: 4; stroke-linecap: round; transition: stroke-dashoffset .25s linear, stroke .3s; }
.timer.urgent .timer__arc { stroke: var(--blood-lit); }
.timer.urgent { animation: pulse 1s ease-in-out infinite; }
.timer__n { position: absolute; inset: 0; display: grid; place-items: center; font: 600 14px var(--mono); color: var(--paper); font-variant-numeric: tabular-nums; }
@keyframes pulse { 50% { transform: scale(1.08); } }

/* ── log drawer ──────────────────────────────────────────────────────── */

.log {
  top: var(--hud); left: 0; bottom: 0; width: min(300px, 80vw); z-index: 25;
  background: rgba(10,8,6,.94); border-right: 1px solid var(--hairline);
  backdrop-filter: blur(6px); transform: translateX(-102%);
  display: flex; flex-direction: column;
}
.log.open { transform: none; }
.log__head { padding: 14px 16px 8px; font: 600 15px var(--sans); color: var(--paper); }
#log { flex: 1; overflow: auto; padding: 0 16px 14px; font: 400 var(--fs-cap)/1.55 var(--sans); }
#log div .t { color: var(--on-ink-dim); font-variant-numeric: tabular-nums; margin-right: .6em; }
#log div { padding: 3px 0; border-bottom: 1px solid rgba(255,255,255,.05); color: var(--on-ink-dim); }
#log div.k-damage { color: var(--blood-lit); }
#log div.k-heal { color: var(--heal); }
#log div.k-roll { color: var(--gold); }
#log div.k-reveal, #log div.k-level_up { color: var(--paper); }
#log div.k-execute, #log div.k-game_over { color: var(--gold); font-weight: 500; }

.banner {
  position: absolute; left: 50%; translate: -50% 0; top: calc(var(--hud) + 8px); z-index: 40;
  background: rgba(20,16,12,.96); border: 1px solid var(--gold-dim); border-radius: 8px;
  padding: 9px 16px; font: 400 var(--fs-label) var(--sans); color: var(--on-ink); cursor: pointer;
  box-shadow: 0 8px 24px rgba(0,0,0,.6); animation: rise .3s var(--ease);
}
.banner b { color: var(--gold); font: 600 14px var(--mono); letter-spacing: .1em; }
.banner.err { border-color: var(--blood); color: var(--blood-lit); }

/* ── seats ───────────────────────────────────────────────────────────── */

.seats, .me { --seat: var(--seat-to); }
.seats {
  left: 0; right: 0; top: calc(var(--hud) + 8px); z-index: 10;
  display: flex; justify-content: center; align-items: flex-start; flex-wrap: wrap;
  gap: calc(var(--seat) * .45); padding: 0 3vw;
}

.seats {
  transition:
    opacity var(--t) var(--ease), translate var(--t) var(--ease), top var(--t) var(--ease), bottom var(--t) var(--ease),
    padding var(--t) var(--ease), visibility 0s linear var(--t), --seat var(--t) var(--ease);
}

.seat {
  position: relative; display: grid;
  grid-template-columns: var(--seat) auto; grid-template-rows: auto auto;
  column-gap: calc(var(--seat) * .26); align-items: center;
  padding: calc(var(--seat) * .2) calc(var(--seat) * .34) calc(var(--seat) * .2) calc(var(--seat) * .2);
  background: linear-gradient(180deg, rgba(40,33,25,.92), rgba(20,16,12,.92));
  border: 1px solid var(--hairline); border-radius: calc(var(--seat) * .28);
  transition: border-color .3s, box-shadow .3s, transform .3s var(--ease), opacity .3s, filter .3s;
}
.seat.is-you { border-color: var(--gold-dim); }
.seat.is-out { opacity: .35; filter: grayscale(1); }
/* lost their connection: still in the match, so dimmed rather than greyed out */
.seat.is-away .seat__portrait { filter: grayscale(1) brightness(.7); }
.seat__away { margin-left: .4em; padding: 0 6px; border-radius: 4px; font: 500 var(--fs-cap) var(--sans); color: var(--on-ink); background: rgba(255,255,255,.08); border: 1px solid var(--hairline); }
.seat.targetable { cursor: pointer; border-color: var(--gold); box-shadow: 0 0 0 1px var(--gold), 0 0 34px rgba(224,185,74,.28); }
.seat.targetable:hover { transform: translateY(-4px) scale(1.03); }
/* While choosing, whatever can be chosen pulses and says so; everything else steps back. */
[data-mode="target"] .seat.targetable { animation: aim 1.6s ease-in-out infinite; }
/*
 * Your own seat, lifted into the enemy row while a card that helps is held up.
 *
 * Green rather than the targeting gold, because the whole point is that this
 * seat is not like the others around it: the row is enemies, and this one is
 * you. Jacob kept healing his opponents because his seat was at the bottom of
 * the screen while his cursor was up here.
 */
.seat.is-promoted {
  border-color: var(--heal);
  box-shadow: 0 0 0 1px var(--heal), 0 0 34px rgba(159, 208, 138, .30);
}
.seat.is-promoted .seat__id::before {
  content: "You · ";
  color: var(--heal);
  font-weight: 700;
}
[data-mode="target"] .seat.targetable::before {
  content: "Choose"; position: absolute; left: 50%; bottom: 100%; translate: -50% -8px;
  padding: 3px 10px; border-radius: 4px; white-space: nowrap;
  font: 600 var(--fs-label) var(--sans); color: var(--ink); background: var(--gold);
  box-shadow: 0 4px 14px rgba(0,0,0,.6); pointer-events: none;
}
[data-mode="target"] .seat:not(.targetable):not(.picked):not(:has(.targetable)) { opacity: .4; filter: grayscale(.7); }
@keyframes aim {
  50% { box-shadow: 0 0 0 3px var(--gold), 0 0 60px rgba(224,185,74,.55); }
}
.seat.picked { border-color: var(--gold); box-shadow: 0 0 0 3px var(--gold), 0 0 44px rgba(224,185,74,.4); }

/* Aiming after the reveal: a Defending Character is still a legal target, but
   the shot is stopped. Shown as shielded rather than removed, because Brutality
   and a banked Assault both want a Defender. */
.seat.is-defending { border-color: var(--ice, #8fb6d8); }
.seat.is-defending .seat__portrait { filter: brightness(.82) saturate(.7); }
/* A real element, not a pseudo: ::after is the hit sweep and ::before is the
   "Choose" chip, and a Defending seat is usually still choosable. */
.seat__shield {
  position: absolute; inset-inline: 0; bottom: -1.05rem;
  font: 600 .62rem/1 var(--sans); letter-spacing: .08em; text-transform: uppercase;
  color: var(--ice, #8fb6d8); text-align: center; opacity: .9; pointer-events: none;
}
/* ── combat (combat.js draws the strikes; these are what a seat does) ── */
.combat { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 58; pointer-events: none; overflow: visible; }
/* a sweep of colour through the seat: up and green for a heal, down and red for a status */
.seat::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; opacity: 0;
  background: linear-gradient(0deg, transparent 0%, var(--sweep, transparent) 50%, transparent 100%) 0 0 / 100% 300% no-repeat;
}
.seat.mending { --sweep: rgba(159,208,138,.55); }
.seat.afflicted { --sweep: rgba(224,113,79,.55); }
.seat.mending::after { animation: sweep-up .85s ease-out; }
.seat.afflicted::after { animation: sweep-down .85s ease-out; }
@keyframes sweep-up { 0% { opacity: 1; background-position: 0 0; } 100% { opacity: 0; background-position: 0 100%; } }
@keyframes sweep-down { 0% { opacity: 1; background-position: 0 100%; } 100% { opacity: 0; background-position: 0 0; } }
.seat.hit.struck { animation: seat-hit .45s ease, struck .52s ease-out; }
@keyframes struck { 0% { box-shadow: inset 0 0 0 2px var(--blood-lit), 0 0 40px rgba(224,113,79,.6); border-color: var(--blood-lit); } 100% { box-shadow: none; } }
.seat.blocked { animation: blocked .6s var(--arrive); }
@keyframes blocked { 0% { box-shadow: 0 0 0 4px var(--gold), 0 0 50px rgba(224,185,74,.7); scale: 1.04; } 100% { scale: 1; } }
/* at 0 Life Sparks: the seat drains of colour and sinks */
.seat.falling { animation: falling 1.1s ease-in forwards; }
@keyframes falling { 0% { filter: none; } 40% { filter: grayscale(1) brightness(1.3); rotate: -2deg; } 100% { filter: grayscale(1) brightness(.6); opacity: .45; translate: 0 10px; rotate: 0deg; } }
/* Defend: the shield comes up over the portrait and stays for the turn */
.seat__ward {
  position: absolute; left: calc(var(--seat) * .2 - 4px); top: calc(var(--seat) * .2 - 4px); z-index: 2;
  width: calc(var(--seat) + 8px); height: calc(var(--seat) + 8px); border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle, rgba(20,16,12,.55) 40%, rgba(224,185,74,.25) 100%), url("/art/sym-defend.webp") center / 72% no-repeat;
  box-shadow: 0 0 0 2px var(--gold), 0 0 26px rgba(224,185,74,.55);
  animation: ward-up .6s var(--arrive) both;
}
@keyframes ward-up { from { opacity: 0; scale: 1.7; } }
@media (prefers-reduced-motion: reduce) { .seat::after, .seat__ward { animation: none !important; } }

.seat.hit { animation: seat-hit .45s ease; }
@keyframes seat-hit {
  0%,100% { translate: 0 0; } 20% { translate: -6px 2px; } 45% { translate: 5px -2px; } 70% { translate: -2px 1px; }
}

.seat__portrait {
  grid-row: 1 / span 2; width: var(--seat); height: var(--seat); border-radius: 50%;
  border: 2px solid var(--gold-dim); object-fit: cover; background: var(--ink-3);
}
.seat__id { min-width: 0; line-height: 1.15; }
.seat__name {
  font: 600 max(14px, calc(var(--seat) * .3)) var(--sans); color: var(--paper);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: calc(var(--seat) * 3.4);
}
.seat__class { font: 400 max(13px, calc(var(--seat) * .19)) var(--sans); color: var(--gold); }
.sparks { display: flex; align-items: center; gap: 5px; }
.sparks img { width: max(12px, calc(var(--seat) * .3)); height: auto; }
.sparks b {
  font: 600 max(15px, calc(var(--seat) * .42)) var(--mono); color: var(--life);
  font-variant-numeric: tabular-nums; line-height: 1; transition: color .2s, transform .2s;
}
.sparks.drain b { color: var(--blood-lit); transform: scale(1.18); }
.sparks.gain b { color: var(--heal); transform: scale(1.18); }

.badges { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 4px; margin-top: calc(var(--seat) * .14); }
.badges:empty { display: none; }
.badge {
  font: 500 var(--fs-cap) var(--sans); text-transform: capitalize;
  padding: 2px 7px; border-radius: 4px;
  background: rgba(180,67,42,.2); border: 1px solid var(--blood); color: var(--blood-lit);
}
.badge.res { background: rgba(224,185,74,.14); border-color: var(--gold-dim); color: var(--gold); }
.badge.imm { background: rgba(120,160,120,.14); border-color: #5E7F5E; color: var(--heal); }

/* the effects strip: statuses (red), protection (green), what they have going
   (gold). Hover or hold a chip to read it. */
.fx { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 4px; margin-top: calc(var(--seat) * .16); max-width: max(250px, calc(var(--seat) * 5.2)); }
.chip {
  display: inline-flex; align-items: baseline; gap: 6px; padding: 3px 8px; border-radius: 4px;
  font: 500 var(--fs-cap)/1.25 var(--sans); white-space: nowrap; cursor: help;
  border: 1px solid var(--hairline); background: rgba(10,8,6,.78); color: var(--on-ink);
}
.chip b { font-weight: 600; }
.chip i { font-style: normal; font-variant-numeric: tabular-nums; opacity: .8; }
.chip.t-bad { border-color: #8A3322; background: rgba(62,20,13,.82); color: #F2A38C; }
.chip.t-good { border-color: #50704F; background: rgba(20,38,22,.82); color: #AFD8AE; }
.chip.t-boost { border-color: var(--gold-dim); background: rgba(40,31,14,.82); color: var(--gold); }
/* a passive switched off (Dazed) keeps its place but reads as off */
.chip.off { border-style: dashed; opacity: .5; }
button.chip.targetable { cursor: pointer; border-color: var(--gold); box-shadow: 0 0 0 1px var(--gold), 0 0 16px rgba(224,185,74,.35); }
button.chip.targetable:hover { background: rgba(80,62,24,.9); }

/* The commitment slot. A tucked-in card back while picking; a full slot on the
   table once everyone is laying cards down. */
.seat__slot {
  position: absolute; top: -9px; right: -9px;
  width: 22px; height: 31px; border-radius: 3px;
  background: rgba(0,0,0,.35) center / cover no-repeat;
  border: 1px dashed var(--hairline);
  opacity: 0; transition: all var(--t) var(--ease);
}
.seat__slot.filled { opacity: 1; border: 1px solid var(--gold-dim); box-shadow: 0 3px 8px rgba(0,0,0,.6); transform: rotate(7deg); }
.seat__slot span { display: none; }

/* ── your own seat ───────────────────────────────────────────────────── */

/* max-content: pinned near an edge, an absolute box otherwise only gets the
   leftover width to lay out in, and the seat collapses. */
.me {
  z-index: 12; left: 20px; width: max-content;
  --me-k: var(--me-k-to); --me-off: var(--me-off-to);
  bottom: calc(var(--hand-card) * var(--me-k) + var(--me-off));
  /* no transition on bottom: it is moved by the three variables animating here */
  transition: opacity var(--t) var(--ease), translate var(--t) var(--ease), left var(--t) var(--ease), visibility 0s linear var(--t),
    --seat var(--t) var(--ease), --me-k var(--t) var(--ease), --me-off var(--t) var(--ease);
}

/* ── stage ───────────────────────────────────────────────────────────── */

.stage {
  left: 0; right: 0; top: 30vh; bottom: 24vh; z-index: 5;
  display: flex; align-items: center; justify-content: center;
  --card-h: clamp(170px, 32vh, 320px);
}
.stage__band {
  position: absolute; top: -6px; left: 50%; translate: -50% 0;
  font: 600 15px var(--sans);
  color: var(--gold); opacity: 0; transition: opacity .3s;
}
.stage__band.on { opacity: 1; }
.stage__cards { display: flex; gap: 2vw; align-items: flex-start; justify-content: center; flex-wrap: wrap; }

/*
 * What just happened, said once per beat.
 *
 * Jacob, 2026-09-18: "spend the time saved on faster dice showing what actually
 * happened, in resolution order." The engine already writes a line for every
 * event; the turn simply never read them out. Below the cards rather than above
 * them, because the eye is on the table and the band label already owns the top.
 */
.stage__say {
  position: absolute; bottom: -12px; left: 50%; translate: -50% 0;
  margin: 0; width: max-content; max-width: min(620px, 88vw);
  font: 500 var(--fs-lead) var(--serif); color: var(--on-ink);
  text-align: center; text-wrap: balance; text-shadow: 0 2px 12px #000;
  opacity: 0;
}
.stage__say.on { animation: say-in .34s var(--arrive) forwards; }
@keyframes say-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .stage__say.on { animation: none; opacity: 1; }
}
.stage__quiet { font: 400 var(--fs-lead) var(--serif); color: var(--on-ink-dim); animation: fade-in .5s ease; }
.stage__owner { text-align: center; font: 500 var(--fs-label) var(--sans); color: var(--gold-dim); margin-top: 10px; }
.pending { animation: deal .7s var(--arrive); text-align: center; }
.pending__card { box-shadow: 0 0 0 1px var(--gold-dim), 0 16px 40px rgba(0,0,0,.75); }
.pending__undo { display: block; margin: 10px auto 0; }
:not([data-mode="lay"]) > .stage .pending__undo { display: none; }
@keyframes deal { from { transform: translateY(40vh) rotate(-10deg) scale(.7); opacity: 0; } }
@keyframes fade-in { from { opacity: 0; } }

/* ── cards ─────────────────────────────────────────────────────────────
   Jacob's printed frame (art/frame-*.webp, by class type) over a parchment
   window. Positions are fractions of the card, measured off the 1500x2100
   frame: the window runs 8–92% across and 11–70% down; the plate under it
   carries the class, then the type line; Lv and the class emblem sit in the
   two bottom sockets. Every size is a fraction of --card-h, so one card reads
   the same in the hand, on the stage and in the zoom. */

.card {
  --floor: 0px;             /* raised to 13px wherever a card is for reading */
  width: calc(var(--card-h) * 5 / 7); height: var(--card-h);
  border-radius: calc(var(--card-h) * .032); position: relative; flex: none; overflow: hidden;
  background: #09090C; box-shadow: 0 4px 14px rgba(0,0,0,.55);
  color: #2B241D; text-align: center; font-family: var(--sans); padding: 0; border: 0;
  contain: layout style;
}
/* the frame, over everything but the lettering that sits on it */
.card::after {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: var(--frame) center / 100% 100% no-repeat;
}
/* plain white behind the rules: nothing competes with the text */
.card__win { position: absolute; left: 6.5%; right: 6.5%; top: 10%; bottom: 28.5%; background: #FFFFFF; }
.card__name, .card__text { position: absolute; z-index: 0; }
.card__num, .card__plate, .card__type, .card__lv, .card__emblem, .card__socket, .card__counter, .card__gap { position: absolute; z-index: 2; }

.card__num {
  left: 3.6%; top: 2.6%; width: 14.5%; aspect-ratio: 1; border-radius: 50%;
  display: grid; place-items: center;
  background: radial-gradient(circle at 50% 38%, #2A2419, #0B0906 70%);
  border: calc(var(--card-h) * .005) solid var(--gold-line);
  box-shadow: 0 0 0 calc(var(--card-h) * .003) rgba(0,0,0,.8), 0 2px 8px rgba(0,0,0,.7);
  font: 600 calc(var(--card-h) * .05) var(--serif); color: var(--gold); font-variant-numeric: lining-nums;
}
/* card names are capitals because the printed cards are */
.card__name {
  left: 12%; right: 12%; top: 12.6%; height: 7%;
  display: flex; align-items: center; justify-content: center;
  font: 700 calc(var(--card-h) * .047)/1.05 var(--serif); letter-spacing: .02em; text-transform: uppercase; color: #1B1510;
  border-bottom: 1px solid rgba(60,45,25,.25);
}
.card[data-name="m"] .card__name { font-size: calc(var(--card-h) * .039); }
.card[data-name="l"] .card__name { font-size: calc(var(--card-h) * .032); }
.card__text {
  left: 11%; right: 11%; top: 20.5%; bottom: 31%;
  display: flex; flex-direction: column; justify-content: safe center;
  overflow: auto; scrollbar-width: thin; color: #2E2720;
  font: 400 max(var(--floor), calc(var(--card-h) * var(--fs, .044) * var(--fit, 1)))/1.36 var(--serif);
}
.card[data-len="m"] { --fs: .037; }
.card[data-len="l"] { --fs: .032; }
.card[data-len="xl"] { --fs: .0285; }
.card__text::-webkit-scrollbar { width: 3px; }
.card__text::-webkit-scrollbar-thumb { background: rgba(0,0,0,.2); border-radius: 2px; }

.card__plate {
  left: 18%; right: 18%; top: 71.6%; height: 5.2%;
  display: grid; place-items: center; white-space: nowrap;
  font: 600 max(var(--floor), calc(var(--card-h) * .042)) var(--serif); color: var(--paper);
  text-shadow: 0 1px 6px #000;
}
/* the type line sits between the two symbol sockets, as printed */
.card__type {
  left: 19%; right: 19%; top: 86.2%; height: 9%;
  display: flex; align-items: center; justify-content: center; text-align: center;
  font: 500 max(var(--floor), calc(var(--card-h) * .031))/1.2 var(--serif); color: var(--gold);
  text-shadow: 0 1px 6px #000, 0 0 2px #000;
}
/* the flex box above centres it; this inner clamp keeps it to two lines */
.card__type { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; align-content: center; }
/* level and class emblem flank the class name bar */
.card__lv, .card__emblem, .card__socket {
  width: 12.6%; aspect-ratio: 1; border-radius: 50%;
  border: calc(var(--card-h) * .004) solid var(--gold-line);
  box-shadow: 0 2px 8px rgba(0,0,0,.7);
}
.card__lv, .card__emblem { top: 69.7%; }
.card__lv {
  left: 4.4%; display: grid; place-items: center;
  background: radial-gradient(circle at 50% 38%, #2A2419, #0B0906 70%);
  font: 600 calc(var(--card-h) * .03) var(--serif); color: var(--gold); font-variant-numeric: lining-nums;
}
.card__emblem { right: 4.4%; background: var(--art) center / cover no-repeat, #0B0906; }
/* the foot sockets: what kind of Action (left), what happens after (right) */
.card__socket { top: 86.2%; background: radial-gradient(circle at 50% 38%, #2A2419, #0B0906 70%); }
.card__socket--l { left: 4.4%; }
.card__socket--r { right: 4.4%; }
/*
 * Priority: the dragon, top right.
 *
 * It gets a socket of its own because Priority is about *when* a card resolves,
 * not what kind it is — every Priority card is also a Defend, a Magic or a
 * Tactic, so sharing the left socket meant the dragon never showed at all.
 *
 * Top right rather than the middle of the foot, which it was first given: the
 * type line runs across there, and the dragon sat straight on top of "Core
 * Mechanic, Defend, Priority, Recharge". Up here it mirrors the level number
 * in the opposite corner and covers nothing.
 */
.card__socket--p { right: 4.4%; top: 3.2%; border-color: var(--gold); }

/*
 * The yellow ring the printed card wears.
 *
 * On the frame rather than the element's own border, which the frame art
 * already owns, so this reads as part of the card instead of a box round it.
 */
.card--priority {
  box-shadow: 0 0 0 calc(var(--card-h) * .006) var(--gold), 0 0 18px rgba(224,185,74,.22);
  border-radius: calc(var(--card-h) * .045);
}
.card__sym { position: absolute; inset: 12%; background: var(--sym) center / contain no-repeat; filter: drop-shadow(0 1px 2px rgba(0,0,0,.6)); }

.card__counter {
  /* the recharge count sits over the right socket's Recharge symbol */
  right: 4.4%; top: 86.2%;
  width: 12.6%; aspect-ratio: 1; border-radius: 50%; display: grid; place-items: center;
  font: 600 calc(var(--card-h) * .045) var(--mono); color: var(--ink);
  background: var(--gold); border: calc(var(--card-h) * .004) solid var(--gold-line);
  box-shadow: 0 0 0 calc(var(--card-h) * .006) rgba(224,185,74,.25), 0 2px 8px rgba(0,0,0,.7);
}
.card__gap { top: 4%; right: 5.5%; width: 3.2%; aspect-ratio: 1; border-radius: 50%; background: var(--blood); border: 1px solid rgba(0,0,0,.4); }

/* a card meant to be read keeps its type at 13px or more */
:is(.hand, .focus__card, .zoom, .select__preview, .cardsheet, .choice) .card { --floor: 13px; }

.card.back { background: center / cover no-repeat; border-color: var(--gold-dim); }
.card.back::before, .card.back::after, .card.back > * { display: none; }

/* flip */
.flip { perspective: 1200px; width: calc(var(--card-h) * 5 / 7); height: var(--card-h); flex: none; transition: transform .35s var(--ease), filter .35s; }
.flip__inner { position: relative; width: 100%; height: 100%; transform-style: preserve-3d; transform: rotateY(180deg); transition: transform .6s var(--ease); }
.flip.revealed .flip__inner { transform: rotateY(0deg); }
.flip__face, .flip__back { position: absolute; inset: 0; backface-visibility: hidden; }
.flip__back { transform: rotateY(180deg); }
.flip.acting { transform: translateY(-18px) scale(1.07); z-index: 4; transition: transform .6s var(--arrive), filter .35s; }
.flip.acting .flip__face .card { box-shadow: 0 0 0 2px var(--gold), 0 22px 50px rgba(0,0,0,.75); }
.flip.spent { filter: brightness(.6) saturate(.7); }

/* ── hand ────────────────────────────────────────────────────────────── */

.hand {
  left: 0; right: 0; bottom: 0; z-index: 15;
  /* the scene's card size, unless a full hand would not fit the width even
     at the deepest overlap; then the cards shrink to fit (--n is set by game.js) */
  --card-h: min(var(--hand-card), calc((100vw - 48px) / (1 + .3 * (var(--n, 1) - 1)) * 7 / 5));
  --cw: calc(var(--card-h) * 5 / 7);
  display: flex; justify-content: safe center; align-items: flex-end;
  padding: 28px 24px max(18px, env(safe-area-inset-bottom));
}
/* The fan is pure CSS: overlap only as much as the row needs to fit, and never
   more than 70% of a card. */
.hand > .card + .card {
  margin-left: clamp(calc(var(--cw) * -.7), calc((100vw - 48px - var(--n, 1) * var(--cw)) / max(1, var(--n, 1) - 1)), 14px);
}
.hand > .card { transform-origin: 50% 100%; }
.hand { transform-origin: 50% 100%; will-change: transform; }
button.card { cursor: pointer; transition: transform .5s var(--arrive), box-shadow .25s, filter .25s, margin .5s var(--settle); }
button.card:hover:not(:disabled) { transform: translateY(-7%) scale(1.03); box-shadow: 0 18px 38px rgba(0,0,0,.65); z-index: 5; }
button.card.selected { transform: translateY(-9%) scale(1.04); box-shadow: 0 0 0 2px var(--gold), 0 20px 42px rgba(0,0,0,.7); z-index: 6; }
button.card:disabled { filter: grayscale(.6) brightness(.72); }
.card.illegal { filter: grayscale(.8) brightness(.55); }
.card.new { animation: new-card 1.1s var(--arrive); }
@keyframes new-card {
  0% { transform: translateY(60%) rotate(4deg); box-shadow: 0 0 0 0 rgba(224,185,74,0); }
  40% { transform: translateY(-10%); box-shadow: 0 0 0 3px var(--gold), 0 0 50px rgba(224,185,74,.55); }
  100% { transform: none; }
}

.handhint {
  left: 50%; translate: -50% 0; width: fit-content; max-width: min(92vw, 62ch);
  z-index: 16; margin: 0; text-align: center;
  /* clear of a hovered card, which lifts about a tenth of its height */
  bottom: calc(var(--hand-card) * 1.12 + 40px);
  transition: opacity var(--t) var(--ease), visibility 0s linear var(--t);
  font: 400 var(--fs-label) var(--sans); color: var(--on-ink); pointer-events: none;
  text-shadow: 0 1px 3px #000, 0 0 14px rgba(0,0,0,.9);
}
.handhint b { color: var(--gold); font-weight: 600; }

/* ── focus (target mode) ─────────────────────────────────────────────── */

.focus {
  z-index: 18; left: 5vw; top: calc(50% + var(--hud) / 2); translate: 0 -50%;
  --card-h: min(62vh, 480px);
  display: flex; flex-direction: column; align-items: center; gap: 14px;
}
.focus__card .card { box-shadow: 0 0 0 2px var(--gold), 0 30px 70px rgba(0,0,0,.8); }
/* the one instruction in this scene, so it reads first */
/* A benefit about to land on an enemy: said in the heal colour, not the alarm
   colour, because nothing has gone wrong yet — it is one more tap away. */
.focus__hint.warn { color: var(--heal); }
.focus__hint { order: -1; margin: 0; max-width: min(420px, 90vw); font: 600 26px/1.2 var(--serif); color: var(--gold); text-align: center; text-wrap: balance; text-shadow: 0 2px 10px #000; }
.focus__btns { display: flex; gap: 10px; }
.focus__modes { display: flex; flex-direction: column; gap: 8px; width: min(340px, 90vw); }
/* stacks to spend on the held-up card (Lethality on Assassinate) */
.focus__spend {
  display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 4px 14px; width: min(340px, 90vw);
  padding: 10px 14px; border-radius: 4px; background: rgba(20,16,12,.92); border: 1px solid var(--gold-dim);
  font: 500 var(--fs-label) var(--sans); color: var(--paper);
}
.focus__spend[hidden] { display: none; }
.focus__spend small { grid-column: 1 / -1; font: 400 var(--fs-cap) var(--sans); color: var(--gold); }
.stepper { display: flex; align-items: center; gap: 10px; }
.stepper b { min-width: 2ch; text-align: center; font: 600 20px var(--mono); color: var(--gold); font-variant-numeric: tabular-nums; }
.stepper button { width: 32px; height: 32px; border-radius: 4px; border: 1px solid var(--gold-dim); background: rgba(0,0,0,.35); color: var(--paper); font: 600 18px/1 var(--sans); cursor: pointer; }
.stepper button:disabled { opacity: .35; cursor: default; }
.focus__modes .mode:disabled { opacity: .5; cursor: not-allowed; }
/* a card that asks how to play it: the list needs the room more than the card does */
.focus:has(.focus__modes:not([hidden])) { --card-h: min(40vh, 480px); }

/* Soothing Rains / Vested Power: the caster's private pick */
.choice {
  position: absolute; left: 50%; top: 50%; translate: -50% -50%; z-index: 60; width: min(1100px, 94vw);
  display: flex; flex-direction: column; align-items: center; gap: 12px; padding: 22px 24px;
  background: rgba(14,11,8,.96); border: 1px solid var(--gold-dim); border-radius: 6px;
  box-shadow: 0 30px 80px rgba(0,0,0,.8); --card-h: min(36vh, 320px);
}
.choice__title { margin: 0; font: 600 22px var(--serif); color: var(--paper); text-align: center; text-wrap: balance; }
.choice__sub { margin: 0; font: 400 var(--fs-label) var(--sans); color: var(--on-ink-dim); text-align: center; }
.choice__cards { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; max-height: 60vh; overflow: auto; padding: 8px; }
.choice__btns { display: flex; gap: 10px; }
.choice__reopen {
  position: absolute; left: 50%; translate: -50% 0; top: calc(var(--hud) + 6px); z-index: 45;
  padding: 7px 14px; border-radius: 4px; font: 600 var(--fs-label) var(--sans);
  color: var(--ink); background: var(--gold); border: 0; cursor: pointer; box-shadow: 0 6px 18px rgba(0,0,0,.6);
}
.focus__modes .mode {
  padding: 12px 16px; border-radius: 4px; text-align: left;
  font: 500 var(--fs-body) var(--sans); color: var(--paper);
  background: rgba(20,16,12,.92); border: 1px solid var(--gold-dim); cursor: pointer;
  transition: background .15s, border-color .15s;
}
.focus__modes .mode:hover, .focus__modes .mode:focus-visible { background: rgba(60,46,20,.95); border-color: var(--gold); }
.focus__modes .mode small { display: block; margin-top: 3px; font: 400 var(--fs-cap)/1.4 var(--sans); color: var(--on-ink-dim); }
/* an Instant set face down as a reaction, still in hand: tap to take it back */
.card.armed { box-shadow: 0 0 0 2px var(--gold), 0 0 26px rgba(224,185,74,.45); }
.card__armed {
  position: absolute; z-index: 3; left: 50%; top: 45%; translate: -50% -50%; rotate: -8deg;
  padding: .3em .8em; border-radius: 4px; font: 700 max(13px, calc(var(--card-h) * .045)) var(--sans);
  color: var(--ink); background: var(--gold); box-shadow: 0 4px 14px rgba(0,0,0,.6); pointer-events: none;
}

/* ── level up ────────────────────────────────────────────────────────── */

.levelup {
  position: absolute; inset: 0; z-index: 50; display: grid; place-items: center; padding: 16px;
  opacity: 0; transition: opacity .45s ease; cursor: pointer;
}
/* The backdrop is its own layer: it clears while the card flies, and the card
   stays solid all the way into the hand. */
.levelup::before {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(ellipse at 50% 46%, rgba(12,9,6,.5), rgba(5,4,3,.94) 70%);
  transition: opacity .6s ease;
}
.levelup.on { opacity: 1; }
.levelup.settle::before { opacity: .55; }
.levelup.leave::before { opacity: 0; }
.levelup.settle .levelup__inner > :not(.levelup__card) { opacity: 0; transition: opacity .3s ease; }
.levelup__inner { position: relative; display: flex; flex-direction: column; align-items: center; gap: 18px; text-align: center; --card-h: min(46vh, 420px); }
.levelup__title {
  margin: 0; font: 600 clamp(56px, 12vh, 120px)/1 var(--serif); color: var(--gold);
  text-shadow: 0 0 48px rgba(224,185,74,.5), 0 4px 20px #000;
  opacity: 0; transform: scale(1.4); filter: blur(10px);
  transition: opacity .5s ease, transform 1s var(--arrive), filter .7s ease;
}
.levelup.on .levelup__title { opacity: 1; transform: none; filter: none; }
.levelup__card {
  position: relative; opacity: 0; transform: translateY(45vh) scale(.55) rotate(-8deg);
  transition: transform .9s var(--arrive), opacity .3s ease;
}
.levelup.deal .levelup__card { opacity: 1; transform: none; }
.levelup.leave .levelup__card { opacity: 1; transition: transform .64s cubic-bezier(.5, 0, .2, 1); will-change: transform; }
.levelup.settle .levelup__card::before { opacity: 0; animation: none; transition: opacity .3s; }
/* a gold swell behind the card as it turns over */
.levelup__card::before {
  content: ""; position: absolute; inset: -18%; border-radius: 50%; z-index: -1;
  background: radial-gradient(closest-side, rgba(224,185,74,.55), rgba(224,185,74,0));
  opacity: 0; transform: scale(.6);
}
.levelup.shown .levelup__card::before { animation: level-swell 1.8s ease-out forwards; }
.levelup.leave .levelup__card::before { display: none; }
@keyframes level-swell { 0% { opacity: 0; transform: scale(.6); } 30% { opacity: 1; transform: scale(1.1); } 100% { opacity: .35; transform: scale(1); } }
.levelup .flip .card { box-shadow: 0 0 0 2px var(--gold), 0 30px 70px rgba(0,0,0,.85); }
.levelup__sub, .levelup__others { margin: 0; opacity: 0; transform: translateY(8px); transition: opacity .4s ease, transform .5s var(--settle); }
.levelup.shown :is(.levelup__sub, .levelup__others) { opacity: 1; transform: none; }
.levelup__sub { font: 400 var(--fs-lead) var(--sans); color: var(--paper); text-shadow: 0 2px 10px #000; }
.levelup__sub b { color: var(--gold); font-weight: 600; }
.levelup__others { font: 400 var(--fs-label) var(--sans); color: var(--on-ink); max-width: 60ch; }

/* in the hand: held open while the card flies in, then a gold ring as it lands */
.hand > .card.landing { opacity: 0; }
.hand > .card.landed { animation: card-landed 1.4s ease-out both; }
/* only the light fades: the card is already exactly where the flying one arrived */
@keyframes card-landed {
  0% { box-shadow: 0 0 0 2px var(--gold), 0 0 60px rgba(224,185,74,.8); }
  100% { box-shadow: 0 0 0 0 rgba(224,185,74,0), 0 4px 14px rgba(0,0,0,.55); }
}

/* ── results ─────────────────────────────────────────────────────────── */

.results {
  z-index: 22; left: 50%; top: 50%; translate: -50% -50%;
  width: min(680px, 92vw); max-height: 78vh; overflow: auto;
  background: linear-gradient(180deg, rgba(34,28,21,.97), rgba(16,13,10,.97));
  border: 1px solid var(--gold-dim); border-radius: 14px;
  box-shadow: 0 30px 80px rgba(0,0,0,.8);
  padding: 22px 24px 18px;
}
.results__title { margin: 0 0 14px; font: 600 26px var(--serif); color: var(--paper); text-align: center; }
.results__rows { display: flex; flex-direction: column; gap: 8px; }
.rrow {
  display: grid; grid-template-columns: 44px 1fr auto; gap: 14px; align-items: center;
  padding: 10px 12px; border-radius: 9px; background: rgba(0,0,0,.28); border: 1px solid rgba(255,255,255,.05);
  animation: rise .7s var(--arrive) both;
}
.rrow img { width: 44px; height: 44px; border-radius: 50%; border: 2px solid var(--gold-dim); object-fit: cover; }
.rrow__who b { display: block; font: 600 15px var(--sans); color: var(--paper); }
.rrow__who span { font: 400 var(--fs-label) var(--sans); color: var(--on-ink-dim); }
.rrow__who span em { font-style: normal; color: var(--gold); }
.rrow__ls { font: 600 20px var(--mono); color: var(--life); text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.rrow__ls small { display: block; font: 600 var(--fs-cap) var(--sans); font-variant-numeric: tabular-nums; }
.rrow__ls .down { color: var(--blood-lit); }
.rrow__ls .up { color: var(--heal); }
.rrow__ls .same { color: var(--on-ink-dim); }
.rrow.out { opacity: .55; }
.rrow .badges { grid-column: 2 / -1; margin-top: 0; }
.results__go {
  display: block; margin: 16px auto 10px; background: transparent; border: 0;
  color: var(--gold); font: 600 var(--fs-label) var(--sans);
}
.results__bar { height: 2px; background: rgba(224,185,74,.15); border-radius: 2px; overflow: hidden; }
.results__bar i { display: block; height: 100%; width: 100%; background: var(--gold); transform-origin: left; }

/* ── game over ───────────────────────────────────────────────────────── */

.over {
  inset: 0; z-index: 35; display: flex; flex-direction: column; align-items: center; justify-content: safe center; gap: 14px;
  overflow-y: auto;
  /* near-solid, so the result reads over whatever the table was doing */
  background: radial-gradient(60% 50% at 50% 38%, rgba(70,52,16,.35), transparent 70%), rgba(7,6,4,.93);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  text-align: center; padding: 5vh 5vw;
}
.over h2 { margin: 0; font: 600 clamp(44px, 8vw, 84px) var(--serif); color: var(--gold); text-shadow: 0 6px 40px rgba(224,185,74,.3); }
.over p { margin: 0 0 12px; font-size: 16px; color: var(--on-ink); }
/* a full table of six scrolls inside the panel rather than pushing the button off screen */
.over__rows { width: min(520px, 90vw); max-height: 50vh; overflow-y: auto; flex: none; display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; padding: 2px; }

.over__btns { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; }

/* ── the tutorial's coach (tutorial.js) ──────────────────────────────── */

/* The ring is a spotlight: a hole in a dim sheet, drawn as one huge shadow. It
   sits under the hover preview, so reading a card is never dimmed. */
.coach__ring {
  position: fixed; left: 0; top: 0; z-index: 62; pointer-events: none; border-radius: 10px;
  border: 2px solid var(--gold); box-shadow: 0 0 0 9999px rgba(4,3,2,.55), 0 0 28px rgba(224,185,74,.45);
  /* never slides or resizes: it fades out, is put down in the new place, and fades in */
  opacity: 0; transition: opacity .26s ease;
}
.coach__ring.in { opacity: 1; }
.coach__tip {
  position: fixed; left: 0; top: 0; z-index: 86; width: min(340px, calc(100vw - 24px));
  padding: 14px 16px 12px; border-radius: 8px; text-align: left;
  background: linear-gradient(180deg, #2B2318, #17120D); border: 1px solid var(--gold);
  box-shadow: 0 18px 50px rgba(0,0,0,.8);
  opacity: 0; transition: opacity .26s ease;
}
.coach__tip.in { opacity: 1; }
.coach__text { margin: 0 0 10px; font: 400 var(--fs-body)/1.5 var(--sans); color: var(--on-ink); }
.coach__text b { color: var(--paper); font-weight: 600; }
.coach__row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.coach__skip { padding: 4px 0; border: 0; background: none; cursor: pointer; font: 400 var(--fs-label) var(--sans); color: var(--on-ink-dim); text-decoration: underline; text-underline-offset: 3px; }
.coach__skip:hover { color: var(--on-ink); }
.coach__next { margin-left: auto; }
.coach__arrow { position: absolute; width: 14px; height: 14px; rotate: 45deg; background: #2B2318; border: 1px solid var(--gold); }
.coach__arrow[data-side="below"] { top: -8px; margin-left: -7px; border-right: 0; border-bottom: 0; }
.coach__arrow[data-side="above"] { bottom: -8px; margin-left: -7px; border-left: 0; border-top: 0; background: #17120D; }
.coach__arrow[data-side="right"] { left: -8px; margin-top: -7px; border-right: 0; border-top: 0; }
.coach__arrow[data-side="left"] { right: -8px; margin-top: -7px; border-left: 0; border-bottom: 0; }
@media (prefers-reduced-motion: reduce) { .coach__ring, .coach__tip { transition: none; } }

/* nothing in hand can be played (Locked, say): the turn can be passed rather than waited out */
.passbtn { position: absolute; left: 50%; translate: -50% 0; bottom: calc(var(--hand-card) * 1.12 + 34px); z-index: 17; }
.passbtn[hidden] { display: none; }
/* a practice table has no clock and nobody to invite */
.lobby[data-practice] :is(#copyLink, #turnLen) { display: none; }

/* ── how to play ─────────────────────────────────────────────────────── */

.help {
  position: fixed; inset: 0; z-index: 95; display: grid; place-items: center; padding: 16px;
  background: rgba(6,5,4,.8); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  animation: fade-in .2s ease;
}
.help[hidden] { display: none; }
.help__panel {
  width: min(680px, 100%); max-height: calc(100vh - 32px); overflow-y: auto; padding: 24px 26px;
  background: linear-gradient(180deg, rgba(34,28,21,.98), rgba(16,13,10,.98));
  border: 1px solid var(--gold-dim); border-radius: 8px; box-shadow: 0 30px 80px rgba(0,0,0,.8);
  text-align: left; color: var(--on-ink);
}
.help__panel h2 { margin: 0 0 12px; font: 600 28px var(--serif); color: var(--paper); }
.help__panel h3 { margin: 18px 0 8px; font: 600 var(--fs-body) var(--sans); color: var(--paper); }
.help__steps { margin: 0; padding-left: 1.3em; display: grid; gap: 8px; font: 400 var(--fs-body)/1.5 var(--sans); }
.help__steps b { color: var(--paper); font-weight: 600; }
.help__terms { margin: 0; display: grid; gap: 6px; }
.help__terms > div { display: grid; grid-template-columns: 9.5em minmax(0, 1fr); gap: 12px; font: 400 15px/1.45 var(--sans); }
.help__terms dt { color: var(--gold); font-weight: 600; }
.help__terms dd { margin: 0; }
.help__tip { margin: 16px 0 18px; font: 400 15px/1.5 var(--sans); color: var(--on-ink); }
@media (max-width: 520px) {
  .help__panel { padding: 18px 16px; }
  .help__terms > div { grid-template-columns: 1fr; gap: 0; }
}

/* ── floating numbers ────────────────────────────────────────────────── */

.float {
  position: fixed; z-index: 60; pointer-events: none;
  font: 600 40px var(--mono); font-variant-numeric: tabular-nums;
  text-shadow: 0 3px 14px rgba(0,0,0,.95);
  animation: float-up 1.15s var(--ease) forwards;
}
.float.dmg { color: var(--blood-lit); }
.float.heal { color: var(--heal); }
.float.word { font: 700 20px var(--sans); text-transform: capitalize; color: var(--gold); }
@keyframes float-up {
  0% { transform: translate(-50%, 0) scale(.5); opacity: 0; }
  18% { transform: translate(-50%, -14px) scale(1.2); opacity: 1; }
  100% { transform: translate(-50%, -80px) scale(1); opacity: 0; }
}

/* ── dice ────────────────────────────────────────────────────────────── */

.dice { position: absolute; left: 50%; top: 50%; translate: -50% -50%; z-index: 45; display: none; flex-direction: column; align-items: center; gap: 10px; }
.dice.on { display: flex; }
.dice svg { width: min(170px, 26vmin); height: auto; filter: drop-shadow(0 14px 30px rgba(0,0,0,.75)); }
.dice[data-rolling="true"] svg { animation: tumble .34s linear infinite; }
@keyframes tumble { 0% { transform: rotate(0) scale(1); } 25% { transform: rotate(-9deg) scale(.96); } 50% { transform: rotate(6deg) scale(1.03); } 75% { transform: rotate(-4deg) scale(.98); } 100% { transform: rotate(0) scale(1); } }
.d-face { fill: #1C1814; } .d-face--lit { fill: #262019; } .d-face--top { fill: #2E2620; }
.d-edge { fill: none; stroke: var(--gold-dim); stroke-width: 1.4; stroke-linejoin: round; }
.d-edge--out { stroke: var(--gold); stroke-width: 3; }
.d-num { fill: var(--paper); font: 700 42px var(--serif); text-anchor: middle; font-variant-numeric: tabular-nums; }
.dice__need { font: 500 var(--fs-label) var(--sans); color: var(--on-ink-dim); background: rgba(0,0,0,.7); border: 1px solid var(--hairline); border-radius: 4px; padding: 5px 12px; }
.dice.hit .dice__need { color: var(--gold); border-color: var(--gold-dim); }
.dice.hit .d-num { fill: var(--gold); }
.dice.crit svg { animation: crit 1s var(--arrive); }
.dice.crit .dice__need { color: var(--ink); background: var(--gold); border-color: var(--gold); }
@keyframes crit { 0% { transform: scale(1); } 30% { transform: scale(1.22); } 100% { transform: scale(1); } }

/* ── the thrown die (die.js) ─────────────────────────────────────────
   A 3D d20 on its own canvas above the table. The panel names the card and
   lays out 1–20 with the success range in gold; while the die tumbles, the
   number on top runs along the track, so everyone can read the throw as it
   happens. */

.die-canvas { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 46; pointer-events: none; display: block; }
/* the table steps back while a die is out; the card that asked for it stays lit */
.roll-scrim { position: absolute; inset: 0; z-index: 40; background: rgba(6,5,4,.7); pointer-events: none; animation: fade-in .3s ease; }
[data-roll] .stage { z-index: 41; }
.die-shadow {
  position: absolute; left: 0; top: 0; z-index: 44; border-radius: 50%; pointer-events: none; will-change: transform;
  background: radial-gradient(closest-side, rgba(0,0,0,.8), rgba(0,0,0,.4) 55%, transparent);
}

/* The grab target is the die itself, a little larger than it looks. The ring
   around it is the time left before the die throws itself. */
.die-grab {
  position: absolute; left: 0; top: 0; z-index: 47; border: 0; padding: 0; border-radius: 50%;
  background: transparent; cursor: grab; touch-action: none; will-change: transform; --p: 1;
}
.die-grab::before, .die-grab::after { content: ""; position: absolute; inset: 0; border-radius: 50%; pointer-events: none; }
.die-grab::before {
  background: conic-gradient(var(--gold) calc(var(--p) * 360deg), rgba(224,185,74,.12) 0);
  -webkit-mask: radial-gradient(closest-side, transparent calc(100% - 3px), #000 calc(100% - 2px));
          mask: radial-gradient(closest-side, transparent calc(100% - 3px), #000 calc(100% - 2px));
}
.die-grab::after { box-shadow: 0 0 0 0 rgba(224,185,74,.35); animation: grab-call 1.6s ease-out infinite; }
.die-grab.held { cursor: grabbing; }
.die-grab.held::before, .die-grab.held::after { opacity: 0; }
.die-grab:focus-visible { outline: 2px solid var(--gold); outline-offset: 4px; }
@keyframes grab-call { 0% { box-shadow: 0 0 0 0 rgba(224,185,74,.34); } 100% { box-shadow: 0 0 0 22px rgba(224,185,74,0); } }

.die-hint {
  position: absolute; left: 0; top: 0; z-index: 47; margin: 0; pointer-events: none; white-space: nowrap;
  font: 500 17px var(--sans); color: var(--paper); text-shadow: 0 2px 12px #000, 0 0 2px #000;
  animation: fade-in .4s ease both;
}

.die-result {
  position: absolute; z-index: 48; translate: -50% -100%; pointer-events: none;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  animation: result-in .7s var(--arrive) both;
}
.die-result.below { translate: -50% 0; }
.die-result b {
  font: 700 clamp(46px, 8vmin, 76px)/1 var(--serif); font-variant-numeric: lining-nums tabular-nums;
  color: var(--gold); text-shadow: 0 0 26px rgba(224,185,74,.55), 0 4px 18px #000;
}
.die-result span { font: 600 16px var(--sans); color: var(--gold); text-shadow: 0 2px 8px #000; }
.die-result.miss b { color: var(--on-ink); text-shadow: 0 4px 18px #000; }
.die-result.miss span { color: var(--on-ink-dim); }
.die-result.crit span { background: var(--gold); color: var(--ink); padding: 3px 9px; border-radius: 4px; text-shadow: none; }
.die-result.out { opacity: 0; transition: opacity .3s; }
@keyframes result-in { from { scale: .3; opacity: 0; } 40% { opacity: 1; } }

.roll {
  position: absolute; left: 50%; top: 21vh; translate: -50% 0; z-index: 42; width: min(620px, 94vw);
  display: flex; flex-direction: column; align-items: center; gap: 4px; text-align: center; pointer-events: none;
  padding: 12px 18px 14px; border-radius: 6px; background: rgba(12,10,7,.92); border: 1px solid var(--hairline);
  box-shadow: 0 18px 50px rgba(0,0,0,.7);
  animation: roll-in .55s var(--arrive) both;
}
.roll p { margin: 0; }
.roll.out { opacity: 0; transition: opacity .3s; }
@keyframes roll-in { from { opacity: 0; translate: -50% -14px; } }
.roll__who { font: 500 15px var(--sans); color: var(--gold); text-shadow: 0 2px 10px #000; }
.roll__card { font: 600 clamp(24px, 3.6vmin, 34px)/1.15 var(--serif); color: var(--paper); text-shadow: 0 2px 12px #000; }
.roll__track { display: grid; grid-template-columns: repeat(20, 1fr); gap: 3px; width: 100%; margin: 8px 0 2px; }
.pip {
  height: 28px; border-radius: 3px; display: grid; place-items: center; font-style: normal;
  font: 500 var(--fs-cap) var(--mono); font-variant-numeric: tabular-nums; color: var(--on-ink-dim);
  background: rgba(14,11,8,.72); border: 1px solid rgba(255,255,255,.08);
  transition: background .1s, color .1s, transform .1s, border-color .1s;
}
.pip.in { border-color: var(--gold-dim); color: var(--gold); background: rgba(40,31,14,.85); }
.pip.live { background: #4a4238; color: var(--paper); transform: translateY(-3px); }
.pip.in.live { background: #8a6f28; color: var(--paper); }
.pip.result { transform: translateY(-5px) scale(1.28); z-index: 1; animation: pip-land .55s var(--arrive); }
.pip.result.hit { background: var(--gold); border-color: var(--gold); color: var(--ink); box-shadow: 0 0 20px rgba(224,185,74,.65); }
.pip.result.miss { background: #3a1a12; border-color: var(--blood); color: var(--paper); }
@keyframes pip-land { from { transform: translateY(-16px) scale(1.6); } }

/* While a die is in the air the table steps back; only the card that asked
   for the roll stays lit. */
[data-roll] .stage__band { opacity: 0; }
[data-roll] .stage .pending:not(:has(.acting)) { opacity: .28; filter: saturate(.6); }
[data-roll] .stage__cards { transform: translateY(9vh) scale(.84); }
.stage .pending { transition: opacity .35s ease, filter .35s ease; }
.stage__cards { transition: transform .6s var(--settle); }

@media (max-width: 560px) {
  .roll { top: calc(var(--hud) + 8px); }
  .roll__track { gap: 2px; }
  /* two rows of ten, so every number stays readable on a phone */
  .roll__track { grid-template-columns: repeat(10, 1fr); }
  .pip { height: 26px; }
  .die-hint { font-size: 16px; }
}
@media (prefers-reduced-motion: reduce) {
  .die-grab::after { animation: none; }
}

/* a seat that Defends wears a brief ward */
.seat.guarding { box-shadow: 0 0 0 2px var(--gold-dim), 0 0 0 7px rgba(224,185,74,.12), 0 0 36px rgba(224,185,74,.25); }

/* ── zoom preview ────────────────────────────────────────────────────── */

.zoom {
  --card-h: min(70vh, 520px, 128vw);
  position: fixed; z-index: 80; top: 50%; left: 50%; transform: translate(-50%, -50%);
  pointer-events: none; display: flex; flex-direction: column; align-items: center; gap: 10px;
  animation: zoom-in .16s ease-out;
}
.zoom .card { box-shadow: 0 24px 60px rgba(0,0,0,.85); transform: none !important; filter: none !important; }
.zoom__why { font: 500 var(--fs-label) var(--sans); color: var(--blood-lit); background: rgba(10,8,6,.92); border: 1px solid var(--blood); border-radius: 4px; padding: 5px 12px; }
/* what a chip means, under the card it came from (or on its own) */
.zoom__note {
  max-width: min(34ch, 80vw); padding: 9px 14px; border-radius: 4px; text-align: center;
  font: 400 15px/1.45 var(--sans); color: var(--paper);
  background: rgba(14,11,8,.95); border: 1px solid var(--hairline); box-shadow: 0 12px 30px rgba(0,0,0,.7);
}
@keyframes zoom-in { from { transform: translate(-50%, -50%) scale(.94); } }

/* ════════════════════════════════════════════════════════════════════
   SCENES — where each region sits, and how big things are, per mode.
   ════════════════════════════════════════════════════════════════════ */

/* lobby: only the lobby exists */
[data-mode="lobby"] :is(.hud, .seats, .stage, .me, .hand, .handhint, .focus, .results, .over) { opacity: 0; pointer-events: none; visibility: hidden; }
[data-mode="lobby"] .seats { top: 0; }
[data-mode="lobby"] .hand { transform: translateY(100%); }
:not([data-mode="lobby"]) > .lobby { opacity: 0; pointer-events: none; visibility: hidden; transform: scale(1.04); }

/* pick: your hand is the scene. Opponents shrink to a strip; the stage is empty. */
[data-mode="pick"] { --seat-to: 38px; --light-y: 86%; --me-k-to: 1; --me-off-to: 58px; }
[data-mode="pick"] .stage { opacity: 0; transform: scale(.94); pointer-events: none; }

/* target: the chosen card held up on the left; targets grow into the space. */
[data-mode="target"] { --seat-to: 74px; --light-y: 50%; }
[data-mode="target"] .seats { top: 50%; translate: 0 -50%; padding-left: calc(min(62vh, 480px) * 5 / 7 + 10vw); }
[data-mode="target"] { --me-k-to: 0; --me-off-to: 3vh; }
[data-mode="target"] .me { left: calc(100% - 3vw); translate: -100% 0; }
[data-mode="target"] .hand { transform: translateY(44%) scale(.57); opacity: .45; pointer-events: none; }
[data-mode="target"] .handhint { opacity: 0; }
[data-mode="target"] .stage { opacity: 0; pointer-events: none; }

/* lay: pull back to the table. Your card lies face down in the middle. */
[data-mode="lay"] { --seat-to: 58px; --light-y: 52%; }
[data-mode="lay"] .seats { top: calc(var(--hud) + 14px); }
[data-mode="lay"] { --me-k-to: .16; --me-off-to: 28px; }
[data-mode="lay"] .me { left: 50%; translate: -50% 0; }
[data-mode="lay"] .hand { transform: translateY(36%) scale(.52); }
[data-mode="lay"] .handhint { opacity: 0; }
[data-mode="lay"] .seat__slot {
  position: static; grid-column: 1 / -1; justify-self: center; margin-top: calc(var(--seat) * .2);
  width: calc(var(--seat) * .8); height: calc(var(--seat) * 1.12); transform: none;
}
[data-mode="lay"] .seat__slot { opacity: 1; }
[data-mode="lay"] .seat__slot:not(.filled) { position: relative; border-style: dashed; animation: breathe 2s ease-in-out infinite; }
[data-mode="lay"] .seat.is-you .seat__slot { display: none; }
@keyframes breathe { 50% { border-color: var(--gold-dim); } }

/* resolve: the turn plays out full size in the centre. Nothing else competes. */
[data-mode="resolve"] { --seat-to: 56px; --light-y: 50%; }
[data-mode="resolve"] .seats { top: calc(var(--hud) + 10px); }
[data-mode="resolve"] { --me-k-to: 0; --me-off-to: 3vh; }
[data-mode="resolve"] .me { left: 50%; translate: -50% 0; }
[data-mode="resolve"] .hand { transform: translateY(112%) scale(.52); pointer-events: none; }
[data-mode="resolve"] .handhint { opacity: 0; }
[data-mode="resolve"] .stage { top: 22vh; bottom: 18vh; }
[data-mode="resolve"] .timer { opacity: .25; }

/* results: the summary rises over a dimmed table */
[data-mode="results"] { --seat-to: 56px; --light-y: 50%; }
[data-mode="results"] .seats { top: calc(var(--hud) + 10px); opacity: .35; }
[data-mode="results"] { --me-k-to: 0; --me-off-to: 3vh; }
[data-mode="results"] .me { left: 50%; translate: -50% 0; opacity: .35; }
[data-mode="results"] .hand { transform: translateY(112%) scale(.52); pointer-events: none; }
[data-mode="results"] .handhint { opacity: 0; }
[data-mode="results"] .stage { opacity: .25; filter: blur(2px); }
[data-mode="results"] .timer { opacity: .25; }

/* level up: the table steps back; the moment is drawn over it (.levelup) */
[data-mode="levelup"] { --seat-to: 56px; --light-y: 46%; }
[data-mode="levelup"] .seats { top: calc(var(--hud) + 10px); opacity: .2; }
[data-mode="levelup"] { --me-k-to: 0; --me-off-to: 3vh; }
[data-mode="levelup"] .me { left: 50%; translate: -50% 0; opacity: .2; }
[data-mode="levelup"] .hand { transform: translateY(112%) scale(.52); pointer-events: none; }
[data-mode="levelup"] :is(.handhint, .stage) { opacity: 0; }

/* spectating: you are out, the table plays on */
[data-mode="spectate"] { --seat-to: 58px; --light-y: 52%; }
[data-mode="spectate"] { --me-k-to: 0; --me-off-to: 3vh; }
[data-mode="spectate"] .me { left: 50%; translate: -50% 0; }
[data-mode="spectate"] .hand { transform: translateY(112%) scale(.52); }

/* focus and results only exist in their own scene */
:not([data-mode="target"]) > .focus { opacity: 0; pointer-events: none; visibility: hidden; translate: -40px -50%; }
:not([data-mode="results"]) > .results { opacity: 0; pointer-events: none; visibility: hidden; translate: -50% -44%; }
:not([data-mode="over"]) > .over { opacity: 0; pointer-events: none; visibility: hidden; }
[data-mode="over"] :is(.hand, .handhint, .focus, .results) { opacity: 0; pointer-events: none; }

/* When a region is in-scene, make it visible immediately — only its exit waits
   for the fade before hiding. */
[data-mode="lobby"] > .lobby,
[data-mode="target"] > .focus,
[data-mode="results"] > .results,
[data-mode="over"] > .over,
#app:not([data-mode="lobby"]) > :is(.hud, .seats, .me, .hand, .handhint) { visibility: visible; transition-delay: 0s; }

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

/* small screens: the same scenes, tighter */
@media (max-width: 720px) {
  /* On a phone the hand is a swipeable row, not a fan: overlapping cards on a
     375px screen leave nothing readable. About two and a half cards show at
     once, each snaps to centre, and press-and-hold reads one in full. */
  :where(#app) { --hand-card: min(44vh, 64vw); }
  .hand {
    --card-h: var(--hand-card); /* a row scrolls; it never shrinks to fit */
    justify-content: flex-start;
    scroll-snap-type: x mandatory; overscroll-behavior-x: contain;
    padding-inline: 19vw; scroll-padding-inline: 19vw;
    -webkit-overflow-scrolling: touch;
    /* The row has to be allowed to scroll, or it cannot be swiped at all —
       Jacob, 2026-09-25: "you can't drag Cards left or right to see all Cards
       available", so the only way to see the hand was to turn the phone. */
    overflow-x: auto; overflow-y: hidden; touch-action: pan-x; scrollbar-width: none;
  }
  .hand::-webkit-scrollbar { display: none; }
  .hand > .card + .card { margin-left: 10px; }
  .hand > .card { scroll-snap-align: center; }
  /* the hint sits to the right of your seat, not across it */
  .handhint { left: auto; right: 12px; translate: none; max-width: calc(100vw - 200px); text-align: right; }
  .hand.fanned > .card { box-shadow: 0 4px 14px rgba(0,0,0,.55); }
  /* the character select stacks: roster, art, dossier, path — the bar stays put */
  .lobby { --pad: 14px; padding: 12px var(--pad) 0; }
  .lobby__top { grid-template-columns: 1fr; justify-items: center; gap: 12px; }
  .lobby__logo { justify-self: center; width: 150px; }
  .classpick { grid-column: 1; gap: 2px; }
  .classpick img { width: 46px; }
  .classpick span { font-size: var(--fs-cap); }
  .select { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto; grid-template-areas: "stage" "dossier" "path"; row-gap: 18px; padding-bottom: 22px; }
  .select__stage { min-height: 0; }
  .dossier { align-self: start; gap: 12px; }
  .dossier__name { font-size: clamp(46px, 14vw, 64px); }
  .facts > div { padding: 0 10px; }
  .facts dd { flex-wrap: wrap; row-gap: 2px; }
  .facts dd b { font-size: 23px; } .facts dd b.word { font-size: 20px; }
  .profile { --k: 92px; --gap: 8px; }
  .path__list { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; grid-auto-flow: row; }
  .select__stage { height: min(58vw, 28vh); }
  /* two rows, thumb-sized: your name, then new room · code · join */
  .lobby__bar { gap: 8px; padding-top: 10px; padding-bottom: calc(10px + env(safe-area-inset-bottom)); }
  .lobby__name { width: 100%; }
  .lobby__name span, .lobby__or { display: none; }
  .lobby__name input { flex: 1; width: auto; min-width: 0; }
  .lobby__join, .lobby__wait { width: 100%; flex-wrap: nowrap; gap: 8px; }
  .lobby__bar .btn.big { padding: 11px 14px; font-size: 14px; }
  .lobby__join #newRoom { flex: 1; }
  .lobby__join #code { width: 6.6em; min-width: 0; }
  #newRoom .lg { display: none; } #newRoom .sm { display: inline; }
  .lobby__wait { flex-wrap: wrap; justify-content: space-between; }
  .lobby__wait #start { flex: 1 1 100%; }
  .hud__logo, .hud__turn { display: none; }
  .hud__btn .lg { display: none; } .hud__btn .sm { display: inline; }
  .lobby__help { position: absolute; top: 12px; right: var(--pad); grid-column: auto; }
  /* targets: the opponents in a row above your own seat, both at the foot of
     the screen; the card you are playing fills the space above */
  [data-mode="target"] { --seat-to: 54px; --me-off-to: calc(var(--hand-card) * .12 + 12px); }
  [data-mode="target"] .seats {
    top: auto; bottom: calc(var(--hand-card) * .12 + 12px + var(--seat) * 1.45 + 10px); translate: none;
    padding-left: 3vw; justify-content: flex-start; gap: 8px;
  }
  [data-mode="target"] .seat.targetable::before { left: auto; right: 8px; bottom: auto; top: 50%; translate: 0 -50%; }
  [data-mode="target"] .seat.targetable .seat__id { padding-right: 68px; }
  [data-mode="target"] .focus { left: 50%; top: calc(var(--hud) + 10px); translate: -50% 0; --card-h: min(42vh, 380px); gap: 10px; }
  [data-mode="target"] .focus:has(.focus__modes:not([hidden])) { --card-h: min(22vh, 380px); }
}

/* ── narrow phones: six medallions across 360px leave no room for six names ── */
@media (max-width: 430px) {
  .classpick { gap: 0; width: 100%; justify-content: space-between; }
  .classpick button { padding: 4px 0; flex: 1 1 0; min-width: 0; }
  .classpick img { width: min(46px, 12vw); }
  /* the chosen name reads in the dossier below; the rest go to screen readers */
  .classpick button > span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .classpick__who { max-width: 180%; }
}

/* ── short screens (a phone on its side): everything shorter, the focus column goes sideways ── */
@media (max-height: 520px) {
  :root { --hud: 46px; }
  .hud { padding: 0 12px; gap: 10px; }
  .hud__logo { display: none; }
  .timer { width: 36px; height: 36px; }
  .banner { padding: 6px 12px; }

  :where(#app) { --hand-card: clamp(140px, 46vh, 220px); }
  [data-mode="pick"] { --seat-to: 32px; --me-off-to: 30px; }
  .hand { padding-top: 10px; }
  .handhint { bottom: calc(var(--hand-card) * 1.08 + 12px); }

  [data-mode="target"] { --seat-to: 44px; }
  .focus { --card-h: min(52vh, 480px); left: 3vw; gap: 8px; }
  .focus__hint { font-size: 20px; }
  [data-mode="target"] .seats { padding-left: calc(min(52vh, 480px) * 5 / 7 + 8vw); }
  /* the card on the left; the question, the list and the buttons beside it */
  .focus:has(.focus__modes:not([hidden])) {
    --card-h: min(52vh, 480px);
    display: grid; grid-template-columns: auto minmax(0, 320px); grid-template-areas: "card hint" "card modes" "card btns";
    column-gap: 16px; row-gap: 8px; align-items: center; justify-items: start;
  }
  .focus:has(.focus__modes:not([hidden])) .focus__card { grid-area: card; }
  .focus:has(.focus__modes:not([hidden])) .focus__hint { grid-area: hint; text-align: left; }
  .focus:has(.focus__modes:not([hidden])) .focus__modes { grid-area: modes; width: 100%; }
  .focus:has(.focus__modes:not([hidden])) .focus__btns { grid-area: btns; }
  .focus__modes .mode { padding: 8px 12px; }
  [data-mode="target"]:has(.focus__modes:not([hidden])) .seats { padding-left: calc(min(52vh, 480px) * 5 / 7 + 340px + 8vw); }

  [data-mode="lay"], [data-mode="resolve"], [data-mode="results"], [data-mode="levelup"], [data-mode="spectate"] { --seat-to: 36px; }
  .stage { --card-h: clamp(120px, 34vh, 320px); }
  [data-mode="resolve"] .stage { top: 19vh; bottom: 13vh; }
  .stage__owner { margin-top: 4px; }

  .levelup__title { font-size: clamp(34px, 10vh, 120px); }
  .levelup__inner { --card-h: min(40vh, 420px); gap: 10px; }
  .roll { top: calc(var(--hud) + 6px); padding: 8px 14px 10px; }
  .roll__track { margin: 4px 0 0; }
  .pip { height: 22px; }
  .zoom { --card-h: min(84vh, 520px); }
  .results { max-height: calc(100vh - var(--hud) - 16px); top: calc(50% + var(--hud) / 2); padding: 14px 16px 12px; }
  .results__title { margin-bottom: 8px; font-size: 22px; }
  .over { gap: 8px; }
  .over h2 { font-size: clamp(32px, 9vh, 84px); }

  /* the lobby bar: one tight row, so the class still has the screen */
  .lobby__bar { gap: 8px 16px; padding-top: 8px; padding-bottom: calc(8px + env(safe-area-inset-bottom)); }
  .lobby__bar .btn.big { padding: 9px 14px; font-size: 14px; }
  .lobby__name input { width: 11em; }
  .lobby__seated .who small { display: none; }
  .select__stage { min-height: 200px; }
}

/* ── big desktops: the same scenes, with the room they have ── */
@media (min-width: 1800px) and (min-height: 1000px) {
  :root { --hud: 64px; }
  :where(#app) { --hand-card: clamp(220px, 42vh, 500px); }
  [data-mode="pick"] { --seat-to: 46px; }
  .handhint { font-size: var(--fs-body); }
  [data-mode="target"] { --seat-to: 88px; }
  .focus { --card-h: min(62vh, 600px); }
  .focus:has(.focus__modes:not([hidden])) { --card-h: min(40vh, 600px); }
  [data-mode="target"] .seats { padding-left: calc(min(62vh, 600px) * 5 / 7 + 10vw); }
  [data-mode="lay"], [data-mode="resolve"], [data-mode="results"], [data-mode="levelup"], [data-mode="spectate"] { --seat-to: 68px; }
  .stage { --card-h: clamp(170px, 32vh, 420px); }
  .zoom { --card-h: min(70vh, 660px); }
  .levelup__inner { --card-h: min(46vh, 520px); }
  .results { width: min(760px, 92vw); }
  .roll { width: min(720px, 94vw); }
  .pip { height: 32px; }
}


/* ── An Instant's moment ───────────────────────────────────────────────
   Jacob: "a flashing icon that shows up for a few seconds (maybe 5) — maybe a
   hot key etc. — dont give them long." So: loud, central, keyboard-first, and
   visibly running out. */
.instant {
  position: fixed; left: 50%; bottom: 12vh; translate: -50% 0; z-index: 90;
  display: grid; gap: .55rem; justify-items: center;
  padding: 1rem 1.15rem 1.15rem; border-radius: 12px;
  background: rgba(14, 12, 10, .94); border: 1px solid var(--gold);
  box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 18px 60px rgba(0,0,0,.7), 0 0 60px rgba(224,185,74,.22);
  animation: instant-in .18s ease-out, instant-pulse 1s ease-in-out infinite .18s;
  max-width: min(92vw, 34rem);
}
@keyframes instant-in { from { opacity: 0; translate: -50% 14px; } }
@keyframes instant-pulse {
  50% { box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 18px 60px rgba(0,0,0,.7), 0 0 78px rgba(224,185,74,.42); }
}
.instant__clock { width: 100%; height: 3px; border-radius: 2px; background: rgba(224,185,74,.18); overflow: hidden; }
.instant__clock i { display: block; height: 100%; width: 100%; background: var(--gold); transform-origin: left; }
.instant__why { margin: 0; font: 600 var(--fs-body) var(--sans); color: var(--parch); text-align: center; }
.instant__cards { display: flex; flex-wrap: wrap; gap: .5rem; justify-content: center; }
.instant__card {
  display: grid; gap: .1rem; justify-items: start; text-align: left;
  padding: .5rem .7rem; border-radius: 8px; cursor: pointer;
  background: var(--gold); color: var(--ink); border: 0;
  font: 700 var(--fs-body) var(--sans);
}
.instant__card:hover, .instant__card:focus-visible { filter: brightness(1.08); outline: 2px solid var(--parch); }
.instant__card small { font: 500 var(--fs-label) var(--sans); opacity: .78; }
.instant__card kbd {
  font: 700 var(--fs-label) var(--mono, var(--sans)); background: rgba(0,0,0,.22);
  border-radius: 4px; padding: 0 .3rem; margin-right: .35rem;
}
.instant__pass kbd { font: 600 var(--fs-label) var(--sans); opacity: .7; margin-left: .3rem; }
@media (prefers-reduced-motion: reduce) {
  .instant { animation: none; }
}

@keyframes instant-drain { to { transform: scaleX(0); } }

/* A summon is aimed at from its chip, so the chip carries the same
   targetable/chosen states a seat does. */
.chip.targetable { cursor: pointer; border-color: var(--gold); }
[data-mode="target"] .chip.targetable { box-shadow: 0 0 0 1px var(--gold), 0 0 14px rgba(224,185,74,.3); }
.chip.picked { background: var(--gold); color: var(--ink); border-color: var(--gold); }
.chip.picked b, .chip.picked i { color: inherit; }


/* ── The convention gate ───────────────────────────────────────────────
   A booth queue is the whole design brief: one field that matters, a big
   button, and the keyboard already in the right place. */
/* `display` on the element beats the hidden attribute, so say so explicitly. */
.gate[hidden] { display: none; }
.gate {
  position: fixed; inset: 0; z-index: 120;
  display: grid; place-items: center; padding: 6vh 1rem;
  background: radial-gradient(60% 50% at 50% 40%, rgba(40,30,12,.6), rgba(8,7,6,.97));
  overflow: auto;
}
.gate__panel {
  width: min(28rem, 100%); display: grid; gap: .55rem; justify-items: center; text-align: center;
  padding: 1.6rem 1.4rem 1.2rem; border-radius: 14px;
  background: rgba(14, 12, 10, .96); border: 1px solid var(--gold);
  box-shadow: 0 24px 80px rgba(0,0,0,.7), 0 0 70px rgba(224,185,74,.16);
}
.gate__mark { width: 4.5rem; height: auto; opacity: .95; margin-bottom: .2rem; }
.gate__title { margin: 0; font: 400 var(--fs-h1, 2rem) var(--display, var(--sans)); color: var(--parch); }
.gate__sub { margin: 0 0 .5rem; font: 400 var(--fs-body) var(--sans); color: var(--parch); opacity: .8; }
.gate__form { display: grid; gap: .3rem; width: 100%; text-align: left; }
.gate__label { font: 600 var(--fs-label) var(--sans); color: var(--parch); opacity: .75; }
.gate__label span { opacity: .6; font-weight: 400; }
.gate__input {
  width: 100%; padding: .7rem .8rem; margin-bottom: .45rem;
  font: 400 1rem var(--sans); color: var(--parch);
  background: rgba(0,0,0,.45); border: 1px solid rgba(224,185,74,.35); border-radius: 8px;
}
.gate__input:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; border-color: var(--gold); }
.gate__go { width: 100%; margin-top: .35rem; }
.gate__err { margin: 0 0 .3rem; font: 600 var(--fs-label) var(--sans); color: var(--ember, #e0714f); }
.gate__fine { margin: .5rem 0 0; font: 400 var(--fs-label) var(--sans); color: var(--parch); opacity: .55; }

/* ── v2 (2026-09-25) ─────────────────────────────────────────────────── */

/* A summon struck on its own chip, so the table can see it was the summon. */
.chip.struck { animation: chip-struck .9s ease-out; box-shadow: 0 0 0 2px var(--blood-lit), 0 0 18px rgba(200, 60, 40, .55); }
@keyframes chip-struck { 0% { transform: scale(1); } 18% { transform: scale(1.28) rotate(-3deg); } 40% { transform: scale(.94); } 100% { transform: none; } }

/* Aim tools: "Target all", and Taciturnity's Blinded-or-Silenced per target. */
.focus__modes .mode--row { display: flex; align-items: center; gap: 8px; cursor: default; }
.focus__modes .mode--row > span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mode__pick { padding: 5px 10px; border-radius: 4px; border: 1px solid var(--hairline); background: transparent; color: var(--on-ink-dim); }
.mode__pick.on { border-color: var(--gold); color: var(--paper); background: rgba(224, 185, 74, .14); }

/* Taunted, said loudly where the hand is. */
.hint__warn { display: inline-block; margin-right: 6px; padding: 3px 10px; border-radius: 4px; color: var(--paper); background: rgba(150, 30, 20, .55); border: 1px solid var(--blood-lit); }

/* ── a phone held upright: the turn plays out in a row, under the seats ──
   Jacob, 2026-09-25: after every play he had to turn the phone sideways "to
   actually see what happens" — upright, the played cards stacked full size down
   the screen, over the seats. They sit in one row now, sized so every card
   played this turn fits across (--n is set by timeline.js). */
@media (max-width: 720px) and (orientation: portrait) {
  [data-mode="resolve"], [data-mode="lay"], [data-mode="results"], [data-mode="levelup"], [data-mode="spectate"] { --seat-to: 34px; }
  :is([data-mode="resolve"], [data-mode="lay"], [data-mode="results"], [data-mode="spectate"]) .seats { gap: 6px; }
  .stage { --card-h: min(24vh, calc((100vw - 24px) / var(--n, 3) * 1.4 - 12px)); }
  .stage__cards { flex-wrap: nowrap; gap: 6px; }
  :is([data-mode="resolve"], [data-mode="lay"]) .stage { top: 34vh; bottom: 16vh; }
  .stage__owner { font-size: 12px; margin-top: 4px; }
  .stage__say { font-size: 17px; bottom: -34px; }
}

/* ── one thing at a time (2026-09-29) ────────────────────────────────────
   Jon: "where not absolutely necessary, hide anything that shouldn't be
   there." Each scene keeps only what it is about. */

/* Playing a card: the card and its buttons, alone and in the middle. */
[data-hold="play"] :is(.seats, .me, .stage, .hand, .handhint) { opacity: 0 !important; pointer-events: none; }
@media (min-width: 721px) and (min-height: 521px) {
  [data-hold="play"] .focus { left: 50%; translate: -50% -50%; }
}
/* Aiming: the card, and who it can go to. The hand has no part in it; your
   own seat only when you are one of the choices. */
[data-mode="target"] :is(.hand, .handhint) { opacity: 0 !important; pointer-events: none; }
[data-hold="aim"]:not([data-self-aim]) .me { opacity: 0; pointer-events: none; }
/* An Instant's moment: the offer, and the seats it can be aimed at. */
[data-hold="instant"] :is(.me, .stage, .focus) { opacity: 0 !important; pointer-events: none; }
/* Waiting on the others: your card face down, and who is still choosing. */
[data-mode="lay"] :is(.hand, .handhint) { opacity: 0; pointer-events: none; }
/* A die in the air: the die and its panel. Everything under it steps out, and
   comes back the moment it lands. */
[data-roll] :is(.seats, .me, .stage, .hand, .handhint) { opacity: 0 !important; transition: opacity .25s ease; }
/* The summary and the level-up sit over an empty table. */
[data-mode="results"] :is(.seats, .me, .stage) { opacity: 0 !important; filter: none; }
[data-mode="levelup"] :is(.seats, .me, .stage, .hud) { opacity: 0 !important; }
.levelup__go { margin: 0; font: 500 var(--fs-label) var(--sans); color: var(--on-ink-dim); opacity: 0; transition: opacity .4s ease .6s; }
.levelup__go.on { opacity: 1; }
@media (max-width: 720px) {
  /* With nothing else on screen, the card can be read at full size. */
  [data-hold="play"] .focus { top: calc(50% + var(--hud) / 2); translate: -50% -50%; --card-h: min(56vh, calc(88vw * 7 / 5)); }
}
@media (max-width: 720px) {
  /* Aiming on a phone: who you can choose along the top, the question and its
     buttons along the bottom. The card itself is left out — the question names
     it, and on a narrow screen it covered the very seats being chosen. */
  [data-hold="aim"] .focus__card { display: none; }
  [data-hold="aim"] .focus { top: auto; bottom: calc(14px + env(safe-area-inset-bottom)); translate: -50% 0; width: min(92vw, 420px); }
  [data-hold="aim"] .focus__hint { font-size: 20px; }
  [data-hold="aim"] .seats { top: calc(var(--hud) + 12px); bottom: auto; justify-content: center; padding-left: 3vw; }
  [data-hold="aim"]:not([data-self-aim]) .me { display: none; }
}
@media (max-width: 720px) {
  /* The "tap to play" line sat over your own seat. On a phone it only shows
     when it has something to warn about (Taunted); the tutorial teaches the rest. */
  .handhint:not(:has(.hint__warn)) { opacity: 0 !important; }
  .handhint:has(.hint__warn) { left: 50%; right: auto; translate: -50% 0; max-width: 92vw; text-align: center; }
}
/* The match is over: the result, over an empty table. */
[data-mode="over"] :is(.seats, .me, .stage) { opacity: 0 !important; pointer-events: none; }
