/* Крапки — notebook paper, ballpoint blue, red pen */
:root {
  --paper: #f7f8f5;
  --paper-deep: #eceee9;
  --sheet: #fbfcfa;
  --grid: #cad7e4;
  --grid-edge: #a3b6c9;
  --margin: #e7a1a7;
  --ink: #1f2833;
  --ink-soft: #5d6a79;
  --ink-faint: #8b97a5;
  --line: #dde3e8;
  --blue: #2a4fc4;
  --red: #c9343c;
  /* 0.1.38: the movement arrows need a third colour — up is not "not red" */
  --ok: #2e8b57;
  --fill-alpha: 0.14;
  --scrim: rgba(24, 32, 42, 0.32);
  --shadow: 0 -10px 40px rgba(31, 40, 51, 0.12);

  --f-display: "Unbounded", "SF Pro Rounded", ui-rounded, system-ui, -apple-system, sans-serif;
  --f-ui: "Onest", -apple-system, "SF Pro Text", system-ui, "Segoe UI", Roboto, sans-serif;
  --f-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  --safe-t: env(safe-area-inset-top, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  color-scheme: light;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #151a20;
    --paper-deep: #0f1318;
    --sheet: #1a2028;
    --grid: #27313c;
    --grid-edge: #3d4b59;
    --margin: #6e3a42;
    --ink: #e7ebf0;
    --ink-soft: #a0abb8;
    --ink-faint: #6d7988;
    --line: #29323c;
    --blue: #7d9dff;
    --red: #ff7d7b;
    --ok: #4fd18b;
    --fill-alpha: 0.2;
    --scrim: rgba(0, 0, 0, 0.5);
    --shadow: 0 -10px 40px rgba(0, 0, 0, 0.45);
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --paper: #151a20;
  --paper-deep: #0f1318;
  --sheet: #1a2028;
  --grid: #27313c;
  --grid-edge: #3d4b59;
  --margin: #6e3a42;
  --ink: #e7ebf0;
  --ink-soft: #a0abb8;
  --ink-faint: #6d7988;
  --line: #29323c;
  --blue: #7d9dff;
  --red: #ff7d7b;
  --ok: #4fd18b;
  --fill-alpha: 0.2;
  --scrim: rgba(0, 0, 0, 0.5);
  --shadow: 0 -10px 40px rgba(0, 0, 0, 0.45);
  color-scheme: dark;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }
html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;
  background: var(--paper-deep);
  color: var(--ink);
  font-family: var(--f-ui);
  font-size: 15px;
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
body { position: fixed; inset: 0; }
button { font: inherit; color: inherit; }
button:focus-visible, [tabindex]:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }

/* 0.1.20: #shell holds the two screens (home and game); on a tablet or desktop it becomes a
   phone-shaped window — see the media query at the end of this file. */
#shell { position: absolute; inset: 0; }
#app {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-rows: auto auto 1fr auto;
  height: 100%;
}

/* ---------- Board ---------- */
.board-wrap { position: relative; min-height: 0; overflow: hidden; }
#board { position: absolute; inset: 0; width: 100%; height: 100%; touch-action: none; display: block; }

.pill {
  position: absolute;
  left: 50%; bottom: 14px;
  transform: translateX(-50%);
  display: inline-flex; align-items: center; gap: 10px;
  padding: 10px 18px 10px 14px;
  border: 0; border-radius: 999px;
  background: var(--ink); color: var(--paper);
  font-weight: 600; font-size: 15px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18);
  cursor: pointer;
}
.pill .dot { width: 12px; height: 12px; border-radius: 50%; background: var(--side-1, var(--blue)); box-shadow: 0 0 0 2px var(--paper); }
.pill .dot.red { background: var(--side-2, var(--red)); }
.pill b { font-family: var(--f-mono); font-weight: 500; }

/*
 * THE MESSAGE THAT NOBODY EVER SAW (0.1.37).
 *
 * The toast lived `position: absolute` with no z-index INSIDE the board wrapper, and sheets are
 * `position: fixed` at `z-index: 11`. So every message fired from a sheet — the championship's
 * whole screen — was written, shown, and painted UNDERNEATH it. That is how the form mark of
 * 0.1.35 came to be reported as doing nothing when tapped: it did exactly what it was told, into
 * a layer the sheet was covering. Measured rather than guessed: `elementFromPoint` at the toast's
 * own centre returned `sheet-champ`.
 *
 * It is now fixed to the viewport and above everything, which is what a toast is for. It also
 * wraps now: the old `white-space: nowrap` meant a long sentence ran off both edges of a phone.
 */
.toast {
  position: fixed; left: 50%; top: calc(var(--safe-t) + 12px);
  transform: translate(-50%, -8px);
  max-width: min(420px, calc(100vw - 32px));
  padding: 8px 14px; border-radius: 10px;
  background: var(--sheet); color: var(--ink);
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
  font-size: 14px; text-align: center; line-height: 1.35;
  opacity: 0; pointer-events: none;
  transition: opacity 0.2s, transform 0.2s;
  z-index: 40;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }

/*
 * ---------- СЛІД НАВІГАЦІЇ (0.1.51) ----------
 *
 * Інструмент для запису екрана: два рядки вгорі, які кажуть, де людина в стеку і що щойно з ним
 * сталося. Тому вимоги в нього не такі, як у решти інтерфейсу.
 *
 * ЧИТАТИСЯ НА КАДРІ ВІДЕО. Запис екрана iPhone стискається, і тонкий сірий текст на світлому тлі
 * в ньому розмивається першим. Звідси чорна плашка з білим моноширинним текстом — однакова в обох
 * темах, навмисно: діагностика не має підлаштовуватись під оформлення, вона має лишатись тим
 * самим прямокутником, хай що під ним.
 *
 * НЕ ЗАВАЖАТИ. `pointer-events: none` — накладка над грою, яка перехоплює хоч один дотик, змінює
 * те, що вимірює. `z-index` вищий за аркуші (11) і за сповіщення (40), бо вада може статися саме
 * на аркуші — і саме там слід потрібен найбільше.
 *
 * НЕ ЗАЙМАТИ ЕКРАН. Рядок притиснутий до верху, вужчий за повідомлення, з переносом: довгий слід
 * у плей-оф не має ховати рахунок.
 */
#nav-trace {
  position: fixed; left: 0; right: 0; top: var(--safe-t);
  padding: 3px calc(var(--safe-l) + 8px) 4px calc(var(--safe-r) + 8px);
  background: rgba(8, 10, 13, 0.82);
  color: #fff;
  font-family: var(--f-mono); font-size: 10px; line-height: 1.3;
  letter-spacing: -0.01em;
  pointer-events: none;
  z-index: 60;
  overflow-wrap: anywhere;
}
#nav-trace b { display: block; font-weight: 600; }
/* другий рядок — історія, тож трохи тихіший за «де я зараз», але все ще білий: сірий у стисненому
   відео перетворюється на кашу */
#nav-trace i { display: block; font-style: normal; opacity: 0.8; }
#nav-trace i:empty { display: none; }
/* коли слід увімкнено, головний екран і шапка гри посуваються, щоб він нічого не накривав */
:root.tracing .home-bar, :root.tracing .game-head { margin-top: 26px; }

/* ---------- Sheets ---------- */
.scrim {
  position: fixed; inset: 0; background: var(--scrim);
  opacity: 0; pointer-events: none; transition: opacity 0.22s;
  z-index: 10;
}
.scrim.open { opacity: 1; pointer-events: auto; }
.sheet {
  position: fixed; left: 0; right: 0; bottom: 0;
  margin: 0 auto;
  max-width: 560px;
  max-height: calc(100% - var(--safe-t) - 24px);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  background: var(--sheet);
  border-radius: 22px 22px 0 0;
  box-shadow: var(--shadow);
  padding: 10px calc(var(--safe-r) + 20px) calc(var(--safe-b) + 22px) calc(var(--safe-l) + 20px);
  transform: translateY(105%);
  transition: transform 0.28s cubic-bezier(0.2, 0.8, 0.2, 1);
  z-index: 11;
  -webkit-user-select: text; user-select: text;
}
.sheet.open { transform: translateY(0); }
.grabber { width: 40px; height: 5px; border-radius: 3px; background: var(--line); margin: 0 auto 12px; }
.sheet h2 {
  font-family: var(--f-display); font-weight: 700; font-size: 22px;
  letter-spacing: -0.01em; margin: 4px 0 4px; text-wrap: balance;
}
.sheet .lead { color: var(--ink-soft); margin: 0 0 18px; line-height: 1.45; }
.sheet h3 {
  font-size: 11px; font-weight: 600; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--ink-faint); margin: 20px 0 8px;
}
.sheet-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.close { margin: -4px -10px 0 0; }

.menu-list { display: grid; gap: 2px; margin-top: 8px; }
.menu-item {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; padding: 14px 4px;
  border: 0; border-bottom: 1px solid var(--line); background: transparent;
  text-align: left; font-size: 17px; cursor: pointer;
}
.menu-item:last-child { border-bottom: 0; }
.menu-item small { color: var(--ink-faint); font-size: 13px; }
.menu-item:disabled { color: var(--ink-faint); cursor: default; }
.menu-item .chev { color: var(--ink-faint); }

/*
 * THE MENU OUTGREW THE SHEET (0.1.33).
 *
 * Ten items now, one added at a time since 0.1.16, and nobody measured the total. On a 375×667
 * phone the list asks for 720 px and the sheet has 643: the last two — «Встановити на iPhone»
 * and «Перевірити оновлення» — sat below the fold. The sheet does scroll, so they were reachable,
 * but nothing said so, and inside a bottom sheet on iOS a drag is just as likely to be read as
 * "close me". The item a player needs to move to a new build was the one that fell off.
 *
 * Two changes, both narrow:
 *   · the LIST scrolls, not the sheet — the title and the close button stay put, the scroll has
 *     its own momentum, and it no longer competes with the sheet's dismiss gesture;
 *   · on a short screen the rows get tighter, so on the phones the app is actually built for the
 *     list needs no scrolling at all. Measured: 720 → 618 px on 375×667, against 643 available.
 */
#sheet-menu { display: flex; flex-direction: column; }
#sheet-menu .menu-list {
  overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
  min-height: 0;
}
@media (max-height: 740px) {
  #sheet-menu .lead { margin-bottom: 8px; }
  #sheet-menu .menu-item { padding: 10px 4px; font-size: 16px; }
}

.seg {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  gap: 4px; padding: 4px; border-radius: 14px; background: var(--paper-deep);
}
.seg button {
  border: 0; background: transparent; border-radius: 10px;
  padding: 10px 6px; font-size: 14px; font-weight: 500; color: var(--ink-soft);
  cursor: pointer; min-height: 40px;
  display: flex; align-items: center; justify-content: center; gap: 7px;
}
.seg button[aria-pressed="true"] { background: var(--sheet); color: var(--ink); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12); }
.seg .sw { width: 11px; height: 11px; border-radius: 50%; }
.sw.blue { background: var(--blue); }
.sw.red { background: var(--red); }

.levels { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; }
.level {
  display: grid; justify-items: center; gap: 6px;
  padding: 10px 2px 9px;
  border: 1px solid var(--line); border-radius: 12px;
  background: transparent; cursor: pointer;
}
.level .pips { display: flex; gap: 3px; height: 8px; align-items: center; }
.level .pips i { width: 5px; height: 5px; border-radius: 50%; background: var(--grid-edge); }
.level .pips i.on { background: var(--ink); }
.level .num { font-family: var(--f-display); font-weight: 700; font-size: 18px; line-height: 1; }
.level .lname { font-size: 10.5px; color: var(--ink-soft); letter-spacing: 0.01em; }
.level .lrating { font-family: var(--f-mono); font-size: 10.5px; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.level[aria-pressed="true"] .lrating { color: var(--ink); }
.level[aria-pressed="true"] { border-color: var(--ink); background: var(--paper-deep); }
.level-note { font-size: 13px; color: var(--ink-soft); margin: 10px 0 0; line-height: 1.45; }

.primary {
  width: 100%; margin-top: 24px; height: 52px;
  border: 0; border-radius: 14px;
  background: var(--ink); color: var(--paper);
  font-size: 16px; font-weight: 600; cursor: pointer;
}
.secondary {
  width: 100%; margin-top: 10px; height: 48px;
  border: 1px solid var(--line); border-radius: 14px;
  background: transparent; color: var(--ink);
  font-size: 15px; font-weight: 500; cursor: pointer;
}

/* an action that throws something away: outlined, never filled — it must not read as the thing
 * you press by default (0.1.25) */
.secondary.danger { color: var(--red); border-color: color-mix(in srgb, var(--red) 34%, transparent); }
.secondary.danger:active { background: color-mix(in srgb, var(--red) 8%, transparent); }
.secondary:disabled { opacity: 0.4; cursor: default; }

/* 0.1.83: the old address says where the game went — only in the build deployed to Netlify */
/* a fourth row in the home grid (bar · banner · hero · modes); the hero gives up its height first */
#home.moved-on { grid-template-rows: auto auto minmax(0, 1fr) auto; }
#home.moved-on .hero { overflow: hidden; }
#home.moved-on .mark, #home.moved-on .hero .glow { display: none; }   /* the banner takes the mark's place */
/* 0.1.83b: on a short window the banner takes the title's place too — a half-clipped «Крапки» under it said nothing */
@media (max-height: 760px) {
  #home.moved-on { grid-template-rows: auto auto minmax(min-content, 1fr); overflow-y: auto; }
  #home.moved-on .hero { display: none; }
  #home.moved-on .modes { align-self: end; }
}
.moved {
  margin: 4px 16px 8px; padding: 14px 16px; border-radius: 16px;
  background: var(--sheet); border: 1px solid var(--line); color: var(--ink);
  font-size: 14px; line-height: 1.4;
}
.moved strong { display: block; font-size: 16px; margin-bottom: 6px; }
.moved p { margin: 0 0 6px; color: var(--ink-soft); }
.moved .moved-note { font-size: 13px; margin: 10px 0 4px; }
.moved .moved-steps { margin: 0; padding-left: 18px; font-size: 13px; color: var(--ink-soft); }
.moved .moved-steps li { margin: 2px 0; }
.moved-actions { display: flex; gap: 10px; margin-top: 4px; }
.moved-actions .primary, .moved-actions .secondary {
  flex: 1; margin: 0; height: 44px; font-size: 14px;
  display: inline-flex; align-items: center; justify-content: center; text-align: center; text-decoration: none;
}

.row {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 12px 0; border-bottom: 1px solid var(--line);
}
.row:last-child { border-bottom: 0; }
.row label { display: grid; gap: 3px; }
.row label small { color: var(--ink-faint); font-size: 13px; line-height: 1.35; }
.row .seg { min-width: 210px; }
.switch { position: relative; width: 51px; height: 31px; flex: none; }
.switch input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; z-index: 1; }
.switch span { position: absolute; inset: 0; border-radius: 16px; background: var(--grid-edge); transition: background 0.2s; }
.switch span::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 27px; height: 27px;
  border-radius: 50%; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); transition: transform 0.2s;
}
.switch input:checked + span { background: #34a853; }
.switch input:checked + span::after { transform: translateX(20px); }
.switch input:focus-visible + span { outline: 2px solid var(--blue); outline-offset: 2px; }
@media (max-width: 420px) { .row { flex-wrap: wrap; } .row .seg { min-width: 0; width: 100%; } }

.rules { line-height: 1.55; color: var(--ink); max-width: 62ch; }
.rules p { margin: 0 0 10px; }
.rules ol { margin: 0; padding-left: 20px; display: grid; gap: 8px; }
.rules b { font-weight: 600; }
.rules .coord { font-family: var(--f-mono); font-size: 13px; background: var(--paper-deep); padding: 1px 5px; border-radius: 5px; }

.result { text-align: center; padding: 8px 0 0; }
.result .big {
  font-family: var(--f-display); font-weight: 700;
  font-size: 56px; letter-spacing: -0.03em; line-height: 1; margin: 12px 0 6px;
  font-variant-numeric: tabular-nums;
}
.result .big .b { color: var(--blue); }
.result .big .r { color: var(--red); }
.result .big .c { color: var(--ink-faint); padding: 0 6px; }
.result h2 { font-size: 24px; }
.result p { color: var(--ink-soft); margin: 6px auto 0; max-width: 40ch; line-height: 1.45; }

.hint { font-size: 13px; color: var(--ink-soft); line-height: 1.45; margin: 14px 0 0; }
.badge { font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-faint); border: 1px solid var(--line); border-radius: 6px; padding: 2px 6px; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}
@media (max-width: 440px) {
  .levels { gap: 4px; }
  .level { padding: 9px 1px 8px; }
  .level .lname { font-size: 9.5px; letter-spacing: 0; }
}

