/* Neon Arena — arcade platform chrome: the game-switcher tab-bar (still used by
   the dashboard) + the per-game lobby, drawn to the Claude Design console's
   «isGame» screen. Loaded after components.css; builds on .panel/.chip-sq/
   .avatar-tile/.segmented rather than re-declaring them.

   EVERY NUMBER BELOW IS THE MOCK'S, copied from console.html rather than
   eyeballed. The hero is `min-height:210px` (250 at the medium size class),
   `border-radius:18px`, a `1px solid <accent>55` edge and the 90° scrim
   `rgba(8,6,15,.94) 24% → rgba(8,6,15,.32) 72% → rgba(8,6,15,.7)`; the body is
   `padding:22px 26px; gap:10px; max-width:660px`; the title is Unbounded 34/800
   at -.01em; the blurb 13px/1.5 at .68 alpha capped to 430px; the plates sit at
   `right:20px; top:18px` with `padding:8px 13px; radius:11px`, a 17px mono
   value and a 9px .1em uppercase caption. The grid is three equal columns at
   `gap:12px` under a 14px gap from the hero, inside `padding:16px 18px`.

   THE BREAKPOINT IS THE SCENE, NOT THE WINDOW. At a 1280px window the console
   leaves the scene ~858px, so a `@media (max-width: 860px)` here fires on a
   desktop and never fires on an 900px window whose scene is 527px. 620 scene
   pixels is the two-column budget stated in shell.css; three columns need 900.
   The one deliberate window query is the 44px touch floor, which is a property
   of the input device rather than of the layout. */

/* === game tabs (dashboard switcher) === */
.game-tabs {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  padding: 4px;
  border-radius: var(--r-pill);
  background: var(--panel-soft);
  border: 1px solid var(--stroke);
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.game-tabs::-webkit-scrollbar { display: none; }
.gt-tab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 14px;
  border-radius: var(--r-pill);
  font-weight: 700;
  font-size: 13px;
  color: var(--text-dim);
  white-space: nowrap;
  transition: background var(--dur-fast), color var(--dur-fast), transform var(--dur-med) var(--spring),
    box-shadow var(--dur-med), opacity var(--dur-fast);
}
@media (hover: hover) {
  .gt-tab:hover:not(:disabled):not(.active) { color: var(--text); background: var(--stroke); transform: translateY(-1px); }
}
.gt-tab.active {
  background: var(--gold-grad);
  color: #201503;
  box-shadow: 0 4px 14px rgba(224, 162, 58, 0.4);
}
.gt-icon { font-size: 15px; line-height: 1; }
.gt-tab.gt-soon { opacity: 0.5; cursor: default; }
.gt-badge {
  font-size: 9px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  padding: 2px 7px;
  border-radius: var(--r-pill);
  border: 1px dashed var(--stroke-strong);
  color: var(--text-faint);
}

/* ==========================================================================
   THE SIX ARCADE BOARDS — what they share with the chess board
   ==========================================================================
   Each of the six is `.view.view-game.view-XX`, so game.css owns the frame:
   a `minmax(0,1fr) 340px` grid whose height is the SCENE's, written by
   `fitMatchColumn()` into `--match-h`, with the opponent bar pinned to the
   top of the board column, the board taking what is left and the player's own
   bar and the action row at the bottom. Before tonight every one of them
   re-declared that frame as a scrolling `align-items:start` stack and every
   one of them ran past the fold: 743px of column in a 660px scene for
   checkers, 811 for durak, 946 for battleship, 1099 for arcomage.

   The one thing they need that chess does not: chess lifts its game buttons
   INTO the player's bar once the column is 560px wide, and at the console's
   own 1280px window the column is 468. Two boxes of buttons at 471px in a
   468px row wrapped to a second 39px line and took it out of the board. They
   shrink instead. */
