/* Блоки прибора: шапка, циферблат, приборная строка, главная цифра, кнопки,
   полки, ленты, честность.

   Общие правила направления: скруглений больше 3 px нет нигде, карточек с тенями нет,
   границы блоков — hairline 1 px, подпись всегда над значением. */

/* ── подпись ───────────────────────────────────────────────────────────── */

.cap {
    display: block;
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    line-height: 1.2;
    letter-spacing: var(--ls-cap);
    text-transform: uppercase;
    color: var(--txt-2);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ── шапка ─────────────────────────────────────────────────────────────── */

.conn {
    flex: 0 0 auto;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--txt-3);
    transition: background var(--t-mid) linear;
}

/* Связь есть — зелёный: это состояние прибора, а не игрока. */
.conn.on {
    background: var(--live);
}

.hdr-brand {
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    text-transform: uppercase;
    color: var(--txt-2);
    white-space: nowrap;
}

.hdr-balance {
    display: flex;
    align-items: baseline;
    gap: var(--s1);
    min-width: 0;
}

#hdr-balance {
    font-size: var(--fs-md);
    color: var(--gain);
    white-space: nowrap;
}

.hdr-cur {
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    color: var(--gain-dim);
}

.icon-btn {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 28px;
    height: 28px;
    padding: 0;
    background: transparent;
    border: 0;
    color: var(--txt-2);
    cursor: pointer;
}

.icon-btn svg {
    width: 16px;
    height: 16px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.3;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.icon-btn .ico-body {
    fill: currentColor;
    stroke: none;
}

.icon-btn .ico-off {
    display: none;
}

/* Выключенный звук: волны заменяются крестом, цвет уходит в выключенный. */
.icon-btn.muted {
    color: var(--txt-3);
}

.icon-btn.muted .ico-on {
    display: none;
}

.icon-btn.muted .ico-off {
    display: inline;
}

/* ── строка режима ─────────────────────────────────────────────────────── */

.mode-line {
    /* Отступ сверху берётся общим правилом колонки: своё значение здесь означало бы
       шестой шаг в шкале из пяти. */
    font-size: var(--fs-sm);
    line-height: 20px;
    color: var(--txt-dim);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ── центр кольца ──────────────────────────────────────────────────────── */

.dial-hint {
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    text-transform: uppercase;
    color: var(--txt-dim);
    white-space: nowrap;
}

.dial-time {
    display: flex;
    align-items: baseline;
    justify-content: center;
    font-weight: 600;
    font-size: clamp(46px, 13.5vw, 58px);
    line-height: 1;
    letter-spacing: var(--tr-hero);
    color: var(--txt);
    /* Ширина фиксирована табличными цифрами; без этого каждый кадр — пересчёт строки. */
    white-space: nowrap;
}

/* Целая часть держит ширину двух знаков.
   Табличные цифры выравнивают ЗНАКИ между собой, но не спасают от смены их
   ЧИСЛА: на переходе «10.0 → 9.0» строка становится на знак короче, и
   отцентрованное число дёргается вбок. Замер (tools/visual/perf.mjs, дрожание
   таймера) давал разброс 4.3 px — половину знака, ровно в последние секунды,
   когда на это число и смотрят. Трёхзначный отсчёт растянет ячейку сам. */
.dial-time .int {
    display: inline-block;
    min-width: 2ch;
    text-align: right;
}

/* Дробная часть на 62% размера, по общей базовой линии: целые секунды — главное. */
.dial-time .frac {
    font-size: 0.62em;
    color: var(--txt-2);
}

/* Последние три секунды: цифра уходит в цвет опасности и дышит в такт
   тиканью. Не в янтарь: истекает время раунда, а не что-то твоё. */
.dial-time.hot,
.dial-time.hot .frac {
    color: var(--risk);
}

.dial-time.hot {
    animation: dial-breath 1000ms ease-in-out infinite;
}

@keyframes dial-breath {
    0%, 100% { transform: scale(1); }
    50% { transform: scale(1.04); }
}

.dial-phase {
    margin-top: 4px;
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    text-transform: uppercase;
    color: var(--txt-2);
    white-space: nowrap;
    max-width: 78%;
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: center;
}

/* Визир: единственное, что делает долю в полпроцента находимой за долю секунды.
   Точка, а не подпись. Подпись «ТЫ · 33.3%» повторяла число, которое в тот же
   момент стоит крупно в центре кольца, — одно и то же значение дважды на одном
   экране, причём меньшая копия ещё и ездила по кругу. Место остаётся находимым:
   ищут его глазами по метке, а не читают. */
/* Метка «ты» — ТРЕУГОЛЬНИК, а не точка, и стоит она на кольце засечек снаружи.
   Круглая точка была неотличима от точки отсчёта, которая едет по соседнему
   радиусу: сначала они сталкивались снаружи («две точки рядом»), потом метку
   убрали внутрь — и она стала читаться как точка НА сегменте. Другая форма
   решает то, чего не решает ни один радиус. */
.me-visor {
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 0;
    height: 0;
    border-left: 5px solid transparent;
    border-right: 5px solid transparent;
    border-top: 8px solid var(--me);
    filter: drop-shadow(0 0 2px var(--bg)) drop-shadow(0 0 4px var(--me-dim));
    pointer-events: none;
}

/* Своё выбывание: место на кольце остаётся, но гаснет. */
.me-visor.out {
    border-top-color: var(--me-out);
    filter: drop-shadow(0 0 2px var(--bg));
}

/* ── строка овертайма и потолка ────────────────────────────────────────── */

.overtime {
    display: flex;
    align-items: center;
    gap: var(--s2);
    min-height: 20px;
    /* Место занято всегда: появление овертайма не должно двигать экран. */
    visibility: hidden;
    font-size: var(--fs-sm);
    color: var(--txt-dim);
    white-space: nowrap;
    overflow: hidden;
}

.overtime.on {
    visibility: visible;
}

/* Одна строка вместо четырёх ячеек. Раньше здесь стояли «ОВЕРТАЙМ»,
   «ПРОДЛЕНИЙ 1/10», распорка и «ПОТОЛОК 179 с»: на телефоне слово съедало место
   у чисел, весь дефицит ширины падал на левую половину, и строка обрезалась
   многоточием ровно в том месте, где написано главное. Слово ушло — оно и так
   стоит в центре кольца вместо «СБОР»; счётчик продлений ушёл — он ничего не
   меняет в решении игрока. Осталось на сколько продлит покупка и сколько
   осталось до потолка. */
.ot-line {
    overflow: hidden;
    text-overflow: ellipsis;
    color: var(--ot-dim);
}

/* Потолок близко — единственный способ увидеть, что аукцион конечен. */
.ot-line.near {
    color: var(--risk);
    animation: cap-pulse 1000ms ease-in-out infinite;
}

@keyframes cap-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: .5; }
}

/* ── приборная строка ──────────────────────────────────────────────────── */

.stats {
    /* Риски держатся на позиционировании родителя; абсолютные псевдоэлементы
       выпадают из сетки и не становятся её ячейками. */
    position: relative;
    display: grid;
    /* Колонки неравные: пот — деньги с двумя знаками после точки, а два соседних
       показания — целые числа в одну-две цифры. Действует только на широкой
       раскладке: на телефоне колонки идут по содержимому (см. медиазапрос ниже),
       и там длину чисел держит ступенчатый кегль .stat-v.len-*. */
    grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr) minmax(0, 1fr);
    align-items: start;
    min-height: 52px;
    /* Показания — своя группа, а не следующая строка: над ними зазор шире. */
    margin-top: var(--s4);
    padding-top: var(--s3);
    /* Гравировка вместо черты: тёмный паз, светлое ребро под ним. При DPR 2-3 обе
       линии попадают в целые пиксели устройства и остаются острыми. */
    border-top: 1px solid var(--groove);
    box-shadow: inset 0 1px 0 var(--rule);
}

.stat {
    min-width: 0;
    padding-right: var(--s2);
}

.stat + .stat {
    padding-left: var(--s3);
    /* Вертикальное ребро света не ловит: лампа сверху, боковой грани она не касается. */
    border-left: 1px solid var(--groove);
    box-shadow: inset 1px 0 0 var(--edge-side);
}

.stat-v {
    margin-top: var(--s1);
    font-size: var(--fs-lg);
    line-height: 1.1;
    color: var(--txt);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Длинное число мельчает, а не обрезается. Многоточие вместо последних разрядов
   пота — это не «немного не поместилось», это неизвестная сумма: «130.6…» и
   «130.66» отличаются на копейку, а «13 0…» и «130» — в десять раз. Ступени
   ставятся классом из stats.js по числу знаков: посчитать их в коде дешевле и
   надёжнее, чем угадывать шириной. */
.stat-v.len-6 { font-size: calc(var(--fs-lg) * 0.86); }
.stat-v.len-7 { font-size: calc(var(--fs-lg) * 0.76); }
.stat-v.len-8 { font-size: calc(var(--fs-lg) * 0.68); }

/* Дробная часть пота мельче: главное — целые. */
.stat-v .frac {
    font-size: 0.7em;
    color: var(--txt-2);
}

/* Пот — деньги, а не просто число: он единственный из трёх показаний измеряется
   в USDT. Билеты и игроки остаются нейтральными — их не в чем измерять.
   id перебивает .stat-v по силе селектора; метка #stat-pot.pl-mine::after
   остаётся янтарной, потому что она означает «в поте есть твой билет». */
#stat-pot { color: var(--gain); }
#stat-pot .frac { color: var(--gain-dim); }

/* ── главная цифра ─────────────────────────────────────────────────────── */

.chance {
    position: relative;
    display: block;
    margin-top: var(--s4);
    padding: var(--s3) var(--pad-x);
    /* Полоса прибора во всю ширину, а не карточка: поля колонки компенсируются. */
    margin-left: calc(-1 * var(--pad-x));
    margin-right: calc(-1 * var(--pad-x));
    /* Цоколь приподнят над страницей, поэтому верхняя грань у него светлая, а не
       паз: свет сверху попадает прямо на неё. Фон объявлен и цветом, и градиентом —
       сплошной цвет берёт самый светлый стоп, чтобы проверка контраста считала
       по худшему случаю, а не по прозрачному фону.
       Янтарь тут только в ребре толщиной в пиксель: заливка панели янтарём отклонена
       судейством — любая янтарная площадь больше своего сегмента размывает смысл
       «янтарь = это про тебя». */
    background-color: var(--plinth-top);
    background-image: linear-gradient(180deg, var(--plinth-top) 0%, var(--plinth-mid) 60%, var(--plinth-bot) 100%);
    border-top: 1px solid var(--plinth-edge);
    border-bottom: 0;
    /* Размытой тени под цоколем здесь быть не может, и это измерено: главная цифра
       перерисовывается каждый кадр переката, а вместе с ней пересчитывалась бы и
       тень — стенд показал падение с 60 до 49 кадров на волнах. Контактная линия
       без размытия даёт ту же посадку «полоса лежит на странице» и стоит ноль. */
    box-shadow:
        inset 0 1px 0 rgba(255, 176, 0, .16),
        var(--shadow-contact);
}

.chance-top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s2);
    min-width: 0;
}

/* Множитель роста за фазу: он превращает «4% → 26%» в историю.
   Подпись «ШАНС» перед числом обязательна: голый «×1.3» не сообщает, чего
   именно множитель, и читался как обещанный бонус к деньгам. */
.chance-mult {
    font-size: 14px;
    color: var(--txt);
    white-space: nowrap;
    cursor: pointer;
}

.chance-mult:focus-visible { outline: 1px solid var(--me); outline-offset: 2px; }

/* Откуда берётся множитель — по нажатию. Поверх строки, к правому краю:
   к нему же прижат и сам множитель. */
.mult-tip {
    position: absolute;
    right: 0;
    bottom: 100%;
    z-index: 6;
    max-width: min(280px, 92vw);
    margin-bottom: var(--s1);
    padding: var(--s2);
    background: var(--surf-2-top);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    color: var(--txt-2);
    font-family: var(--font-ui);
    font-size: var(--fs-sm);
    letter-spacing: 0;
    line-height: 1.35;
    text-align: left;
    white-space: normal;
    box-shadow: var(--shadow-sheet);
}

.chance-row {
    display: flex;
    align-items: baseline;
    gap: var(--s2);
    min-width: 0;
}

.chance-value {
    display: inline-flex;
    align-items: baseline;
    font-weight: 800;
    letter-spacing: var(--tr-hero);
    font-size: clamp(48px, 17vw, 72px);
    line-height: 0.95;
    color: var(--me);
    white-space: nowrap;
    min-width: 0;
}

/* Выплата длиннее процента: тем же кеглем она не помещается на 320 px. */
.chance-value.long {
    font-size: clamp(30px, 10vw, 44px);
}

.chance-value .sfx {
    font-size: 0.44em;
    color: var(--txt-2);
    transform: translateY(-0.42em);
}

/* У процента отбивки нет — это часть числа. У валюты есть: иначе «1234.56USDT». */
.chance-value.long .sfx {
    margin-left: 6px;
}

/* Не участвуешь — прочерк выключенным цветом. */
.chance-value.idle {
    color: var(--txt-3);
}

/* Выбыл — цифра гаснет, но не исчезает: место, куда можно вернуться. */
.chance-value.out {
    color: var(--me-out);
}

.chance-delta {
    font-size: var(--fs-md);
    color: var(--txt-2);
    white-space: nowrap;
    opacity: 0;
}

/* Дельта всплывает на 1100 мс и растворяется за последние 300. */
.chance-delta.show {
    animation: delta-pop 1100ms var(--ease-out) forwards;
}

@keyframes delta-pop {
    0% { opacity: 0; transform: translateY(0); }
    12% { opacity: 1; transform: translateY(-4px); }
    73% { opacity: 1; transform: translateY(-10px); }
    100% { opacity: 0; transform: translateY(-12px); }
}

/* Микрошкала: третий канал того же числа. Едет той же пружиной, что и сектор. */
.chance-bar {
    position: relative;
    height: 3px;
    margin-top: var(--s2);
    /* Не полоска, а паз: дно темнее всего вокруг, снизу тонкий блик — так глаз
       читает выемку, а не наклеенный прямоугольник. */
    background: var(--ink-2);
    box-shadow:
        var(--shadow-groove);
    overflow: hidden;
}

.chance-bar::after {
    /* Риски на 25/50/75% — чтобы шкала читалась без цифр. */
    content: "";
    position: absolute;
    inset: 0;
    background:
        linear-gradient(to right, transparent 0 calc(25% - 1px), var(--line) calc(25% - 1px) 25%, transparent 25%),
        linear-gradient(to right, transparent 0 calc(50% - 1px), var(--line-hi) calc(50% - 1px) 50%, transparent 50%),
        linear-gradient(to right, transparent 0 calc(75% - 1px), var(--line) calc(75% - 1px) 75%, transparent 75%);
    pointer-events: none;
}

.chance-fill {
    display: block;
    width: 0;
    height: 100%;
    /* Вертикальный градиент на трёх пикселях — ровно та деталь, которой не видно,
       но которая отличает предмет от прямоугольника. Внешнего свечения нет намеренно:
       паз обрезает всё за своими краями, а платить кадрами за невидимое размытие
       на полосе, которая едет из JS, нельзя. */
    background: linear-gradient(180deg, var(--me-grad-1) 0%, var(--me-grad-2) 55%, var(--me-grad-3) 100%);
}

.chance-fill.out {
    background: linear-gradient(180deg, var(--me-press-1) 0%, var(--me-press-2) 55%, var(--me-press-3) 100%);
}

.chance-note {
    margin-top: var(--s2);
    min-height: 16px;
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    text-transform: uppercase;
    color: var(--txt-2);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ── кнопки ────────────────────────────────────────────────────────────── */

.buy {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--s2);
    /* Действие — отдельная группа: между ним и цоколем зазор шире внутригрупповых. */
    margin-top: var(--s4);
}

/* Не рамка, а клавиша: тело с фаской сверху, тенью снизу и контактной линией под
   собой. Отклик становится тактильным без единой анимации — материал меняется
   скачком вместе с состоянием, а скачок стоит ноль кадров.
   Фон объявлен и цветом, и градиентом: сплошной цвет равен самому светлому стопу,
   чтобы проверка контраста считала подпись по худшей точке клавиши. */
.btn {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-width: 0;
    height: 52px;
    padding: 0 var(--s1);
    background-color: var(--key-top);
    background-image: linear-gradient(180deg,
        var(--key-top) 0%, var(--key-mid) 52%, var(--key-bot) 100%);
    border: 1px solid var(--key-line);
    border-radius: var(--radius);
    box-shadow: var(--shadow-key);
    color: var(--txt);
    font-family: var(--font-ui);
    cursor: pointer;
    /* Тело клавиши — градиент, а градиенты не анимируются: свет переворачивается
       в тот же кадр, что и нажатие, и это правильно. Переходом живёт только рамка. */
    transition: border-color var(--t-fast) linear;
    -webkit-tap-highlight-color: transparent;
}

.btn-n {
    font-size: 16px;
    font-weight: 500;
    letter-spacing: 0.04em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}

/* Цена стоит в тёмной нижней половине клавиши: на самой светлой точке #161C22
   --txt-dim даёт 4.63:1 (впритык к норме), на нижней #0C1116 — 5.11:1.
   При любой перестановке содержимого цена уезжает вниз, а не наверх. */
.btn-sub {
    font-size: var(--fs-sm);
    /* Подпись клавиши — это всегда цена. Выключенная клавиша ниже остаётся
       серой: у неё цены нет. */
    color: var(--gain-dim);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}

/* Нажатие переворачивает свет: фаска гаснет, верх уходит в тень, низ подсвечивается.
   Класс .is-down поддержан заранее — в iOS WebView :active залипает при прокрутке,
   и когда нажатие переедет на pointer-события, стили менять не придётся. */
.btn:active:not(:disabled),
.btn.is-down:not(:disabled) {
    background-color: var(--key-press-top);
    background-image: linear-gradient(180deg, var(--key-press-top) 0%, var(--key-press-bot) 100%);
    box-shadow:
        var(--shadow-key-press);
    border-color: var(--me);
}

/* Нажать нельзя — фаска гаснет вчетверо: клавиша выглядит утопленной в панель. */
.btn:disabled {
    background-color: var(--surf-2-bot);
    background-image: linear-gradient(180deg, var(--surf-2-bot) 0%, var(--surf-3-bot) 100%);
    border-color: var(--surf-2-top);
    box-shadow: var(--shadow-key-off);
    color: var(--txt-off);
    cursor: default;
}

.btn:disabled .btn-sub {
    color: var(--txt-off);
}

/* Отказ сервера: три мигания цветом опасности. */
.btn.reject {
    animation: btn-reject 270ms steps(1, end) 1;
}

@keyframes btn-reject {
    0%, 100% { border-color: var(--key-line); }
    16%, 50%, 83% { border-color: var(--risk); }
}

@media (hover: hover) {
    .btn:hover:not(:disabled) {
        border-color: var(--txt-3);
    }
}

/* Единственная клавиша, которая про тебя: её фаска ловит янтарь вместо белого.
   Ребро в один пиксель — не заливка, смысл акцента этим не размывается. */
.btn-wide {
    width: 100%;
    height: 56px;
    flex-direction: row;
    justify-content: space-between;
    padding: 0 var(--s3);
    box-shadow:
        inset 0 1px 0 rgba(255, 176, 0, .14),
        var(--shadow-key-body);
}

/* Ряд клавиш докупа стоит внутри полки, а не под цоколем: сверху над ним уже есть
   шапка полки со своим отступом, и внешний margin ряда добавил бы второй зазор
   к тому же стыку — полка стала бы выше на пустое место. */
.buy-duel {
    margin-top: 0;
    /* Гибкий ряд, а не сетка из трёх колонок, как у клавиш покупки. Причина одна:
       клавиш в ряду докупа бывает и меньше трёх. Когда пот вырос, а денег у игрока
       осталось на пару билетов, лишние объёмы прячутся (см. duelVolumes), и сетка
       оставила бы на их месте дыру у правого края — ряд читался бы как поломка.
       Флекс скрытых детей не считает вовсе и делит ширину между оставшимися. */
    display: flex;
    gap: var(--s2);
}

.buy-duel > .btn {
    flex: 1 1 0;
    min-width: 0;
}

/* ── полки контекста ───────────────────────────────────────────────────── */

.shelf {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    padding: var(--s3) var(--pad-x) 0;
    padding-bottom: calc(var(--safe-bottom) + var(--s3));
    /* Полка ближе всех к лампе — значит она и самая светлая поверхность сцены. */
    background-color: var(--surf-2-top);
    background-image: linear-gradient(180deg,
        var(--surf-2-top) 0%, var(--surf-2-mid) 46%, var(--surf-2-bot) 100%);
    border-top: 1px solid var(--me);
    animation: shelf-in var(--t-mid) var(--ease-pop) 1;
}

/* Тень темнее #07090B невозможна, а затенение СОДЕРЖИМОГО под полкой — возможно.
   Поэтому полка не отбрасывает тень, а гасит то, что под ней: статический градиент,
   появляется раз на волну, живого размытия не требует. */
.shelf::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 100%;
    height: 28px;
    background: var(--scrim);
    pointer-events: none;
}

@keyframes shelf-in {
    from { transform: translateY(100%); }
    to { transform: translateY(0); }
}

.shelf-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s2);
    margin-bottom: var(--s2);
    min-width: 0;
}

/* Причина отказа под клавишами полки.
   Строка появляется только когда есть что сказать, поэтому её высота не заложена
   в раскладку: пустая подпись под клавишами всё время отнимала бы у кольца
   четырнадцать пикселей ради сообщения, которого в большинстве раундов не будет. */
.shelf-note {
    /* Отступ такой же, как у подписи, на чьём месте она стоит: высота полки от
       подмены меняться не должна. */
    margin: 0 0 var(--s2);
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    /* Риск, а не янтарь: янтарь в этом приборе значит «это про тебя», а отказ —
       это про запрет (см. docs/design/palette-v2.md). */
    color: var(--risk);
    text-align: center;
    line-height: 1.25;
}

/* Клавиша, на которую не хватает денег.
   Она НЕ исчезает, и это осознанно: цена под ней — единственный способ узнать,
   сколько стоит следующая ступень докупа. Гаснет она заметно сильнее обычной
   выключенной клавиши, потому что причина у неё другая и поправимая. */
.btn.is-poor {
    opacity: .38;
    border-color: var(--key-line);
    color: var(--txt-3);
}

@media (min-width: 760px) {
    /* На десктопе полка не всплывает снизу окна, а встаёт под кнопки. */
    .shelf {
        position: static;
        padding: var(--s3) 0 0;
        animation: none;
    }

    /* В потоке колонки затенять нечего: под полкой не содержимое, а страница. */
    .shelf::before {
        display: none;
    }
}

/* ── панели правой колонки ─────────────────────────────────────────────── */

.panel {
    position: relative;
    display: block;
    padding-top: var(--s3);
    /* Тот же гравированный паз, что и в приборной строке: правило света исключений
       не имеет, иначе половина блоков поймает свет снизу и сцена рассыплется. */
    border-top: 1px solid var(--groove);
    box-shadow: inset 0 1px 0 var(--rule);
}

.panel-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s2);
    margin-bottom: var(--s2);
    min-width: 0;
}

/* ── лента участников ──────────────────────────────────────────────────── */

.roster {
    display: block;
}

/* Развёрнутая лента прокручивается сама, а не растит страницу. Высота кратна строке:
   обрезанная пополам строка читается как дефект вёрстки. */
.roster.open {
    max-height: calc((var(--row-h) + 1px) * 11);
    overflow-y: auto;
    overscroll-behavior: contain;
}

/* Строк на экране до сорока, поэтому рисок они не получают: приём работает, пока
   их дюжина. Но свет и здесь идёт сверху — паз снизу, светлое ребро под ним,
   то есть на верхней грани следующей строки. */
.row {
    display: grid;
    grid-template-columns: 3px minmax(0, 1fr) minmax(0, auto) minmax(0, auto);
    align-items: center;
    gap: var(--s2);
    height: var(--row-h);
    padding-left: 2px;
    border-bottom: 1px solid var(--groove);
    box-shadow: 0 1px 0 var(--rule);
}

.row-chip {
    width: 3px;
    height: 14px;
    background: var(--seg-3);
}

.row-pid {
    font-family: var(--font-code);
    font-size: var(--fs-md);
    color: var(--txt-dim);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}

.row-tickets,
.row-share {
    font-family: var(--font-num);
    letter-spacing: var(--tr-big);
    font-variant-numeric: tabular-nums;
    font-size: var(--fs-md);
    color: var(--txt-2);
    text-align: right;
    /* Длинные числа не ломают строку и не выталкивают соседей. */
    white-space: nowrap;
}

.row-share {
    min-width: 56px;
    color: var(--txt);
}

/* Своя строка: планка во всю высоту и полный контраст текста. Видна всегда. */
.row.me {
    background: rgba(255, 176, 0, .06);
    box-shadow: inset 2px 0 0 0 var(--me), 0 1px 0 var(--rule);
}

.row.me .row-pid {
    color: var(--me);
}

.row.me .row-share {
    color: var(--me);
}

/* Выбывший гаснет, но не зачёркивается: прибор не зачёркивает. */
.row.out .row-chip {
    background: var(--seg-out);
}

.row.out .row-pid,
.row.out .row-tickets,
.row.out .row-share {
    color: var(--txt-3);
}

.row.me.out {
    box-shadow: inset 2px 0 0 0 var(--me-out), 0 1px 0 var(--rule);
}

.row.me.out .row-pid,
.row.me.out .row-share {
    color: var(--me-out);
}

.roster-more {
    display: block;
    width: 100%;
    padding: var(--s2) 0;
    background: transparent;
    border: 0;
    color: var(--txt-2);
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    text-transform: uppercase;
    text-align: left;
    cursor: pointer;
}

.roster-empty {
    padding: var(--s2) 0;
    font-size: var(--fs-md);
    color: var(--txt-dim);
}

/* ── лента волн ────────────────────────────────────────────────────────── */

.timeline {
    display: block;
}

.wave-row {
    display: flex;
    align-items: baseline;
    gap: 8px;
    min-height: 20px;
    font-family: var(--font-num);
    letter-spacing: var(--tr-big);
    font-variant-numeric: tabular-nums;
    font-size: var(--fs-md);
    color: var(--txt-2);
    white-space: nowrap;
    overflow: hidden;
    animation: wave-in 180ms var(--ease-out) 1;
}

@keyframes wave-in {
    from { opacity: 0; transform: translateY(8px); }
    to { opacity: 1; transform: translateY(0); }
}

.wave-no {
    color: var(--txt);
}

/* Сколько осталось после удара — это «столько пережило». */
.wave-row:not(.pending) .wave-cut { color: var(--live-dim); }

/* Ещё не сыгранная волна расписания: она есть, но она бледнее. */
.wave-row.pending {
    color: var(--txt-3);
    animation: none;
}

.wave-row.pending .wave-no {
    color: var(--txt-dim);
}

