/*
 * ДИЗАЙН-СИСТЕМА «КОЛХОЗА»
 *
 * Один источник облика для всех экранов и всех игр. Любой новый
 * экран подключает этот файл и получает тот же стиль — цвета, отступы,
 * кнопки, бильярдный шар как опорный образ — без единой своей строки.
 *
 * Правило: ничего игрового здесь быть не должно. Только то, что
 * одинаково для любой игры. Если правило нужно лишь одной игре —
 * ему место в файле этой игры, а не здесь.
 *
 * Раньше лежало прямо в странице. Вынесено, потому что страница
 * меняется с каждой правкой, а облик — нет: теперь браузер
 * перекачивает только то, что действительно изменилось.
 */
/* ============================================================
   ШРИФТ JetBrains Mono — лежит на нашем сервере, не на чужом CDN,
   иначе приложение перестанет открываться без интернета.
   Взяты только кириллица и латиница: греческий и вьетнамский
   здесь не нужны, это лишние 40 КБ.
============================================================ */
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:100 800;font-display:swap;
  src:url('/fonts/jetbrains-mono-cyrillic-ext.woff2') format('woff2');
  unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:100 800;font-display:swap;
  src:url('/fonts/jetbrains-mono-cyrillic.woff2') format('woff2');
  unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
/*
 * Только знак рубля. Раньше здесь висел весь набор «латиница
 * расширенная» — 11,4 КБ, из которых приложению нужен ровно
 * один знак: ₽ стоит в каждой сумме, а больше оттуда ничего
 * не встречается. Урезанный файл весит 884 байта, ось
 * насыщенности в нём сохранена.
 *
 * Цена: если в имени игрока попадётся редкая латинская буква
 * с надстрочным знаком, она нарисуется системным шрифтом.
 * Для русских и английских имён это не случается.
 */
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:100 800;font-display:swap;
  src:url('/fonts/jetbrains-mono-ruble.woff2') format('woff2');
  unicode-range:U+20BD}
/*
 * ЗАГОЛОВОЧНЫЙ ШРИФТ — Montserrat Alternates, начертание 700.
 * Им набраны и «ЛУЗЕРЫ» на входе, и название игры в шапке: одна
 * рука на всех заголовках.
 *
 * Урезан до прописной кириллицы, цифр и разделителей — 3,9 КБ на два
 * файла вместо целого набора. Строчных букв в нём нет нарочно:
 * заголовки в этом приложении всегда прописные, и лишние начертания
 * возит с собой каждый телефон.
 *
 * Файлов два, потому что у Google цифры и кириллица лежат в разных
 * наборах: сначала урезал один кириллический — и «Пирамида 140»
 * осталась без цифр, они нарисовались бы запасным шрифтом.
 *
 * Лежат у нас, а не тянутся с чужого сервера: иначе заголовки
 * пропадали бы без сети, а приложение обязано работать в подвале
 * бильярдной. Начертание ровно одно, поэтому просить у него другую
 * жирность бессмысленно — браузер нарисует поддельный жир.
 */
@font-face{font-family:'Montserrat Alternates';font-style:normal;font-weight:700;font-display:swap;
  src:url('/fonts/ma-logo-cyr.woff2') format('woff2');
  unicode-range:U+0401,U+0410-042F,U+2116}
@font-face{font-family:'Montserrat Alternates';font-style:normal;font-weight:700;font-display:swap;
  src:url('/fonts/ma-logo-lat.woff2') format('woff2');
  unicode-range:U+0020,U+0021,U+0028-0029,U+002B,U+002C-002E,U+0030-0039,U+003A,U+003F,U+00AB,U+00B7,U+00BB,U+00D7,U+2013-2014}