/* ------------------------------------------------------------ championship (0.1.16) */
/* Two tokens the rest of the sheet never needed: a surface for pickable cards and a wash for the
   row that is yours. Derived from the existing palette so both themes follow automatically. */
:root { --card: var(--sheet); --press: rgba(125, 140, 160, 0.10); }
/* 0.1.22: the sheet the board is drawn on. The notebook skin keeps the page and the field the same
   colour, because there the whole screen IS the page; Air lifts the board out of the tinted field,
   which is what puts it inside its frame rather than behind it. */
:root { --board-bg: var(--paper); }
:root[data-skin="air"] { --board-bg: var(--sheet); }
/* 0.1.20: the same fix as the palette above — `:not([data-theme="light"])` alone matches the
   default page, so it belongs inside the media query. */
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --press: rgba(160, 175, 195, 0.12); } }
:root[data-theme="dark"] { --press: rgba(160, 175, 195, 0.12); }
.sheet.tall { max-height: 88vh; }

/*
 * A PANE IS A PLACE (0.1.55).
 *
 * A sheet is right for what you glance at and dismiss: a confirmation, a picker, the settings. The
 * championship is not that — five tabs, long tables, its own navigation, minutes at a time. So it
 * stops being a card lying on the home screen and becomes a screen: full height, square top, no
 * grabber, and its title row pinned above the content instead of scrolling away with it.
 *
 * `top: 0` with `height: 100%` rather than a `max-height`: a pane that stopped short of the top
 * edge would still show a strip of whatever is behind it, which is the very thing that makes a
 * sheet read as temporary.
 */
.sheet.pane {
  /* `top` and `bottom` rather than a height: `height: 100%` resolves against the containing block,
     and a transformed ancestor quietly made that 750 px of an 852 px screen — the home screen was
     seen through the last eighty pixels of the window. Two anchors cannot be argued with. */
  top: 0; bottom: 0; height: auto; max-height: none;
  border-radius: 0;
  padding-top: 0;
}
.sheet.pane .grabber { display: none; }
/*
 * The title row and the tabs are ONE pinned block, so the way out of the screen is always under
 * the thumb. Pinning them separately would need the height of the first as the offset of the
 * second — a number that changes with the notch — so they are pinned together instead.
 */
.pane-top {
  position: sticky; top: 0; z-index: 4;
  background: var(--sheet);
  padding-top: calc(var(--safe-t) + 12px);
  /* 0.1.82 (iPhone, Слава 02.10): the block is as wide as the SCREEN, not as the sheet's content.
   * It used to stop at the sheet's 20 px side padding, and the list scrolling underneath showed
   * through the two gutters beside the title — grey strips left and right of «Grupy» and the ×.
   * The tabs inside keep their own −20 px reach, which now ends exactly at the screen's edges. */
  margin-left: calc(-1 * (var(--safe-l) + 20px));
  margin-right: calc(-1 * (var(--safe-r) + 20px));
  padding-left: calc(var(--safe-l) + 20px);
  padding-right: calc(var(--safe-r) + 20px);
}
.head-actions { display: flex; gap: 6px; align-items: center; }

/* ---------- the championship's own navigation (0.1.24) ----------
 * Five destinations, at the top, pinned while the body scrolls. The sheet is the scroll
 * container, so `sticky` here sticks to the sheet's top edge and the title scrolls away above it.
 */
/* the sheet's own top padding would leave a transparent strip above a `top: 0` sticky row, and
 * the list would be seen scrolling through it — so this one sheet pads from the grabber instead */
/* inside `.pane-top` the tabs travel with the title; the sticky here still serves the paper
   sheet, where the title scrolls away and the tabs alone stay */
.ch-tabs {
  position: sticky; top: 0; z-index: 3;
  display: flex; gap: 1px;
  margin: 4px calc(-1 * (var(--safe-r) + 20px)) 10px calc(-1 * (var(--safe-l) + 20px));
  padding: 6px calc(var(--safe-r) + 14px) 6px calc(var(--safe-l) + 14px);
  background: var(--sheet);
  box-shadow: 0 1px 0 var(--line);
  overflow-x: auto; scrollbar-width: none;
}
.ch-tabs::-webkit-scrollbar { display: none; }
.ch-tabs button {
  flex: 1 1 auto; min-height: 34px; padding: 0 8px;
  border: 0; border-radius: 999px; background: transparent;
  color: var(--ink-soft); font: inherit; font-size: 12.5px; font-weight: 500;
  white-space: nowrap; cursor: pointer;
}
.ch-tabs button[aria-selected="true"] {
  background: var(--resume-bg); color: var(--ink); font-weight: 600;
}
.ch-tabs button:active { opacity: 0.7; }

/* team picker */

/* fixtures and ties */

/* the away side reads right-to-left: name, then flag, so the two flags frame the score */

/* 0.1.23: a row is a way into the match it names — every one of the 104, the simulated ones
 * included. It has to look like something you can press. */

/* ---------- the knockout as a tree (0.1.25) ----------
 * Five columns, left to right, to the final. The alignment is not hand-tuned: every column is the
 * same height and every tie takes an equal share of it, so a column of eight sits exactly at the
 * midpoints of a column of sixteen. The connectors are half-height borders on alternate slots,
 * which meet in the middle for the same reason.
 */
/*
 * 0.1.50: THE TREE SCROLLS SIDEWAYS ONLY.
 *
 * It used to scroll both ways inside a `max-height`, and the play-test found what that costs:
 * measured on a 393×852 screen, the tree's own box took 597 of the sheet's 750 px, so four fifths
 * of the screen belonged to the INNER scroller. The third-place match sits under the tree, one
 * pixel below the window edge (top 838 of 852), and the 72 px of sheet scroll that would bring it
 * up could only be started from the 26 px strip below the tree. That is why it appeared once and
 * could not be made to appear again — «я так і не зрозумів що я зробив».
 *
 * A bracket is wide, not tall: the horizontal axis is the one it needs. Give the vertical axis
 * back to the sheet and there is ONE vertical scroller on the screen, so a drag anywhere reaches
 * the bottom of the page. `overflow-y: hidden` clips nothing here because the height is no longer
 * capped — the box is exactly as tall as the tree.
 */

/* the stub out of every tie towards its pair's line */

/* half a vertical line each: odd slots downwards, even slots upwards — together, one bracket */

/* and the stub into the tie the pair feeds */

/* progress */

/* a back row at the TOP of a screen you drilled into — reached without scrolling past the
 * screen's whole content, which is the point of it (0.1.24, used by the group screen in 0.1.26) */

/*
 * 0.1.39: THE BACK ROW HAD TO BE FOUND BEFORE IT COULD BE PRESSED. It was a `secondary` button —
 * the same quiet grey as «Очистити журнал» — on a screen a person reaches by drilling in and
 * leaves immediately. The play-test called it «дуже непомітна», which is the right word for a
 * control that is the whole reason the screen has a top.
 *
 * It keeps its size (it must not compete with the content) and gains the one thing it lacked:
 * contrast, and a chevron that says which way it goes.
 */

/* a group heading that is a door (0.1.26) */
.group-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; margin: 20px 0 8px; padding: 0;
  border: 0; background: transparent; color: var(--ink-faint);
  font: inherit; font-size: 11px; font-weight: 600; letter-spacing: 0.09em;
  text-transform: uppercase; text-align: left; cursor: pointer;
}
.group-head .chev { font-style: normal; font-size: 18px; letter-spacing: 0; opacity: 0.6; }
.group-head:active { opacity: 0.6; }

/* form chip in a group table (0.1.18) */

/*
 * 0.1.38: the summary door on a bracket card. A corner mark rather than a chip — the card is
 * 100-odd pixels wide and a word does not fit — with a hit area grown by a pseudo-element, the
 * same trick the form mark uses, so the tree's geometry is untouched.
 */

/* ------------------------------------------------------------ the scouting report (0.1.38) */

/* the one number the screen exists for: what this opponent will actually play at */

/* the steps of the number, between the box and the table — with room, or the table's own top
 * border draws straight through the second line of it */

/* the last few results, as a strip of letters rather than a sentence */

/* a slot in the bracket or the calendar that has no team in it yet */
.tbd { font-style: normal; color: var(--ink-faint); opacity: .7; }

/* ============================================================ home screen (0.1.20)
 *
 * THE DESIGN CONTRACT.
 * ------------------------------------------------------------
 * The visual design for this screen comes from a prototype (Dots_Air_Prototype_v5), and more
 * prototypes will follow — one per screen. So the point of this block is not "the home screen's
 * CSS": it is a NAMED SET OF ROLES that every future prototype can be mapped onto mechanically,
 * instead of each new screen arriving with its own hex codes and quietly making the app into five
 * apps. A prototype's token → the role it fills here:
 *
 *   --bg          → --paper          the screen behind everything
 *   --bg-deep     → --paper-deep     the shade under the screen (rails, segmented track)
 *   --panel       → --panel          a translucent raised surface (the brand mark, the toast)
 *   --panel-solid → --sheet          an opaque raised surface (sheets, cards)
 *   --control     → --control        a translucent surface a finger presses (mode buttons)
 *   --text        → --ink
 *   --muted       → --ink-soft
 *   --faint       → --faint          hairline text and inactive marks
 *   --line        → --line
 *   --blue/--red  → --blue/--red     the two players; never decoration, always a side
 *   --shadow-*    → the three-step elevation scale below
 *   --r-*         → the three-step radius scale below
 *
 * WHICH PALETTE. The prototype is Apple-system grey-and-#1769ff; this app is notebook paper,
 * ballpoint blue and red pen, and the game screen — which 0.1.20 does not touch — is built on it.
 * A home screen in the prototype's palette would have made the first tap a change of app. So the
 * home screen takes the prototype's STRUCTURE, MOTION and COMPONENT PATTERNS, and wears the
 * game's own colours and typefaces.
 * The prototype's palette is kept, whole, as `data-skin="air"` — one attribute on <html>, a switch
 * in Settings. It is there so the choice can be made by eye on a real screen rather than argued
 * about in a report, and because it costs one block of variables to keep.
 */
:root {
  --panel: rgba(251, 252, 250, 0.82);
  --control: rgba(251, 252, 250, 0.76);
  --faint: var(--ink-faint);
  /*
   * A CARD FOR SOMETHING ALREADY UNDER WAY (0.1.20; re-coloured in 0.1.23).
   * 0.1.20 filled it with `--ink`: a black slab, which did the job of catching the eye and was
   * heavier than anything else on a screen whose whole point is calm. 0.1.23 makes it a raised
   * grey — still clearly filled rather than outlined, still the first thing the eye lands on,
   * but a shade of the page instead of its opposite. The text stays `--ink`, so contrast is the
   * page's own and does not have to be re-checked per skin.
   */
  --resume-bg: #dfe3e8;
  --resume-tag: rgba(31, 40, 51, 0.10);
  --shadow-1: 0 1px 3px rgba(31, 40, 51, 0.08);
  --shadow-2: 0 8px 22px rgba(31, 40, 51, 0.07), inset 0 0 0 1px var(--line);
  --shadow-3: 0 18px 48px rgba(31, 40, 51, 0.12);
  --r-1: 12px;
  --r-2: 18px;
  --r-3: 26px;
  /* 0.1.58 · чемпіонат (Д-01, Р-60): у зошиті сім токенів — посилання на наявні, тож зошит лишається
     зошитом. Air дає власні значення нижче. */
  --ch-clear: transparent; --ch-tint: var(--paper); --ch-glass: var(--sheet); --ch-edge: var(--line);
  --ch-selected: var(--resume-bg); --ch-action: var(--ink); --ch-shadow: var(--shadow-2);
}
/* The dark values go in twice, exactly as the palette above does it: once behind the media query
 * for "the system is dark and the player has not overridden it", and once for the explicit
 * choice. `:root:not([data-theme="light"])` on its own matches a page with no attribute at all —
 * which is the default — so outside the media query it would make every screen dark. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --panel: rgba(26, 32, 40, 0.84);
    --control: rgba(26, 32, 40, 0.78);
    --resume-bg: #38465a;
    --resume-tag: rgba(255, 255, 255, 0.16);
    --shadow-1: 0 1px 3px rgba(0, 0, 0, 0.4);
    --shadow-2: 0 8px 22px rgba(0, 0, 0, 0.28), inset 0 0 0 1px var(--line);
    --shadow-3: 0 18px 48px rgba(0, 0, 0, 0.45);
    --ch-clear: transparent; --ch-tint: var(--paper); --ch-glass: var(--sheet); --ch-edge: var(--line);
    --ch-selected: var(--resume-bg); --ch-action: var(--ink); --ch-shadow: var(--shadow-2);
  }
}
:root[data-theme="dark"] {
  --panel: rgba(26, 32, 40, 0.84);
  --control: rgba(26, 32, 40, 0.78);
  --resume-bg: #38465a;
  --resume-tag: rgba(255, 255, 255, 0.16);
  --shadow-1: 0 1px 3px rgba(0, 0, 0, 0.4);
  --shadow-2: 0 8px 22px rgba(0, 0, 0, 0.28), inset 0 0 0 1px var(--line);
  --shadow-3: 0 18px 48px rgba(0, 0, 0, 0.45);
  --ch-clear: transparent; --ch-tint: var(--paper); --ch-glass: var(--sheet); --ch-edge: var(--line);
  --ch-selected: var(--resume-bg); --ch-action: var(--ink); --ch-shadow: var(--shadow-2);
}
/* the prototype's own palette, kept whole so it can be compared on a real screen */
:root[data-skin="air"] {
  --paper: #f5f5f7; --paper-deep: #e9ebef; --sheet: #ffffff;
  --ink: #1d1d1f; --ink-soft: #6e6e73; --ink-faint: #98989d;
  --line: rgba(29, 29, 31, 0.10);
  --blue: #1769ff; --red: #ff453a; --ok: #23a45e;
  /* 0.1.28: the notebook's red margin rule belongs to the notebook. In Air the board sits in a
     glass frame on a white sheet, and the rule read as a stray red stripe down the right of the
     screen — reported from the play-test on the "Усе поле" scale, where it is most visible. */
  --margin: transparent;
  --panel: rgba(255, 255, 255, 0.84); --control: rgba(255, 255, 255, 0.80);
  --resume-bg: #e3e4e9; --resume-tag: rgba(29, 29, 31, 0.10);
  --scrim: rgba(20, 20, 24, 0.30);
  --f-display: -apple-system, BlinkMacSystemFont, "SF Pro Display", system-ui, sans-serif;
  --f-ui: -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;
  /* 0.1.58 · чемпіонат (Д-01): значення макета, світла тема */
  --ch-clear: transparent; --ch-tint: #eef2f8; --ch-glass: rgba(255, 255, 255, 0.86);
  --ch-edge: rgba(255, 255, 255, 0.95); --ch-selected: #e7ecf3; --ch-action: #20252d;
  --ch-shadow: 0 9px 24px rgba(47, 66, 99, 0.065), inset 0 1px 0 rgba(255, 255, 255, 0.9);
}
@media (prefers-color-scheme: dark) {
  :root[data-skin="air"]:not([data-theme="light"]) {
    --paper: #000000; --paper-deep: #101114; --sheet: #1c1c1e;
    --ink: #f5f5f7; --ink-soft: #a1a1a6; --ink-faint: #737378;
    --line: rgba(255, 255, 255, 0.12);
    --blue: #5d8cff; --red: #ff6961; --ok: #4fd18b;
    --margin: transparent;
    --panel: rgba(28, 28, 30, 0.86); --control: rgba(28, 28, 30, 0.82);
    --resume-bg: #3a3a3c; --resume-tag: rgba(255, 255, 255, 0.18);
    --scrim: rgba(0, 0, 0, 0.58);
    --ch-clear: transparent; --ch-tint: #171a21; --ch-glass: rgba(45, 47, 54, 0.92);
    --ch-edge: rgba(255, 255, 255, 0.14); --ch-selected: #323742; --ch-action: #dbe2ed;
    --ch-shadow: 0 9px 24px rgba(0, 0, 0, 0.20), inset 0 1px 0 rgba(255, 255, 255, 0.07);
  }
}
:root[data-skin="air"][data-theme="dark"] {
  --paper: #000000; --paper-deep: #101114; --sheet: #1c1c1e;
  --ink: #f5f5f7; --ink-soft: #a1a1a6; --ink-faint: #737378;
  --line: rgba(255, 255, 255, 0.12);
  --blue: #5d8cff; --red: #ff6961; --ok: #4fd18b;
  --margin: transparent;
  --panel: rgba(28, 28, 30, 0.86); --control: rgba(28, 28, 30, 0.82);
  --resume-bg: #3a3a3c; --resume-tag: rgba(255, 255, 255, 0.18);
  --scrim: rgba(0, 0, 0, 0.58);
  --ch-clear: transparent; --ch-tint: #171a21; --ch-glass: rgba(45, 47, 54, 0.92);
  --ch-edge: rgba(255, 255, 255, 0.14); --ch-selected: #323742; --ch-action: #dbe2ed;
  --ch-shadow: 0 9px 24px rgba(0, 0, 0, 0.20), inset 0 1px 0 rgba(255, 255, 255, 0.07);
}

