/* ── Fonts ────────────────────────────────────────────────────────────────────
   Three VARIABLE faces, not a pile of static instances. Each carries a live
   `wght` axis over 400..700, so 400/600/700 — and anything between — come out of
   one file instead of needing one download per weight.

   NB the naive version of this is a REGRESSION, not a saving: a variable font
   ships interpolation deltas for its whole design space, so the stock Noto Sans
   VF is 513 KB against 181 KB for a single static instance. Two things pay for
   it, and both are load-bearing:
     · the axes are cut to what we use — `wdth` pinned out entirely, `wght`
       clamped to 400..700 instead of 100..900;
     · the astral plane is subset away (mathematical alphanumerics, phonetic
       extensions). Every BMP codepoint the old fonts had survives — all Cyrillic
       and Greek, punctuation, arrows, math, and U+0301 COMBINING ACUTE, which is
       what ЧГК stress accents are made of. Dropping that would corrupt questions.
   Net: 416 KB for three faces vs 362 KB for the old two, and we gain real
   italics (previously the browser SYNTHESISED them by shearing the roman) and a
   real mono. Regenerate with the recipe in DESIGN.md if a face is ever rebuilt. */
@font-face {
  font-family: "Noto Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/static/fonts/noto-sans-var.woff2") format("woff2");
}

@font-face {
  font-family: "Noto Sans";
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url("/static/fonts/noto-sans-var-italic.woff2") format("woff2");
}

@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/static/fonts/jetbrains-mono-var.woff2") format("woff2");
}

:root {
  color-scheme: light;
  --grid: #e3e3e4;
  --grid-dark: #cececf;
  --text: #202124;
  --text-strong: #3c4043;
  --muted: #5f6368;
  --muted-light: #9aa0a6;
  --blue: #1a73e8;
  --blue-tint: #e8f0fe;
  --green: #69a882;
  --green-dark: #206836;
  --red: #ecc5c9;
  --red-dark: #9b3f48;
  --red-soft: #fff7f7;
  --success: #16a34a;
  --success-alt: #34a853;
  --success-soft: #edf8f1;
  --danger: #dc2626;
  --danger-soft: #fdf0f2;
  --warning-soft: #dfe9f9;
  /* Amber/warning cluster: the unnumbered-teams banner and fuzzy-match badge. */
  --amber-bg: #fde68a;
  --amber-border: #f59e0b;
  --amber-text-strong: #7c2d12;
  --amber-text: #b06a00;
  /* Diagnostic chip (download-log button): stays dark in every theme. */
  --diag-bg: rgba(30, 30, 30, 0.82);
  --diag-fg: #ffffff;
  --paper: #ffffff;
  --paper-transparent: rgba(255, 255, 255, 0);
  --structure: #f1f1f2;
  /* Transparent twins for gradient endpoints: a fade must end on the alpha-0
     version of ITS OWN color — `transparent` (black) or a hardcoded light value
     tints the ramp on the other themes. */
  --structure-transparent: rgba(241, 241, 242, 0);
  --accent-soft: #eef3fa;
  /* Semantic aliases onto existing theme-aware tokens (theme-aware through
     their referents, so no dark-block override needed). */
  --accent: var(--success);
  --text-muted: var(--muted);
  --orange-dark: var(--amber-text);
  --blue-strong: #174ea6;
  --blue-bright: #1d4ed8;
  --blue-soft: #d3ebff;
  --danger-text: #9f1239;

  /* THE NEUTRAL LADDER. Light keeps exactly four fill grays — paper (#fff),
     structure, the control fill, the control hover — plus the two line grays
     (--grid/--grid-dark). Every rung is NEUTRAL — the ghost wash's base
     rgb(32,33,36) over white — not blue-gray; a cool-tinted rung next to the
     neutral ghost read as two unrelated grays. Everything else aliases onto a rung: page,
     surface-tint and hover-bg all resolve to --structure. The old palette had
     nine near-identical fills (#f8fafc/#f6f7f9/#eef1f5/#eef1f4/#eaeef3/
     #e9eaec/#e3e8ef/…) whose differences carried no meaning and read as dirt.
     Dark keeps its own ladder below: there the steps ARE the elevation. */
  --page: var(--structure);
  --surface: var(--paper);
  --surface-tint: var(--structure);
  --hover-bg: var(--structure);

  /* ── Element outlines are OFF ──────────────────────────────────────────
     Buttons, menus, popovers, cards, modals and inputs separate themselves by
     FILL (+ shadow for floating layers), never by a contrast outline. The four
     outline tokens below are therefore `transparent` in the regular themes, and
     only the [data-contrast=high] blocks give them real colors — that mode
     exists precisely to carry separation on displays that cannot resolve the
     fills, so it is the one place an outline earns its keep.

     They stay `transparent` rather than being deleted for three reasons: the 1px
     box is still reserved, so nothing reflows; state rules that only set
     `border-color` (`.input:focus` → --blue, `.btn-danger`) still paint; and
     high contrast re-enables everything by re-pointing four variables.

     STRUCTURAL rules are a different thing and are NOT tokens: table cell grids,
     the topbar divider, pane splitters, dashed "add"/page-break affordances and
     dope's keypad key-seams all reference --grid / --grid-dark directly, and are
     unaffected by anything here. Do not route them back through --border. */
  --border: transparent;
  --border-soft: transparent;
  --action-border: transparent;
  --action-disabled-border: transparent;

  /* THE control fill. It carries the whole affordance now that the outlines
     are gone, so it must read against BOTH --surface (#fff) and --structure —
     the two backgrounds controls actually sit on. Buttons and fields share it
     in light (shape and typography already tell them apart; a 2% gray
     difference told nobody anything); dark keeps them split below, where
     recessed-field vs lifted-button is real elevation.
     NEUTRAL, not blue-gray: it must sit in the same hue family as the ghost
     wash below — a bluish filled button next to a neutral ghost read as two
     unrelated grays. These are the ghost's base rgb(32,33,36) over white at
     ~10% / ~15%. */
  --action-bg: #e9e9ea;
  --action-hover-bg: #dedee0;
  --action-fg: var(--text);
  --field-bg: var(--action-bg);

  /* Ghost = the low-emphasis button. It is TRANSLUCENT on purpose: a ghost sits
     on --surface, --structure and --page alike, and any opaque fill picked to
     read on one of them disappears into another. A wash of the text color works
     against all three. It must still read as a button — with the outlines gone,
     a ghost with no fill at all is just a word (which is what it had become). */
  --action-ghost-bg: rgba(32, 33, 36, 0.06);
  --action-ghost-hover-bg: rgba(32, 33, 36, 0.12);

  --action-disabled-bg: var(--structure);
  --action-disabled-fg: var(--muted-light);
  --action-disabled-opacity: 0.72;
  --cursor-action: pointer;
  --cursor-disabled: not-allowed;

  --space-0: 0;
  --space-0-5: 2px;
  --space-1: 4px;
  --space-1-5: 6px;
  --space-2: 8px;
  --space-2-5: 10px;
  --space-3: 12px;
  --space-3-5: 14px;
  --space-4: 16px;
  --space-5: 24px;
  --space-5-5: 28px;
  --space-6: 32px;
  --space-8: 48px;

  --radius-xs: 3px;
  --radius-sm: 4px;
  --radius-sm-plus: 5px;
  --radius-md: 6px;
  --radius-lg: 8px;
  --radius-pill: 24px;
  --radius-full: 50%;

  --text-2xs: 10px;
  --text-xs-tight: 11px;
  --text-xs: 12px;
  --text-xs-plus: 13px;
  --text-sm: 14px;
  --text-base: 15px;
  --text-md: 16px;
  --text-lg: 18px;
  --text-lg-xl: 21px;
  --text-xl: 22px;
  --text-xl-plus: 24px;
  --text-2xl: 30px;

  /* THE CONTROL HEIGHT SCALE. Everything that can share a row with a field —
     buttons, icon buttons, selects, the file field — resolves to one of these, so
     a row of mixed controls lines up by construction instead of by luck. It used
     to be luck, and it lost: a card header held a 38px select, a 30px button and
     a 34px icon button, three heights in one row, and nothing agreed with
     anything. Replaced elements (input[type=color], input[type=file]) can't be
     stretched by flexbox at all, so they MUST be told a height — hence a token
     rather than a guess at each call site. */
  --control-h: 38px;
  --control-h-sm: 30px;
  /* the micro tier: buttons that live INSIDE a table cell or a text line */
  --control-h-xs: 20px;

  --leading-tight: 1;
  --leading-snug: 1.25;
  --leading-relaxed: 1.55;

  --fw-regular: 400;
  --fw-semibold: 600;
  --fw-bold: 700;

  --shadow-sm: 0 1px 2px rgba(32, 33, 36, 0.12);
  --shadow-popover: 0 8px 20px rgba(32, 33, 36, 0.18);
  --shadow-popover-up: 0 -8px 20px rgba(32, 33, 36, 0.18);
  --shadow-card: 0 8px 24px rgba(32, 33, 36, 0.18);
  --shadow-modal: 0 18px 48px rgba(32, 33, 36, 0.22);
  --shadow-scroll-inset: inset -24px 0 18px -22px rgba(32, 33, 36, 0.45);
  --focus-ring-none: 0;
  --focus-outline-offset-tight: calc(var(--space-0-5) * -1);

  --backdrop: rgba(32, 33, 36, 0.35);

  --z-dropdown: 30;
  --z-popover: 35;
  --z-modal: 80;
  --z-toast: 120;
  --z-tooltip: 999;

  /* page chrome padding tiers */
  --page-pad-x: 24px;
  --page-pad-x-tight: 16px;
  --page-pad-y-tight: 8px;

  /* the ONE header height — every top bar (host, compact game/board, public)
     is this tall, and the mains/overlays that lay out against the header
     (calc(100dvh - …), sticky offsets) read the same token. */
  --header-h: 44px;

  /* scroll-fade utility */
  --scroll-fade-size: 24px;
  --scroll-fade-color: rgba(32, 33, 36, 0.18);
  --inline-fade-width: 34px;
  --inline-fade-bg: linear-gradient(to right, var(--paper-transparent), var(--paper));

  --font-sans: "Noto Sans", Arial, sans-serif;
  /* JetBrains Mono is shipped now (it was system-mono roulette before), but the
     stack keeps its fallbacks: the subset drops astral codepoints, so anything
     exotic in the 4s editor still lands on a system mono rather than tofu. */
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* ── scroll-fade utilities ────────────────────────────────
   Pure-CSS scroll shadows (Lea Verou trick): a soft fade appears
   at the edge the user CAN scroll toward, and disappears when
   they've reached that end. Background covers move with content
   (background-attachment: local) so they reveal the static
   shadows underneath as scrolling progresses.

   Two utilities on the overflow:auto element itself: .scroll-fade-y
   (vertical) and .scroll-fade-xy (all four edges). The kit's own scroll
   surfaces (.table-scroll, .sheet-frame) are in the groups below; an app
   container composes the utility class and overrides --scroll-fade-bg
   where its background isn't --surface. */
.scroll-fade-y,
.scroll-fade-xy,
.table-scroll,
.sheet-frame {
  --scroll-fade-bg: var(--surface);
}

.scroll-fade-y {
  background-image:
    linear-gradient(var(--scroll-fade-bg) 30%, transparent),
    linear-gradient(transparent, var(--scroll-fade-bg) 70%),
    radial-gradient(farthest-side at 50% 0, var(--scroll-fade-color), transparent),
    radial-gradient(farthest-side at 50% 100%, var(--scroll-fade-color), transparent);
  background-position: center top, center bottom, center top, center bottom;
  background-repeat: no-repeat;
  background-size:
    100% var(--scroll-fade-size),
    100% var(--scroll-fade-size),
    100% calc(var(--scroll-fade-size) * 0.5),
    100% calc(var(--scroll-fade-size) * 0.5);
  background-attachment: local, local, scroll, scroll;
}

.scroll-fade-xy,
.table-scroll,
.sheet-frame {
  background-image:
    linear-gradient(var(--scroll-fade-bg) 30%, transparent),
    linear-gradient(transparent, var(--scroll-fade-bg) 70%),
    linear-gradient(to right, var(--scroll-fade-bg) 30%, transparent),
    linear-gradient(to left, var(--scroll-fade-bg) 30%, transparent),
    radial-gradient(farthest-side at 50% 0, var(--scroll-fade-color), transparent),
    radial-gradient(farthest-side at 50% 100%, var(--scroll-fade-color), transparent),
    radial-gradient(farthest-side at 0 50%, var(--scroll-fade-color), transparent),
    radial-gradient(farthest-side at 100% 50%, var(--scroll-fade-color), transparent);
  background-position:
    center top, center bottom,
    left center, right center,
    center top, center bottom,
    left center, right center;
  background-repeat: no-repeat;
  background-size:
    100% var(--scroll-fade-size),
    100% var(--scroll-fade-size),
    var(--scroll-fade-size) 100%,
    var(--scroll-fade-size) 100%,
    100% calc(var(--scroll-fade-size) * 0.5),
    100% calc(var(--scroll-fade-size) * 0.5),
    calc(var(--scroll-fade-size) * 0.5) 100%,
    calc(var(--scroll-fade-size) * 0.5) 100%;
  background-attachment: local, local, local, local, scroll, scroll, scroll, scroll;
}

/* per-container background tint to keep covers matching the bg */
.sheet-frame { --scroll-fade-bg: var(--structure); }

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  min-height: 100%;
  font-family: var(--font-sans);
  color: var(--text);
}

button,
input,
select,
textarea {
  font: inherit;
}

[hidden] {
  display: none !important;
}

/* ── design system: surfaces ─────────────────────────────── */

.list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.list-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--surface);
  text-decoration: none;
  color: inherit;
}

a.list-row:hover { background: var(--hover-bg); }

.list-row-title { font-weight: var(--fw-semibold); }

.muted {
  color: var(--muted);
  font-size: var(--text-sm);
}

.table-scroll {
  overflow: auto;
}

.data-table {
  width: 100%;
  min-width: 420px;
  border-collapse: collapse;
  background: var(--surface);
  font-size: var(--text-sm);
}

.data-table th,
.data-table td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--grid);
  text-align: left;
  vertical-align: top;
}

.data-table th {
  color: var(--muted);
  font-weight: var(--fw-semibold);
}

.data-table input:not([type="checkbox"]),
.data-table select {
  width: 100%;
  min-height: 34px;
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--field-bg);
  color: var(--text);
}

/* ── design system: form controls ────────────────────────── */

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  font-size: var(--text-sm);
}

/* Filled fields: --field-bg is what makes an input an input now that the outline
   is transparent — on --surface it reads as recessed, and it stays distinct from
   --structure too (forms sit on both). The transparent 1px border is kept so the
   :focus rule below still has a box to paint blue. */
.input,
.field > input,
.field > textarea,
.field > select {
  font-size: var(--text-base);
  min-height: var(--control-h);
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--field-bg);
  color: var(--text);
  outline: 0;
}

.input:focus,
.field > input:focus,
.field > textarea:focus,
.field > select:focus {
  border-color: var(--blue);
}

.field > textarea {
  resize: vertical;
  min-height: 96px;
  font-family: inherit;
}

/* A <select> sizes itself from its own UA box and lands 2px over --control-h on
   a bare min-height, which is exactly the 1px-off look in a header where it sits
   beside buttons. Pin the height; the padding has to go to 0 or the text clips. */
select.input,
.field > select {
  height: var(--control-h);
  padding-top: 0;
  padding-bottom: 0;
}

.checkbox {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
}

/* Native checkboxes/radios draw their own 1px box, which no token of ours can
   reach — appearance:none is the only way to stop them, and once it's off we owe
   them the whole widget (fill, tick, focus ring). The ring is --grid-dark and
   NOT the (transparent) --action-border: for an empty checkbox the outline IS
   the widget — an 18px pale circle with no edge reads as nothing at all on the
   page gray. A graphic, like the sync spinner's ring. */
input[type="checkbox"],
input[type="radio"] {
  appearance: none;
  -webkit-appearance: none;
  flex: none;
  display: inline-grid;
  place-content: center;
  width: 18px;
  height: 18px;
  margin: 0;
  padding: 0;
  border: 1px solid var(--grid-dark);
  border-radius: var(--radius-sm);
  background: var(--field-bg);
  cursor: var(--cursor-action);
}

input[type="radio"] { border-radius: var(--radius-full); }

input[type="checkbox"]::before,
input[type="radio"]::before {
  content: "";
  width: 10px;
  height: 10px;
  transform: scale(0);
  background: #fff;
  /* a tick, drawn rather than typed: a glyph would depend on the font stack */
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
}

input[type="radio"]::before {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  clip-path: none;
}

input[type="checkbox"]:checked,
input[type="radio"]:checked {
  background: var(--blue);
  border-color: var(--blue);
}

input[type="checkbox"]:checked::before,
input[type="radio"]:checked::before { transform: scale(1); }

input[type="checkbox"]:disabled,
input[type="radio"]:disabled {
  background: var(--action-disabled-bg);
  opacity: var(--action-disabled-opacity);
  cursor: var(--cursor-disabled);
}

/* appearance:none also removes the UA focus ring — put a real one back, or the
   box becomes unreachable-looking for keyboard users. */
input[type="checkbox"]:focus-visible,
input[type="radio"]:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
}

/* The file field's "Browse…" is a UA-drawn button with its own border. Style the
   ::file-selector-button into an ordinary small button so the control stops
   looking like a stray piece of 1998 and matches --control-h. */
.input[type="file"] {
  height: var(--control-h);
  padding: 0;
  padding-right: var(--space-3);
  overflow: hidden;
}

.input[type="file"]::file-selector-button {
  height: 100%;
  margin-right: var(--space-3);
  padding: 0 var(--space-3);
  border: 0;
  border-radius: var(--radius-md) 0 0 var(--radius-md);
  /* one step down from the field fill (they share --action-bg now), so the
     button keeps a visible seam inside the field */
  background: var(--action-hover-bg);
  color: var(--text);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: var(--fw-semibold);
  cursor: var(--cursor-action);
}