@container scene (min-width: 620px) {
  .view-ck .g-actions-row,
  .view-nd .g-actions-row,
  .view-dk .g-actions-row,
  .view-bs .g-actions-row,
  .view-tc .g-actions-row,
  .view-tr .g-actions-row { flex-wrap: nowrap; }
  .view-ck .g-actions-row > .g-controls:first-child,
  .view-nd .g-actions-row > .g-controls:first-child,
  .view-dk .g-actions-row > .g-controls:first-child,
  .view-bs .g-actions-row > .g-controls:first-child,
  .view-tc .g-actions-row > .g-controls:first-child,
  .view-tr .g-actions-row > .g-controls:first-child { flex: 0 1 auto; min-width: 0; flex-wrap: nowrap; }
  .view-ck .g-actions-row .btn,
  .view-nd .g-actions-row .btn,
  .view-dk .g-actions-row .btn,
  .view-bs .g-actions-row .btn,
  .view-tc .g-actions-row .btn,
  .view-tr .g-actions-row .btn { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
}

/* On a phone the utility controls move to the persistent bottom rail, leaving
   the match-action group alone in a full-width row.  Its desktop `flex:0 0
   auto` kept the group at a 280px intrinsic width on a 359px row, so Russian
   «Предложить ничью» was ellipsized while 79px sat unused to its right. */
@container scene (max-width: 619px) {
  .view-ck .g-actions-row > .g-controls:first-child,
  .view-nd .g-actions-row > .g-controls:first-child,
  .view-dk .g-actions-row > .g-controls:first-child,
  .view-bs .g-actions-row > .g-controls:first-child,
  .view-tc .g-actions-row > .g-controls:first-child,
  .view-tr .g-actions-row > .g-controls:first-child {
    flex: 1 1 100%;
    min-width: 0;
  }
}

/* ==========================================================================
   PER-GAME LOBBY
   ========================================================================== */

/* The mock's outer frame: `height:100%; grid-template-rows:auto minmax(0,1fr);
   gap:14px; padding:16px 18px; overflow:hidden`. `--al-h` is the scene height
   the shell measured; without it (no ResizeObserver) the screen falls back to
   the scrolling stack it had before rather than collapsing to nothing. */
/* Five children in this order: hero, the phone action row, the hero's retry
   strip, the panel grid, the footer. Only the grid gives — everything else is
   its own height, and a row count that did not match the child count left the
   panels short of the fold by exactly the footer's height. */
.al {
  display: grid;
  grid-template-rows: auto auto auto minmax(0, 1fr) auto;
  gap: 14px;
  height: var(--al-h, auto);
  min-height: 0;
  padding: 2px 0 0;
  overflow: hidden;
}

/* --- hero ---------------------------------------------------------------- */
.al-hero {
  position: relative;
  display: flex;
  align-items: stretch;
  min-height: 210px;
  border-radius: var(--r-xl);
  overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--accent, #f1c152) 34%, transparent);
  isolation: isolate;
}
/* Both art layers (raster capsule, procedural fallback) sit under the scrim. */
.al-hero-art { z-index: 0; opacity: 1; }
.al-hero-scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(90deg, rgba(8, 6, 15, 0.94) 24%, rgba(8, 6, 15, 0.32) 72%, rgba(8, 6, 15, 0.7));
}
.al-hero-body {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 10px;
  min-width: 0;
  max-width: 660px;
  padding: 22px 26px;
}
.al-kicker {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent, var(--gold));
}
.al-name {
  margin: 0;
  font-family: var(--font-display);
  font-size: 34px;
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1.1;
}
.al-blurb {
  margin: 0;
  max-width: 430px;
  font-size: 13px;
  line-height: 1.5;
  color: rgba(var(--t-mute), 0.68);
  text-wrap: pretty;
}

/* --- hero actions -------------------------------------------------------- */
.al-acts {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 4px;
}
.al-cta-slot { display: contents; }
/* «⚡ В БОЙ» — the mock's gold pill: 11px 22px, r11, Unbounded 13/800. */
.al-cta {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 11px 22px;
  border-radius: var(--r-md);
  background: var(--gold-grad);
  color: var(--gold-ink);
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 800;
  white-space: nowrap;
  box-shadow: 0 8px 24px rgba(var(--t-gold), 0.3);
  transition: transform 0.18s var(--ease-out), box-shadow 0.18s;
}
.al-cta:disabled { opacity: 0.55; }
/* «▶ К доске» — outlined gold, because the board already exists and the loud
   button on this screen is the one that starts something. */