@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:100 800;font-display:swap;
  src:url('/fonts/jetbrains-mono-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}

/* ============================================================
   1. ОСНОВАНИЯ СИСТЕМЫ
   Все величины заданы здесь: цвета, поверхности, скругления,
   отступы, тени. Ниже в разметке чисел быть не должно —
   иначе система расползётся, как было до этого.
============================================================ */
:root{
  /*
   * Приложение тёмное всегда, и об этом надо сказать браузеру вслух:
   * иначе он рисует свои части — галочки, переключатели, стрелки
   * выпадающих списков, полосы прокрутки, подсветку выделения —
   * по светлой схеме, и они выглядят чужими заплатами на сукне.
   * Здесь же это лечит и системные цвета вроде `buttontext`.
   */
  color-scheme:dark;

  /* сукно стола */
  --felt-1:#157349;            /* свет сверху */
  --felt-2:#0c4630;            /* основной зелёный */
  --felt-3:#07281d;            /* затемнение к краям */

  /* поверхности: одна лестница вместо россыпи разных rgba */
  --surface-1:rgba(0,0,0,.18);   /* строки, чипы, мягкая подложка */
  --surface-2:rgba(0,0,0,.30);   /* поля ввода, плашки, кнопки счётчика */
  --surface-3:rgba(5,38,27,.90); /* карточки */

  /* линии */
  --line:rgba(255,255,255,.11);
  --line-2:rgba(255,255,255,.20);       /* заметнее: разделители в таблицах */
  --line-gold:rgba(212,175,55,.30);

  /* золото — акцент, а не заливка */
  --gold:#d4af37;
  --gold-2:#f1d97a;
  --gold-dim:rgba(212,175,55,.10);

  /* текст */
  --text:#f3efe2;
  --muted:#b5d0c1;            /* поднят контраст: было #9fbfae */
  --muted-2:#93b3a2;          /* совсем второстепенное */

  /* значения */
  --pos:#7ee6b2;
  --neg:#ff9d90;
  --danger:#e0574a;
  --plus:#ff5c4d;             /* кнопка «больше» */
  --minus:#6fc9ff;            /* кнопка «меньше» */

  /* скругления */
  --r-s:10px; --r-m:14px; --r-l:18px; --r-pill:999px;

  /* отступы */
  --sp-1:6px; --sp-2:10px; --sp-3:14px; --sp-4:18px; --sp-5:24px;

  /* тени */
  --sh-card:0 10px 30px rgba(0,0,0,.35);
  --sh-btn:0 6px 18px rgba(212,175,55,.22);
  --sh-pop:0 24px 60px rgba(0,0,0,.60);

  /* высота элементов, в которые надо попадать пальцем */
  --tap:54px;
  --tap-min:44px;
}

*{box-sizing:border-box;margin:0;padding:0}
html{-webkit-text-size-adjust:100%}
body{
  min-height:100vh;min-height:100svh;
  font-family:'JetBrains Mono',ui-monospace,'SF Mono',Menlo,Consolas,'Liberation Mono',monospace;
  color:var(--text);line-height:1.45;
  background:radial-gradient(120% 90% at 50% -10%,var(--felt-1) 0%,var(--felt-2) 48%,var(--felt-3) 100%) var(--felt-3);
  padding:16px 16px 40px;
  padding:clamp(10px,3vw,24px) clamp(10px,3vw,24px) 40px;
}
body::before{content:'';position:fixed;top:0;right:0;bottom:0;left:0;inset:0;z-index:-1;pointer-events:none;
  box-shadow:inset 0 0 170px rgba(0,0,0,.5)}
:focus-visible{outline:2px solid var(--gold-2);outline-offset:2px;border-radius:4px}
.app{max-width:780px;margin:0 auto}

/* Подписи — единый приём на всё приложение */
.field-label,.stat .k,.st-label,.code-panel .cap,thead th{
  font-size:.72rem;text-transform:uppercase;letter-spacing:.06em;font-weight:700}

/* ============================================================
   2. ШАПКА
============================================================ */
/*
 * Шапка всегда в две строки, и на телефоне, и на большом экране.
 *
 * Верхняя: название игры слева, выход из партии справа — прижат
 * к самому краю окна, потому что туда тянется большой палец.
 * Нижняя: полоса состояния во всю ширину.
 *
 * Раньше выход лежал внутри правой колонки вместе с плашками, и на
 * телефоне колонка занимала всю ширину — кнопка уезжала под название
 * вместо того, чтобы стоять рядом с ним. Строка задаётся здесь один
 * раз, поэтому телефон и десктоп больше не расходятся.
 */
.app-header{display:flex;flex-direction:column;align-items:stretch;
  gap:var(--sp-2);margin-bottom:var(--sp-3)}

/*
 * Верхняя строка — отдельный ряд, и переносов в нём нет.
 *
 * Раньше название и выход были прямыми детьми шапки, а перенос строк
 * делал сам `flex-wrap`. На узком экране это подводило: длинное
 * название («ПИРАМИДА 140») не давало себя ужать — у слова есть
 * наименьшая ширина, меньше которой оно не становится, — и выход
 * спрыгивал под заголовок. У кого экран поуже или шрифт системы
 * покрупнее, тот это и видел.
 *
 * Теперь ряд жёсткий: `flex-wrap:nowrap`. Если названию не хватит
 * места, оно перенесётся внутри себя на две строки, а плашка выхода
 * останется в правом верхнем углу. Лучше двухэтажный заголовок,
 * чем уехавшая кнопка: заголовок читают, кнопку ищут.
 */
.header-top{display:flex;flex-wrap:nowrap;align-items:center;
  justify-content:space-between;gap:var(--sp-2);width:100%}
.title-wrap{flex:1 1 auto;min-width:0}
.title-wrap h1{min-width:0;flex-wrap:wrap}
.pill-back{flex:none}
/*
 * Название игры в шапке — тем же шрифтом, что «ЛУЗЕРЫ» на входе.
 * Разрядка меньше, чем была у моноширинного: у пропорционального
 * шрифта буквы и так стоят свободно, и прежние .10em растаскивали
 * слово так, что «ПИРАМИДА 140» переставала читаться как одно целое.
 * Жирность ровно 700 — другой в файле нет, а просить её значило бы
 * получить поддельный жир с поплывшими краями.
 */
h1{font-family:'Montserrat Alternates','JetBrains Mono',ui-monospace,monospace;
  font-size:1.8rem;font-size:clamp(1.55rem,6vw,2.1rem);letter-spacing:.03em;
  color:var(--gold-2);display:flex;align-items:center;gap:var(--sp-2);font-weight:700}
.logo{font-size:1.15em}
.subtitle{color:var(--muted-2);font-size:.82rem;margin-top:2px}
.header-side{flex:1 1 100%;display:flex;flex-direction:column;
  align-items:flex-end;gap:var(--sp-2)}

/*
 * Шапка на время партии. Всё, что нужно за столом, — название игры,
 * связь, цена очка и выход. Остальное съедало верх экрана, а бьют
 * по шарам стоя, держа телефон одной рукой.
 */
/*
 * `align-items` здесь не задаём. Когда шапка была одной строкой,
 * `center` выравнивал название и плашки по высоте. Теперь шапка —
 * колонка, и та же строка стала выравнивать по горизонтали: полоса
 * состояния переставала тянуться во всю ширину и съезжала к середине,
 * отчего «ОНЛАЙН» отходил от левого края на два десятка точек.
 * Третья подпорка от прежней раскладки подряд — их теперь ни одной.
 */
.app-header.tight{margin-bottom:var(--sp-2)}
.app-header.tight h1{font-size:1.2rem;font-size:clamp(1.05rem,4.6vw,1.45rem);
  letter-spacing:.08em}
.app-header.tight .logo{font-size:1em}
/* отдельного размера в ужатой шапке не нужно: плашка одна и та же */

.btn-link{background:none;border:none;color:var(--muted);text-decoration:underline;
  font-size:.85rem;cursor:pointer;padding:6px;font-family:inherit;
  transition:color .15s}
/* «Профиль» больше не ссылка и не живёт в боковой колонке — см. .pill-back */

.status{display:flex;align-items:center;gap:var(--sp-2);flex-wrap:wrap;justify-content:flex-end}

/* Плашка — один компонент на все состояния */
/*
 * `color` здесь обязателен, и вот почему. Плашка комнаты — это <button>,
 * а у кнопки в правилах самого браузера уже стоит `color: buttontext`.
 * Это сильнее наследования, так что без своей строки цвет надписи
 * назначает не мы, а система: в Chrome на настольной машине выходил
 * чёрный по тёмному, в Chrome на телефоне — белый. Разными были не
 * браузеры, а системная палитра, на которую мы по недосмотру опирались.
 */
.pill,.nominal-badge{display:inline-flex;align-items:center;gap:6px;
  border-radius:var(--r-pill);padding:5px 11px;font-size:.78rem;font-weight:700;
  white-space:nowrap;border:1px solid var(--line);background:var(--surface-2);
  color:var(--text)}
.nominal-badge{color:var(--gold-2)}
/* Ширина плашки связи постоянна, иначе она дёргается при смене надписи */
#statusBar .pill:first-child{min-width:84px;justify-content:center}
.pill.ok{border-color:rgba(126,230,178,.5);color:var(--pos)}
.pill.wait{border-color:rgba(212,175,55,.6);color:var(--gold-2)}
.pill.off{border-color:rgba(224,87,74,.6);color:var(--neg)}
.pill button{background:none;border:none;color:inherit;font:inherit;cursor:pointer;
  text-decoration:underline;padding:0}
.led{width:9px;height:9px;border-radius:50%;background:currentColor;flex:none}
.pill.ok .led{box-shadow:0 0 8px currentColor}
/*
 * Плашка выхода из партии.
 *
 * Размеры не свои: всё берётся от `.pill` — те же отступы, тот же кегль,
 * та же высота, что у «ОНЛАЙН» и у плашки комнаты в соседней строке.
 * Своя высота «не меньше пальца» была ошибкой: плашка вырастала вдвое
 * против соседних и сбивала строку. В шапке ряд одинаковых плашек
 * читается быстрее, чем одна большая среди мелких.
 *
 * Красная — как у «нет связи»: это выход, и он должен опознаваться
 * до чтения надписи. Под пальцем разгорается, а не меняет цвет:
 * красный здесь значение, а не подсветка.
 */
.pill-back{cursor:pointer;font-family:inherit;
  border-color:rgba(224,87,74,.55);color:var(--neg);
  text-transform:uppercase;letter-spacing:.04em;
  transition:border-color .15s,background .15s}
/*
 * Стрелка нарисована рамками, а не набрана знаком «←».
 *
 * Знака U+2190 в нашем наборе JetBrains Mono нет — там только ↑ и ↓.
 * Браузер молча брал его из системного шрифта: другой кегль, другая
 * базовая линия, оттого стрелка выглядела мелкой и осевшей вниз.
 * Проверяется это не на глаз, а сравнением ширин: у моноширинного
 * шрифта «←» и «П» обязаны занимать одинаково, а они расходились.
 *
 * Нарисованная стрелка ничего не просит у шрифта: размер задан в `em`,
 * поэтому она растёт вместе с надписью, цвет берётся у неё же, а стоит
 * ровно посередине — за это отвечает `align-items:center` у плашки.
 */
.pill-back::before{content:'';width:.44em;height:.44em;flex:none;
  border-left:.17em solid currentColor;border-bottom:.17em solid currentColor;
  transform:rotate(45deg);margin-right:1px}
.pill-back:active{transform:scale(.96)}

.room-pill{cursor:pointer;font-family:inherit;padding-left:18.5px;padding-right:18.5px;
  transition:border-color .15s}

/* ============================================================
   3. КАРТОЧКИ
============================================================ */
.card{background:var(--surface-3);border:1px solid var(--line-gold);border-radius:var(--r-l);
  padding:18px;padding:clamp(14px,4vw,22px);margin-bottom:var(--sp-4);box-shadow:var(--sh-card)}
.card-title{color:var(--gold-2);font-size:1.12rem;margin-bottom:var(--sp-3);
  letter-spacing:.06em;text-transform:uppercase;font-weight:800}
.card-sub{color:var(--muted);font-size:.85rem;margin:-8px 0 var(--sp-3)}
.hint{color:var(--muted);font-size:.85rem;margin:var(--sp-2) 0}
.empty{color:var(--muted);font-size:.9rem;padding:6px 0}

/* ============================================================
   4. БИЛЬЯРДНЫЙ ШАР — опорный образ всей системы.
   Используется и в очереди игроков, и в таблице результатов,
   чтобы номер игрока всюду читался одинаково.
============================================================ */
.q-ball{
  --ball:34px;
  width:var(--ball);height:var(--ball);min-width:var(--ball);
  border-radius:50%;display:inline-grid;place-items:center;
  color:#fff;font-weight:900;font-size:calc(var(--ball) * .45);
  text-shadow:0 1px 2px rgba(0,0,0,.75);
  border:2px solid rgba(255,255,255,.78);
  box-shadow:
    inset -4px -6px 10px rgba(0,0,0,.45),   /* объём снизу */
    inset  3px  4px  8px rgba(255,255,255,.30), /* блик сверху */
    0 2px 5px rgba(0,0,0,.45);
}
.q-ball.sm{--ball:26px;border-width:1.5px}
.q-ball.xs{--ball:22px;border-width:1.5px}

/* ============================================================
   5. ПОЛЯ ВВОДА
============================================================ */
.field{display:block;margin-bottom:var(--sp-3)}
.field-label{display:block;color:var(--muted);margin-bottom:6px}
select,.name-input,.balls,.text-box{
  width:100%;min-height:var(--tap);font-size:1.05rem;font-weight:500;
  color:var(--text);font-family:inherit;
  background:var(--surface-2);border:2px solid var(--line);
  border-radius:var(--r-s);padding:8px 14px;transition:border-color .15s,box-shadow .15s}
select{appearance:none;-webkit-appearance:none;padding-right:38px;cursor:pointer;
  background-image:linear-gradient(45deg,transparent 50%,var(--gold-2) 50%),
                   linear-gradient(135deg,var(--gold-2) 50%,transparent 50%);
  background-position:calc(100% - 20px) calc(50% + 2px),calc(100% - 14px) calc(50% + 2px);
  background-size:6px 6px,6px 6px;background-repeat:no-repeat}
select option{background:#0c4630;color:var(--text)}
select:focus,.name-input:focus,.balls:focus,.text-box:focus{
  border-color:var(--gold);outline:none;box-shadow:0 0 0 3px rgba(212,175,55,.22)}
.invalid{border-color:var(--danger)!important;background:rgba(224,87,74,.10)}
input[disabled],select[disabled]{opacity:.55;cursor:not-allowed}
.names{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-2);margin-bottom:var(--sp-3)}

/* ============================================================
   6. КНОПКИ
   Действие всегда заглавными: так кнопка читается как кнопка,
   а не как обычная строка текста.
============================================================ */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;
  min-height:var(--tap);padding:10px 22px;border-radius:var(--r-m);border:none;
  cursor:pointer;font-family:inherit;font-size:1rem;font-weight:800;
  letter-spacing:.06em;text-transform:uppercase;
  transition:transform .06s ease,filter .15s,box-shadow .15s}