.input[type="file"]:hover::file-selector-button { filter: brightness(0.96); }

/* The shadow carries the modal's elevation; the border paints only in high
   contrast (--border is transparent elsewhere). */
.modal-dialog {
  width: min(560px, calc(100vw - var(--space-6)));
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background-color: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-modal);
}

.modal-dialog::backdrop {
  background: var(--backdrop);
}

.modal-dialog h2 {
  margin: 0;
  font-size: var(--text-lg);
}

/* the right-aligned button row a dialog ends with */
.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
}

/* ── design system: buttons & links ───────────────────────────────────────────
   THE BUTTON KINDS. The vocabulary has always declared four (primary / ghost /
   danger / secondary, see kit/vocab.json) but the stylesheet only ever painted
   two of them, so `secondary` and `primary` silently rendered as a plain .btn.
   All four are defined here now, and they separate by FILL WEIGHT — there are no
   outlines to separate them with:

     primary    strongest — an accent fill. One per view, the thing to do next.
     .btn       default (= secondary): a tonal fill. The everyday button.
     ghost      quietest — a translucent wash. Still a button, still clickable.
     danger     a red fill. Destructive, and it should look it.

   Peer actions must share a kind: a filled button next to a ghost one reads as
   "this one is real and that one is a label", which is a lie when both do the
   same class of thing. */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* --control-h, so a button sitting in a row with a select or a field shares
     its height instead of being 2px short of it */
  min-height: var(--control-h);
  padding: 0 var(--space-4);
  border: 1px solid var(--action-border);
  border-radius: var(--radius-md);
  background: var(--action-bg);
  color: var(--text);
  /* semibold, not bold: every button on every page shouting at 700 is what made
     the UI feel heavy. Weight is for emphasis, and if all of it is emphasised
     none of it is. */
  font-weight: var(--fw-semibold);
  cursor: var(--cursor-action);
  text-decoration: none;
}

/* Hover is a fill step, not an outline. (It used to promote border-color to
   --text, which is exactly the "contrast border" this pass removes.) */
.btn:hover { background: var(--action-hover-bg); }

.btn.btn-primary {
  background: var(--blue);
  color: #fff;
}

.btn.btn-primary:hover { background: var(--blue); filter: brightness(1.08); }

/* Ghost: the quiet kind. It carries a faint wash rather than nothing at all —
   with the outlines gone, a fill-less ghost is indistinguishable from body text,
   and «Удалить карточку» / «Переместить» stopped reading as buttons entirely.
   Lives in the kit (not an app layer): it is one of the core button kinds. */
.btn.btn-ghost {
  background: var(--action-ghost-bg);
  color: var(--muted);
  font-weight: var(--fw-regular);
}

.btn.btn-ghost:hover {
  background: var(--action-ghost-hover-bg);
  color: var(--text);
}

/* Danger reads through its fill too. --red-soft was near-white and relied on the
   red outline to be legible as a danger button; --red is the actual tinted fill,
   and --red-dark is its designated on-color in every theme. */
.btn.btn-danger {
  color: var(--red-dark);
  background: var(--red);
}

.btn.btn-danger:hover { background: var(--red); filter: brightness(0.94); }

/* The micro button: composes .btn (fill, fill-step hover, disabled states) at
   in-cell scale. Peers of table content, not of fields — hence its own tier. */
.btn.btn-xs {
  min-height: var(--control-h-xs);
  padding: 0 var(--space-1-5);
  border-radius: var(--radius-sm-plus);
  font-size: var(--text-xs);
  font-weight: var(--fw-regular);
  line-height: var(--leading-tight);
  white-space: nowrap;
}

.btn:disabled,
.btn[aria-disabled="true"] {
  border-color: var(--action-disabled-border);
  background: var(--action-disabled-bg);
  color: var(--action-disabled-fg);
  cursor: var(--cursor-disabled);
  opacity: var(--action-disabled-opacity);
}

/* :hover also matches disabled buttons — re-assert the disabled fill after it. */
.btn:disabled:hover,
.btn[aria-disabled="true"]:hover {
  background: var(--action-disabled-bg);
}

.action-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* square, on the same scale as every other control — it was 34px against a
     38px select and a 30px button in the very same card header */
  width: var(--control-h);
  height: var(--control-h);
  padding: 0;
  border: 1px solid var(--action-border);
  border-radius: var(--radius-md);
  background: var(--action-bg);
  color: var(--text);
  font-size: 17px;
  font-weight: var(--fw-bold);
  cursor: pointer;
  text-decoration: none;
}

.action-icon:hover { background: var(--action-hover-bg); }

.disclosure > summary {
  width: max-content;
  list-style: none;
}

.disclosure > summary::-webkit-details-marker {
  display: none;
}

.disclosure > form {
  margin-top: var(--space-3);
}

/* ── public + host common page chrome ────────────────────── */

.host {
  overflow: hidden;
  background: var(--structure);
}

.host-top {
  position: relative;
  z-index: var(--z-dropdown);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  height: var(--header-h);
  padding: var(--space-1-5) var(--page-pad-x);
  border-bottom: 0;
  background: var(--structure);
}

/* Compact chrome — game/board pages where the content below the header is the
   point: tighter gaps and side padding (the height is the shared token). */
.host-compact .host-top {
  gap: var(--space-2-5);
  padding: var(--space-1-5) var(--page-pad-x-tight);
}

.host h1 {
  margin: 0;
  /* one title size on every top bar — sheet pages don't shout louder than
     board/game pages */
  font-size: var(--text-xl);
  line-height: var(--leading-tight);
  letter-spacing: 0;
}

.host-actions {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  gap: var(--space-2);
  position: relative; /* anchors .notif-panel under the 🔔 bell */
}

.divider {
  width: 1px;
  height: var(--space-5-5);
  background: var(--grid-dark);
}

.sync-status {
  display: inline-grid;
  place-items: center;
  width: 28px;
  height: 28px;
  color: var(--blue);
}

.sync-status::before {
  content: "";
  display: block;
  box-sizing: border-box;
}

/* The spinner ring and the offline/pending dots below are DRAWN out of their
   border — it is the graphic, not an outline — so they take --grid-dark
   directly and must never be routed through the (transparent) --action-border. */
.sync-status:not([data-state="saved"])::before {
  width: 16px;
  height: 16px;
  border: 2px solid var(--grid-dark);
  border-top-color: currentColor;
  border-radius: var(--radius-full);
  animation: sync-spin 700ms linear infinite;
}

.sync-status[data-state="error"] {
  color: var(--red-dark);
}

.sync-status[data-state="saved"]::before {
  width: 14px;
  height: 8px;
  border-left: 2.5px solid var(--green-dark);
  border-bottom: 2.5px solid var(--green-dark);
  transform: translateY(-2px) rotate(-45deg);
}

/* Offline (no connection) and pending (queued offline edits) are steady states,
   not spinners — override the generic non-saved spinner. */
.sync-status[data-state="offline"]::before,
.sync-status[data-state="pending"]::before {
  width: 12px;
  height: 12px;
  border: 2px solid var(--grid-dark);
  border-radius: var(--radius-full);
  animation: none;
  transform: none;
}

.sync-status[data-state="offline"] {
  color: var(--text-muted);
}

.sync-status[data-state="pending"] {
  color: var(--orange-dark);
}

.sync-status[data-state="pending"]::before {
  background: currentColor;
  border-color: currentColor;
}

@keyframes sync-spin {
  to {
    transform: rotate(360deg);
  }
}

.match-main {
  height: calc(100vh - var(--header-h));
  height: calc(100dvh - var(--header-h));
  min-height: 360px;
  padding: var(--space-3-5);
  overflow: hidden;
  background: var(--structure);
}

.sheet-frame {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: auto;
  border-top: 0;
  background-color: var(--structure);
}

.import-frame {
  padding: var(--space-4);
}

/* the message primitive: a status/result line that preserves line breaks. The
   min-height reserves the line so text appearing doesn't shift the layout; when
   empty it collapses instead of leaving a dead gap at the end of a dialog. */
.message {
  min-height: 24px;
  margin: 0;
  color: var(--muted);
  white-space: pre-wrap;
}

.message:empty {
  min-height: 0;
  display: none;
}

/* the section primitive: a titled column of page content.
   Inputs use .input, submit buttons use .btn — see design system above. */
.section {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* on sheet pages (login, register, profile stages) a section is a form/step
   column and keeps the narrow column measure */
.sheet-frame .section {
  max-width: 420px;
}

/* stacked sections must not glue together (two one-button sections otherwise
   render as one visual block) */
.section + .section {
  margin-top: var(--space-4);
}

/* the hint primitive: muted helper text under a field or section */
.hint {
  margin: 0;
  color: var(--muted);
  font-size: var(--text-sm);
}

/* danger variant: a warning that must not read as quiet helper text */
.hint-danger {
  color: var(--danger-text);
}

/* the empty primitive: an "nothing here yet" placeholder line */
.empty {
  margin: 0;
  color: var(--muted);
}

@media (max-width: 760px) {
  :root {
    --header-h: 48px;
  }

  .host-top {
    gap: 8px;
    padding: 7px 10px;
  }

  .host h1 {
    font-size: var(--text-lg-xl);
  }

  .match-main {
    min-height: 0;
    padding: 4px;
  }

  .import-frame {
    padding: 6px;
  }
}

/* ════════════════════════════════════════════════════════════════════════
   APPEARANCE: dark theme + high-contrast mode
   Two independent axes set by menu.js on <html>:
     data-theme    = light | dark
     data-contrast = regular | high
   Default (no overrides) is the light/regular palette in :root above. Each
   block below only re-points existing color variables, so the whole app
   re-themes through the variables it already uses. The high-contrast blocks
   are scoped per theme ([data-theme=light/dark][data-contrast=high]) so they
   layer on top of the active theme with higher specificity than the bare
   dark block — order-independent.
   Palette rationale (Material dark theme + WCAG): no pure black; depth via
   lighter surfaces ~3-4 L apart, not shadows; off-white (not #fff) text;
   lightened/desaturated accents; high-contrast separates adjacent surfaces
   with stronger, text-colored borders and darker grays.
   ════════════════════════════════════════════════════════════════════════ */

/* ── Dark theme ─────────────────────────────────────────────────────────── */
:root[data-theme="dark"] {
  /* let native controls (select dropdown arrows, scrollbars, the date picker)
     render with the dark UA palette — otherwise the <select> arrows stay black
     and vanish against dark fills */
  color-scheme: dark;
  /* neutral surfaces — base (page) darkest, paper/cards lifted, structure/
     header/hover progressively lighter to read as elevation */
  --page: #14161b;
  --paper: #1e2128;
  --paper-transparent: rgba(30, 33, 40, 0);
  --surface: var(--paper);
  --structure: #262a31;
  --structure-transparent: rgba(38, 42, 49, 0);
  --hover-bg: #2c313a;
  --accent-soft: #1d2532;
  --grid: #353b45;
  --grid-dark: #474e5a;
  /* outlines off (see the :root block) — high contrast turns them back on */
  --border: transparent;
  --border-soft: transparent;
  --action-border: transparent;
  --action-disabled-border: transparent;
  /* lifted clear of --structure (#262a31) and --paper (#1e2128): with no outline
     the fill is the only thing that makes a control a control */
  --action-bg: #333944;
  --action-hover-bg: #3e4552;
  --field-bg: #2b303a;
  --action-ghost-bg: rgba(255, 255, 255, 0.07);
  --action-ghost-hover-bg: rgba(255, 255, 255, 0.14);
  --action-disabled-bg: #20242b;

  /* text — off-white high-emphasis, dimmer for muted (avoids halation) */
  --text: #e4e6ea;
  --text-strong: #f2f4f6;
  --muted: #a6acb5;
  --muted-light: #7a818b;

  /* accents — lighter + a touch desaturated so they don't vibrate on dark */
  --blue: #5b9bf5;
  --blue-tint: #1b2942;
  --blue-soft: #213450;
  --blue-strong: #8fbaf8;
  --blue-bright: #5e8ff2;
  --warning-soft: #243349;

  /* status — re-picked for dark, not desaturated light values */
  --green: #33814b;
  /* --green-dark is a *foreground* on dark: green text on dark paper, and the
     question-number digit over the --green cell fill. #2b8446 sat too close to
     both surfaces in luminance to read — lift it to a legible light green. */
  --green-dark: #7fd29b;
  --red: #9a353c;
  --red-dark: #f0a0aa;
  --red-soft: #2a1c1e;
  --success: #2ecc6f;
  --success-alt: #3ed47e;
  --success-soft: #15291d;
  --danger: #f25555;
  --danger-soft: #2c1518;
  --danger-text: #f7a6b4;

  /* amber/warning — shifted warm so it stays visible on dark */
  --amber-bg: #463713;
  --amber-border: #c4881d;
  --amber-text-strong: #f6ddc0;
  --amber-text: #e3aa55;

  /* overlays — deepen black-based scrims; shadows are a weak cue on dark */
  --backdrop: rgba(0, 0, 0, 0.6);
  --scroll-fade-color: rgba(0, 0, 0, 0.5);
}

:root[data-theme="dark"] body {
  background: var(--page);
}

/* The detailed team number is muted (secondary) in light, but on dark a muted
   gray drops below the names/scores and disappears on poor displays — the very
   monitors this feature targets. Promote it to the primary text color. */
:root[data-theme="dark"] .match-table.od-detailed .od-detailed-team-number {
  color: var(--text);
}

/* ── High contrast, light theme ─────────────────────────────────────────────
   Motivating case: a monitor that can't separate white from light gray.
   Darken the structural grays well clear of white and strengthen borders so
   cell/row boundaries carry separation regardless of fill. Push text toward
   AAA. */
:root[data-theme="light"][data-contrast="high"] {
  --grid: #9aa1ab;
  --grid-dark: #5f6772;
  --border: var(--grid-dark);
  --border-soft: #b5bcc6;
  --structure: #d2d8e0;
  --structure-transparent: rgba(210, 216, 224, 0);
  --page: #e6e9ee;
  --hover-bg: #ccd2da;
  --accent-soft: #dbe5f4;
  --surface-tint: #e9edf2;
  /* Outlines back ON — the fills alone are what a bad display cannot resolve, so
     this mode re-points every outline token the regular themes zero out. */
  --action-bg: #e9edf2;
  --action-hover-bg: #ccd2da;
  --field-bg: #ffffff;
  --action-border: #8b929c;
  --action-disabled-border: var(--grid-dark);

  --text: #16181b;
  --text-strong: #000000;
  --muted: #3c4043;
  --muted-light: #5f6368;

  --blue-strong: #103d86;
  --green-dark: #1f6e44;
  --red-dark: #8a2e37;
  --success-text: #0f5132;
  --danger-text: #7a0c2e;

  /* answer-cell fills (right/wrong). The default pastel 'wrong' reads as gray
     on the high-contrast gray page. Saturate both, and separate them in
     LUMINANCE as well as hue (dark green vs light coral) so they stay
     distinguishable for red-green colour-blindness, not by hue alone. */
  --green: #3d8f5b;
  --red: #ec8585;
}

/* ── High contrast, dark theme ─────────────────────────────────────────────
   Brighten borders and text, and widen the surface lightness gaps so adjacent
   panels stay distinct on a poor display. */
:root[data-theme="dark"][data-contrast="high"] {
  --page: #0e1014;
  --paper: #1f232b;
  --paper-transparent: rgba(31, 35, 43, 0);
  --structure: #2f3540;
  --structure-transparent: rgba(47, 53, 64, 0);
  --header: #2b313b;
  --toolbar: #282d36;
  --hover-bg: #363d49;
  --grid: #5a626e;
  --grid-dark: #828b98;
  /* Outlines back ON — see the light high-contrast block. */
  --border: var(--grid-dark);
  --border-soft: #4a515c;
  --action-border: #5a626e;
  --action-disabled-border: var(--grid-dark);
  --field-bg: #171a20;

  --text: #f6f8fa;
  --text-strong: #ffffff;
  --muted: #c4cad2;
  --muted-light: #9aa1ab;

  --blue: #7fb0f8;
  --green-dark: #9be0b3;
  --red-dark: #f6bcc3;
}

/* ── ☰ menu (Appearance item, edit/view jump, downloads, account) ───────── */
.menu {
  position: relative;
  display: inline-flex;
}

.menu-floating {
  position: fixed;
  top: var(--space-2);
  right: var(--space-2);
  z-index: var(--z-dropdown);
}

.menu-trigger svg {
  display: block;
}

/* In a .public-top bar, pin the menu to the right edge. */
.menu-public {
  margin-left: auto;
}

.menu-floating .menu-trigger {
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-popover);
}

/* ── popover base ─────────────────────────────────────────────────────────
   Every floating surface — the ☰ dropdown, hover name popovers, suggestion
   lists, notification panels — shares one silhouette: paper + the popover
   shadow (outlines are OFF; the 1px --border box paints only in high
   contrast). App emitters compose by adding this class next to their
   positioning class; .menu-dropdown gets it here (it is emitted in too many
   places to retrofit). */
.popover,
.menu-dropdown {
  background-color: var(--paper);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-popover);
  z-index: var(--z-dropdown);
}

/* The dropdown sizes itself to its longest item (width: max-content) instead of
   forcing every label to wrap inside a fixed 184px column — menu labels are
   single actions and must each read on ONE line. min-width keeps short menus
   from collapsing to a sliver; max-width keeps a long label from running off a
   narrow screen (it ellipsises there rather than wrapping). */
.menu-dropdown {
  position: absolute;
  top: calc(100% + var(--space-1));
  right: 0;
  width: max-content;
  min-width: 184px;
  max-width: min(360px, calc(100vw - var(--space-4)));
  display: flex;
  flex-direction: column;
  padding: var(--space-1);
}