.al-resume {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 11px 20px;
  border-radius: var(--r-md);
  border: 1px solid rgba(var(--t-gold), 0.5);
  background: rgba(var(--t-gold), 0.12);
  color: var(--gold);
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 800;
  white-space: nowrap;
  transition: background 0.18s, transform 0.18s var(--ease-out);
}
.al-lock {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 9px 14px;
  border-radius: var(--r-md);
  border: 1px dashed rgba(var(--t-violet), 0.28);
  background: rgba(16, 11, 30, 0.5);
  font-size: 11px;
  font-weight: 700;
  color: rgba(var(--t-mute), 0.44);
}
.al-seek {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 11px 18px;
  border-radius: var(--r-md);
  border: 1px solid rgba(var(--t-violet), 0.4);
  background: rgba(124, 58, 237, 0.14);
  font-size: 12px;
  font-weight: 800;
  color: var(--violet-300);
}
.al-seek-dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: conic-gradient(from 0deg, rgba(var(--t-violet), 0) 40%, var(--violet));
  animation: ac-spin 1.4s linear infinite;
}
.al-seek-time { font-size: 12px; }
/* «You are already searching, over there.» Same family as .al-seek — this is a
   live search too, just not this game's — but in gold, because it is a warning
   about what the button beside it will undo, not a status of this screen. */
.al-elsewhere {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 11px 18px;
  border-radius: var(--r-md);
  border: 1px solid rgba(var(--t-gold), 0.4);
  background: rgba(241, 193, 82, 0.1);
  font-size: 12px;
  font-weight: 800;
  color: var(--gold);
}
.al-elsewhere .al-seek-dot { background: conic-gradient(from 0deg, rgba(var(--t-gold), 0) 40%, var(--gold)); }
.al-elsewhere-txt { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.al-cta.is-replace { background: rgba(241, 193, 82, 0.16); border-color: rgba(var(--t-gold), 0.5); color: var(--gold); }
.al-cancel {
  padding: 11px 18px;
  border-radius: var(--r-md);
  border: 1px solid rgba(var(--t-danger), 0.34);
  background: rgba(var(--t-danger), 0.07);
  font-size: 12px;
  font-weight: 700;
  color: var(--danger-soft);
  white-space: nowrap;
  transition: border-color 0.18s;
}
.al-act {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 11px 18px;
  border-radius: var(--r-md);
  border: 1px solid rgba(var(--t-violet), 0.3);
  background: rgba(20, 13, 38, 0.62);
  font-size: 12px;
  font-weight: 700;
  color: var(--text-dim);
  white-space: nowrap;
  transition: border-color 0.18s, transform 0.18s var(--ease-out);
}
.al-act:disabled { opacity: 0.45; }
@media (hover: hover) {
  .al-cta:hover:not(:disabled) { transform: translateY(-2px); }
  .al-resume:hover { background: rgba(var(--t-gold), 0.22); transform: translateY(-2px); }
  .al-cancel:hover { border-color: rgba(var(--t-danger), 0.7); }
  .al-act:hover:not(:disabled) { border-color: rgba(var(--t-violet), 0.6); }
}

/* --- hero plates --------------------------------------------------------- */
.al-plates {
  position: absolute;
  z-index: 2;
  right: 20px;
  top: 18px;
  display: flex;
  gap: 8px;
}
.al-plate {
  padding: 8px 13px;
  border-radius: var(--r-md);
  background: rgba(10, 7, 20, 0.72);
  border: 1px solid rgba(var(--t-violet), 0.22);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  text-align: center;
}
.al-plate-val {
  font-size: 17px;
  font-weight: 700;
  line-height: 1.2;
}
.al-plate.is-gold .al-plate-val { color: var(--gold); }
/* In flight: the plate keeps its size and says nothing. A number here would be
   a claim about the player that no request has answered. */
.al-plate-val.is-waiting {
  color: transparent;
  border-radius: 4px;
  background: linear-gradient(90deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.05));
  background-size: 200% 100%;
  animation: shimmer 1.4s linear infinite;
}
.al-plate-cap {
  font-size: 9px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(var(--t-mute), 0.44);
}

/* --- the hero's one retry ------------------------------------------------ */
.al-fail { margin: 0; }
.al-fail:empty { display: none; }

/* --- three panels -------------------------------------------------------- */
.al-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
  min-height: 0;
  height: 100%;
  overflow: hidden;
}
/* deadends-map C3: quiz/poker drop the table panel (no arcade leaderboard),
   so their grid is two real columns, not three with an empty third. Scoped to
   the wide reading only — the 839/619 breakpoints below already collapse
   everyone to 2/1 columns and must keep winning. */