.wave-me {
    margin-left: auto;
    color: var(--me);
}

.wave-me.out {
    color: var(--me-out);
}

.timeline-empty {
    padding: var(--s1) 0;
    font-size: var(--fs-md);
    color: var(--txt-dim);
}

/* ── честность ─────────────────────────────────────────────────────────── */

.fair-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: 0 0 var(--s2);
    background: transparent;
    border: 0;
    color: inherit;
    cursor: pointer;
}

.fair-caret {
    color: var(--txt-2);
    transition: transform var(--t-fast) linear;
}

.fair-head[aria-expanded="true"] .fair-caret {
    transform: rotate(90deg);
}

.fair-body > .cap {
    margin-top: var(--s3);
}

/* Хэш разбит по четыре символа тонкими шпациями: его сверяют глазами. Здесь
   моноширинный на своём месте: это код, а не величина. */
.hash {
    font-family: var(--font-code);
    margin-top: var(--s1);
    font-size: var(--fs-md);
    line-height: 1.5;
    color: var(--txt-dim);
    /* Длинный хэш переносится в любом месте — иначе он вылезает за экран. */
    overflow-wrap: anywhere;
    word-break: break-all;
}

.fair-links {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s4);
    margin-top: var(--s4);
    padding-bottom: var(--s1);
}

.lnk {
    font-size: var(--fs-md);
    color: var(--txt);
    text-decoration: underline;
    text-decoration-color: var(--line-hi);
    text-underline-offset: 3px;
}

/* ── переключатель комнат ──────────────────────────────────────────────── */

/* Ряд вкладок под шапкой. Три равные доли и min-width: 0 у каждой — единственный
   способ гарантировать, что на 320 px ряд поместится целиком при любой длине
   названия комнаты. Прокрутки вбок на главном экране быть не должно. */
.rooms {
    display: flex;
    gap: var(--s2);
    margin-top: var(--s3);
}

.room {
    flex: 1 1 0;
    /* Без этого длинное название распирает кнопку изнутри и ряд вылезает за экран. */
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 1px;
    /* Поля узкие не от жадности: на 320 px вкладке достаётся 89 px, и каждый
       пиксель здесь — это буква подписи, которая не обрежется. */
    padding: var(--s1) var(--s2);
    /* Вкладка — такая же клавиша, что и кнопки покупки, и свет на ней тот же. */
    background-color: var(--key-top);
    background-image: linear-gradient(180deg,
        var(--key-top) 0%, var(--key-mid) 55%, var(--key-bot) 100%);
    border: 1px solid var(--key-line);
    border-radius: var(--radius);
    box-shadow: inset 0 1px 0 var(--rule);
    color: var(--txt);
    font-family: var(--font-ui);
    text-align: left;
    cursor: pointer;
    transition: border-color var(--t-fast) linear;
    -webkit-tap-highlight-color: transparent;
}

.room:active {
    background-image: linear-gradient(180deg, var(--key-press-top) 0%, var(--key-press-bot) 100%);
    box-shadow:
        var(--shadow-key-press);
}

/* Своя вкладка тоже обязана отзываться на нажатие: её заливка объявлена ниже и
   иначе перебила бы :active порядком в файле. */
.room.is-here:active {
    background-color: var(--key-press-top);
    background-image: linear-gradient(180deg, var(--key-press-top) 0%, var(--key-press-bot) 100%);
}

.room:focus-visible {
    outline: 1px solid var(--me);
    outline-offset: 1px;
}

/* Своя комната. Янтарь во всём приборе означает «это про тебя», поэтому выделение
   ровно одно и оно янтарное. Полоса нарисована внутренней тенью, а не рамкой:
   рамка изменила бы ширину вкладки, и ряд дёргался бы при каждом переключении. */
/* Активная вкладка не обведена, а подсвечена: её фаска ловит янтарь вместо белого.
   Планка слева остаётся внутренней тенью — рамка изменила бы ширину вкладки,
   и ряд дёргался бы при каждом переключении. */
/* Выбранный стол. Янтарная планка слева говорит «это твой стол», но заметить
   её можно, только уже глядя на ряд; заливка отвечает на вопрос «который
   выбран» с одного взгляда, а на светлой теме — ещё и единственным способом:
   там все три вкладки тёмные, и одна светлая читается мгновенно. */
.room.is-here {
    border-color: var(--line-hi);
    background-color: var(--key-on-top);
    background-image: linear-gradient(180deg,
        var(--key-on-top) 0%, var(--key-on-mid) 55%, var(--key-on-bot) 100%);
    box-shadow:
        inset 0 1px 0 rgba(255, 176, 0, .16),
        inset 2px 0 0 var(--me);
}

.room.is-here .room-name {
    color: var(--me);
}

/* Попросились, но сервер ещё не подтвердил. Приглушённый янтарь — тот же смысл,
   что и у выбывшего своего сегмента: место уже твоё, но ты ещё не там. */
.room.is-wait .room-name {
    color: var(--me-out);
}

.room-head {
    display: flex;
    align-items: center;
    gap: var(--s1);
    min-width: 0;
}

/* Метка фазы. Цвет сам по себе ничего не объясняет, поэтому фаза словами лежит
   в подписи всей вкладки (aria-label и title) — там её видит и голосовое чтение. */
.room-dot {
    flex: 0 0 auto;
    width: 5px;
    height: 5px;
    background: var(--line-hi);
}

/* Точка вкладки говорит цветом, что в комнате происходит. Раньше сбор был
   серым, а овертайм и выбывание — одинаково белыми: две совершенно разные
   фазы выглядели одним состоянием. Слова остаются в aria-label вкладки. */
.room[data-phase="waiting"] .room-dot {
    background: var(--live-dim);
}

.room[data-phase="collect"] .room-dot {
    background: var(--live);
}

.room[data-phase="overtime"] .room-dot {
    background: var(--ot);
}

.room[data-phase="elimination"] .room-dot {
    background: var(--risk);
    animation: room-live 1.4s ease-in-out infinite alternate;
}

@keyframes room-live {
    from { opacity: 1; }
    to { opacity: .3; }
}

/* ── что со мной за ЭТИМ столом ───────────────────────────────────────────
   Игрок держит два-три стола сразу, а смотрит в один. Пока он смотрит, на
   остальных его успевают срезать волной и там успевает закончиться раунд.
   Узнать об этом ему неоткуда: чужой стол живёт своей жизнью за той же
   вкладкой, где написан только чужой пот.

   Показано это ТОЙ ЖЕ точкой, что уже говорит про фазу, и ни одним пикселем
   высоты больше: внизу экрана игры ровно ноль запаса, кольцо и так упирается
   в стойку приборов. Работают только тень и заливка — на раскладку они не
   влияют вовсе.

   Грамматика взята готовой, а не выдумана: янтарь во всём приборе значит
   «это про тебя» (--me), тусклый янтарь — «место за тобой, но ты выбыл»
   (--me-out, тот же смысл у выбывшего сегмента на кольце), а деньги всегда
   бирюзовые (--gain). Цвет самой точки при этом остаётся фазой комнаты:
   врать про то, что там происходит, нельзя даже ради своей пометки. */

/* Я в игре: точка комнаты берёт янтарное кольцо. */
.room[data-you="playing"] .room-dot,
.room[data-you="alive"] .room-dot {
    /* Первое кольцо — цветом самой клавиши: без просвета янтарь слипся бы
       с цветом фазы и перестал читаться и как кольцо, и как фаза. */
    box-shadow: 0 0 0 1px var(--surf-2-mid), 0 0 0 2px var(--me);
}

/* Выбыл или доиграл без приза: точка гаснет изнутри. Мигание фазы выбывания
   при этом снимается — тревога «сейчас режут» уже не про тебя. */
.room[data-you="out"] .room-dot,
.room[data-you="lost"] .room-dot {
    background: transparent;
    box-shadow: inset 0 0 0 1px var(--me-out);
    animation: none;
}

/* Выиграл. Заметно, но не кричит: свечение вместо мигания — игрок в этот
   момент смотрит на ДРУГОЙ стол, и дёргать его оттуда мы не вправе. */
.room[data-you="won"] .room-dot {
    background: var(--gain);
    box-shadow: 0 0 4px var(--gain);
    animation: none;
}

/* И то же самое крупно — планкой у левого края вкладки, чтобы исход было видно
   боковым зрением, не вчитываясь в пять пикселей точки. Только на ЧУЖИХ
   вкладках: на своей эту планку занимает янтарь «ты здесь», и он важнее —
   про свой стол игрок и так всё видит на кольце. */
.room:not(.is-here)[data-you="out"],
.room:not(.is-here)[data-you="lost"] {
    box-shadow:
        inset 0 1px 0 var(--rule),
        inset 2px 0 0 var(--me-out);
}

.room:not(.is-here)[data-you="won"] {
    box-shadow:
        inset 0 1px 0 var(--rule),
        inset 2px 0 0 var(--gain);
}

.room-name {
    font-size: var(--fs-cap);
    line-height: 1.25;
    letter-spacing: var(--ls-cap);
    text-transform: uppercase;
    color: var(--txt-2);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Подпись слева, число справа: две такие строки читаются как маленькая таблица,
   и колонка чисел выравнивается по правому краю во всех вкладках сразу. */
.room-line {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s1);
    min-width: 0;
    /* Строка обязана обрезать себя сама: без этого сжатие детей ничего не
       решает — они выпирают наружу вместе со строкой. */
    overflow: hidden;
    line-height: 1.25;
}

/* Обрезается подпись, а не число: число здесь и есть содержание. */
.room-k {
    flex: 0 1 auto;
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    text-transform: uppercase;
    /* На поднятой поверхности клавиши --txt-3 даёт 4.33:1 и норму не проходит:
       подпись, которую игрок обязан прочитать, берёт --txt-dim (4.63:1 в худшей
       точке градиента, 5.11:1 в реальной нижней половине). */
    color: var(--txt-dim);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.room-v {
    /* Сжимается, но не ниже читаемого, и обрезается многоточием.
       Раньше стояло flex: 0 0 auto без overflow: пятизначный пот («21330.00»)
       рядом с ценой «100» не помещался во вкладку 72-85 px на телефоне 320-360
       и вылезал ЗА её край, наезжая на соседнюю. Ровно это владелец и назвал
       «визуальными багами с цифрами». Обрезка честнее наезда: обрезанное число
       видно как обрезанное, а наехавшее читается как чужое. */
    flex: 0 1 auto;
    min-width: 0;
    font-size: var(--fs-md);
    color: var(--txt);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Первая строка вкладки — пот, вторая — игроки. Деньги красятся, люди нет.
   nth-child(2), а не first-of-type: первый элемент внутри вкладки — .room-head. */
.room-line:nth-child(2) .room-v { color: var(--gain); }

/* ── фирменная риска ───────────────────────────────────────────────────────
   Разделитель — не граница карточки, а отрезок измерительной шкалы. Грамматика
   не придумана: она снята с лимба кольца, где 120 рисок и каждая десятая длиннее
   и светлее. Мажорная риска 6 px на базовом (левом) конце, минорная 4 px на правом,
   обе седлают линию — 1 px по одну сторону, остальное по другую. Это единственная
   деталь, которая связывает язык канвы с языком панелей.

   Ограничение — часть приёма: 12-15 рисок на экран. На телефоне их ровно 12
   (шапка, приборная строка, цоколь, три панели правой колонки). Строки ленты
   и вкладки комнат рисок не получают: при сорока штуках приём превращается в шум.

   Риска в 1 px на дробном DPR размазывается, если её двигать трансформацией,
   поэтому она лежит ровно на left/right родителя и не двигается никогда.
   Кадров не стоит: двенадцать статических псевдоэлементов, которые композитятся
   один раз и не перерисовываются ни на волне, ни на прокрутке. */

.hdr::before,
.hdr::after,
.stats::before,
.stats::after,
.chance::before,
.chance::after,
.panel::before,
.panel::after {
    content: "";
    position: absolute;
    width: 1px;
    pointer-events: none;
}

/* Мажорная — слева, от базового края шкалы. */
.hdr::before,
.stats::before,
.chance::before,
.panel::before {
    left: 0;
    height: 6px;
    background: var(--line-hi);
}

.hdr::after,
.stats::after,
.chance::after,
.panel::after {
    right: 0;
    height: 4px;
    background: var(--line);
}

.stats::before,
.stats::after,
.chance::before,
.chance::after,
.panel::before,
.panel::after {
    top: -1px;
}

/* У шапки линия снизу — риска седлает её с той же стороны, откуда идёт свет. */
.hdr::before,
.hdr::after {
    bottom: -1px;
}

/* Цоколь и шапка идут во всю ширину окна, поэтому их риски держат не край экрана,
   а поля колонки: шкала обязана начинаться там же, где начинается содержимое. */
.hdr::before,
.chance::before {
    left: var(--pad-x);
}

.hdr::after,
.chance::after {
    right: var(--pad-x);
}

/* ── объяснение игры ───────────────────────────────────────────────────────
   Карточка новичка стоит В ПОТОКЕ под клавишами покупки и потому не спорит ни с
   одним правилом направления: она ничего не перекрывает, не заводит слоя над
   канвой кольца и не стоит ни одного кадра.

   Поверхность — та же, что у панели, но приподнятая: карточка временная, её надо
   заметить. Свет по общему правилу строго сверху — светлое ребро под верхним пазом,
   нижний край тонет. Янтарь достаётся ровно двум предметам: своему билету и своему
   сегменту на схемах плюс единственной клавише «ДАЛЕЕ». Точки прогресса, рамки и
   стрелки серые — иначе «янтарь = это про тебя» перестаёт что-либо значить.

   Отступ задан селектором из двух классов намеренно: общий шаг колонки в layout.css
   назначен через `.col > * + *`, и правило равной силы он бы перебил по порядку. */

.controls > .onb {
    margin-top: var(--s4);
}

.onb {
    position: relative;
    padding: var(--s3);
    background-color: var(--surf-1-mid);
    background-image: linear-gradient(180deg,
        var(--surf-1-top) 0%, var(--surf-1-mid) 46%, var(--surf-1-bot) 100%);
    border-top: 1px solid var(--groove);
    border-bottom: 1px solid var(--groove);
    box-shadow:
        inset 0 1px 0 var(--edge-top-2),
        inset 1px 0 0 var(--edge-side),
        inset -1px 0 0 var(--edge-side);
    /* Появление: только прозрачность и сдвиг — раскладку в кадре не двигает ничего.
       При prefers-reduced-motion общее правило в tokens.css срезает длительность. */
    animation: onb-in var(--t-mid) var(--ease-out) 1;
}

@keyframes onb-in {
    from { opacity: 0; transform: translateY(6px); }
    to { opacity: 1; transform: none; }
}

.onb-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s2);
    min-width: 0;
}

/* Крест выступает в поле карточки: оптически он тогда стоит в углу, а не отступив
   от него, а палец всё равно получает свои 26 пикселей. */
.onb-x {
    width: 26px;
    height: 26px;
    margin: -4px -4px -4px 0;
}

/* Все четыре экрана лежат в ОДНОЙ ячейке сетки, как два регистра в центре кольца.
   Причина та же: высота блока тогда равна самому высокому экрану и не меняется при
   переключении. Иначе на 320 px последний экран переносится на четвёртую строку,
   и карточка на глазах подрастает на восемнадцать пикселей, дёргая всё под собой. */
.onb-body {
    display: grid;
    margin-top: var(--s3);
}

.onb-step {
    grid-area: 1 / 1;
    /* Именно visibility, а не display: погашенный экран продолжает занимать высоту,
       но уходит и из порядка обхода клавиатурой, и из дерева доступности. */
    visibility: hidden;
}

.onb-step.on {
    visibility: visible;
}

/* Схема нарисована кодом и НЕ тянется по ширине колонки: подписи внутри набраны
   теми же 12 пикселями, что и подписи интерфейса, а растянутый viewBox раздул бы
   их вдвое на десктопе и вдвое же уменьшил на 320. */
.onb-svg {
    display: block;
    max-width: 100%;
    margin-bottom: var(--s3);
}

.onb-track {
    fill: none;
    stroke: var(--seg-2);
    stroke-width: 5;
}

.onb-me {
    fill: none;
    stroke: var(--me);
    stroke-width: 5;
}

.onb-tick {
    fill: none;
    stroke: var(--seg-2);
    stroke-width: 1;
}

/* Твой билет: янтарь тут занимает 22×14 — меньше любого сегмента на кольце. */
.onb-tick-me {
    fill: rgba(255, 176, 0, .12);
    stroke: var(--me);
    stroke-width: 1;
}

.onb-box {
    fill: none;
    stroke: var(--line-hi);
    stroke-width: 1;
}

