/* Neon Arena — the hub, restored to the Claude Design mock (RB2, the «back
   to the mock» rollback). The hub is a ROOM: two sections — «СЕЙЧАС» (the
   hero card in its four honest readings) and «ИГРЫ АРЕНЫ» (the capsule
   grid, four across by two down with the «скоро» cell as the eighth tile).
   The document frame left with the rethink's rollback: no masthead, no
   chess context strip, no feed digest, no wall, no shelf — the grid IS the
   shelf's content.
   Every number below is the mock's own, copied rather than eyeballed — the
   mock writes its styles inline with exact pixels, so a port that guesses
   is a port that drifts. Scoped under .lobby3 / .l3-* / .cap* / .vs-* so it
   is inert elsewhere. The .l3h-* block grammar stays: the arcade lobbies
   render the same game-hub blocks and every sheet is global. */

/* host: the hub owns the whole scene, edge to edge.
   The selector carries #scene because shell.css sets `padding-top: 18px` on
   `#scene.shell-scene .container`, which outranks a plain `#view` rule and
   would win the top edge back. */
#view.lobby3-host,
#scene.shell-scene #view.lobby3-host {
  max-width: none;
  padding: 12px 14px 16px;
  overflow-x: hidden;
}

/* ---------- the room ----------
   The mock's hub is a pinned two-row grid — the hero sizes to its content,
   the capsule grid takes the rest of the height and breathes with the
   window (that is the 1728 answer: taller capsules, not more chrome). The
   pinned reading needs the scene's height, so it starts where the mock's
   own laptop composition starts (~880 scene px); below that the hub is a
   scrolling column — the tablet's two columns and the phone's one. */
.lobby3 { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.l3-sec { min-width: 0; }
.l3-games { display: flex; flex-direction: column; }

@container scene (min-width: 840px) {
  /* The room takes the scene's height: the HOST gets the definite height
     (#view is the scene's child, so 100% resolves), and .lobby3 resolves
     its own 100% against it — that is what lets the capsule grid's fr rows
     breathe with the window instead of collapsing to their content. A very
     short window gets a floor: the host grows past 100% and the scene
     scrolls it, so the capsules stop shrinking at the minmax below. */
  #view.lobby3-host,
  #scene.shell-scene #view.lobby3-host { padding: 16px 18px; height: 100%; min-height: 560px; }
  .lobby3 {
    display: grid; grid-template-rows: auto minmax(0, 1fr); gap: 16px;
    height: 100%;
  }
  .l3-games { min-height: 0; }
}

/* ---------- section header ----------
   mock: display:flex;align-items:baseline;gap:10px;margin-bottom:9px */
.l3-sec-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 9px; }
.l3-sec-title {
  margin: 0; font-family: var(--font-display);
  font-size: 13px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
}
.l3-sec-kicker { font-size: 11px; font-weight: 700; color: var(--text-soft); }
.l3-sec-kicker.is-match { color: var(--gold); }
.l3-sec-kicker.is-search { color: var(--violet-300); }
.l3-sec-kicker.is-free,
.l3-sec-kicker.is-first { color: rgba(53, 199, 131, 0.9); }
.l3-sec-gap { flex: 1; }
.l3-sec-note {
  display: flex; align-items: center; gap: 6px; white-space: nowrap;
  font-size: 10px; color: rgba(53, 199, 131, 0.85);
}
/* the UA's `[hidden]{display:none}` loses to any class selector that sets
   display, so hiding this by attribute needs the rule said out loud. */
.l3-sec-note[hidden] { display: none; }
.l3-sec-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 7px var(--ok); }

/* ---------- «СЕЙЧАС» — the shared card frame ----------
   mock: border-radius:18px, gold rim on the match card, violet on the
   search. The body is the reconciled mount's container: one card, held
   across polls by ui/row.js. */
.l3-now-body { display: flex; flex-direction: column; min-width: 0; border-radius: 18px; }
/* Chess's tile has nowhere to navigate — its route IS this screen — so the
   click points at the card that already carries its controls. */
.l3-now-body.is-flash { animation: l3-flash 640ms ease-out 1; }
@keyframes l3-flash {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--gold) 55%, transparent); }
  100% { box-shadow: 0 0 0 12px transparent; }
}
.l3-now-card {
  position: relative; overflow: hidden;
  display: flex; align-items: center; flex-wrap: wrap; gap: 12px;
  padding: 13px 14px; border-radius: 18px;
  border: 1px solid color-mix(in srgb, var(--gold) 44%, transparent);
  background: linear-gradient(115deg, rgba(56, 36, 12, 0.66), rgba(20, 13, 38, 0.76));
  box-shadow: 0 0 40px rgba(241, 193, 82, 0.12);
}
/* The mock's warm bloom in the card's corner — atmosphere, not a control. */
.l3-glow {
  position: absolute; right: -60px; top: -70px; width: 260px; height: 260px;
  border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle, rgba(241, 193, 82, 0.16), transparent 70%);
}
/* OPP_TURN is the same board calm: the gold hairline stays (a game IS
   running), the glow and the breath leave — YOUR_MOVE is the one glow that
   means something. */
.l3-now-card.is-opp {
  border-color: color-mix(in srgb, var(--gold) 26%, transparent);
  box-shadow: none;
}
.l3-now-card.is-opp .l3-glow { opacity: 0.4; }
.l3-now-main { position: relative; flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 7px; }
.l3-now-foot { font-size: 11px; line-height: 1.4; color: var(--text-soft); }