@container scene (min-width: 840px) {
  .al-grid.is-duo { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* rules panel */
.al-setup { padding-bottom: 0; }
.al-chips {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 12px 14px;
}
/* A rule in force, not an option. No hover, no press, no gold — the row states
   how the server plays and there is nothing here to choose. */
.chip-sq.is-fixed {
  border-style: dashed;
  border-color: rgba(var(--t-violet), 0.24);
  background: rgba(20, 13, 38, 0.5);
  color: rgba(var(--t-mute), 0.6);
  cursor: default;
}
@media (hover: hover) {
  .chip-sq.is-fixed:hover { border-color: rgba(var(--t-violet), 0.24); color: rgba(var(--t-mute), 0.6); }
}
/* THE BULLETS GIVE, THE DOOR DOES NOT. The panel is a fixed-height grid cell
   with `overflow: hidden`, so whatever does not fit is not scrolled to — it is
   cut off and unreachable. With the door added, nardy and arcomage (the two
   longest chip rows) pushed it clean out of the panel at 1280×720. The teaser
   lines are the part that can be shortened by scrolling; the door is the part
   that must always be in the same place, so it is the bullets that take the
   squeeze. */
.al-bullets {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 0 14px 12px;
}
.al-bullet {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 11px;
  line-height: 1.5;
  color: rgba(var(--t-mute), 0.6);
}
.al-bullet-mark { color: var(--gold); font-size: 11px; line-height: 1.5; }
/* The door to the full rules. `margin-top: auto` pins it to the foot of the
   panel whatever the game's teaser lines add up to, so it is in the same place
   on every game's lobby; `flex: 0 0 auto` keeps a long chip row from squeezing
   it. It is a quiet control on purpose — it opens reading, not a game. */
.al-rules-door {
  flex: 0 0 auto;
  margin: 0 14px 14px;
  padding: 9px;
  border-radius: var(--r-sm);
  border: 1px solid rgba(var(--t-violet), 0.22);
  background: rgba(20, 13, 38, 0.5);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--text-soft);
  transition: border-color 0.16s, color 0.16s;
}
@media (hover: hover) {
  .al-rules-door:hover { border-color: rgba(var(--t-violet), 0.45); color: var(--text); }
}
/* The queue note is pinned to the foot by an auto margin of its own. Two auto
   margins in one column SHARE the slack, which would leave the note floating
   in the middle of the panel — so when the note is up, the door stops claiming
   any and simply sits under it. */
.al-note:not(.hidden) + .al-rules-door { margin-top: 0; }
/* The sheet. Capped at a readable measure and at the viewport, and it is the
   BODY that scrolls, not the page: the lobby behind it is a fixed-height grid
   with `overflow: hidden` and a modal that grows past the fold is a modal with
   an unreachable bottom. */
.al-rules-card {
  display: flex;
  flex-direction: column;
  width: min(560px, 100%);
  max-height: 100%;
  min-height: 0;
  border-radius: var(--r-lg, 16px);
  border: 1px solid var(--stroke);
  background: var(--panel, #14102a);
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.55);
  overflow: hidden;
}
.al-rules-head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--stroke);
}
.al-rules-title {
  flex: 1;
  min-width: 0;
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 800;
}
.al-rules-x {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  color: var(--text-soft);
  font-size: 13px;
}
@media (hover: hover) { .al-rules-x:hover { background: var(--stroke); color: var(--text); } }
.al-rules-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 4px 16px 16px;
}
.al-rules-sec {
  margin: 16px 0 8px;
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold);
}
.al-rules-sec:first-child { margin-top: 10px; }
.al-rules-line {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-bottom: 7px;
  font-size: 12px;
  line-height: 1.55;
  color: rgba(var(--t-mute), 0.72);
  text-wrap: pretty;
}
.al-rules-mark { flex: 0 0 auto; color: rgba(var(--t-violet), 0.7); line-height: 1.55; }
.al-note {
  display: flex;
  align-items: center;
  gap: 9px;
  margin: auto 14px 14px;
  padding: 11px 13px;
  border-radius: var(--r-md);
  background: rgba(var(--t-ok), 0.08);
  border: 1px solid rgba(var(--t-ok), 0.24);
  font-size: 11px;
  line-height: 1.4;
  color: rgba(var(--t-mute), 0.72);
  text-wrap: pretty;
}
.al-note-dot {
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ok);
  animation: ac-pulse 1.6s infinite;
}