.btn:active:not([disabled]){transform:scale(.97)}
.btn[disabled]{opacity:.5;cursor:not-allowed}
.btn-primary{background:linear-gradient(180deg,var(--gold-2),var(--gold) 60%,#b8952b);
  color:#000;box-shadow:var(--sh-btn)}
.btn-ghost{background:var(--surface-1);border:2px solid var(--gold);color:var(--gold-2)}
.btn-ghost.danger{border-color:var(--danger);color:var(--neg)}
.btn-big{width:100%}
/*
 * Третий вес кнопки. Золото на «Колхозе» означает «жми сюда»,
 * и когда золотых плашек подряд четыре, оно перестаёт значить
 * что-либо. Приглушённая — для действий, которые нужны редко,
 * но ошибиться в которых дорого: «Сменить игру» сбрасывает выбор.
 * Размер пальца сохранён: тише не значит мельче.
 */
.btn-quiet{width:100%;background:transparent;border:1px solid var(--line-2);
  color:var(--muted);font-weight:700;min-height:var(--tap-min);
  font-size:.85rem;letter-spacing:.1em}
.btn-row{display:flex;gap:var(--sp-2);flex-wrap:wrap;margin-top:var(--sp-4)}
.btn-row .btn{flex:1 1 200px}

/* ============================================================
   7. СЧЁТЧИК ШАРОВ
   Родные стрелки <input type=number> не годятся: на телефоне их
   нет вовсе, а на десктопе они видны только при наведении и
   слишком мелкие для пальца. Поэтому свои, во всю высоту поля.
============================================================ */
.balls::-webkit-outer-spin-button,
.balls::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.balls{-moz-appearance:textfield;appearance:textfield;
  width:130px;text-align:center;font-size:1.35rem;font-weight:800}

.stepper{display:inline-flex;align-items:stretch;flex:none}
.stepper .balls{width:clamp(72px,18vw,110px);border-radius:0;
  border-left-color:transparent;border-right-color:transparent;position:relative;z-index:1}
.step-btn{flex:none;width:54px;min-height:var(--tap);
  display:inline-flex;align-items:center;justify-content:center;
  font-family:inherit;font-size:1.6rem;font-weight:800;line-height:1;
  background:var(--surface-2);border:2px solid var(--line);cursor:pointer;
  -webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;
  transition:filter .15s,transform .06s ease,border-color .15s}
.step-btn:first-child{border-radius:var(--r-s) 0 0 var(--r-s)}
.step-btn:last-child{border-radius:0 var(--r-s) var(--r-s) 0}
.step-btn:active:not([disabled]){transform:scale(.94)}
.step-btn[disabled]{opacity:.55;cursor:not-allowed}
.step-btn[data-step="1"]{color:var(--plus)}
.step-btn[data-step="-1"]{color:var(--minus)}
.stepper:focus-within .step-btn{border-color:var(--gold)}

/* ============================================================
   8. ОЧЕРЕДЬ И СТРОКИ ИГРОКОВ
============================================================ */
.queue{display:flex;align-items:center;flex-wrap:wrap;gap:var(--sp-1);margin-bottom:var(--sp-2)}
.q-chip{display:inline-flex;align-items:center;gap:8px;background:var(--surface-2);
  border:1px solid var(--line);border-radius:var(--r-pill);
  padding:5px 14px 5px 5px;font-weight:700;font-size:.92rem}

.standings{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-1);
  margin:4px 0 var(--sp-3);background:var(--surface-1);border:1px solid var(--line);
  border-radius:var(--r-s);padding:var(--sp-2) var(--sp-3)}
.st-label{color:var(--gold-2);margin-right:2px}
.st-chip{border:1px solid var(--line);border-radius:var(--r-pill);padding:4px 12px;
  font-weight:700;font-size:.9rem;background:var(--surface-2)}

.players{display:flex;flex-direction:column;gap:var(--sp-2);margin-bottom:var(--sp-1)}
.player-row{background:var(--surface-1);border:1px solid var(--line);
  border-radius:var(--r-m);padding:var(--sp-3);transition:border-color .2s,box-shadow .2s}
.player-row.is-last{border-color:var(--gold);box-shadow:0 0 0 1px var(--gold) inset}
.pr-top{display:flex;align-items:center;gap:var(--sp-2);margin-bottom:var(--sp-2);flex-wrap:wrap}
.pname{font-weight:700;font-size:1.05rem;overflow-wrap:break-word;overflow-wrap:anywhere}
.pr-inputs{display:flex;align-items:center;gap:var(--sp-2);flex-wrap:wrap}
.last-toggle{display:inline-flex;align-items:center;gap:var(--sp-2);cursor:pointer;
  -webkit-user-select:none;user-select:none;min-height:var(--tap-min);padding:10px 14px;
  border:1.5px dashed var(--line-gold);border-radius:var(--r-s);
  font-size:.95rem;font-weight:600;color:var(--gold-2);transition:border-color .15s,background .15s}
/*
 * Переключатель последнего шара — сам белый шар, а не системная точка.
 *
 * Рисовать его самим пришлось не ради красоты. Родной переключатель
 * каждая система красит по-своему: под светлой схемой он выходил белым
 * кружком, под тёмной — серым пятном на зелёном, которое едва видно.
 * Опираться на чужую палитру в единственном спорном месте партии
 * («кто забил последний?») нельзя.
 *
 * Объём собран теми же тенями, что у нумерованных шаров: внутренняя
 * тень снизу, блик сверху. Выбранный обведён золотом и светится —
 * издали видно, у кого стоит отметка, не читая подписей.
 */
.last-toggle input{
  appearance:none;-webkit-appearance:none;
  width:28px;height:28px;min-width:28px;flex:none;cursor:pointer;
  border-radius:50%;margin:0;
  background:radial-gradient(circle at 34% 28%,#fff 0%,#f2ecdd 45%,#bdb5a4 100%);
  border:2px solid rgba(255,255,255,.78);
  box-shadow:
    inset -3px -5px 8px rgba(0,0,0,.35),
    inset  2px  3px 6px rgba(255,255,255,.45),
    0 2px 5px rgba(0,0,0,.45);
  transition:box-shadow .15s,transform .06s ease}
/*
 * Выбранный — с ярко-жёлтой сердцевиной, золотом дизайн-системы.
 * Одной обводки было мало: она читается как «сюда навели», а не
 * «здесь отмечено», и с полуметра, на который человек отходит от
 * телефона у стола, разница между отмеченным и пустым пропадала.
 * Сердцевина видна сразу, не считая надписей.
 *
 * Нарисована слоями фона, а не `::before`: у полей ввода собственных
 * детей не бывает, и псевдоэлемент у них в большинстве браузеров
 * не рисуется вовсе.
 */
.last-toggle input:checked{
  background:
    radial-gradient(circle at 50% 50%,
      var(--gold-2) 0 26%, var(--gold) 27% 34%, rgba(0,0,0,0) 35%),
    radial-gradient(circle at 34% 28%,#fff 0%,#f2ecdd 45%,#bdb5a4 100%);
  box-shadow:
    inset -3px -5px 8px rgba(0,0,0,.35),
    inset  2px  3px 6px rgba(255,255,255,.45),
    0 0 0 3px var(--gold),
    0 0 12px rgba(212,175,55,.65)}
.last-toggle input:active:not([disabled]){transform:scale(.92)}
.last-toggle input[disabled]{opacity:.45;cursor:not-allowed}
.last-toggle small{opacity:.75;font-weight:700}

/* ============================================================
   9. ТАБЛИЦЫ РЕЗУЛЬТАТОВ
   На широком экране — обычная таблица. На телефоне она
   превращается в карточки: восемь столбцов в 390 точек не
   помещаются никак, а горизонтальная прокрутка прячет как раз
   самое важное — итог.
============================================================ */
/* -webkit-overflow-scrolling убран: в iOS его нет с 13-й версии,
   инерционная прокрутка там давно сама собой. */
.table-wrap{overflow-x:auto;border:1px solid var(--line);border-radius:var(--r-s)}
table{width:100%;border-collapse:collapse;font-size:.95rem;min-width:540px}
table.final{min-width:640px}
th,td{padding:11px 9px;text-align:center;white-space:nowrap}
thead th{color:var(--gold-2);background:rgba(0,0,0,.30)}
tbody tr{border-top:1px solid var(--line)}
tbody tr:nth-child(even){background:rgba(255,255,255,.022)}
td.name{text-align:left;font-weight:700;overflow-wrap:break-word;overflow-wrap:anywhere}
td.place{width:1%}
td.sum{font-weight:900;font-size:1.05rem}
.total-row td{font-weight:800;color:var(--gold-2);background:var(--gold-dim);
  border-top:1px solid var(--line-2)}
.star{color:var(--gold-2)}
.bonus{color:var(--gold-2);font-weight:800;margin-left:2px}
.pos{color:var(--pos)}
.neg{color:var(--neg)}
.zero{color:var(--muted-2)}
.medal{font-size:1.15rem;line-height:1}

.winner-card{text-align:center;border-color:var(--gold)}
.winner{font-size:1.15rem;margin-top:4px}
.winner b{color:var(--gold-2)}

/* ============================================================
   10. ПРОФИЛЬ УСТРОЙСТВА
============================================================ */
.who-row{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);
  flex-wrap:wrap;border-bottom:1px solid var(--line);padding-bottom:var(--sp-3);
  margin-bottom:var(--sp-3)}
.who-name{font-size:1.2rem;font-weight:800;color:var(--gold-2);
  display:flex;align-items:center;gap:var(--sp-2)}
.stat-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));
  gap:var(--sp-2);margin-bottom:var(--sp-4)}