/* board thumbnail — mock: 114px, 8×8, 11px glyphs, brown board */
.l3-mini {
  position: relative; width: 92px; height: 92px; flex: 0 0 auto;
  border-radius: 14px; overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--gold) 30%, transparent);
  display: grid; grid-template-columns: repeat(8, 1fr); grid-template-rows: repeat(8, 1fr);
  box-shadow: 0 12px 34px rgba(0, 0, 0, 0.5);
  background: #f0d9b5;
}
.l3-mini-sq {
  display: flex; align-items: center; justify-content: center;
  font-size: 11px; line-height: 1; background: #f0d9b5;
}
.l3-mini-sq.is-dark { background: #b58863; }
.l3-mini-p { font-style: normal; color: #17110a; text-shadow: 0 1px 1px rgba(255, 255, 255, 0.25); }
.l3-mini-p.is-white { color: #fff; text-shadow: 0 0 1px #2a1d10, 0 1px 2px rgba(0, 0, 0, 0.45); }

/* the arcade emblem rides where the chess board rides */
.hero-emblem {
  position: relative; width: 92px; height: 92px; flex: 0 0 auto;
  display: grid; place-items: center; overflow: hidden;
  border-radius: 14px;
  border: 1px solid color-mix(in srgb, var(--accent, var(--gold)) 30%, transparent);
  background:
    radial-gradient(135% 100% at 84% -14%, color-mix(in srgb, var(--accent, var(--gold)) 55%, transparent), transparent 56%),
    linear-gradient(160deg, #241638, #0e0a1c);
  box-shadow: 0 12px 34px rgba(0, 0, 0, 0.5);
}
.hero-emblem-glyph {
  font-size: 44px; line-height: 1;
  filter: drop-shadow(0 4px 14px color-mix(in srgb, var(--accent, var(--gold)) 55%, transparent));
}

.l3-now-who { display: flex; align-items: center; gap: 9px; min-width: 0; }
.l3-avatar {
  width: 34px; height: 34px; flex: 0 0 auto; border-radius: 10px;
  display: grid; place-items: center; font-size: 16px;
  background: linear-gradient(135deg, #241638, #120c22);
  border: 1px solid var(--stroke-strong);
}
.l3-now-namecol { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.l3-oppname {
  font-family: var(--font-display); font-size: 15px; font-weight: 700;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.l3-oppsub { font-size: 11px; color: rgba(236, 231, 250, 0.46); }

/* mock's chip: 4px 10px / r7 / 10px 700 */
.l3-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.l3-chip2 {
  padding: 4px 10px; border-radius: 7px; white-space: nowrap;
  background: rgba(167, 139, 250, 0.1); border: 1px solid rgba(167, 139, 250, 0.2);
  font-size: 10px; font-weight: 700; color: rgba(236, 231, 250, 0.6);
}

/* the head-to-head line — one of the mock's warm lines. Renders only when
   the record exists (js/views/lobby.js h2hNode), so it never states a
   meeting that did not happen. */
.l3-h2h { font-size: 11px; line-height: 1.4; color: rgba(236, 231, 250, 0.4); }

/* clock — mock: 37px mono, gold, .16em uppercase label */
.l3-clock { position: relative; flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-start; gap: 3px; }
.l3-clock-val {
  font-family: var(--font-mono); font-size: 30px; font-weight: 700; line-height: 1.15;
  color: var(--gold); text-shadow: 0 0 28px rgba(241, 193, 82, 0.5);
  animation: l3-breathe 2s ease-in-out infinite;
}
.l3-clock-val.is-idle { color: var(--text-soft); text-shadow: none; animation: none; }
.l3-clock-val.is-queue { font-size: 30px; line-height: 1; color: var(--violet-300); text-shadow: 0 0 24px rgba(167, 139, 250, 0.45); animation: none; }
.l3-clock-lbl {
  font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase;
  font-weight: 800; color: var(--gold); white-space: nowrap;
}
.l3-clock-lbl.is-idle { font-weight: 700; letter-spacing: 0.14em; color: var(--text-soft); }
@keyframes l3-breathe { 0%, 100% { opacity: 0.6; } 50% { opacity: 1; } }

/* the gold primary — mock: 14px 20px / r12 / Unbounded 14/800 */
.l3-now-act { position: relative; flex: 1 0 100%; display: flex; flex-direction: column; gap: 8px; align-items: stretch; }
.l3-cta {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  white-space: nowrap; cursor: pointer; border: 0;
  padding: 14px 20px; border-radius: 12px;
  background: var(--gold-grad); color: var(--gold-ink);
  font-family: var(--font-display); font-size: 14px; font-weight: 800; letter-spacing: 0.03em;
  box-shadow: 0 10px 28px rgba(241, 193, 82, 0.34);
  transition: transform var(--dur-fast, 0.16s), box-shadow var(--dur-fast, 0.16s);
}
@media (hover: hover) {
  .l3-cta:hover { transform: translateY(-2px); box-shadow: 0 14px 34px rgba(241, 193, 82, 0.48); }
}
.l3-cta-glyph { font-size: 15px; line-height: 1; }
.l3-cta-note { text-align: center; white-space: nowrap; font-size: 9px; line-height: 1.4; color: rgba(236, 231, 250, 0.3); }
/* The phone «СЕЙЧАС» card's turn pill (the mock's «ТВОЙ ХОД» chip, top-right of
   the opponent line). Desktop and the medium fold keep the turn label under
   the clock, so the pill is inert there and only the phone grid shows it. */
.l3-turn-pill {
  display: none; align-items: center; justify-content: center; white-space: nowrap;
  padding: 3px 9px; border-radius: 6px;
  background: rgba(241, 193, 82, 0.2); color: var(--gold);
  font-size: 9px; font-weight: 800; letter-spacing: 0.05em; text-transform: uppercase;
}
.l3-turn-pill.is-idle { background: rgba(167, 139, 250, 0.14); color: var(--text-soft); }

/* ---------- searching ---------- */
.l3-now-card.is-search {
  gap: 14px; padding: 16px;
  border-color: color-mix(in srgb, var(--violet) 45%, transparent);
  background: linear-gradient(115deg, rgba(42, 26, 80, 0.72), rgba(20, 13, 38, 0.78));
  box-shadow: 0 0 44px rgba(124, 58, 237, 0.16);
}
/* mock: a conic disc, a solid core 3px inside it, then three hairline rings at
   16 / 30 / 44 — not a masked donut, which is what a hand-rolled version
   reaches for and which loses the core's flat fill. */
.l3-rings { position: relative; width: 92px; height: 92px; flex: 0 0 auto; display: grid; place-items: center; }
.l3-ring { position: absolute; border-radius: 50%; }
.l3-ring-spin {
  inset: 0; background: conic-gradient(from 0deg, rgba(167, 139, 250, 0) 40%, rgba(167, 139, 250, 0.55));
  animation: l3-spin 2.2s linear infinite;
}
.l3-ring-core { inset: 3px; background: #130d24; }
.l3-ring-a { inset: 12px; border: 1px solid rgba(167, 139, 250, 0.26); }
.l3-ring-b { inset: 22px; border: 1px solid rgba(167, 139, 250, 0.22); animation: l3-ringpulse 2.1s ease-in-out infinite; }
.l3-ring-c { inset: 32px; border: 1px solid rgba(167, 139, 250, 0.18); animation: l3-ringpulse 2.1s ease-in-out 0.5s infinite; }
.l3-ring-sigil { position: relative; font-size: 30px; line-height: 1; }
@keyframes l3-spin { to { transform: rotate(360deg); } }
@keyframes l3-ringpulse { 0%, 100% { opacity: 0.35; transform: scale(1); } 50% { opacity: 1; transform: scale(1.1); } }
.l3-search-title { font-family: var(--font-display); font-size: 17px; font-weight: 700; }
.l3-bar { position: relative; height: 5px; max-width: 420px; border-radius: 3px; background: rgba(167, 139, 250, 0.14); overflow: hidden; }
.l3-bar-i {
  position: absolute; top: 0; bottom: 0; width: 42%; border-radius: 3px;
  background: linear-gradient(90deg, transparent, var(--violet), var(--gold), transparent);
  animation: l3-sheen 1.8s linear infinite;
}
@keyframes l3-sheen { from { transform: translateX(-120%) skewX(-18deg); } to { transform: translateX(320%) skewX(-18deg); } }
.l3-cancel {
  white-space: nowrap; cursor: pointer;
  padding: 14px 26px; border-radius: 12px;
  border: 1px solid color-mix(in srgb, var(--danger) 36%, transparent);
  background: color-mix(in srgb, var(--danger) 8%, transparent);
  font-family: var(--font-display); font-size: 13px; font-weight: 800; color: var(--danger-soft);
  transition: background var(--dur-fast, 0.16s), border-color var(--dur-fast, 0.16s);
}
@media (hover: hover) {
  .l3-cancel:hover { background: color-mix(in srgb, var(--danger) 18%, transparent); border-color: color-mix(in srgb, var(--danger) 70%, transparent); }
}

/* ---------- free: the quick match ----------
   mock: the big gold В БОЙ with the shortest starts under it. One card,
   full width — the mock's friend/bot columns are not drawn here because
   the product has neither door (see js/views/lobby.js). */
.l3-now-card.is-free {
  flex-direction: column; align-items: stretch; gap: 18px; padding: 16px;
  border-color: color-mix(in srgb, var(--gold) 36%, transparent);
  background: linear-gradient(115deg, rgba(60, 34, 10, 0.7), rgba(28, 18, 46, 0.62));
}
.l3-sheen { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.l3-sheen::before {
  content: ""; position: absolute; top: 0; bottom: 0; width: 70px;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.13), transparent);
  animation: l3-sheen 5.5s ease-in-out infinite;
}
.l3-fight {
  position: relative; flex: 0 0 auto; cursor: pointer; border: 0;
  display: flex; align-items: center; justify-content: center; gap: 12px; white-space: nowrap;
  padding: 19px 32px; border-radius: 13px;
  background: var(--gold-grad); color: var(--gold-ink);
  font-family: var(--font-display); font-size: 18px; font-weight: 800; letter-spacing: 0.04em;
  box-shadow: 0 12px 32px rgba(241, 193, 82, 0.34);
  transition: transform var(--dur-fast, 0.16s), box-shadow var(--dur-fast, 0.16s);
}
@media (hover: hover) {
  .l3-fight:hover { transform: translateY(-2px); box-shadow: 0 16px 40px rgba(241, 193, 82, 0.5); }
}
.l3-fight .l3-cta-glyph { font-size: 20px; }
.l3-fight-col { position: relative; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.l3-fight-kick { font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; color: rgba(236, 231, 250, 0.44); }
.l3-fight-sub { font-size: 10px; line-height: 1.4; color: rgba(236, 231, 250, 0.34); }
.l3-tcrow { display: flex; gap: 5px; flex-wrap: wrap; }
.l3-tc {
  cursor: pointer; padding: 4px 10px; border-radius: 7px;
  font-family: var(--font-mono); font-size: 11px; font-weight: 700;
  border: 1px solid var(--stroke); background: var(--panel-soft); color: rgba(236, 231, 250, 0.52);
  transition: border-color var(--dur-fast, 0.16s), color var(--dur-fast, 0.16s);
}
.l3-tc.is-on { border-color: color-mix(in srgb, var(--gold) 55%, transparent); background: color-mix(in srgb, var(--gold) 14%, transparent); color: var(--gold); }

/* ---------- first evening ----------
   mock: grid-template-columns: minmax(0,1.5fr) minmax(0,1fr); gap 12 — the
   gold starter card and the green «что открыто сразу» card. One column
   below 620 scene px, where the green card leaves (the mock's phone
   firstRun is the gold card alone). */
.l3-first { position: relative; display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; }
.l3-first-main {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; gap: 9px;
  padding: 14px 16px; border-radius: 16px;
  border: 1px solid color-mix(in srgb, var(--gold) 34%, transparent);
  background: linear-gradient(115deg, rgba(58, 33, 10, 0.66), rgba(26, 17, 44, 0.62));
}
.l3-first-title { position: relative; font-family: var(--font-display); font-size: 16px; font-weight: 800; }
.l3-first-body {
  position: relative; font-size: 12px; line-height: 1.5;
  color: rgba(236, 231, 250, 0.66); max-width: 430px;
}
.l3-first-games { position: relative; display: grid; grid-template-columns: minmax(0, 1fr); gap: 6px; }
@container scene (min-width: 620px) {
  .l3-first-games { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.l3-first-game {
  display: flex; align-items: center; gap: 11px; min-width: 0;
  padding: 8px 12px; border-radius: 11px; cursor: pointer; text-align: left; color: inherit;
  border: 1px solid color-mix(in srgb, var(--gold) 30%, transparent);
  background: linear-gradient(120deg, rgba(52, 34, 10, 0.6), rgba(24, 16, 42, 0.6));
  transition: border-color var(--dur-fast, 0.16s), transform var(--dur-fast, 0.16s);
}
@media (hover: hover) {
  .l3-first-game:hover { border-color: color-mix(in srgb, var(--gold) 70%, transparent); transform: translateY(-2px); }
}
.l3-first-game-icon { flex: 0 0 auto; font-size: 21px; line-height: 1; }
.l3-first-game-col { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.l3-first-game-name { font-family: var(--font-display); font-size: 12px; font-weight: 700; }
.l3-first-game-sub { font-size: 9px; line-height: 1.3; color: rgba(236, 231, 250, 0.5); }
.l3-first-foot { position: relative; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.l3-first-watch {
  white-space: nowrap; display: flex; align-items: center; gap: 7px; cursor: pointer;
  padding: 9px 16px; border-radius: 11px;
  border: 1px solid rgba(167, 139, 250, 0.34); background: rgba(20, 13, 38, 0.6);
  font-size: 11px; font-weight: 700; color: rgba(236, 231, 250, 0.78);
  transition: border-color var(--dur-fast, 0.16s);
}
@media (hover: hover) {
  .l3-first-watch:hover { border-color: rgba(167, 139, 250, 0.7); }
}
.l3-first-invite { font-size: 10px; color: rgba(236, 231, 250, 0.36); }
.l3-first-open {
  display: flex; flex-direction: column; gap: 9px;
  padding: 16px 18px; border-radius: 18px;
  border: 1px solid rgba(53, 199, 131, 0.26); background: rgba(53, 199, 131, 0.05);
}
.l3-first-open-title {
  font-family: var(--font-display); font-size: 10px; font-weight: 700;
  letter-spacing: 0.11em; text-transform: uppercase; color: var(--ok);
}
.l3-first-open-body { font-size: 11px; line-height: 1.6; color: rgba(236, 231, 250, 0.72); }
.l3-first-open-gap { flex: 1; }
.l3-first-open-cta {
  align-self: flex-start; white-space: nowrap; cursor: pointer;
  padding: 11px 20px; border-radius: 11px;
  background: rgba(53, 199, 131, 0.14); border: 1px solid rgba(53, 199, 131, 0.42);
  font-family: var(--font-display); font-size: 11px; font-weight: 800; color: var(--ok);
  transition: background var(--dur-fast, 0.16s);
}
@media (hover: hover) {
  .l3-first-open-cta:hover { background: rgba(53, 199, 131, 0.24); }
}
/* deadends-map S1: the green «что открыто сразу» card used to be display:none
   below a 599px scene — the day-one phone lost the /training door entirely
   while the copy above promised it. The card stacks under the main card (the
   .l3-first grid is single-column) and only tightens its padding. */
@container scene (max-width: 599px) {
  .l3-first-open { padding: 13px 14px; gap: 7px; }
}

/* loading + failure share the frame so the card does not change size when the
   answer lands. Never an affirmative empty state: neither says «свободен». */
.l3-now-card.is-load, .l3-now-card.is-fail {
  border-color: var(--stroke); background: var(--panel-strong); box-shadow: none;
  min-height: 144px;
}
.l3-now-card.is-fail { align-items: center; justify-content: center; }
.l3-skel { border-radius: 10px; background: linear-gradient(90deg, rgba(167, 139, 250, 0.08), rgba(167, 139, 250, 0.16), rgba(167, 139, 250, 0.08)); background-size: 200% 100%; animation: l3-skel 1.4s linear infinite; }
.l3-skel-board { width: 92px; height: 92px; flex: 0 0 auto; border-radius: 14px; }
.l3-skel-line { height: 13px; width: 40%; }
.l3-skel-line.is-wide { height: 20px; width: 62%; }
.l3-skel-cta { width: 150px; height: 46px; flex: 0 0 auto; border-radius: 12px; }
@keyframes l3-skel { from { background-position: 200% 0; } to { background-position: -200% 0; } }

/* ---------- the capsule grid ----------
   The mock's showcase: seven live capsules plus one «скоро» cell — exactly
   eight tiles, four across by two down, no hole in the grid. Three
   readings: the pinned room (>= 840 scene px — the grid takes the rest of
   the height and the capsules breathe with it), the tablet's two scrolling
   columns at 112px, and the phone's two columns at 88px. */
.l3-grid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 9px;
  min-width: 0;
}
.l3-grid .cap { height: 88px; }
.l3-grid .cap-soon { min-height: 88px; }
@container scene (min-width: 600px) {
  .l3-grid { gap: 10px; }
  .l3-grid .cap { height: 112px; }
  .l3-grid .cap-soon { min-height: 112px; }
}
@container scene (min-width: 840px) {
  .l3-grid {
    flex: 1; min-height: 0; gap: 11px;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    grid-template-rows: repeat(2, minmax(96px, 1fr));
  }
  .l3-grid .cap { height: auto; }
  .l3-grid .cap-soon { min-height: 0; }
}
@keyframes cap-in { from { opacity: 0; transform: translateY(10px) scale(0.985); } to { opacity: 1; transform: none; } }
.l3-grid:not(.no-anim) .cap { animation: cap-in 0.42s var(--spring) both; }

/* the tile — mock: r14 (r13 on a phone), art full-bleed, name+pitch over a
   bottom scrim */
.cap {
  position: relative; border-radius: 14px; overflow: hidden; cursor: pointer;
  display: flex; flex-direction: column; text-align: left;
  border: 1px solid var(--stroke); padding: 0; isolation: isolate;
  background: var(--panel-strong);
  transition: transform var(--dur-med) var(--spring), filter var(--dur-med);
}
@media (hover: hover) {
  .cap:hover { transform: translateY(-4px); filter: brightness(1.12); }
}
.cap:active { transform: translateY(-1px); }
.cap:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* procedural key-art for a game with no raster capsule: accent glow + dark base
   + neon scanlines + a giant sigil. */
.cap-art {
  position: absolute; inset: 0; z-index: 0;
  background:
    radial-gradient(135% 100% at 84% -14%, color-mix(in srgb, var(--accent) 78%, transparent), transparent 56%),
    radial-gradient(95% 85% at 4% 116%, color-mix(in srgb, var(--accent) 30%, transparent), transparent 68%),
    linear-gradient(160deg, #241638, #0e0a1c);
}
.cap-art::before {
  content: attr(data-sigil); position: absolute; right: -2px; top: -14px;
  font-size: 112px; line-height: 1; opacity: 0.24;
  filter: blur(0.3px) drop-shadow(0 4px 18px color-mix(in srgb, var(--accent) 50%, transparent));
  transform: rotate(-8deg);
}
.cap-art::after {
  content: ""; position: absolute; inset: 0; opacity: 0.55; mix-blend-mode: screen;
  background-image: repeating-linear-gradient(118deg, color-mix(in srgb, var(--accent) 18%, transparent) 0 1px, transparent 1px 10px);
}
/* .86, the mock's own opacity: the art is a layer of the tile, not a picture
   pasted on it, and at full strength the title stops being the loudest thing. */
.cap-photo { position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0.86; }
.cap-shade {
  position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(8, 6, 15, 0.05) 28%, rgba(8, 6, 15, 0.9) 88%);
}
/* mock's g.rim — the accent rides the whole edge plus a bottom bloom */
.cap-rim {
  position: absolute; inset: 0; z-index: 3; pointer-events: none; border-radius: inherit;
  box-shadow:
    inset 0 0 0 1px color-mix(in srgb, var(--accent) 23%, transparent),
    inset 0 -40px 50px -30px color-mix(in srgb, var(--accent) 33%, transparent);
}

.cap-top {
  position: relative; z-index: 2;
  display: flex; align-items: flex-start; gap: 5px;
  padding: 8px 9px;
}
.cap-body {
  position: relative; z-index: 2;
  margin-top: auto; padding: 0 9px 9px;
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
}
/* live counter — mock: 2px 7px / r5 / 9px 800, red when playing, gold queued */
.cap-live {
  display: inline-flex; align-items: center; white-space: nowrap;
  padding: 2px 7px; border-radius: 5px; font-size: 9px; font-weight: 800;
  background: rgba(255, 93, 108, 0.85); color: #fff; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
}
.cap-live.is-queue { background: rgba(241, 193, 82, 0.85); color: var(--gold-ink); }
/* fourth state: the corner used to go empty when nothing was live and nothing
   was queued, which is what a half-loaded tile looks like. */
.cap-live.is-quiet {
  background: rgba(20, 13, 38, 0.66); color: var(--gold); box-shadow: none;
  border: 1px dashed color-mix(in srgb, var(--gold) 55%, transparent);
}
/* build status, not table status — so it sits BESIDE the counter, never instead */
.cap-plate {
  display: inline-flex; align-items: center; white-space: nowrap;
  padding: 2px 7px; border-radius: 5px; background: rgba(192, 132, 252, 0.9);
  font-size: 9px; font-weight: 800; color: #1a1030;
}
.cap-diff {
  margin-left: auto; padding-top: 1px; white-space: nowrap;
  font-size: 9px; letter-spacing: 0.1em; color: rgba(236, 231, 250, 0.6);
  text-shadow: 0 1px 4px #000;
}
.cap-name { font-family: var(--font-display); font-size: 11px; font-weight: 700; text-shadow: 0 2px 8px #000; }
.cap-pitch {
  font-size: 9px; color: rgba(236, 231, 250, 0.62); line-height: 1.3; text-shadow: 0 1px 5px #000;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* The phone tile is the mock's phone capsule: the live pill in the corner,
   the name at the foot — no pitch, no stars, no plate (the 88px height has
   no room for three lines and the corner says the only thing that moves). */
@container scene (max-width: 599px) {
  .cap { border-radius: 13px; }
  .cap-top { position: absolute; right: 0; top: 0; padding: 7px; }
  .cap-plate, .cap-diff { display: none; }
  .cap-pitch { display: none; }
  .cap-name { font-size: 11px; }
}
@container scene (min-width: 600px) {
  .cap-name { font-size: 12px; }
}

/* the eighth cell — mock: dashed, a pill title, one row per game, reason at
   the foot. The rows are buttons here: both games are reachable and stay so. */
.cap-soon {
  position: relative; display: flex; flex-direction: column; gap: 6px;
  padding: 9px; border-radius: 14px; min-width: 0;
  border: 1px dashed rgba(167, 139, 250, 0.26); background: rgba(16, 11, 30, 0.44);
}
.cap-soon-title {
  align-self: flex-start; padding: 2px 8px; border-radius: 5px;
  background: rgba(167, 139, 250, 0.14); border: 1px solid rgba(167, 139, 250, 0.26);
  font-size: 9px; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase;
  color: rgba(196, 181, 253, 0.9);
}
.cap-soon-row {
  display: flex; align-items: center; gap: 7px; width: 100%;
  padding: 0; border: 0; background: none; cursor: pointer; text-align: left;
  transition: opacity var(--dur-fast, 0.16s);
}
@media (hover: hover) {
  .cap-soon-row:hover { opacity: 0.75; }
}
.cap-soon-icon { font-size: 14px; line-height: 1; opacity: 0.5; }
.cap-soon-name { font-family: var(--font-display); font-size: 11px; font-weight: 700; color: rgba(236, 231, 250, 0.62); }
.cap-soon-why { margin-top: auto; font-size: 9px; line-height: 1.4; color: rgba(236, 231, 250, 0.36); }

/* ---------- the wide card readings ----------
   620: the cards can stand their parts side by side — the mock's laptop
   geometry. Below it the cards stack (flex-wrap) and the clock shrinks. */
@container scene (min-width: 620px) {
  .l3-now-card { flex-wrap: nowrap; gap: 14px; padding: 15px 18px; }
  .l3-now-card.is-search { gap: 24px; padding: 18px 22px; }
  .l3-now-card.is-free { flex-direction: row; align-items: center; padding: 22px 24px; }
  .l3-mini { width: 114px; height: 114px; }
  .hero-emblem { width: 114px; height: 114px; }
  .l3-rings { width: 124px; height: 124px; }
  .l3-ring-a { inset: 16px; }
  .l3-ring-b { inset: 30px; }
  .l3-ring-c { inset: 44px; }
  .l3-clock { align-items: center; }
  .l3-clock-val { font-size: 37px; }
  .l3-clock-val.is-queue { font-size: 40px; }
  .l3-now-act { flex: 0 0 auto; }
  .l3-oppname { font-size: 17px; }
  .l3-search-title { font-size: 21px; }
  .l3-skel-board { width: 114px; height: 114px; }
  .l3h-first { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* The phone fold: the board and the clock shrink to the mock's phone
   measures (74px board, 30px clock) and the act row takes the full width. */
@container scene (max-width: 599px) {
  .l3-mini { width: 74px; height: 74px; border-radius: 11px; }
  .l3-mini-sq { font-size: 8px; }
  .hero-emblem { width: 74px; height: 74px; border-radius: 11px; }
  .hero-emblem-glyph { font-size: 34px; }
  .l3-clock-val { font-size: 26px; }
  .l3-fight { width: 100%; padding: 18px; font-size: 16px; }

  /* RB4b — the mock's phone «СЕЙЧАС» card, read off the rendered mock: the
     opponent line on top (avatar + name, the game·tc·move as the name's
     subline, the «ТВОЙ ХОД» pill pinned right), then a second row of board
     thumbnail + the big gold clock + the «К доске» button, with the
     head-to-head line tucked under the clock. The earlier `order` rebuild put
     the whole opponent block (rating + bordered chips + the turn label under
     the clock) on top and read as a shrunken laptop, not the mock. A grid with
     named areas lays the same DOM the desktop card uses into the mock's two
     rows; `display: contents` on the main block lets its children take their
     own cells. Scoped to the match card so search / free / load / fail keep
     their shapes. The rating and the desktop resign note are phone-only
     deletions (the mock shows neither there); the turn label moves into the
     pill, so the clock's own label is hidden on the phone. */
  .l3-now-card:not(.is-search):not(.is-free):not(.is-load):not(.is-fail) {
    display: grid; gap: 6px 12px; padding: 14px; border-radius: 16px;
    align-items: center;
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-rows: auto auto auto auto;
    grid-template-areas:
      "who   who   turn"
      "sub   sub   turn"
      "board clock cta"
      "board h2h   cta";
  }
  .l3-now-card:not(.is-search):not(.is-free):not(.is-load):not(.is-fail) .l3-now-main {
    display: contents;
  }
  .l3-now-card:not(.is-search):not(.is-free):not(.is-load):not(.is-fail) .l3-now-who { grid-area: who; }
  .l3-now-card:not(.is-search):not(.is-free):not(.is-load):not(.is-fail) .l3-chips {
    grid-area: sub; padding-left: 44px; gap: 5px;
  }
  .l3-now-card:not(.is-search):not(.is-free):not(.is-load):not(.is-fail) .l3-chips .l3-chip2 {
    padding: 0; background: none; border: 0;
    font-size: 10px; font-weight: 600; color: rgba(236, 231, 250, 0.44);
  }
  .l3-now-card:not(.is-search):not(.is-free):not(.is-load):not(.is-fail) .l3-oppsub { display: none; }
  .l3-now-card:not(.is-search):not(.is-free):not(.is-load):not(.is-fail) .l3-turn-pill {
    display: inline-flex; grid-area: turn;
  }
  .l3-now-card:not(.is-search):not(.is-free):not(.is-load):not(.is-fail) .l3-mini,
  .l3-now-card:not(.is-search):not(.is-free):not(.is-load):not(.is-fail) .hero-emblem {
    grid-area: board; align-self: center;
  }
  .l3-now-card:not(.is-search):not(.is-free):not(.is-load):not(.is-fail) .l3-clock { grid-area: clock; }
  .l3-now-card:not(.is-search):not(.is-free):not(.is-load):not(.is-fail) .l3-h2h { grid-area: h2h; }
  .l3-now-card:not(.is-search):not(.is-free):not(.is-load):not(.is-fail) .l3-now-act {
    grid-area: cta; flex: 0 0 auto; align-self: stretch; justify-content: center; gap: 0;
  }
  .l3-now-card:not(.is-search):not(.is-free):not(.is-load):not(.is-fail) .l3-cta {
    padding: 11px 16px; font-size: 12px;
  }
  .l3-now-card:not(.is-search):not(.is-free):not(.is-load):not(.is-fail) .l3-clock-lbl { display: none; }
  .l3-now-card:not(.is-search):not(.is-free):not(.is-load):not(.is-fail) .l3-cta-note { display: none; }
}

/* ---------- VS overlay (prematch latency mask) ---------- */
.vs-overlay {
  position: fixed; inset: 0; z-index: 200; display: flex; align-items: center; justify-content: center;
  /* The season duel art rides UNDER the scrim, not instead of it: the radial
     still darkens the centre so the two names and the VS sigil stay the
     loudest thing on the glass, and the art reads as atmosphere at the edges.
     `backdrop-filter` blurs the page BEHIND the overlay, never this image, so
     the art stays crisp. */
  background:
    radial-gradient(60% 60% at 50% 40%, rgba(20, 12, 30, 0.6), rgba(5, 7, 11, 0.92)),
    url("/chess/arena/assets/season/vs_duel_background.webp") center / cover no-repeat;
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  animation: vs-fade 220ms ease both;
}
/* `vw` IS THE WHOLE VIEWPORT, INCLUDING THE BANDS THE PHONE DOES NOT RENDER
   INTO, and this overlay is the one surface in the app that cannot borrow the
   shell's answer: ui/vs-screen.js appends it to <body>, so it is outside
   `.shell` (whose safe-area padding is in css/shell.css) and outside `#scene`
   (so `@container scene` cannot reach it either). `vw` is nonetheless the
   right unit here — the overlay is `position: fixed; inset: 0`, i.e. a
   window-sized latency mask, not a scene-sized panel. What was wrong was only
   that the 8% gutter was measured against pixels the device keeps.
   Measured over CDP with a notched phone's landscape insets (47 left, 47
   right) at 812x375 — an iPhone X/XS/11 Pro held sideways, the narrowest
   landscape a notch ships on: `92vw` = 747, clamped by the 720 cap, and the
   stage rendered 46..766 against a safe band of 47..765. One pixel into each
   ear, on both sides, with the 720px cap doing the only clamping. Taking the
   same 92% of the SAFE width instead: measured 660.55 wide at 75.72..736.27,
   28.7px clear of each ear. The cap is untouched wherever it was already the
   binding term — 390x844 measures 358.8 and 1280x800 measures 720, identical
   to before — and 844x390 with the same ears goes 720 -> 690, which is the 8%
   gutter this line has always asked for, now counted off the screen the phone
   actually has. */
.vs-stage {
  width: min(
    calc((100vw - env(safe-area-inset-left) - env(safe-area-inset-right)) * 0.92),
    720px
  );
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 14px;
}
.vs-card {
  display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center;
  padding: 22px 14px; border-radius: 18px; border: 1px solid var(--stroke);
  background: var(--panel); box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}
.vs-card.you { animation: vs-slide-l 360ms var(--spring) both; }
.vs-card.opp { animation: vs-slide-r 360ms var(--spring) both; }
.vs-avatar { width: 76px; height: 76px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 38px; background: var(--panel-soft); border: 2px solid var(--accent, var(--gold)); box-shadow: 0 0 24px color-mix(in srgb, var(--accent, var(--gold)) 40%, transparent); }
.vs-name { font-size: 17px; font-weight: 800; }
.vs-sub { font-size: 12px; color: var(--text-dim); }
.vs-mid { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.vs-sigil { font-size: 40px; font-weight: 900; color: var(--gold); text-shadow: 0 0 24px rgba(241, 193, 82, 0.6); animation: vs-pop 420ms var(--spring) both; }
.vs-game { font-size: 12px; font-weight: 800; letter-spacing: 0.6px; text-transform: uppercase; color: var(--text-dim); }
/* The only control on the overlay, and the only way past the 1.5s timer — so
   it is also the only one that cannot afford to be under the home indicator.
   `bottom: 26px` is measured from the window edge, and on a notched phone the
   bottom 34px of that window is a band the device reserves: measured at
   390x844 with a 34px bottom inset, the button occupied y 774..818 against a
   safe floor of 810 — 8px of a 44px target inside the gesture area. The 26px
   is the design's gap and is kept; it is now a gap above the indicator rather
   than above the glass. Inert wherever the inset is 0. */
.vs-skip { position: absolute; bottom: calc(26px + env(safe-area-inset-bottom)); left: 50%; transform: translateX(-50%); font-size: 12px; color: var(--text-dim); background: none; border: 1px solid var(--stroke); border-radius: var(--r-pill); padding: 7px 16px; cursor: pointer; }
@media (hover: hover) {
  .vs-skip:hover { color: var(--text); border-color: var(--gold); }
}

@keyframes vs-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes vs-slide-l { from { opacity: 0; transform: translateX(-40px) scale(0.94); } to { opacity: 1; transform: none; } }
@keyframes vs-slide-r { from { opacity: 0; transform: translateX(40px) scale(0.94); } to { opacity: 1; transform: none; } }
@keyframes vs-pop { 0% { opacity: 0; transform: scale(0.4) rotate(-12deg); } 60% { transform: scale(1.18) rotate(4deg); } 100% { opacity: 1; transform: none; } }

/* ---------- per-game hub blocks ----------
   The inner markup of the blocks in ui/game-hub.js. The hub no longer
   renders them (the chess context strip left with the document frame — RB2,
   back to the mock), but the blocks stay a tested library for the per-game
   screens to mount, and their grammar follows the library — every sheet is
   global, so the rules stay with the markup they style. */
.l3h-section { margin-top: 12px; }
.l3h-section-title { font-size: 11px; font-weight: 800; letter-spacing: 0.8px; text-transform: uppercase; color: var(--text-dim); margin: 0 0 6px; }
.l3h-empty { font-size: 12px; color: var(--text-dim); padding: 6px 0; }
.l3h-dim { font-size: 11px; color: var(--text-dim); }
.l3h-mystats { display: flex; align-items: center; gap: 12px; padding: 8px 11px; border: 1px solid var(--stroke); border-radius: 10px; background: var(--panel-soft); }
.l3h-rating { font-size: 24px; font-weight: 800; line-height: 1; }
.l3h-mystats-meta { display: flex; flex-direction: column; gap: 1px; font-size: 12px; }
.l3h-lb { display: flex; flex-direction: column; gap: 2px; }
.l3h-lb-row { display: grid; grid-template-columns: 22px 1fr auto; align-items: center; gap: 8px; padding: 4px 6px; border-radius: 7px; font-size: 12px; }
.l3h-lb-row.is-me { background: color-mix(in srgb, var(--gold) 14%, transparent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--gold) 40%, transparent); }
.l3h-lb-row.prov-row .l3h-rtg { color: var(--text-dim); }
.l3h-rank { color: var(--text-dim); font-weight: 700; text-align: right; }
.l3h-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.l3h-namecol { display: flex; flex-direction: column; min-width: 0; gap: 1px; }
.l3h-rtg { font-weight: 700; color: var(--text); }
.l3h-list { display: flex; flex-direction: column; gap: 2px; }
.l3h-list-row { display: flex; align-items: center; gap: 8px; padding: 4px 6px; font-size: 12px; }
.l3h-list-row .l3h-name { flex: 1; }
.l3h-rings { display: flex; flex-direction: column; gap: 5px; }
.l3h-ring-bar { height: 6px; border-radius: var(--r-pill); background: rgba(255, 255, 255, 0.08); overflow: hidden; }
.l3h-ring-bar > i { display: block; height: 100%; border-radius: var(--r-pill); background: linear-gradient(90deg, var(--gold), color-mix(in srgb, var(--gold) 60%, #fff)); box-shadow: 0 0 10px color-mix(in srgb, var(--gold) 50%, transparent); transition: width var(--dur-med) var(--spring); }
/* the three facts about the rules — 27 registry keys, rendered by blockRules */
.l3-bullets { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.l3-bullets li { display: flex; gap: 8px; align-items: flex-start; font-size: 11.5px; line-height: 1.5; color: var(--text-dim); }
.l3-bullets li::before { content: "\25B8"; flex: 0 0 auto; color: var(--gold); font-size: 11px; line-height: 1.5; }
.l3h-first { position: relative; display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px; }
.l3h-first-row {
  display: flex; align-items: center; gap: 10px; min-width: 0; text-align: left;
  padding: 10px 12px; border-radius: 12px; cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--gold) 30%, transparent);
  background: linear-gradient(120deg, color-mix(in srgb, var(--gold) 9%, var(--panel-soft)), var(--panel-soft));
  color: var(--text); transition: border-color var(--dur-fast), transform var(--dur-fast);
}
@media (hover: hover) {
  .l3h-first-row:hover { border-color: color-mix(in srgb, var(--gold) 70%, transparent); transform: translateY(-2px); }
}
.l3h-first-row:active { transform: none; }
.l3h-first-icon { flex: 0 0 auto; font-size: 21px; line-height: 1; }
.l3h-first-col { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.l3h-first-name { font-family: var(--font-display); font-size: 12px; font-weight: 700; }
.l3h-first-sub { font-size: 9px; line-height: 1.3; color: var(--text-soft); overflow: hidden; text-overflow: ellipsis; }
.l3h-link { background: none; border: none; color: var(--gold); font-size: 11px; font-weight: 700; cursor: pointer; padding: 2px 4px; }
@media (hover: hover) {
  .l3h-link:hover { text-decoration: underline; }
}
.l3h-first-watch { grid-column: 1 / -1; justify-self: start; padding: 2px 0; }
.l3h-first-note { grid-column: 1 / -1; font-size: 10px; color: rgba(236, 231, 250, 0.36); line-height: 1.4; }

@media (prefers-reduced-motion: reduce) {
  .vs-card.you, .vs-card.opp, .vs-sigil, .vs-overlay { animation: none; }
  .l3-grid:not(.no-anim) .cap { animation: none; }
  .l3-clock-val, .l3-ring-spin, .l3-ring-b, .l3-ring-c, .l3-bar-i, .l3-sheen::before,
  .l3-skel, .l3-now-body.is-flash { animation: none; }
}

/* neon icon assets sized into the glyph boxes above (iconNode renders <img>) */
img.hero-emblem-glyph { width: 48px; height: 48px; object-fit: contain; display: block; }
img.l3-avatar { width: 22px; height: 22px; object-fit: contain; }
img.l3-cta-glyph { width: 16px; height: 16px; object-fit: contain; }
.l3-fight img.l3-cta-glyph { width: 20px; height: 20px; }
img.l3-ring-sigil { width: 34px; height: 34px; object-fit: contain; }
img.l3-first-game-icon { width: 22px; height: 22px; object-fit: contain; }
img.cap-soon-icon { width: 16px; height: 16px; object-fit: contain; }
img.l3h-first-icon { width: 22px; height: 22px; object-fit: contain; }
img.vs-avatar { width: 46px; height: 46px; object-fit: contain; border-radius: 50%; }