/* live panel */
.al-live-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--danger);
  animation: ac-pulse 1.4s infinite;
}
/* The stream carries live games AND the ones that just ended, so unlike the
   old live-only list it is always longer than the panel. Same column/scroll
   treatment `.al-top` got: without it the tail is sheared by `.al{overflow:
   hidden}` and nothing is scrollable, because nothing knows it overflowed. */
.al-live .panel-body {
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.al-live-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}
/* The list takes the slack; the foot never gives any. `flex: 0 0 auto` is the
   whole reason «Показать ещё» stays put: as a shrinkable child of a column
   flex parent it would be squeezed by a full list, and it is the one control
   in this panel that must be the same size and in the same place every time
   you look for it. */
.al-live-foot {
  flex: 0 0 auto;
  padding: 0 8px 8px;
}
.al-live-foot:empty { display: none; }
.al-live-foot .al-more { width: 100%; margin: 0; }
.al-live-watch { font-size: 10px; color: rgba(var(--t-mute), 0.5); white-space: nowrap; }

/* ── the stream ──────────────────────────────────────────────────────────
   One list, two states. A running game is lit from the left and pulses; a
   finished one keeps the same box and simply stops — no colour, no motion,
   its score where the pulse was. That sameness is the point: the row does
   not move house when the game ends, it just goes quiet and drifts down as
   newer games land above it. */
.al-st-row {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 8px 10px;
  border-radius: 10px;
  background: rgba(30, 20, 54, 0.5);
  border: 1px solid transparent;
  border-left: 2px solid transparent;
  text-align: left;
  transition: border-color 0.16s, background 0.16s, opacity 0.3s;
}
.al-st-row.is-live { border-left-color: rgba(var(--t-violet), 0.55); }
.al-st-row.is-done {
  background: rgba(24, 17, 44, 0.42);
  opacity: 0.82;
}
@media (hover: hover) {
  .al-st-row:hover { border-color: rgba(var(--t-violet), 0.36); background: rgba(40, 26, 70, 0.6); }
  .al-st-row.is-done:hover { opacity: 1; }
}
.al-st-row:active { background: rgba(40, 26, 70, 0.6); }
/* «Вот эта только что закончилась» — one pass, then it is an ordinary
   finished row like the rest. */