.onb-arrow {
    fill: none;
    stroke: var(--txt-3);
    stroke-width: 1.2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* Цвет подписи задан через color, а fill только наследует его: иначе проверка
   контраста читает у SVG-текста унаследованный color и меряет не тот цвет,
   которым он на самом деле нарисован. */
.onb-lbl,
.onb-lbl-sm,
.onb-eq {
    fill: currentColor;
    color: var(--txt-2);
    font-family: var(--font-ui);
    font-size: 12px;
    text-anchor: middle;
}

.onb-lbl {
    font-weight: 600;
    letter-spacing: var(--ls-cap);
}

.onb-text {
    margin: 0;
    font-size: var(--fs-md);
    line-height: 1.45;
    color: var(--txt);
}

.onb-foot {
    display: flex;
    align-items: center;
    gap: var(--s3);
    margin-top: var(--s3);
}

.onb-dots {
    display: flex;
    align-items: center;
    gap: 6px;
}

/* Риски прогресса, а не точки: тот же язык шкалы, что и на лимбе кольца. */
.onb-dot {
    display: block;
    width: 10px;
    height: 2px;
    background: var(--line-hi);
}

.onb-dot.on {
    background: var(--txt-2);
}

.onb-gap {
    flex: 1 1 auto;
    min-width: 0;
}

.onb-rules {
    flex: 0 0 auto;
    font-size: var(--fs-sm);
}

/* Клавишей это делать нельзя: рядом с клавишами покупки вторая клавиша начинает
   спорить с деньгами. Единственное действие карточки набрано подписью. */
.onb-next {
    flex: 0 0 auto;
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    text-transform: uppercase;
    color: var(--me);
    background: transparent;
    border: 0;
    padding: 8px 0 8px var(--s3);
    cursor: pointer;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
}

/* ── видимость ─────────────────────────────────────────────────────────── */

/* Повтор правила последним по каскаду. В прошлой версии .hidden перебивалось
   более поздними правилами, и скрытые блоки видели все (дефект 9).
   Здесь оно и последнее, и с !important — перебить его нечем. */
.hidden,
[hidden] {
    display: none !important;
}

/* ─── мелочи, по которым видно, что над продуктом работали ─────────────────
   Каждая из них по отдельности незаметна. Вместе они и создают разницу между
   «сделано» и «спроектировано». */

/* Выделение текста берёт цвет продукта, а не синий цвет браузера по умолчанию. */
::selection {
    background: rgba(255, 176, 0, .22);
    color: var(--txt);
}

/* Долгое нажатие на элементах управления не должно вызывать системное меню
   копирования: игрок целится в кнопку, а не в текст. Читаемые данные — хэши,
   псевдонимы, суммы — наоборот, обязаны выделяться и копироваться. */
.btn,
.room-tab,
.stage,
.cap {
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
}

.hash,
.row-pid,
.stat-v,
.chance-value {
    -webkit-user-select: text;
    user-select: text;
}

/* Видимый фокус для клавиатуры — но не для мыши и пальца: подсветка после
   каждого нажатия выглядит как сбой. */
:focus-visible {
    outline: 2px solid var(--me);
    outline-offset: 2px;
    border-radius: 4px;
}

/* Подсветка нажатия на мобильных браузерах — своя, а не серый прямоугольник системы. */
* {
    -webkit-tap-highlight-color: transparent;
}

/* ── совсем мало высоты: маленький телефон набок в полноэкранном режиме ──────
   Telegram в полном экране забирает под свои кнопки до 105 px сверху. На экране
   высотой 320 после шапки остаётся 158 px под ВСЁ — ряд комнат, показания,
   шкалу, клавиши и строку состояния, которым нужно 187 (замер:
   tools/visual/landscape.mjs, 568×320 полноэкранный).

   Сжимаем ровно то, без чего можно обойтись: у вкладки комнаты остаётся
   название (пот своей комнаты и так стоит в приборной строке рядом), клавиши
   садятся с 52 до 44 — это всё ещё выше 44 px, минимума для пальца, — а у
   приборной строки уходит запас высоты. Правило привязано к КЛАССУ полного
   экрана, а не только к высоте окна: без него на тех же 320 px места хватает,
   и резать там нечего.
   Отдельным блоком, а не внутри общего ландшафтного: в обычном режиме эта
   раскладка сходится и без сжатия. */
@media (max-height: 400px) and (min-width: 560px) {
    .tg-fullscreen .room-line {
        display: none;
    }

    .tg-fullscreen .btn {
        height: 44px;
    }

    .tg-fullscreen .stats {
        min-height: 26px;
    }
}

/* На очень низком экране строка овертайма уступает место кнопкам покупки:
   продления видны и по прыжку таймера, а без кнопок играть нечем.
   Правило живёт здесь, а не в layout.css: этот файл грузится последним,
   и объявление display у .overtime иначе перебивает его обратно. */
@media (max-height: 520px) {
    .overtime {
        display: none !important;
    }
}

/* Знак процента у главного числа вывешивается ЗА его пределы. Центрируются сами
   цифры, а не «цифры со знаком»: иначе число уезжает вправо на половину ширины
   знака, и в кольце это сразу видно. Приём обычный для крупных величин —
   единица измерения не участвует в оптическом центре.
   Правило живёт здесь, а не во встроенных стилях разметки: этот файл грузится
   последним, и объявление .chance-value .sfx иначе перебивает его обратно. */
.dial-reg-chance .chance-value {
    position: relative;
}

/* У длинного варианта в знаке стоит «USDT», а не процент: обнулять ему ширину
   нельзя — надпись наложилась бы на само число. */
.dial-reg-chance .chance-value.long .sfx {
    width: auto;
    min-width: auto;
    flex: 0 0 auto;
}

.dial-reg-chance .chance-value:not(.long) .sfx {
    /* Нулевая ширина вместо абсолютного позиционирования: знак остаётся в потоке,
       но не участвует в расчёте ширины ни на одном уровне — ни у самого числа,
       ни у ячейки сетки над ним. Абсолютное позиционирование этого не давало:
       ячейка всё равно мерила знак как обычный. */
    /* Флексовый потомок по умолчанию имеет min-width: auto, то есть не может быть
       уже своего содержимого, — и знак продолжал резервировать место, даже когда
       ширина задана нулём. Отсюда и брались те самые 13 пикселей смещения. */
    width: 0;
    min-width: 0;
    flex: 0 0 0;
    overflow: visible;
    white-space: nowrap;
    margin-left: .05em;
    transform: translateY(-.62em);
}

/* Кегль верхней подписи привязан к кольцу — но только когда кольцо совсем
   маленькое. «ДО ЗАКРЫТИЯ» — одиннадцать знаков в разрядку, и при стороне сцены
   меньше ~147 px они перестают помещаться в отведённые 0.72 диаметра: строка
   вылезала за круг (замер: 568×320 в полноэкранном режиме, «текст не помещается»).
   Множитель 0.075 подобран по ширине этой самой длинной подписи, пол в 8 px —
   предел читаемости капители. Выше 147 работает обычный --fs-cap, поэтому в
   портрете и на планшете не меняется ничего.

   Правило живёт здесь, а не во встроенных стилях разметки, и селектор из двух
   классов не случаен: встроенный блок в index.html грузится после ссылок на
   стили и правило равной силы перебил бы обратно. */
.dial .dial-hint {
    font-size: max(8px, min(var(--fs-cap), calc(var(--stage-side) * .075)));
}

/* Подпись в центре кольца не имеет права вылезти за круг. В финале она самая
   длинная («ИЗ 14 ИГРОКОВ · РАУНД 3») и упиралась в кольцо, обрезаясь на полуслове.
   Ширина считается от стороны сцены: внутренний диаметр примерно 0.81 от неё,
   берём с запасом, потому что у окружности края уже середины.
   Важное: перенос вместо многоточия — в финале эта строка несёт смысл. */
.dial .dial-phase {
    /* Центрирование задаётся здесь же, а не в другом правиле: у этого селектора
       специфичность выше, и разнесённые по файлам объявления уже дважды приводили
       к тому, что left применялся, а компенсирующее смещение — нет, и подпись
       уезжала вправо от центра кольца. */
    left: 0;
    right: 0;
    margin-inline: auto;
    max-width: calc(var(--stage-side) * .68);
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    line-height: 1.25;
    text-align: center;
}

/* Строка состояния оболочки в справке: мелкая, приглушённая, для разбора жалоб. */
.onb-shell {
    margin: 8px 0 0;
    font-size: 12px;
    line-height: 1.35;
    color: var(--txt-3);
    text-align: center;
}

/* Кнопка запроса полного экрана в справке: обычная клавиша, но узкая и тихая —
   она нужна редко, только когда автоматический запрос не сработал. */
.onb-fs {
    display: block;
    margin: 8px auto 0;
    padding: 8px 14px;
    background: transparent;
    border: 1px solid var(--line-hi);
    border-radius: var(--radius);
    color: var(--txt-2);
    font-family: var(--font-ui);
    font-size: 12px;
    letter-spacing: var(--ls-cap);
    text-transform: uppercase;
    cursor: pointer;
}

.onb-fs:active {
    border-color: var(--me);
    color: var(--me);
}

/* Двойной тап по игровому полю не должен масштабировать страницу: в игре,
   где кнопки жмут быстро и подряд, это всегда промах, а не намерение.
   manipulation отключает именно двойной тап, оставляя обычную прокрутку. */
html,
body,
.wrap,
.stage,
.btn,
.room-tab,
.icon-btn {
    touch-action: manipulation;
}

/* Стили и разметка разошлись — игрок смотрит на старое оформление. Это не
   украшение строки состояния, а единственный видимый признак того, что
   у клиента в кэше застряла прошлая версия. */
.onb-shell.onb-stale {
    color: var(--risk);
}

/* ── заслонка «открой из Telegram» ─────────────────────────────────────────
   Видна только вне Telegram (см. index.html). Перекрывает всё: за ней нет
   ничего работающего, и позволить в это тыкать — значит обмануть. */
.outside {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: grid;
    place-items: center;
    padding: var(--s5);
    background: var(--ink-0);
}

.outside[hidden] { display: none; }

.outside-card {
    max-width: 30rem;
    text-align: center;
}

.outside-cap {
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    color: var(--txt-3);
    margin-bottom: var(--s4);
}

.outside-h {
    font-family: var(--font-ui);
    font-size: var(--fs-lg);
    line-height: 1.2;
    color: var(--txt);
    margin-bottom: var(--s3);
}

.outside-t {
    font-size: var(--fs-sm);
    line-height: 1.5;
    color: var(--txt-2);
    margin-bottom: var(--s5);
}

/* Единственное действие на экране — оно и красится в янтарь: здесь янтарь
   означает «это про тебя» ровно в том же смысле, что и всегда. */
.outside-btn {
    display: inline-block;
    padding: var(--s3) var(--s5);
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    text-transform: uppercase;
    text-decoration: none;
    color: var(--ink-0);
    background: var(--me);
    border-radius: 2px;
}

/* ── что в шапке уступает место первым ────────────────────────────────────
   Раньше в шапку помещалось всё, и правил не требовалось. С третьим значком
   (тема) перестало: в полноэкранном режиме справа зарезервировано 92 px под
   кнопки Telegram, и на телефоне 390 px на содержимое остаётся 278. Всё
   поголовно объявлено nowrap, поэтому лишнее не переносилось, а обрезалось
   на полусимволе — это и выглядело как «вёрстка налезает друг на друга».

   Очередь уступки задана по важности, а не по порядку в разметке:
   1. номер раунда — он же стоит в подписи под циферблатом;
   2. слово USDT — валюта в игре одна;
   баланс не сжимается никогда: это единственное число, ради которого в шапку
   и смотрят.

   Название игры из этой очереди ВЫШЛО (решение владельца 2026-09-07: «пусть
   слева будет написано название»). Раньше оно уступало первым и на телефоне
   не показывалось вовсе — то есть не показывалось никому, потому что играют
   с телефона. Место для него нашлось за счёт номера раунда: тот и правда
   продублирован под циферблатом, а имя игры на экране больше нигде не стоит. */
.hdr-rnd {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
}

.hdr-balance {
    flex: 0 0 auto;
    white-space: nowrap;
}

.hdr .icon-btn {
    flex: 0 0 auto;
}

/* Узкий экран и полный экран — два разных повода для одной и той же тесноты. */
@media (max-width: 479.98px) {
    /* Порог поднят с 410 до 480: место, которое занимал номер раунда, теперь
       занято именем игры, и на 430 номер уже не помещался — обрезался на
       «RND 128». Номер продублирован под циферблатом, имя игры — нигде. */
    .hdr-rnd { display: none; }
}

/* В самой тесноте имя набирается без разрядки: восемь букв с ls-cap шире
   свободного места на 3-4 px, а обрезать имя нельзя. Разрядка — это стиль,
   ширина — это работает или нет. */
@media (max-width: 409.98px) {
    .hdr-brand { letter-spacing: 0; }
}

.tg-fullscreen .hdr-rnd {
    display: none;
}

@media (max-width: 359.98px) {
    .hdr-cur { display: none; }
}

/* Полный экран — самая тесная раскладка из всех: справа 92 px отдано кнопкам
   Telegram, и ряд разделов упирается в них первым. Место под имя игры берётся
   у слова USDT (валюта в игре одна, и она подписана всюду, где есть суммы) и у
   разрядки имени. Обрезать имя или прятать его нельзя — оно здесь и стоит. */
.tg-fullscreen .hdr-cur {
    display: none;
}

.tg-fullscreen .hdr-brand {
    letter-spacing: 0;
}

/* Единственное место, где имя всё-таки уходит: полный экран на телефоне уже
   360 px. Там на содержимое остаётся 228 px, из которых 130 занимает ряд
   разделов и 57 — баланс; имя не помещается ни в какой набор. Выбор здесь не
   между «красиво» и «некрасиво», а между именем и разделами, которые иначе
   уезжают под кнопки Telegram и перестают нажиматься. Замер —
   tools/visual/hdrcheck.mjs full. */
@media (max-width: 360.5px) {
    .tg-fullscreen .hdr-brand { display: none; }
}

/* Название стоит всегда и целиком — обрезок читался бы как поломка, поэтому
   уступает место не оно, а номер раунда (см. очередь уступки выше). Уйти ему
   некуда: имя игры на экране больше нигде не написано. */
.hdr-brand {
    flex: 0 0 auto;
    color: var(--txt);
    font-weight: 500;
}


/* ══ ЭКРАН БЕЗ ПРОКРУТКИ ═══════════════════════════════════════════════════
   Правила ниже собраны в одном месте намеренно: все они служат одному
   требованию — экран игры помещается в окно целиком на любом телефоне.
   Разнесённые по своим разделам, они через месяц разойдутся поодиночке.

   Считано стендом tools/visual/fitcheck.mjs. До правки на 360×640 в полном
   экране документ был 1129 px при окне 640; лишними были справочная колонка
   (379 px), двухэтажная приборная строка, трёхстрочные вкладки комнат и пустая
   строка овертайма. */

/* ── ручка выдвижной панели ───────────────────────────────────────────────
   Приборная строка и список участников — одно и то же сведение в двух
   подробностях, поэтому список открывается ОТСЮДА. Отдельная кнопка стоила бы
   ещё одной строки высоты, то есть куска кольца. */
.stats {
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.stats:focus-visible {
    outline: 1px solid var(--me);
    outline-offset: 2px;
}

.stats-caret {
    align-self: center;
    padding-left: var(--s2);
    font-size: var(--fs-cap);
    line-height: 1;
    color: var(--txt-3);
    transition: transform var(--t-fast) var(--ease-out);
}

.stats[aria-expanded="true"] .stats-caret {
    transform: rotate(90deg);
}

/* На десктопе справочная колонка стоит на виду, открывать нечего: ручка
   перестаёт быть ручкой и снова становится просто приборной строкой. */
@media (min-width: 760px) {
    .stats {
        cursor: default;
    }

    .stats-caret {
        display: none;
    }
}

/* ── шапка выдвижной панели: только на телефоне ───────────────────────────── */
.sheet-head {
    display: none;
}

.sheet-x {
    appearance: none;
    background: none;
    border: 0;
    padding: 0 var(--s1);
    font-size: 15px;
    line-height: 1;
    color: var(--txt-3);
    cursor: pointer;
}

/* ── кнопка «подробности раунда» ───────────────────────────────────────────
   Тихий финал: после развязки не выезжает ничего, а справка ждёт за одной
   маленькой кнопкой. Поэтому она намеренно НЕ клавиша игры — ни размера, ни
   веса клавиш покупки у неё нет: рядом с итогом раунда крупная клавиша читалась
   бы как «сделай что-нибудь», хотя делать уже нечего. Строчный набор вместо
   капители по той же причине: это предложение почитать, а не команда прибора. */
.round-more {
    appearance: none;
    display: block;
    margin: var(--s3) auto 0;
    padding: var(--s2) var(--s4);
    font-family: var(--font-ui);
    font-size: var(--fs-md);
    line-height: 1;
    /* Заметная, а не служебная.
       Прежнее решение было осознанным — «предложение почитать, а не команда
       прибора», — и записано рядом. Владелец посмотрел на живой экран и
       попросил обратного: серая строка в подвале не читается как ссылка вовсе,
       и доказательство честности раунда, ради которого вся игра и затевалась,
       никто не открывает. Текст остаётся строчным (это по-прежнему не клавиша
       покупки), но цвет, рамка и подчёркивающая черта делают её видимой. */
    color: var(--me);
    background: none;
    border: 1px solid var(--me-dim);
    border-radius: var(--radius);
    box-shadow: inset 0 1px 0 var(--edge-top);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.round-more::after {
    content: ' →';
    /* Тем же цветом, что и текст: --me-dim в тёмной теме объявлен «для текста
       темновато» и годится только рамке. */
    color: var(--me);
}

.round-more:active {
    color: var(--txt);
    border-color: var(--me);
    background: var(--surf-2-bot);
}

/* На десктопе открывать нечего: справочная колонка и так стоит в потоке справа
   и после раунда никуда не девается. */
@media (min-width: 760px) {
    .round-more {
        display: none !important;
    }
}

/* Плотные варианты приборной строки и вкладок: не только «узкий экран», но и
   «низкий». В альбомной ориентации ширины хватает, а высоты нет — и двухэтажная
   приборная строка (52 px) со вкладкой в три этажа (60 px) съедают там ровно ту
   вертикаль, которой не хватает кольцу и клавишам. Условие через запятую: узкий
   ИЛИ низкий. */
@media (max-width: 759.98px), (max-height: 620px) {
    /* ── приборная строка в одну строку ───────────────────────────────────
       Подпись и значение стояли этажами: 52 px. На одной базовой линии они
       занимают 30 и читаются не хуже — ступень кегля 12→18 разделяет их
       надёжнее, чем перенос строки. */
    .stats {
        /* Колонки по содержимому, а не тремя равными долями: равные доли режут
           «БИЛЕТОВ» многоточием ради пустоты в колонке пота. Свободное место
           уходит в промежутки между показаниями. */
        grid-template-columns: auto auto auto auto;
        justify-content: space-between;
        align-items: center;
        min-height: 30px;
        margin-top: var(--s3);
        padding-top: var(--s2);
    }

    .stat {
        display: flex;
        align-items: baseline;
        gap: 6px;
        padding-right: var(--s1);
    }

    .stat + .stat {
        padding-left: var(--s2);
    }

    .stat > .cap {
        flex: 0 1 auto;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .stat-v {
        margin-top: 0;
        flex: 0 0 auto;
        font-size: 17px;
    }

    /* ── вкладки комнат: два этажа вместо трёх ────────────────────────────
       Три этажа (название, пот, игроки) = 60 px. Во время раунда вкладка
       отвечает на один вопрос — «где сейчас крупнее», — и для него довольно
       названия и пота. */
    .rooms {
        margin-top: var(--s2);
    }

    /* Снимается ВТОРАЯ строка данных — число игроков в ЧУЖОЙ комнате. Она не
       решает ничего: перейти туда игрок решает по размеру пота. Название и пот
       остаются на своих строках; в одну строку они не сходятся ни на одной
       ширине — «ОСНОВНАЯ 16.20» в 101 px вкладки на экране 360 не влезает,
       и попытка сжать их в строку резала название до «ОСН…». */
    .room-line:nth-child(3) {
        display: none;
    }

}

/* Шторка справочной колонки — только по УЗКОМУ экрану, без «или низкий». В
   альбомной ориентации от 760 px лента стоит третьей колонкой в потоке
   (layout.css), и превращать её обратно в шторку нельзя: выезжая снизу, она
   накрыла бы клавиши покупки, которые там стоят справа, а не внизу. */
@media (max-width: 759.98px) {
    /* ── справочная колонка выезжает снизу ────────────────────────────────
       Участники, волны и честность читаются раз в раунд или раз в жизни,
       а высоту отнимали каждую секунду. Панель лежит вне потока, поэтому
       документ от неё не растёт вовсе. */
    .col-right {
        position: fixed;
        left: 0;
        right: 0;
        /* Панель садится НА причал, а не на край экрана: шкала, клавиши покупки
           и строка состояния остаются на виду и в работе, пока читают список. */
        bottom: calc(var(--dock-h) + var(--safe-bottom));
        z-index: 46;
        max-height: min(62dvh, 520px);
        overflow-y: auto;
        overscroll-behavior: contain;
        padding: var(--s3) var(--pad-x);
        background-color: var(--surf-1-top);
        background-image: linear-gradient(180deg,
            var(--surf-1-top) 0%, var(--surf-1-mid) 40%, var(--surf-1-bot) 100%);
        border-top: 1px solid var(--line-hi);
        border-bottom: 1px solid var(--groove);
        box-shadow: var(--shadow-panel);
        /* Закрытая панель убрана и трансформацией, и видимостью: одной трансформации
           мало — фокус и чтение с экрана продолжают в неё заходить. */
        /* Уезжает ниже своего причала целиком: 100% её собственной высоты мало,
           когда она стоит не на краю экрана. */
        transform: translateY(calc(100% + var(--dock-h) + var(--safe-bottom)));
        visibility: hidden;
        transition:
            transform var(--t-mid) var(--ease-out),
            visibility 0s linear var(--t-mid);
    }

    .col-right.is-open {
        transform: none;
        visibility: visible;
        transition: transform var(--t-mid) var(--ease-out), visibility 0s;
    }

    .sheet-head {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: var(--s2);
        margin-bottom: var(--s2);
    }

    /* Первая панель в выехавшей шторке своего паза не получает: над ней шапка шторки. */
    .col-right > .panel:first-of-type {
        border-top: 0;
        box-shadow: none;
        padding-top: 0;
    }
}

@media (max-width: 759.98px) {
    /* ── объяснение игры — накладка, а не блок в колонке ───────────────────
       В потоке карточка «как это работает» занимает больше двухсот пикселей.
       Пока страница листалась, она просто отодвигала ленту вниз; теперь высоту
       у неё пришлось бы отнять у кольца, то есть у самой игры. Объяснение —
       это разговор вместо игры, а не рядом с ней, поэтому оно ложится поверх. */
    .controls > .onb {
        position: fixed;
        left: var(--pad-x);
        right: var(--pad-x);
        top: 50%;
        z-index: 48;
        margin-top: 0;
        max-width: calc(var(--col-max) - 2 * var(--pad-x));
        margin-inline: auto;
        max-height: calc(100dvh - var(--safe-top) - var(--safe-bottom) - 2 * var(--s4));
        overflow-y: auto;
        overscroll-behavior: contain;
        box-shadow:
            inset 0 1px 0 var(--edge-top-2),
            inset 1px 0 0 var(--edge-side),
            inset -1px 0 0 var(--edge-side),
            var(--shadow-sheet);
        /* Своя анимация: общая onb-in ставит transform и стирает центрирование. */
        transform: translateY(-50%);
        animation: onb-in-fixed var(--t-mid) var(--ease-out) 1;
    }

    @keyframes onb-in-fixed {
        from { opacity: 0; transform: translateY(calc(-50% + 6px)); }
        to { opacity: 1; transform: translateY(-50%); }
    }
}

/* ── РАЗДЕЛЫ: игра, кошелёк, профиль, друзья ─────────────────────────────
   Игра видна БЕЗ участия навигации: скрыты именно профиль и друзья, а не игра.
   Обратная схема — «прятать игру, пока навигация не сказала иначе» — означала
   бы пустой экран от первой отрисовки до запуска модуля, а при его падении —
   навсегда. Интерфейс переживает падение любой панели, и переживать его должен
   с игрой на экране. */
.screen {
    display: none;
}

body.on-wallet #screen-wallet,
body.on-profile #screen-profile,
body.on-friends #screen-friends,
body.on-leaders #screen-leaders {
    display: block;
    /* Лёгкий вход вместо мгновенной смены: display none→block перезапускает
       анимацию сам. Reduced-motion гасит её глобальным правилом tokens.css. */
    animation: screen-in var(--t-mid) var(--ease-out);
    /* Вбок эти экраны не прокручиваются никогда: любое переполнение — дефект
       строки, а не повод возить страницу пальцем в стороны. */
    overflow-x: hidden;
    /* Своя прокрутка обязательна: на телефоне прокрутка документа выключена
       ради безпрокруточного экрана игры. */
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    width: 100%;
    max-width: var(--col-max);
    margin: 0 auto;
    padding: 0 var(--pad-x) calc(var(--safe-bottom) + 12px);
}

body.on-wallet #screen-game,
body.on-profile #screen-game,
body.on-friends #screen-game,
body.on-leaders #screen-game {
    display: none;
}

/* На десктопе мобильные 420 px посреди широкого окна выглядят щелью: колонка
   чтения расширяется до книжной. Ширина явная — тело страницы флекс-колонка,
   и без неё авто-поля сжали бы экран до содержимого (см. layout.css про .hdr). */
@media (min-width: 760px) {
    body.on-wallet #screen-wallet,
    body.on-profile #screen-profile,
    body.on-friends #screen-friends,
    body.on-leaders #screen-leaders {
        width: 100%;
        max-width: 560px;
    }
}

/* Правый угол шапки: «?» и переключатель разделов одной группой.
   Зазор внутри группы 4 px, а не общий шаг шапки: «?» и разделы стоят рядом и
   читаются как один блок органов управления, а не как два разных.

   Числа, из-за которых группа вообще появилась. Свободного места в шапке
   (ширина распорки .hdr-gap, замер на стенде при балансе «12480.00»):
     обычный режим   320/360/390/430 → 103, 102, 132, 108 px;
     полный экран    320/360/390/430 →  31,  30,  60, 100 px.
   В полном экране справа зарезервировано 92 px под кнопки Telegram, и там места
   в обрез. Кнопка «?» — это 28 px плюс 4 px зазора, то есть 32 px; на 320 и 360
   в полном экране свободного было 31 и 30. Недостающее берётся из зазоров между
   блоками шапки: правило ниже сужает их с 8 до 4 px там, где тесно, и это отдаёт
   ещё 12-16 px. Проверено tools/visual/fitcheck.mjs и tools/visual/run.mjs. */
.hdr-tools {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 4px;
}

/* Теснота бывает по двум разным поводам, а лечится одинаково: в полном экране
   92 px справа отданы кнопкам Telegram, узкий телефон просто узок.

   Замер (tools/visual/hdrcheck.mjs full): на 320 в полном экране под содержимое
   шапки остаётся 208 px. Ряд справа из пяти кнопок по 28 px с зазорами занимает
   154, баланс в худшем случае 98 — итого 252, и правый край ряда уезжал ПОД
   кнопки Telegram. Это было и до появления значка звука (ряд из четырёх — 122,
   уже на 12 px больше места), просто на двенадцати пикселях этого не видно.
   Лечится не выбрасыванием кнопки, а размером: 24 px вместо 28 и зазор 2 вместо
   4 дают ряду 128 px, и всё сходится. Значок внутри остаётся 16 px — уменьшается
   поле вокруг него, а не сам знак. */
.tg-fullscreen .hdr {
    gap: 4px;
}

@media (max-width: 399.98px) {
    .hdr { gap: 4px; }
}

.tg-fullscreen .hdr-tools { gap: 2px; }

.tg-fullscreen .hdr .icon-btn {
    width: 24px;
    height: 24px;
}

@media (max-width: 399.98px) {
    .hdr-tools { gap: 2px; }
    .hdr .icon-btn { width: 24px; height: 24px; }
}

/* Последние пиксели берутся у того, что можно не показывать, и только в самой
   тесноте — в полном экране на телефоне уже 320.

   Замер до правки (hdrcheck full, худший случай «12480.00 USDT»): содержимое
   шапки занимало 20…266 при том, что кнопки Telegram начинаются на 228, — то
   есть разделы уезжали под них на 38 px. Это было и раньше, до значка звука, на
   12 px: беда старая, просто на двенадцати пикселях её не видно.

   Убирается: слово USDT (валюта в приложении одна, и «12480.00» без неё читается
   так же), плюс у баланса (пополнение доступно и с экрана профиля), поле слева
   и по два пикселя от каждой кнопки. Ни одно число и ни один раздел при этом не
   пропадает. */
@media (max-width: 399.98px) {
    .tg-fullscreen .hdr {
        padding-left: var(--s3);
        gap: 2px;
    }

    .tg-fullscreen .hdr .hdr-cur,
    .tg-fullscreen .hdr .hdr-plus { display: none; }

    /* Шестая кнопка (кошелёк) съела прежний запас: с ней ряд на 320 в полном
       экране стоял впритык к кнопкам Telegram, а шестизначный баланс задвигал
       вкладку «друзья» под них. Номер раунда — самое дешёвое, что можно не
       показывать: он есть в финале и в разделе честности, а здесь это 67 px. */
    .tg-fullscreen .hdr .hdr-rnd { display: none; }

    .tg-fullscreen .hdr .icon-btn { width: 22px; height: 22px; }
}

/* Самая теснота: 320–360 в полном экране. Шесть кнопок по 22 px при
   шестизначном балансе всё ещё вылезали под кнопки Telegram на 3 px — ещё по
   два пикселя поля с каждой кнопки возвращают запас. Знак внутри остаётся
   16 px: уменьшается поле, а не сам знак. Замер: tools/visual/hdrcheck.mjs. */
@media (max-width: 369.98px) {
    .tg-fullscreen .hdr .icon-btn { width: 20px; height: 20px; }
}

/* Переключатель разделов в шапке. Рамка вокруг всей тройки, а не вокруг
   каждой кнопки: три отдельные рамки в 28 пикселей читаются как три кнопки,
   а не как один переключатель. */
/* Рамка вокруг тройки значков — своей сплошной линией, а не общим --key-line.
   Общий стал полупрозрачным белым, а под этой рамкой нет своего фона: девять
   процентов белого поверх шапки дают 1.29:1 против 1.51 у прежней сплошной, и
   единственное, что делает тройку одним переключателем, тускнеет. */
.hdr-nav {
    flex: 0 0 auto;
    display: flex;
    gap: 0;
    padding: 2px;
    /* Цвет записан прямо в сокращённое свойство. Отдельная строка
       border-color ВЫШЕ него не работает вовсе: сокращённое свойство сбрасывает
       цвет обратно к своему значению, и починка, написанная строкой выше, молча
       не действовала — замер в браузере давал rgba(255,255,255,.09). */
    border: 1px solid var(--line-hi);
    border-radius: 6px;
}

.hdr-nav .nav-i {
    color: var(--txt-3);
    border-radius: 4px;
}

.hdr-nav .nav-i[aria-selected="true"] {
    color: var(--me);
    background: var(--surf-2-mid);
}

.screen-cap {
    color: var(--txt-2);
    margin: var(--s4) 0 var(--s3);
}

.screen-sub {
    color: var(--txt-3);
    margin: var(--s4) 0 var(--s2);
}

.prof-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s3);
    min-height: var(--row-h);
    box-shadow: 0 1px 0 var(--rule);
}

.prof-k {
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    color: var(--txt-3);
    white-space: nowrap;
}

.prof-v {
    font-size: var(--fs-md);
    color: var(--txt);
    white-space: nowrap;
    /* Флекс-ребёнок по умолчанию не сжимается уже содержимого: длинное
       значение («Kevin MacLeod (incompetech.com) · CC BY 4.0») растягивало
       весь профиль вширь, и экран начинал прокручиваться в стороны. Сжатие с
       многоточием — правильное умолчание; строкам, где обрезать нельзя
       (атрибуция по лицензии), выдан свой класс ниже. */
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Значение, которое обязано быть прочитано целиком: переносится, а не
   режется. CC BY требует называть автора — многоточие здесь не годится. */
.prof-v-wrap {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    overflow-wrap: anywhere;
    text-align: right;
}

.prof-v-hi {
    font-size: var(--fs-lg);
    color: var(--gain);
}

.prof-note {
    font-size: var(--fs-sm);
    line-height: 1.5;
    color: var(--txt-3);
    margin: var(--s2) 0 0;
}

/* Настройки в профиле: те же три кнопки, что раньше жили в шапке. Они не
   разделы, их нажимают раз в жизни — в шапке они занимали место навигации. */
#prof-settings .set-row {
    display: flex;
    gap: var(--s3);
    margin-top: var(--s2);
}

/* Ссылка-приглашение: показана целиком и мелко. Обрезать её многоточием
   нельзя — человек должен видеть, что копирует. */
.ref-link {
    padding: var(--s2) var(--s3);
    background: var(--surf-1-mid);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    word-break: break-all;
}

.ref-link-t {
    font-family: var(--font-code);
    font-size: var(--fs-sm);
    color: var(--txt-2);
}

.ref-copy {
    margin-top: var(--s2);
}

.ref-list {
    margin-top: var(--s2);
}

.ref-item {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s3);
    min-height: var(--row-h);
    box-shadow: 0 1px 0 var(--rule);
}

.ref-name {
    font-size: var(--fs-md);
    color: var(--txt);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    /* Имя отдаёт место дате и сумме, а не наоборот: сумма — это ответ на вопрос,
       ради которого страницу открыли, и обрезаться она не имеет права. */
    flex: 1 1 auto;
    min-width: 0;
}

.ref-date {
    font-size: var(--fs-sm);
    color: var(--txt-3);
    flex: 0 0 auto;
}

/* Сколько принёс этот человек. Ноль не выделяется цветом денег — иначе строка
   обещает заработок, которого не было. */
.ref-earn {
    flex: 0 0 auto;
    font-size: var(--fs-md);
    color: var(--txt-3);
    white-space: nowrap;
}

.ref-earn-hi {
    color: var(--gain);
}

/* ── шкала уровня в профиле ───────────────────────────────────────────────
   Полоса, а не число: «сколько осталось» человек оценивает глазом, а точные
   цифры стоят строкой ниже словами. Высота маленькая намеренно — это шкала
   прибора, а не полоса загрузки. */
.lvl-bar {
    position: relative;
    height: 6px;
    margin: var(--s2) 0 var(--s2);
    background: var(--ink-2);
    /* Контур по всему прямоугольнику, а не риска сверху: у новичка заполнено ноль,
       и без контура шкала читается как случайная тёмная полоска. Видно должно быть
       и то, сколько пройдено, и то, сколько всего. */
    box-shadow: inset 0 0 0 1px var(--rule);
    overflow: hidden;
}

.lvl-fill {
    display: block;
    height: 100%;
    /* Янтарь — «это про тебя», тот же смысл, что у своего сегмента на кольце. */
    background: var(--me);
    /* Ширину ставит js по ответу сервера; переход — чтобы возвращение в профиль
       после сыгранной партии было видно, а не случалось скачком. */
    transition: width var(--t-slow) ease-out;
}

/* Вход в панель управления. Не кнопка покупки и не выглядит ею: это переход
   в другое приложение, а не действие в игре. */
.prof-admin-link {
    display: inline-block;
    margin-top: var(--s2);
    padding: var(--s2) var(--s3);
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    text-transform: uppercase;
    text-decoration: none;
    color: var(--txt-2);
    background: var(--surf-2-mid);
    border: 1px solid var(--key-line);
    border-radius: var(--radius);
}

/* ── пополнение ───────────────────────────────────────────────────────────
   Раздел сознательно без единого акцента, кроме суммы: здесь человек расстаётся
   с настоящими деньгами, и всё, что мигает и торопит, читается как обман. */
.dep-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
    gap: var(--s2);
    margin-top: var(--s2);
}

/* Выбранная сеть и выбранная сумма помечены той же янтарной гранью, что и
   «своя» клавиша в игре: один язык выделения на всё приложение. */
.dep-chip.is-on {
    border-color: var(--me);
    color: var(--me);
}

.dep-go,
.dep-invoice .ref-copy {
    margin-top: var(--s3);
}

/* Адрес кошелька рвётся по символам и набран моноширинным: его сверяют глазами,
   и перенос по словам показал бы его иначе, чем он выглядит в кошельке. */
.dep-invoice .ref-link-t {
    font-size: var(--fs-md);
    color: var(--txt);
}

.dep-err {
    color: var(--risk);
}

/* ── выбор сети: карточки ─────────────────────────────────────────────────
   Сеть выбирают один раз и надолго, а ошибка в ней стоит всей суммы. Поэтому
   не значок, а карточка: полное имя сети тем же словом, каким она названа в
   списке вывода на бирже, и своя нижняя граница рядом. */
.dep-nets {
    display: grid;
    gap: var(--s2);
    margin-top: var(--s2);
}