/* ---------- screens ----------
 * Two screens now share the window: the home screen and the game. Both are absolutely positioned
 * and cross-fade, so the board keeps its size across the transition and does not re-layout. */
.screen {
  position: absolute; inset: 0;
  opacity: 0; pointer-events: none;
  transform: translateY(14px) scale(0.988);
  transition: opacity 0.30s cubic-bezier(0.2, 0.75, 0.25, 1), transform 0.40s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.screen.on { opacity: 1; pointer-events: auto; transform: none; }
.screen.leaving { opacity: 0; transform: scale(0.972); }

#home {
  display: grid;
  grid-template-rows: auto 1fr auto;
  background: var(--paper);
  overflow: hidden;
}

.home-bar {
  display: grid; grid-template-columns: 44px 1fr 44px; align-items: center;
  padding: calc(var(--safe-t) + 6px) calc(var(--safe-r) + 10px) 0 calc(var(--safe-l) + 10px);
}
.home-bar .icon-btn { grid-column: 3; }

.hero {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-height: 0;
  padding: 0 24px clamp(14px, 3.5vh, 32px);
  text-align: center;
}
/* the wash behind the mark: the blue of the game, at 12%, once */
.hero .glow {
  position: absolute; top: 46%; left: 50%;
  width: min(86vw, 380px); aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, color-mix(in srgb, var(--blue) 12%, transparent), transparent 68%);
  filter: blur(10px);
  opacity: 0; pointer-events: none;
  animation: glow-in 0.9s 0.2s ease forwards;
}

/* THE MARK. Five by five nodes with the four dots of the starting cross in place — the position
 * every game begins from, which makes the logo the rules rather than a decoration. */
.mark {
  position: relative;
  display: grid; place-items: center;
  width: clamp(104px, 29vw, 124px); aspect-ratio: 1;
  border: 1px solid var(--line); border-radius: 31%;
  background: var(--panel);
  box-shadow: var(--shadow-3), inset 0 1px rgba(255, 255, 255, 0.4);
  backdrop-filter: blur(24px) saturate(130%);
  -webkit-backdrop-filter: blur(24px) saturate(130%);
  opacity: 0; transform: translateY(10px) scale(0.93);
  animation: mark-in 0.7s 0.09s cubic-bezier(0.2, 0.8, 0.2, 1) forwards;
}
.mark .grid5 {
  display: grid;
  grid-template-columns: repeat(5, 7px); grid-template-rows: repeat(5, 7px);
  gap: clamp(6px, 1.9vw, 8px); place-content: center;
}
.mark i {
  width: 7px; height: 7px; border-radius: 50%;
  background: color-mix(in srgb, var(--faint) 52%, transparent);
  opacity: 0; transform: scale(0.15);
  animation: dot-in 0.34s cubic-bezier(0.2, 0.9, 0.25, 1.25) forwards;
}
/* the corners first, then inwards — the same ripple the prototype used */
.mark i:nth-child(1), .mark i:nth-child(5), .mark i:nth-child(21), .mark i:nth-child(25) { animation-delay: 0.21s; }
.mark i:nth-child(2), .mark i:nth-child(4), .mark i:nth-child(6), .mark i:nth-child(10),
.mark i:nth-child(16), .mark i:nth-child(20), .mark i:nth-child(22), .mark i:nth-child(24) { animation-delay: 0.28s; }
.mark i:nth-child(3), .mark i:nth-child(7), .mark i:nth-child(9), .mark i:nth-child(11),
.mark i:nth-child(15), .mark i:nth-child(17), .mark i:nth-child(19), .mark i:nth-child(23) { animation-delay: 0.35s; }
.mark i:nth-child(8), .mark i:nth-child(12), .mark i:nth-child(14), .mark i:nth-child(18) { animation-delay: 0.43s; }
.mark i:nth-child(13) { animation-delay: 0.51s; }
.mark i.b { background: var(--blue); }
.mark i.r { background: var(--red); }

.brand { position: relative; opacity: 0; transform: translateY(12px); animation: copy-in 0.6s 0.62s cubic-bezier(0.2, 0.8, 0.2, 1) forwards; }
.brand h1 {
  font-family: var(--f-display); font-weight: 700;
  margin: clamp(20px, 3.6vh, 32px) 0 8px;
  font-size: clamp(38px, 12vw, 52px); line-height: 0.95; letter-spacing: -0.045em;
}
.brand p { margin: 0; color: var(--ink-soft); font-size: 15px; line-height: 1.4; }

/* ---------- the mode list ---------- */
.modes {
  display: grid; gap: 8px;
  padding: 0 calc(var(--safe-r) + 20px) calc(var(--safe-b) + 18px) calc(var(--safe-l) + 20px);
  opacity: 0; transform: translateY(16px);
  animation: modes-in 0.65s 0.78s cubic-bezier(0.2, 0.8, 0.2, 1) forwards;
}
.mode {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; min-height: 64px;
  padding: 11px 14px 11px 18px;
  border: 0; border-radius: var(--r-2);
  color: var(--ink); background: var(--control);
  box-shadow: var(--shadow-2);
  backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
  text-align: left; cursor: pointer;
  transition: transform 0.13s ease, opacity 0.13s ease;
}
.mode:active { transform: scale(0.975); opacity: 0.8; }
/* A GAME IN PROGRESS IS THE ONE THING ON THIS SCREEN THAT IS NOT AN OFFER. It is filled, not
 * outlined, so the eye lands on it before it reads anything — a raised grey from 0.1.23, where it
 * was a black slab before; see --resume-bg. */
.mode.resume {
  color: var(--ink); background: var(--resume-bg);
  box-shadow: 0 8px 20px color-mix(in srgb, var(--ink) 9%, transparent),
    inset 0 0 0 1px color-mix(in srgb, var(--ink) 6%, transparent);
  backdrop-filter: none; -webkit-backdrop-filter: none;
}
.mode .copy { display: grid; gap: 4px; flex: 1; min-width: 0; }
.mode .copy strong { font-size: 16px; line-height: 1.15; font-weight: 600; }
/* 0.1.86 (П1): «бета» beside «Грати з другом» — a quiet label, not a button */
.mode .copy strong .beta { display: inline-block; vertical-align: 2px; margin-left: 4px; font-size: 10px; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--ink-faint); border: 1px solid var(--line); border-radius: 6px; padding: 1px 5px; line-height: 1.3; }
.mode .copy small { font-size: 12px; line-height: 1.25; opacity: 0.66; }
.mode .chev { flex: none; padding: 0 3px; font-size: 26px; line-height: 1; font-weight: 300; opacity: 0.42; }
.mode .tag {
  flex: none; padding: 5px 8px; border-radius: 999px;
  font-size: 10.5px; line-height: 1; font-weight: 600; letter-spacing: 0.02em;
  color: var(--ink-soft); background: color-mix(in srgb, var(--faint) 14%, transparent);
}
.mode.resume .tag { color: var(--ink); background: var(--resume-tag); }
/* the second action on a card that resumes something: the tables, when the card itself resumes
 * the match. Small, quiet, and never the thing a thumb hits by accident. */
.mode .mini {
  flex: none; min-height: 30px; padding: 6px 11px;
  border: 0; border-radius: 999px;
  font: inherit; font-size: 12px; font-weight: 600;
  color: var(--ink); background: var(--resume-tag); cursor: pointer;
}
.mode .mini:active { opacity: 0.7; }
.mode.soon { background: color-mix(in srgb, var(--control) 62%, transparent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--line) 70%, transparent); }
.mode.soon .copy { opacity: 0.6; }
.modes-head { margin: 8px 0 2px; text-align: center; font-size: 15px; font-weight: 600; color: var(--ink); }
.text-btn {
  justify-self: center; min-height: 38px; padding: 8px 14px;
  border: 0; border-radius: var(--r-1); background: transparent;
  color: var(--ink-soft); font-size: 14px; font-weight: 500; cursor: pointer;
}
.text-btn:active { opacity: 0.6; }

@keyframes mark-in { to { opacity: 1; transform: none; } }
@keyframes dot-in { to { opacity: 1; transform: scale(1); } }
@keyframes copy-in { to { opacity: 1; transform: none; } }
@keyframes modes-in { to { opacity: 1; transform: none; } }
@keyframes glow-in { to { opacity: 0.9; } }

/* the switch in Settings: one class, and nothing on the screen animates in */
.still .mark, .still .brand, .still .modes, .still .glow, .still .mark i { animation: none !important; opacity: 1 !important; transform: none !important; }
.still .screen { transition: none !important; }

@media (max-height: 700px) {
  .mark { width: 92px; }
  .hero { padding-bottom: 10px; }
  .brand h1 { margin-top: 16px; font-size: 36px; }
  .brand p { font-size: 13.5px; }
  .modes { gap: 6px; padding-bottom: calc(var(--safe-b) + 10px); }
  .mode { min-height: 56px; padding-block: 8px; }
  .mode .copy { gap: 2px; }
  .mode .copy strong { font-size: 15px; }
  .mode .copy small { font-size: 11px; line-height: 1.15; }
  .text-btn { min-height: 34px; padding-block: 6px; }
}

/* A phone-shaped window on a tablet or a desktop: the layout is designed for one hand, and
 * stretched across 1400 px it stops being a design. */
/* ============================================================ BIGGER SCREENS (0.1.25)
 *
 * 0.1.20 gave anything wider than 620px a fixed 440×920 phone-shaped window in the middle of the
 * screen. On a desktop that was a reasonable way to preview a phone app. On an iPad it was the
 * wrong answer twice over: the game ignored most of the glass, and it ignored the rotation — the
 * same little rectangle held upright and on its side.
 *
 * The rule now is the one that should have been there from the start: the app fills the screen it
 * is on, whatever that screen is, and it re-lays itself out when the screen changes shape.
 *   · portrait, any size → the phone layout, with everything given more room
 *   · landscape and wide → the dock moves BESIDE the board instead of under it, because a board
 *     that is short and wide is the one thing this game cannot use. The board keeps the height.
 * The canvas follows automatically: a ResizeObserver on the frame already drives resize(), and
 * 0.1.25 makes that call re-fit the view when the frame's shape changes rather than only its size.
 */
@media (min-width: 700px) {
  .sheet { max-width: 620px; border-radius: var(--r-3) var(--r-3) 0 0; }
  /* the home screen keeps a readable column instead of stretching its cards to a metre wide */
  .modes { max-width: 560px; margin-inline: auto; width: 100%; }
  .modes-head, .text-btn { justify-self: center; }
  /* 0.1.83b: sized by the HEIGHT too. A wide but short window (a laptop at 150% scaling: 1280 × 574 CSS px,
   * Slava's Chrome 03.10) took the desktop sizes from this block — it comes after the max-height: 700px one and
   * wins — and the 86 px title pushed the tagline under the first card. min(…vw, …vh) leaves tall windows as
   * they were (the vw term is the smaller there) and shrinks the hero only where the height is short. */
  .brand h1 { font-size: clamp(36px, min(7vw, 9.5vh), 86px); }
  .mark { width: clamp(84px, min(15vw, 18vh), 168px); }
  .mark .grid5 { grid-template-columns: repeat(5, 9px); grid-template-rows: repeat(5, 9px); }
  .mark i { width: 9px; height: 9px; }
  .board-stage { padding: 4px clamp(10px, 3vw, 40px) 8px; }
  .score-pill { min-width: 240px; height: 54px; }
  .match-caption { font-size: 12.5px; }
}
/* 0.1.83b: wide and very short (a laptop window at 150%, a championship card on the home screen) — the hero gives up
 * the most: a smaller mark, a smaller title, less air between them */
@media (min-width: 700px) and (max-height: 620px) {
  .mark { width: 76px; }
  .mark .grid5 { grid-template-columns: repeat(5, 7px); grid-template-rows: repeat(5, 7px); }
  .mark i { width: 7px; height: 7px; }
  .brand h1 { margin-top: 10px; font-size: 42px; }
}
/* 0.1.86 (П1, задача 5 — поворот): an iPhone on its side is 852 × 393 (667 × 375 for an SE). Four cards and the links
 * take ~350 px of that, and the mark and the 36 px title drew UNDER the first card (Chromium, 852 × 393). Below 480 px
 * of height the hero keeps only the name, small; the mark, the wash and the tagline step aside. */
@media (max-height: 480px) {
  .hero .mark, .hero .glow, #home-tagline { display: none; }
  .hero { padding-bottom: 4px; }
  .brand h1 { margin-top: 0; font-size: 22px; }
  .modes { gap: 5px; padding-bottom: calc(var(--safe-b) + 6px); }
  .mode { min-height: 50px; padding-block: 6px; }
  .text-btn { min-height: 30px; padding-block: 4px; }
}

/*
 * LANDSCAPE, AND WIDE ENOUGH TO SPARE THE WIDTH. The dock becomes a column on the right: the
 * turn card above, the buttons below it, and the board takes the whole height of the window.
 * 860px is where a 260px column still leaves the board more width than a phone has.
 */