.al-st-row.is-just { animation: al-st-settle 1.5s ease-out 1; }
@keyframes al-st-settle {
  0% { background: rgba(var(--t-violet), 0.3); opacity: 1; }
  100% { background: rgba(24, 17, 44, 0.42); opacity: 0.82; }
}
.al-st-pulse {
  flex: 0 0 auto;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--danger);
  animation: ac-pulse 1.4s infinite;
}
.al-st-score {
  flex: 0 0 auto;
  min-width: 26px;
  font-size: 10px;
  font-weight: 700;
  color: rgba(var(--t-mute), 0.55);
}
.al-st-id { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.al-st-pair {
  font-size: 11px;
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.al-st-vs { color: rgba(var(--t-mute), 0.45); }
.al-st-win { color: var(--gold); }
.al-st-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px;
  font-size: 9px;
  color: rgba(var(--t-mute), 0.45);
}
.al-st-sep { color: rgba(var(--t-mute), 0.25); }
.al-st-clock { font-variant-numeric: tabular-nums; }
.al-st-delta { color: rgba(var(--t-mute), 0.6); }
.al-st-ago { color: rgba(var(--t-mute), 0.35); }

/* table panel */
/* THE TENTH ROW USED TO BE CUT IN HALF, and «показать ещё» under «Недавние»
   could not be reached at all. Both were the same omission: `.panel-body` is
   `flex:1; min-height:0` but was never told to lay its children out in a
   column, so the list had no bounded height, grew past the panel and was
   sheared off by `.al { overflow: hidden }`. Nothing was scrollable because
   nothing knew it had overflowed.
   The body is a column now: the list takes the slack and scrolls only if it
   still cannot fit, and the foot (pager + your own rank) is pinned to the
   bottom where it is always reachable. */
.al-top .panel-body {
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.al-top-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 6px 8px;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.al-top-row {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 7px 9px;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  text-align: left;
}
.al-top-row.is-me {
  background: rgba(var(--t-gold), 0.12);
  border-color: rgba(var(--t-gold), 0.3);
}
.al-top-row.is-prov { opacity: 0.72; }
.al-top-rank {
  width: 26px;
  flex: 0 0 auto;
  font-size: 10px;
  font-weight: 700;
  color: rgba(var(--t-mute), 0.3);
}
/* ПОДИУМ — ЭТО МЕСТО, А НЕ СТРОКА. Правило было `:nth-child(-n + 3)`, то есть
   красило первые три строки СПИСКА: на третьей странице золотыми становились
   13, 14 и 15, а закреплённая внизу строка «твоё место» — единственный ребёнок
   своего контейнера — красилась всегда, каким бы место ни было. Класс ставит
   JS по `row.rank`, и позиция на экране больше ни на что не влияет. */
.al-top-row.is-podium .al-top-rank { color: var(--gold); }
.al-top-name {
  flex: 1;
  min-width: 0;
  font-size: 11px;
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.al-top-wl { font-size: 9px; color: rgba(var(--t-mute), 0.36); white-space: nowrap; }
.al-top-rating { font-size: 11px; font-weight: 700; color: var(--gold); }
/* The pager and «your place» live in the foot, so they survive any list
   length: the list may scroll, these never leave the screen. */
.al-top-foot { flex: 0 0 auto; }
.al-pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 4px 9px 0;
}
.al-pager button {
  min-width: 26px;
  padding: 3px 7px;
  font-size: 11px;
  line-height: 1;
  border-radius: var(--r-sm);
  border: 1px solid rgba(var(--t-mute), 0.18);
  background: transparent;
  color: rgba(var(--t-mute), 0.75);
}
.al-pager button:disabled { opacity: 0.3; }
@media (hover: hover) {
  .al-pager button:not(:disabled):hover {
    border-color: rgba(var(--t-gold), 0.4);
    color: var(--gold);
  }
}
.al-pager-at { font-size: 10px; color: rgba(var(--t-mute), 0.5); font-variant-numeric: tabular-nums; }
/* Your own line when the page you are looking at is not the page you are on.
   Same row grammar as the board above it, separated by a rule so it reads as
   an answer to «а я где?» rather than as an eleventh place. */
.al-top-mine {
  margin: 4px 8px 0;
  padding-top: 5px;
  border-top: 1px solid rgba(var(--t-mute), 0.12);
}
.al-top-mine .al-top-row { background: rgba(var(--t-gold), 0.12); border-color: rgba(var(--t-gold), 0.3); }
.al-top-foot {
  padding: 0 9px 9px;
  font-size: 9px;
  color: rgba(var(--t-mute), 0.36);
}
@media (hover: hover) {
}

/* --- shared list furniture ---------------------------------------------- */
.al-more {
  margin: 4px 2px 0;
  padding: 8px;
  border-radius: var(--r-sm);
  border: 1px solid rgba(var(--t-violet), 0.2);
  background: rgba(20, 13, 38, 0.5);
  font-size: 10px;
  font-weight: 700;
  color: var(--text-soft);
}
/* Answered, and the answer was «nobody». Deliberately not the load-fail block:
   this one is a fact about the game, that one is a fact about the request. */
.al-empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 9px;
  padding: 20px 16px;
  text-align: center;
}
.al-empty-icon { font-size: 26px; opacity: 0.5; }
.al-empty-title { font-size: 12px; font-weight: 800; }
.al-empty-sub {
  font-size: 10px;
  line-height: 1.45;
  color: rgba(var(--t-mute), 0.44);
  text-wrap: pretty;
}
.al-empty-line {
  padding: 14px 9px;
  font-size: 11px;
  color: rgba(var(--t-mute), 0.44);
}
/* Not an empty state and not a failure: the table exists, this browser is
   simply not allowed to ask for it. */
.al-guest {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 9px;
  padding: 12px 9px;
}
/* Skeletons at the FINAL geometry: a live row is 44px tall and a table row 30,
   so the panel does not resize when the data lands. */
.al-sk-live { height: 44px; border-radius: 10px; margin: 0; }
.al-sk-row { height: 30px; border-radius: var(--r-sm); margin: 0; }

.al-foot { display: none; }
.al-foot:has(.al-foot-btn) {
  display: flex;
  justify-content: center;
  padding: 2px 0 0;
}
.al-foot-btn { font-size: 11px; color: var(--text-faint); }

/* === below 840 scene px: two columns, and the frame scrolls ===
   840, not shell.css's generic 900. Measured rather than assumed: at the
   console's flagship 1280px window the scene is 858px and the mock draws all
   three panels there — its own laptop class has a 902px scene and three
   columns of ~292. Three of ours at 858 come out 278 wide, which is wider than
   the narrowest panel the mock ever draws (252, the medium context column).
   Below 840 the third column would go under 272 and the leaderboard's four
   fields start colliding, so it drops to two and the third panel spans.

   The mock's `layScroll` rule applies from here down: collapsed columns mean
   the content is taller than the frame, so the container must scroll or the
   panels get crushed below their own height. */
@container scene (max-width: 839px) {
  .al { height: auto; overflow: visible; }
  .al-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-rows: min-content;
    height: auto;
    overflow: visible;
  }
  .al-live, .al-top { min-height: 296px; }
  .al-top { grid-column: 1 / -1; }
  .al-hero { min-height: 250px; }
}