.dep-net {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 3px;
    width: 100%;
    padding: var(--s2) var(--s3);
    background: var(--surf-1-mid);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    color: var(--txt);
    font-family: var(--font-ui);
    text-align: left;
    cursor: pointer;
}

/* Выбранная сеть помечена той же янтарной гранью, что и «своя» клавиша в игре:
   один язык выделения на всё приложение. */
.dep-net.is-on {
    border-color: var(--me);
    background: var(--surf-2-mid);
}

.dep-net-h {
    display: flex;
    align-items: center;
    gap: var(--s2);
}

.dep-net-t {
    font-size: var(--fs-md);
    letter-spacing: 0.02em;
}

.dep-net.is-on .dep-net-t { color: var(--me); }

/* Совет считается по данным — сеть с самой низкой границей, — а не зашит в код.
   Если границы у сетей сравняются, значка не будет вовсе: совет без причины
   читается как реклама. */
.dep-net-badge {
    padding: 1px var(--s2);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    color: var(--gain);
    border: 1px solid var(--gain-dim);
    border-radius: 999px;
}

.dep-net-s {
    font-size: var(--fs-sm);
    color: var(--txt-3);
}

/* Вторая строка про деньги — настоящий запрет шлюза, а не наш порог. Тише
   первой: это ограничение, а не предложение, и читают его только когда
   набранная сумма мала. */
.dep-net-hard {
    color: var(--txt-4, var(--txt-3));
    opacity: .75;
}

/* ── набор суммы ──────────────────────────────────────────────────────────
   Поле — не input: системная клавиатура в мини-приложении выезжает поверх окна
   и уводит набранное под себя. Свой набор занимает известное место и ничего
   вокруг не двигает. */
.dep-amount {
    display: flex;
    align-items: baseline;
    justify-content: center;
    gap: var(--s2);
    margin-top: var(--s2);
    padding: var(--s3) var(--s2);
    background: var(--surf-1-mid);
    border: 1px solid var(--line);
    border-radius: var(--radius);
}

.dep-amount-v {
    font-size: 34px;
    line-height: 1;
    color: var(--txt);
    font-variant-numeric: tabular-nums;
}

.dep-amount-c {
    font-size: var(--fs-sm);
    letter-spacing: var(--ls-cap);
    color: var(--txt-3);
}

.dep-pad {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--s2);
    margin-top: var(--s2);
}

.dep-key {
    height: 48px;
    background: var(--surf-2-mid);
    border: 1px solid var(--key-line);
    border-radius: var(--radius);
    color: var(--txt);
    font-family: var(--font-num, var(--font-ui));
    font-size: 20px;
    cursor: pointer;
}

.dep-key:active { background: var(--surf-2-top); }
.dep-key-del { color: var(--txt-3); }

/* Подпись под полем и включённость кнопки говорят одно и то же: кнопка,
   обещающая то, что сервер откажет, хуже выключенной. */
.dep-hint {
    margin-top: var(--s2);
    text-align: center;
}

.dep-hint-bad { color: var(--risk); }

.dep-go { width: 100%; }

/* ── счёт ─────────────────────────────────────────────────────────────────
   Код с адресом рисуется своим кодом (js/qr.js). Светлая подложка обязательна:
   распознаватели ждут тёмные модули на светлом, и «тёмная тема» здесь стоила бы
   человеку ручного ввода адреса из 34 символов. */
.dep-qr {
    display: flex;
    justify-content: center;
    margin: var(--s3) 0;
    padding: var(--s2);
    background: #eef3ef;
    border-radius: var(--radius);
}

.dep-qr-c {
    display: block;
    width: 232px;
    max-width: 100%;
    height: auto;
    image-rendering: pixelated;
}

.dep-copy-sum,
.dep-invoice .ref-copy {
    width: 100%;
    margin-top: var(--s2);
}

/* Единственная строка на экране, невнимательность к которой стоит всей суммы
   и не чинится. Поэтому цвет риска и рамка, а не обычная сноска. */
.dep-warn {
    margin-top: var(--s3);
    padding: var(--s2) var(--s3);
    color: var(--risk);
    border: 1px solid var(--risk-dim, var(--line));
    border-radius: var(--radius);
}

.dep-wait {
    display: flex;
    align-items: baseline;
    gap: var(--s2);
    margin-top: var(--s3);
}

.dep-dot {
    flex: 0 0 auto;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--gain);
    animation: dep-pulse 1800ms ease-in-out infinite;
}

@keyframes dep-pulse {
    0%, 100% { opacity: .25; }
    50%      { opacity: 1; }
}

.dep-done { color: var(--gain); }

/* Выход из «набрал не ту сумму». Ссылкой, а не клавишей: это не действие с
   деньгами, счёт у провайдера остаётся живым и оплаченный всё равно зачислится. */
.dep-again {
    display: block;
    width: 100%;
    margin-top: var(--s3);
    padding: var(--s2);
    background: none;
    border: none;
    color: var(--txt-3);
    font-family: var(--font-ui);
    font-size: var(--fs-sm);
    text-decoration: underline;
    cursor: pointer;
}

@media (prefers-reduced-motion: reduce) {
    .dep-dot { animation: none; opacity: 1; }
}

/* ── клавиша пополнения ───────────────────────────────────────────────────
   Стоит НА МЕСТЕ ряда покупки, а не под ним. Причина не в красоте: экран игры
   не листается, всю свободную высоту забирает кольцо, и лишняя строка в 52 px
   отнимается именно у него. Клавиша растягивается на все три колонки сетки,
   а сами клавиши покупки в этот момент погашены — высота ряда не меняется. */
.btn-topup {
    grid-column: 1 / -1;
    /* В ряду докупа сетки нет, там флекс: занять всю строку одному ребёнку. */
    flex: 1 1 100%;
}

/* Единственная клавиша, которая уводит с экрана игры, — значит она обязана
   отличаться от денежных клавиш рядом. Янтарь тут не годится: он в проекте
   означает «это про тебя». Берём цвет дохода — пополнение и есть приход. */
.btn-topup .btn-n {
    color: var(--gain);
    letter-spacing: var(--ls-cap);
}

.btn-topup .btn-sub {
    color: var(--txt-3);
}

/* Подсветка раздела пополнения, когда игрока привели сюда с экрана игры.
   Полторы секунды и никакого движения — это указатель «ты пришёл вот сюда»,
   а не украшение. */
.dep-look {
    animation: dep-look 1.6s ease-out 1;
}

@keyframes dep-look {
    0%, 100% { box-shadow: none; }
    20%, 60% { box-shadow: 0 0 0 2px var(--gain-dim); }
}

@media (prefers-reduced-motion: reduce) {
    .dep-look { animation: none; }
}

/* ── шаги мастера пополнения: 1 СЕТЬ · 2 СУММА · 3 ОПЛАТА ─────────────────
   Полоска отвечает на один вопрос — «где я и сколько ещё». Пройденные шаги
   гаснут, текущий держит акцент; линий-стрелок нет: три слова читаются сами. */
.dep-steps {
    display: flex;
    align-items: center;
    gap: var(--s3);
    margin-top: var(--s2);
}

.dep-step {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: var(--font-ui);
    font-size: var(--fs-sm);
    letter-spacing: .08em;
    color: var(--txt-3);
}

.dep-step-n {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    border: 1px solid var(--line-hi);
    font-style: normal;
    font-size: 11px;
    color: var(--txt-3);
}

.dep-step.is-on { color: var(--txt); }

.dep-step.is-on .dep-step-n {
    border-color: var(--me);
    color: var(--me);
}

.dep-step.is-done .dep-step-n {
    /* Заливка цветом ПРИГЛУШЁННОГО ТЕКСТА, а не линий: у --line-hi с цифрой
       цвета фона выходило 1.5:1 в светлой теме — клякса вместо «пройдено».
       --txt-3 в обеих темах держит к фону 4.5+, значит и фонова́я цифра на нём
       читается так же. */
    background: var(--txt-3);
    border-color: transparent;
    color: var(--bg);
}

/* Выбранная шагом раньше сеть: одна строка с дорогой назад. Кнопка на всю
   ширину — по ней попадают пальцем, а не курсором. */
.dep-chosen {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s2);
    width: 100%;
    margin-top: var(--s2);
    padding: 10px 12px;
    background: var(--bg-elevated);
    border: 1px solid var(--line-hi);
    border-radius: var(--radius);
    cursor: pointer;
    font-family: var(--font-ui);
}

.dep-chosen-t {
    font-size: var(--fs-sm);
    color: var(--txt-2);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dep-chosen-x {
    flex: 0 0 auto;
    font-size: var(--fs-sm);
    letter-spacing: .08em;
    color: var(--accent-money);
}

/* ── кошелёк ──────────────────────────────────────────────────────────────
   Счёт крупно и две клавиши; ниже билеты и подарки строками той же сетки,
   что и остальные экраны: подпись слева, значение справа. */
.wal-balance {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-top: var(--s2);
}

.wal-balance-k { color: var(--txt-3); }

.wal-balance-v {
    font-size: 26px;
    color: var(--gain);
    line-height: 1.1;
}

.wal-tk,
.wal-gift,
.wal-hist {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s2);
    padding: 10px 0;
    border-bottom: 1px solid var(--line);
}

.wal-tk-who,
.wal-gift-who,
.wal-hist-who {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.wal-tk-room,
.wal-gift-t,
.wal-hist-t {
    font-size: var(--fs-sm);
    color: var(--txt-2);
}

.wal-tk-sub,
.wal-gift-sub,
.wal-hist-sub,
.wal-gift-next {
    font-size: var(--fs-sm);
    color: var(--txt-3);
}

.wal-tk-n {
    flex: 0 0 auto;
    font-size: var(--fs-md);
    color: var(--txt);
}

/* «Сгорит через 14 ч» — предупреждение, а не украшение. */
.wal-gift-burn {
    font-size: var(--fs-sm);
    color: var(--risk-dim, var(--txt-3));
}

.wal-claim {
    flex: 0 0 auto;
    height: 38px;
    padding: 0 var(--s4);
    color: var(--gain);
}

.wal-gift-next { flex: 0 0 auto; text-align: right; }

.wal-hist-st {
    flex: 0 0 auto;
    font-size: var(--fs-sm);
    color: var(--txt-3);
    text-align: right;
}

.wal-hist-st.is-ok { color: var(--gain); font-size: var(--fs-sm); }

/* ── прослушивание звуков (раздел владельца в профиле) ───────────────────── */
.lsn-row {
    display: flex;
    align-items: center;
    gap: var(--s3);
    padding: 8px 0;
    border-bottom: 1px solid var(--line);
}

.lsn-play {
    flex: 0 0 auto;
    width: 34px;
    height: 34px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-elevated);
    border: 1px solid var(--line-hi);
    border-radius: 50%;
    color: var(--txt);
    font-size: 13px;
    line-height: 1;
    cursor: pointer;
}

/* ── тумблер «платить подарочными» ────────────────────────────────────────
   Маленький переключатель в строке состояния: дорожка, ползунок и остаток
   билетов. Включён — ряд покупки торгует подарочными билетами и подсвечен
   своим цветом. */
.paytgl {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 0;
    background: none;
    border: 0;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    flex: 0 0 auto;
}

.paytgl-track {
    position: relative;
    width: 24px;
    height: 13px;
    border-radius: 7px;
    background: var(--bg-elevated);
    border: 1px solid var(--line-hi);
    transition: background var(--t-fast) var(--ease-out);
}

.paytgl-knob {
    position: absolute;
    top: 1px;
    left: 1px;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--txt-3);
    transition: transform var(--t-fast) var(--ease-out), background var(--t-fast) var(--ease-out);
}

.paytgl[aria-checked="true"] .paytgl-track {
    background: var(--me-dim);
    border-color: var(--me-dim);
}

.paytgl[aria-checked="true"] .paytgl-knob {
    transform: translateX(11px);
    background: var(--bg);
}

.paytgl-t {
    font-family: var(--font-ui);
    font-size: var(--fs-sm);
    letter-spacing: .06em;
    color: var(--txt-3);
}

.paytgl[aria-checked="true"] .paytgl-t { color: var(--me); }

.paytgl-n {
    font-size: var(--fs-sm);
    color: var(--txt-3);
}

/* Ряд покупки в бонусном режиме помечен своим цветом: видно, ЧЕМ платишь. */
/* Ряд покупки в бонусном режиме больше не красится: в этом режиме его нет
   вовсе — место занимает одна золотая клавиша подарочного билета (.btn-bonus). */

/* ── док докупа в тишине между волнами ────────────────────────────────────
   Панель стоит всю фазу выбывания; когда окно закрыто, клавиши тускнеют и не
   принимают нажатий. Прозрачность, а не подмена цветов: значения остаются
   читаемыми — это пауза, а не поломка. */
.shelf-idle .buy-duel .btn {
    opacity: .42;
    pointer-events: none;
}


.lsn-name {
    font-family: var(--font-ui);
    font-size: var(--fs-sm);
    color: var(--txt-2);
    min-width: 0;
}

/* ── баланс в шапке стал кнопкой ──────────────────────────────────────────
   Вид не меняется: это по-прежнему сумма, а не клавиша. Меняется только то,
   что на неё можно нажать — свободного места под отдельную кнопку «Пополнить»
   в шапке нет физически (30 px на ширине 320 в полноэкранном режиме). */
button.hdr-balance {
    appearance: none;
    background: none;
    border: 0;
    padding: 0;
    margin: 0;
    font: inherit;
    color: inherit;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

/* Плюс висит в потоке всегда, но занимает место только когда виден: иначе его
   появление сдвигало бы номер раунда влево на глазах у игрока. */
.hdr-plus {
    font-size: var(--fs-sm);
    line-height: 1;
    color: var(--gain);
    width: 0;
    overflow: hidden;
    opacity: 0;
    transition: opacity var(--t-fast) linear;
}

.hdr-balance.is-empty .hdr-plus {
    width: auto;
    opacity: 1;
}

/* Пустой счёт — сумма гаснет до служебного цвета: это не деньги, это ноль. */
.hdr-balance.is-empty #hdr-balance {
    color: var(--txt-3);
}

/* ── полка выехала: ряд покупки уходит из-под неё ─────────────────────────
   Полка прибита к низу окна, ряд клавиш покупки остаётся в потоке ровно там же,
   и на телефоне клавиши +1/+5/+25 просвечивали из-под полки докупа. В окне
   длиной четыре секунды это означало прицел между двумя рядами клавиш.

   visibility, а не display: место ряда обязано остаться занятым. Высота приборов
   сложена вручную (--chrome-h), и от неё считается размер кольца — выпади ряд из
   потока, кольцо выросло бы на 52 px и схлопнулось обратно на каждой волне. */
body.on-shelf .buy:not(.buy-duel),
body.on-shelf .chance-bar {
    visibility: hidden;
}

/* ── длинные подписи приборной строки ─────────────────────────────────────
   «ОСТАЛОСЬ» и «ВЫБЫЛО» длиннее, чем «БИЛЕТОВ» и «ИГРОКОВ», и на ширине 320
   первое из них обрезалось многоточием (замер: tools/visual/statfit.mjs).
   Обрезанная подпись хуже короткой: «ОСТАЛОС…» читается как опечатка.

   Разрядка убирается первой — на восьми знаках она стоит почти целого знака, а
   в верхнем регистре её роль чисто декоративная. Кегль уменьшается следом. */
.cap.cap-tight {
    letter-spacing: 0;
    font-size: calc(var(--fs-cap) * 0.92);
}

/* Подпись полки докупа. Раньше она стояла в шапке вместе с отсчётом окна;
   отсчёт ушёл в центр кольца, и от шапки осталось одно слово — оно и живёт
   отдельной строкой над рядом клавиш. */
.duel-cap {
    display: block;
    margin-bottom: var(--s2);
    color: var(--txt-3);
}

/* Бонусный билет: тот же слот, что и пополнение, но другой смысл. Пополнение —
   это «заплати»; бонусный билет — «у тебя уже есть чем сыграть». Поэтому имя
   янтарное (это про тебя), а не цвета дохода. */
/* ── подарочный билет: одна золотая клавиша вместо ряда ───────────────────
   Появляется, когда игрок повернул тумблер «БОНУСНЫЕ» или когда платить нечем,
   а билет есть. Ряд из трёх объёмов в этот момент не нужен вовсе: подарочный
   билет ставится РОВНО ОДИН на раунд (правило владельца), и три клавиши, две из
   которых всегда погашены, читались бы как поломка.

   Клавиша нарочно не похожа на денежные: своё тело, своя рамка, свой свет.
   Это единственное золото в цоколе — и единственное действие, которое ничего не
   стоит. */
.btn-bonus {
    position: relative;
    overflow: hidden;
    background-color: var(--bonus-mid);
    background-image: linear-gradient(180deg,
        var(--bonus-top) 0%, var(--bonus-mid) 54%, var(--bonus-bot) 100%);
    border-color: var(--bonus-line);
    box-shadow: var(--shadow-key), 0 0 18px var(--bonus-glow);
}

.btn-bonus .btn-n,
.btn-bonus .btn-sub {
    color: var(--bonus-ink);
    position: relative;   /* над бликом */
    z-index: 1;
}

.btn-bonus .btn-n {
    letter-spacing: var(--ls-cap);
}

.btn-bonus .btn-sub {
    opacity: .82;
}

/* Блик: узкая полоса света, проходящая по клавише раз в четыре секунды. Он и
   делает её «дорогой» — но двигается ТОЛЬКО он, transform по одной оси, без
   единой перерисовки раскладки. Клавиша погашена — блика нет: это обещание
   действия, а погашенная клавиша ничего не обещает. */
.bonus-shine {
    position: absolute;
    top: 0;
    bottom: 0;
    left: -40%;
    width: 34%;
    pointer-events: none;
    background: linear-gradient(105deg, transparent 0%, var(--bonus-sheen) 50%, transparent 100%);
    transform: translateX(0);
    animation: bonus-shine 4s ease-in-out infinite;
}

.btn-bonus:disabled {
    box-shadow: var(--shadow-key-off);
    opacity: .55;
}

.btn-bonus:disabled .bonus-shine {
    display: none;
}

@keyframes bonus-shine {
    0%   { transform: translateX(0); }
    55%  { transform: translateX(420%); }
    100% { transform: translateX(420%); }
}

@media (prefers-reduced-motion: reduce) {
    .bonus-shine { animation: none; opacity: .35; transform: translateX(180%); }
}

/* ── итог раунда ──────────────────────────────────────────────────────────
   Карточка стоит под кольцом, где и так стоит взгляд, а не в подвале. Высоты у
   кольца она не отнимает: показывается только между развязкой и следующим
   раундом и занимает место строки состояния и микрошкалы, которые в этот момент
   не значат ничего (класс on-settled ниже). */
.finale {
    margin-top: var(--s3);
}

/* Три места в одну строку: каждая лишняя строка внизу отнимается у кольца. */
.fin-row {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--s2);
    min-height: 22px;
}

.fin-cell {
    display: flex;
    align-items: baseline;
    gap: var(--s2);
    min-width: 0;
}

.fin-place {
    flex: 0 0 auto;
    font-size: var(--fs-sm);
    color: var(--txt-3);
}

.fin-sum {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--fs-md);
    color: var(--txt);
}

/* Первое место — золотом. Единственное место в приборе, где цвет означает «взял
   больше всех», и он не спорит с янтарём: янтарь значит «это про тебя». */