/* flex + gap gives the emoji a real column of its own, so the labels align down
   the menu instead of starting wherever the previous glyph happened to end. */
.menu-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  text-align: left;
  padding: var(--space-2) var(--space-2-5);
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: var(--text-base);
  line-height: var(--leading-snug);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-decoration: none;
  cursor: var(--cursor-action);
}

.menu-item:hover,
.menu-item:focus-visible {
  background: var(--hover-bg);
}

/* ── Appearance modal ───────────────────────────────────────────────────── */
.appearance-modal-overlay {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
  background: var(--backdrop);
  z-index: var(--z-modal);
}

.appearance-modal {
  width: min(420px, 100%);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-5);
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-modal);
}

/* modal variant="wide": for dialogs that show content rather than a few
   controls — the 360px default turns prose and images into a column of scraps. */
.modal-wide { width: min(720px, 100%); text-align: left; }

.appearance-modal-title {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: var(--fw-semibold);
}

.appearance-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.appearance-row-label {
  font-size: var(--text-sm);
  color: var(--muted);
}

.appearance-modal-done {
  align-self: flex-end;
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--action-border);
  border-radius: var(--radius-md);
  background: var(--action-bg);
  color: var(--action-fg);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: var(--fw-semibold);
  cursor: var(--cursor-action);
}

/* === ui layout utilities (internal/ui render.go) ============================
   The closed set of flex utilities the v2 UI primitives (col/row/spacer)
   compile to. Gap tokens map onto the existing --space-* scale, calibrated so
   form layouts keep their rhythm (auth-form/auth-actions used --space-2 = sm).
   Do not extend ad hoc: add a token here only when a primitive emits it. */
.u-col { display: flex; flex-direction: column; }

.u-row { display: flex; flex-direction: row; }

.u-row.u-wrap { flex-wrap: wrap; }

.u-grow { flex: 1 1 auto; min-width: 0; }

.u-spacer { flex: 1 1 auto; }

.u-gap-xs { gap: var(--space-1); }

.u-gap-sm { gap: var(--space-2); }

.u-gap-md { gap: var(--space-3); }

.u-gap-lg { gap: var(--space-4); }

.u-gap-xl { gap: var(--space-6); }

.u-align-start { align-items: flex-start; }

.u-align-center { align-items: center; }

.u-align-end { align-items: flex-end; }

.u-justify-center { justify-content: center; }

.u-justify-end { justify-content: flex-end; }

.u-justify-between { justify-content: space-between; }

/* === core widget rules promoted from the xy layer ==========================
   Base styling for classes emitted by core primitives (unreaddot, label,
   bigcode, subhead, tabs/tab/tabpanel, sliderrow) — they belong to the shared
   design system so any app using these primitives is styled. App-specific
   overrides (e.g. xy's mobile .section-label margin) stay in the app layer,
   which is concatenated after this file. */
.unread-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--blue); vertical-align: middle; }

/* iconbtn badgeid: the dot pinned to the button's corner, ringed with the
   button's own fill so it reads as a cutout */
.action-icon.has-badge { position: relative; }

.unread-dot-badge {
  position: absolute;
  top: 3px;
  right: 3px;
  border: 1.5px solid var(--action-bg);
}

/* the floating variant: pinned to the top-right of a positioned card/tile */
.unread-dot-corner {
  position: absolute;
  top: var(--space-1-5);
  right: var(--space-1-5);
}

/* Section labels read in the case they were authored in ("Метки", not "МЕТКИ").
   The uppercase + tracked-out + 700 treatment is three separate emphasis devices
   stacked on a word whose whole job is to be quiet scaffolding; muted color and
   a small size already say "label". Same reasoning for .fld-label in xy.

   PROXIMITY: a label must sit much closer to the thing it labels than to the
   block above it, or it reads as floating between the two and you cannot tell
   what it refers to. It was 20px above / 16px below — near-equidistant, so it
   bound to nothing. The margin-bottom is NEGATIVE on purpose: these labels live
   in flex columns with a `gap`, which applies equally on both sides, and pulling
   the bottom back in is the only way to beat it. Ratio is now ~32 above / ~4-12
   below. */
.section-label {
  font-size: var(--text-sm);
  font-weight: var(--fw-semibold);
  color: var(--muted);
  margin-top: var(--space-5);
  margin-bottom: calc(var(--space-1) * -1);
}

/* The dash IS the affordance here (a tear-off code box), so it takes --grid-dark
   directly rather than the transparent --border. */
.bigcode { font-family: var(--font-mono); font-size: var(--text-2xl); font-weight: var(--fw-bold); letter-spacing: 0.08em; text-align: center; padding: var(--space-3); border: 1px dashed var(--grid-dark); border-radius: var(--radius-md); }

/* Click-to-copy inline token (login: bot handle + code). The dashed underline
   marks it actionable; .copied (set by JS for ~1s) floats a confirmation. */
.copyable { font-family: var(--font-mono); font-weight: var(--fw-bold); cursor: pointer; border-bottom: 1px dashed var(--grid-dark); position: relative; }
.copyable::after { content: "скопировано"; position: absolute; left: 50%; bottom: calc(100% + var(--space-1)); transform: translateX(-50%); padding: var(--space-0-5) var(--space-1); font-family: var(--font-sans); font-size: var(--text-xs); font-weight: var(--fw-regular); white-space: nowrap; color: var(--surface); background: var(--text-strong); border-radius: var(--radius-sm); opacity: 0; pointer-events: none; z-index: var(--z-tooltip); }
.copyable.copied::after { opacity: 1; }

.subhead { margin: 0; font-size: var(--text-md); font-weight: var(--fw-semibold); color: var(--text-strong); }

/* ── segmented control ────────────────────────────────────────────────────
   THE either/or switch: the tabs primitive (the card-detail view switch), the
   appearance modal's theme/contrast rows, xy's editor toggles. A recessed
   track holding flat buttons; the active one lifts to a surface card
   (.active, toggled by the page's JS). .seg-grow splits the row equally. */
.seg {
  display: inline-flex;
  gap: var(--space-0-5);
  padding: var(--space-0-5);
  background: var(--structure);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

.seg-grow { display: flex; }

.seg-grow .seg-btn { flex: 1 1 0; }

.seg-btn {
  padding: var(--space-1-5) var(--space-3);
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: var(--text-sm);
  cursor: var(--cursor-action);
}

.seg-btn:hover { color: var(--text); }

.seg-btn.active {
  background: var(--surface);
  color: var(--text-strong);
  font-weight: var(--fw-semibold);
  box-shadow: var(--shadow-sm);
}

.tabpanel { margin-top: var(--space-2); }

.sizes-row { display: flex; flex-direction: column; gap: var(--space-1); }

.sizes-row-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-2); }

.sizes-value { font-size: var(--text-sm); color: var(--muted); font-variant-numeric: tabular-nums; }

.sizes-hint { margin: 0; font-size: var(--text-xs); color: var(--muted); }

.sizes-row input[type="range"] { width: 100%; accent-color: var(--blue-strong); cursor: var(--cursor-action); }

/* ════════════════════════════════════════════════════════════════════════
   dope app layer: tournament tables, grids, entry sheets, festival grid.
   Moved out of DopeUIKit's core.css — none of these selectors exist in any
   other app. Source order (and therefore the cascade) is unchanged: core.css
   is still concatenated ahead of this file (dope/server/css.go), and this
   block sits ahead of the rest of the dope layer, exactly where these rules
   used to sit relative to it.
   ════════════════════════════════════════════════════════════════════════ */

:root {
  /* Score-sheet geometry. Only the rules below read these. */
  --cell-gap: 1.5px;
  --cell-radius: 4px;
  --theme-radius: var(--cell-radius);
  --team-col: 180px;
  --total-col: 38px;
  --place-col: 42px;
  --place-gap: 14px;
  --question-size: 28px;
  --question-col: 40px;
  --theme-score-col: 34px;
  --theme-gap-col: 20px;
  --shootout-control-col: 44px;
  --narrow-col: 30px;
  --sheet-corner-col: 56px;
  --sheet-corner-row: 32px;
  --od-detailed-number-digits: 3ch;
  --od-detailed-number-pad-left: var(--space-0-5);
  --od-detailed-number-pad-right: var(--space-1);
  --od-detailed-name-base-col: 180px;
  --od-detailed-name-base-col-compact: 90px;
  --od-detailed-name-extra-col: 3em;
  --results-place-col-mobile: 48px;
  --results-team-col-mobile: 101px;
  --results-team-pad-mobile: var(--space-1-5);
  --results-total-col-mobile: 42px;
  --results-num-col-mobile: 40px;
  --results-team-city-size-mobile: 9px;
}

/* ── popovers ─────────────────────────────────────────────
   The surface (paper + hairline + shadow) is the kit's .popover base — every
   JS emitter adds that class. The block below holds only what a hover-revealed
   NAME popover adds: measure, type, and pointer transparency. Individual rules
   further down only set positioning + display behavior. */
.player-select-popover,
.grid-slot-team-popover,
.results-team-name-popover,
.match-table.od-detailed .od-detailed-team-name-popover,
.match-table .ek-team-cell .od-detailed-team-name-popover,
.floating-name-popover {
  max-width: min(420px, calc(100vw - 32px));
  padding: var(--space-1) var(--space-2);
  color: var(--text);
  font-size: var(--text-xs);
  font-weight: var(--fw-regular);
  line-height: var(--leading-snug);
  white-space: normal;
  pointer-events: none;
}

.list-action-row {
  display: flex;
  align-items: stretch;
  gap: var(--space-2);
}

.list-action-row .list-row {
  flex: 1 1 auto;
}

.list-action-row form {
  display: flex;
}

.list-action-row .btn {
  min-height: 100%;
}

/* a title plus three buttons can't fit a phone: the link takes its own line,
   the buttons line up beneath it */
@media (max-width: 760px) {
  .list-action-row {
    flex-wrap: wrap;
  }

  .list-action-row .list-row {
    flex-basis: 100%;
  }
}

.game-type-fieldset {
  margin: 0;
  padding: 0;
  border: 0;
}

.number-form { gap: var(--space-3); }

.number-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.number-row {
  display: grid;
  grid-template-columns: 4.5em 1fr;
  align-items: center;
  gap: var(--space-3);
}

.number-row-num,
.number-row-team {
  font-size: var(--text-base);
  min-height: 36px;
  padding: 8px 12px;
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  color: var(--text);
  outline: 0;
  font-family: inherit;
  display: flex;
  align-items: center;
}

.number-row-num {
  font-variant-numeric: tabular-nums;
  text-align: right;
  width: 100%;
  justify-content: flex-end;
}

.number-row-team {
  text-align: left;
}

/* Default view: number is passive (gray flat), team name looks like a white card. */
.number-form .number-row-num {
  background: var(--structure);
  border-color: transparent;
}

.number-form .number-row-team {
  background: var(--surface);
  border-color: var(--border);
}

/* Edit view (Замена номера): swap — number becomes the editable card, team name becomes the gray flat label. */
.number-form.editing .number-row-num {
  background: var(--surface);
  border-color: var(--border);
}

.number-form.editing .number-row-team {
  background: var(--structure);
  border-color: transparent;
}

.number-row-num:not([readonly]):focus { border-color: var(--blue); }

.numbers-help {
  margin: 0;
}

.code-display {
  margin: 0;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--surface);
  font-family: var(--font-mono);
  font-size: var(--text-xl);
  letter-spacing: 2px;
  text-align: center;
}

/* A link that reads as plain text — hover underline is the only affordance.
   For dense data surfaces (tables, timelines) where blue-everywhere is noise. */
.quiet-link {
  color: inherit;
  text-decoration: none;
}

.quiet-link:hover {
  text-decoration: underline;
}

.public-back {
  font-size: var(--text-lg);
  text-decoration: none;
  color: inherit;
}

.public-description {
  margin: 0;
  line-height: var(--leading-relaxed);
}

.public-description img {
  max-width: 100%;
  height: auto;
}

.game-header-main {
  display: flex;
  align-items: center;
  flex: 1 1 auto;
  overflow: hidden;
  min-width: 0;
  gap: var(--space-3);
}

.game-header-main h1 {
  flex: 0 0 auto;
  font-size: var(--text-xl);
}

.od-header-progress {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  color: var(--muted);
  font-size: var(--text-xs-plus);
  line-height: var(--leading-tight);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.game-breadcrumbs {
  display: inline-flex;
  flex: 0 1 auto;
  align-items: center;
  min-width: 120px;
  max-width: min(34vw, 420px);
  gap: 6px;
  overflow: hidden;
  color: var(--muted);
  font-size: var(--text-xs);
  line-height: var(--leading-tight);
  white-space: nowrap;
}

.game-breadcrumbs a,
.game-breadcrumbs span {
  overflow: hidden;
  text-overflow: ellipsis;
}

.game-breadcrumbs a {
  flex: 0 1 auto;
  color: var(--muted);
  font-weight: var(--fw-regular);
  text-decoration: none;
}

.game-breadcrumbs a:hover {
  text-decoration: underline;
}

.game-breadcrumbs .game-breadcrumbs-game,
.game-breadcrumbs .game-breadcrumbs-current {
  color: var(--text);
  font-weight: var(--fw-bold);
}

.game-breadcrumbs-sep {
  flex: 0 0 auto;
  color: var(--grid-dark);
}

.od-page .match-main,
.si-page .match-main,
.ek-page .match-main {
  padding-top: var(--space-2);
}

.sync-stack {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.viewers-count {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-xs-plus);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  color: var(--muted);
  white-space: nowrap;
  user-select: none;
}

.viewers-count[hidden] {
  display: none;
}

.fight-frame {
  border: 0;
  background-color: var(--structure);
}

.table-host {
  min-width: max-content;
  background: var(--structure);
}

.table-host.grid-host {
  width: 100%;
  min-width: 0;
}

.match-table {
  border-collapse: separate;
  border-spacing: 0;
  margin-left: var(--sheet-corner-col);
  margin-top: calc(var(--sheet-corner-row) * -1);
  table-layout: fixed;
  background: var(--structure);
  font-size: var(--text-sm);
  line-height: 1.15;
}

.fight-frame .match-table {
  margin: var(--space-0);
}

.match-table th,
.match-table td {
  --cell-fill: var(--paper);
  height: var(--question-size);
  min-width: var(--question-size);
  padding: 2px 4px;
  border: var(--cell-gap) solid transparent;
  border-radius: var(--cell-radius);
  background:
    linear-gradient(var(--cell-fill), var(--cell-fill)) padding-box,
    linear-gradient(var(--structure), var(--structure)) border-box;
  white-space: nowrap;
  vertical-align: middle;
}

.match-table thead th {
  --cell-fill: var(--structure);
  position: sticky;
  top: 0;
  z-index: 4;
  height: var(--question-size);
  border-color: var(--structure);
  font-weight: var(--fw-regular);
  text-align: center;
}

.match-table .battle {
  overflow: hidden;
  padding-left: 8px;
  text-align: left;
}

.battle-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 6px;
  width: 100%;
  min-width: 0;
}

.battle-title {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.btn-xs.venue-edit-button {
  width: 23px;
  height: 22px;
  padding: 0;
}

.venue-dialog {
  width: min(340px, calc(100vw - var(--space-6)));
}

.venue-dialog-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.venue-dialog-select {
  width: 100%;
  min-height: 34px;
  padding: 5px 8px;
  border: 1px solid var(--action-border);
  border-radius: var(--radius-sm);
  /* --field-bg, not --paper: this select sits ON a paper dialog, and the outline
     that used to give it a shape is transparent now (see core.css). */
  background: var(--field-bg);
  color: var(--text);
  font: inherit;
}

.finish-control {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-strong);
  font-size: var(--text-xs);
  line-height: var(--leading-tight);
  white-space: nowrap;
  cursor: pointer;
}

.finish-toggle {
  width: 13px;
  height: 13px;
  margin: 0;
  accent-color: var(--blue);
}

.btn-xs.shootout-add-button,
.btn-xs.theme-delete-button {
  min-width: 34px;
  height: var(--control-h-xs);
}

.sticky {
  position: sticky;
  z-index: 5;
}

.match-table thead .sticky {
  z-index: 9;
}

.match-table tbody .sticky {
  z-index: 3;
}

/* Frozen cells sit above scrolling content, so the transparency around their
   rounded-corner cards (the corner pockets and inter-cell seams) lets colored
   answer cells show through as they scroll underneath. A structure-colored
   box-shadow fills those gaps with solid gray. The sticky header row gets the
   same treatment so its bottom edge stays a clean gray line instead of going
   jagged where body cells scroll under the rounded corners. */
.match-table thead th,
.match-table tbody .sticky {
  box-shadow: 0 0 0 var(--cell-gap) var(--structure);
}

/* The sticky header's rounded bottom corners leave transparent pockets that
   reveal body rows scrolling underneath, so its bottom edge reads as a jagged,
   scalloped line. Squaring just the bottom corners turns it into one clean gray
   rule while keeping the rounded top. */
.match-table thead th {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}

.sticky-name {
  left: var(--sheet-corner-col);
  width: var(--team-col);
  min-width: var(--team-col);
  max-width: var(--team-col);
}

.fight-frame .sticky-name {
  left: 0;
}

.sticky-total {
  left: calc(var(--sheet-corner-col) + var(--team-col));
  width: var(--total-col);
  min-width: var(--total-col);
  max-width: var(--total-col);
}

.fight-frame .sticky-total {
  left: var(--team-col);
}

.sticky-place {
  left: calc(var(--sheet-corner-col) + var(--team-col) + var(--total-col));
  width: var(--place-col);
  min-width: var(--place-col);
  max-width: var(--place-col);
}

.fight-frame .sticky-place {
  left: calc(var(--team-col) + var(--total-col));
}