/* === compact: the mock's phone screen ===
   132px art with a 180° scrim and the name at its foot, the two plates as a
   full-width pair under it, then the blurb, the buttons and the panels. */
@container scene (max-width: 619px) {
  .al { gap: 11px; }
  .al-grid { grid-template-columns: minmax(0, 1fr); }
  .al-top { grid-column: auto; }
  .al-live, .al-top { min-height: 0; }
  .al-hero {
    flex-direction: column;
    justify-content: flex-end;
    min-height: 132px;
    border-radius: var(--r-lg);
  }
  .al-hero-scrim {
    background: linear-gradient(180deg, rgba(8, 6, 15, 0.1) 30%, rgba(8, 6, 15, 0.94));
  }
  .al-hero-body {
    max-width: none;
    justify-content: flex-end;
    gap: 3px;
    padding: 11px 13px;
  }
  .al-name { font-size: 19px; }
  .al-blurb { display: none; }
  .al-acts { display: none; }
  /* On the phone the plates are a row of their own under the art, not an
     overlay: at 390px they would sit on top of the title. */
  .al-plates {
    position: static;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
  }
  .al-plate {
    padding: 9px 11px;
    border-radius: 12px;
    text-align: left;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  .al-plate.is-gold {
    background: rgba(var(--t-gold), 0.09);
    border-color: rgba(var(--t-gold), 0.28);
  }
  .al-plate:not(.is-gold) {
    background: rgba(var(--t-violet), 0.08);
    border-color: rgba(var(--t-violet), 0.18);
  }
  .al-plate:not(.is-gold) .al-plate-val { color: var(--violet-300); }
  .al-plate-cap { font-size: 8px; }
  /* The hero's buttons move below it, full width and thumb-sized. */
  .al-phone-acts {
    display: flex;
    flex-direction: column;
    gap: 7px;
  }
  .al-phone-acts .al-acts { display: flex; margin: 0; }
  .al-phone-acts .al-cta,
  .al-phone-acts .al-resume {
    flex: 1 1 100%;
    justify-content: center;
    padding: 14px;
    border-radius: 13px;
    font-size: 14px;
  }
  .al-phone-acts .al-act,
  .al-phone-acts .al-cancel {
    flex: 1 1 0;
    justify-content: center;
    min-height: 44px;
    padding: 12px;
    border-radius: 12px;
    font-size: 11px;
  }
  .al-phone-acts .al-seek { flex: 1 1 100%; justify-content: center; min-height: 44px; }
  .al-phone-acts .al-lock { flex: 1 1 100%; font-size: 10px; line-height: 1.4; }
}

/* The 44px touch floor is a property of the pointer, not of the layout, so it
   is the one deliberate window query in this file. */
@media (pointer: coarse) {
  .al-cta, .al-resume, .al-act, .al-cancel { min-height: 44px; }
  .al-st-row { min-height: 44px; }
}

@media (prefers-reduced-motion: reduce) {
  .al-seek-dot, .al-note-dot, .al-live-dot, .al-st-pulse { animation: none; }
  .al-st-row.is-just { animation: none; }
  .al-plate-val.is-waiting { animation: none; }
}

/* The hero's promo slot: the searching state's bot-warm-up suggestion and
   the push pill stack under the action row without touching its geometry. */
.al-promo-slot { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; max-width: 430px; }
.al-promo-slot:empty { display: none; }

/* A withheld rating is not a dim rating — it is a different kind of value, so
   it reads as a word rather than a smaller number. */
.al-top-rating.is-tbd { color: var(--text-dim); font-weight: 700; letter-spacing: 0.06em; }