.fin-first .fin-sum,
.fin-first .fin-place {
    color: var(--gold, #E8C46A);
}

.fin-mine .fin-place,
.fin-mine .fin-sum {
    color: var(--me);
}

/* Честная строка: вложено — вернулось — разница. Плюс цветом дохода, минус
   служебным серым, а не красным: проигранный раунд — это не авария. */
.fin-note {
    margin: var(--s2) 0 0;
    font-size: var(--fs-sm);
    color: var(--txt-3);
    line-height: 1.4;
}

.fin-note.fin-plus {
    color: var(--gain);
}

/* Итог ставки на финал — отдельной строкой под строкой честности. Это ДРУГИЕ
   деньги: билеты это участие, ставка это угадывание чужого исхода. Поэтому
   строка своя, а не приписка к первой, и отбита тонкой чертой сверху. */
.fin-note.fin-bet {
    margin-top: var(--s1);
    padding-top: var(--s1);
    border-top: 1px solid var(--line);
}

/* Пока виден итог, строка состояния и микрошкала уходят: их высота — ровно то
   место, на котором стоит карточка. */
body.on-settled .status,
body.on-settled .chance-bar {
    display: none;
}

/* ── куда ушёл пот ────────────────────────────────────────────────────────
   Восемь пикселей на весь ответ. Ширины долями, а не пикселями: карточка стоит
   в резиновой колонке, и любой пиксельный расчёт разъехался бы на узком экране. */
.fin-bar {
    display: flex;
    gap: 2px;
    margin-top: var(--s2);
    height: 8px;
}

.fin-seg {
    min-width: 3px;
    height: 100%;
    padding: 0;
    border: none;
    border-radius: 2px;
    background: var(--line-hi, #2A3A33);
    cursor: pointer;
}

.fin-seg-1 { background: var(--gold, #E8C46A); }
.fin-seg-2 { background: var(--txt-2, #9FB4AA); }
.fin-seg-3 { background: var(--txt-3); }
/* Комиссия — самой тусклой из долей: она в полосе есть, но она не приз.
   Не --line: на светлой теме та почти сливается с фоном, и доля, которую не
   видно, честнее не показывать вовсе, чем показывать невидимой. */
.fin-seg-rake { background: var(--line-hi); }
/* Своя доля обведена янтарём: тот же язык, что и «своё» везде в приборе. */
.fin-seg-mine { box-shadow: inset 0 0 0 1px var(--me); }

/* Подсказка лежит ПОВЕРХ карточки: появление и уход не двигают ни пикселя,
   а карточка стоит в самом тесном месте экрана. */
.fin-tip {
    position: absolute;
    bottom: 100%;
    left: 50%;
    transform: translate(-50%, -6px);
    z-index: 5;
    padding: var(--s1) var(--s2);
    background: var(--surf-2-top);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    color: var(--txt);
    font-family: var(--font-ui);
    font-size: var(--fs-sm);
    white-space: nowrap;
    pointer-events: none;
}

.finale { position: relative; }

/* Полёт выигрыша в баланс и конфетти победителю — украшение поверх уже
   начисленных денег. Оба гасятся целиком при просьбе не двигать интерфейс. */
.fin-fly {
    position: fixed;
    z-index: 60;
    transform: translate(-50%, -50%);
    color: var(--gain);
    font-size: var(--fs-md);
    pointer-events: none;
    animation: fin-fly 700ms cubic-bezier(.4, 0, .2, 1) 1 forwards;
}

@keyframes fin-fly {
    0%   { opacity: 0; transform: translate(-50%, -50%) scale(.9); }
    18%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
    100% { opacity: 0; transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy))) scale(.7); }
}

.fin-confetti {
    position: fixed;
    inset: 0;
    z-index: 55;
    overflow: hidden;
    pointer-events: none;
}

/* Три сорта частиц. Одна форма на всё поле читается как дождь; лента, монета
   и искра вместе — как салют. Длительность у каждой своя (--t): одинаковая
   выдаёт машину, разная — горсть. */
.fin-flake {
    position: absolute;
    top: -14px;
    left: var(--x);
    width: 6px;
    height: 11px;
    border-radius: 1px;
    background: var(--gold, #E8C46A);
    animation: fin-flake var(--t, 1600ms) cubic-bezier(.3, .6, .5, 1) var(--d) 1 forwards;
}

/* Монета: кружок с бликом, крутится вокруг вертикали — то ребром, то плашмя. */
.fin-flake.coin {
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: radial-gradient(60% 60% at 35% 30%, #FFF3CE 0%, var(--gold, #E8C46A) 55%, #A9761B 100%);
    animation-name: fin-coin;
}

/* Искра: тонкая длинная черта, летит быстрее и гаснет раньше. */
.fin-flake.spark {
    width: 2px;
    height: 16px;
    border-radius: 2px;
    background: linear-gradient(to bottom, #FFF6DA, rgba(255, 214, 120, 0));
    animation-duration: calc(var(--t, 1600ms) * .72);
}

.fin-flake:nth-child(5n)   { background: var(--me); }
.fin-flake:nth-child(7n+1) { background: var(--gain); }
.fin-flake.coin:nth-child(n) { background: radial-gradient(60% 60% at 35% 30%, #FFF3CE 0%, var(--gold, #E8C46A) 55%, #A9761B 100%); }

@keyframes fin-flake {
    0%   { opacity: 0; transform: translate(0, 0) rotate(0); }
    10%  { opacity: 1; }
    100% { opacity: 0; transform: translate(var(--dx), 96vh) rotate(var(--r)); }
}

/* Монета переворачивается: scaleX от 1 до −1 и обратно — дешёвый способ
   показать вращение плоского кружка без трёхмерных преобразований. */
@keyframes fin-coin {
    0%   { opacity: 0; transform: translate(0, 0) scaleX(1); }
    10%  { opacity: 1; }
    35%  { transform: translate(calc(var(--dx) * .35), 34vh) scaleX(-.35); }
    68%  { transform: translate(calc(var(--dx) * .7), 66vh) scaleX(.8); }
    100% { opacity: 0; transform: translate(var(--dx), 96vh) scaleX(-1); }
}

/* Удар джекпота: сцена коротко «подпрыгивает». Анимация висит на .scene, а не
   на #stage: на самом кольце transform ставит рендерер (тряска волны), и два
   хозяина одного свойства сталкивались бы ровно в момент победы. */
body.win-blast .scene { animation: win-pop 620ms var(--ease-pop, cubic-bezier(.2, .9, .3, 1.2)) 1; }

@keyframes win-pop {
    0%   { transform: scale(1); }
    18%  { transform: scale(1.035); }
    52%  { transform: scale(.994); }
    100% { transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
    .fin-fly,
    .fin-confetti { display: none; }
    body.win-blast .scene { animation: none; }
}

/* ── дуэль: экран гаснет вокруг кольца ────────────────────────────────────
   Всё, кроме сцены и полки докупа, уходит в тень. Это не украшение: в дуэли на
   экране остаётся одно решение и четыре секунды на него, и любая деталь, за
   которую цепляется глаз, — это украденная секунда.

   Гасим прозрачностью, а не подменой цветов: цвет — это смысл (янтарь значит
   «это про тебя»), и перекрашивать его нельзя. Переход медленнее обычного:
   резкое затемнение читается как сбой связи. */
body.on-duel .hdr,
body.on-duel .rooms,
body.on-duel .stats,
body.on-duel .status,
body.on-duel .col-right {
    opacity: .35;
    transition: opacity 320ms ease-out;
}

body.on-duel .scene {
    transition: filter 320ms ease-out;
    filter: saturate(1.08);
}

/* ── финал двоих ──────────────────────────────────────────────────────────
   Класс ставит js/ui/tension.js по составу круга, а не по открытой полке: зритель
   обязан видеть финал так же, как участник. Экран сжимается виньеткой и мерно
   пульсирует — в такт сердцу, которое в этот момент звучит (audio.heartbeat).
   Ни один пиксель не перерисовывается: работает только прозрачность накладки. */
body.final-heat::after {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 40;
    pointer-events: none;
    background: radial-gradient(78% 62% at 50% 44%, transparent 40%, rgba(120, 0, 12, .30) 78%, rgba(60, 0, 6, .55) 100%);
    animation: final-vignette 2200ms ease-in-out infinite;
}

@keyframes final-vignette {
    0%   { opacity: .62; }
    46%  { opacity: 1; }
    100% { opacity: .62; }
}

[data-theme="light"] body.final-heat::after,
body[data-theme="light"].final-heat::after {
    background: radial-gradient(78% 62% at 50% 44%, transparent 44%, rgba(201, 33, 75, .12) 80%, rgba(201, 33, 75, .22) 100%);
}

@media (prefers-reduced-motion: reduce) {
    body.final-heat::after { animation: none; opacity: .8; }
    body.on-duel .hdr,
    body.on-duel .rooms,
    body.on-duel .stats,
    body.on-duel .status,
    body.on-duel .col-right,
    body.on-duel .scene {
        transition: none;
    }
}

/* Цена билета на месте слова «ПОТ». Мелко и служебным цветом: это признак стола,
   а не его показание — читают её один раз при выборе, дальше она не меняется.
   Слово «ПОТ» ушло вместе с двадцатью шестью пикселями из восьмидесяти восьми:
   пот на вкладке стола ни с чем не спутать. */
.room-price {
    /* Уступает место поту, а не выталкивает его: цену читают один раз при
       выборе стола, а пот — каждую секунду. Она же обрезается первой. */
    flex: 0 1 auto;
    min-width: 0;
    font-size: var(--fs-cap);
    color: var(--txt-3);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ── лента событий ────────────────────────────────────────────────────────
   Накладка внутри сцены, под кольцом. Не в потоке: экран игры не листается, и
   любая строка в потоке отнимается у кольца. А внутри сцены под кольцом есть
   от 100 до 131 пикселя ничьей пустоты (замер tools/visual/gaps.mjs) — ровно
   то, что владелец называл «пустой зоной между кольцом и статистикой».
   С появлением экипажа (полосы медальонов, .crew) нижняя пустота отдана ему,
   а лента ушла в такую же по размеру верхнюю: кольцо в сцене стоит по центру,
   и над ним ровно столько же места, сколько под ним.

   Событий не читает голосовой доступ и не ловит палец: это фон, который
   сообщает, что стол живой, а не список, с которым работают. */
.feed {
    position: absolute;
    left: 50%;
    top: 0;
    transform: translateX(-50%);
    width: min(100%, 320px);
    pointer-events: none;
    text-align: center;
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    color: var(--txt-3);
}

.feed-row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 5px;
    height: 16px;
    line-height: 16px;
    overflow: hidden;
    white-space: nowrap;
    /* Старое тусклее нового: лента читается сверху вниз без единой подписи. */
    opacity: .55;
}

/* Значок вида события. Один знак: строка высотой шестнадцать пикселей, и всё,
   что сложнее знака, в ней не читается.

   Колонка ФИКСИРОВАННОЙ ширины и одна на все виды. Восемь пикселей с
   flex-basis: auto хватало плюсу и минусу, но не часам: строка «время
   продлено» уезжала относительно соседних, и лента переставала читаться
   колонкой. Часы к тому же рисуются цветным эмодзи там, где система решит, —
   font-variant-emoji возвращает их к обычному знаку. */
.feed-icon {
    flex: 0 0 12px;
    width: 12px;
    text-align: center;
    line-height: 16px;
    font-variant-emoji: text;
    color: var(--txt-3);
}

/* Часы крупнее плюса при том же кегле — сбавляем, чтобы вес знаков совпал. */
.feed-row.k-extend .feed-icon { font-size: 11px; }

.feed-row.k-buy .feed-icon { color: var(--gain); }
.feed-row.k-extend .feed-icon { color: var(--ot); }

/* Точка в цвет сегмента — единственный способ понять, КТО это, пока у игрока
   нет имени. Тот же цвет, что на кольце: связь между строкой и сегментом
   держится только на нём. */
.feed-dot {
    flex: 0 0 auto;
    width: 6px;
    height: 6px;
    border-radius: 50%;
}

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

/* Своё событие — янтарём, как и всё своё в этом приборе. */
.feed-row.is-me .feed-text { color: var(--me); }

.feed-row:first-child { opacity: 1; }
.feed-row:last-child { opacity: .3; }

/* Свежая строка въезжает СВЕРХУ, сдвигая остальные вниз: подмена текста на
   месте не читается как событие, а въезд снизу спорил бы с порядком «новое
   сверху». */
/* Два имени под одну анимацию: строка ленты меняется, а класс у верхней строки
   оставался тот же — className не переприсваивался, и со ВТОРОЙ волны новая
   запись появлялась без движения. Лента чередует is-new и is-new2, имя меняется,
   анимация играет заново. */
.feed-row.is-new,
.feed-row.is-new2 {
    animation: feed-in 260ms var(--ease-pop, ease-out) 1;
}

@keyframes feed-in {
    from { opacity: 0; transform: translateY(-6px); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .feed-row.is-new,
    .feed-row.is-new2 { animation: none; }
}

/* Прогноз шанса на клавише докупа: «58→64%».
   Третья строка внутри той же клавиши, а не новая строка на экране: полка
   выезжает поверх содержимого, и каждый лишний пиксель её высоты закрывает
   приборную строку под ней. Кегль мельче цены — цена говорит, сколько снимут,
   а это зачем; читают их в таком порядке. */
.btn-odds {
    font-size: var(--fs-cap);
    line-height: 1.1;
    letter-spacing: 0;
    color: var(--me);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}

/* На выключенной клавише прогноз гаснет вместе с ней: обещать рост шанса там,
   где нажать нельзя, — это обещание без возможности. */
.btn:disabled .btn-odds { color: var(--txt-off); }

/* Три строки в клавише высотой 52 px помещаются впритык: замер (duelfit.mjs)
   показал перелив в один пиксель на всех размерах. Забираем его у межстрочных
   зазоров, а не у высоты клавиши — высоту полки трогать нельзя, она выезжает
   поверх приборной строки. */
.buy-duel .btn {
    gap: 0;
}

.buy-duel .btn-n { line-height: 1.05; }
.buy-duel .btn-sub { line-height: 1.1; }
.buy-duel .btn-odds { line-height: 1; }

/* ── профиль: карточки ────────────────────────────────────────────────────
   Плоский список пар «подпись — значение» читался как выписка из базы. Карточки
   разделяют его на четыре вопроса: кто я, какой у меня уровень, как я играю,
   что в сезоне. Разделители — те же тонкие пазы, что и во всём приборе. */
#prof-card,
#prof-level,
#prof-stats,
#prof-season {
    margin-top: var(--s4);
    padding: var(--s3);
    background: var(--surf-1-mid);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: inset 0 1px 0 var(--edge-top);
}

/* Шапка карточки счёта: аватар и главное число рядом, а не этажами. */
.acc-head {
    display: flex;
    align-items: center;
    gap: var(--s3);
}

/* Аватар из идентификатора: цвет закреплён за человеком навсегда. Цвет сегмента
   на кольце раздаётся заново каждый раунд, поэтому обещать совпадение с ним
   нельзя — но набор оттенков тот же, и аватар выглядит своим в этой игре. */
.acc-ava {
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font-ui);
    font-size: 20px;
    font-weight: 600;
    /* Буква цветом ФОНА своей темы, а не белым.

       Белая была верным решением при прежней палитре: там чипы в обеих темах
       были средней светлоты. Новый набор игроков ровный по яркости — в тёмной
       теме чипы светлые, в светлой тёмные, — и белая буква на светлых чипах
       тёмной темы давала 2.6:1 при норме 3 (четыре чипа из восьми). Фон своей
       темы даёт 5.4–7.2 в тёмной и 3.3–4.5 в светлой: он и есть тот цвет,
       который в каждой теме противоположен чипу. */
    color: var(--bg);
    text-shadow: var(--shadow-text);
}

.acc-letter { line-height: 1; }

.acc-who { min-width: 0; }

.acc-name {
    font-size: var(--fs-md);
    color: var(--txt-2);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.acc-sum {
    font-size: var(--fs-lg);
    color: var(--gain);
    line-height: 1.1;
}

/* Две клавиши в ряд. «Вывести» выключена, и это честнее её отсутствия: игрок
   обязан видеть, что вывод предусмотрен и что прямо сейчас его нет. */
.acc-buttons {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--s2);
    margin-top: var(--s3);
}

.acc-buttons .btn { height: 46px; }
.acc-top .btn-n, .acc-top { color: var(--gain); }

/* ── уровень: круговой прогресс вокруг числа ─────────────────────────────── */
.lvl-head {
    display: flex;
    align-items: center;
    gap: var(--s3);
    margin-top: var(--s2);
}

.lvl-круг {
    position: relative;
    flex: 0 0 auto;
    width: 64px;
    height: 64px;
}

.lvl-ring {
    width: 64px;
    height: 64px;
    /* Ноль сверху, а не справа: круг читается как циферблат. */
    transform: rotate(-90deg);
}

.lvl-ring-track,
.lvl-ring-fill {
    fill: none;
    stroke-width: 4;
}

.lvl-ring-track { stroke: var(--ink-2); }

.lvl-ring-fill {
    stroke: var(--me);
    stroke-linecap: round;
    transition: stroke-dashoffset var(--t-mid) ease-out;
}

.lvl-num {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--fs-lg);
    color: var(--txt);
}

.lvl-рядом { min-width: 0; }
.lvl-of { font-size: var(--fs-sm); color: var(--txt-3); }

.lvl-next {
    margin-top: 2px;
    font-size: var(--fs-sm);
    color: var(--txt-2);
    line-height: 1.35;
}

/* Раскрывашка: объяснение нужное, но в развёрнутом виде это три строки цифр,
   забивающих карточку, ради которой человек сюда и зашёл. */
.prof-fold { margin-top: var(--s3); }

.prof-fold-head {
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    color: var(--me);
    cursor: pointer;
    list-style: none;
}

.prof-fold-head::-webkit-details-marker { display: none; }
.prof-fold-head::after { content: ' ▾'; color: var(--txt-3); }
.prof-fold[open] .prof-fold-head::after { content: ' ▴'; }

/* ── первая загрузка: скелетон ────────────────────────────────────────────
   Скелетон не рисуется своей разметкой, а накладывается на настоящую. Любой
   отдельный каркас пришлось бы держать одного размера с настоящими блоками
   руками, и он разъехался бы с ними на первой правке вёрстки — а тогда переход
   «скелетон → интерфейс» стал бы прыжком макета. Накладка совпадает по размерам
   по построению: это и есть те самые блоки. */
body.on-boot .rooms,
body.on-boot .stats,
body.on-boot .buy,
body.on-boot .chance-bar,
body.on-boot #hdr-balance,
body.on-boot #hdr-round {
    position: relative;
    /* Содержимое прячется, а место остаётся: числа приедут ровно сюда. */
    color: transparent;
    overflow: hidden;
}

body.on-boot .rooms::after,
body.on-boot .stats::after,
body.on-boot .buy::after,
body.on-boot .chance-bar::after,
body.on-boot #hdr-balance::after,
body.on-boot #hdr-round::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: var(--radius);
    background: linear-gradient(100deg,
        var(--surf-1-mid) 0%, var(--surf-2-top) 45%, var(--surf-1-mid) 90%);
    background-size: 240% 100%;
    animation: boot-shimmer 1400ms linear infinite;
}

/* Клавиши покупки под скелетоном не должны выглядеть нажимаемыми. */
body.on-boot .buy .btn { visibility: hidden; }

@keyframes boot-shimmer {
    from { background-position: 120% 0; }
    to   { background-position: -120% 0; }
}

/* Уход скелетона — растворением, а не переключением: подмена в один кадр
   читается как моргание экрана. */
body:not(.on-boot) .rooms,
body:not(.on-boot) .stats,
body:not(.on-boot) .buy {
    transition: color 240ms ease-out;
}

/* Долгое подключение. Кнопка не украшение: после шести отказов подписи клиент
   перестаёт переподключаться сам, молча и навсегда, и это единственный выход. */
.boot-late {
    position: absolute;
    left: 50%;
    bottom: 8px;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: var(--s2);
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    color: var(--txt-3);
    white-space: nowrap;
}

.boot-retry {
    appearance: none;
    padding: var(--s1) var(--s3);
    font: inherit;
    color: var(--me);
    background: none;
    border: 1px solid var(--me-dim);
    border-radius: var(--radius);
    cursor: pointer;
}

.boot-retry:disabled { color: var(--txt-off); border-color: var(--line); }

@media (prefers-reduced-motion: reduce) {
    body.on-boot .rooms::after,
    body.on-boot .stats::after,
    body.on-boot .buy::after,
    body.on-boot .chance-bar::after,
    body.on-boot #hdr-balance::after,
    body.on-boot #hdr-round::after { animation: none; }
}

/* ── ход паузы на клавише «СЛЕДУЮЩИЙ РАУНД» ───────────────────────────────
   Круг у правого края, а не полоса под кнопкой: полоса — это ещё одна строка
   внизу экрана, а внизу экрана каждая строка отнимается у кольца. Круг лежит
   абсолютно и не занимает в кнопке ни пикселя.

   Заливка — конический градиент, вырезанный маской в кольцо. Один параметр
   --p в градусах: обновление паузы стоит одной записи стиля, без разметки
   и без перерасчёта раскладки. */
.btn-next { position: relative; }

.next-ring {
    position: absolute;
    top: 50%;
    right: var(--s3);
    width: 18px;
    height: 18px;
    transform: translateY(-50%);
    border-radius: 50%;
    /* Дорожка — токеном, а не белым с прозрачностью: на светлой теме белое по
       светлому не видно вовсе, и кольцо превращалось в пустое место. */
    background: conic-gradient(var(--gain) var(--p, 0deg), var(--line-hi) 0);
    -webkit-mask: radial-gradient(circle, transparent 54%, #000 56%);
    mask: radial-gradient(circle, transparent 54%, #000 56%);
    pointer-events: none;
}

/* ── звук ─────────────────────────────────────────────────────────────────
   Подтверждение первого включения. Один раз за жизнь, две секунды: звук
   выключен по умолчанию, и без ответа на нажатие непонятно, случилось ли
   что-нибудь — первое событие со звуком может быть через минуту. */
.snd-tip {
    position: absolute;
    top: calc(100% + var(--s1));
    right: 0;
    z-index: 30;
    padding: var(--s1) var(--s2);
    background: var(--surf-2-top);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    color: var(--txt-2);
    font-family: var(--font-ui);
    font-size: var(--fs-sm);
    white-space: nowrap;
    box-shadow: var(--shadow-sheet);
    animation: snd-tip 2200ms ease-out 1 forwards;
}

/* При системной просьбе о покое анимации срезаются до миллисекунды, и заливка
   forwards мгновенно защёлкивала последний кадр — opacity: 0. Подсказка не
   показывалась вовсе, а показывается она ОДИН раз за жизнь: второго шанса нет.
   Здесь анимация не нужна совсем — узел и так снимается по таймеру. */
@media (prefers-reduced-motion: reduce) {
    .snd-tip { animation: none; opacity: 1; }
}

@keyframes snd-tip {
    0%   { opacity: 0; transform: translateY(-4px); }
    12%  { opacity: 1; transform: none; }
    82%  { opacity: 1; }
    100% { opacity: 0; }
}

.hdr-tools { position: relative; }

/* Выключённый звук — перечёркнутый динамик, включённый — с волнами. */
.icon-btn.muted .ico-on { display: none; }
.icon-btn:not(.muted) .ico-off { display: none; }

/* ── громкости ────────────────────────────────────────────────────────────
   Ползунок, а не кнопки «тише/громче»: громкость подбирают на слух, и путь
   «нажал — послушал — нажал» здесь длиннее самого действия. */
.vol-row {
    display: flex;
    align-items: center;
    gap: var(--s3);
    margin-top: var(--s2);
}

.vol-cap {
    flex: 0 0 84px;
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    color: var(--txt-3);
}

.vol {
    flex: 1 1 auto;
    min-width: 0;
    height: 24px;
    accent-color: var(--me);
    background: none;
}

.vol-val {
    flex: 0 0 32px;
    text-align: right;
    font-size: var(--fs-sm);
    color: var(--txt-2);
}

/* ─── строка списка (row.js): пополнения, лидеры, приглашённые ─────────────
   Одна фигура на три страницы: [метка] [аватар] [текст+подпись] [значение].
   Прежние .wal-hist/.ref-item остаются у старых блоков; новые списки собираются
   только из этих классов. */
.lrow {
    display: flex;
    align-items: center;
    gap: var(--s2);
    padding: 10px 0;
    border-bottom: 1px solid var(--line);
    min-height: var(--row-h);
}

/* Номер места: фиксированная колонка, чтобы имена начинались в одну линию. */
.lrow-rank {
    flex: 0 0 26px;
    font-size: var(--fs-sm);
    color: var(--txt-3);
    text-align: right;
}

.lrow-ava {
    flex: 0 0 auto;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    font-size: var(--fs-sm);
    /* Буква цветом фона темы — то же обоснование контраста, что у .acc-letter. */
    color: var(--bg);
}

.lrow-ava-t { line-height: 1; }

.lrow-who {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    flex: 1 1 auto;
}

.lrow-t {
    font-size: var(--fs-sm);
    color: var(--txt-2);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.lrow-s {
    font-size: var(--fs-sm);
    color: var(--txt-3);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.lrow-val {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 2px;
    flex: 0 0 auto;
}

.lrow-v {
    font-size: var(--fs-md);
    color: var(--txt);
}

.lrow-vs {
    font-size: var(--fs-sm);
    color: var(--txt-3);
}

/* ─── подарки: срочность и отклик забора ──────────────────────────────── */

/* Точка на иконке кошелька: «в кошельке лежит незабранный подарок».
   Акцентный цвет игрока — то самое золото «это ты»: подарок — твой. */
.nav-dot {
    position: absolute;
    top: 4px;
    right: 4px;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--accent-self);
    pointer-events: none;
}

/* Кнопки навигации и так позиционируются от себя? Нет — точке нужен якорь. */
.nav-i { position: relative; }

/* Меньше часа до сгорания: предупреждение краснеет и мягко пульсирует.
   Глобальное правило prefers-reduced-motion в tokens.css гасит пульс само. */
.wal-gift-burn-soon {
    color: var(--risk);
    animation: wal-burn-pulse 1.6s ease-in-out infinite;
}

@keyframes wal-burn-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: .55; }
}

/* Забранный билет улетает в блок «Билеты» — тот же жест, что «сумма к балансу»
   у финальной карточки (.fin-fly), но короче: путь внутри одного экрана. */
.wal-fly {
    position: fixed;
    z-index: 60;
    transform: translate(-50%, -50%);
    color: var(--accent-self);
    font-size: var(--fs-md);
    pointer-events: none;
    animation: wal-fly 900ms cubic-bezier(.4, 0, .2, 1) 1 forwards;
}

@keyframes wal-fly {
    0%   { opacity: 0; transform: translate(-50%, -50%) scale(.9); }
    15%  { opacity: 1; transform: translate(-50%, -50%) scale(1.05); }
    100% { opacity: 0; transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy))) scale(.8); }
}

/* Строка стола мигает принявшим цветом: счётчик именно здесь стал больше. */
.wal-tk-flash {
    animation: wal-tk-flash 900ms ease-out 1;
}

@keyframes wal-tk-flash {
    0%   { background: rgb(217 180 74 / .22); background: color-mix(in srgb, var(--accent-self) 22%, transparent); }
    100% { background: transparent; }
}

/* ─── история пополнений: жизненный цикл и детали ─────────────────────── */

/* Детали раскрываются ВНУТРИ строки, поэтому строке разрешён перенос. */
.lrow { flex-wrap: wrap; }

.lrow-details {
    flex: 1 1 100%;
    display: flex;
    flex-direction: column;
    gap: var(--s1);
    padding: var(--s2) 0 var(--s1) 0;
}

.lrow-det-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s3);
}

.lrow-det-k {
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    color: var(--txt-3);
    white-space: nowrap;
}

.lrow-det-v {
    font-size: var(--fs-sm);
    color: var(--txt-2);
    overflow: hidden;
    text-overflow: ellipsis;
}

.lrow-det-copy {
    align-self: flex-start;
    margin-top: var(--s1);
}

/* Заглохший счёт: погашен, пока его не позвали ссылкой «Показать старые». */
.lrow-dim .lrow-t,
.lrow-dim .lrow-v,
.lrow-dim .lrow-s,
.lrow-dim .lrow-vs { color: var(--txt-dim); }

.lrow-v.is-ok { color: var(--gain); }
.lrow-vs.is-dead { color: var(--txt-3); }

/* Зачисление встречает строку зелёной вспышкой — глаз находит, что изменилось. */
.lrow-flash {
    animation: lrow-flash 1200ms ease-out 1;
}

@keyframes lrow-flash {
    0%   { background: rgb(110 200 160 / .24); background: color-mix(in srgb, var(--gain) 24%, transparent); }
    100% { background: transparent; }
}

.hist-more {
    display: block;
    margin-top: var(--s2);
    padding: 6px 0;
    background: none;
    border: 0;
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    color: var(--txt-3);
    cursor: pointer;
}

.hist-more:active { color: var(--txt-2); }

/* Причина недоступности вывода — тихая строка под клавишами счёта. */
.acc-out-why {
    margin: var(--s1) 0 0;
    font-size: var(--fs-sm);
    color: var(--txt-3);
    text-align: right;
}

/* Заготовка на день, когда вывод откроется: кнопка загорается акцентом. */
.acc-out-on {
    border-color: var(--accent-money);
    color: var(--accent-money);
}

/* ─── мелочи кошелька: глаз на балансе и «В игру» у столов ────────────── */

.wal-balance-cap {
    display: flex;
    align-items: center;
    gap: var(--s2);
}

.wal-eye {
    background: none;
    border: 0;
    padding: 0 4px;
    font-size: 13px;
    line-height: 1;
    color: var(--txt-3);
    cursor: pointer;
}

.wal-eye:active { color: var(--txt-2); }

.wal-tk-go {
    flex: 0 0 auto;
    padding: 4px 8px;
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
}

/* ─── лидеры сезона ───────────────────────────────────────────────────── */

.ldr-back {
    display: block;
    margin: var(--s2) 0 0;
    padding: 6px 0;
    background: none;
    border: 0;
    color: var(--txt-3);
    cursor: pointer;
}

.ldr-back:active { color: var(--txt-2); }

/* Карточка сезона: название, сколько осталось, твоё место и очки.
   Закреплена сверху прокрутки: своё место видно на любой глубине списка. */
.ldr-card {
    position: sticky;
    top: 0;
    z-index: 2;
    margin-top: var(--s2);
    padding: var(--s3);
    background: var(--bg-elevated);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: inset 0 1px 0 var(--edge-top), 0 6px 16px rgb(0 0 0 / .14);
}

.ldr-card-top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s3);
}

.ldr-season {
    font-size: var(--fs-md);
    color: var(--txt-2);
}

.ldr-days {
    font-size: var(--fs-sm);
    color: var(--txt-3);
}

.ldr-card-me {
    display: flex;
    align-items: baseline;
    gap: var(--s3);
    margin-top: var(--s2);
}

.ldr-me-rank {
    font-size: 24px;
    color: var(--accent-self);
    line-height: 1.1;
}

.ldr-me-pts {
    font-size: var(--fs-md);
    color: var(--txt);
}

.ldr-me-none {
    font-size: var(--fs-sm);
    color: var(--txt-3);
}

.ldr-list { margin-top: var(--s3); }

/* Твоя строка выделена тем же смыслом, что и всё «твоё» в игре.
   Первый background — запас для WebView без color-mix (Chromium < 111). */
.lrow-me {
    background: rgb(217 180 74 / .10);
    background: color-mix(in srgb, var(--accent-self) 9%, transparent);
    box-shadow: inset 2px 0 0 var(--accent-self);
    padding-left: var(--s2);
}

/* Топ-3: золото, серебро, бронза у номера места. Цвета заданы явно — они
   значат металл, а не тему; в светлой теме металл темнее, иначе серебро на
   бумаге даёт контраст 2.4:1 и номер исчезает. */