.stat{background:var(--surface-1);border:1px solid var(--line);
  border-radius:var(--r-s);padding:11px 13px}
.stat .k{color:var(--muted);margin-bottom:3px}
.stat .v{font-size:1.3rem;font-weight:800;color:var(--gold-2);line-height:1.2}
.stat .v.sm{font-size:1rem}
.people-list{display:flex;flex-direction:column;gap:var(--sp-1);margin-top:var(--sp-1)}
.person{display:flex;align-items:center;gap:var(--sp-2);background:var(--surface-1);
  border:1px solid var(--line);border-radius:var(--r-s);padding:9px 12px;font-size:.92rem}
.person .nm{font-weight:700;flex:1;overflow-wrap:break-word;overflow-wrap:anywhere}
.person .g{color:var(--muted);font-size:.8rem;white-space:nowrap}
/* прошедшая игра — маленький счёт за стол */
.games-list{display:flex;flex-direction:column;gap:var(--sp-2);margin-top:var(--sp-1)}
.game-card{background:var(--surface-1);border:1px solid var(--line);
  border-radius:var(--r-m);padding:10px 12px}
.game-when{color:var(--muted);font-size:.78rem;letter-spacing:.03em;
  text-transform:uppercase;margin-bottom:8px}
.game-scores{list-style:none;margin:0;padding:0;display:flex;
  flex-direction:column;gap:5px}