@media (min-width: 860px) and (orientation: landscape) {
  #app {
    grid-template-columns: minmax(0, 1fr) clamp(240px, 24vw, 320px);
    grid-template-rows: auto auto minmax(0, 1fr);
    grid-template-areas:
      "head  side"
      "cap   side"
      "board side";
    column-gap: clamp(8px, 1.6vw, 20px);
  }
  .game-head { grid-area: head; }
  .match-caption { grid-area: cap; }
  .board-stage { grid-area: board; padding-bottom: calc(var(--safe-b) + 10px); }
  .game-dock {
    grid-area: side;
    align-content: center;
    gap: 12px;
    padding: calc(var(--safe-t) + 10px) calc(var(--safe-r) + 16px) calc(var(--safe-b) + 16px) 0;
  }
  .game-tools { grid-template-columns: minmax(0, 1fr); }
  .dock-btn { min-height: 48px; }
  .turn-card { min-height: 66px; }
  /* the review bar has room to breathe, so the slider is not squeezed under five buttons */
  .review-bar { gap: 12px; }
}

/* A DESKTOP WINDOW is not a tablet: keep the reading column, but do not stretch the home screen
 * across an ultrawide monitor. The board is still free to use everything. */
@media (min-width: 1500px) {
  .modes { max-width: 620px; }
}

/* ------------------------------------------------------------ statistics and profile (0.1.21) */
/* Three numbers side by side, big and unlabelled-until-you-look: the shape a person scans before
   deciding whether to read the table under it. */

/* 0.1.26: the calendar is a schedule — matchday headings, and the venue on every row */

/* the calendar reuses .fix, so a match looks the same wherever it is listed */
#ch-filter { margin-bottom: 12px; }
#ch-filter button:disabled { opacity: 0.4; }

/* ============================================================ the game screen (0.1.22)
 *
 * From the Air prototype. What is taken is the SURFACE LANGUAGE — glass panels that float over a
 * tinted field, a score pill instead of a header row, a framed board, a dock whose turn card
 * belongs to whoever is to move. What is NOT taken is the prototype's board: that one is a CSS
 * grid with absolutely positioned dots and two hand-drawn blobs for captures. Ours is a canvas
 * with the engine behind it, zoom, pan, rulers and real territory. The frame is new; what is
 * inside it is untouched.
 *
 * THE GLASS, AND WHY IT NEEDED A TOKEN. The prototype builds every panel out of
 * `color-mix(in srgb, white N%, ...)` — white highlights, white inner borders, white sheen. That
 * is correct for a light theme and wrong for a dark one, where it turns every panel into a grey
 * slab. So the tint is a variable: white in the light theme, and in the dark theme a light that is
 * far weaker and a shadow that is far stronger, which is what glass actually does against a dark
 * background.
 */
:root {
  --glass-tint: #ffffff;
  --glass-edge: 45%;          /* how much tint goes into a panel's border */
  --glass-top: 0.60;          /* the highlight along a panel's top edge */
  --glass-sheen: 0.20;        /* the diagonal sweep across a panel */
  --glass-drop: rgba(22, 27, 36, 0.09);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --glass-tint: #b9c6d8;
    --glass-edge: 12%;
    --glass-top: 0.10;
    --glass-sheen: 0.05;
    --glass-drop: rgba(0, 0, 0, 0.45);
  }
}
:root[data-theme="dark"] {
  --glass-tint: #b9c6d8;
  --glass-edge: 12%;
  --glass-top: 0.10;
  --glass-sheen: 0.05;
  --glass-drop: rgba(0, 0, 0, 0.45);
}

/* one class, every floating panel */
.glass, .score-pill, .turn-card, .board-frame {
  border: 1px solid color-mix(in srgb, var(--glass-tint) var(--glass-edge), var(--line));
  background:
    linear-gradient(145deg, color-mix(in srgb, var(--glass-tint) 26%, transparent), transparent 50%),
    color-mix(in srgb, var(--panel) 74%, transparent);
  box-shadow: 0 10px 28px var(--glass-drop),
    inset 0 1px rgba(255, 255, 255, var(--glass-top)),
    inset 0 -1px color-mix(in srgb, var(--line) 74%, transparent);
  backdrop-filter: blur(26px) saturate(150%);
  -webkit-backdrop-filter: blur(26px) saturate(150%);
}

#app {
  background:
    radial-gradient(circle at 50% 50%, color-mix(in srgb, var(--blue) 5%, transparent), transparent 48%),
    var(--paper);
}

/* ---------- head ---------- */
.game-head {
  position: relative;
  z-index: 4;
  display: grid;
  grid-template-columns: 48px minmax(0, 1fr) 48px;
  align-items: center;
  gap: 8px;
  padding: calc(var(--safe-t) + 10px) calc(var(--safe-r) + 12px) 4px calc(var(--safe-l) + 12px);
}
/* the wash behind the pill; in a championship it takes both sides' colours */
.game-head::before {
  content: "";
  position: absolute; z-index: 0; left: 50%; bottom: -3px;
  width: min(76vw, 308px); height: 68px;
  border-radius: 50%;
  background: radial-gradient(ellipse, color-mix(in srgb, var(--blue) 12%, transparent), transparent 70%);
  filter: blur(17px); opacity: 0.46;
  transform: translateX(-50%);
  pointer-events: none;
  transition: opacity 0.32s ease;
}
#app.is-champ .game-head::before {
  background:
    radial-gradient(circle at 24% 50%, color-mix(in srgb, var(--blue) 25%, transparent), transparent 45%),
    radial-gradient(circle at 76% 50%, color-mix(in srgb, var(--red) 22%, transparent), transparent 45%);
  opacity: 0.72;
}
.game-head > * { position: relative; z-index: 1; }

/*
 * THE CAPTURE BANNER (0.1.39). It covers the score pill for a moment and carries the capturing
 * country's own flag as its background — the same bands the board's backdrop is drawn from, so
 * one reading of a flag serves both. The text sits on a scrim because a flag is not a background
 * anybody designed for words: white on Japan and black on Germany both have to stay readable, and
 * the only thing that is true of every flag is that a dark veil over it is dark.
 */
.capture-flash {
  position: absolute; z-index: 3;
  /* 0.1.39: measured, not eyeballed — a fixed 44px was two pixels shorter than the pill it is
     supposed to cover, so a sliver of the score showed under it. It now spans the header band
     between the two buttons, which covers the pill whatever the pill's own height turns out to be. */
  left: 52px; right: 52px; top: 3px; bottom: 3px;
  min-height: 44px; border-radius: 999px; overflow: hidden;
  display: flex; align-items: center; justify-content: center; gap: 14px;
  background-color: var(--cf-side, var(--blue));
  background-size: cover; background-position: center;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.22);
  pointer-events: none;
}
.capture-flash.plain { background-image: none; }
/* the scrim: drawn over the flag, under the words */
.capture-flash::before {
  content: ''; position: absolute; inset: 0;
  background: rgba(0, 0, 0, 0.42);
}
.capture-flash > * { position: relative; z-index: 1; color: #fff; }
.cf-who {
  font-family: var(--f-display); font-weight: 600; font-size: 14px;
  max-width: 55%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}
.cf-gain {
  font-family: var(--f-display); font-weight: 700; font-size: 16px;
  font-variant-numeric: tabular-nums; white-space: nowrap;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}
.capture-flash.in { animation: cf-in 0.26s cubic-bezier(0.2, 0.9, 0.3, 1.2) both; }
.capture-flash.out { animation: cf-out 0.26s ease-in both; }
@keyframes cf-in {
  from { opacity: 0; transform: scale(0.88); }
  to   { opacity: 1; transform: scale(1); }
}
@keyframes cf-out {
  from { opacity: 1; transform: scale(1); }
  to   { opacity: 0; transform: scale(0.96); }
}
@media (prefers-reduced-motion: reduce) {
  .capture-flash.in, .capture-flash.out { animation: none; opacity: 1; }
}

.icon-btn {
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border: 0; background: transparent; border-radius: 12px;
  color: var(--ink-soft); cursor: pointer;
  transition: transform 0.13s ease, opacity 0.13s ease;
}
.icon-btn:active { transform: scale(0.95); opacity: 0.75; }
.icon-btn svg { width: 21px; height: 21px; }
/*
 * 0.1.41: «‹» and «×» are two different verbs and must not look alike — back goes one level, close
 * leaves the section. They live in the same corner and only one is ever shown (see `paintChrome`),
 * so the chevron is given the ink weight of a thing you are meant to press on the way through,
 * while × keeps the softer colour of a way out.
 */
.icon-btn.back { color: var(--ink); }
.icon-btn.back svg { width: 23px; height: 23px; }
.icon-btn[hidden] { display: none; }
.game-head .icon-btn { width: 48px; height: 48px; border-radius: 50%; color: var(--ink); }

.score-pill {
  justify-self: center;
  position: relative; isolation: isolate;
  display: flex; align-items: center; justify-content: center; gap: 9px;
  min-width: 190px; height: 48px;
  padding: 5px 14px;
  border-radius: 18px;
  overflow: hidden;
  transition: min-width 0.26s ease, padding 0.26s ease;
}
.score-pill::after {
  content: "";
  position: absolute; z-index: 0; inset: 0;
  background: linear-gradient(100deg, transparent 15%, rgba(255, 255, 255, var(--glass-sheen)) 44%, transparent 67%);
  pointer-events: none;
}
.score-pill > * { position: relative; z-index: 1; }
/* 0.1.37: the pill names the two teams in COLOUR order, so until the coin lands it would be
 * announcing the draw. `visibility` rather than `display`: the header must not change height
 * underneath the ceremony. */
.score-pill.undecided > * { visibility: hidden; }
/* 0.1.39: on a replay the pill opens the summary, and it says so by behaving like a control */
.score-pill.tappable { cursor: pointer; }
.score-pill.tappable::after {
  content: ''; position: absolute; inset: -4px; border-radius: inherit;
  border: 1px solid color-mix(in srgb, var(--blue) 35%, transparent);
  opacity: .8; pointer-events: none;
}
.score-pill.tappable:active { transform: scale(0.985); }
#app.is-champ .score-pill {
  min-width: 254px; padding-inline: 9px;
  background:
    radial-gradient(circle at 11% 125%, color-mix(in srgb, var(--blue) 22%, transparent), transparent 40%),
    radial-gradient(circle at 91% -20%, color-mix(in srgb, var(--red) 19%, transparent), transparent 42%),
    linear-gradient(145deg, color-mix(in srgb, var(--glass-tint) 30%, transparent), transparent 50%),
    color-mix(in srgb, var(--panel) 72%, transparent);
}

.score-side { display: flex; align-items: center; gap: 6px; min-width: 0; }
/* 0.1.25: `--side-1` / `--side-2` are the two SIDES' colours. They are the game's blue and red
 * by default, and a championship match in team colours overwrites them on #app — so every place
 * that shows a side follows without knowing anything about flags. */
.score-side.blue { color: var(--side-1, var(--blue)); }
.score-side.red { color: var(--side-2, var(--red)); }
.score-dot {
  width: 8px; height: 8px; flex: none; border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 0 4px color-mix(in srgb, currentColor 11%, transparent);
  transition: box-shadow 0.22s ease;
}
/* whose turn it is, said twice: here and on the dock */
.score-side.active .score-dot { box-shadow: 0 0 0 6px color-mix(in srgb, currentColor 22%, transparent); }
.score-pill .pname {
  color: var(--ink-soft); font-size: 11.5px; line-height: 1; font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 8ch;
}
/* 0.1.70: «Niebieskie» is ten letters — the one side name of a game «Удвох» that 8ch cut to
 * «Niebies…» (tests/l10n-screens.js, 393 px). A championship keeps its own 7ch below: team names
 * there are long in every language and the ellipsis is by design. */
#app:not(.is-champ) .score-pill .pname { max-width: 10.5ch; }
/* …except on a 320-px screen: there even 8ch («Niebies…» + «Czerwone») pushes the pill under the two
 * round buttons (pill 36…284 px, buttons end at 50 and start at 270) */
@media (max-width: 360px) {
  #app:not(.is-champ) .score-pill .pname { max-width: 8ch; }
  :root[lang="pl"] #app:not(.is-champ) .score-pill .pname { max-width: 6ch; }
}
#app.is-champ .score-pill .pname { color: var(--ink); font-size: 10.5px; max-width: 7ch; }
.score-pill .pscore {
  font-family: var(--f-display); color: var(--ink);
  font-size: 21px; line-height: 1; font-weight: 700;
  letter-spacing: -0.03em; font-variant-numeric: tabular-nums;
}
.score-pill .vs { color: var(--faint); font-size: 15px; font-weight: 400; }

/* ---------- the flag ---------- */
.flag-slot { display: flex; flex: none; }
/*
 * A FLAG, NOT A BANNER (rectangular from 0.1.26).
 *
 * Apple draws the flag emoji as a piece of cloth: it waves, the top and bottom edges curve, and
 * in a row of eight it reads as eight different shapes. What a table wants is a rectangle.
 *
 * We still do not ship artwork — the device draws the flag, which is the whole argument of
 * flags.js — so the wave is CROPPED instead: the glyph is scaled up inside a fixed rectangle with
 * `overflow: hidden`, and the curved edges fall outside it. The scale is the smallest that clears
 * the wave on Apple's glyphs (about 1.22 vertically) and no more, because everything past that is
 * detail thrown away.
 */
.flag {
  position: relative;
  display: grid; place-items: center;
  width: 30px; height: 21px; flex: none;
  border-radius: 3px; overflow: hidden;
  font-size: 17px; line-height: 1;
  box-shadow: 0 2px 6px rgba(23, 29, 40, 0.16), inset 0 0 0 1px color-mix(in srgb, var(--ink) 14%, transparent);
}
/* the glyph itself, blown up past the frame so the wavy edges are cut off by it */
.flag:not(.flag-plate) { text-indent: 0; }
.flag:not(.flag-plate)::first-line { line-height: 1; }
.flag > .fg {
  display: block; transform: scale(1.26, 1.34); transform-origin: center;
  line-height: 1; white-space: nowrap;
}
/*
 * THE TABLE-ROW FLAG (0.1.23). Same component, a third of the area: in a list of twelve rows a
 * flag is a bullet, not a picture, and at full size it would outweigh the name it belongs to.
 * The shadow goes too — a raised element per row is noise at this density.
 */