.sticky-place-gap {
  left: calc(var(--sheet-corner-col) + var(--team-col) + var(--total-col) + var(--place-col));
}

.fight-frame .sticky-place-gap {
  left: calc(var(--team-col) + var(--total-col) + var(--place-col));
}

.match-table .place-gap,
.match-table .place-gap-head {
  --cell-fill: var(--structure);
  width: var(--place-gap);
  min-width: var(--place-gap);
  max-width: var(--place-gap);
  padding: 0;
  border-color: var(--structure);
  border-radius: 0;
}

.match-table .team-name {
  padding-left: 8px;
  font-size: var(--text-sm);
  white-space: normal;
}

.number {
  text-align: center;
}

.question-head {
  width: var(--question-col);
  min-width: var(--question-col);
}

/* Подробно question header: count of teams that took the question, muted and
   smaller, stacked in the free space above the question number. */
.match-table.od-detailed thead th.question-head:has(.od-detailed-qhead) {
  vertical-align: bottom;
  height: calc(var(--question-size) + 11px);
}

.od-detailed-qhead {
  display: flex;
  flex-direction: column;
  align-items: center;
  line-height: 1.1;
}

.od-detailed-qcount {
  font-size: 0.62em;
  color: var(--muted);
  min-height: 1em;
}

.theme-head,
.theme-score {
  width: var(--theme-score-col);
  min-width: var(--theme-score-col);
  max-width: var(--theme-score-col);
  text-align: center;
}

.shootout-controls-head,
.shootout-controls-cell {
  width: var(--shootout-control-col);
  min-width: var(--shootout-control-col);
  max-width: var(--shootout-control-col);
  text-align: center;
}

.match-table .shootout-controls-head {
  overflow: visible;
  padding: 2px;
  vertical-align: middle;
}

.match-table .shootout-controls-cell {
  --cell-fill: var(--structure);
  border-color: var(--structure);
  border-radius: 0;
}

.match-table .shootout-adjacent-gap,
.match-table .shootout-adjacent-gap-head {
  width: 2px;
  min-width: 2px;
  max-width: 2px;
  padding: 0;
  border: 0;
}

.shootout-controls-head .shootout-add-button,
.shootout-controls-head .theme-delete-button {
  display: flex;
  margin: 0 auto;
}

.shootout-controls-head .theme-delete-button {
  position: absolute;
  top: calc(50% + 12px);
  left: 50%;
  z-index: 8;
  margin: 0;
  transform: translateX(-50%);
}

.match-table .theme-block {
  border-color: var(--structure);
  border-width: 1px;
  border-radius: 0;
}

.match-table .theme-block-top-left {
  border-top-left-radius: var(--theme-radius);
}

.match-table .theme-block-bottom-left {
  border-bottom-left-radius: var(--theme-radius);
}

.match-table .theme-block-score {
  border-top-right-radius: var(--theme-radius);
  border-bottom-right-radius: var(--theme-radius);
}

.gap,
.gap-head {
  width: var(--theme-gap-col);
  min-width: var(--theme-gap-col);
  border-right: 0;
}

.match-table .gap,
.match-table .gap-head {
  --cell-fill: var(--structure);
  border-radius: 0;
  border-color: var(--structure);
}

.match-table .player-cell {
  position: relative;
  width: calc(var(--question-col) * 5);
  min-width: calc(var(--question-col) * 5);
  padding: 0;
}

.player-editor {
  display: flex;
  align-items: stretch;
  width: 100%;
  height: 100%;
  min-width: 0;
}

.player-select-wrap {
  position: relative;
  display: block;
  flex: 1 1 auto;
  min-width: 0;
  height: 100%;
}

.player-select-wrap::before {
  content: "";
  position: absolute;
  top: 0;
  right: 22px;
  bottom: 0;
  z-index: 1;
  display: none;
  width: 32px;
  background: linear-gradient(to right, var(--paper-transparent), var(--paper));
  pointer-events: none;
}

.player-select-wrap::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 10px;
  z-index: 2;
  width: 6px;
  height: 6px;
  border-right: 1.5px solid var(--text);
  border-bottom: 1.5px solid var(--text);
  pointer-events: none;
  transform: translateY(-65%) rotate(45deg);
}

.player-select-wrap.player-select-truncated::before {
  display: block;
}

.player-select-popover {
  position: absolute;
  top: calc(100% - 2px);
  left: var(--space-1-5);
  z-index: var(--z-dropdown);
  display: none;
}

.player-cell:hover,
.player-cell:focus-within {
  z-index: var(--z-popover);
}

.player-select-truncated:hover .player-select-popover,
.player-select-truncated:focus-within .player-select-popover {
  display: block;
}

.floating-popovers-enabled .player-select-truncated:hover .player-select-popover,
.floating-popovers-enabled .player-select-truncated:focus-within .player-select-popover {
  display: none;
}

.player-cell select {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  height: calc(var(--question-size) - 2 * var(--cell-gap));
  padding-left: 8px;
  padding-right: 26px;
  border: 0;
  outline: 0;
  background: transparent;
  color: var(--text);
  font-size: var(--text-sm);
}

.player-cell select:focus {
  box-shadow: inset 0 0 0 2px var(--blue);
}

.match-table .readonly-player {
  width: calc(var(--question-col) * 5);
  min-width: calc(var(--question-col) * 5);
  padding-left: 8px;
  color: var(--text);
  font-size: var(--text-sm);
}

.answer-cell {
  width: var(--question-col);
  min-width: var(--question-col);
  cursor: pointer;
}

.match-finished .answer-cell {
  cursor: default;
}

.answer-cell:hover:not(.active),
.answer-cell:focus:not(.active) {
  outline: 2px dashed color-mix(in srgb, var(--blue) 40%, transparent);
  outline-offset: -3px;
}

.match-finished .answer-cell:hover:not(.active),
.match-finished .answer-cell:focus:not(.active) {
  outline: 0;
}

.answer-cell.active {
  outline: 2px solid var(--blue);
  outline-offset: -3px;
}

.answer-cell.active {
  box-shadow:
    inset 0 0 0 2px var(--blue),
    inset 0 0 0 3px var(--paper);
}

.collab-cursor-layer {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  pointer-events: none;
}

.collab-cursor {
  position: fixed;
  box-sizing: border-box;
  border: 2px solid var(--cursor-color);
  box-shadow: inset 0 0 0 1px var(--paper);
  pointer-events: none;
}

.collab-cursor-marker {
  position: absolute;
  top: -6px;
  left: -6px;
  width: 10px;
  height: 10px;
  border-radius: var(--radius-full);
  background: var(--cursor-color);
  box-shadow: 0 0 0 2px var(--paper);
  pointer-events: auto;
}

.collab-cursor-label {
  position: absolute;
  left: -2px;
  bottom: 14px;
  display: none;
  max-width: 160px;
  padding: 3px 7px;
  border-radius: var(--radius-sm);
  background: var(--cursor-color);
  color: var(--paper);
  font-size: var(--text-xs);
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  box-shadow: var(--shadow-popover);
}

.collab-cursor-marker:hover .collab-cursor-label,
.collab-cursor-marker:focus .collab-cursor-label {
  display: block;
}

.answer-cell.right {
  --cell-fill: var(--green);
}

.answer-cell.wrong {
  --cell-fill: var(--red);
}

/* Pending edit: a small spinner in the cell's lower-right corner while the
   edit is in flight, cleared once the server confirms it (refreshPendingMarkers).
   Covers KSI/EK answer cells and OD entry cells (OD shootout cells also carry
   .answer-cell). */
.answer-cell.pending,
.entry-cell.pending {
  position: relative;
}

.answer-cell.pending::before,
.entry-cell.pending::before {
  content: "";
  position: absolute;
  right: 2px;
  bottom: 2px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  border: 1.5px solid rgba(0, 0, 0, 0.2);
  border-top-color: var(--blue);
  animation: sync-spin 700ms linear infinite;
  pointer-events: none;
  z-index: 2;
}

.answer-cell.cell-selected {
  position: relative;
}

.answer-cell.cell-selected::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: color-mix(in srgb, var(--blue) 18%, transparent);
  box-shadow:
    var(--cell-selection-top, 0 0 0 0 transparent),
    var(--cell-selection-bottom, 0 0 0 0 transparent),
    var(--cell-selection-left, 0 0 0 0 transparent),
    var(--cell-selection-right, 0 0 0 0 transparent);
  z-index: 1;
}

.answer-cell.cell-selected.active::after {
  background: transparent;
}

.answer-cell.cell-selection-top {
  --cell-selection-top: inset 0 2px 0 var(--blue);
}

.answer-cell.cell-selection-bottom {
  --cell-selection-bottom: inset 0 -2px 0 var(--blue);
}

.answer-cell.cell-selection-left {
  --cell-selection-left: inset 2px 0 0 var(--blue);
}

.answer-cell.cell-selection-right {
  --cell-selection-right: inset -2px 0 0 var(--blue);
}

.answer-row td {
  height: var(--question-size);
}

.match-table .team-gap {
  height: 6px;
  min-width: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: var(--structure);
  line-height: 0;
}

.total-cell,
.place-cell,
.plus-cell {
  font-variant-numeric: tabular-nums;
}

.tiebreak-cell {
  width: var(--place-col);
  min-width: var(--place-col);
}