.game-score{display:flex;align-items:center;gap:9px;min-width:0}
.gs-name{font-weight:700;font-size:.92rem;flex:1;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.gs-sum{flex:none;font-variant-numeric:tabular-nums;white-space:nowrap}

.best-line{background:var(--gold-dim);border:1px solid var(--line-gold);
  border-radius:var(--r-s);padding:11px 14px;font-size:.95rem;margin-bottom:var(--sp-3)}
.best-line b{color:var(--gold-2)}

/* код комнаты */
.code-panel{border:2px solid var(--gold);border-radius:var(--r-l);padding:var(--sp-4);
  margin-bottom:var(--sp-4);background:var(--gold-dim);text-align:center}
.code-panel .cap{color:var(--muted)}
.code-big{font-size:2.5rem;font-size:clamp(2rem,12vw,3rem);font-weight:900;letter-spacing:.18em;
  color:var(--gold-2);margin:6px 0 4px}
.code-hint{color:var(--muted);font-size:.85rem;margin-top:8px}

/* QR-приглашение: белое поле обязательно, иначе камера не найдёт код */
/*
 * Размер тут не украшение: чем крупнее клетка, тем увереннее камера
 * берёт код при слабом свете. Поэтому поле занимает почти всю ширину.
 */
/*
 * Ширина считается от поля, а не от окна.
 *
 * Было `min(340px, 84vw)`. `vw` — доля **окна**, а поле у кода куда
 * уже: из ширины телефона вычитаются поля страницы, поля карточки
 * и поля золотой рамки — на 390 точках остаётся около 294, а блок
 * просил 328. Блок шире своего поля не ужимается: `margin:auto`
 * перестаёт работать, и он вылезает вправо — слева зазор, справа
 * обрез. Ровно это и было видно.
 *
 * `width:100%` считается от поля, поэтому блок всегда по нему, а
 * `max-width` держит верхний предел, чтобы на планшете код не раздуло.
 *
 * Правило на будущее: **ширину вложенного блока не задают в `vw`.**
 * Окно знает только браузер, а поле — тот, кто рисует карточку.
 */
.qr-holder{background:#fff;border-radius:var(--r-m);padding:12px;margin:var(--sp-3) auto 0;
  width:100%;max-width:320px;line-height:0;box-shadow:0 4px 14px rgba(0,0,0,.35)}
.qr-holder.big{max-width:340px}
.qr-holder svg{width:100%;height:auto;display:block}
.invite-code{font-size:2.2rem;font-size:clamp(1.9rem,11vw,2.6rem);font-weight:900;letter-spacing:.18em;
  color:var(--gold-2);text-align:center;margin:var(--sp-3) 0 0}
.invite-url{color:var(--muted-2);font-size:.76rem;text-align:center;
  margin-top:6px;overflow-wrap:break-word;overflow-wrap:anywhere}



/* режим наблюдателя */
.watch{background:var(--gold-dim);border:1px solid var(--line-gold);color:var(--gold-2);
  border-radius:var(--r-m);padding:13px 16px;margin-bottom:var(--sp-3);
  font-size:.95rem;font-weight:600}
.watch .small{display:block;color:var(--muted);font-weight:500;font-size:.85rem;margin-top:5px}

/* ============================================================
   11. СООБЩЕНИЯ
============================================================ */
.alert{background:rgba(224,87,74,.14);border:1px solid rgba(224,87,74,.55);color:#ffc4bd;
  border-radius:var(--r-s);padding:12px 14px;font-size:.92rem;margin:var(--sp-2) 0}
.toast{position:fixed;left:50%;bottom:22px;transform:translateX(-50%);z-index:60;
  background:rgba(5,38,27,.97);border:1px solid var(--gold);color:var(--gold-2);
  border-radius:var(--r-m);padding:12px 18px;font-size:.92rem;font-weight:600;
  max-width:92vw;box-shadow:var(--sh-pop)}

.modal{position:fixed;top:0;right:0;bottom:0;left:0;inset:0;z-index:70;display:grid;place-items:center;padding:18px;
  background:rgba(2,14,10,.82);
  -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}
.modal[hidden]{display:none}
.modal-box{background:linear-gradient(180deg,rgba(11,58,42,.99),rgba(5,38,27,.99));
  border:1px solid var(--gold);border-radius:20px;
  padding:24px;padding:clamp(18px,5vw,30px);
  max-width:430px;width:100%;box-shadow:var(--sh-pop)}
.modal-box h2{color:var(--gold-2);font-size:1.2rem;margin-bottom:8px;
  text-transform:uppercase;letter-spacing:.05em}
.modal-box p{color:var(--muted);font-size:.9rem;margin-bottom:16px}
.modal-actions{display:flex;gap:var(--sp-2);margin-top:16px;flex-wrap:wrap}
.modal-actions .btn{flex:1 1 140px;min-height:50px;font-size:.95rem}

/* ============================================================
   12. ТЕЛЕФОН
   Всё ниже включается только на узком экране; на десктопе
   раскладка остаётся прежней.
============================================================ */
@media(max-width:560px){

  /* --- таблицы превращаются в карточки --- */
  .table-wrap{overflow:visible;border:none;border-radius:0}
  table,table.final{min-width:0;width:100%;display:block;font-size:.95rem}
  thead{display:none}
  tbody{display:block}
  tbody tr{display:block;background:var(--surface-1);
    border:1px solid var(--line);border-radius:var(--r-m);
    padding:var(--sp-2) var(--sp-3);margin-bottom:var(--sp-2)}
  tbody tr:nth-child(even){background:var(--surface-1)}

  /* значение в строку с подписью */
  tbody td{display:flex;align-items:center;justify-content:flex-end;gap:4px;
    padding:5px 0;text-align:right;white-space:nowrap;border:none}
  /* подпись прижата влево, значение с бонусом остаются рядом справа */
  tbody td::before{content:attr(data-label);color:var(--muted);font-weight:700;
    font-size:.72rem;text-transform:uppercase;letter-spacing:.05em;
    text-align:left;flex:none;margin-right:auto;padding-right:var(--sp-3)}

  /* шапка карточки: шар и имя в одну строку, без подписей */
  tbody td.place,tbody td.name{display:inline-flex;width:auto;
    padding:0 0 var(--sp-2);vertical-align:middle}
  tbody td.place::before,tbody td.name::before{display:none}
  tbody td.name{font-size:1.08rem;margin-left:var(--sp-2)}

  /* итог выделяем отдельной карточкой */
  tbody tr.total-row{background:var(--gold-dim);border-color:var(--line-gold)}
  tbody tr.total-row td{background:none;border-top:none}
  tbody tr.total-row td:empty{display:none}
  tbody td.sum{font-size:1.15rem}
  tbody td.sum::before{color:var(--gold-2)}

  /*
   * Итоги круга. В карточке игрока шесть строк подряд — очки и деньги
   * за каждую партию, потом «всего» дважды, — и все набраны одинаково.
   * А спор за столом идёт ровно об одной из них: сколько он должен.
   *
   * Поэтому разбор по партиям уходит на второй план — мельче и тише,
   * он нужен, когда счёт оспаривают, — а итоговые деньги становятся
   * самой крупной строкой карточки и отделяются чертой. Порядок строк
   * не меняется: проверять сверху вниз по-прежнему удобно.
   */
  table.final tbody td:not(.sum):not(.place):not(.name){
    font-size:.82rem;color:var(--muted);padding:3px 0}
  table.final tbody td:not(.sum):not(.place):not(.name)::before{
    font-size:.66rem;opacity:.8}
  table.final tbody td.sum{border-top:1px solid var(--line)}
  table.final tbody td.sum + td.sum{border-top:none}
  table.final tbody td.sum:last-child{font-size:1.5rem;padding-bottom:2px}

  .card-title{font-size:1rem;letter-spacing:.04em}

  /* --- счётчик во всю ширину карточки --- */
  .pr-inputs{display:block}
  .stepper{display:flex;width:100%}
  .stepper .balls{flex:1 1 auto;width:auto;min-width:0;font-size:1.875rem}
  .step-btn{width:76px;font-size:2.375rem}
  .last-toggle{display:flex;width:100%;margin-top:var(--sp-2)}

  /* цель нажатия не меньше 44px */
  .btn-link{min-height:var(--tap-min);padding:10px 12px;
    display:inline-flex;align-items:center}

  /* полоса состояния во всю ширину: связь слева, комната справа */
  .status{width:100%;justify-content:space-between;flex-wrap:nowrap}
  .pill,.nominal-badge{font-size:13.5px}
  .nominal-badge{align-self:center}
  .subtitle{align-self:center;margin-top:0}

  /*
   * Здесь стояла подпорка: «Профиль» вынимали из потока в абсолют,
   * потому что правый блок шапки переносился под заголовок. Теперь
   * шапка задана в две строки для любой ширины, и подпорка не просто
   * лишняя — она ломала плашку: обнуляла правый отступ, отчего
   * надпись прижималась к рамке, а высота не совпадала с соседними.
   *
   * Такое правило не видно ни по разметке, ни по своим стилям: оно
   * лежит в телефонном разделе и срабатывает только на узком экране.
   * При перестройке раскладки старые адресные правила надо вычищать
   * сразу, а не оставлять «на всякий случай».
   */

  /* кнопки в столбик: в строку они ужимаются до нечитаемых */
  .btn-row .btn{flex:1 1 100%}
}

/* ------------------------------------------------------------
   ОЧЕНЬ УЗКИЙ ЭКРАН (iPhone SE и прочие 320 точек)

   Три плашки состояния в один ряд, которому запрещён перенос,
   переставали помещаться: плашка комнаты вылезала за правый край
   на три точки, и вся страница начинала ездить вбок. Заметно это
   не по виду, а по тому, что `scrollWidth` больше `clientWidth`.

   Лечим не переносом — связь слева, комната справа читается как
   одна строка и ломать её незачем, — а лишними отступами: у плашки
   комнаты они были почти вдвое шире общих. На 320 точках это
   роскошь, на 390 всё и так помещалось.
------------------------------------------------------------ */
@media(max-width:379px){
  .room-pill{padding-left:11px;padding-right:11px}
  .status{gap:6px}
}

/* ============================================================
   «СПРЯТАНО» ЗНАЧИТ СПРЯТАНО

   Браузер прячет [hidden] своим правилом display:none, но любое
   наше правило с display его перебивает — у них одинаковый вес,
   и наше просто идёт позже. Так пустое поле ввода вылезало
   в окне подтверждения: у него класс .field{display:block}.

   Ловушка одна и та же для любого элемента, который прячут
   атрибутом, поэтому закрываем её здесь целиком, а не по месту.
============================================================ */
[hidden]{display:none!important}


/* ============================================================
   РАЗДАЧА ШАРОВ «ПИРАМИДЫ 140»

   Нажимать придётся в полутьме и одной рукой, поэтому цель
   крупная: шар не меньше 56px, между рядами настоящий зазор.
============================================================ */
.pick-row{display:flex;flex-wrap:wrap;gap:var(--sp-2)}
.pick-who{display:inline-flex;align-items:center;gap:8px;cursor:pointer;
  min-height:var(--tap-min);padding:8px 14px 8px 8px;font:inherit;
  background:var(--surface-1);color:var(--text);
  border:2px solid var(--line);border-radius:var(--r-pill);
  transition:border-color .15s,background .15s}
.pick-who.is-on{border-color:var(--gold);background:var(--gold-dim)}
.pick-who[disabled]{opacity:.55;cursor:not-allowed}
.pw-name{font-weight:700;font-size:.95rem}
.pw-pts{margin-left:4px;color:var(--gold-2);font-variant-numeric:tabular-nums}
.pick-who.small{padding:6px 12px 6px 6px}

.pyr-rack{display:grid;grid-template-columns:repeat(auto-fill,minmax(58px,1fr));
  gap:var(--sp-2)}
.pyr-ball{position:relative;aspect-ratio:1/1;min-height:58px;cursor:pointer;
  display:flex;align-items:center;justify-content:center;
  font:inherit;font-weight:900;font-size:1.25rem;color:var(--text);
  background:var(--surface-2);border:2px solid var(--line);border-radius:50%;
  transition:transform .06s ease,border-color .15s}
.pyr-ball:active:not([disabled]){transform:scale(.94)}
.pyr-ball[disabled]{opacity:.55;cursor:not-allowed}
/* Хозяин найден — шар заливается его цветом, как настоящий */
.pyr-ball.is-taken{color:#fff;border-color:rgba(255,255,255,.78);
  text-shadow:0 1px 2px rgba(0,0,0,.75);
  box-shadow:inset -4px -6px 10px rgba(0,0,0,.45),
             inset 3px 4px 8px rgba(255,255,255,.30),
             0 2px 5px rgba(0,0,0,.45)}
.pb-bonus{position:absolute;top:-2px;right:-2px;font-size:.6rem;font-weight:800;
  color:#000;background:var(--gold-2);border-radius:var(--r-pill);
  padding:1px 5px;line-height:1.3}

/* aspect-ratio появился поздно (iOS 15); без него держим высоту руками */
@supports not (aspect-ratio: 1 / 1){
  .pyr-ball{height:58px}
}


/* ============================================================
   ПРИВЕТСТВЕННЫЙ ЭКРАН

   Первое, что видит человек. Здесь нет ничего, кроме имени
   и выбора игры: одно решение на экран.
============================================================ */
/*
 * Занимаем весь экран и ставим выбор ровно посередине. Вычет —
 * на отступы страницы; dvh второй строкой, потому что на телефоне
 * адресная строка ворует часть высоты, а старые браузеры dvh
 * не знают и возьмут первую.
 */
/*
 * Экран занимает окно целиком: вычитаем отступы страницы (сверху
 * до 24px, снизу 40px). dvh второй строкой — на телефоне адресная
 * строка ворует высоту, а старые браузеры dvh не знают и возьмут vh.
 *
 * Свободное место делится поровну между двумя «auto»: над логотипом
 * и над подписью. Поэтому выбор игры стоит посередине, а подпись
 * прижата к низу — без выдумок с position:absolute, которые
 * на коротком экране налезли бы на кнопки.
 */
/*
 * svh, а не dvh.
 *
 * dvh — высота видимой части «прямо сейчас», и при скрытии адресной
 * строки она меняется на каждом кадре прокрутки. Подпись, прижатая
 * к нижнему краю, от этого ползала вверх-вниз вслед за полосой
 * браузера — движение, которого никто не просил.
 *
 * svh — высота при развёрнутой адресной строке, то есть наименьшая
 * из возможных. Она не меняется вовсе: экран вмещается всегда,
 * и ничего не прыгает. 100vh оставлен строкой выше на случай
 * совсем старого браузера.
 */
.welcome{min-height:calc(100vh - 64px);min-height:calc(100svh - 64px);
  display:flex;flex-direction:column;
  align-items:center;justify-content:flex-start;text-align:center;
  padding:16px 0 0;gap:26px}

.wl-logo{display:flex;flex-direction:column;align-items:center;gap:4px;margin-top:auto}
/*
 * У Stick одно начертание и тонкие штрихи, поэтому 900 ему ни к чему,
 * а размер взят крупнее прежнего: тонкие буквы на тёмном читаются
 * хуже жирных, и добирать приходится величиной. Разрядка меньше —
 * у этого шрифта буквы и так стоят просторно.
 */
.wl-l1{font-family:'Montserrat Alternates','JetBrains Mono',ui-monospace,monospace;
  font-size:2.6rem;font-size:clamp(2.2rem,13vw,4rem);
  font-weight:700;letter-spacing:.02em;color:var(--gold-2);line-height:1.05}
.wl-l2{font-size:.95rem;font-size:clamp(.85rem,3.6vw,1.15rem);
  letter-spacing:.22em;text-transform:uppercase;color:var(--muted)}

.wl-games{width:100%;display:flex;flex-direction:column;gap:14px}
/* Шар равняется по названию игры, а не по середине описания */
.wl-game{display:flex;align-items:flex-start;gap:14px;width:100%;text-align:left;
  cursor:pointer;font:inherit;color:var(--text);
  background:var(--surface-3);border:2px solid var(--line-gold);
  border-radius:var(--r-l);padding:16px 18px;box-shadow:var(--sh-card);
  transition:border-color .15s,transform .06s ease,background .15s}
.wl-game:active{transform:scale(.985)}
.wl-ball{flex:none;margin-top:2px}
.wl-text{display:flex;flex-direction:column;gap:3px;min-width:0}
/*
 * Название игры набирается заголовочным шрифтом везде, где появляется:
 * на кнопке выбора, в шапке страницы и в карточке «Начать». Это имя
 * собственное, а не подпись к элементу, — и по нему человек сверяет,
 * туда ли попал, переходя с экрана на экран.
 */
.wl-name{font-family:'Montserrat Alternates','JetBrains Mono',ui-monospace,monospace;
  font-size:1.3rem;font-weight:700;color:var(--gold-2);
  letter-spacing:.01em;text-transform:uppercase}
.wl-about{font-size:.85rem;color:var(--muted);line-height:1.35}
.wl-who{font-size:.75rem;color:var(--muted-2);text-transform:uppercase;
  letter-spacing:.06em;font-weight:700}
.wl-foot{color:var(--muted-2);font-size:.85rem;max-width:34em}

/*
 * Подпись авторов. Мелко и приглушённо: она должна быть заметна тому,
 * кто её ищет, и не спорить с выбором игры, ради которого сюда пришли.
 * Разрядка — чтобы строка читалась как клеймо, а не как текст.
 */
/*
 * Подпись бьётся, как сердце: два удара подряд и пауза — «тук-тук …».
 * Ровное мигание выглядело бы как неисправность, сдвоенный удар
 * читается живым.
 *
 * Цикл 2,8 с — около 21 пары в минуту: дыхание, а не пульс бегущего.
 * Было вдвое быстрее и заметно суетилось внизу экрана.
 * Первая попытка была на 1,8 с и гасла почти в ноль: строка мелкая
 * и стоит внизу, и две трети цикла она просто отсутствовала — со
 * стороны это читалось как «ничего не происходит». Поэтому нижняя
 * точка поднята до .32: строка видна всегда, а удар — вспышка поверх.
 *
 * Меняются opacity, transform и свечение. Первые две браузер считает
 * на видеокарте, разметка не пересчитывается, соседние строки не
 * дёргаются. Точка роста — низ: подпись прижата к нижнему краю окна,
 * и расти ей некуда, кроме как вверх.
 */
.wl-credit{margin-top:auto;padding-top:20px;color:var(--pos);opacity:.32;
  font-size:.66rem;letter-spacing:.16em;text-transform:uppercase;
  font-weight:700;
  transform-origin:50% 100%;
  animation:пульс 2.8s ease-in-out infinite}

@keyframes пульс{
  0%   {opacity:.32;transform:scale(1);    text-shadow:none}
  8%   {opacity:1;  transform:scale(1.1);  text-shadow:0 0 14px rgba(126,230,178,.75)}  /* тук */
  18%  {opacity:.50;transform:scale(1.01); text-shadow:none}
  28%  {opacity:.9; transform:scale(1.07); text-shadow:0 0 10px rgba(126,230,178,.5)}   /* тук */
  42%  {opacity:.32;transform:scale(1);    text-shadow:none}
  100% {opacity:.32;transform:scale(1);    text-shadow:none}
}

/*
 * Если система просит меньше движения — убираем не пульс, а рост.
 * Неприятны в таких настройках именно перемещения и масштаб; разгорание
 * их не задевает. Полностью гасить нельзя: в Windows этот запрос включён
 * у многих по умолчанию (настройка «Эффекты анимации»), и подпись просто
 * застыла бы, хотя её задумали живой. На этом уже один раз попались:
 * в браузере разработчика пульса не было вовсе.
 */
@media (prefers-reduced-motion: reduce){
  .wl-credit{animation-name:пульс-тихий}
}
@keyframes пульс-тихий{
  0%   {opacity:.32;text-shadow:none}
  8%   {opacity:1;  text-shadow:0 0 14px rgba(126,230,178,.75)}
  18%  {opacity:.50;text-shadow:none}
  28%  {opacity:.9; text-shadow:0 0 10px rgba(126,230,178,.5)}
  42%  {opacity:.32;text-shadow:none}
  100% {opacity:.32;text-shadow:none}
}

/*
 * Выбранная игра в карточке «Начать»: шар с номером и название.
 * Шар — тот же и того же цвета, что на экране выбора; это
 * единственное, по чему видно, что попал куда хотел.
 */
.chosen-game{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  margin:2px 0 var(--sp-3)}
.cg-name{font-family:'Montserrat Alternates','JetBrains Mono',ui-monospace,monospace;
  font-size:1.15rem;font-weight:700;color:var(--gold-2);
  text-transform:uppercase;letter-spacing:.01em}

/*
 * «Сменить игру» — возврат к самому первому решению, поэтому стоит
 * особняком, за чертой: рядом с «Войти» её легко нажать по ошибке,
 * а уход отсюда сбрасывает выбор.
 */
.switch-game{margin-top:var(--sp-4);padding-top:var(--sp-3);
  border-top:1px solid var(--line)}

/* Зазоры во flex появились в Safari поздно — на старых айфонах
   подстрахуем самое заметное: карточки игр не должны слипаться. */
@supports not (inset: 0){
  .wl-games>*+*{margin-top:14px}
  .welcome>*+*{margin-top:26px}
}


/* ============================================================
   ЭКРАН ИСТОРИИ ИГР

   Отборы стоят в сетке: на телефоне ложатся в один столбец,
   на широком экране — в два, и не растягиваются во всю ширину.
============================================================ */
.hist-filters{display:grid;grid-template-columns:1fr;gap:var(--sp-2);
  margin-bottom:var(--sp-3)}
@media(min-width:520px){
  .hist-filters{grid-template-columns:1fr 1fr}
}
.hf{display:block}
.hf .field-label{display:block;color:var(--muted);margin-bottom:5px}
.hf select{min-height:var(--tap-min)}


/* ============================================================
   НАВЕДЕНИЕ — ТОЛЬКО ТАМ, ГДЕ ЕСТЬ ЧЕМ НАВОДИТЬ

   У пальца нет «наведения». Браузер на телефоне всё равно
   применяет :hover при касании и снимает его не сразу: кнопка
   остаётся подсвеченной после того, как её отпустили, иногда
   до следующего касания в другом месте. Человек видит, будто
   выбор куда-то «залип».

   Отклик на касание даёт :active — он и остаётся. Все правила
   наведения собраны здесь, в одном месте: так видно, что ни одно
   не потерялось, и так они не разъедутся при следующих правках.
============================================================ */
@media (hover: hover){
  .btn-link:hover{color:var(--gold-2)}
  .pill-back:hover{border-color:var(--danger);background:rgba(224,87,74,.14)}
  .room-pill:hover{border-color:var(--gold)}
  .btn:hover:not([disabled]){filter:brightness(1.07)}
  .btn-ghost:hover:not([disabled]){background:var(--gold-dim)}
  .btn-ghost.danger:hover:not([disabled]){background:rgba(224,87,74,.12)}
  .btn-quiet:hover:not([disabled]){border-color:var(--gold);color:var(--gold-2)}
  .step-btn:hover:not([disabled]){filter:brightness(1.4)}
  .last-toggle:hover{border-color:var(--gold);background:var(--gold-dim)}
  .pick-who:hover:not([disabled]){border-color:var(--line-gold)}
  .wl-game:hover{border-color:var(--gold);background:var(--gold-dim)}
}


/* ============================================================
   МЕЛОЧИ, КОТОРЫЕ ВИДНО ТОЛЬКО НА ТЕЛЕФОНЕ
============================================================ */

/*
 * touch-action: manipulation — правильный способ убрать остаточную
 * задержку перед срабатыванием нажатия. Браузер ждёт, не будет ли
 * второго касания (двойной тап увеличивает страницу); на кнопке
 * ждать нечего, и мы это говорим прямо.
 *
 * Именно так, а не запретом масштабирования в meta viewport:
 * запрет отнимает увеличение у тех, кто плохо видит, и это прямое
 * нарушение правил доступности. Здесь же увеличение страницы
 * остаётся, а кнопки срабатывают сразу.
 */
.btn,.btn-link,.pill,.step-btn,.wl-game,.last-toggle,.pick-who,
select,summary,label.field{touch-action:manipulation}

/*
 * Прокрутка не проваливается наружу. Без этого протяжка внутри
 * таблицы итогов или окна подтверждения докручивает страницу за
 * ними, а сверху вдобавок срабатывает «потянуть, чтобы обновить» —
 * и посреди партии страница перезагружается.
 */
.modal,.modal-box,.table-wrap{overscroll-behavior:contain}

/*
 * Переносы строк.
 *
 * balance — для заголовков и коротких подписей: браузер раскладывает
 * строки поровну, и не остаётся висящего одного слова. Работает
 * только до шести строк, поэтому абзацам его давать бессмысленно.
 *
 * pretty — для текста: правит хвост, чтобы последняя строка не
 * состояла из одного слова. Где не поддержан, перенос просто
 * обычный — потерять тут нечего.
 */
h1,.card-title,.wl-l2,.wl-name,.cg-name,.invite-code,.code-panel .cap,
.empty,.best-line,.toast{text-wrap:balance}
p,.hint,.card-sub,.wl-about,.wl-foot,.code-hint{text-wrap:pretty}


/* ============================================================
   ПЕРЕХОД МЕЖДУ ЭКРАНАМИ

   Старый кадр уходит вверх и растворяется, новый приходит снизу.
   Направление одно на все переходы нарочно: приложение плоское,
   «назад» и «вперёд» в нём равноправны, и разное движение только
   сбивало бы с толку.

   Двести миллисекунд — столько, чтобы глаз заметил смену, и не
   столько, чтобы её пришлось ждать. Двигаются только прозрачность
   и сдвиг: то и другое браузер считает на видеокарте, разметка
   не пересчитывается.
============================================================ */
::view-transition-old(root),
::view-transition-new(root){
  animation-duration:.2s;
  animation-timing-function:cubic-bezier(.2,.65,.2,1)}
::view-transition-old(root){animation-name:экран-уходит}
::view-transition-new(root){animation-name:экран-приходит}

@keyframes экран-уходит{
  to{opacity:0;transform:translateY(-8px)}}
@keyframes экран-приходит{
  from{opacity:0;transform:translateY(10px)}}

/*
 * Кому движение мешает — тому чистое растворение, без сдвига.
 * Убирать переход совсем нельзя: без него экран снова сменяется
 * рывком, а это ровно та беда, ради которой всё и делалось.
 */
@media (prefers-reduced-motion: reduce){
  ::view-transition-old(root){animation-name:экран-гаснет}
  ::view-transition-new(root){animation-name:экран-светлеет}
}
@keyframes экран-гаснет{to{opacity:0}}
@keyframes экран-светлеет{from{opacity:0}}