.flag-xs {
  width: 20px; height: 14px; border-radius: 3px; font-size: 12px;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 12%, transparent);
}
.flag-xs.flag-plate { font-size: 6px; letter-spacing: 0; }
/* 0.2.0a: a play-off place — two flags, the second tucked under the first's edge, so the pair is one bullet wide-ish */
.flag-pair { display: inline-flex; align-items: center; flex: none; }
.flag-pair > .flag + .flag { margin-left: -6px; box-shadow: 0 0 0 1.5px var(--sheet, #fff), inset 0 0 0 1px color-mix(in srgb, var(--ink) 12%, transparent); }

/* the profile's own heading: the team, once, at a size worth looking at */

/* the fallback where the device draws no flag: the team's own two colours and its code */
.flag-plate {
  background: linear-gradient(105deg, var(--f1) 0 52%, var(--f2) 52% 100%);
  color: #fff;
  font-family: var(--f-ui); font-size: 8.5px; font-weight: 700; letter-spacing: 0.02em;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55);
}

.match-caption {
  position: relative; z-index: 3;
  margin: 2px 0 6px;
  color: var(--ink-soft);
  font-size: 11px; line-height: 1.2; font-weight: 600; letter-spacing: 0.035em;
  text-align: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  padding: 0 16px;
}

/* ---------- the board, in a frame ---------- */
.board-stage {
  position: relative;
  display: grid; place-items: stretch;
  min-height: 0;
  padding: 2px 10px 6px;
}
.board-frame {
  position: relative;
  min-height: 0;
  border-radius: 24px;
  overflow: hidden;
}
.board-frame #board { border-radius: 24px; }

/* ---------- dock ---------- */
.game-dock {
  position: relative; z-index: 3;
  display: grid; gap: 7px;
  padding: 6px calc(var(--safe-r) + 10px) calc(var(--safe-b) + 10px) calc(var(--safe-l) + 10px);
}
.turn-card {
  position: relative; isolation: isolate;
  display: flex; align-items: center; gap: 11px;
  min-width: 0; min-height: 50px;
  padding: 8px 13px;
  border-radius: 18px;
  overflow: hidden;
  background:
    radial-gradient(circle at 6% 120%, color-mix(in srgb, var(--blue) 13%, transparent), transparent 34%),
    linear-gradient(145deg, color-mix(in srgb, var(--glass-tint) 24%, transparent), transparent 48%),
    color-mix(in srgb, var(--panel) 74%, transparent);
}
/* THE CARD CHANGES SIDES. When the engine is to move the card is tinted red, so "whose turn" is
   answered by the colour of the biggest object on the dock and not only by the words on it. */
.turn-card.theirs {
  background:
    radial-gradient(circle at 6% 120%, color-mix(in srgb, var(--red) 13%, transparent), transparent 34%),
    linear-gradient(145deg, color-mix(in srgb, var(--glass-tint) 24%, transparent), transparent 48%),
    color-mix(in srgb, var(--panel) 74%, transparent);
}
.turn-card > * { position: relative; z-index: 1; }
.turn-copy { display: grid; gap: 3px; flex: 1; min-width: 0; }
.turn-copy strong { font-size: 15px; line-height: 1.15; font-weight: 650; display: flex; align-items: center; gap: 8px; }
.turn-copy small {
  font-family: var(--f-mono); color: var(--ink-soft);
  font-size: 11px; line-height: 1.2;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 0.1.77: 60 and 40 half-moves before the whistle (or the Full format's minimum / ceiling) the
   counter lights up — the ink, not a side colour, so it never reads as «red is winning» */
.turn-copy small.lim-60 { color: var(--ink); font-weight: 600; }
.turn-copy small.lim-40 {
  color: var(--ink); font-weight: 700;
  justify-self: start; max-width: 100%;
  padding: 0 5px; border-radius: 6px;
  background: color-mix(in srgb, var(--ink) 11%, transparent);
}
.turn-dot {
  position: relative; flex: none;
  width: 12px; height: 12px; border-radius: 50%;
  color: var(--side-1, var(--blue)); background: currentColor;
  box-shadow: 0 0 0 5px color-mix(in srgb, currentColor 11%, transparent);
  transition: color 0.22s ease;
}
.turn-dot.red { color: var(--side-2, var(--red)); }
.turn-dot::after {
  content: "";
  position: absolute; inset: -5px;
  border: 1px solid currentColor; border-radius: 50%;
  opacity: 0.28;
  animation: turn-pulse 1.8s ease-out infinite;
}
@keyframes turn-pulse { 0% { transform: scale(0.85); opacity: 0.32; } 70%, 100% { transform: scale(1.35); opacity: 0; } }
.still .turn-dot::after { animation: none; }

.thinking { display: inline-flex; gap: 3px; }
.thinking i { width: 4px; height: 4px; border-radius: 50%; background: currentColor; opacity: 0.25; animation: think 1.1s infinite; }
.thinking i:nth-child(2) { animation-delay: 0.18s; }
.thinking i:nth-child(3) { animation-delay: 0.36s; }
@keyframes think { 0%, 60%, 100% { opacity: 0.25; } 30% { opacity: 1; } }

.game-tools { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 7px; }
.dock-btn {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  min-width: 0; min-height: 43px; padding: 0 10px;
  border-radius: 15px;
  color: var(--ink);
  font-size: 13px; font-weight: 600;
  cursor: pointer;
  transition: transform 0.13s ease, opacity 0.13s ease;
}
.dock-btn:active { transform: scale(0.975); opacity: 0.8; }
.dock-btn svg { width: 18px; height: 18px; flex: none; color: var(--ink-soft); }
.dock-btn:disabled { color: var(--faint); opacity: 0.42; }
.dock-btn:disabled svg { color: var(--faint); }
/* 0.1.80: undo not allowed now (limit used up, game over) — looks off, still answers a tap */
.dock-btn.is-off { color: var(--faint); opacity: 0.42; }
.dock-btn.is-off svg { color: var(--faint); }

/* ---------- the review bar (0.1.23) ----------
 * It replaces the two dock buttons while a finished match is being walked through. The board above
 * it is the ordinary board — same canvas, same gestures, same territory — so everything here is
 * transport controls and a place in the match. */
.review-bar { display: grid; gap: 8px; }
.review-line { display: flex; align-items: center; justify-content: center; gap: 6px; }
.rev-btn {
  min-width: 44px; min-height: 38px; padding: 0 10px;
  border: 0; border-radius: var(--r-1);
  background: var(--control); color: var(--ink);
  box-shadow: var(--shadow-1); font-size: 14px; cursor: pointer;
}
.rev-btn:active { opacity: 0.7; }
.rev-btn:disabled { opacity: 0.35; }
/* the play button is the one you reach for, so it is the one that looks like a button */
.rev-play {
  min-width: 56px; min-height: 44px;
  background: var(--ink); color: var(--paper); font-size: 16px;
  box-shadow: 0 6px 16px color-mix(in srgb, var(--ink) 18%, transparent);
}
.rev-play.playing { background: var(--blue); color: #fff; }
.rev-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.rev-count {
  font-size: 13px; font-weight: 600;
  font-variant-numeric: tabular-nums; color: var(--ink-soft);
}
.rev-speed {
  min-height: 28px; padding: 0 10px;
  border: 0; border-radius: 999px;
  background: var(--control); color: var(--ink-soft);
  box-shadow: var(--shadow-1);
  font: inherit; font-size: 12px; font-weight: 600;
  font-variant-numeric: tabular-nums; cursor: pointer;
}
/*
 * 0.1.39: the door to the summary is a WORD, not an abbreviation squeezed beside «×2». The
 * play-test asked for it wider and named for what it opens, and it was right: «Підсумок» next to
 * a speed control reads as another control, «Підсумок матчу» reads as a place to go.
 */
/* the same HEIGHT as the speed control beside it: the play-test asked for it longer, and a
 * taller review bar costs the board its space — at 320px it shrank the advertising band below
 * the size at which it is drawn at all, which the geometry check caught. */
.rev-sum {
  min-height: 28px; padding: 0 18px;
  border: 0; border-radius: 999px;
  background: var(--blue); color: #fff;
  box-shadow: var(--shadow-1);
  font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
  white-space: nowrap;
}
.rev-sum:active { transform: translateY(1px); }
#rev-range { width: 100%; accent-color: var(--blue); }
.rev-note { margin: 0; text-align: center; font-size: 11px; color: var(--faint); }
/*
 * 0.1.39: on the narrowest screens the review bar takes the board's space, and the first thing
 * to go is the help line — three wrapped lines explaining ▶ and a slider, read once. Measured:
 * the 0.1.39 summary button cost the band about five pixels at 320×640, enough to drop the
 * advertising boards below the size at which they are drawn; the note gives back five times that.
 * The board is what the screen is for.
 */
@media (max-height: 700px), (max-width: 360px) {
  .rev-note { display: none; }
}

@media (max-height: 700px) {
  .game-head { padding-top: calc(var(--safe-t) + 6px); }
  .game-head .icon-btn { width: 44px; height: 44px; }
  .score-pill { height: 44px; min-width: 176px; }
  .match-caption { margin: 0 0 4px; }
  .turn-card { min-height: 46px; }
  .dock-btn { min-height: 40px; }
}
@media (max-width: 380px) {
  .game-head { grid-template-columns: 42px minmax(0, 1fr) 42px; padding-inline: 8px; }
  .game-head .icon-btn { width: 42px; height: 42px; }
  #app.is-champ .score-pill { min-width: 218px; padding-inline: 7px; }
  #app.is-champ .score-side { gap: 3px; }
  #app.is-champ .score-pill .pname { font-size: 9px; max-width: 6ch; }
  .flag { width: 25px; height: 18px; font-size: 14px; }
  /*
   * 0.1.28: the dock keeps its words on a narrow phone. Hiding them was harmless while both
   * buttons meant one fixed thing; now one of them is a TOGGLE whose whole job is to say where
   * it takes you — "Наблизити" or "Усе поле" — and an icon cannot say that. Found by shooting
   * the game at 325 px: two icons and no way to know what the second one would do.
   */
  .dock-btn { gap: 6px; padding-inline: 10px; }
  .dock-btn span { font-size: 11px; letter-spacing: -0.01em; }
  .dock-btn svg { width: 18px; height: 18px; }
}
@media (max-width: 300px) {
  /* below this even a short word will not fit beside the icon */
  .dock-btn span { display: none; }
}

/* ------------------------------------------------------------------ match summary (0.1.26)
 *
 * The football ending: the score between two flags, one line saying how the match went, three
 * two-sided bars, and the handful of moments that decided it. It lives at the bottom of the
 * result sheet, so a person sees the verdict first and the story when they scroll — never the
 * other way round.
 */
#over-story { margin: 18px 0 4px; text-align: left; }
#over-story h3 { margin: 18px 0 8px; }

/*
 * 0.1.40: the name WRAPS instead of being cut. «Боснія і Герцеговина» came out as
 * «Боснія і Герцег…» on the result screen — a country the player had just beaten, named half.
 * An ellipsis is the right answer in a table of twelve rows where every row must be the same
 * height; here there are two names and all the room the sheet needs. Two lines cost nothing and
 * say the whole word.
 */

.lead-chart { width: 100%; height: 72px; display: block; margin: 4px 0 12px; }
.lc-1 { fill: var(--side-1, var(--blue)); opacity: .26; }
.lc-2 { fill: var(--side-2, var(--red)); opacity: .26; }
.lc-line { fill: none; stroke: var(--ink); stroke-width: 1.4; opacity: .55; vector-effect: non-scaling-stroke; }
.lc-axis { stroke: var(--ink-soft); stroke-width: 1; opacity: .3; vector-effect: non-scaling-stroke; }

/* ---------- a tab that has nothing to show yet (0.1.26) ----------
 * The playoff bracket does not exist until the groups are played. One grey line of text made the
 * whole sheet shrink to that line's height, so the tab row jumped down the screen and back. The
 * sheet now keeps its height whenever the tournament's tabs are up, and the empty tab gets a
 * screen of its own rather than a footnote.
 *
 * 0.1.55: as a pane it is anchored top AND bottom, so it keeps its height by construction and this
 * line has nothing left to hold up. It stayed for one build too long and won on specificity —
 * `#sheet-champ.has-tabs` outranks `.sheet.pane` — which left eighty pixels of the home screen
 * showing under the window. A rule that outlives its reason is a rule that will win an argument
 * nobody remembers having.
 */
#sheet-champ.has-tabs:not(.pane) { height: 88vh; }

/* three empty slots, the shape of a bracket column that is waiting to be filled */

/* ---------- the result sheet arrives, it does not land (0.1.28) ----------
 * Every other sheet is something the player asked for by pressing a button, and 0.28 s is right
 * for that: the answer should feel immediate. The result sheet is the one nobody presses — it
 * appears on its own, over the move that just decided the match — so the same 0.28 s reads as
 * being shoved out of the way. This one takes almost twice as long, on a softer curve, and its
 * contents settle a beat behind the sheet itself.
 */
#sheet-over {
  transition: transform 0.52s cubic-bezier(0.16, 0.84, 0.28, 1);
}
#sheet-over.open > * {
  animation: over-settle 0.46s cubic-bezier(0.22, 0.9, 0.3, 1) both;
  animation-delay: 0.12s;
}
#sheet-over.open > *:nth-child(3) { animation-delay: 0.18s; }
#sheet-over.open > *:nth-child(4) { animation-delay: 0.22s; }
#sheet-over.open > *:nth-child(5) { animation-delay: 0.26s; }
@keyframes over-settle {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
/* the scrim comes up with the sheet rather than snapping on ahead of it */
.scrim.open { transition: opacity 0.42s ease; }

@media (prefers-reduced-motion: reduce) {
  #sheet-over, .scrim.open { transition-duration: 0.01ms !important; }
  #sheet-over.open > * { animation: none !important; }
}

/* the name column takes whatever is left, and elides rather than pushing the numbers around.
 * 0.1.35: the cell is a flex row so the MARK keeps its own size and the NAME gives way — before,
 * both were inline and the name lost, which is how «Мексика» became «М..». */

/*
 * On the narrowest phones the numbers alone ask for 210px of a 280px table, and the name is left
 * with 43. Something has to go, and the DIFFERENCE is the one column that is not a fact of its
 * own: «З» and «‑З» are both on the row, and the table is already sorted by it. Dropping it, and
 * the flag with it, gives the name 100px instead of 43 — measured on a 320px screen.
 */

/* how hard the opponent is playing, on the same card (0.1.36) */

/* the toss, on the card that offers the match (0.1.32) */

/* ============================================================ the toss (0.1.36)
 *
 * A coin over the board, for the two seconds before the first move. It is an announcement, not a
 * decision: the seed settled who opens long before this ran. The stage covers the board and
 * nothing else, so the score pill and the dock stay readable behind it, and any tap ends it —
 * a ceremony you cannot skip stops being a ceremony by the third match.
 */
.toss-stage {
  position: absolute; inset: 0; z-index: 6;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 18px;
  background: color-mix(in srgb, var(--sheet) 72%, transparent);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  border-radius: inherit;
  animation: toss-in 0.22s ease-out both;
}
.toss-stage.out { animation: toss-out 0.3s ease-in both; }
@keyframes toss-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes toss-out { from { opacity: 1; } to { opacity: 0; } }

/*
 * 0.1.37: the coin WAITS. It used to start spinning the moment the stage appeared, because the
 * answer was already known and there was nothing to wait for. Now the draw happens when the
 * player throws it, so the spin is a class that script adds at that instant — and the coin is a
 * real button, which is what makes it reachable by keyboard and by a screen reader.
 */
.coin {
  width: 96px; height: 96px; position: relative;
  transform-style: preserve-3d;
  padding: 0; border: 0; background: none; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.18s ease-out;
}
.coin.idle { animation: coin-wait 2.4s ease-in-out infinite; }
.coin.idle:active { transform: scale(0.94); }
.coin.spin { animation: coin-spin 1.15s cubic-bezier(0.25, 0.7, 0.3, 1) both; }
.coin[disabled] { cursor: default; }
/* a coin nobody has touched yet breathes, so it reads as "press me" rather than as a picture */
@keyframes coin-wait {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50%      { transform: translateY(-6px) rotate(-4deg); }
}
.coin .face {
  position: absolute; inset: 0; border-radius: 50%;
  display: block; backface-visibility: hidden;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.22), inset 0 0 0 3px rgba(255, 255, 255, 0.55);
  background-size: cover; background-position: center;
}
.coin .face.b { transform: rotateY(180deg); }
/*
 * The face is a box that CENTRES its flag, and the flag is the ordinary component at coin size —
 * `.flag` brings its own width, height and crop, so the face must not try to lay it out as text.
 */
.coin .face {
  display: grid; place-items: center;
  background: var(--sheet);
}
.flag-coin { width: 66px; height: 46px; border-radius: 6px; font-size: 40px; }
/* the flags of the two sides are set as the faces from script, so the coin is THIS match's coin */
/*
 * WHICH FACE IT LANDS ON IS THE WHOLE POINT. The spin ends at `--land`, and that is a whole
 * number of turns when the home side opens and half a turn more when the guests do — so the face
 * you are left looking at is the team the verdict names. A coin that always shows the same side
 * is not a coin, and the first version of this did exactly that.
 */
.coin { --land: 1800deg; }
@keyframes coin-spin {
  0%   { transform: translateY(0) rotateY(0deg) scale(0.9); }
  45%  { transform: translateY(-38px) rotateY(calc(var(--land) / 2)) scale(1.06); }
  100% { transform: translateY(0) rotateY(var(--land)) scale(1); }
}
.coin.settled { transform: rotateY(var(--land)); }

/* the invitation, before the throw */
.toss-ask {
  margin: 0; font-size: 14px; color: var(--ink-soft); text-align: center;
}
/*
 * 0.1.39: the verdict is OPAQUE BY DEFAULT. It used to rest at `opacity: 0` and be made visible
 * by the `.said` animation — so anything that stopped that animation from running stopped the
 * sentence from existing, which is what the play-test saw. The animation now only slides it in;
 * whether the text is on the screen is no longer an animation's decision.
 */
.toss-verdict {
  margin: 0; font-family: var(--f-display); font-weight: 700; font-size: 17px;
  text-align: center; text-wrap: balance;
  padding: 0 16px; min-height: 1.2em;
}
.toss-verdict:empty { min-height: 0; }
.toss-verdict.said { animation: toss-said 0.3s ease-out; }
@keyframes toss-said { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/*
 * 0.1.54: NOTHING IS WRITTEN WHILE THE COIN IS IN THE AIR.
 *
 * The invitation goes the instant the coin is thrown, and the sentence — already written into the
 * element, as 0.1.39 requires — waits out the 1.15 s spin before it is visible.
 *
 * `forwards`, and the last keyframe is `opacity: 1`: THE APPEARING IS THE ANIMATION'S OWN END, not
 * a class somebody has to remember to remove. If `.flipping` were left on for ever, or the timer
 * that removes it never ran, the sentence would still be on the screen a second later. That is the
 * 0.1.39 rule kept — an animation may decide WHEN the text shows, never WHETHER.
 *
 * The invitation is `display: none` rather than transparent so the caption block is one line tall
 * during the flip, which is the height it keeps afterwards: the coin does not move under the text.
 */
.toss-stage.flipping .toss-ask { display: none; }
.toss-stage.flipping .toss-verdict { animation: toss-hold 1.15s linear forwards; }
@keyframes toss-hold { 0%, 93% { opacity: 0; } 100% { opacity: 1; } }
/* the ask line wraps now that it is a sentence rather than two words */
.toss-ask { max-width: 17em; }

/*
 * «Розпочати гру» (0.1.37). The ceremony no longer dismisses itself after 2.4 seconds — the
 * play-test asked for the match to begin when the person says so. Until this is pressed the
 * engine has not been asked to think and no clock of any kind is running.
 */
.toss-go {
  font: inherit; font-weight: 600; font-size: 15px;
  padding: 11px 22px; border-radius: 12px; border: 0;
  background: var(--blue); color: #fff; cursor: pointer;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
  animation: toss-said 0.3s ease-out both;
}
.toss-go:active { transform: translateY(1px); }

@media (prefers-reduced-motion: reduce) {
  .coin, .coin.idle, .coin.spin, .coin.settled,
  .toss-verdict, .toss-verdict.said, .toss-go,
  .toss-stage, .toss-stage.out { animation: none; opacity: 1; }
  /* 0.1.54: with no spin there is nothing to wait for, so the sentence is simply there. Named
   * explicitly because `.toss-stage.flipping .toss-verdict` outranks the line above. */
  .toss-stage.flipping .toss-verdict { animation: none; opacity: 1; }
}

/* 0.1.41: a setting that exists but cannot act — the haptics switch on a platform with no
   Vibration API. Dimmed and inert rather than removed, because the native build will read it. */
.row.is-off { opacity: 0.45; }
.row.is-off .switch { pointer-events: none; }

/* ------------------------------------------------------------------ Як грати (0.1.42)
   The animation screen: our board and nothing around it — no ad bands, no flags, as asked.
   Everything here is sized from the sheet, so the canvas keeps its aspect on a phone in portrait
   and on a tablet without a second layout. */
.menu-item.big { align-items: flex-start; padding: 15px 16px; }
.menu-item.big .copy { display: flex; flex-direction: column; gap: 3px; text-align: left; }
.menu-item.big .copy small { color: var(--ink-soft); font-size: 13px; line-height: 1.35; }

.anim-chapter {
  margin: 2px 16px 8px; font: 500 12px/1.2 var(--f-ui);
  color: var(--ink-faint); letter-spacing: 0.02em; text-transform: uppercase;
}
.anim-stage {
  position: relative; margin: 0 12px;
  height: min(46vh, 340px);
  border-radius: var(--r-2, 16px); overflow: hidden;
  background: var(--board-bg, var(--paper));
  box-shadow: var(--shadow-1, 0 2px 10px rgba(0, 0, 0, 0.08));
}
.anim-stage canvas { display: block; width: 100%; height: 100%; }
.anim-say {
  margin: 12px 16px 6px; min-height: 3.4em;
  font: 400 15px/1.45 var(--f-ui); color: var(--ink);
}
.anim-bar { display: flex; align-items: center; gap: 10px; margin: 2px 12px 6px; }
.anim-bar input[type="range"] {
  flex: 1; appearance: none; height: 4px; border-radius: 2px;
  background: var(--line); outline: none;
}
.anim-bar input[type="range"]::-webkit-slider-thumb {
  appearance: none; width: 18px; height: 18px; border-radius: 50%;
  background: var(--ink); border: 0; cursor: pointer;
}
.anim-bar input[type="range"]::-moz-range-thumb {
  width: 18px; height: 18px; border-radius: 50%; background: var(--ink); border: 0;
}
.anim-chapters {
  display: flex; gap: 6px; overflow-x: auto; margin: 0 12px 10px;
  padding-bottom: 4px; -webkit-overflow-scrolling: touch;
}
.anim-chapters::-webkit-scrollbar { display: none; }
.anim-mark {
  flex: 0 0 auto; border: 0; cursor: pointer;
  padding: 6px 11px; border-radius: 999px;
  background: var(--press, rgba(125, 140, 160, 0.10));
  color: var(--ink-soft); font: 500 12px/1 var(--f-ui);
}
.anim-mark.on { background: var(--ink); color: var(--paper); }
.anim-exits { display: flex; gap: 8px; margin: 0 12px 4px; }
.anim-exits .secondary { flex: 1; margin: 0; }
@media (max-height: 700px) { .anim-stage { height: 38vh; } .anim-say { min-height: 3em; } }

/* 0.1.43: the two text links under the mode cards. «Покупки» sits beside «Як грати» in the same
   weight, deliberately: the four cards above are about playing, and a shop is not one of them. */
.home-links { display: flex; justify-content: center; gap: 18px; margin-top: 2px; }
.home-links .text-btn { margin: 0; }

/* ================================================================ championship · Д-01 (0.1.58)
 *
 * THE EIGHT SCREENS IN THE DESIGN ACCEPTED IN Д-01. The rules below are the mock-up's own
 * `source/championship.css` and the ten added states' `add.css`, with three changes and nothing else:
 *
 *   1. SCOPED. Every selector sits under `.ch-content` — the <main> each screen is drawn into. The
 *      mock-up used bare names (`.primary`, `.flag`, `.section`, `.badge`) and this stylesheet
 *      already has four of them for other screens; scoped, the championship cannot restyle the
 *      home screen and the home screen cannot restyle the championship.
 *   2. NOT CARRIED OVER: the first six rules (`.sheet-head h2`, `.head-actions`, `.icon-btn…` —
 *      already here), `@font-face` and its fonts, `backdrop-filter` on `.ch-card` (Д-01 §2.4: the
 *      cards lie on a flat gradient, so the blur changed only text anti-aliasing, and it costs a
 *      compositing layer per card on a phone whose memory was already the problem, Р-51), the
 *      prototype's dialog and its drawn switch (the pick screen uses the app's real one).
 *   3. The seven `--ch-*` tokens live in the palette blocks at the top of this file (Р-60): Air
 *      gets the mock-up's values, the notebook gets references to its own tokens.
 *
 * Colour literals in this section: 0.
 */
.ch-tabs { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 2px; }
.ch-tabs button { display: flex; align-items: center; justify-content: center; width: 100%; min-width: 0; padding: 0 2px; text-align: center; font-size: clamp(10px, 2.85vw, 12px); line-height: 1.2; }
.ch-content { position: relative; isolation: isolate; padding-top: 2px; color: var(--ink); font: 14px/1.45 var(--f-ui); }
.ch-content::before { content: ""; position: absolute; z-index: -1; inset: -10px -20px -62px; background: linear-gradient(140deg, var(--sheet), var(--ch-tint) 48%, var(--sheet)); pointer-events: none; }
.ch-content *, .ch-content *::before, .ch-content *::after { min-width: 0; }
/* 0.1.65: the story on the result sheet after the player's own match is the same D-01 markup as
 * «Підсумок матчу», so it lives under `.ch-content` too. The sheet already has a background of its
 * own; the championship page backdrop would paint past the sheet's edges. */
.ch-content.ch-sheet { margin-top: 6px; }
.ch-content.ch-sheet::before { content: none; }
#sheet-over.with-card .result .big, #sheet-over.with-card #over-title { display: none; }
#sheet-over.with-card .result { padding-top: 4px; }
.ch-content h2, .ch-content h3, .ch-content p { margin: 0; }
.ch-content h2 { font: 600 17px/1.35 var(--f-ui); letter-spacing: -.025em; }
.ch-content h3 { font: 600 15px/1.35 var(--f-ui); }
.ch-content a { color: var(--ink); text-decoration: none; }
.ch-content button, .ch-content select, .ch-content input { font: inherit; color: var(--ink); }
.ch-content button, .ch-content a { -webkit-tap-highlight-color: var(--ch-clear); }
.ch-content :is(p, h2, h3, a, button, label, summary, td, th, .team-name) { overflow-wrap: anywhere; }
.ch-content .ch-icon { width: 18px; height: 18px; flex: 0 0 18px; vertical-align: middle; fill: none; stroke: var(--ink-soft); stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.ch-content .ch-card { background: linear-gradient(145deg, var(--ch-glass), var(--control)); border: 1px solid var(--ch-edge); border-radius: var(--r-3); box-shadow: var(--ch-shadow); }
.ch-content .pad { padding: 16px; }
.ch-content .stack { display: grid; gap: 12px; }
.ch-content .section { margin-top: 22px; }
.ch-content .section-head { display: grid; grid-template-columns: minmax(0,1fr) auto; align-items: center; gap: 10px; margin: 0 0 10px; }
.ch-content .section-head > * { flex-shrink: 1; }
.ch-content .section-head > :last-child:not(:first-child) { max-width: 145px; }
.ch-content .section-link { display: inline-flex; align-items: center; justify-content: flex-end; gap: 4px; font-size: 12px; color: var(--ink-soft) !important; text-align: right; min-height: 32px; min-width: 0; }
.ch-content .section-head > .section-link { max-width: none; width: max-content; white-space: nowrap; }
.ch-content .subtle, .ch-content .muted { color: var(--ink-soft); }
.ch-content .small { font-size: 12px; line-height: 1.45; }
.ch-content .kicker { color: var(--ink-soft); font-size: 11px; font-weight: 500; letter-spacing: .05em; text-transform: uppercase; }
.ch-content .small + .kicker { margin-top: 16px; }   /* 0.1.87: the level block under the match-length note */
.ch-content .mono, .ch-content .number { font-family: var(--f-mono); font-variant-numeric: tabular-nums; }
.ch-content .big-number { font: 500 40px/1.12 var(--f-mono); letter-spacing: -.05em; }
.ch-content .status-line { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; color: var(--ink-soft); font-size: 12px; margin-bottom: 12px; }
.ch-content .status-line .mono { white-space: nowrap; font-size: 12px; }
.ch-content .flag { display: inline-flex; width: 22px; height: 15px; flex: 0 0 22px; align-items: center; justify-content: center; line-height: 15px; font-size: 21px; vertical-align: middle; }
.ch-content .flag-case { display: inline-grid; place-items: center; padding: 6px; background: var(--ch-glass); border: 1px solid var(--ch-edge); box-shadow: var(--shadow-1); border-radius: 9px; flex: 0 0 auto; }
.ch-content .team-name { display: inline-flex; align-items: center; gap: 7px; }
.ch-content .ch-btn { min-height: 44px; padding: 11px 14px; display: flex; align-items: center; justify-content: center; gap: 9px; border-radius: 16px; border: 1px solid var(--ch-edge); background: var(--ch-glass); box-shadow: var(--ch-shadow); cursor: pointer; text-align: center; line-height: 1.35; width: 100%; }
.ch-content .ch-btn.primary { background: linear-gradient(145deg, var(--ink), var(--ch-action)); color: var(--sheet); border-color: var(--ch-action); font-weight: 600; box-shadow: var(--shadow-2); }
.ch-content .primary .ch-icon { stroke: var(--sheet); }
.ch-content .ch-btn:active { transform: translateY(1px); }
.ch-content .ch-btn[disabled] { opacity: .6; cursor: default; }
.ch-content .text-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-height: 44px; border: 0; background: var(--ch-clear); color: var(--ink-soft); cursor: pointer; padding: 8px 10px; }
.ch-content .action-card { padding: 12px; }
.ch-content .action-card .kicker { text-transform: none; letter-spacing: 0; }
.ch-content .versus { display: grid; grid-template-columns: minmax(0,1fr) auto minmax(0,1fr); gap: 8px; align-items: center; margin: 8px 0; }
.ch-content .versus .side { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 7px; text-align: center; font-weight: 600; font-size: 15px; line-height: 1.25; }
.ch-content .versus .separator { font-family: var(--f-mono); color: var(--ink-soft); }
.ch-content .action-meta { text-align: center; color: var(--ink-soft); font-size: 11px; line-height: 1.6; margin: 8px 0 10px !important; }
.ch-content .next-secondary { margin-top: 8px; font-size: 13px; }
.ch-content .table-card { padding: 6px 8px; border-radius: 20px; }
.ch-content .standings { width: 100%; table-layout: fixed; border-collapse: separate; border-spacing: 0; font-size: 13px; }
.ch-content .standings th { font-weight: 400; color: var(--ink-soft); font-size: 10px; height: 24px; text-align: right; border-bottom: 1px solid var(--line); }
.ch-content .standings th.team-label { text-align: left; padding-left: 33px; }
.ch-content .standings td { padding: 7px 3px; text-align: right; font-family: var(--f-mono); font-size: 12px; }
.ch-content .standings td.name-cell { text-align: left; font-family: var(--f-ui); font-size: 13px; }
.ch-content .standings td.pos { text-align: center; width: 24px; color: var(--ink-soft); }
.ch-content .standings td.points { font-weight: 500; }
.ch-content .standings .team-col { width: auto; }
.ch-content .standings .pos-col { width: 24px; }
.ch-content .standings .num-col { width: 32px; }
.ch-content .team-row.is-human td { background: var(--ch-selected); }
.ch-content .team-row td:first-child { border-radius: 8px 0 0 8px; }
.ch-content .team-row td:last-child { border-radius: 0 8px 8px 0; }
.ch-content .standings .team-row.qualified .pos { color: var(--ok); font-weight: 500; }
.ch-content .standings .name-cell a { display: flex; gap: 7px; align-items: center; min-height: 23px; }
.ch-content .standings .name-cell a > span:last-child { flex: 1; }
.ch-content .qual-note { display: flex; align-items: flex-start; gap: 6px; font-size: 11px; color: var(--ink-soft); padding: 7px 4px 0; }
.ch-content .qual-note .ch-icon { width: 14px; height: 14px; flex-basis: 14px; stroke: var(--ok); margin-top: 1px; }
.ch-content .legend { font-size: 11px; line-height: 1.5; color: var(--ink-soft); padding: 9px 12px 0; }
.ch-content .match-list { padding: 4px 6px; border-radius: 20px; }
.ch-content .match-item + .match-item { border-top: 1px solid var(--line); }
.ch-content .match-row { display: grid; grid-template-columns: minmax(0,1fr) auto minmax(0,1fr); gap: 6px; align-items: center; padding: 10px 7px; min-height: 44px; font-size: 12px; border-radius: 10px; }
.ch-content .match-row.is-human { background: var(--ch-selected); }
.ch-content .match-row .away { flex-direction: row-reverse; text-align: right; justify-content: flex-start; }
.ch-content .match-score { font: 500 12px/1.2 var(--f-mono); text-align: center; white-space: nowrap; min-width: 38px; }
.ch-content .match-row.scheduled .match-score { color: var(--ink-soft); font-weight: 400; }
.ch-content .match-meta { display: flex; justify-content: space-between; gap: 10px; padding: 0 8px 8px; color: var(--ink-soft); font-size: 10px; }
.ch-content .match-venue { padding: 0 9px 10px; font-size: 10px; color: var(--ink-soft); text-align: center; }
.ch-content.hub-compact .section { margin-top: 10px; }
.ch-content.hub-compact .section-head { margin-bottom: 5px; }
.ch-content.hub-compact .standings td { padding-top: 5px; padding-bottom: 5px; }
.ch-content.hub-compact .match-row { min-height: 29px; padding-top: 5px; padding-bottom: 5px; }
.ch-content.hub-compact .match-item + .match-item { border: 0; }
.ch-content.hub-compact .section-head h2 { font-size: 18px; }
.ch-content .service-actions { margin-top: 20px; border-top: 1px solid var(--line); padding-top: 12px; display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.ch-content .badge { display: inline-flex; align-items: center; gap: 4px; border-radius: 20px; padding: 4px 8px; color: var(--ink-soft); background: var(--resume-tag); font-size: 10px; font-weight: 500; }
.ch-content .pill-row { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.ch-content .summary-line { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 0; }
.ch-content .summary-line + .summary-line { border-top: 1px solid var(--line); }
.ch-content .summary-line strong { text-align: right; font-size: 13px; font-weight: 500; }
.ch-content .summary-line span { color: var(--ink-soft); font-size: 12px; }
.ch-content .metrics { display: grid; grid-template-columns: repeat(3,minmax(0,1fr)); gap: 8px; }
.ch-content .metric { padding: 13px 7px; text-align: center; border-radius: 16px; }
.ch-content .metric strong { display: block; font: 500 23px/1.25 var(--f-mono); letter-spacing: -.03em; }
.ch-content .metric span { display: block; font-size: 10px; color: var(--ink-soft); margin-top: 5px; }
.ch-content .group-block { scroll-margin-top: 118px; }
.ch-content .group-block:first-of-type { margin-top: 16px; }
.ch-content .group-block + .group-block { margin-top: 18px; }
.ch-content .group-block .section-head { margin-bottom: 6px; }
.ch-content .group-block .section-head h2 { font-size: 16px; }
.ch-content .disclosure { margin-top: 12px; }
.ch-content .disclosure > summary { min-height: 44px; padding: 11px 2px; cursor: pointer; color: var(--ink-soft); font-size: 12px; }
.ch-content .table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; border-radius: 16px; }
.ch-content .full-table { min-width: 485px; }
.ch-content .full-table .num-col { width: 33px; }
.ch-content .full-table .name-cell { width: 170px; }
.ch-content .empty-state { padding: 24px 20px; text-align: center; }
.ch-content .empty-state .empty-symbol { display: grid; place-items: center; width: 54px; height: 54px; margin: 0 auto 14px; border-radius: 18px; background: var(--ch-selected); border: 1px solid var(--ch-edge); }
.ch-content .empty-symbol .ch-icon { width: 28px; height: 28px; }
.ch-content .empty-state h2 { margin-bottom: 9px; }
.ch-content .empty-state p { font-size: 12px; color: var(--ink-soft); }
.ch-content .empty-state .ch-btn { margin-top: 18px; }
.ch-content .progress-track { display: block; width: 100%; height: 4px; background: var(--line); border-radius: 9px; margin-top: 13px; }
.ch-content .progress-track > span { display: block; height: 100%; width: var(--progress); background: var(--ink-soft); border-radius: inherit; }
.ch-content .round-steps { list-style: none; padding: 4px 0; margin: 0; }
.ch-content .round-steps li { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-height: 45px; padding: 10px 2px; border-bottom: 1px solid var(--line); }
.ch-content .round-steps li:last-child { border: 0; }
.ch-content .round-steps span { color: var(--ink-soft); font-size: 12px; }
.ch-content .segmented { padding: 4px; display: flex; gap: 3px; border: 1px solid var(--line); border-radius: 14px; background: var(--ch-selected); margin-bottom: 14px; }
.ch-content .segmented button { flex: 1; padding: 8px 6px; min-height: 36px; border: 0; border-radius: 10px; background: var(--ch-clear); color: var(--ink-soft); cursor: pointer; font-size: 12px; line-height: 1.25; }
.ch-content .segmented button[aria-pressed="true"] { background: var(--ch-glass); color: var(--ink); box-shadow: var(--shadow-1); }
.ch-content .filter-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.ch-content .filter-row label { font-size: 11px; color: var(--ink-soft); }
.ch-content .filter-row select { max-width: 65%; border: 1px solid var(--line); background: var(--control); padding: 9px; border-radius: 10px; font-size: 12px; min-height: 40px; }
.ch-content .date-group { margin-top: 15px; }
.ch-content .date-head { display: flex; justify-content: space-between; gap: 8px; color: var(--ink-soft); font-size: 11px; padding: 0 2px 7px; }
.ch-content .date-head h2 { flex: 1; color: var(--ink); font: 600 13px var(--f-ui); }
.ch-content .opponent-head { display: grid; grid-template-columns: minmax(0,1fr) auto; align-items: center; gap: 8px; margin-bottom: 14px; }
.ch-content .opponent-head .team-name { display: flex; }
.ch-content .opponent-head h2 { font-size: 20px; }
.ch-content .strength-title { margin-top: 14px !important; font-size: 20px !important; }
.ch-content .strength-pair { display: grid; grid-template-columns: 1fr auto 1fr; gap: 10px; align-items: end; margin: 16px 0; }
.ch-content .strength-pair > div { display: grid; gap: 5px; }
.ch-content .strength-pair > div:last-child { text-align: right; }
.ch-content .strength-pair strong { font: 500 27px var(--f-mono); letter-spacing: -.045em; }
.ch-content .strength-pair span { font-size: 11px; color: var(--ink-soft); }
.ch-content .strength-bars { display: grid; gap: 6px; }
.ch-content .strength-track { height: 5px; background: var(--line); border-radius: 8px; overflow: hidden; }
.ch-content .strength-track span { display: block; height: 100%; width: var(--strength); background: var(--ink); border-radius: inherit; }
.ch-content .strength-track.opponent span { background: var(--ink-soft); }
.ch-content .strength-note { margin-top: 13px !important; color: var(--ink-soft); font-size: 12px; }
.ch-content .rating-list { list-style: none; padding: 4px 8px; margin: 0; }
.ch-content .rating-row { display: grid; grid-template-columns: 25px minmax(0,1fr) 55px; gap: 6px; align-items: center; min-height: 41px; padding: 7px 3px; border-bottom: 1px solid var(--line); }
.ch-content .rating-row:last-child { border: 0; }
.ch-content .rating-row.is-human { background: var(--ch-selected); border-radius: 8px; }
.ch-content .rating-row > .mono { color: var(--ink-soft); font-size: 11px; }
.ch-content .rating-row strong { text-align: right; font: 500 13px var(--f-mono); }
.ch-content .profile-head { display: flex; gap: 12px; align-items: center; margin-bottom: 16px; }
.ch-content .profile-head h2 { font-size: 22px; }
.ch-content .profile-head .flag-case { padding: 14px; border-radius: 16px; }
.ch-content .scoreboard { text-align: center; padding: 20px 14px; }
.ch-content .scoreboard .versus { grid-template-columns: minmax(0,1fr) auto minmax(0,1fr); margin: 18px 0; }
.ch-content .scoreboard .side { flex-direction: column; gap: 9px; font-size: 13px; font-weight: 500; }
.ch-content .scoreboard .score-main { font: 500 30px var(--f-mono); letter-spacing: -.07em; white-space: nowrap; }
.ch-content .scoreboard .result-label { font-size: 17px; font-weight: 600; margin-top: 10px; }
.ch-content .bracket-scroll { overflow-x: auto; scroll-snap-type: x mandatory; padding: 6px 0 16px; }
.ch-content .bracket-tree { display: flex; align-items: stretch; gap: 22px; width: max-content; padding-right: 18px; }
.ch-content .bracket-round { flex: 0 0 276px; width: 276px; scroll-snap-align: start; display: flex; flex-direction: column; gap: 12px; }
.ch-content .bracket-round h3 { font-size: 13px; padding: 0 3px; }
.ch-content .bracket-round .round-matches { display: flex; flex-direction: column; justify-content: space-around; flex: 1; gap: 14px; }
.ch-content .bracket-match { position: relative; border-radius: 15px; padding: 10px; }
.ch-content .bracket-match::after { content: ""; position: absolute; left: 100%; top: 50%; width: 23px; height: 1px; background: var(--line); }
.ch-content .bracket-round:last-child .bracket-match::after { display: none; }
.ch-content .bracket-match .team-line { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-size: 12px; padding: 6px 0; }
.ch-content .bracket-match .team-line.winner { font-weight: 600; }
.ch-content .bracket-match .team-line .mono { min-width: 22px; text-align: right; }
.ch-content .bracket-match.is-human { border-color: var(--ink-soft); }
.ch-content .round-picker { display: flex; gap: 6px; overflow-x: auto; padding: 2px 0 10px; margin-top: 10px; }
.ch-content .round-picker a { min-height: 38px; flex: 0 0 auto; padding: 9px 13px; border: 1px solid var(--line); border-radius: 12px; font-size: 11px; background: var(--control); }
.ch-content .detail-back { min-height: 34px; display: flex; align-items: center; gap: 5px; color: var(--ink-soft) !important; font-size: 12px; margin-bottom: 10px; }
.ch-content .notice { padding: 12px 14px; border-radius: 16px; background: var(--ch-selected); font-size: 12px; color: var(--ink-soft); }
@media (prefers-reduced-motion: reduce) {
  .ch-content .ch-btn:active { transform: none; }
}
.ch-content .team-row.is-human .pos, .ch-content .rating-row.is-human > .mono, .ch-content .match-row.is-human .match-score { color: var(--ink); }
.ch-content .side-mark { display: inline-block; width: 9px; height: 9px; border-radius: 50%; flex: 0 0 9px; }
.ch-content .side-mark.s1 { background: var(--blue); }
.ch-content .side-mark.s2 { background: var(--red); }
.ch-content .side-caption { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; color: var(--ink-soft); font-weight: 400; }
.ch-content .lead-card { padding: 14px 14px 10px; }
.ch-content .lead-chart { display: block; width: 100%; height: 118px; overflow: visible; }
.ch-content .lead-chart .zero { stroke: var(--line); stroke-width: 1; }
.ch-content .lead-chart .up { fill: var(--blue); fill-opacity: var(--fill-alpha); }
.ch-content .lead-chart .down { fill: var(--red); fill-opacity: var(--fill-alpha); }
.ch-content .lead-chart .line { fill: none; stroke: var(--ink-soft); stroke-width: 1.4; vector-effect: non-scaling-stroke; }
.ch-content .lead-chart .tick { stroke: var(--ink-faint); stroke-width: 1; vector-effect: non-scaling-stroke; }
.ch-content .lead-axis { display: flex; justify-content: space-between; margin-top: 6px; font: 10px var(--f-mono); color: var(--ink-soft); }
.ch-content .duel { display: grid; gap: 12px; }
.ch-content .duel-row { display: grid; grid-template-columns: 34px minmax(0,1fr) 34px; gap: 8px; align-items: center; }
.ch-content .duel-row b { font: 500 13px var(--f-mono); text-align: center; }
.ch-content .duel-row .lab { grid-column: 1 / -1; font-size: 11px; color: var(--ink-soft); text-align: center; margin-top: -8px; }
.ch-content .duel-track { display: flex; height: 6px; border-radius: 6px; overflow: hidden; background: var(--line); }
.ch-content .duel-track i { display: block; height: 100%; }
.ch-content .duel-track i.s1 { background: var(--blue); }
.ch-content .duel-track i.s2 { background: var(--red); }
.ch-content .event-list { list-style: none; margin: 0; padding: 2px 12px; }
.ch-content .event-list li { display: grid; grid-template-columns: 52px 12px minmax(0,1fr); gap: 8px; align-items: center; min-height: 42px; border-bottom: 1px solid var(--line); font-size: 12px; }
.ch-content .event-list li:last-child { border: 0; }
.ch-content .event-list .mono { color: var(--ink-soft); font-size: 11px; }
.ch-content .form-mark, .ch-content .lot-mark { display: inline-flex; align-items: center; gap: 2px; margin-left: 4px; padding: 1px 5px; border-radius: 6px; background: var(--resume-tag); color: var(--ink); font: 500 10px/1.5 var(--f-mono); white-space: nowrap; }
.ch-content .rating-row.wide { grid-template-columns: 22px 18px minmax(0,1fr) 44px 44px; }
.ch-content .rating-row.head { min-height: 28px; font-size: 10px; color: var(--ink-soft); }
.ch-content .rating-row.head span:nth-last-child(-n+2) { text-align: right; }
.ch-content .mv { font: 10px var(--f-mono); text-align: center; color: var(--ink-faint); }
.ch-content .mv.up { color: var(--ok); }
.ch-content .mv.down { color: var(--ink-soft); }
.ch-content .rating-row .lvl { text-align: right; font: 13px var(--f-mono); color: var(--ink-soft); }
.ch-content .wl { display: inline-flex; gap: 3px; vertical-align: middle; }
.ch-content .wl i { display: inline-grid; place-items: center; width: 18px; height: 18px; border-radius: 5px; font: 500 10px var(--f-mono); font-style: normal; background: var(--ch-selected); color: var(--ink); }
.ch-content .run-card { padding: 12px 14px; }
.ch-content .run-card .status-line { margin: 0; }
.ch-content .run-card .progress-track { margin: 8px 0 12px; }
.ch-content .team-line.tbd span:not(.mono) { color: var(--ink-soft); font-style: italic; }
.ch-content .round-picker button { min-height: 38px; flex: 0 0 auto; padding: 9px 13px; border: 1px solid var(--line); border-radius: 12px; font-size: 11px; background: var(--control); cursor: pointer; }
.ch-content .round-picker button[aria-pressed="true"] { background: var(--ch-selected); color: var(--ink); }
.ch-content .service { margin-top: 22px; border-top: 1px solid var(--line); padding-top: 14px; display: grid; gap: 8px; }
.ch-content .service .row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.ch-content .pick-group { margin-top: 16px; }
.ch-content .pick-list { padding: 4px 8px; }
.ch-content .pick-team { display: grid; grid-template-columns: minmax(0,1fr) auto; align-items: center; gap: 8px; width: 100%; min-height: 44px; padding: 8px 6px; border: 0; border-bottom: 1px solid var(--line); background: var(--ch-clear); text-align: left; cursor: pointer; }
.ch-content .pick-team:last-child { border: 0; }
.ch-content .pick-team .mono { color: var(--ink-soft); font-size: 12px; }
.ch-content .toggle-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 44px; }
/* ---- 0.1.58: what the game needs on top of the mock-up (tokens only, no colour literals) ---- */
.ch-content :is(a, button, summary, input, select, [role="button"]):focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
/* no href anywhere (task 17): the tap targets are <a role="button"> and <button> — make both
   behave like the mock-up's links */
.ch-content [role="button"], .ch-content button { cursor: pointer; }
.ch-content button.detail-back { border: 0; background: var(--ch-clear); padding: 0; }
.ch-content .ch-btn { margin: 0; height: auto; font-size: 14px; color: var(--ink); }
.ch-content .ch-btn.primary { color: var(--sheet); }
.ch-content .ch-btn + .ch-btn, .ch-content .ch-btn + .text-btn { margin-top: 8px; }
.ch-content .text-btn { width: 100%; margin: 4px 0 0; font-size: 13px; }
.ch-content .action-card .next-secondary { margin-top: 10px; }
.ch-content .rule-card .muted { margin-top: 6px; }
.ch-content .legend.pick-lead { padding: 12px 2px 0; }
.ch-content .swipe-hint { margin-bottom: 4px; }
.ch-content .thirds-note { margin: -4px 0 10px; }
.ch-content .scoreboard .when { margin-top: 8px; }
.ch-content .notes { margin-top: 10px; }
.ch-content .scoreboard + .ch-btn { margin-top: 12px; }
/* the flag: the game's own component (flags.js) in the mock-up's 22×15 frame (task 20) */
.ch-content .flag { width: 22px; height: 15px; flex: 0 0 22px; border-radius: 3px; font-size: 13px; }
.ch-content .flag.flag-plate { font-size: 5.5px; letter-spacing: 0; }
.ch-content .team-name.tbd > span { color: var(--ink-soft); font-style: italic; }
/* a team name with a form or lot mark still wraps inside its cell, the marks do not */
.ch-content .standings .name-cell a { cursor: pointer; }
.ch-content .form-mark { cursor: pointer; position: relative; }
.ch-content .form-mark::after { content: ""; position: absolute; inset: -8px -4px; }
/* contrast (Д-01 §2.3): the qualified place number is ink; only the ✓ is green */
.ch-content .standings .team-row.qualified .pos { color: var(--ink); }
/* secondary text on a selected surface is ink (4,27 → 14,18 in Air light) */
.ch-content .segmented button { color: var(--ink); }
.ch-content .notice { color: var(--ink); }
/* the calendar's stage filter */
.ch-content .filter-row select { color: var(--ink); }
/* the whole row is the target for a match, the bracket card too */
.ch-content .match-row, .ch-content .bracket-match { display: grid; cursor: pointer; }
.ch-content .bracket-match { display: block; }
.ch-content .match-row:active, .ch-content .bracket-match:active { background: var(--ch-selected); }
.ch-content .match-row.is-human:active { background: var(--line); }
/* the pick screen reuses the app's own switch (Settings) — the mock-up's was a picture of one */
.ch-content .toggle-row { cursor: pointer; }
.ch-content .toggle-row .switch { flex: 0 0 51px; }
/* the third places: rank · team · group · points · ± · dots for (task 8) */
.ch-content .rating-row.third { grid-template-columns: 22px minmax(0, 1fr) 26px 26px 36px 30px; }
.ch-content .rating-row.third > :nth-child(n+3) { text-align: right; }
.ch-content .rating-row.third strong { font: 500 13px var(--f-mono); }
.ch-content .rating-row.third.head { min-height: 28px; font-size: 10px; color: var(--ink-soft); }
.ch-content .rating-row.third.through > .mono:first-child { color: var(--ok); }
.ch-content .rating-row.cut { display: block; min-height: 0; padding: 6px 3px; text-align: center; font-size: 10px;
  letter-spacing: .04em; color: var(--ink-soft); border-bottom: 1px dashed var(--ink-faint); }
.ch-content .rating-row.wide > a, .ch-content .rating-row.third > a { cursor: pointer; }
/* the rating list's movement mark sits in its own column */
.ch-content .rating-row .mv { white-space: nowrap; }
/* the lead graph's axis never wraps into the chart */
.ch-content .lead-axis span:nth-child(2) { text-align: center; }
@media (prefers-reduced-motion: reduce) { .ch-content .ch-btn:active { transform: none; } }
.ch-content .row2 > .ch-btn { margin-top: 0; }
/* `.sheet h3` is the small-caps label of the other sheets; inside the championship an h3 is a heading */
.ch-content h3 { text-transform: none; letter-spacing: normal; color: var(--ink); margin: 0; }
/* «move 100» is wider than «хід 100»: the move column takes what its words need, never wraps */
.ch-content .event-list li { grid-template-columns: minmax(52px, max-content) 12px minmax(0, 1fr); }
.ch-content .event-list .mono { white-space: nowrap; }
/*
 * 0.1.58a · two defects from the first look on an iPhone (Слава, 25.09).
 *
 * 1. A NUMBER NEVER BREAKS. «+176» came out as «+17» over «6»: the mock-up sized every number
 *    column for its own data (32 px), and a real group of three reaches four characters. Numbers
 *    stay on one line, and the ± column — the only one that reaches four — is wider.
 * 2. A LONG NAME WRAPS ITS WORDS, NOT ITS FLAG. «Саудівська Аравія» did not fit beside its flag,
 *    so the flag went up to a line of its own while the other side kept it inline: two different
 *    layouts in one card. The side no longer wraps; the name inside it does.
 */
.ch-content .standings td, .ch-content .standings th { white-space: nowrap; }
.ch-content .standings td.name-cell { white-space: normal; }
.ch-content .standings .diff-col { width: 42px; }
.ch-content .full-table { min-width: 540px; }
.ch-content .full-table .num-col { width: 38px; }
.ch-content .full-table .diff-col { width: 44px; }
.ch-content .versus .side { flex-wrap: nowrap; }
.ch-content .versus .side > span:not(.flag-case) { min-width: 0; }
.ch-content .rating-row.third { grid-template-columns: 22px minmax(0, 1fr) 26px 26px 42px 34px; }
.ch-content .rating-row > * { white-space: nowrap; }
.ch-content .rating-row > a { white-space: normal; }
.ch-content .metric strong { white-space: nowrap; }
.ch-content .metric.long strong { font-size: 18px; line-height: 1.6; }
/*
 * 0.1.58b · THE BRACKET AS A TREE AGAIN, AND ALL OF IT ON ONE SCREEN.
 *
 * Connectors (Слава's photo, 25.09): the mock-up's were 1 px stubs of `--line` — 10 % grey in Air —
 * sticking out of each card and joining nothing. Now every column has the same height (`--rows`
 * slots), a tie sits in the middle of its slot, and the joins are half-height borders: out of each
 * card to the middle of the gap, down (or up) to where its pair meets, and in to the next card —
 * which sits exactly there, because its slot is two of the previous column's. `--ink-faint`, 1.5 px:
 * an existing token, no new one.
 */
.ch-content .bracket-tree { --slot: 118px; --half: 11px; gap: calc(2 * var(--half)); }
.ch-content .bracket-round .round-matches { display: flex; flex-direction: column; justify-content: flex-start;
  gap: 0; flex: none; height: calc(var(--rows, 16) * var(--slot)); }
.ch-content .bracket-round .slot { position: relative; flex: 1 0 0; min-height: 0; display: flex; align-items: center; }
.ch-content .bracket-round .slot > .bracket-match { width: 100%; }
.ch-content .bracket-match::after { display: none; }
.ch-content .bracket-round:not(:last-child) .slot::after { content: ""; position: absolute; right: calc(-1 * var(--half));
  width: var(--half); height: 50%; border-right: 1.5px solid var(--ink-faint); box-sizing: border-box; }
.ch-content .bracket-round:not(:last-child) .slot:nth-child(odd)::after { top: 50%; border-top: 1.5px solid var(--ink-faint); border-top-right-radius: 6px; }
.ch-content .bracket-round:not(:last-child) .slot:nth-child(even)::after { top: 0; border-bottom: 1.5px solid var(--ink-faint); border-bottom-right-radius: 6px; }
.ch-content .bracket-round:not(:first-child) .slot::before { content: ""; position: absolute; left: calc(-1 * var(--half));
  top: calc(50% - 0.75px); width: var(--half); border-top: 1.5px solid var(--ink-faint); }
/* «Уся сітка»: compact cards, narrow columns, no snapping — champ.js scales the tree to the sheet */
.ch-content .bracket-tools { display: flex; justify-content: flex-end; margin: 10px 0 0; }
.ch-content .bracket-tools .ch-btn { width: auto; min-height: 38px; padding: 8px 14px; font-size: 12px; }
.ch-content .bracket-tools .ch-btn[aria-pressed="true"] { background: var(--ch-selected); }
.ch-content .bracket-scroll.overview { overflow: hidden; scroll-snap-type: none; }
.ch-content .bracket-tree.overview { --slot: 64px; --half: 8px; padding-right: 0; }
/* 0.1.65: once two fingers have set the scale, the tree scrolls freely both ways — snapping to a
 * round would fight the scale, and «Уся сітка» (overflow hidden) must scroll once it is enlarged */
.ch-content .bracket-scroll.pinched { overflow-x: auto; scroll-snap-type: none; touch-action: pan-x pan-y; }
.ch-content .bracket-tree.overview .bracket-round { flex: 0 0 112px; width: 112px; }
.ch-content .bracket-match.compact { padding: 5px 8px; border-radius: 10px; }
.ch-content .bracket-match.compact .team-line { padding: 2px 0; gap: 6px; font-size: 12px; }
.ch-content .bracket-match.compact .team-name { gap: 5px; }

/* ======================================================================================================
 * ONLINE (0.1.85, «Онлайн 2 · Онлайн у грі»)
 * ====================================================================================================== */

/* «Грати з другом»: the code is the thing a person reads aloud or types — big, monospaced, unbreakable */
#sheet-online .onl-lead { color: var(--ink-soft); margin: 4px 0 6px; line-height: 1.45; }
#sheet-online .onl-code {
  margin: 14px 0 0; text-align: center;
  font-family: var(--f-mono); font-size: 26px; font-weight: 500; letter-spacing: 0.06em;
  color: var(--ink); white-space: nowrap; font-variant-numeric: tabular-nums;
  user-select: all; -webkit-user-select: all;
}
/* 0.1.85a: the code is a button — one tap copies it (П2) */
#sheet-online button.onl-code {
  display: block; width: 100%; padding: 6px 0; border: 0; border-radius: 12px; background: transparent;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
#sheet-online button.onl-code:active { background: var(--line); }
#sheet-online .onl-tap { text-align: center; margin-top: 2px; }
#sheet-online .onl-status { text-align: center; color: var(--ink-soft); font-size: 14px; margin: 16px 0 4px; min-height: 1.4em; }
#sheet-online .text-btn { display: block; margin: 8px auto 0; }
#sheet-online .onl-rtt { text-align: center; font-size: 12px; color: var(--ink-faint); margin-top: 18px; }
.onl-code-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; align-items: stretch; }
.onl-code-row input {
  min-width: 0; height: 48px; padding: 0 12px;
  border: 1px solid var(--line); border-radius: 14px; background: transparent; color: var(--ink);
  font-family: var(--f-mono); font-size: 16px; letter-spacing: 0.04em; text-transform: uppercase;
}
.onl-code-row input::placeholder { color: var(--ink-faint); }
.onl-code-row .secondary { width: auto; margin-top: 0; padding: 0 16px; }

/* the line over the board, online: my clock · the format · theirs. The running clock is lit, under 30 s red. */
.match-caption.is-online {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  letter-spacing: 0.02em; text-overflow: clip;
}
.match-caption .oc {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 8px; border-radius: 999px;
  color: var(--ink-soft); font-weight: 600;
}
.match-caption .oc i { width: 7px; height: 7px; border-radius: 50%; background: var(--blue); flex: none; }
.match-caption .oc.red i { background: var(--red); }
.match-caption .oc b { font-family: var(--f-mono); font-weight: 500; font-variant-numeric: tabular-nums; color: var(--ink); }
.match-caption .oc.run { background: var(--paper-deep); color: var(--ink); }
.match-caption .oc.low b { color: var(--red); }
.match-caption .oc-mid { color: var(--ink-faint); font-weight: 500; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
@media (max-width: 380px) { .match-caption .oc-mid { display: none; } }

/* the undo button turned «Здатися» */
.dock-btn.is-resign span { color: var(--red); }
.dock-btn.is-resign svg { display: none; }

/* ---- 0.1.87: the picker over every FIFA member, and the draw on screen (Р-283, Р-284) ---- */
.ch-content .pick-search { width: 100%; min-height: 44px; margin: 4px 0 10px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 12px; background: var(--control); font-size: 15px; }
/* the continents wrap rather than scroll sideways: a chip off the edge is a chip nobody finds (champ-sweep) */
.ch-content .pick-confs { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 4px; }
.ch-content .pick-confs button { flex: 0 0 auto; min-height: 34px; padding: 6px 12px; border: 1px solid var(--line); border-radius: 999px; background: var(--ch-clear); color: var(--ink-soft); font-size: 12px; cursor: pointer; }
.ch-content .pick-confs button[aria-pressed="true"] { background: var(--ch-action); color: var(--sheet); border-color: var(--ch-action); }
.ch-content .pick-side { display: inline-flex; align-items: center; gap: 8px; }
.ch-content .pick-tag { font-size: 11px; color: var(--ink-soft); white-space: nowrap; }
.ch-content .pick-tag.in { color: var(--ink); }
.ch-content .card.section, .ch-content .ch-card.section { margin-top: 12px; }
.ch-content .draw-now { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; font-size: 15px; margin: 6px 0 4px; }
.ch-content .draw-arrow { color: var(--ink-soft); }
.ch-content .draw-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin: 12px 0; }
@media (min-width: 620px) { .ch-content .draw-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.ch-content .draw-group { padding: 8px 10px; }
.ch-content .draw-group .kicker { margin-bottom: 4px; }
.ch-content .draw-slot { display: flex; align-items: center; gap: 6px; min-height: 24px; font-size: 13px; line-height: 1.2; white-space: nowrap; overflow: hidden; }
.ch-content .draw-slot > span { overflow: hidden; text-overflow: ellipsis; }
.ch-content .draw-slot .tbd { color: var(--ink-soft); }
.ch-content .draw-slot.new { font-weight: 600; animation: draw-in .45s ease-out; }
.ch-content .draw-slot.you { font-weight: 600; }
.ch-content .draw-slot.slot { font-weight: 600; }
.ch-content .draw-or { color: var(--ink-soft); font-weight: 500; flex: 0 1 auto; }
@keyframes draw-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .ch-content .draw-slot.new { animation: none; } }
.ch-content .draw-slot.slot { white-space: normal; flex-wrap: wrap; row-gap: 0; }
.ch-content .draw-slot.slot > span { overflow: visible; }

/* 0.2.1 «Бета-тест»: the policy link under the question and in Settings — the game's ink, not the browser's default blue */
.sheet .hint a.beta-policy { color: var(--ink-soft); text-decoration: underline; text-underline-offset: 2px; }