.place-input {
  width: 100%;
  height: 100%;
  border: 0;
  outline: 0;
  background: transparent;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.match-finished input:not(.finish-toggle),
.match-finished select {
  color: var(--muted);
  cursor: not-allowed;
  opacity: 1;
}

.place-cell:focus-within {
  outline: 2px solid var(--blue);
  outline-offset: -3px;
}

.viewer-readonly .answer-cell {
  cursor: default;
}

.viewer-readonly .answer-cell:hover,
.viewer-readonly .answer-cell:focus {
  outline: 0;
}

.narrow {
  width: var(--narrow-col);
  min-width: var(--narrow-col);
}

.teams-cell span {
  display: block;
  max-width: 320px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.seed-import-panel {
  align-items: flex-start;
}

.seed-import-table {
  --results-place-col: 76px;
  --results-team-col: 420px;
  width: max-content;
  min-width: 560px;
}

.results-table.seed-import-table thead th.results-team-head,
.results-table.seed-import-table .results-team {
  width: var(--results-team-col);
  min-width: var(--results-team-col);
  max-width: var(--results-team-col);
}

.seed-import-table .seed-number-head,
.seed-import-table .seed-number-cell {
  width: var(--results-place-col);
  min-width: var(--results-place-col);
  max-width: var(--results-place-col);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.seed-import-table .seed-declined-head,
.seed-import-table .seed-declined-cell {
  width: 120px;
  min-width: 120px;
  max-width: 120px;
  text-align: center;
}

.seed-import-table .seed-declined-row td {
  color: var(--muted);
  background: var(--surface-tint);
}

.seed-import-table .seed-declined-row .results-team-truncated .results-team-name::after {
  background: linear-gradient(to right, transparent, var(--surface-tint));
}

.seed-import-table .seed-waitlist-cell {
  color: var(--text);
  font-size: var(--text-xs-plus);
  font-weight: var(--fw-regular);
  letter-spacing: 0;
  text-transform: none;
  background: var(--structure);
}

.venue-input {
  width: 100%;
  min-height: 28px;
  padding: 3px 6px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm-plus);
  background: transparent;
  outline: 0;
}

.venue-input:focus {
  border-color: var(--blue);
  background: var(--paper);
}

.fest-grid {
  width: 100%;
  min-width: 1140px;
  padding-bottom: 24px;
}

.fest-columns {
  display: grid;
  grid-template-columns:
    minmax(190px, 1fr)
    minmax(190px, 1fr)
    minmax(190px, 1fr)
    minmax(190px, 1fr)
    minmax(190px, 1fr)
    minmax(190px, 1fr);
  grid-template-rows: auto 1fr;
  gap: 8px;
  align-items: start;
}

.grid-stage {
  display: contents;
}

.grid-stage-head {
  grid-row: 1;
  min-width: 0;
}

.grid-matches {
  grid-row: 2;
}

.grid-stage-head {
  display: block;
  min-height: 42px;
  margin-bottom: 4px;
  padding: 6px 8px;
  border: var(--cell-gap) solid transparent;
  border-radius: var(--cell-radius);
  background:
    linear-gradient(var(--structure), var(--structure)) padding-box,
    linear-gradient(var(--structure), var(--structure)) border-box;
}

.grid-stage-link {
  color: inherit;
  text-decoration: none;
  cursor: pointer;
}

.grid-stage h2 {
  margin: 0;
  font-size: var(--text-md);
  line-height: 1.1;
}

.grid-matches {
  display: flex;
  flex-direction: column;
  gap: 4px;
  width: 100%;
}

.grid-match {
  border: var(--cell-gap) solid var(--structure);
  border-radius: var(--radius-md);
  background: var(--structure);
  overflow: hidden;
}

body:not(.embedded-match) .grid-match {
  content-visibility: auto;
  contain-intrinsic-size: auto 160px;
}

.grid-match-title,
.grid-match-venue {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.grid-match-title {
  color: var(--text);
  font-size: var(--text-xs-plus);
  font-weight: var(--fw-regular);
  line-height: 1.15;
}

.grid-match-title-link {
  text-decoration: none;
}

.grid-match-title-link:hover,
.grid-match-title-link:focus {
  color: var(--blue);
  outline: 0;
}

.grid-match-venue {
  color: var(--muted);
  font-size: var(--text-xs-tight);
}

.grid-slot-grid {
  width: 100%;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 32px 32px;
  grid-auto-rows: 24px;
  border-radius: inherit;
  overflow: hidden;
}

.grid-slot-cell {
  height: 24px;
  min-width: 0;
  box-sizing: border-box;
  padding: 3px 6px;
  border-bottom: var(--cell-gap) solid var(--structure);
  background: var(--paper);
  font-size: var(--text-xs);
  font-weight: var(--fw-regular);
  line-height: 16px;
  display: flex;
  align-items: center;
}

.grid-slot-head {
  background: var(--structure);
  color: var(--text);
  font-size: var(--text-xs-plus);
  line-height: 15px;
}

.grid-match-head-cell {
  justify-content: flex-start;
}

.grid-match-head-layout {
  display: flex;
  align-items: baseline;
  gap: 6px;
  width: 100%;
  min-width: 0;
  line-height: 15px;
}

.grid-match-head-layout .grid-match-title {
  flex: 0 0 auto;
}

.grid-match-head-layout .grid-match-venue {
  flex: 1 1 auto;
  min-width: 0;
}

.grid-head-metric {
  display: block;
  line-height: 15px;
}

.grid-slot-grid .slot-total-head,
.grid-slot-grid .slot-place-head,
.grid-slot-grid .slot-total,
.grid-slot-grid .slot-place {
  justify-content: flex-end;
  text-align: right;
}

.grid-slot-row-last,
.grid-slot-phantom-cell {
  border-bottom: 0;
}

.grid-slot-phantom-cell {
  background: var(--structure);
}

.grid-slot-top-left { border-top-left-radius: var(--radius-md); }

.grid-slot-top-right { border-top-right-radius: var(--radius-md); }

.grid-slot-bottom-left { border-bottom-left-radius: var(--radius-md); }

.grid-slot-bottom-right { border-bottom-right-radius: var(--radius-md); }

.slot-source {
  position: relative;
  overflow: hidden;
  text-overflow: clip;
  white-space: nowrap;
}

.grid-slot-team:hover,
.grid-slot-team:focus-within {
  overflow: visible;
  z-index: 10;
}

.grid-slot-team-name {
  position: relative;
  display: block;
  flex: 1 1 auto;
  min-width: 0;
  max-width: 100%;
  overflow: hidden;
  text-overflow: clip;
  white-space: nowrap;
  outline-offset: -2px;
}

.grid-slot-team-truncated .grid-slot-team-name::after {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 34px;
  background: linear-gradient(to right, var(--paper-transparent), var(--paper));
  pointer-events: none;
}

.grid-slot-team-popover {
  position: absolute;
  top: calc(100% - 2px);
  left: var(--space-1-5);
  z-index: var(--z-dropdown);
  display: none;
}

.grid-slot-team-truncated:hover .grid-slot-team-popover,
.grid-slot-team-truncated:focus-within .grid-slot-team-popover {
  display: block;
}

.floating-popovers-enabled .grid-slot-team-truncated:hover .grid-slot-team-popover,
.floating-popovers-enabled .grid-slot-team-truncated:focus-within .grid-slot-team-popover {
  display: none;
}

.slot-total,
.slot-place {
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.stage-table-stack {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
  min-width: max-content;
}

.stage-match-frame {
  width: max-content;
  content-visibility: auto;
  contain-intrinsic-size: 1180px 220px;
}

.stage-match-placeholder {
  display: flex;
  align-items: center;
  width: min(960px, calc(100vw - 48px));
  min-width: 520px;
  min-height: 132px;
  padding: 10px 12px;
  border-radius: var(--radius-md);
  background: var(--paper);
  color: var(--muted);
  font-size: var(--text-sm);
}

.embedded-match {
  min-height: 0;
  overflow: visible;
}

.embedded-match .host-top {
  display: none;
}

.embedded-match .match-main,
.embedded-match .sheet-frame {
  height: auto;
  min-height: 0;
  padding: 0;
  overflow: visible;
}

.embedded-match .fight-frame {
  background-color: var(--structure);
}

@media (max-width: 760px) {
  .match-table {
    --team-col: 90px;
    --total-col: 28px;
    --place-col: 32px;
    --place-gap: 6px;
    font-size: var(--text-xs-plus);
    line-height: 1.1;
  }

  .match-table.ek-stage-table {
    --total-col: 46px;
    --theme-score-col: 46px;
  }

  .match-table th,
  .match-table td {
    height: 24px;
    min-width: 24px;
    padding: 2px 3px;
  }

  .match-table thead th {
    height: 24px;
  }

  .match-table .sticky-name {
    width: 90px;
    min-width: 90px;
    max-width: 90px;
  }

  .match-table .battle {
    padding-left: 6px;
  }

  .battle-layout {
    gap: 4px;
  }

  .btn-xs.venue-edit-button {
    width: 21px;
    height: 20px;
    font-size: var(--text-xs-tight);
  }

  .finish-control {
    gap: 0;
  }

  .btn-xs.shootout-add-button,
  .btn-xs.theme-delete-button {
    min-width: 30px;
    height: 18px;
    padding: 0 var(--space-1);
    font-size: var(--text-xs-tight);
  }

  .fight-frame .sticky-name {
    left: 0;
  }

  .match-table .sticky-total {
    left: var(--team-col);
    width: var(--total-col);
    min-width: var(--total-col);
    max-width: var(--total-col);
  }

  .fight-frame .sticky-total {
    left: var(--team-col);
  }

  .match-table .sticky-place {
    left: calc(var(--team-col) + var(--total-col));
    width: var(--place-col);
    min-width: var(--place-col);
    max-width: var(--place-col);
  }

  .fight-frame .sticky-place {
    left: calc(var(--team-col) + var(--total-col));
  }

  .match-table .sticky-place-gap {
    left: calc(var(--team-col) + var(--total-col) + var(--place-col));
  }

  .match-table .team-name {
    font-size: var(--text-xs-tight);
    line-height: 1.15;
    overflow-wrap: anywhere;
    white-space: normal;
  }

  .match-table .question-head,
  .match-table .answer-cell {
    width: 22px;
    min-width: 22px;
  }

  .match-table .theme-head,
  .match-table .theme-score {
    width: 30px;
    min-width: 30px;
  }

  .match-table.ek-stage-table .theme-head,
  .match-table.ek-stage-table .theme-score {
    width: var(--theme-score-col);
    min-width: var(--theme-score-col);
    max-width: var(--theme-score-col);
  }

  .match-table .shootout-controls-head,
  .match-table .shootout-controls-cell {
    width: 36px;
    min-width: 36px;
    max-width: 36px;
  }

  .match-table .gap,
  .match-table .gap-head {
    width: 10px;
    min-width: 10px;
    max-width: 10px;
    padding: 0;
    border: 0;
  }

  .match-table .player-cell,
  .match-table .readonly-player {
    width: 110px;
    min-width: 110px;
    font-size: var(--text-xs-tight);
  }

  .player-select-wrap::after {
    right: 6px;
  }

  .player-select-wrap::before {
    right: 16px;
    width: 26px;
  }

  .player-cell select {
    height: 22px;
    padding-left: 5px;
    padding-right: 18px;
    font-size: var(--text-xs);
  }

  .answer-row td {
    height: 21px;
  }

  .match-table .tiebreak-cell {
    width: 24px;
    min-width: 24px;
  }

  .place-input {
    width: 100%;
    font-size: var(--text-xs);
  }

  .match-table .narrow {
    width: 20px;
    min-width: 20px;
  }

  .teams-cell span {
    max-width: 220px;
  }

  .fest-grid {
    min-width: 1020px;
  }

  .fest-columns {
    grid-template-columns:
      minmax(170px, 1fr)
      minmax(170px, 1fr)
      minmax(170px, 1fr)
      minmax(170px, 1fr)
      minmax(170px, 1fr)
      minmax(170px, 1fr);
  }

  .grid-matches {
    gap: 5px;
  }

  .grid-stage h2 {
    font-size: var(--text-base);
  }

  .grid-slot-cell {
    height: 24px;
    padding: 3px 4px;
    font-size: var(--text-xs-tight);
  }

  .grid-slot-grid {
    grid-template-columns: minmax(0, 1fr) 30px 30px;
  }

  .answer-cell:hover:not(.active),
  .answer-cell:focus:not(.active),
  .answer-cell.active {
    outline-width: 1px;
  }
}

/* Match tabs + OD entry grid + results */
.match-main:has(.match-tabs:not([hidden])) {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.match-main:has(.match-tabs:not([hidden])) .sheet-frame {
  flex: 1 1 auto;
  min-height: 0;
}

.match-tabs {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0;
  background: transparent;
}

.match-tab {
  padding: 6px 14px;
  border: 1px solid var(--action-border);
  border-radius: var(--radius-md);
  background: var(--action-bg);
  color: var(--text);
  font-size: var(--text-xs-plus);
  font-weight: var(--fw-bold);
  cursor: pointer;
}

.match-tab:hover {
  background: var(--accent-soft);
}

.match-tab.active {
  background: var(--paper);
  border-color: var(--blue);
  color: var(--blue);
}

.game-host-top .match-tabs {
  display: flex;
  align-items: stretch;
  gap: 0;
  overflow: visible;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
}

.game-host-top .match-tab {
  position: relative;
  height: 24px;
  padding: 0 8px;
  margin-left: -1px;
  border: 1px solid var(--structure);
  border-radius: 0;
  background: var(--paper);
  color: var(--muted);
  font-size: var(--text-xs-plus);
  line-height: 22px;
}

.game-host-top a.match-tab {
  display: flex;
  align-items: center;
  text-decoration: none;
}

.game-host-top .match-tab:first-child {
  margin-left: 0;
  border-top-left-radius: var(--cell-radius);
  border-bottom-left-radius: var(--cell-radius);
}

.game-host-top .match-tab:last-child {
  border-top-right-radius: var(--cell-radius);
  border-bottom-right-radius: var(--cell-radius);
}

.game-host-top .match-tab + .match-tab {
  border-left-color: var(--structure);
}

.game-host-top .match-tab:hover {
  background: var(--accent-soft);
}

.game-host-top .match-tab.active {
  z-index: 1;
  border-color: var(--text);
  background: var(--paper);
  color: var(--text);
  box-shadow: none;
}

@media (max-width: 760px) {
  .ek-page .ek-tabs {
    flex: 1 1 auto;
    min-width: 0;
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }

  .ek-page .ek-tabs::-webkit-scrollbar {
    display: none;
  }

  .ek-page .ek-tabs .match-tab {
    flex: 0 0 auto;
  }

  .ek-page .ek-tabs.tabs-scroll-left {
    -webkit-mask-image: linear-gradient(to right, transparent, #000 28px);
    mask-image: linear-gradient(to right, transparent, #000 28px);
  }

  .ek-page .ek-tabs.tabs-scroll-right {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
  }

  .ek-page .ek-tabs.tabs-scroll-left.tabs-scroll-right {
    -webkit-mask-image: linear-gradient(to right, transparent, #000 28px, #000 calc(100% - 28px), transparent);
    mask-image: linear-gradient(to right, transparent, #000 28px, #000 calc(100% - 28px), transparent);
  }
}

.od-pane {
  display: contents;
}

.od-pane[hidden] {
  display: none;
}

.od-input-gate {
  margin: 24px;
  font-size: var(--text-base);
  color: var(--text);
}

.od-input-gate a {
  color: var(--blue);
  text-decoration: underline;
}

.team-number-badge {
  display: inline-block;
  min-width: 1.5em;
  margin-right: 6px;
  padding: 0 4px;
  border-radius: var(--radius-xs);
  background: var(--structure);
  color: var(--text);
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-semibold);
  text-align: center;
}

.entry-table {
  border-collapse: separate;
  border-spacing: 0;
  table-layout: fixed;
  background: var(--structure);
  contain: paint;
  font-size: var(--text-xs-plus);
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}

.col-entry-q { width: 38px; }

.col-entry-tour-end { width: calc(38px + 14px - var(--cell-gap)); }

.col-entry-shootout-team { width: 230px; }

.col-entry-shootout-check { width: 38px; }

.col-entry-shootout-check.col-entry-tour-end { width: calc(38px + 14px - var(--cell-gap)); }

.entry-table th,
.entry-table td {
  height: 26px;
  padding: 0;
  border-top: 0;
  border-bottom: 0;
  border-left: var(--cell-gap) solid transparent;
  border-right: var(--cell-gap) solid transparent;
  background-clip: padding-box;
  background-color: var(--paper);
  text-align: center;
  vertical-align: middle;
}

.entry-table thead th {
  position: sticky;
  z-index: 4;
  background-color: var(--structure);
  font-weight: var(--fw-regular);
  color: var(--text-strong);
}

.entry-table thead tr:first-child th { top: 0; height: 28px; }

.entry-table thead tr:nth-child(2) th { top: 28px; height: 22px; }

.entry-q-head { font-size: var(--text-xs); }

/* Floating "Инвертировать" button: a small yin-yang parked in the gap above the
   active column's number. Fixed-positioned (placed by positionInvertOverlay) so
   the entry table's `contain: paint` doesn't clip it. */
.entry-invert {
  position: fixed;
  width: 16px;
  height: 16px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text);
  cursor: var(--cursor-action);
  line-height: 0;
  z-index: var(--z-dropdown);
}

.entry-invert[hidden] { display: none; }

.entry-invert:hover { color: var(--blue); }

.entry-invert svg { display: block; width: 16px; height: 16px; }

.entry-invert .yy-light { fill: var(--surface); }

.entry-invert .yy-dark { fill: currentColor; }

.entry-invert .yy-ring { stroke: currentColor; stroke-width: 4; }

.entry-invert.spinning { animation: yinyang-spin 0.45s ease; }

/* Floating ↑/↓ cell-navigation bar (installCellNavBar). Pinned just above the
   on-screen keyboard via visualViewport (top set in JS) because mobile numeric
   keypads have no Return key on iOS — this is the only way to step between
   cells without dismissing the keypad. Touch devices only. */
/* NB: this background is not an outline — it shows THROUGH the 1px gaps between
   the keys and is what draws the key seams. It takes --grid-dark directly;
   --border is transparent by design (see core.css) and would erase the grid. */
.entry-nav-bar {
  position: fixed;
  left: 0;
  right: 0;
  display: flex;
  gap: 1px;
  padding: 0;
  background: var(--grid-dark);
  box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.12);
  z-index: var(--z-toast);
}

.entry-nav-bar[hidden] { display: none; }

.entry-nav-bar button {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 48px;
  padding: 0;
  font-size: var(--text-lg);
  line-height: 1;
  border: 0;
  background: var(--surface-tint);
  color: var(--text);
  cursor: var(--cursor-action);
}

.entry-nav-bar button:active { background: var(--blue-tint); }

/* Virtual numeric keypad (installVirtualKeypad). Replaces the OS keyboard for
   digit-only cell entry on touch devices: the host input sets inputmode="none"
   to suppress the native keypad, and these keys drive it via callbacks. Pinned
   to the bottom of the visual viewport (top set in JS). Touch devices only. A
   nav row (← ↑ ↓ →) sits above a 3-column digit pad; 1px gaps over the
   --grid-dark backdrop draw the key seams, so every key stays equal-width via
   the grid. (Not --border: that token is transparent — see core.css.) */
.entry-keypad {
  position: fixed;
  left: 0;
  right: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding-top: 1px;
  background: var(--grid-dark);
  box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.18);
  z-index: var(--z-toast);
  user-select: none;
  -webkit-user-select: none;
}

.entry-keypad[hidden] { display: none; }

.entry-keypad-nav,
.entry-keypad-digits {
  display: grid;
  gap: 1px;
}

.entry-keypad-nav { grid-template-columns: repeat(4, 1fr); }

.entry-keypad-digits { grid-template-columns: repeat(3, 1fr); }

.entry-keypad-zero { grid-column: span 2; }

.entry-keypad-key {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  height: 52px;
  padding: 0;
  border: 0;
  background: var(--paper);
  color: var(--text);
  font-size: var(--text-xl);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  cursor: var(--cursor-action);
  touch-action: manipulation;
}

.entry-keypad-arrow {
  height: 44px;
  background: var(--surface-tint);
  font-size: var(--text-lg);
}

.entry-keypad-back { font-size: var(--text-lg); }

.entry-keypad-key:active { background: var(--blue-tint); }

@keyframes yinyang-spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

.host:not(.viewer-readonly) .fight-frame .entry-table tbody tr.active-entry-row > .active-row-marker {
  background:
    radial-gradient(circle at 50% 50%, var(--blue) 0 3px, transparent 3.5px),
    var(--structure);
}

.entry-table .entry-tour-end {
  border-right-width: 14px;
}

.entry-lock-cell { padding: 0; }

.entry-lock-label {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  cursor: pointer;
}

.entry-lock-checkbox {
  width: 14px;
  height: 14px;
  margin: 0;
  accent-color: var(--blue);
  cursor: pointer;
}

.entry-cell {
  padding: 0;
  color: var(--text);
  cursor: cell;
  user-select: none;
}

.entry-cell.entry-selected {
  position: relative;
  background-color: var(--blue-tint);
}

/* Draw the selection frame on a pseudo-element rather than as a box-shadow on
   the cell itself. End-of-tour cells carry a wide (14px) transparent right
   border for the inter-tour gap; an inset box-shadow on the cell is measured
   from the border box and would bleed the frame across that whole gap into the
   free space. The pseudo is clamped to the normal --cell-gap on every side, so
   the frame hugs the visible cell and never spills into the gap. Mirrors the
   .answer-cell selection. */
.entry-cell.entry-selected::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(-1 * var(--cell-gap));
  right: calc(-1 * var(--cell-gap));
  pointer-events: none;
  box-shadow:
    var(--entry-selection-top, 0 0 0 0 transparent),
    var(--entry-selection-bottom, 0 0 0 0 transparent),
    var(--entry-selection-left, 0 0 0 0 transparent),
    var(--entry-selection-right, 0 0 0 0 transparent);
}

.entry-cell.entry-selection-top {
  --entry-selection-top: inset 0 2px 0 var(--blue);
}

.entry-cell.entry-selection-bottom {
  --entry-selection-bottom: inset 0 -2px 0 var(--blue);
}

.entry-cell.entry-selection-left {
  --entry-selection-left: inset 2px 0 0 var(--blue);
}

.entry-cell.entry-selection-right {
  --entry-selection-right: inset -2px 0 0 var(--blue);
}

.entry-cell.entry-selected:focus {
  outline: 0;
  background-color: var(--blue-tint);
}

.entry-cell:focus {
  outline: 2px solid var(--blue);
  outline-offset: -2px;
  background-color: var(--paper);
}

.entry-cell.entry-editing:focus {
  outline: 0;
}

.entry-input {
  display: block;
  width: 100%;
  height: 26px;
  padding: 0;
  border: 0;
  outline: 0;
  background: transparent;
  text-align: center;
  font: inherit;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

.entry-input:focus {
  outline: 2px solid var(--blue);
  outline-offset: -2px;
  background: var(--paper);
}

.shootout-entry-check-label {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 26px;
  cursor: pointer;
}

.shootout-entry-checkbox {
  width: 14px;
  height: 14px;
  margin: 0;
  accent-color: var(--blue);
  cursor: pointer;
}

.shootout-entry-checkbox:focus {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
}

.entry-suggest {
  position: fixed;
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--space-0-5);
  max-height: 260px;
  overflow: auto;
  padding: var(--space-1);
}

.entry-suggest-option {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 28px;
  padding: 3px 6px;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.entry-suggest-option.active,
.entry-suggest-option:hover {
  background: var(--accent-soft);
}

.entry-suggest-option .team-number-badge {
  flex: 0 0 auto;
  margin-right: 0;
}

.entry-table .entry-input-bad,
.entry-table .entry-input-dup {
  background-color: var(--danger-soft);
  color: var(--red-dark);
}

.entry-readonly .entry-cell {
  color: var(--muted);
  cursor: not-allowed;
}

.entry-readonly .entry-lock-label,
.entry-readonly .entry-lock-checkbox,
.entry-readonly .shootout-entry-check-label,
.entry-readonly .shootout-entry-checkbox {
  cursor: default;
}

.match-table .answer-cell.readonly { cursor: default; }

.match-table .answer-cell.readonly:hover,
.match-table .answer-cell.readonly:focus { outline: 0; }

.od-detailed-wrap {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  min-width: max-content;
}

.od-input-wrap {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  min-width: max-content;
}

.od-input-tables {
  display: flex;
  align-items: flex-start;
  gap: 0;
  min-width: max-content;
}

.od-shootout-entry-wrap {
  position: sticky;
  top: 0;
  display: flex;
  align-items: flex-start;
  gap: 0;
  min-width: max-content;
  align-self: flex-start;
}

.entry-table .od-shootout-meta-round-cell,
.entry-table .od-shootout-meta-cell,
.entry-table .od-shootout-meta-lock-cell {
  width: 230px;
  min-width: 230px;
  max-width: 230px;
}

.entry-table .od-shootout-meta-cell {
  padding: 0 8px;
  text-align: left;
  vertical-align: middle;
}

.od-shootout-meta-inner {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-height: 46px;
  overflow: hidden;
  white-space: normal;
}

.od-shootout-entry-title {
  font-size: var(--text-xs-plus);
  font-weight: var(--fw-bold);
}

.od-shootout-entry-table thead tr:first-child th { top: 0; height: 28px; }

.od-shootout-entry-table thead tr:nth-child(2) th { top: 28px; height: 28px; }

.od-shootout-entry-table thead tr:nth-child(3) th { top: 56px; height: 22px; }

.entry-table .od-shootout-controls-head {
  top: 0;
}

.od-shootout-entry-table .od-shootout-round-head {
  color: var(--muted);
  font-size: var(--text-xs);
  font-weight: var(--fw-bold);
}

.entry-table .od-shootout-team-cell {
  background-color: var(--structure);
  padding: 0 8px;
  text-align: left;
  white-space: nowrap;
}

.entry-table .od-shootout-excluded {
  background-color: var(--structure);
}

.od-shootout-check-cell {
  cursor: pointer;
}

.col-entry-shootout-controls {
  width: 280px;
}

.entry-table .od-shootout-controls-head,
.entry-table .od-shootout-controls-cell,
.match-table .od-shootout-controls-head,
.match-table .od-shootout-controls-cell {
  width: 280px;
  min-width: 280px;
  max-width: 280px;
}

.entry-table .od-shootout-controls-head,
.match-table .od-shootout-controls-head {
  --cell-fill: var(--structure);
  padding: 2px;
  vertical-align: top;
}

.entry-table .od-shootout-controls-head {
  overflow: visible;
}

.entry-table .od-shootout-controls-head .od-shootout-controls-panel {
  position: absolute;
  top: 2px;
  right: 2px;
  left: 2px;
  z-index: 6;
}

.entry-table .od-shootout-controls-cell,
.match-table .od-shootout-controls-cell {
  --cell-fill: var(--structure);
  background-color: var(--structure);
  border-color: var(--structure);
  border-radius: 0;
}

.od-shootout-controls-panel {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 4px;
  width: 100%;
}

.od-shootout-controls-panel .btn {
  min-height: 28px;
  padding: 0 10px;
  border-radius: var(--radius-sm-plus);
  font-size: var(--text-xs);
  line-height: var(--leading-tight);
}

.od-shootout-controls-panel .btn:disabled {
  color: var(--muted);
  cursor: not-allowed;
}

.od-shootout-round-controls {
  display: grid;
  grid-template-columns: 32px minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: 4px;
  padding: 2px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper);
  box-shadow: var(--shadow-sm);
}

.od-shootout-round-label {
  min-width: 0;
  padding: 0 4px;
  color: var(--muted);
  font-size: var(--text-xs);
  font-weight: var(--fw-bold);
  text-align: center;
}

.od-shootout-round-controls .btn {
  min-width: 0;
}

.match-table.od-detailed .shootout-head {
  --cell-fill: var(--accent-soft);
}

.match-table.od-detailed .od-shootout-cell {
  color: transparent;
}

.match-table.od-detailed .od-shootout-excluded {
  --cell-fill: var(--structure);
  cursor: default;
}

.od-shootout-dialog {
  width: min(520px, calc(100vw - var(--space-6)));
  max-height: calc(100vh - var(--space-8));
}

.od-shootout-dialog-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.od-shootout-team-list {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 4px;
  max-height: min(420px, calc(100vh - 190px));
  overflow: auto;
}

.od-shootout-team-option {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 34px;
  padding: 5px 8px;
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.od-shootout-team-option:hover {
  background: var(--hover-bg);
}

.od-shootout-team-option input {
  width: 15px;
  height: 15px;
  margin: 0;
  accent-color: var(--blue);
}

.od-shootout-team-option .team-number-badge {
  margin-right: 0;
}

/* Итог */
.results-wrapper {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.results-table {
  --results-place-col: 60px;
  --results-team-col: 232px;
  --results-team-pad: 10px;
  --results-total-col: 54px;
  border-collapse: separate;
  border-spacing: 0;
  background: transparent;
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  font-variant-numeric: tabular-nums;
}

.od-results-table,
.ksi-results-table {
  width: max-content;
}

.venues-results-wrapper {
  align-items: flex-start;
}

.venues-results-table {
  --results-place-col: 64px;
  --results-team-col: 420px;
  width: max-content;
  min-width: 420px;
}

.reseed-results-wrapper {
  align-items: flex-start;
}

.reseed-results-table {
  --results-place-col: 72px;
  --results-team-col: 320px;
  width: max-content;
  min-width: 720px;
}

.ek-stats-wrapper {
  align-items: flex-start;
}

.ek-stats-table {
  /* Cap on the two name columns. They size to their content (so short names sit
     tight and the whole table stays compact), and only names past this cap
     truncate with the fade + popover. Generous on desktop where there's room. */
  --ek-stats-name-col: 320px;
  width: max-content;
}

.ek-stats-table thead th.ek-stats-name-head,
.ek-stats-table .ek-stats-name {
  text-align: left;
  padding-left: var(--results-team-pad);
}

/* The name cells borrow the .results-team(-head) classes only for the truncate
   + fade + popover primitive — NOT the OD/KSI frozen-column layout. Cancel the
   inherited place-column offset (those rules set position:sticky; left:place-col,
   which on this table just shoves the first column 60px in and overlaps the two
   name columns). Higher specificity than the .results-table sticky rules, so
   this holds regardless of source order. Mobile re-pins just the player column. */
.ek-stats-table td.ek-stats-name {
  position: static;
}

.ek-stats-table thead th.ek-stats-name-head,
.ek-stats-table td.ek-stats-name {
  left: auto;
}

/* Size each name column to its content, capped at --ek-stats-name-col. The cell
   follows its inner wrap, so the column is exactly as wide as the longest name
   (up to the cap) — no dead space, no premature clipping. */
.ek-stats-table .ek-stats-name .results-team-name-wrap {
  max-width: var(--ek-stats-name-col);
}

.ek-stats-table .number {
  text-align: center;
}

/* Many numeric columns — keep them compact so more fits before scrolling. */
.ek-stats-table thead th.number,
.ek-stats-table td.number {
  padding-left: var(--space-1);
  padding-right: var(--space-1);
}

.ek-stats-table .narrow {
  width: 30px;
  min-width: 30px;
}

.ek-stats-table thead th.ek-stats-sum-head,
.ek-stats-table .ek-stats-sum {
  font-weight: var(--fw-semibold);
}

.ek-stats-table thead th.ek-stats-wrong-head,
.ek-stats-table .ek-stats-wrong,
.ek-stats-table .ek-stats-share,
.ek-stats-table thead th.ek-stats-share-head {
  color: var(--muted);
}

.results-table th,
.results-table td {
  padding: 8px 10px;
  background: var(--paper);
  vertical-align: middle;
  white-space: nowrap;
}

.results-table thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--structure);
  padding: 6px 10px;
  color: var(--text);
  font-size: var(--text-xs-plus);
  font-weight: var(--fw-regular);
  text-align: center;
}

.results-place-head,
.results-num-head,
.results-tour-head {
  width: 60px;
  min-width: 60px;
  max-width: 60px;
}

.results-place-head,
.results-place {
  width: var(--results-place-col);
  min-width: var(--results-place-col);
  max-width: var(--results-place-col);
}

.results-table thead th.results-team-head {
  text-align: left;
  padding-left: var(--results-team-pad);
}

.od-results-table thead th.results-team-head,
.od-results-table .results-team,
.ksi-results-table thead th.results-team-head,
.ksi-results-table .results-team,
.venues-results-table thead th.results-team-head,
.venues-results-table .results-team,
.reseed-results-table thead th.results-team-head,
.reseed-results-table .results-team {
  width: var(--results-team-col);
  min-width: var(--results-team-col);
  max-width: var(--results-team-col);
}

.results-table thead th.results-total-head,
.results-table .results-total {
  width: var(--results-total-col);
  min-width: var(--results-total-col);
  max-width: var(--results-total-col);
  font-weight: var(--fw-bold);
}

.od-results-table thead th.results-team-head,
.ksi-results-table thead th.results-team-head {
  --results-sticky-fade-from: var(--structure);
  --results-sticky-fade-to: var(--structure-transparent);
}

.od-results-table thead th.results-total-head,
.ksi-results-table thead th.results-total-head {
  --results-sticky-fade-from: var(--structure);
  --results-sticky-fade-to: var(--structure-transparent);
}

.od-results-table .results-total,
.ksi-results-table .results-total {
  --results-sticky-fade-from: var(--paper);
  --results-sticky-fade-to: var(--paper-transparent);
}

.results-scroll-left .od-results-table thead th.results-total-head::after,
.results-scroll-left .od-results-table .results-total::after,
.results-scroll-left .ksi-results-table thead th.results-total-head::after,
.results-scroll-left .ksi-results-table .results-total::after {
  content: "";
  position: absolute;
  top: 0;
  right: -28px;
  bottom: 0;
  width: 28px;
  pointer-events: none;
  background: linear-gradient(to right, var(--results-sticky-fade-from), var(--results-sticky-fade-to));
}

.detailed-scroll-left .match-table.od-detailed .sticky-total::after {
  content: "";
  position: absolute;
  top: 0;
  right: -28px;
  bottom: 0;
  width: 28px;
  pointer-events: none;
  background: linear-gradient(to right, var(--structure), var(--structure-transparent));
}

/* EK stage tables get the same scrolled-under fade as OD/KSI, drawn off the
   right edge of the place-gap (the rightmost frozen column) once the score
   columns have been scrolled. .stage-scroll-left is toggled on the scroll
   frame by host.js / viewer.js. */
.sheet-frame.stage-scroll-left .match-table.ek-stage-table .sticky-place-gap::after {
  content: "";
  position: absolute;
  top: 0;
  right: -28px;
  bottom: 0;
  width: 28px;
  pointer-events: none;
  background: linear-gradient(to right, var(--structure), var(--structure-transparent));
}

.results-table .results-place,
.results-table .results-team,
.results-table .results-total {
  position: sticky;
  z-index: 1;
  background: var(--paper);
}

.results-table .results-place,
.results-table thead .results-place-head {
  left: 0;
}

.results-table .results-team,
.results-table thead .results-team-head {
  left: var(--results-place-col);
}

.results-table .results-total,
.results-table thead .results-total-head {
  left: calc(var(--results-place-col) + var(--results-team-col));
}

.results-table thead .results-place-head,
.results-table thead .results-team-head,
.results-table thead .results-total-head {
  z-index: 3;
}

.results-place {
  text-align: center;
  font-weight: var(--fw-regular);
}

.results-table .results-team {
  text-align: left;
  padding-left: var(--results-team-pad);
  overflow: visible;
}

.results-team:hover,
.results-team:focus-within {
  z-index: var(--z-popover);
}

.results-team-name-wrap {
  position: relative;
  display: block;
  min-width: 0;
  max-width: 100%;
}

.results-team-name,
.results-team-city {
  position: relative;
  display: block;
  min-width: 0;
  max-width: 100%;
  overflow: hidden;
  text-overflow: clip;
  white-space: nowrap;
}

.results-team-name {
  outline-offset: var(--focus-outline-offset-tight);
}

.results-team-truncated .results-team-name::after,
.results-team-city-truncated::after {
  content: "";
  position: absolute;
  top: var(--space-0);
  right: var(--space-0);
  bottom: var(--space-0);
  width: var(--inline-fade-width);
  background: var(--inline-fade-bg);
  pointer-events: none;
}

.results-team-name:focus {
  outline: var(--focus-ring-none);
}

.results-team-city {
  color: var(--muted);
  font-size: var(--text-xs);
}

.results-team-name-popover {
  position: absolute;
  top: calc(100% - 2px);
  left: var(--space-3);
  z-index: var(--z-dropdown);
  display: none;
}

.results-team-truncated:hover .results-team-name-popover,
.results-team-truncated:focus-within .results-team-name-popover {
  display: block;
}

.floating-popovers-enabled .results-team-truncated:hover .results-team-name-popover,
.floating-popovers-enabled .results-team-truncated:focus-within .results-team-name-popover,
.ek-page .results-team-truncated:hover .results-team-name-popover,
.ek-page .results-team-truncated:focus-within .results-team-name-popover {
  display: none;
}

.results-num,
.results-tour { text-align: center; }

.results-tour-toggle {
  display: inline-grid;
  place-items: center;
  width: 100%;
  min-height: 24px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: inherit;
  cursor: pointer;
}

.results-tour-toggle:hover,
.results-tour-toggle:focus-visible {
  background: color-mix(in srgb, var(--blue) 8%, transparent);
  outline: 0;
}

.results-tour-toggle-head.expanded .results-tour-toggle {
  color: var(--blue);
  font-weight: var(--fw-bold);
}

.results-tour-pending {
  color: var(--muted-light);
}

.results-table thead th.results-answer-head {
  width: 24px;
  min-width: 24px;
  max-width: 24px;
  padding: 0;
  color: var(--muted);
  font-size: var(--text-xs-plus);
  line-height: var(--leading-tight);
  text-align: center;
}

.results-table td.results-answer {
  width: 24px;
  min-width: 24px;
  max-width: 24px;
  padding: 0;
  font-size: 0;
  line-height: 0;
  text-align: center;
}

.results-answer-mark {
  --cell-fill: var(--paper);
  display: inline-grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border: 1px solid var(--structure);
  border-radius: 0;
  background: var(--cell-fill);
  color: var(--green-dark);
  font-size: var(--text-2xs);
  line-height: var(--leading-tight);
  vertical-align: middle;
}

.results-answer-left .results-answer-mark {
  border-top-left-radius: var(--cell-radius);
  border-bottom-left-radius: var(--cell-radius);
}

.results-answer-right .results-answer-mark {
  border-top-right-radius: var(--cell-radius);
  border-bottom-right-radius: var(--cell-radius);
}

.results-answer.right .results-answer-mark {
  --cell-fill: var(--green);
}

.results-group-gap td {
  height: 8px;
  padding: 0;
  background: var(--structure);
  border: 0;
  line-height: 0;
}

.results-row:not(.results-group-first) td {
  border-top: 1px solid var(--structure);
}

.ksi-results-table tbody tr + tr td {
  border-top: 1px solid var(--structure);
}

.venues-results-table tbody tr + tr td {
  border-top: 1px solid var(--structure);
}

.venues-results-table .venue-input {
  width: 100%;
  min-height: 26px;
  padding: 2px 6px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text);
  font: inherit;
  outline: 0;
}

.venues-results-table .venue-input:focus {
  border-color: var(--blue);
  background: var(--paper);
}

.results-row.results-group-first td:first-child { border-top-left-radius: var(--radius-md); }

.results-row.results-group-first td:last-child { border-top-right-radius: var(--radius-md); }

.results-row.results-group-last td:first-child { border-bottom-left-radius: var(--radius-md); }

.results-row.results-group-last td:last-child { border-bottom-right-radius: var(--radius-md); }

/* Compact score tables for large OD/KSI grids. */
.match-table.compact-score-table {
  --question-size: 24px;
  contain: paint;
}

.match-table.ek-stage-table {
  --total-col: 50px;
  --theme-score-col: 50px;
}

.match-table.od-detailed {
  --od-detailed-number-col: calc(var(--od-detailed-number-digits) + var(--od-detailed-number-pad-left) + var(--od-detailed-number-pad-right));
  --od-detailed-name-col: calc(var(--od-detailed-name-base-col) + var(--od-detailed-name-extra-col));
  --team-col: calc(var(--od-detailed-number-col) + var(--od-detailed-name-col));
  --place-col: 58px;
  margin-left: var(--space-0);
  margin-top: var(--space-0);
}

.match-table.ksi-detailed {
  --od-detailed-name-col: 190px;
  --team-col: var(--od-detailed-name-col);
  --total-col: 58px;
  --theme-score-col: 40px;
}

.match-table.od-detailed .sticky-name {
  width: var(--team-col);
  min-width: var(--team-col);
  max-width: var(--team-col);
}

.match-table.od-detailed:not(.ksi-detailed) .sticky-place,
.match-table.od-detailed:not(.ksi-detailed) .sticky-place-gap {
  position: static;
  z-index: auto;
}

.match-table.compact-score-table,
.match-table.compact-score-table th,
.match-table.compact-score-table td {
  height: 24px;
  line-height: var(--leading-tight);
}

.match-table.compact-score-table th,
.match-table.compact-score-table td {
  border-color: var(--structure);
  background: var(--cell-fill);
  background-clip: border-box;
}

.match-table.compact-score-table .gap,
.match-table.compact-score-table .gap-head,
.match-table.compact-score-table .place-gap,
.match-table.compact-score-table .place-gap-head,
.match-table.compact-score-table .team-gap {
  background: var(--structure);
  border-color: var(--structure);
}

.match-table.compact-score-table .team-gap { height: 4px; }

.match-table .row-marker {
  --cell-fill: var(--structure);
  left: 0;
  width: 12px;
  min-width: 12px;
  max-width: 12px;
  padding: 0;
  border-color: var(--structure);
  border-radius: 0;
  background: var(--structure);
}

.fight-frame .match-table .row-marker + .sticky-name {
  left: 12px;
}

.fight-frame .match-table .row-marker ~ .sticky-total {
  left: calc(12px + var(--team-col));
}

.fight-frame .match-table .row-marker ~ .sticky-place {
  left: calc(12px + var(--team-col) + var(--total-col));
}

.fight-frame .match-table .row-marker ~ .sticky-place-gap {
  left: calc(12px + var(--team-col) + var(--total-col) + var(--place-col));
}

.host:not(.viewer-readonly) .fight-frame .match-table tbody tr.active-team-row > .active-row-marker {
  background:
    radial-gradient(circle at 50% 50%, var(--blue) 0 3px, transparent 3.5px),
    var(--structure);
}

.match-table.compact-score-table .team-name {
  padding: 0 4px 0 6px;
  font-size: var(--text-xs-plus);
}

.match-table.ek-stage-table .ek-team-cell.team-name {
  padding-left: 0;
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .team-name {
  padding-right: 8px;
  padding-left: 8px;
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .readonly-battle-head {
  padding-right: 8px;
  padding-left: 8px;
}

.readonly-battle-title {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
}

.readonly-battle-venue {
  min-width: 0;
  overflow: hidden;
  color: var(--muted);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.match-table.od-detailed .od-detailed-team-cell,
.match-table .ek-team-cell {
  --cell-fill: var(--structure);
  padding: 0;
  border-radius: 0;
  overflow: visible;
  white-space: nowrap;
}

.match-table.od-detailed .od-detailed-team-cell:hover,
.match-table.od-detailed .od-detailed-team-cell:focus-within,
.match-table .ek-team-cell:hover,
.match-table .ek-team-cell:focus-within {
  z-index: var(--z-popover);
}

.match-table.od-detailed .od-detailed-team-layout,
.match-table .ek-team-cell .od-detailed-team-layout {
  display: grid;
  grid-template-columns: var(--od-detailed-number-col) minmax(0, 1fr);
  align-items: stretch;
  width: 100%;
  height: 100%;
  min-width: 0;
}

.match-table .ek-team-cell .od-detailed-team-layout {
  grid-template-columns: minmax(0, 1fr);
  min-height: calc(var(--question-size) * 2 + var(--cell-gap) * 2);
}

.match-table.ek-stage-table .ek-team-cell .od-detailed-team-layout {
  min-height: calc(var(--question-size) * 2 - var(--cell-gap) * 2);
  max-height: calc(var(--question-size) * 2 - var(--cell-gap) * 2);
  overflow: hidden;
}

.match-table .ek-team-cell .od-detailed-team-name-popover {
  left: 4px;
}

.match-table.od-detailed .od-detailed-team-number {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  min-width: 0;
  padding: 0 var(--od-detailed-number-pad-right) 0 var(--od-detailed-number-pad-left);
  background: var(--structure);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-semibold);
}

.match-table.od-detailed .od-detailed-team-name-wrap,
.match-table .ek-team-cell .od-detailed-team-name-wrap {
  position: relative;
  display: flex;
  align-items: center;
  width: 100%;
  min-width: 0;
  height: 100%;
  min-height: inherit;
  padding: 0 6px;
  border-radius: var(--cell-radius);
  background: var(--paper);
}

.match-table.od-detailed .od-detailed-team-name,
.match-table .ek-team-cell .od-detailed-team-name {
  position: relative;
  display: block;
  min-width: 0;
  max-width: 100%;
  overflow: hidden;
  text-overflow: clip;
  white-space: nowrap;
  line-height: var(--leading-snug);
  padding-bottom: 0.12em;
  margin-bottom: -0.12em;
  outline-offset: var(--focus-outline-offset-tight);
}

.match-table.ek-stage-table .ek-team-cell .od-detailed-team-name {
  display: block;
  width: 100%;
  min-width: 0;
  max-height: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  overflow-wrap: normal;
  word-break: normal;
  white-space: normal;
}

.match-table.ek-stage-table .ek-team-cell .od-detailed-team-name::-webkit-scrollbar {
  display: none;
}

.match-table.ek-stage-table .ek-team-cell.od-detailed-team-cell-truncated .od-detailed-team-name::after {
  content: none;
}

.match-table.ek-stage-table .ek-team-cell.od-detailed-team-cell-truncated .od-detailed-team-name-wrap::after {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 28px;
  background: linear-gradient(to right, var(--paper-transparent), var(--paper));
  pointer-events: none;
}

.match-table.od-detailed .od-detailed-team-cell-truncated .od-detailed-team-name::after,
.match-table .ek-team-cell.od-detailed-team-cell-truncated .od-detailed-team-name::after {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 34px;
  background: linear-gradient(to right, var(--paper-transparent), var(--paper));
  pointer-events: none;
}

.match-table.od-detailed .od-detailed-team-name:focus,
.match-table .ek-team-cell .od-detailed-team-name:focus {
  outline: var(--focus-ring-none);
}

.match-table.od-detailed .od-detailed-team-name-popover,
.match-table .ek-team-cell .od-detailed-team-name-popover {
  position: absolute;
  top: calc(100% - 2px);
  left: calc(var(--od-detailed-number-col) + var(--space-1));
  z-index: var(--z-dropdown);
  display: none;
}

.match-table .ek-team-cell .od-detailed-team-name-popover {
  left: var(--space-1);
}

.match-table.od-detailed .od-detailed-team-cell-truncated:hover .od-detailed-team-name-popover,
.match-table.od-detailed .od-detailed-team-cell-truncated:focus-within .od-detailed-team-name-popover,
.match-table .ek-team-cell.od-detailed-team-cell-truncated:hover .od-detailed-team-name-popover,
.match-table .ek-team-cell.od-detailed-team-cell-truncated:focus-within .od-detailed-team-name-popover {
  display: block;
}

.floating-popovers-enabled .match-table.od-detailed .od-detailed-team-cell-truncated:hover .od-detailed-team-name-popover,
.floating-popovers-enabled .match-table.od-detailed .od-detailed-team-cell-truncated:focus-within .od-detailed-team-name-popover,
.floating-popovers-enabled .match-table .ek-team-cell.od-detailed-team-cell-truncated:hover .od-detailed-team-name-popover,
.floating-popovers-enabled .match-table .ek-team-cell.od-detailed-team-cell-truncated:focus-within .od-detailed-team-name-popover,
.ek-page .match-table.od-detailed .od-detailed-team-cell-truncated:hover .od-detailed-team-name-popover,
.ek-page .match-table.od-detailed .od-detailed-team-cell-truncated:focus-within .od-detailed-team-name-popover,
.ek-page .match-table .ek-team-cell.od-detailed-team-cell-truncated:hover .od-detailed-team-name-popover,
.ek-page .match-table .ek-team-cell.od-detailed-team-cell-truncated:focus-within .od-detailed-team-name-popover {
  display: none;
}

.floating-name-popover {
  position: fixed;
  z-index: var(--z-tooltip);
  display: none;
  max-width: min(420px, calc(100vw - var(--space-4)));
}

.floating-name-popover.visible {
  display: block;
}

.floating-name-popover.above {
  box-shadow: var(--shadow-popover-up);
}

.match-table.od-detailed .od-detailed-team-head {
  padding: 0;
  overflow: hidden;
}

.match-table.od-detailed .od-detailed-team-head-layout {
  height: 100%;
}

.match-table.od-detailed .od-detailed-team-head-label {
  display: flex;
  align-items: center;
  min-width: 0;
  padding: 0 6px;
  text-align: left;
}

/* KSI «Подробно» sortable headers: the № column and the Команда label are
   buttons that pick the local row order. Reset native button chrome but keep
   each one's column styling (.od-detailed-team-number / -head-label). */
.match-table.od-detailed .ksi-sort-head {
  appearance: none;
  -webkit-appearance: none;
  border: 0;
  /* <button> defaults to the UA text colour (black) rather than inheriting, so
     the inactive sort header vanished on dark themes. Inherit the themed header
     colour instead. */
  color: inherit;
  font-family: inherit;
  font-size: inherit;
  cursor: pointer;
}

.match-table.od-detailed .od-detailed-team-head-label.ksi-sort-head {
  background: none;
}

.match-table.od-detailed .ksi-sort-head:hover {
  text-decoration: underline;
}

.match-table.od-detailed .ksi-sort-head.ksi-sort-active {
  color: var(--text);
  text-decoration: underline;
}

.match-table.ksi-detailed .sticky-total {
  padding-right: 4px;
  padding-left: 4px;
}

.match-table.compact-score-table .team-name .venue-input {
  min-height: 0;
  height: 21px;
  padding: 0 4px;
  line-height: 19px;
  font-size: var(--text-xs-plus);
  color: var(--text);
  box-sizing: border-box;
  -webkit-appearance: none;
  appearance: none;
}

.match-table.compact-score-table .answer-cell,
.match-table.compact-score-table .question-head {
  width: var(--question-size);
  min-width: var(--question-size);
  max-width: var(--question-size);
  height: var(--question-size);
  padding: 0;
}

.match-table.od-detailed .answer-cell {
  font-size: var(--text-2xs);
  font-weight: var(--fw-regular);
  color: var(--green-dark);
  text-align: center;
  vertical-align: middle;
}

@media (max-width: 760px) {
  .match-tabs { padding: 2px 0; }

  .match-tab { padding: 4px 10px; font-size: var(--text-xs); }

  .results-table {
    --results-place-col: var(--results-place-col-mobile);
    --results-team-col: var(--results-team-col-mobile);
    --results-team-pad: var(--results-team-pad-mobile);
    --results-total-col: var(--results-total-col-mobile);
  }

  .match-table.od-detailed {
    --od-detailed-name-col: calc(var(--od-detailed-name-base-col-compact) + var(--od-detailed-name-extra-col));
  }

  .match-table.ksi-detailed {
    --od-detailed-name-col: 140px;
    --team-col: var(--od-detailed-name-col);
    --total-col: 54px;
    --theme-score-col: 40px;
  }

  .match-table.od-detailed .od-detailed-team-name,
  .match-table .ek-team-cell .od-detailed-team-name {
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
  }

  .match-table.od-detailed .od-detailed-team-name::-webkit-scrollbar,
  .match-table .ek-team-cell .od-detailed-team-name::-webkit-scrollbar {
    display: none;
  }

  .match-table.od-detailed .od-detailed-team-cell-truncated .od-detailed-team-name::after,
  .match-table .ek-team-cell.od-detailed-team-cell-truncated .od-detailed-team-name::after {
    content: none;
  }

  .match-table.od-detailed .od-detailed-team-cell-truncated .od-detailed-team-name-wrap::after,
  .match-table .ek-team-cell.od-detailed-team-cell-truncated .od-detailed-team-name-wrap::after {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 28px;
    background: linear-gradient(to right, var(--paper-transparent), var(--paper));
    pointer-events: none;
  }

  .grid-slot-team-name {
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
  }

  .grid-slot-team-name::-webkit-scrollbar {
    display: none;
  }

  .grid-slot-team-truncated .grid-slot-team-name::after {
    content: none;
  }

  .grid-slot-team-truncated::after {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 28px;
    background: linear-gradient(to right, var(--paper-transparent), var(--paper));
    pointer-events: none;
  }

  .results-team-name {
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
  }

  .results-team-name::-webkit-scrollbar {
    display: none;
  }

  .results-team-truncated .results-team-name::after {
    content: none;
  }

  .results-team-truncated .results-team-name-wrap::after {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: var(--inline-fade-width);
    background: var(--inline-fade-bg);
    pointer-events: none;
  }

  .results-table th,
  .results-table td { padding: 4px 6px; font-size: var(--text-xs); }

  .results-table .results-team-head,
  .results-table .results-team { padding-left: var(--results-team-pad); min-width: 0; }

  .results-team-city { font-size: var(--results-team-city-size-mobile); }

  /* Phone: tighten the name cap and the count columns so the wide stats table
     stays usable. Long names truncate (fade + popover); the numeric grid scrolls
     horizontally. The player column (Игрок) freezes at the left edge — same
     frozen-first-column pattern as the OD/KSI results tables — so it stays put
     while Команда and the counts scroll under it. */
  .ek-stats-table { --ek-stats-name-col: 132px; }

  .ek-stats-table thead th.ek-stats-player-head,
  .ek-stats-table td.ek-stats-player {
    position: sticky;
    left: 0;
  }

  .ek-stats-table td.ek-stats-player { z-index: 1; --ek-stats-fade-from: var(--paper); --ek-stats-fade-to: var(--paper-transparent); }

  .ek-stats-table thead th.ek-stats-player-head { z-index: 4; --ek-stats-fade-from: var(--structure); --ek-stats-fade-to: var(--structure-transparent); }

  /* Scrolled-under fade off the frozen player column's right edge, drawn once
     the rest has scrolled — same cue as the OD/KSI results tables. The scroll
     frame toggles .stage-scroll-left (viewer.js / host.js). */
  .sheet-frame.stage-scroll-left .ek-stats-table td.ek-stats-player::after,
  .sheet-frame.stage-scroll-left .ek-stats-table thead th.ek-stats-player-head::after {
    content: "";
    position: absolute;
    top: 0;
    right: -28px;
    bottom: 0;
    width: 28px;
    pointer-events: none;
    background: linear-gradient(to right, var(--ek-stats-fade-from), var(--ek-stats-fade-to));
  }

  .ek-stats-table .narrow { width: 26px; min-width: 26px; }

  .results-place-head,
  .results-place {
    width: var(--results-place-col);
    min-width: var(--results-place-col);
    max-width: var(--results-place-col);
    padding-right: 0;
    padding-left: 0;
    font-size: var(--text-xs-tight);
  }

  .results-table thead th.results-place-head {
    font-size: 0;
  }

  .results-table thead th.results-place-head::after {
    content: "М";
    font-size: var(--text-xs-tight);
  }

  .seed-import-table thead th.seed-number-head {
    font-size: var(--text-xs-tight);
  }

  .seed-import-table thead th.seed-number-head::after {
    content: none;
  }

  .results-num-head,
  .results-tour-head {
    width: var(--results-num-col-mobile);
    min-width: var(--results-num-col-mobile);
    max-width: var(--results-num-col-mobile);
  }

  .results-table .results-team,
  .results-table thead .results-team-head { left: var(--results-place-col); }

  .results-table .results-total,
  .results-table thead .results-total-head {
    left: calc(var(--results-place-col) + var(--results-team-col));
  }

  .od-results-table .results-team,
  .od-results-table thead .results-team-head,
  .ksi-results-table .results-team,
  .ksi-results-table thead .results-team-head,
  .venues-results-table .results-team,
  .venues-results-table thead .results-team-head {
    width: var(--results-team-col);
    min-width: var(--results-team-col);
    max-width: var(--results-team-col);
  }

  .results-table thead th.results-total-head,
  .results-table .results-total {
    width: var(--results-total-col);
    min-width: var(--results-total-col);
    max-width: var(--results-total-col);
  }
}

/* Public EK viewer tuning. */
.readonly-battle-popover {
  display: none;
}

.viewer-readonly .match-main:has(.fight-host) .fight-frame {
  box-shadow: var(--shadow-scroll-inset);
}

.viewer-readonly .match-table.ek-stage-table.readonly-table {
  --question-size: 26px;
  --team-col: 158px;
  --total-col: 46px;
  --place-col: 30px;
  --place-gap: 8px;
  --theme-score-col: 42px;
  --theme-gap-col: 16px;
  --narrow-col: 24px;
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .sticky-name {
  width: var(--team-col);
  min-width: var(--team-col);
  max-width: var(--team-col);
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .sticky-total {
  left: var(--team-col);
  width: var(--total-col);
  min-width: var(--total-col);
  max-width: var(--total-col);
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .sticky-place {
  width: var(--place-col);
  min-width: var(--place-col);
  max-width: var(--place-col);
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .place-gap,
.viewer-readonly .match-table.ek-stage-table.readonly-table .place-gap-head {
  width: var(--place-gap);
  min-width: var(--place-gap);
  max-width: var(--place-gap);
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .theme-head,
.viewer-readonly .match-table.ek-stage-table.readonly-table .theme-score {
  width: var(--theme-score-col);
  min-width: var(--theme-score-col);
  max-width: var(--theme-score-col);
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .gap,
.viewer-readonly .match-table.ek-stage-table.readonly-table .gap-head {
  width: var(--theme-gap-col);
  min-width: var(--theme-gap-col);
  max-width: var(--theme-gap-col);
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .narrow {
  width: var(--narrow-col);
  min-width: var(--narrow-col);
  max-width: var(--narrow-col);
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .readonly-battle-head {
  padding-right: 6px;
  padding-left: 6px;
  font-size: var(--text-xs);
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .readonly-battle-title {
  display: flex;
  align-items: center;
  gap: 5px;
  width: 100%;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .readonly-battle-title:focus {
  outline: var(--focus-ring-none);
  outline-offset: var(--focus-outline-offset-tight);
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .readonly-battle-name {
  flex: 0 0 auto;
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .readonly-battle-venue {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  color: var(--muted);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .readonly-player {
  position: relative;
  overflow: hidden;
  padding-right: 8px;
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .readonly-player-text-wrap {
  position: absolute;
  top: 0;
  right: 8px;
  bottom: 0;
  left: 8px;
  display: flex;
  align-items: center;
  min-width: 0;
  overflow: hidden;
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .readonly-player-text {
  display: block;
  max-width: 100%;
  min-width: 0;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  white-space: nowrap;
  text-overflow: clip;
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .readonly-player-text::-webkit-scrollbar {
  display: none;
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .readonly-player.readonly-player-cell-truncated .readonly-player-text-wrap::after {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: var(--inline-fade-width);
  background: var(--inline-fade-bg);
  pointer-events: none;
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .readonly-player-popover {
  position: absolute;
  top: calc(100% - 2px);
  left: var(--space-1);
  z-index: var(--z-dropdown);
  display: none;
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .readonly-player.readonly-player-cell-truncated:hover .readonly-player-popover,
.viewer-readonly .match-table.ek-stage-table.readonly-table .readonly-player.readonly-player-cell-truncated:focus-within .readonly-player-popover {
  display: block;
}

.floating-popovers-enabled .viewer-readonly .match-table.ek-stage-table.readonly-table .readonly-player.readonly-player-cell-truncated:hover .readonly-player-popover,
.floating-popovers-enabled .viewer-readonly .match-table.ek-stage-table.readonly-table .readonly-player.readonly-player-cell-truncated:focus-within .readonly-player-popover,
.ek-page .viewer-readonly .match-table.ek-stage-table.readonly-table .readonly-player.readonly-player-cell-truncated:hover .readonly-player-popover,
.ek-page .viewer-readonly .match-table.ek-stage-table.readonly-table .readonly-player.readonly-player-cell-truncated:focus-within .readonly-player-popover {
  display: none;
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .ek-team-cell .od-detailed-team-name-wrap {
  align-items: center;
  overflow: hidden;
  padding-right: 7px;
  padding-left: 7px;
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .ek-team-cell .od-detailed-team-name {
  display: block;
  max-height: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  line-height: 1.15;
  overflow-wrap: normal;
  text-overflow: clip;
  white-space: normal;
  word-break: normal;
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .ek-team-cell .od-detailed-team-name::-webkit-scrollbar {
  display: none;
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .ek-team-cell.od-detailed-team-cell-truncated .od-detailed-team-name::after {
  content: none;
}

.viewer-readonly .match-table.ek-stage-table.readonly-table .ek-team-cell.od-detailed-team-cell-truncated .od-detailed-team-name-wrap::after {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 28px;
  background: linear-gradient(to right, var(--paper-transparent), var(--paper));
  pointer-events: none;
}

@media (max-width: 760px) {
  .game-host-top {
    display: grid;
    grid-template-columns: minmax(0, auto) 1fr auto;
    grid-template-rows: auto;
    align-items: center;
    column-gap: var(--space-2);
    row-gap: var(--space-1);
    height: auto;
    padding: var(--space-1-5) var(--space-2-5);
  }

  .game-host-top:has(.match-tabs:not([hidden]):not(:empty)) {
    grid-template-rows: auto auto;
  }

  .game-host-top .game-header-main {
    display: contents;
  }

  .game-host-top .game-breadcrumbs {
    grid-column: 1;
    grid-row: 1;
    min-width: 0;
    max-width: 100%;
    font-size: var(--text-xs);
  }

  .game-host-top .od-header-progress {
    grid-column: 2;
    grid-row: 1;
    justify-self: start;
    font-size: var(--text-xs);
  }

  .game-host-top .match-tabs {
    grid-column: 1 / span 2;
    grid-row: 2;
    width: 100%;
  }

  .game-host-top .host-actions {
    grid-column: 3;
    grid-row: 1 / -1;
    align-self: center;
    justify-self: end;
  }

  /* Stack the viewer count above the sync tick, shrinking both so the pair
     stays the same height as the viewer-mode button instead of widening. */
  .host-actions .sync-stack {
    flex-direction: column;
    gap: var(--space-0-5);
  }

  .host-actions .sync-stack .viewers-count {
    font-size: var(--text-xs);
  }

  .host-actions .sync-stack .sync-status {
    width: auto;
    height: 16px;
  }

  .ek-page .ek-tabs .match-tab {
    height: 24px;
    padding: 0 var(--space-2-5);
    font-size: var(--text-xs-plus);
    line-height: 22px;
  }

  .od-page .match-main,
  .si-page .match-main {
    height: calc(100vh - 60px);
    height: calc(100dvh - 60px);
  }

  .ek-page .match-main {
    height: calc(100vh - 60px);
    height: calc(100dvh - 60px);
    padding: 3px 4px 4px;
  }

  .viewer-readonly .match-table.ek-stage-table.readonly-table {
    --question-size: 26px;
    --team-col: 112px;
    --total-col: 42px;
    --place-col: 24px;
    --place-gap: 4px;
    --theme-score-col: 40px;
    --theme-gap-col: 12px;
    --narrow-col: 22px;
    font-size: var(--text-xs);
  }

  .viewer-readonly .match-table.ek-stage-table.readonly-table .team-name {
    font-size: var(--text-xs);
  }

  .viewer-readonly .match-table.ek-stage-table.readonly-table .readonly-battle-head {
    font-size: var(--text-xs);
  }

  .viewer-readonly .match-table.ek-stage-table.readonly-table .readonly-player {
    font-size: var(--text-xs);
  }

  .venues-results-table {
    width: 100%;
    min-width: 0;
  }
}

/* Mass number import (Импорт номеров) — paste modal + confirmation modal. */
.numbers-import-dialog {
  width: min(640px, calc(100vw - var(--space-6)));
}

.numbers-import-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  max-height: calc(100vh - var(--space-6));
}

.numbers-import-textarea {
  width: 100%;
  resize: vertical;
  padding: var(--space-2) var(--space-2-5);
  border: 1px solid var(--action-border);
  border-radius: var(--radius-sm);
  background: var(--field-bg);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
}

.numbers-import-errors {
  margin: 0;
  padding-left: 1.2em;
  color: var(--muted);
  font-size: var(--text-sm);
}

.numbers-import-list {
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.numbers-import-row {
  display: grid;
  grid-template-columns: 2.5em minmax(0, 1fr) auto minmax(0, 1.4fr) auto;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) 0;
}

.numbers-import-num {
  font-weight: var(--fw-bold);
  text-align: right;
}

.numbers-import-raw {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.numbers-import-arrow {
  color: var(--muted-light);
}

.numbers-import-select {
  width: 100%;
  min-height: 32px;
  padding: 4px 6px;
  border: 1px solid var(--action-border);
  border-radius: var(--radius-sm);
  background: var(--field-bg);
  color: var(--text);
  font: inherit;
}

.numbers-import-badge {
  font-size: var(--text-xs);
  color: var(--muted);
  white-space: nowrap;
}

.numbers-import-row.is-fuzzy .numbers-import-badge { color: var(--amber-text); }

.numbers-import-row.is-unmatched .numbers-import-badge { color: var(--muted-light); }

/* ════════════════════════════════════════════════════════════════════════
   end of the section moved out of core.css — the original dope layer follows
   ════════════════════════════════════════════════════════════════════════ */

/* Kept in the app layer (a duplicate of core.css) because it is load-bearing:
   [hidden] must beat the layout classes on hidden auth/game sections. */
[hidden] {
  display: none !important;
}

:root {
  /* Экран (projector board) defaults. Theme-independent on purpose: a projector
     board is white/black regardless of the host's UI theme. The host can
     override each via the Экран settings panel (stored per game). */
  --screen-bg-default: #ffffff;
  --screen-fg-default: #000000;
  --screen-muted-default: #5f6b7a;
  --ksi-sticker-col: 36px;
  --transition-fast: 120ms ease;
}

/* ── design system: layout primitives ─────────────────────── */
.cluster {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}

/* ── public + host common page chrome ────────────────────── */
.public {
  margin: 0;
  padding: 0;
  background: var(--page);
  color: var(--text);
}

.public-top {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  height: var(--header-h);
  padding: 0 var(--space-5);
  background: var(--surface);
}

.public-top h1 {
  margin: 0;
  font-size: var(--text-xl);
  font-weight: var(--fw-bold);
  /* the bar's height is fixed (--header-h) — a long fest title must not wrap */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.public-main {
  max-width: 720px;
  margin: 0 auto;
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

/* ── fest lists (public index + host landing): collapsible groups + stacked rows ──── */
.fest-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.fest-group-title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-lg);
  font-weight: var(--fw-semibold);
  cursor: pointer;
  list-style: none;
}

.fest-group-title::-webkit-details-marker {
  display: none;
}

.fest-group-title::before {
  content: "▾";
  font-size: var(--text-sm);
  color: var(--muted);
  transition: transform 0.15s ease;
}

.fest-group:not([open]) > .fest-group-title::before {
  transform: rotate(-90deg);
}

.fest-row {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: var(--space-3);
  row-gap: var(--space-1);
}

.fest-row-title {
  font-weight: var(--fw-semibold);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

.fest-row-dates {
  color: var(--muted);
  font-size: var(--text-xs);
  white-space: nowrap;
}

.public-user {
  margin-left: auto;
  color: var(--muted);
  font-size: var(--text-sm);
  font-weight: var(--fw-semibold);
  text-decoration: none;
}

.public-user:hover {
  text-decoration: underline;
}
/* Итог */
/* Экран: projector scoreboard. Reuses the Итог (.results-table) styling; the
   columns are laid out side by side and the whole board is scaled with `zoom`
   by layoutScreen() so every team fits the screen. */
.screen-wrapper {
  /* Host-overridable projector palette; JS sets the --screen-* vars inline from
     the saved settings, falling back to the theme-independent defaults. */
  --screen-bg: var(--screen-bg-default);
  --screen-fg: var(--screen-fg-default);
  --screen-muted: var(--screen-muted-default);
  --screen-border: color-mix(in srgb, var(--screen-fg) 16%, transparent);
  --screen-team-col: 160px;
  position: relative;
  box-sizing: border-box;
  width: 100%;
  display: flex;
  overflow: auto;
  background: var(--screen-bg);
  color: var(--screen-fg);
}

.screen-cols {
  /* margin:auto centres on both axes AND, unlike justify-content, never clips
     the start edge when the (font-scaled) board overflows — so the host can
     scroll to it. */
  margin: auto;
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  /* The board is zoom-scaled, which under Chrome's modern `zoom` makes it a
     stacking context and (worse) intercepts pointer events over the floating
     overlay even when the overlay has a higher z-index — so clicks on the
     controls / colour pickers did nothing once the board filled the width. The
     board is display-only, so drop its pointer events entirely; the positioned
     overlay above it then reliably receives clicks. */
  position: relative;
  z-index: 0;
  pointer-events: none;
}

.screen-table.od-results-table {
  --results-team-col: var(--screen-team-col);
}

/* Recolour the board from the host palette: cells take the chosen background and
   text colour; headers are muted so they don't distract from the content; city
   sub-labels use the muted colour. */
.screen-table.results-table th,
.screen-table.results-table td,
.screen-table.results-table .results-place,
.screen-table.results-table .results-team,
.screen-table.results-table .results-total,
.screen-table.results-table thead th {
  background: var(--screen-bg);
  color: var(--screen-fg);
}

.screen-table.results-table thead th,
.screen-table.results-table .results-team-city {
  color: var(--screen-muted);
}

/* Separators: NONE inside a place-group (rows glued), one line BETWEEN groups.
   The packer inserts a .results-group-gap row at every in-column group boundary,
   so the line lives there; the per-row Итог divider is dropped. */
.screen-table.results-table .results-row:not(.results-group-first) td {
  border-top: 0;
}

.screen-table.results-table .results-group-gap td {
  height: var(--space-1);
  padding: 0;
  background: var(--screen-bg);
  border-bottom: 2px solid var(--screen-border);
}

.screen-empty {
  color: var(--screen-muted);
  font-size: var(--text-lg);
}

/* Floating host overlay (controls bar + settings panel) at the top-right of the
   board. Hidden by default; revealed only when the pointer is in the top-right
   ninth of the board (JS toggles .screen-overlay-active) or the panel is open,
   so it never sits on the projected image. */
.screen-overlay {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  z-index: var(--z-modal);
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-2);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--transition-fast);
}

.screen-wrapper.screen-overlay-active .screen-overlay {
  opacity: 1;
  pointer-events: auto;
}

.screen-controls {
  display: flex;
  gap: var(--space-2);
}

.screen-btn {
  background: var(--surface);
  color: var(--text);
}

.screen-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  width: min(280px, calc(100vw - var(--space-6)));
  max-height: 70vh;
  overflow-y: auto;
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-modal);
}

.screen-field {
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.screen-field > input[type="color"] {
  width: 48px;
  min-height: 32px;
  padding: 2px;
  cursor: var(--cursor-action);
}

.screen-field > input[type="range"] {
  flex: 1;
}

.screen-field > input[type="number"] {
  width: 72px;
}

.screen-check {
  font-size: var(--text-base);
}

.screen-reset {
  align-self: flex-start;
}

/* The diagnostic "Скачать лог" chip must never show on the projected board. */
body.od-screen-active .dope-rec-btn {
  display: none;
}

/* Fullscreen (projector) mode: drop the site chrome so only the board shows. */
body.is-fullscreen .host-top {
  display: none;
}

body.is-fullscreen.od-page .match-main {
  height: 100vh;
}

/* Составы (rosters) — shared team→players table, used on every game page
   (EK/OD/KSI, host and viewer). Reuses the results-table design-system styling
   (links are the kit's .quiet-link); only the players column and the empty
   states need local rules. The score-table host is min-width:max-content (so wide boards can
   scroll), but the roster should instead fit the frame and wrap the players
   column, so it never scrolls sideways and the sticky "Игроки" header stays put.
   Neutralize the max-content host just for the roster and let the table be 100%
   wide with a fixed layout — the fixed №/Команда columns leave the rest to the
   players column, which wraps. */
.table-host:has(.roster-results-wrapper) {
  width: 100%;
  min-width: 0;
}

.roster-results-wrapper {
  width: 100%;
}

.roster-results-table {
  width: 100%;
  table-layout: fixed;
}

.roster-results-table .results-team-head {
  width: var(--results-team-col);
}

/* The team name reuses .results-team-name (clip + fade + popover from the shared
   results-table styling); we only add the roster's bold weight on top. */
.roster-team-name {
  font-weight: var(--fw-semibold);
}

.roster-team-city {
  display: block;
  color: var(--muted);
  font-size: var(--text-xs);
}

/* Left-align the Игроки header (the shared thead th centers by default, and its
   `.results-table thead th` selector out-specifies a bare class). */
.roster-results-table .roster-players-head,
.roster-results-table .roster-players-cell {
  text-align: left;
}

.roster-players-cell {
  padding: var(--space-2) var(--results-team-pad);
  white-space: normal;
}

/* Keep each full name intact; allow wrapping only at the space between names. */
.roster-player {
  white-space: nowrap;
}

.roster-player:not(:last-child)::after {
  content: ", ";
  white-space: normal;
}

.roster-empty {
  color: var(--muted);
  padding: var(--space-3);
}

/* KSI-with-stickers: the per-(team, theme) sticker dropdown lives in the theme's
   spacer column, so that gap widens to hold the control. */
.match-table.ksi-detailed .ksi-sticker-cell {
  /* Cell footprint = sticker column + an empty strip after it (border-box, so
     the gap is added to the width rather than eaten out of the sticker area).
     The strip groups the sticker with the theme on its left, not the next one
     — same separation as the inter-theme gap in plain KSI. */
  width: calc(var(--ksi-sticker-col) + var(--theme-gap-col));
  min-width: calc(var(--ksi-sticker-col) + var(--theme-gap-col));
  max-width: calc(var(--ksi-sticker-col) + var(--theme-gap-col));
  overflow: visible;
  padding: 0;
  background: var(--cell-fill);
  border-color: var(--structure);
  vertical-align: middle;
  border-right: var(--theme-gap-col) solid transparent;
  background-clip: padding-box;
}

.ksi-sticker-wrap {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  min-height: 24px;
}

.ksi-sticker-icon {
  pointer-events: none;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Composes with the SVG's inline colorize filter; dark theme dims stickers
     a shade so they don't read as over-contrasty on the dark background. */
  filter: var(--ksi-sticker-dim, none);
}

.ksi-sticker-icon svg {
  width: 28px;
  height: 28px;
}

/* Transparent select overlay covers the whole cell for interaction. */
.ksi-sticker-select {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}

.ksi-sticker-select:disabled {
  cursor: default;
}

/* Unselected: subtle chevron placeholder. */
.ksi-sticker-wrap:not(.has-sticker)::after {
  content: "▾";
  font-size: var(--text-xs);
  color: var(--muted);
  pointer-events: none;
}

/* Over the allowed count: thick red inset border on the cell. */
.match-table.ksi-detailed .ksi-sticker-cell.ksi-sticker-over {
  box-shadow: inset 0 0 0 3px var(--danger);
}

.ksi-sticker-cell.ksi-sticker-over .ksi-sticker-icon {
  color: var(--danger-text) !important;
}

/* The fixed sticker colour palette — the only colours an organizer may pick.
   Hex values mirror stickerPaletteColors in host_games.go; keep the two in
   sync. Each swatch is a hidden radio over a coloured dot. */
:root {
  --sticker-c-white: #ffffff;
  --sticker-c-yellow: #fdf66f;
  --sticker-c-green: #aded87;
  --sticker-c-red: #ff7a6b;
  --sticker-c-blue: #68caff;
  --sticker-c-pink: #f4a8ff;
  --sticker-c-orange: #ffae37;
}

.sticker-palette {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1-5);
  margin-right: var(--space-3);
}

.sticker-palette > span {
  font-size: var(--text-sm);
}

.sticker-palette .swatch {
  position: relative;
  display: inline-block;
  line-height: 0;
  cursor: pointer;
}

.sticker-palette .swatch input {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

/* the ring keeps a pale swatch from vanishing into the palette — a graphic, so
   --grid-dark rather than the transparent --border */
.sticker-palette .swatch-dot {
  display: inline-block;
  width: var(--space-5);
  height: var(--space-5);
  border-radius: var(--radius-sm);
  background: var(--swatch);
  border: 1px solid var(--grid-dark);
  box-sizing: border-box;
}

/* The colour comes from expandSwatchradio's closed palette enum as data-color,
   mapped here to --swatch so the dot needs no inline style. Keep the values in
   sync with the --sticker-c-* palette above. */
.sticker-palette .swatch-dot[data-color="white"] { --swatch: var(--sticker-c-white); }
.sticker-palette .swatch-dot[data-color="yellow"] { --swatch: var(--sticker-c-yellow); }
.sticker-palette .swatch-dot[data-color="green"] { --swatch: var(--sticker-c-green); }
.sticker-palette .swatch-dot[data-color="red"] { --swatch: var(--sticker-c-red); }
.sticker-palette .swatch-dot[data-color="blue"] { --swatch: var(--sticker-c-blue); }
.sticker-palette .swatch-dot[data-color="pink"] { --swatch: var(--sticker-c-pink); }
.sticker-palette .swatch-dot[data-color="orange"] { --swatch: var(--sticker-c-orange); }

.sticker-palette .swatch input:checked + .swatch-dot,
.sticker-palette .swatch input:focus-visible + .swatch-dot {
  outline: var(--space-0-5) solid var(--blue-bright);
  outline-offset: var(--space-0-5);
}

/* ════════════════════════════════════════════════════════════════════════
   APPEARANCE: dark theme + high-contrast mode
   Two independent axes set by menu.js on <html>:
     data-theme    = light | dark
     data-contrast = regular | high
   Default (no overrides) is the light/regular palette in :root above. Each
   block below only re-points existing color variables, so the whole app
   re-themes through the variables it already uses. The high-contrast blocks
   are scoped per theme ([data-theme=light/dark][data-contrast=high]) so they
   layer on top of the active theme with higher specificity than the bare
   dark block — order-independent.
   Palette rationale (Material dark theme + WCAG): no pure black; depth via
   lighter surfaces ~3-4 L apart, not shadows; off-white (not #fff) text;
   lightened/desaturated accents; high-contrast separates adjacent surfaces
   with stronger, text-colored borders and darker grays.
   ════════════════════════════════════════════════════════════════════════ */

/* ── Dark theme ─────────────────────────────────────────────────────────── */
:root[data-theme="dark"] {
  /* Dim KSI stickers a shade on dark theme — full-bright colors read as
     over-contrasty against the dark cells. */
  --ksi-sticker-dim: brightness(0.85);
}