.ldr-top1 .lrow-rank { color: var(--gold); font-weight: 700; }
.ldr-top2 .lrow-rank { color: #97A2AD; font-weight: 700; }
.ldr-top3 .lrow-rank { color: #B07A4F; font-weight: 700; }

[data-theme="light"] .ldr-top2 .lrow-rank { color: #5C6B77; }
[data-theme="light"] .ldr-top3 .lrow-rank { color: #8A5230; }

/* Прилипший низ: «а я где?» без прокрутки. Живёт в прокрутке экрана. */
.ldr-sticky {
    position: sticky;
    bottom: 0;
    display: flex;
    align-items: center;
    gap: var(--s2);
    padding: 10px var(--s2);
    background: var(--bg-elevated);
    border-top: 1px solid var(--line-hi);
    box-shadow: 0 -6px 16px rgb(0 0 0 / .18);
}

.ldr-sticky-t {
    flex: 1 1 auto;
    font-size: var(--fs-sm);
    color: var(--txt-2);
}

/* Жест обновления: короткая жизнь класса — лёгкий кивок. Кивает и экран
   ошибки (там карточки нет): жест обязан отвечать глазам всегда. */
.ldr-refreshing .ldr-card,
.ldr-refreshing .prof-note { animation: ldr-nod 500ms ease-out 1; }

@keyframes ldr-nod {
    0%   { transform: translateY(0); }
    35%  { transform: translateY(6px); }
    100% { transform: translateY(0); }
}

/* Скелетон списка: полосы на месте строк, шиммер как у загрузки приложения. */
.ldr-skel { margin-top: var(--s3); }

.ldr-skel-row {
    display: flex;
    align-items: center;
    gap: var(--s2);
    padding: 12px 0;
    border-bottom: 1px solid var(--line);
}

.ldr-skel-a,
.ldr-skel-b,
.ldr-skel-c {
    display: block;
    height: 12px;
    border-radius: 6px;
    background: var(--surf-2-mid, var(--surf-1-mid));
    animation: ldr-skel 1.1s ease-in-out infinite;
}

.ldr-skel-a { width: 28px; height: 28px; border-radius: 50%; flex: none; }
.ldr-skel-b { flex: 1 1 auto; }
.ldr-skel-c { width: 42px; flex: none; }

@keyframes ldr-skel {
    0%, 100% { opacity: .55; }
    50%      { opacity: 1; }
}

/* ─── сезонная карточка профиля ───────────────────────────────────────── */

/* Линия сезона: сколько уже прожито. Тонкая — это фон, а не показатель. */
.season-track {
    height: 3px;
    margin: var(--s1) 0 var(--s2);
    border-radius: 2px;
    background: var(--surf-2-mid, var(--surf-1-mid));
    overflow: hidden;
}

.season-fill {
    display: block;
    height: 100%;
    border-radius: 2px;
    background: var(--accent-self);
    opacity: .7;
}

/* Строка-ссылка: МЕСТО ведёт в таблицу лидеров. */
.prof-row-link { cursor: pointer; }
.prof-row-link:active .prof-v { color: var(--accent-self); }

/* ─── настройки: тема тремя положениями и mute каналов ────────────────── */

.set-theme {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s3);
    margin-top: var(--s2);
    min-height: var(--row-h);
}

.seg {
    display: flex;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    overflow: hidden;
}

.seg-b {
    padding: 6px 10px;
    background: none;
    border: 0;
    border-left: 1px solid var(--line);
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    color: var(--txt-3);
    cursor: pointer;
    white-space: nowrap;
}

.seg-b:first-child { border-left: 0; }

.seg-b.is-on {
    background: var(--surf-1-mid);
    color: var(--txt);
    box-shadow: inset 0 1px 0 var(--edge-top);
}

/* Динамик у ползунка: честная кнопка, а не декорация подписи. */
.vol-mute {
    flex: 0 0 auto;
    width: 24px;
    height: 24px;
    padding: 3px;
    background: none;
    border: 0;
    color: var(--txt-3);
    cursor: pointer;
}

.vol-mute svg {
    width: 100%;
    height: 100%;
    fill: currentColor;
    stroke: currentColor;
    stroke-width: 1.2;
}

.vol-mute .vm-wave { fill: none; }
.vol-mute .vm-cross { fill: none; display: none; }

/* Заглушенный ряд гаснет, у динамика волна сменяется крестом. */
.vol-row-off .vol-cap,
.vol-row-off .vol-val { color: var(--txt-dim); }
.vol-row-off .vol { opacity: .4; }
.vol-row-off .vol-mute .vm-wave { display: none; }
.vol-row-off .vol-mute .vm-cross { display: block; }

/* ─── друзья: оффер и отправка ────────────────────────────────────────── */

.ref-offer {
    margin-top: var(--s2);
    padding: var(--s3);
    background: var(--surf-1-mid);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: inset 0 1px 0 var(--edge-top);
}

.ref-offer-big {
    margin: 0;
    font-size: var(--fs-lg);
    line-height: 1.25;
    color: var(--txt);
}

.ref-offer-sub {
    margin: var(--s1) 0 0;
    font-size: var(--fs-sm);
    color: var(--txt-3);
}

.ref-offer-ex {
    margin: var(--s2) 0 0;
    font-size: var(--fs-sm);
    color: var(--accent-money);
}

/* Главная кнопка страницы — отправить приглашение. */
.ref-share {
    border-color: var(--accent-self);
    color: var(--accent-self);
}

/* Кто уже принёс денег — строка чуть ярче остальных. */
.ref-item-hi .lrow-v { color: var(--gain); }

@keyframes screen-in {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* Подпись выбранного таба: тает сама, в вёрстке места не занимает. */
.nav-tip {
    position: fixed;
    z-index: 70;
    transform: translateX(-50%);
    padding: 3px 8px;
    background: var(--bg-elevated);
    border: 1px solid var(--line-hi);
    border-radius: var(--radius);
    color: var(--txt-2);
    pointer-events: none;
    opacity: 0;
    transition: opacity var(--t-fast) ease-out;
}

.nav-tip.on { opacity: 1; }

/* Адрес целиком (буфер обмена закрыт): переносим, а не режем многоточием. */
.lrow-det-full {
    white-space: normal;
    word-break: break-all;
    text-overflow: clip;
}

/* Предупреждение в оффере: начисления закрыты барьером — причина стоит
   ПРЯМО у обещания, а не только под списком приглашённых. */
.ref-offer-warn {
    margin: var(--s2) 0 0;
    font-size: var(--fs-sm);
    color: var(--txt-3);
    border-top: 1px solid var(--line);
    padding-top: var(--s2);
}

/* ─── агентство на странице друзей ─────────────────────────────────────
   Отдельная карточка, а не строка в списке: это другой разговор — не «сколько
   мне капнуло», а «давай договоримся». */
.ref-agent {
    margin-top: var(--s3);
    padding: var(--s3);
    background: var(--surf-1-mid);
    border: 1px solid var(--line);
    border-radius: var(--radius);
}

.ref-agent-t {
    margin: 0 0 var(--s1);
    font-size: var(--fs-md);
    color: var(--txt);
}

.ref-agent-btn { margin-top: var(--s2); }

.ref-agent-err { color: var(--risk); }

.ref-field {
    display: block;
    margin-top: var(--s2);
}

.ref-field-t {
    display: block;
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    color: var(--txt-3);
    margin-bottom: 4px;
}

/* Поля ввода 16px не для красоты: Safari на iPhone приближает страницу к любому
   меньшему полю и обратно уже не отдаляет. */
.ref-input {
    width: 100%;
    min-height: 40px;
    padding: 8px 10px;
    font: inherit;
    font-size: 16px;
    color: var(--txt);
    background: var(--surf-2-mid);
    border: 1px solid var(--line);
    border-radius: var(--radius);
}

.ref-input:focus { outline: 1px solid var(--accent-self); }

.ref-cancel {
    display: block;
    margin: var(--s2) auto 0;
    padding: 6px 10px;
    background: none;
    border: 0;
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    color: var(--txt-3);
    cursor: pointer;
}

.ref-linkform {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2);
    margin-top: var(--s2);
}

.ref-linkform .ref-input { flex: 1 1 140px; width: auto; }

.ref-linkadd { flex: 0 0 auto; }

/* Строка ссылки: адрес длинный, поэтому переносится, а не режется. */
.ref-link-row .lrow-s {
    white-space: normal;
    word-break: break-all;
}

/* Строка ссылки агента: счётчик держится справа, а не съезжает под адрес.
   Переносится только сам адрес — ему это и нужно. */
.ref-link-row { flex-wrap: nowrap; align-items: flex-start; }
.ref-link-row .lrow-val { padding-top: 2px; }

/* ── вывод средств ────────────────────────────────────────────────────────
   Форма живёт на экране кошелька и намеренно скромнее пополнения: пополнение
   — это путь внутрь, его показывают; вывод — редкое действие, и кричать о нём
   незачем. Поля здесь обычные <input>, а не свой набор, как в пополнении:
   адрес кошелька набирают вставкой из буфера, и своя клавиатура тут только
   мешала бы. */
.wd-block {
    margin-top: var(--s4);
    display: flex;
    flex-direction: column;
    gap: var(--s2);
}

.wd-input {
    width: 100%;
    padding: var(--s3);
    font: inherit;
    font-size: var(--fs-md);
    color: var(--txt-1);
    background: var(--bg-2);
    border: 1px solid var(--line);
    border-radius: var(--r2);
}

.wd-input:focus {
    outline: none;
    border-color: var(--accent-money);
}

.wd-hint {
    margin: 0;
    font-size: var(--fs-sm);
    color: var(--txt-3);
    min-height: 1.2em;
}

.wd-hint.is-bad { color: var(--loss, #E06C5A); }

.wd-all,
.wd-cancel {
    align-self: flex-start;
    font-size: var(--fs-sm);
}

.wd-send { margin-top: var(--s2); }

/* Карточка заявки. Живая — с рамкой: она требует внимания, прошлые нет. */
.wd-card {
    margin-top: var(--s3);
    padding: var(--s3);
    background: var(--bg-2);
    border: 1px solid var(--line);
    border-radius: var(--r2);
}

.wd-card.is-live { border-color: var(--accent-money); }

.wd-card-h {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s2);
}

.wd-card-sum { font-size: var(--fs-lg); }

.wd-card-st {
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    text-transform: uppercase;
    color: var(--txt-3);
}

.wd-card-st.is-paid { color: var(--gain); }
.wd-card-st.is-rejected { color: var(--loss, #E06C5A); }
.wd-card-st.is-review { color: var(--gold, #E8C46A); }

/* Адрес — моноширинный и с переносом: он длинный и его сверяют глазами. */
.wd-card-addr {
    margin: var(--s2) 0 0;
    font-family: var(--font-num, monospace);
    font-size: var(--fs-sm);
    color: var(--txt-3);
    overflow-wrap: anywhere;
}

.wd-card-note {
    margin: var(--s2) 0 0;
    font-size: var(--fs-sm);
    color: var(--txt-2);
    overflow-wrap: anywhere;
}

.wd-history { margin-top: var(--s4); }

/* ── поддержка ────────────────────────────────────────────────────────────
   Диалог, а не форма. Реплики сторон разведены по краям — так видно, кто
   говорил, без подписи у каждой строки; служебные строки («обращение закрыто»)
   идут по центру и тише всех: это не реплика, а отметка о событии. */
.sup-list {
    display: flex;
    flex-direction: column;
    gap: var(--s2);
    margin-top: var(--s2);
}

.sup-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s2);
    width: 100%;
    padding: var(--s3);
    text-align: left;
    background: var(--bg-2);
    border: 1px solid var(--line);
    border-radius: var(--r2);
}

.sup-row.has-unread { border-color: var(--accent-money); }

.sup-row-l {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.sup-row-subj {
    color: var(--txt-1);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.sup-row-sub,
.sup-row-when {
    font-size: var(--fs-sm);
    color: var(--txt-3);
}

.sup-dot {
    width: 8px;
    height: 8px;
    border-radius: 999px;
    background: var(--accent-money);
    flex: 0 0 auto;
}

.sup-new,
.sup-back,
.sup-close { margin-top: var(--s3); }

.sup-head {
    margin: var(--s2) 0;
    font-size: var(--fs-sm);
    color: var(--txt-3);
}

.sup-cats {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2);
    margin-top: var(--s2);
}

.sup-cat {
    padding: var(--s2) var(--s3);
    font-size: var(--fs-sm);
    color: var(--txt-2);
    background: var(--bg-2);
    border: 1px solid var(--line);
    border-radius: 999px;
}

.sup-cat.is-on {
    color: var(--txt-1);
    border-color: var(--accent-money);
}

.sup-feed {
    display: flex;
    flex-direction: column;
    gap: var(--s2);
    margin: var(--s3) 0;
    max-height: 50vh;
    overflow-y: auto;
}

.sup-msg {
    max-width: 85%;
    padding: var(--s2) var(--s3);
    background: var(--bg-2);
    border: 1px solid var(--line);
    border-radius: var(--r2);
}

.sup-msg.is-me {
    align-self: flex-end;
    border-color: var(--accent-money);
}

.sup-msg.is-them { align-self: flex-start; }

.sup-msg.is-sys {
    align-self: center;
    max-width: 100%;
    background: none;
    border: none;
    text-align: center;
}

.sup-msg-who {
    display: block;
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    text-transform: uppercase;
    color: var(--txt-3);
}

.sup-msg-t {
    margin: 2px 0 0;
    color: var(--txt-1);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.sup-msg-when {
    font-size: var(--fs-sm);
    color: var(--txt-3);
}

.sup-input {
    width: 100%;
    margin-top: var(--s2);
    padding: var(--s3);
    font: inherit;
    color: var(--txt-1);
    background: var(--bg-2);
    border: 1px solid var(--line);
    border-radius: var(--r2);
    resize: vertical;
}

.sup-input:focus {
    outline: none;
    border-color: var(--accent-money);
}

.sup-send { margin-top: var(--s2); }

/* Ссылка агента, которая уже отработала своё: срок вышел или набралось нужное
   число людей. Не прячем — агент должен видеть, что кампания закрыта, — но
   гасим: живые ссылки важнее. */
.ref-link-row.is-off { opacity: .55; }

/* ── два уровня в оффере друзей ───────────────────────────────────────────
   Две колонки: подпись уровня, крупный процент, пояснение. Программа
   двухуровневая, и раньше витрина показывала половину. */
.ref-levels {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--s2);
    margin: var(--s3) 0;
}

.ref-level {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--s2) var(--s3);
    background: var(--ink-1);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    min-width: 0;
}

.ref-level-k { color: var(--txt-3); }

.ref-level-v {
    font-size: 22px;
    line-height: 1.1;
    color: var(--gain);
}

.ref-level-s {
    font-size: var(--fs-sm);
    color: var(--txt-3);
}

/* ═══════════════════════════════════════════════════════════════════════
   СЛУЖЕБНЫЕ ЭКРАНЫ v7 — язык прибора
   ───────────────────────────────────────────────────────────────────────
   Кольцо в игре живёт на сцене с глубиной: градиентные поверхности, светлое
   ребро сверху, паз снизу, золото «это ты» и свечение вокруг главного числа.
   Служебные экраны при этом были плоскими списками пар «подпись — значение».
   Ниже — тот же язык, перенесённый на кошелёк, профиль, друзей и лидеров:
   панели с фаской, главные числа крупно и со свечением, билеты с цветной
   полосой своего стола, подиум у лидеров. Правила прежние: обе темы через
   токены, reduced-motion гасит анимации глобально, ничего не выезжает вбок.
   Секция стоит ПОСЛЕ прежних правил и перекрывает их той же специфичностью.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── заголовок экрана: крупнее и с золотой чертой ─────────────────────── */
.screen-cap {
    position: relative;
    margin: var(--s4) 0 var(--s3);
    padding-bottom: var(--s2);
    font-size: 20px;
    letter-spacing: .04em;
    color: var(--txt);
}

.screen-cap::after {
    content: '';
    position: absolute;
    left: 0;
    bottom: 0;
    width: 28px;
    height: 2px;
    border-radius: 1px;
    background: var(--me);
}

/* Подпись раздела чуть тише и с воздухом над ней. */
.screen-sub { margin: var(--s5) 0 var(--s2); }

/* ── панель: одна поверхность на все карточки ─────────────────────────── */
#wal-card,
#prof-card,
#prof-level,
#prof-stats,
#prof-season,
.ref-offer,
.ref-agent,
.ldr-card,
.wd-card,
.wd-block .dep-net,
.dep-net,
.sup-row,
.sup-dialog,
.kpi,
.ldr-pod {
    background-color: var(--surf-1-mid);
    background-image: linear-gradient(180deg,
        var(--surf-1-top) 0%, var(--surf-1-mid) 46%, var(--surf-1-bot) 100%);
    border: 1px solid var(--groove);
    border-radius: var(--radius);
    box-shadow:
        inset 0 1px 0 var(--edge-top-2),
        inset 1px 0 0 var(--edge-side),
        inset -1px 0 0 var(--edge-side),
        var(--shadow-contact);
}

/* Поля ввода — выемка в панели, как паз под кольцом. */
.wd-input,
.sup-input,
.ref-input {
    background: var(--ink-2);
    border: 1px solid var(--groove);
    box-shadow: var(--shadow-groove);
}

/* ── кошелёк: счёт как главный прибор ─────────────────────────────────── */
#wal-card {
    position: relative;
    overflow: hidden;
    margin-top: var(--s3);
    padding: var(--s4) var(--s3) var(--s3);
}

/* Свет за главным числом: воздух вокруг цифры, как у кольца. Не текст и не
   контраст — сплошной декоративный слой, который на reduced-motion не
   двигается, потому что и не анимируется. */
#wal-card::before {
    content: '';
    position: absolute;
    left: -10%;
    top: -60px;
    width: 70%;
    height: 180px;
    background: radial-gradient(ellipse at 35% 45%, rgba(126, 207, 192, .14), transparent 62%);
    pointer-events: none;
}

.wal-balance { position: relative; }

.wal-balance-v {
    font-size: 34px;
    letter-spacing: -.01em;
    line-height: 1.05;
    text-shadow: var(--glow-gain);
}

.acc-buttons { margin-top: var(--s4); }
.acc-buttons .btn { height: 52px; }

/* Ключ пополнения — деньги, поэтому ребро цвета денег, как у клавиш покупки. */
.acc-top {
    box-shadow:
        inset 0 1px 0 rgba(126, 207, 192, .18),
        var(--shadow-key);
}

.acc-out-on .btn-n,
.acc-out-on { color: var(--txt); }

/* Билеты и подарки — корешки с цветной полосой своего стола. */
.wal-tk,
.wal-gift {
    position: relative;
    margin-top: var(--s2);
    padding: var(--s3) var(--s3) var(--s3) calc(var(--s3) + 8px);
    background-color: var(--surf-1-mid);
    background-image: linear-gradient(180deg, var(--surf-1-top) 0%, var(--surf-1-bot) 100%);
    border: 1px solid var(--groove);
    border-radius: var(--radius);
    box-shadow: inset 0 1px 0 var(--edge-top-2), var(--shadow-contact);
}

.wal-tk::before,
.wal-gift::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 4px;
    border-radius: var(--radius) 0 0 var(--radius);
    background: var(--room, var(--txt-3));
}

[data-room="micro"] { --room: var(--room-micro); }
[data-room="mid"]   { --room: var(--room-mid); }
[data-room="whale"] { --room: var(--room-whale); }

.wal-tk-room,
.wal-gift-t {
    font-size: var(--fs-md);
    color: var(--txt);
}

.wal-tk-n {
    font-size: 22px;
    line-height: 1.1;
}

.wal-claim {
    height: 40px;
    box-shadow:
        inset 0 1px 0 rgba(126, 207, 192, .18),
        var(--shadow-key);
}

/* ── профиль: личность, уровень, показатели ───────────────────────────── */
#prof-card { padding: var(--s4) var(--s3); }

.acc-head { gap: var(--s4); }

/* Аватар в золотом ободе со свечением: тот же знак «это ты», что на кольце. */
.acc-ava {
    width: 56px;
    height: 56px;
    font-size: 24px;
    box-shadow:
        0 0 0 2px var(--surf-1-mid),
        0 0 0 3px var(--me),
        var(--glow-me);
}

.acc-sum {
    font-size: 24px;
    text-shadow: var(--glow-gain);
}

/* Уровень крупнее: кольцо в 88 пикселей и число в 30. Кольцо здесь — тот же
   прибор, что на игровом экране, только про рост, а не про отсчёт. */
.lvl-круг,
.lvl-ring {
    width: 88px;
    height: 88px;
}

.lvl-ring-track,
.lvl-ring-fill { stroke-width: 5; }

.lvl-ring-fill { filter: drop-shadow(0 0 4px var(--me-dim)); }

.lvl-num {
    font-size: 30px;
    color: var(--me);
}

.lvl-of { font-size: var(--fs-md); }
.lvl-next { font-size: var(--fs-md); }

.lvl-bar {
    height: 6px;
    margin-top: var(--s3);
    border-radius: 3px;
    background: var(--ink-2);
    box-shadow: var(--shadow-groove);
    overflow: hidden;
}

.lvl-fill {
    display: block;
    height: 100%;
    border-radius: 3px;
    background: linear-gradient(90deg, var(--me-grad-3), var(--me-grad-1));
    box-shadow: 0 0 8px var(--me-dim);
    transition: width var(--t-slow) var(--ease-out);
}

/* Показатели плитками, а не строками: главное число крупно и своим цветом. */
.kpi-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--s2);
    margin-top: var(--s2);
}

.kpi {
    padding: var(--s3);
    min-width: 0;
}

.kpi-k {
    display: block;
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    text-transform: uppercase;
    color: var(--txt-3);
}

.kpi-v {
    display: block;
    margin-top: 4px;
    font-size: 22px;
    line-height: 1.1;
    color: var(--txt);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.kpi-v.is-money { color: var(--gain); }
.kpi-v.is-me { color: var(--me); }

.kpi-s {
    display: block;
    margin-top: 2px;
    font-size: var(--fs-sm);
    color: var(--txt-3);
}

/* Сезон: место — главное число карточки, золотом, как первое место на кольце. */
.season-hero {
    display: flex;
    align-items: baseline;
    gap: var(--s3);
    margin: var(--s2) 0 var(--s1);
}

.season-rank {
    font-size: 34px;
    line-height: 1;
    color: var(--me);
    text-shadow: var(--glow-me);
}

.season-pts {
    font-size: var(--fs-lg);
    color: var(--txt);
}

.season-pts-s {
    font-size: var(--fs-sm);
    color: var(--txt-3);
}

.season-track {
    height: 5px;
    border-radius: 3px;
    background: var(--ink-2);
    box-shadow: var(--shadow-groove);
}

.season-fill {
    opacity: 1;
    background: linear-gradient(90deg, var(--me-grad-3), var(--me-grad-1));
}

/* ── друзья: оффер и купон ────────────────────────────────────────────── */
.ref-offer {
    position: relative;
    overflow: hidden;
    padding: var(--s4) var(--s3) var(--s3);
}

.ref-offer::before {
    content: '';
    position: absolute;
    right: -20%;
    top: -70px;
    width: 60%;
    height: 180px;
    background: radial-gradient(ellipse at 60% 45%, rgba(126, 207, 192, .12), transparent 62%);
    pointer-events: none;
}

.ref-offer-big {
    position: relative;
    font-size: 20px;
    line-height: 1.25;
}

.ref-level {
    background-color: var(--surf-1-mid);
    background-image: linear-gradient(180deg, var(--surf-1-top) 0%, var(--surf-1-bot) 100%);
    border: 1px solid var(--groove);
    box-shadow: inset 0 1px 0 var(--edge-top-2), var(--shadow-contact);
}

.ref-level-v {
    font-size: 26px;
    text-shadow: var(--glow-gain);
}

/* Ссылка — купон: пунктирная рамка, моноширинный адрес. */
.ref-link {
    padding: var(--s3);
    background: var(--ink-1);
    border: 1px dashed var(--line-hi);
    text-align: center;
}

.ref-link-t { color: var(--txt); }

.ref-share { height: 56px; }

/* ── лидеры: подиум и медали ──────────────────────────────────────────── */
.ldr-podium {
    display: grid;
    grid-template-columns: 1fr 1.15fr 1fr;
    gap: var(--s2);
    align-items: end;
    margin-top: var(--s3);
}

.ldr-pod {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    min-width: 0;
    padding: var(--s3) var(--s2);
    text-align: center;
}

.ldr-pod-1 { padding-top: var(--s4); padding-bottom: var(--s4); }

.ldr-pod .lrow-ava {
    width: 40px;
    height: 40px;
    font-size: 16px;
    margin-bottom: 2px;
}

.ldr-pod-1 .lrow-ava {
    width: 52px;
    height: 52px;
    font-size: 20px;
    box-shadow:
        0 0 0 2px var(--surf-1-mid),
        0 0 0 3px var(--gold),
        var(--glow-gold);
}

.ldr-pod-2 .lrow-ava { box-shadow: 0 0 0 2px var(--surf-1-mid), 0 0 0 3px var(--medal-2); }
.ldr-pod-3 .lrow-ava { box-shadow: 0 0 0 2px var(--surf-1-mid), 0 0 0 3px var(--medal-3); }

.ldr-pod-rank {
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    color: var(--txt-3);
}

.ldr-pod-1 .ldr-pod-rank { color: var(--gold); }
.ldr-pod-2 .ldr-pod-rank { color: var(--medal-2); }
.ldr-pod-3 .ldr-pod-rank { color: var(--medal-3); }

.ldr-pod-name {
    max-width: 100%;
    font-size: var(--fs-sm);
    color: var(--txt-2);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ldr-pod-pts {
    font-size: var(--fs-lg);
    line-height: 1.1;
    color: var(--txt);
}

.ldr-pod-1 .ldr-pod-pts { font-size: 22px; }

.ldr-top1 .lrow-rank { color: var(--gold); }
.ldr-top2 .lrow-rank { color: var(--medal-2); }
.ldr-top3 .lrow-rank { color: var(--medal-3); }

/* Строки списков: ровнее и с воздухом; своя строка — золотой ободок аватара. */
.lrow { padding: var(--s3) 0; }
.lrow-t { font-size: var(--fs-md); }
.lrow-v { font-size: var(--fs-lg); }
.lrow-me .lrow-ava { box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px var(--me); }

/* ── пополнение, вывод, поддержка: та же панель ───────────────────────── */
.dep-net.is-on {
    border-color: var(--me);
    box-shadow:
        inset 0 1px 0 var(--edge-top-2),
        0 0 0 1px var(--me-dim),
        var(--shadow-contact);
}

.wd-card.is-live { border-color: var(--me); }

.sup-row.has-unread { border-color: var(--me); }

.sup-msg.is-them {
    background-color: var(--surf-1-mid);
    background-image: linear-gradient(180deg, var(--surf-1-top) 0%, var(--surf-1-bot) 100%);
    border-color: var(--groove);
}

.sup-msg.is-me {
    background: var(--ink-1);
    border-color: var(--me-dim);
}

/* ── поддержка: диалог как лента, а не набор клавиш ───────────────────── */
.sup-dialog { padding: var(--s3); }

/* «К списку» — путь назад, а не действие: текстом, как у лидеров. */
.sup-back {
    align-self: flex-start;
    height: auto;
    margin: 0 0 var(--s2);
    padding: 4px 0;
    background: none;
    border: 0;
    box-shadow: none;
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    color: var(--txt-3);
}

.sup-back:active { color: var(--txt-2); }

.sup-head {
    margin: 0 0 var(--s2);
    padding-bottom: var(--s2);
    border-bottom: 1px solid var(--rule);
}

.sup-feed { margin: var(--s2) 0 var(--s3); }

.sup-send {
    width: 100%;
    height: 52px;
    margin-top: var(--s2);
}

/* Закрыть обращение — второстепенно: текстом под формой, а не второй клавишей. */
.sup-close {
    align-self: center;
    height: auto;
    margin-top: var(--s3);
    padding: 6px 0;
    background: none;
    border: 0;
    box-shadow: none;
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    color: var(--txt-3);
}

.sup-close:active { color: var(--txt-2); }

.sup-new { width: 100%; height: 52px; }

/* Список обращений — та же панель, что везде; непрочитанное — золотая точка. */
.sup-row { padding: var(--s3); }

/* Вывод: «всё» и «отменить» — подсказки, а не клавиши. */
.wd-all,
.wd-cancel {
    height: auto;
    padding: 6px 0;
    background: none;
    border: 0;
    box-shadow: none;
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    color: var(--gain);
}

.wd-all:active,
.wd-cancel:active { color: var(--gain-dim); }

.wd-send { width: 100%; height: 52px; }

/* ══════════════════════════════════════════════════════════════════════════
   ЭКИПАЖ — полоса медальонов игроков (js/ui/crew.js)
   Накладка в пустоте сцены под кольцом; в поток не добавляет ни пикселя.
   Цвета только токенами: медальон берёт --c из общей палитры инлайном.
   ══════════════════════════════════════════════════════════════════════════ */
.crew {
    position: absolute;
    left: calc(var(--pad-x) - 4px);
    right: calc(var(--pad-x) - 4px);
    bottom: 2px;
    height: 58px;
    display: flex;
    justify-content: center;
    align-items: flex-start;
    gap: 5px;
    z-index: 2;
    contain: layout;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

@media (min-width: 760px) {
    .crew { left: 0; right: 0; }
}

/* Пустая или лишняя полоса невидима, но ширину держит: её меряет модуль. */
.crew.is-empty {
    visibility: hidden;
    pointer-events: none;
}

.crew-chip {
    position: relative;
    width: 62px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: 0;
    border: 0;
    background: none;
    color: var(--txt-2);
    font-family: var(--font-ui);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.crew-medal {
    position: relative;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: 2px solid var(--c, var(--seg-3));
    background: var(--surf-2-top);
    box-shadow: var(--shadow-contact);
    display: grid;
    place-items: center;
    transition: border-color 200ms var(--ease-out);
}

.crew-medal::after {
    content: '';
    position: absolute;
    inset: -2px;
    border-radius: 50%;
    background: var(--flash);
    opacity: 0;
    pointer-events: none;
}

.crew-n {
    font-size: var(--fs-sm);
    line-height: 1;
    color: var(--txt);
}

.crew-nick {
    display: block;
    max-width: 62px;
    font-size: 12px;
    line-height: 14px;
    letter-spacing: -0.01em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Своё — янтарём, как всё своё в приборе. */
.crew-chip.is-me .crew-medal { box-shadow: var(--glow-me); }
.crew-chip.is-me .crew-nick { color: var(--me); }
/* Выбыл, но место за тобой: тот же тусклый янтарь, что у призрачного сегмента. */
.crew-chip.is-out .crew-medal { border-color: var(--me-dim); box-shadow: none; }
.crew-chip.is-out .crew-nick { color: var(--me-out); }

.crew-more { width: 44px; }
.crew-more .crew-medal { border-style: dashed; border-color: var(--txt-3); }
.crew-more .crew-n,
.crew-more .crew-nick { color: var(--txt-3); letter-spacing: var(--ls-cap); }

/* Выбывший медальон не просто исчезает — его место схлопывается, и соседи
   съезжаются сами, средствами flex. Раньше место (62 px + 5 px промежутка)
   стояло пустым до пересборки полосы на 760 мс, и дыра закрывалась скачком —
   на четыре десятых секунды позже, чем кольцо смыкало свою. */
.crew-chip.is-gone {
    visibility: hidden;
    width: 0;
    margin-left: -5px;
    transition: width 280ms var(--ease-out), margin-left 280ms var(--ease-out);
}
.crew.rail .crew-chip.is-gone { margin-left: -4px; }

/* Рельс: точки без ников там, где под кольцом всего 15–59 px. */
.crew.rail {
    height: 14px;
    bottom: 1px;
    gap: 4px;
    align-items: center;
}
.crew.rail .crew-chip { width: 12px; gap: 0; }
.crew.rail .crew-medal { width: 12px; height: 12px; box-shadow: none; }
.crew.rail .crew-n,
.crew.rail .crew-nick { display: none; }
.crew.rail .crew-more { width: auto; }
.crew.rail .crew-more .crew-medal { display: none; }
.crew.rail .crew-more .crew-n { display: inline; font-size: 12px; }

/* Канва пыли: больше полосы на 48 px вверх и по 20 в стороны. */
.crew-fx {
    position: absolute;
    left: -20px;
    top: -48px;
    pointer-events: none;
    z-index: 3;
}

/* Движение: только opacity/transform/цвет. */
.crew-chip.is-hit .crew-medal { animation: crew-hit 220ms var(--ease-out) 1; }
.crew-chip.is-hit .crew-medal::after { animation: crew-flash 70ms linear 1; }
.crew-chip.is-hit-me .crew-medal { animation: crew-hit-me 220ms var(--ease-out) 1; }
.crew-chip.is-hit-me .crew-medal::after { background: var(--me-hi); animation: crew-flash 70ms linear 1; }
.crew-chip.is-hit-me { animation: crew-shake 160ms linear 1; }
.crew-chip.is-dust { animation: crew-dust 340ms var(--ease-out) 1 forwards; pointer-events: none; }
.crew-chip.crew-in { animation: crew-in 180ms var(--ease-out) 1; }
.crew-medal.crew-revive { animation: crew-revive 260ms var(--ease-pop) 1; }
.crew-medal.crew-buy { animation: crew-buy 260ms var(--ease-pop) 1; }

@keyframes crew-hit { 0% { transform: scale(1); } 12% { transform: scale(1.08); } 100% { transform: scale(1); } }
@keyframes crew-hit-me { 0% { transform: scale(1); } 12% { transform: scale(1.12); } 100% { transform: scale(1); } }
@keyframes crew-flash { 0% { opacity: 0; } 40% { opacity: .55; } 100% { opacity: 0; } }
@keyframes crew-shake {
    0%, 100% { transform: translateX(0); }
    25% { transform: translateX(-2px); }
    50% { transform: translateX(2px); }
    75% { transform: translateX(-1px); }
}
@keyframes crew-dust {
    from { opacity: 1; transform: none; }
    to   { opacity: 0; transform: translate(6px, -8px) scale(.94) rotate(3deg); }
}
@keyframes crew-in { from { opacity: 0; transform: translateX(-6px); } to { opacity: 1; transform: none; } }
@keyframes crew-revive { from { opacity: .6; transform: scale(.9); } to { opacity: 1; transform: none; } }
@keyframes crew-buy { 0% { transform: scale(1); } 45% { transform: scale(1.12); } 100% { transform: scale(1); } }

/* В дуэли светится только кольцо; на развязке персонажи переезжают на подиум. */
body.on-duel .crew { opacity: .35; transition: opacity 320ms ease-out; }
body.on-settled .crew { opacity: 0; pointer-events: none; transition: opacity 200ms ease-out; }

@media (prefers-reduced-motion: reduce) {
    .crew-fx { display: none; }
    .crew-chip.is-dust { opacity: 0; }
    .crew-chip.is-gone { transition: none; }
    .crew-chip,
    .crew-medal { animation: none !important; }
}

/* ══════════════════════════════════════════════════════════════════════════
   ПОДИУМ — крупный итог 1·2·3 (js/ui/finale.js)
   Накладка ровно над стойкой приборов: приборной строкой, клавишами и
   карточкой. На кольцо не заходит — исход рассказывает кольцо, подиум
   появляется после его шва. z 45: ниже шторки (46) и объяснения (48).
   ══════════════════════════════════════════════════════════════════════════ */
.podium {
    position: absolute;
    inset: 0;
    z-index: 45;
    display: grid;
    grid-template-rows: auto 1fr auto;
    gap: var(--s2);
    padding: var(--s2) var(--s3);
    background: linear-gradient(180deg, var(--surf-1-top), var(--surf-1-bot));
    border-top: 1px solid var(--groove);
    box-shadow: inset 0 1px 0 var(--edge-top-2), var(--shadow-panel);
    cursor: pointer;
    animation: pod-in 140ms var(--ease-out) 1;
}

/* Ритм стойки (.controls > * + *) даёт поле сверху и накладке — снимаем:
   подиум накрывает стойку целиком, от верхнего края. */
.controls > .podium { margin-top: 0; }

.podium.pod-out { animation: pod-out 220ms var(--ease-out) 1 forwards; pointer-events: none; }

.pod-cap {
    display: flex;
    justify-content: space-between;
    gap: var(--s2);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    text-transform: uppercase;
    color: var(--txt-3);
    white-space: nowrap;
}
/* Отсчёт набран в JS уже капителью, а единица «с» — строчная и такой остаётся. */
.pod-cap .num { text-transform: none; }

.pod-steps {
    display: grid;
    grid-template-columns: 1fr 1.25fr 1fr;
    gap: var(--s2);
    align-items: end;
    min-height: 0;
}

.pod-step {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    min-width: 0;
    opacity: 0;
}
.pod-step.p2 { order: 1; }
.pod-step.p1 { order: 2; }
.pod-step.p3 { order: 3; }
.pod-step.pod-step-in { animation: pod-step-in 260ms var(--ease-out) 1 forwards; }
.pod-step.p1.pod-step-in { animation-duration: 320ms; animation-timing-function: var(--ease-pop); }
/* Опоздавший зритель и просьба о покое: подиум стоит целиком, без подъёма. */
.podium.pod-quiet .pod-step { opacity: 1; animation: none; }

.pod-medal {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    border: 2px solid var(--c, var(--seg-3));
    background: var(--surf-2-top);
    box-shadow: var(--shadow-contact);
    display: grid;
    place-items: center;
}
.p1 .pod-medal { width: 40px; height: 40px; }

.pod-place { font-size: var(--fs-md); font-weight: 600; line-height: 1; color: var(--txt-2); }
.p1 .pod-place { font-size: var(--fs-lg); color: var(--gold); }
.p2 .pod-place { color: var(--medal-2); }
.p3 .pod-place { color: var(--medal-3); }

.pod-nick {
    display: block;
    max-width: 100%;
    font-family: var(--font-ui);
    font-size: 12px;
    line-height: 14px;
    color: var(--txt-2);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.pod-sum { font-size: 15px; line-height: 1.1; color: var(--txt); }
.p1 .pod-sum { font-size: var(--fs-lg); color: var(--gold); }
.pod-share { font-size: var(--fs-sm); color: var(--txt-3); }

/* Ступень: клавишная поверхность, у первого места самая высокая. */
.pod-base {
    display: block;
    width: 100%;
    margin-top: 4px;
    border-radius: var(--radius) var(--radius) 0 0;
    background: linear-gradient(180deg, var(--surf-3-top), var(--surf-3-bot));
    box-shadow: inset 0 1px 0 var(--edge-top-2);
}
.p1 .pod-base { height: 24px; }
.p2 .pod-base { height: 16px; }
.p3 .pod-base { height: 10px; }

/* Своё место — янтарём; золотое свечение медали — только своей победе. */
.pod-step.pod-mine .pod-nick { color: var(--me); }
.pod-step.pod-mine .pod-base { box-shadow: inset 0 2px 0 var(--me); }
.podium.pod-win .p1 .pod-medal { box-shadow: var(--glow-gold); }

.pod-note {
    font-size: var(--fs-sm);
    color: var(--txt-3);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.pod-note.fin-plus { color: var(--gain); }

/* Герой подиума: сколько человек заработал за раунд. Самое крупное число на
   экране итога — всё остальное там его разбор. */
.pod-hero {
    display: flex; align-items: baseline; justify-content: center; gap: 10px;
    margin-top: var(--s2); min-width: 0;
}
.pod-you { font-size: 30px; line-height: 1.1; font-variant-numeric: tabular-nums; color: var(--txt); }
.pod-you.fin-plus { color: var(--gain); }
.pod-place-of { font-size: var(--fs-cap); color: var(--txt-2); white-space: nowrap; }

/* Крестик подиума: для тех, кто хочет вернуться к кольцу, не открывая подробностей. */
.pod-x {
    position: absolute; top: 6px; right: 6px;
    width: 44px; height: 44px; border: 0; border-radius: 50%;
    background: transparent; color: var(--txt-3);
    font-size: 18px; line-height: 1; cursor: pointer;
}
.pod-x:active { background: var(--surf-1-mid); }

/* Итог ставки на подиуме — отдельной строкой под строкой участника. Тоньше
   отбит сверху: это другие деньги (билеты — участие, ставка — угадывание).

   Две строки, а не одна с многоточием: строка вида «СТАВКА НА SWIFTFALCON42
   30$ → 51.38$ · ×1.71 · +21.38$» в одну строку обрезалась ровно на сумме
   выигрыша — то есть терялось единственное, ради чего её и читают. Места на
   подиуме под вторую строку хватает. */
.pod-note.pod-bet {
    margin-top: 4px;
    opacity: 0.95;
    white-space: normal;
    line-height: 1.25;
    overflow: visible;
}
.pod-note.pod-bet.hidden { display: none; }

/* Полка ставок, доигрывающая раунд: приём закрыт, но своя ставка на виду.
   Клавиши гаснут, а не исчезают — пропадающие кнопки читаются как поломка. */
#panel-bets.is-closed .bet-side { opacity: 0.5; }
#panel-bets.is-closed .bet-side-odds { visibility: hidden; }
/* А своя ставка НЕ гаснет: в закрытом состоянии это единственное, что человеку
   важно — сколько он поставил и что вернётся, если угадал. */
#panel-bets.is-closed .bet-mine { opacity: 1; }

/* Карточка итога ждёт шва кольца, как и подиум: цифры не объявляются раньше. */
.finale.pre-seam { visibility: hidden; }

@keyframes pod-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes pod-step-in { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@keyframes pod-out { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(8px); } }

@media (prefers-reduced-motion: reduce) {
    .podium, .pod-step { animation: none; }
    .pod-step { opacity: 1; }
}

/* Компактная карточка после подиума: место цифрой, ник и сумма столбиком.
   Высота прежняя (замер finfit): ячейка выросла на 8 px, поля строки честности
   и полосы долей ужаты на те же 8. */
.fin-row { min-height: 30px; }
.fin-cell {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-rows: auto auto;
    column-gap: var(--s1);
    align-items: center;
}
.fin-place {
    grid-row: 1 / 3;
    font-size: 18px;
    line-height: 1;
    color: var(--txt-3);
}
.fin-cell:nth-child(2) .fin-place { color: var(--medal-2); }
.fin-cell:nth-child(3) .fin-place { color: var(--medal-3); }
.fin-nick {
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    line-height: 14px;
    color: var(--txt-3);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.fin-sum { font-size: 14px; line-height: 16px; }
.fin-mine .fin-nick { color: var(--me); }
.fin-note { margin-top: var(--s1); }
.fin-bar { margin-top: var(--s1); }

/* Ник в подписях центра кольца — без капители: «WittyViking93», не «WITTYVIKING93». */
.dial-phase .nick,
.dial-hint .nick { text-transform: none; letter-spacing: 0; }

/* Список в шторке: ник — слово, а не хеш. В узкой альбомной колонке билеты
   уступают место нику. */
.row-pid { font-family: var(--font-ui); }
@media (max-height: 620px) and (min-width: 760px) {
    .col-right .row-tickets { display: none; }
    .col-right .row-pid { font-size: var(--fs-cap); }
}


/* ══════════════════════════════════════════════════════════════════════════
   НАПРЯЖЕНИЕ (js/ui/tension.js, ring/renderer.js)
   Классы на теле: near-wave — последние три секунды до волны; survived —
   первые 0.9 с после «пережил»; me-out — выбыл и не выкупился.
   Всё движение — только opacity/transform; цвет опасности — --risk.
   ══════════════════════════════════════════════════════════════════════════ */

/* Отсчёт до волны в последние секунды — цветом опасности и с дыханием 1.04:
   тот же предел, что у отсчёта сбора. Текст не двигается, только дышит. */
body.near-wave .dial-time-sub {
    color: var(--risk);
    animation: dial-breath 500ms ease-in-out infinite alternate;
}

/* Пережил: цифра шанса коротко вздыхает (≤ 6 % масштаба), подпись под ней — янтарём. */
body.survived .chance-value {
    animation: survive-breath 520ms var(--ease-out) 1;
}
body.survived .chance-note {
    color: var(--me);
}
@keyframes survive-breath {
    0%   { transform: scale(1); }
    30%  { transform: scale(1.05); }
    100% { transform: scale(1); }
}

/* Выбыл: мир продолжается без тебя. Виньетка тем же слоем, что у окна докупа,
   приборы и экипаж тише; кольцо (канва) обесцвечивает рендерер сам. Это
   состояние, а не дожим: внутри окна возврата ничего не усиливается. */
body.me-out .bg-fx > .bg-dim { opacity: 1; }
body.me-out .rooms,
body.me-out .crew,
body.me-out .feed { opacity: .45; transition: opacity 500ms ease-out; }

/* Слово «ВЫБЫЛ» поверх кольца в момент своего вылета.
   Событие, а не состояние: краснота горит фоном и её видно краем глаза, а это
   надо ПРОЧИТАТЬ. Поэтому крупно, по центру сцены и всего на полторы секунды.
   Ничего не перехватывает нажатия — под ним живые клавиши. */
.out-stamp {
    position: fixed;
    left: 50%;
    top: 38%;
    z-index: 60;
    transform: translate(-50%, -50%);
    padding: 6px 18px;
    font-size: clamp(34px, 13vw, 68px);
    font-weight: 800;
    letter-spacing: 0.08em;
    color: var(--me-out);
    text-shadow: 0 0 22px var(--risk), 0 2px 10px var(--veil);
    pointer-events: none;
    white-space: nowrap;
    animation: out-stamp 1600ms var(--ease-out) 1 both;
}
/* Удар — резкий, уход — долгий: слово прилетает, замирает и растворяется. */
@keyframes out-stamp {
    0%   { opacity: 0; transform: translate(-50%, -50%) scale(1.7); filter: blur(6px); }
    12%  { opacity: 1; transform: translate(-50%, -50%) scale(0.96); filter: none; }
    20%  { transform: translate(-50%, -50%) scale(1); }
    68%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
    100% { opacity: 0; transform: translate(-50%, -52%) scale(1.06); }
}
/* Просьба о покое: слово остаётся, движение уходит. */
.out-stamp.is-calm { animation: out-stamp-calm 1600ms linear 1 both; }
@keyframes out-stamp-calm {
    0%   { opacity: 0; }
    10%  { opacity: 1; }
    72%  { opacity: 1; }
    100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
    .out-stamp { animation: out-stamp-calm 1600ms linear 1 both; }
}

/* Свой медальон вздыхает, пережив волну. */
.crew-medal.crew-survive { animation: crew-survive 520ms var(--ease-pop) 1; }
@keyframes crew-survive {
    0%   { transform: scale(1); }
    35%  { transform: scale(1.14); box-shadow: 0 0 0 3px var(--flash); }
    100% { transform: scale(1); }
}

/* Подиум: серебро и бронза — кромка своей медали и ступени. */
.pod-step.pod-silver .pod-medal { border-color: var(--medal-2); box-shadow: 0 0 14px var(--medal-2); }
.pod-step.pod-bronze .pod-medal { border-color: var(--medal-3); box-shadow: 0 0 14px var(--medal-3); }
.pod-step.pod-silver .pod-base { box-shadow: inset 0 2px 0 var(--medal-2); }
.pod-step.pod-bronze .pod-base { box-shadow: inset 0 2px 0 var(--medal-3); }

@media (prefers-reduced-motion: reduce) {
    body.near-wave .dial-time-sub,
    body.survived .chance-value,
    .crew-medal.crew-survive { animation: none; }
}

/* ─── кэшбэк в кошельке ──────────────────────────────────────────────────
   Строка «сколько вернулось» и полоска до следующей ступени. Полоска считается
   от предыдущей ступени, а не от нуля: на десятой ступени полная от нуля она
   не говорила бы уже ничего. */
.wal-cb {
    display: flex; align-items: center; gap: 12px;
    background: var(--surf-2-mid); border: 1px solid var(--line);
    border-radius: var(--radius); padding: 12px 14px;
}
.wal-cb-main { flex: 1 1 auto; min-width: 0; }
.wal-cb-sum { font-size: var(--fs-lg); color: var(--gain); }
.wal-cb-sub { font-size: var(--fs-sm); color: var(--txt-3); margin-top: 2px; }
.wal-cb-btn { flex: 0 0 auto; }
.wal-cb-bar {
    height: 6px; border-radius: 3px; overflow: hidden;
    background: var(--surf-2-mid); border: 1px solid var(--line);
    margin: 8px 0 4px;
}
.wal-cb-bar i { display: block; height: 100%; background: var(--gain); }

/* ─── ставки на финал ────────────────────────────────────────────────────
   Полка того же вида, что возврат и докуп: она живёт в той же стойке приборов
   и не имеет права выглядеть чужой. Две клавиши по числу сторон финала —
   именно две, поэтому сетка фиксированная, а не auto-fit: «две колонки» здесь
   правило игры, а не следствие ширины. */
.bets-sides { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.bet-side {
    --bet-color: var(--txt-3);
    display: flex; flex-direction: column; align-items: center; gap: 2px;
    min-height: 62px; padding: 8px 6px;
    border: 1px solid color-mix(in srgb, var(--bet-color) 55%, transparent);
    border-radius: var(--radius);
    background: color-mix(in srgb, var(--bet-color) 12%, transparent);
    color: var(--txt); font: inherit; cursor: pointer;
}
.bet-side:active,
.bet-side.is-down { background: color-mix(in srgb, var(--bet-color) 22%, transparent); transform: scale(.985); }
.bet-side:disabled { cursor: default; transform: none; }
/* Своя сторона — янтарной рамкой: янтарь во всём приборе значит «это про тебя»,
   и вопрос «куда я поставил» перестаёт возникать. */
.bet-side.is-mine { border-color: var(--me); box-shadow: inset 0 0 0 1px var(--me); }
.bet-side-nick {
    font-size: var(--fs-sm); font-weight: 600; color: var(--bet-color);
    max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Шанс — единственный процент на клавише и самое крупное на ней число: это
   ответ на вопрос «кто сильнее». Доля котла отсюда убрана совсем — два процента
   про одного человека на одном экране прочитать нельзя. */
.bet-side-chance { font-size: var(--fs-lg); font-variant-numeric: tabular-nums; }
.bet-side-odds { font-size: var(--fs-sm); color: var(--txt-3); }
/* Деньги — единственная сумма на клавише, и она подписана словами. */
.bet-side-pool { font-size: var(--fs-cap); color: var(--txt-3); font-variant-numeric: tabular-nums; }

/* ─── своя ставка ────────────────────────────────────────────────────────
   Слот постоянной высоты: полка стоит поверх приборной строки, и вырастающая
   на ходу — накрывает её. Этот урок уже записан в полке докупа. */
.bet-mine { margin-top: var(--s2); min-height: 34px; }
.bet-mine-empty { margin: 6px 0 0; font-size: var(--fs-sm); color: var(--txt-3); }
.bet-mine-head { display: flex; justify-content: space-between; gap: 8px; }
.bet-mine-row {
    display: flex; align-items: baseline; gap: 8px; margin-top: 2px;
    font-variant-numeric: tabular-nums;
}
.bet-mine-dot {
    --bet-color: var(--txt-3);
    flex: 0 0 auto; width: 8px; height: 8px; border-radius: 50%;
    background: var(--bet-color);
}
.bet-mine-nick {
    flex: 1 1 auto; min-width: 0; font-size: var(--fs-sm);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bet-mine-sum { flex: 0 0 auto; }
/* Возврат зеленью — только когда он БОЛЬШЕ ставки. «Вернём ровно твоё» это не
   выигрыш, и красить его цветом дохода значило бы обещать доход. */
.bet-mine-back { flex: 0 0 auto; color: var(--txt-3); }
.bet-mine-back.is-gain { color: var(--gain); }

/* Меню быстрых ставок — накладка снизу.
   Механика та же, что у шторки: на пятнадцати секундах диалог посреди экрана
   заставляет искать его глазами, а нижняя полоса всегда под большим пальцем.
   Карточка отделена от подложки: подложка гасит фон и ловит нажатие мимо,
   карточка держит содержимое. */
.bets-menu {
    position: fixed;
    inset: 0;
    z-index: 60;                 /* выше шторки и объяснения новичку */
    display: flex;
    align-items: flex-end;
    justify-content: center;
    background: var(--veil);
    padding: 12px;
    padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
}
.bets-menu.hidden { display: none; }

/* Карточка меню. Всё содержимое лежит в ней, поэтому нажатие по подложке
   (и только по ней) закрывает меню. */
.bets-menu-card {
    width: 100%;
    max-width: 520px;
    background: var(--surf-1-mid);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 14px;
    /* Та же тень, что у всплывающих панелей: своя выглядела бы чужой. */
    box-shadow: var(--shadow-panel);
}
.bets-menu-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 12px;
}
.bets-quick {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 8px;
}
.bet-quick {
    min-height: 52px; border-radius: var(--radius);
    border: 1px solid var(--line); background: var(--surf-2-mid);
    color: var(--txt); font: inherit; font-variant-numeric: tabular-nums; cursor: pointer;
}
.bet-quick:disabled { opacity: .4; cursor: default; }
.bet-quick:active:not(:disabled),
.bet-quick.is-down:not(:disabled) { background: var(--surf-1-mid); transform: scale(.985); }
/* Отказ мигает рамкой ТОЙ клавиши, на которую нажали: причина стоит там же,
   в подсказке под ними, а не под вуалью меню на полке. */
.bet-quick.is-reject { animation: bets-reject 270ms var(--ease-out) 3; }
@keyframes bets-reject {
    0%, 100% { border-color: var(--line); }
    50%      { border-color: var(--risk); }
}
.bets-hint { margin: 10px 0 0; font-size: var(--fs-sm); color: var(--txt-2); min-height: 2.6em; }
.bets-hint.is-error { color: var(--risk); }

/* На кого ставишь: точка цветом стороны, ник как есть и его шанс. */
.bets-menu-who { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.bets-who-nick {
    font-size: var(--fs-md); min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bets-who-chance { font-size: var(--fs-sm); color: var(--txt-3); font-variant-numeric: tabular-nums; }
.bets-close {
    flex: 0 0 auto;
    width: 36px; height: 36px; border-radius: 50%;
    border: 1px solid var(--line); background: var(--surf-2-mid);
    color: var(--txt-2); font-size: 20px; line-height: 1; cursor: pointer;
}

/* ─── список живых игр ───────────────────────────────────────────────────
   Строка на комнату: слева стол и фаза, справа банк и люди. Нажатие уводит
   смотреть — поэтому это кнопка, а не div с обработчиком. */
.live-list { display: flex; flex-direction: column; gap: 6px; }
.live-row {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    /* Перенос нужен ровно одной вещи — строке «твои 50.00 · вернём через 6:12»:
       она занимает всю ширину под остальным. Остальное как было. */
    flex-wrap: wrap;
    width: 100%; min-height: 44px; padding: 6px 10px;
    border: 1px solid var(--line); border-radius: var(--radius);
    background: var(--surf-2-mid); color: var(--txt); font: inherit; text-align: left; cursor: pointer;
}
.live-row:active { background: var(--surf-1-mid); }
.live-row.is-here { border-color: var(--me); }
.live-main { display: flex; flex-direction: column; min-width: 0; }
.live-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.live-phase { font-size: var(--fs-sm); color: var(--txt-3); }
/* ИДЁТ ПРИЁМ СТАВОК. Пятнадцать секунд из полутора минут раунда — если строка
   не бросается в глаза, человек их не увидит вовсе. Пульс, а не заливка: в
   списке из шести строк заливка читается как «всё важно», то есть как ничего. */
.live-row.is-bets .live-phase { color: var(--me); }
.live-row.is-bets { border-color: var(--me-dim, var(--me)); }
/* Приём вот-вот откроется: то же место, но спокойнее — это ещё не событие,
   а предупреждение о нём. */
.live-row.is-bets-soon .live-phase { color: var(--txt-2); }
@media (prefers-reduced-motion: no-preference) {
    .live-row.is-bets .live-phase { animation: live-bets-pulse 1.6s ease-in-out infinite; }
}
@keyframes live-bets-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .55; } }
.live-nums { display: flex; flex-direction: column; align-items: flex-end; flex: 0 0 auto; }
.live-pot { color: var(--gain); font-variant-numeric: tabular-nums; }
.live-players { font-size: var(--fs-sm); color: var(--txt-3); font-variant-numeric: tabular-nums; }
.live-mine { flex: 0 0 auto; font-size: var(--fs-sm); color: var(--me); }
/* Своя неначавшаяся игра: сколько там моих денег и когда вернутся. Строкой во
   всю ширину под остальным — это не про выбор комнаты, а про свои деньги, и
   мешать её с числами справа нельзя. */
.live-own {
    flex: 1 0 100%;
    display: flex; align-items: center; justify-content: space-between; gap: var(--s2);
    margin-top: var(--s1); padding-top: var(--s1);
    border-top: 1px solid var(--line);
}
.live-own-t { font-size: var(--fs-sm); color: var(--txt-2); font-variant-numeric: tabular-nums; }
.live-take {
    flex: 0 0 auto;
    min-height: 28px; padding: 0 var(--s2);
    display: inline-flex; align-items: center;
    border: 1px solid var(--line); border-radius: var(--radius);
    font-size: var(--fs-cap); letter-spacing: var(--ls-cap); color: var(--me);
    cursor: pointer;
}

/* Свои игры отделены от чужих не заголовком, а видом строки: заголовок «ТВОИ
   ИГРЫ» над одной строкой съедает больше места, чем сама строка. */
.live-own { margin-bottom: 8px; }
.live-own .live-row { border-color: var(--me); background: var(--surf-1-mid); }
.live-row.is-mine .live-name { color: var(--me); }
/* Выбывшая своя игра остаётся своей — но приглушённой: смотреть там ещё есть на
   что (финал, ставки), а вот шансов уже нет, и гореть янтарём ей не за что. */
.live-row.is-out { opacity: 0.72; }
.live-own .live-row.is-out { border-color: var(--line); }

/* Переключатели столов: одна прокручиваемая вбок строка. Вбок, а не в две
   строки, потому что столов может стать больше трёх, а высота панели —
   единственное, чего здесь в обрез. */
.live-tabs {
    display: flex; gap: 6px; margin-bottom: 8px;
    overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch;
}
.live-tabs::-webkit-scrollbar { display: none; }
.live-tab {
    display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto;
    min-height: 44px; min-width: 44px; padding: 6px 10px;
    border: 1px solid var(--line); border-radius: var(--radius);
    background: var(--surf-2-mid); color: var(--txt-3); font: inherit; cursor: pointer;
}
.live-tab.is-on { border-color: var(--me); color: var(--txt); background: var(--surf-1-mid); }
/* Точка на переключателе стола, где идёт ТВОЯ игра: под фильтром «Все» иначе
   не видно, куда смотреть. */
.live-tab.has-mine .live-tab-name::after {
    content: '';
    display: inline-block; width: 5px; height: 5px; margin-left: 5px;
    border-radius: 50%; background: var(--me); vertical-align: middle;
}
.live-tab-n { font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }

.live-more {
    width: 100%; min-height: 44px; margin-top: 8px;
    border: 1px solid var(--line); border-radius: var(--radius);
    background: transparent; color: var(--txt-3); font: inherit; cursor: pointer;
}
.live-more:active { background: var(--surf-1-mid); }

/* Быстрая ставка: сверху сумма, снизу — сколько вернётся. Второе и есть ответ
   на вопрос «зачем нажимать», поэтому оно на клавише, а не в подсказке. */
.bet-quick { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; padding: 6px 4px; }
.bet-quick-sum { font-size: var(--fs-md); }
.bet-quick-back { font-size: var(--fs-sm); color: var(--gain); }
.bet-quick:disabled .bet-quick-back { color: var(--txt-3); }
/* Коэффициент на минимуме — не тревога, а спокойная подпись. */
.bet-side-odds.is-min { color: var(--txt-3); }
/* Возврат своей ставки — не выигрыш, и подсвечивать его зелёным нельзя. */
.bet-quick-back.is-min { color: var(--txt-3); }

/* ─── подробный итог раунда ──────────────────────────────────────────────
   Владелец: «после игры непонятно вообще ничего, слишком мало времени
   посмотреть результаты». Накладка снизу — тем же приёмом, что и меню ставок:
   подложка гасит фон и ловит нажатие мимо, карточка держит содержимое.
   Снизу, а не посреди экрана: до низа дотягивается большой палец. */
.result {
    position: fixed;
    inset: 0;
    z-index: 62;                 /* выше меню ставок: его открывают ИЗ раунда, а этот итог — после */
    display: flex;
    align-items: flex-end;
    justify-content: center;
    background: var(--veil);
    padding: 12px;
    padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
}
.result.hidden { display: none; }

.result-card {
    width: 100%;
    max-width: 520px;
    /* Выше половины экрана карточка не растёт: за ней идёт кольцо следующего
       раунда, и полностью закрытый экран читается как «игра встала». Длинный
       разбор прокручивается внутри карточки, а не растягивает её. */
    max-height: min(86vh, 720px);
    display: flex;
    flex-direction: column;
    background: var(--surf-1-mid);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow-panel);
    animation: res-in 220ms var(--ease-out) 1;
}
@keyframes res-in { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }

.result-head {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 12px 14px 8px;
    border-bottom: 1px solid var(--line);
}
.result-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 14px;
}
.result-foot {
    position: relative;
    flex: 0 0 auto;
    padding: 10px 14px calc(12px + env(safe-area-inset-bottom, 0px));
    border-top: 1px solid var(--line);
}
/* Мягкая кромка над клавишами — только когда ниже действительно что-то есть
   (класс ставит js/ui/result.js). На узком телефоне разбор обрезается ровно
   посередине строки, и без этой кромки обрез читается как поломка вёрстки,
   а не как «прокрути». Постоянной её делать нельзя: на широком экране она
   гасила бы последнюю строку, под которой ничего нет. */
.result-card.is-scroll .result-foot::before {
    content: "";
    position: absolute;
    left: 0; right: 0; bottom: 100%;
    height: 22px;
    background: linear-gradient(to top, var(--surf-1-mid), transparent);
    pointer-events: none;
}

/* Главное число. Крупнее всего на этом экране — оно и есть ответ на вопрос,
   с которым сюда пришли: «сколько я заработал». */
.res-hero {
    display: flex; flex-direction: column; align-items: center; gap: var(--s1);
    padding: var(--s3) 0 var(--s4);
}
.res-num {
    font-size: 44px; line-height: 1;
    letter-spacing: var(--tr-hero);
    font-variant-numeric: tabular-nums;
    color: var(--txt);
}
.res-num.is-plus  { color: var(--gain); text-shadow: var(--glow-gain); }
/* Минус здесь КРАСНЫЙ — и это осознанное отступление от общего правила «минус
   служебным серым». В карточке под кольцом строка итога стоит между делом, и
   красный там читался бы как авария. Здесь человек пришёл специально за одним
   вопросом — прибыль или убыток, — и просил владелец ровно этого: «чтобы
   ориентировался, где прибыль, а где убыток». Приглушённый, а не сигнальный:
   это плохая новость, а не поломка. */
.res-num.is-minus { color: var(--risk); }
.res-num.is-gold  { color: var(--gold); }

/* Исход красит верх карточки: цвет виден раньше, чем прочитан знак.
   Отдельным слоем с прозрачностью, а не цветом фона напрямую, — токены --risk
   и --gain подобраны для ТЕКСТА (контраст 5-7:1), и залитый ими прямоугольник
   кричит: первый заход выглядел экраном аварии, а не проигранным раундом.
   Прозрачность вместо color-mix: в старых вебвью Telegram его нет. */
.res-hero { position: relative; border-radius: var(--radius); overflow: hidden; }
.res-hero > * { position: relative; }
.result-card.is-win .res-hero::before,
.result-card.is-loss .res-hero::before {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
}
.result-card.is-win .res-hero::before {
    background: linear-gradient(180deg, var(--gain), transparent 78%);
    opacity: .14;
}
.result-card.is-loss .res-hero::before {
    background: linear-gradient(180deg, var(--risk), transparent 78%);
    opacity: .12;
}
.result-card.is-win  .res-under { color: var(--gain); }
.result-card.is-loss .res-under { color: var(--risk); }
.res-under {
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    text-transform: uppercase;
    color: var(--txt-3);
}
.res-badge {
    margin-top: var(--s2);
    padding: 4px 12px;
    border: 1px solid var(--line);
    border-radius: 999px;
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    text-transform: uppercase;
    color: var(--txt-2);
}
.res-badge.is-win {
    border-color: var(--gold);
    color: var(--gold);
    box-shadow: var(--glow-gold);
}

/* Два столбика «вложено — вернулось». Самая быстрая форма ответа на вопрос,
   с которым сюда приходят: разную длину полос видно до чтения цифр. Масштаб
   общий, по большей из двух величин, — иначе полосы врут. */
.res-scale { display: flex; flex-direction: column; gap: var(--s1); margin-top: var(--s3); }
.res-scale-row { display: flex; align-items: center; gap: var(--s2); }
.res-scale-k {
    flex: 0 0 78px;
    font-family: var(--font-ui); font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap); color: var(--txt-3);
}
.res-scale-bar {
    flex: 1 1 auto; min-width: 0; height: 10px;
    border-radius: 3px; background: var(--ink-2); overflow: hidden;
}
.res-scale-fill { display: block; height: 100%; border-radius: 3px; background: var(--txt-3); }
.res-scale-fill.is-in   { background: var(--txt-3); }
.res-scale-fill.is-up   { background: var(--gain); }
.res-scale-fill.is-down { background: var(--risk); }
.res-scale-v {
    flex: 0 0 auto; min-width: 54px; text-align: right;
    font-variant-numeric: tabular-nums; font-size: var(--fs-md); color: var(--txt);
}

.res-sec { margin-top: var(--s4); }
.res-sec-cap { display: block; margin-bottom: var(--s2); color: var(--txt-3); }

/* Призовые места — строками, а не подиумом: подиум человек только что видел,
   и второй его показ ничего не добавит. Здесь нужен список, который читается. */
.res-top { display: flex; flex-direction: column; gap: 2px; }
.res-row {
    display: flex; align-items: center; gap: var(--s2);
    min-height: 38px; padding: 0 var(--s2);
    border-radius: var(--radius);
    background: var(--surf-2-mid);
}
.res-place {
    flex: 0 0 auto; width: 22px; text-align: center;
    font-size: var(--fs-lg); color: var(--txt-2);
}
.res-row.p1 .res-place { color: var(--gold); }
.res-row.p2 .res-place { color: var(--medal-2); }
.res-row.p3 .res-place { color: var(--medal-3); }
.res-nick {
    flex: 1 1 auto; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.res-sum { flex: 0 0 auto; font-variant-numeric: tabular-nums; color: var(--txt); }
.res-row.p1 .res-sum { color: var(--gold); }
/* Своя строка — янтарём: тот же язык, что и «своё» везде в приборе. */
.res-row.is-mine { box-shadow: inset 0 0 0 1px var(--me); }
.res-row.is-mine .res-nick { color: var(--me); }

/* Разбор: подпись слева, число справа, «Итого» отбито чертой. */
.res-lines { display: flex; flex-direction: column; }
.res-line {
    display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3);
    min-height: 30px;
    font-size: var(--fs-md);
    color: var(--txt-2);
}
.res-line-k { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.res-line-v { flex: 0 0 auto; font-variant-numeric: tabular-nums; color: var(--txt-2); }
.res-line-v.is-plus  { color: var(--gain); }
/* Расход красным — той же приглушённой краской, что и главное число: на одном
   экране с полосами это читается как одна система, а не как три разных серых. */
.res-line-v.is-minus { color: var(--risk); }
.res-line.is-total {
    margin-top: var(--s2); padding-top: var(--s2);
    border-top: 1px solid var(--line);
    color: var(--txt);
}
.res-line.is-total .res-line-k { text-transform: uppercase; letter-spacing: var(--ls-cap); font-size: var(--fs-cap); }
.res-line.is-total .res-line-v { font-size: var(--fs-lg); color: var(--txt); }
.res-line.is-total .res-line-v.is-plus  { color: var(--gain); }
.res-line.is-total .res-line-v.is-minus { color: var(--risk); }

/* Полоса долей — та же, что в карточке, но выше: здесь на неё смотрят,
   а не косятся мимоходом, и под ней есть место для подписей. */
.res-bar { height: 12px; margin-top: 0; }
.res-seg { cursor: default; }
.res-legend {
    display: flex; flex-wrap: wrap; gap: var(--s1) var(--s3);
    margin-top: var(--s2);
    font-size: var(--fs-sm); color: var(--txt-3);
}
.res-leg { display: inline-flex; align-items: center; gap: 6px; }
.res-leg-dot { width: 8px; height: 8px; border-radius: 2px; }
.res-facts {
    margin-top: var(--s2);
    font-size: var(--fs-sm); color: var(--txt-3);
    font-variant-numeric: tabular-nums;
}

.res-btns { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s2); }
.res-btn {
    min-height: 44px; border-radius: var(--radius);
    border: 1px solid var(--line); background: var(--surf-2-mid);
    color: var(--txt); font: inherit; cursor: pointer;
}
.res-btn:active { background: var(--surf-1-mid); transform: scale(.985); }
.res-btn.is-main { border-color: var(--me); color: var(--me); }
.res-btn.is-ghost { color: var(--txt-2); }
.res-next {
    margin-top: var(--s2);
    text-align: center;
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    color: var(--txt-3);
    font-variant-numeric: tabular-nums;
    min-height: 14px;
}

/* Пилюля входа. Стоит на месте карточки итога и ровно тогда, когда карточки
   нет: высоты они стоят по очереди, а не вдвоём. */
.res-pill {
    display: flex; align-items: center; gap: var(--s2);
    width: 100%; min-height: 34px; margin-top: var(--s2);
    padding: 0 var(--s3);
    border: 1px solid var(--line); border-radius: var(--radius);
    background: var(--surf-2-mid); color: var(--txt-2);
    font: inherit; text-align: left; cursor: pointer;
}
.res-pill.hidden { display: none; }
.res-pill:active { background: var(--surf-1-mid); }
.res-pill-t {
    flex: 1 1 auto; min-width: 0;
    font-family: var(--font-ui); font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap); text-transform: uppercase;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.res-pill-n { flex: 0 0 auto; font-variant-numeric: tabular-nums; color: var(--txt-2); }
.res-pill-n.is-plus  { color: var(--gain); }
.res-pill-n.is-minus { color: var(--risk); }
.res-pill-x { flex: 0 0 auto; color: var(--txt-3); }

/* Низкий экран (320×568 и все альбомные): главное число и его поля ужимаются.
   Смысл не в красоте — на 568 разбор начинался ниже сгиба карточки, и человек
   видел только герой и призовые места, а пришёл он за разбором. */
@media (max-height: 640px) {
    .res-hero { padding: var(--s2) 0 var(--s3); }
    .res-num { font-size: 34px; }
    .res-badge { margin-top: var(--s1); }
    .res-sec { margin-top: var(--s3); }
    .res-row { min-height: 32px; }
}

@media (prefers-reduced-motion: reduce) {
    .result-card { animation: none; }
}

/* «Играть дальше» — уход выбывшего в новую игру за тем же столом. Занимает
   весь ряд покупки (класс btn-topup даёт grid-column: 1 / -1), своей строки
   высоты у неё нет. Цвет — янтарь: это единственная клавиша ряда, которая
   действует за самого игрока, а не с деньгами; зелёный цвет пополнения тут
   обещал бы приход, которого не будет. */
/* «Уйти и забрать билет» — та же форма, что у «Играть дальше», но тон другой:
   это не приглашение в новую игру, а выход из тупика. Поэтому спокойный
   контур, без зова и без крестика: убирать её незачем, она и так появляется
   только у того, кто сидит один. */
.btn-leave { border-color: var(--line); color: var(--txt-2); }
.btn-leave .btn-sub { color: var(--txt-3); }

.btn-elsewhere .btn-n { color: var(--me); }

/* ─── проверка раунда внутри игры ────────────────────────────────────────
   Владелец: «проверка честности переносит человека на сайт, а надо, чтобы это
   всё было встроено в клиент». Кнопка и список из одиннадцати строк живут в
   панели честности; наружу больше ничего не уводит. */
.btn-check {
    display: block;
    width: 100%;
    min-height: 44px;
    margin-top: var(--s3);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surf-2-mid);
    color: var(--txt);
    font: inherit;
    cursor: pointer;
}
.btn-check:active:not(:disabled) { background: var(--surf-1-mid); transform: scale(.99); }
.btn-check:disabled { opacity: .6; cursor: default; }
.btn-check.hidden { display: none; }

.fair-checks { margin-top: var(--s2); }
.fair-checks.hidden { display: none; }
.fair-check-head {
    margin-bottom: var(--s2);
    font-family: var(--font-ui);
    font-size: var(--fs-cap);
    letter-spacing: var(--ls-cap);
    text-transform: uppercase;
}
.fair-check-head.is-ok  { color: var(--gain); }
.fair-check-head.is-bad { color: var(--risk); }
.fair-check-row {
    display: flex;
    align-items: flex-start;
    gap: var(--s2);
    padding: 3px 0;
    font-size: var(--fs-sm);
    color: var(--txt-2);
    line-height: 1.35;
}
.fair-check-mark { flex: 0 0 auto; width: 14px; text-align: center; }
.fair-check-row.is-ok  .fair-check-mark { color: var(--gain); }
.fair-check-row.is-bad .fair-check-mark { color: var(--risk); }
.fair-check-row.is-bad .fair-check-text { color: var(--txt); }
.fair-check-text { min-width: 0; }
/* Обрыв связи — не приговор площадке, и краска у него служебная, не красная. */
.fair-check-note { font-size: var(--fs-sm); color: var(--txt-3); line-height: 1.4; }

/* «Играть дальше» светится, пока на неё не посмотрели.
   Владелец: «должна светиться кнопка после вылета». Свечение живёт первые
   секунды и гаснет само: пульсирующая до конца раунда клавиша перестаёт быть
   сообщением и становится фоном. Дыхание рамкой, а не тенью на всю клавишу, —
   тень в этом ряду уже занята материалом клавиш. */
.btn-elsewhere.is-call { animation: elsewhere-call 1500ms var(--ease-out) infinite; }
@keyframes elsewhere-call {
    0%, 100% { box-shadow: var(--shadow-key), 0 0 0 0 rgba(217, 180, 74, 0); }
    45%      { box-shadow: var(--shadow-key), 0 0 0 3px rgba(217, 180, 74, .28); }
}

/* Крестик «не сейчас» — внутри клавиши, у правого края. Зона нажатия 44 px
   выходит за его видимый размер: промахнуться по нему и уехать в другую игру
   человек не должен. */
.btn-x {
    position: absolute;
    top: 50%;
    right: 2px;
    transform: translateY(-50%);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    color: var(--txt-3);
    font-size: 15px;
    line-height: 1;
    cursor: pointer;
}
.btn-x:active { color: var(--txt); }

@media (prefers-reduced-motion: reduce) {
    .btn-elsewhere.is-call { animation: none; }
}

/* Строка списка, в которую сейчас переходим. Отклик обязан быть мгновенным:
   рамка «ты здесь» пересчитывается раз в четыре секунды, и до этой пометки
   нажатие не меняло на экране ровно ничего — человек жал ещё раз. */
.live-row.is-going {
    border-color: var(--me);
    opacity: 0.75;
}
.live-row.is-going .live-name::after {
    content: " · переходим…";
    color: var(--me);
    font-size: var(--fs-sm);
}

/* Фаза стола цветом точки. Набор состава и пауза раньше были неотличимы от
   мёртвого стола: точка красилась только для сбора, овертайма и выбывания, а
   стол, куда прямо сейчас имеет смысл сесть, выглядел так же, как пустой. */
.room[data-phase="filling"] .room-dot {
    background: var(--me);
    animation: room-fill-pulse 1400ms ease-in-out infinite;
}
.room[data-phase="pause"] .room-dot { background: var(--txt-3); }
@keyframes room-fill-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: .35; }
}
@media (prefers-reduced-motion: reduce) {
    .room[data-phase="filling"] .room-dot { animation: none; }
}

/* Строка предупреждений списка игр. Появляется только когда есть что сказать. */
.live-ask {
    margin: 0 0 var(--s2);
    font-size: var(--fs-sm);
    color: var(--ot);
    line-height: 1.35;
}
.live-ask.hidden { display: none; }
.live-row.is-ask { border-color: var(--ot); }

/* Причина, по которой покупка не прошла. Лежит ПОВЕРХ ряда клавиш: появление и
   уход не двигают ни пикселя, а высота внизу отнимается у кольца. До этой
   строки отказ в фазе сбора не показывался ни одним словом — только красная
   вспышка на клавише, из которой не узнать ни «не хватило», ни «окно
   закрылось», ни «слишком часто». */
.buy { position: relative; }
.buy-note {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 100%;
    z-index: 6;
    margin: 0 0 var(--s1);
    padding: var(--s1) var(--s2);
    border-radius: var(--radius);
    background: var(--surf-2-top);
    border: 1px solid var(--line);
    color: var(--risk);
    font-size: var(--fs-sm);
    line-height: 1.3;
    text-align: center;
    pointer-events: none;
}
.buy-note.hidden { display: none; }

/* Полоса утекающего окна ставок. Три пикселя по верхнему краю полки и карточки
   меню. Второго ОТСЧЁТА на экране не появляется — на экране появляется та же
   длительность в другой форме: цифры остаются одни, в центре кольца. Нужна она
   потому, что открытое меню сумм накрывает кольцо вуалью, и ровно в те
   секунды, когда человек выбирает сумму, времени он не видит вовсе. */
.bets-time {
    position: absolute;
    top: 0;
    left: 0;
    height: 3px;
    width: 100%;
    transform-origin: left center;
    transform: scaleX(1);
    border-radius: 0 3px 3px 0;
    background: var(--timer, var(--txt-2));
    pointer-events: none;
}
/* Последние секунды — краской риска, той же, какой мигает потолок овертайма. */
.bets-time.is-hot { background: var(--risk); }
#panel-bets { position: relative; }
.bets-menu-card { position: relative; overflow: hidden; }
/* Приём закрыт — полосы нет: пустая полоса читается как «время ещё есть». */
#panel-bets.is-closed .bets-time { display: none; }

/* Клавиша суммы, пока запрос в пути. До этой пометки между нажатием и ответом
   не менялось ровно ничего, и человек на медленной сети жал второй раз. */
.bet-quick.is-busy {
    animation: bet-busy 900ms ease-in-out infinite;
}
@keyframes bet-busy {
    0%, 100% { border-color: var(--line); }
    50%      { border-color: var(--me); }
}
@media (prefers-reduced-motion: reduce) {
    .bet-quick.is-busy { animation: none; border-color: var(--me); }
}

/* Ставка на обоих финалистов: одна честная строка. Пара ставок гарантированно
   в минусе на долю площадки, и две зелёные строки обещали бы доход, которого
   не бывает ни при каком исходе. */
.bet-mine-both { color: var(--txt-2); }
.bet-mine-both .bet-mine-back { color: var(--txt-3); }
/* Проигравшая ставка после расчёта — не серым «как обычно», а видимым нулём. */
.bet-mine-back.is-lost { color: var(--risk); }

/* ─── история игр и ставок в профиле ─────────────────────────────────────
   Единица списка — РАУНД, а не покупка: игра и ставка на финал в одном раунде
   это один вечер человека, и двумя списками эту историю не рассказать. Отсюда
   форма: карточка с шапкой (стол, дата, итог) и одной-двумя строками внутри. */
.hi-tabs {
    display: flex; gap: var(--s1);
    padding: var(--s1) var(--s2) var(--s2);
}
.hi-tab {
    flex: 1 1 0;
    min-height: 32px;
    padding: 0 var(--s2);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: transparent;
    color: var(--txt-3);
    font-family: var(--font-ui);
    font-size: var(--fs-cap); letter-spacing: var(--ls-cap);
    cursor: pointer;
    white-space: nowrap;
}
.hi-tab.is-on {
    color: var(--txt);
    border-color: var(--txt-3);
    background: var(--surf-1-mid);
    box-shadow: inset 0 1px 0 var(--edge-top);
}
.hi-list { display: flex; flex-direction: column; gap: var(--s1); }
.hi-row {
    padding: var(--s2);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    /* Исход красит кромку слева, а не всю карточку: список из восьми залитых
       прямоугольников читается как тревога, а не как история. */
    border-left-width: 3px;
    border-left-color: var(--line);
}
.hi-row.is-gain { border-left-color: var(--gain); }
.hi-row.is-loss { border-left-color: var(--risk); }
/* Идущий раунд: деньги вложены, исхода ещё нет. Ни прибыли, ни убытка. */
.hi-row.is-open { border-left-color: var(--live); }
.hi-live { color: var(--live); font-size: var(--fs-cap); letter-spacing: var(--ls-cap); }
.hi-head {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--s2); margin-bottom: var(--s1);
}
.hi-when { display: flex; flex-direction: column; min-width: 0; }
.hi-room {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-size: var(--fs-md); color: var(--txt);
}
.hi-date { font-size: var(--fs-sm); color: var(--txt-3); }
.hi-delta { flex: 0 0 auto; font-size: var(--fs-lg); color: var(--txt-2); }
.hi-delta.is-gain { color: var(--gain); }
.hi-delta.is-loss { color: var(--risk); }
.hi-body { display: flex; flex-direction: column; gap: 2px; }
.hi-line {
    display: flex; align-items: baseline; justify-content: space-between; gap: var(--s2);
    min-height: 22px;
}
.hi-what { display: flex; align-items: baseline; gap: var(--s1); min-width: 0; }
.hi-kind {
    flex: 0 0 auto;
    font-size: var(--fs-cap); letter-spacing: var(--ls-cap);
    color: var(--txt-3);
}
.hi-res {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-size: var(--fs-sm); color: var(--txt-2);
}
.hi-line.is-won  .hi-res { color: var(--gain); }
.hi-line.is-lost .hi-res { color: var(--txt-3); }
.hi-nums {
    display: flex; align-items: baseline; gap: var(--s1);
    flex: 0 0 auto; font-variant-numeric: tabular-nums; font-size: var(--fs-sm);
}
.hi-in  { color: var(--txt-3); }
.hi-out { color: var(--txt-2); }
.hi-out.is-gain { color: var(--gain); }
.hi-odds { color: var(--txt-3); }
.hi-sub { font-size: var(--fs-sm); color: var(--txt-3); padding-left: 2px; }
.hi-empty { padding: var(--s2); color: var(--txt-3); font-size: var(--fs-sm); }
.hi-pager {
    display: flex; align-items: center; justify-content: center; gap: var(--s2);
    padding: var(--s2) 0 var(--s1);
}
.hi-page-btn {
    min-width: 44px; min-height: 36px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: transparent; color: var(--txt);
    font-family: var(--font-ui); font-size: var(--fs-lg); line-height: 1;
    cursor: pointer;
}
.hi-page-btn:disabled { color: var(--txt-3); opacity: .45; cursor: default; }
.hi-page { color: var(--txt-2); font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }

.cap.is-gain { color: var(--gain); }
.cap.is-loss { color: var(--risk); }
