﻿:root {
    --accent-gold: #fbbf24;
    --accent-gold-dim: rgba(251,191,36,0.35);
    --panel-bg: rgba(10,6,18,0.93);
    --text-main: #f8fafc;
    --border-dim: rgba(255,255,255,0.07);
    --purple-mid: #1e1038;
    --purple-dark: #0d0818;
    --bg-obsidian: #09090b;
    --timer-red: #ef4444;

    /* ===== LIQUID GOLD — the whole UI is built from these two surfaces =====
       GOLD: primary actions and anything currently "on".
       OBSIDIAN: secondary actions, toggles and inputs; ignites to gold when pressed.
       Every button/field/panel below references these, so the look is changed in one place. */
    --gold-3d: linear-gradient(145deg,#F9D976 0%,#EAA82C 30%,#C18825 60%,#7A4B11 85%,#B88428 100%);
    --gold-3d-shadow: inset 0 2.5px 4px rgba(255,255,255,0.7), inset 0 -3px 6px rgba(122,75,17,0.8), 0 8px 20px rgba(0,0,0,0.8);
    --gold-ink: #381D02;          /* text ON a gold surface */
    --gold-text: #D39D3C;         /* text on an obsidian control */
    --gold-text-lit: #FFF2B2;     /* …the same, hovered */
    --gold-line: #A8721A;         /* obsidian control border */
    --gold-line-lit: #F6C147;     /* …the same, hovered / focused */
    --obsidian: linear-gradient(180deg,#1E1E21 0%,#0A0A0C 100%);
    --obsidian-shadow: inset 0 1px 1px rgba(255,255,255,0.05), 0 6px 15px rgba(0,0,0,0.7);
    --field-bg: linear-gradient(180deg,#1a1a1f,#0d0d10);
    /* woven guilloché over obsidian — the material of every setup/lobby/cabinet panel */
    --panel-weave: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22'%3E%3Cpath d='M0 0L22 22M22 0L0 22' stroke='%23FFF8DC' stroke-opacity='0.05' stroke-width='0.6'/%3E%3C/svg%3E");
    /* lit microphone — marks the player who holds the floor (see "WHO IS SPEAKING" at the end) */
    --mic-lit: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23FFD875'%3E%3Cpath d='M12 14.2a3.6 3.6 0 0 0 3.6-3.6V4.9a3.6 3.6 0 0 0-7.2 0v5.7a3.6 3.6 0 0 0 3.6 3.6z'/%3E%3Cpath d='M18.3 9.6a.95.95 0 0 1 1.9 0 8.2 8.2 0 0 1-7.25 8.14v2.16h2.4a.95.95 0 0 1 0 1.9H8.65a.95.95 0 0 1 0-1.9h2.4v-2.16A8.2 8.2 0 0 1 3.8 9.6a.95.95 0 0 1 1.9 0 6.3 6.3 0 0 0 12.6 0z'/%3E%3C/svg%3E");
}
/* --net-sweepx is driven by ONE rAF clock in net.js (netGoldClock), so every gold surface on the
   page catches the same moving light at the same instant. Declared so it can animate smoothly. */
@property --net-sweepx { syntax: '<length-percentage>'; initial-value: -150%; inherits: true; }

*, *::before, *::after { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
body {
    margin: 0; font-family: 'Segoe UI', Georgia, Tahoma, sans-serif;
    font-size: 16px; color: var(--text-main);
    display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
    /* Снизу отступ считается по нижнему ряду кнопок, а не «на глаз».
     * Стояло 40px, а ряд занимает больше — и последняя строка страницы уходила
     * под кнопки насовсем (владелец, 16.09: «то же самое с нижними кнопками»).
     * Переменные заданы ниже, рядом с объяснением. */
    /* ⚠ overflow-x ОСТАЁТСЯ ЗДЕСЬ, НА <body>. НЕ ПЕРЕНОСИТЬ.
     *
     * Он ненадолго переезжал на <html> — ради приклеивания строки раунда
     * (position:sticky), которое в собственном поле прокрутки <body> не
     * работает. Приклеивание оказалось причиной дёрганья и убрано: строка
     * теперь прибита к экрану, как верхние кнопки, и ей поле прокрутки
     * безразлично.
     *
     * Значит и повода трогать эту строку больше нет. Возвращено туда, где
     * стояло годами и работало: меньше отличий от известного рабочего
     * состояния — меньше поводов для новой неожиданности. */
    min-height: 100vh; padding-top: 96px; overflow-x: hidden;
    min-height: 100vh; padding-top: 96px;
    padding-bottom: 78px;   /* ⚠ ЗАПАСНОЙ ВАРИАНТ, НЕ УДАЛЯТЬ: старые браузеры не
                             * понимают calc с env() и переменными — для них строка
                             * ниже недействительна, и без этой они остались бы
                             * ВООБЩЕ без отступа, то есть с той же бедой. */
    padding-bottom: calc(var(--bar-bottom, 64px) + env(safe-area-inset-bottom, 0px) + 14px);
    background: var(--bg-obsidian);
}

/* ===== COSMIC BACKGROUND ===== */
.stars-container {
    position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: -3;
    background: radial-gradient(circle at 50% 45%, #14151c 0%, #0c0d12 45%, var(--bg-obsidian) 100%);
    overflow: hidden;
}
/* Base star grid — checkerboard fill across whole background */
.star-layer {
    position: absolute; top: 0; left: 0; width: 100%; height: 100%;
    background-image: radial-gradient(white 1px, transparent 1.4px);
    background-size: 38px 38px; opacity: 0.16;
}
/* layer1 — offset half a cell => checkerboard pattern with layer2 */
.star-layer.layer1 { background-size: 38px 38px; background-position: 0 0; opacity: 0.22; }
.star-layer.layer2 { background-size: 38px 38px; background-position: 19px 19px; opacity: 0.13; }
.star-layer.layer3 { background-image: radial-gradient(rgba(255,235,180,0.9) 1px, transparent 1.4px); background-size: 95px 95px; background-position: 30px 12px; opacity: 0.18; }

/* Chaotic twinkling stars (JS-generated, random positions/delays).
   POWER: 34 of these run for the whole session on a fixed full-screen layer, so they are the one
   animation that is always on every screen. They therefore move ONLY opacity/transform — the two
   properties the compositor can animate without repainting — and wear their glow as a static
   shadow. Animating box-shadow here kept the GPU awake and warmed the phones. */
.twinkle-star {
    position: absolute; width: 2px; height: 2px; border-radius: 50%;
    background: #fff; opacity: 0.12; pointer-events: none;
    box-shadow: 0 0 6px rgba(255,255,255,0.5), 0 0 13px rgba(255,235,160,0.45);
    animation: twinkle 3.4s ease-in-out infinite;
}
@keyframes twinkle {
    0%, 100% { opacity: 0.10; transform: scale(1); }
    50%      { opacity: 0.95; transform: scale(1.9); }
}
.spotlight {
    position: fixed; top: 0; left: 0; width: 100%; height: 100%;
    background: radial-gradient(circle at 50% 45%, rgba(255,235,160,0.04) 0%, transparent 65%);
    pointer-events: none; z-index: -2;
}
/* 3D mode adds warm floor glow */
body.view-3d .spotlight {
    background:
        radial-gradient(ellipse 80% 35% at 50% 5%, rgba(200,140,40,0.07) 0%, transparent 55%),
        radial-gradient(circle at 50% 45%, rgba(255,235,160,0.05) 0%, transparent 65%);
}

/* ===== ANIMATIONS =====
   Anything that loops FOREVER animates opacity/transform only (composited, no repaint) — see the
   note on .twinkle-star. Short, one-shot animations may still animate shadows. */
@keyframes logo-glow { 0%,100% { opacity:0.9; } 50% { opacity:1; } }
@keyframes joker-glow { 0% { box-shadow:0 0 15px #ef4444; border-color:#ef4444; background:#7f1d1d; } 50% { box-shadow:0 0 40px #fbbf24; border-color:#fbbf24; background:#1c0606; } 100% { box-shadow:0 0 15px #ef4444; border-color:#ef4444; background:#7f1d1d; } }
@keyframes pulse-speaker { 0% { box-shadow:0 0 10px var(--accent-gold); } 100% { box-shadow:0 0 28px var(--accent-gold); border-color:#fff; } }
@keyframes gold-ring-pulse { 0% { box-shadow:0 0 0 0 rgba(251,191,36,0.7),0 0 18px rgba(251,191,36,0.3); } 70% { box-shadow:0 0 0 14px rgba(251,191,36,0),0 0 28px rgba(251,191,36,0.15); } 100% { box-shadow:0 0 0 0 rgba(251,191,36,0),0 0 18px rgba(251,191,36,0.3); } }
@keyframes fadeInUp { from { opacity:0; transform:translateY(10px); } to { opacity:1; transform:translateY(0); } }
@keyframes flyAnimation { 0% { transform:scale(1); } 30% { transform:scale(1.6) translateY(-20px); } 100% { transform:translate(-45vw,-45vh) scale(0.2); opacity:0; } }
@keyframes yula-pulse {
    0%,100% { box-shadow:3px 3px 20px rgba(0,0,0,0.9),0 0 18px rgba(251,191,36,0.35),inset 0 2px 4px rgba(255,255,255,0.14); }
    50%      { box-shadow:3px 3px 28px rgba(0,0,0,0.9),0 0 40px rgba(251,191,36,0.8),inset 0 2px 4px rgba(255,255,255,0.22); }
}

/* ============================================
   LANGUAGE SWITCHER — bottom-right, opens UP
   ============================================ */
/* НИЖНЯЯ ПАНЕЛЬ — ПОЛОСА ПО ВСЕЙ ШИРИНЕ, СОДЕРЖИМОЕ ПО ЦЕНТРУ.
 *
 * Раскладка переехала сюда из кода. Там она ставилась дважды, с !important, и
 * из-за этого подстроить ряд под узкий экран было нечем: любое правило отсюда
 * проигрывало. Владелец, 16.09, со снимка телефона: «кнопка звука не помещается
 * в рядочек» — ряд просто не влезал в ширину экрана, а ужать его было нельзя.
 *
 * Полоса прозрачна и не ловит палец: нажимаются только сами кнопки. Своя
 * подложка тут лишняя — звёздное небо идёт под ними сплошняком. */
#lang-switcher-wrap {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto; z-index: 200000;
    display: flex; flex-direction: row; align-items: center; justify-content: center;
    flex-wrap: nowrap; gap: 7px; width: auto;
    padding: 7px 8px calc(7px + env(safe-area-inset-bottom, 0px));
    background: none; border: none;
    pointer-events: none;
}
#lang-switcher-wrap > * { pointer-events: auto; }
/* Пока открыты правила, в ряду одна кнопка — «← Назад», закрывающая их
   (net-rules.js). !important нужен потому, что видимость кнопок ряда
   скрипты ставят инлайном, а это состояние должно перекрыть любую. */
body.rules-open #lang-switcher-wrap > :not(#net-rules-back) { display: none !important; }

/* ПОРЯДОК В НИЖНЕМ РЯДУ — ЗДЕСЬ, А НЕ В СКРИПТЕ [26.09].
   Раньше он задавался стилем, который вставляется вместе с кнопкой выхода,
   то есть только за столом; на главном меню порядок зависел от того, какая
   кнопка успела создаться первой. Ряд есть на каждом экране — значит и
   правило про него должно быть на каждом.

   Владелец, 26.09: «поменять местами кнопку выход и обновить». */
/* «Назад» с экрана настроек комнаты — первой слева, как стрелка «назад» везде [01.10]. */
#net-admin-back    { order: -2; }
#net-lobby-leave   { order: -2; }   /* выход из лобби — тоже первой слева [01.10] */
#net-sync-btn      { order: -1; }
#lang-switcher-wrap #lang-switcher-btn { order: 0; }
#net-narration-btn { order: 1; }
#net-tv-open       { order: 3; }
#net-tv-share      { order: 4; }
#net-room-code     { order: 5; }
#net-exit-btn      { order: 9; }
/* Выход из аккаунта — там же, где выход из-за стола: справа. Вместе они
   не показываются, каждая кнопка знает свой экран. */
#net-logout-btn    { order: 9; }

/* ОТСТУП СВЕРХУ — ТОЛЬКО ПОД ПАНЕЛЬ СТОЛА.
   body.padding-top в 96px (88 на телефоне) существует ради #top-game-header.
   Панель показывается только за столом, а отступ стоял всегда: на телефоне это
   два сантиметра пустоты над карточкой [владелец, 26.09]. Класс gos-hdr вешает
   netTopPad по факту видимости панели. */
/* .gos-bar-on — полоса установки приложения, под неё отступ тоже нужен:
   исключаем её, иначе карточка уедет под полосу. */
body:not(.gos-hdr):not(.gos-bar-on) { padding-top: calc(8px + env(safe-area-inset-top, 0px)); }

/* ── ЭКРАН РОВНО В ОКНО: ГЛАВНОЕ МЕНЮ (nd-lock) И ВХОД В КОМНАТУ (nc-lock) ──
   [владелец, выкладка 158: «экран скроллится и застревает»]. Эти экраны сами
   занимают окно целиком (высоту считает net.js) и сами держат место под нижний
   ряд кнопок. Обычные отступы body — 8 px сверху и 78 px снизу — ложились
   поверх: документ выходил выше окна на 86 px (на 136 с полосой установки).
   Колесом его не прокрутить (overflow:hidden), но выделение текста или фокус
   на поле сдвигали страницу — и она застревала, а внизу открывался обрыв
   подложки. Под замком у body своих отступов нет; сверху остаётся только
   полоса установки, если она показана (--gos-bar, install.js), и её высоту
   экран вычитает из своей. Вес селектора как у правила выше (0,2,1) — стоит
   после него и потому побеждает. */
body.nd-lock:not(.gos-bar-on), body.nc-lock:not(.gos-bar-on) { padding-top: 0; }
body.nd-lock, body.nc-lock { padding-bottom: 0; min-height: 0; }

/* Поле кода комнаты со значком сканера — те же классы, что на регистрации. */
.nsg-field.has-qr > input:not(.nsg-code) { padding-right: 58px; }

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

/* ⚠ У КНОПКИ ЯЗЫКА БОЛЬШЕ НЕТ СВОЕГО ВИДА — И ЭТО НАМЕРЕННО.
   Здесь стояло полное описание: свой фон, своя рамка, свои отступы, свой
   размер шрифта. Ровно из-за него ряд и не сходился: соседи одевались «как
   кнопка языка» — кто копированием класса, кто отдельным правилом, — и каждый
   получался чуть другим. Теперь язык такой же житель ряда, как все остальные,
   и описан общим правилом ниже. Ничего своего ему не нужно. */

/* ════════════════════════════════════════════════════════════════════════════
   НИЖНИЙ РЯД: ТОЖЕ ОДНА КНОПКА, ПОВТОРЁННАЯ НЕСКОЛЬКО РАЗ

   Владелец, 16.09: «внизу посмотри на кнопки, что это такое».

   И правда: «Рус» была одной высоты, значок обновления — квадратиком поменьше,
   код стола — широкой пилюлей, «Выход» — ещё чуть выше остальных и вдобавок
   сдвинут вниз собственным отступом. Причина та же, что была наверху: у ряда
   не было ОБЩЕЙ высоты. Каждая кнопка росла по своему содержимому
   (min-height + height:auto), а содержимое у всех разное — флаг, значок, пять
   букв кода, слово «Выход».

   Здесь задана общая высота и общий вид для ВСЕГО, что лежит в нижнем ряду,
   включая код стола (он не кнопка, а надпись, и раньше жил по своим правилам).
   Меню выбора языка исключено: это всплывающий список, а не кнопка ряда.
   ════════════════════════════════════════════════════════════════════════════ */
#lang-switcher-wrap > *:not(.lang-menu) {
    height: 34px; min-height: 34px; margin: 0; padding: 0 11px;
    display: inline-flex; align-items: center; justify-content: center; gap: 5px;
    border-radius: 10px; font-size: 12px; font-weight: 700; line-height: 1;
    white-space: nowrap; box-sizing: border-box; flex: 0 0 auto;
    background: linear-gradient(145deg, rgba(30,16,56,0.95), rgba(15,8,28,0.95));
    border: 1px solid rgba(251,191,36,0.28);
    box-shadow: 0 2px 12px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.07);
    color: var(--accent-gold);
    font-family: inherit; cursor: pointer; touch-action: manipulation;
    transition: border-color 0.2s, box-shadow 0.2s, transform 0.15s;
}
#lang-switcher-wrap > *:not(.lang-menu):hover { border-color: var(--accent-gold); box-shadow: 0 2px 18px rgba(251,191,36,0.3); }
/* НАЖАТИЕ ВИДНО. Это единственное выделение, которое осталось, — и оно должно
 * быть ЗАМЕТНЫМ.
 *
 * Владелец, 16.09: «ты забрал уже все подсветки, даже когда нажимаешь, кнопка
 * не выделяется — нажал ты на неё или нет, не видно».
 *
 * Справедливо: убирая постоянное свечение, я оставила от нажатия только
 * уменьшение на шесть процентов. На телефоне палец закрывает кнопку целиком, и
 * увидеть это невозможно в принципе. Теперь при нажатии кнопка светлеет,
 * рамка наливается золотом и появляется ореол — видно даже из-под пальца.
 * Правило одно на обе панели, чтобы отклик был всюду одинаковым. */
#top-game-header > *:active,
#lang-switcher-wrap > *:not(.lang-menu):active {
    transform: scale(0.94);
    border-color: var(--accent-gold);
    background: linear-gradient(145deg, rgba(74,48,14,0.98), rgba(42,26,6,0.98));
    box-shadow: 0 0 18px rgba(251,191,36,0.6), inset 0 1px 0 rgba(255,255,255,0.12);
    color: #ffe9a8;
}

/* ЗНАЧКИ БЕЗ ПОДПИСИ — КВАДРАТНЫЕ, И ПОДПИСЬ У НИХ СКРЫТА НАСОВСЕМ.
 *
 * Владелец, 16.09, со снимка: «кнопка маленькая, а слово Обновить вылезло за
 * кнопку». Так и было: ширину задали в 34 точки, а слово внутри осталось и
 * торчало наружу. Прятало его отдельное правило, которое ставится ВМЕСТЕ С
 * КНОПКОЙ ВЫХОДА, то есть только за столом, — а на экране входа выхода ещё
 * нет, и слово вылезало. Прячем здесь: одно место, все экраны. */
#net-sync-btn, #net-narration-btn { width: 34px; padding: 0; }
#net-sync-btn .lang-label { display: none; }

/* ⚠ У «ВЫХОДА» БОЛЬШЕ НЕТ ПОСТОЯННОЙ ПОДСВЕТКИ.
 * Здесь ему задавалась плотная золотая рамка — «подсказка о важности». На
 * экране это читается ровно наоборот: будто кнопку уже нажали и она включена
 * (владелец, 16.09: «постоянно светится, ощущение что её нажали»). Теперь она
 * выглядит как все, а подсвечивается только под пальцем — общим правилом
 * :hover/:active выше. */

/* ⚠ ЗДЕСЬ ЖИЛ КЛАСС .net-lux-ctl — УБРАН ВМЕСТЕ СО ВСЕМИ, КТО ЕГО НОСИЛ.
   Он появился 11.09, чтобы служебные кнопки перестали одеваться копированием
   чужого className и не выходили белыми пилюлями. Задачу свою он выполнил, но
   стал ТРЕТЬИМ описанием одного и того же: у ряда был свой вид, у класса свой,
   и у каждой кнопки ещё по правилу сверху. Ряд от этого и был рваным.
   Теперь вид нижнего ряда задан ОДНИМ правилом — выше, рядом с кнопкой языка,
   и распространяется на всех его жителей сразу, включая те, что создаются из
   кода. Одеваться «как соседняя кнопка» больше не нужно никому. */
.lang-arrow { font-size: 9px; transition: transform 0.2s; }
#lang-switcher-wrap.open .lang-arrow { transform: rotate(180deg); }
#lang-flag { font-size: 18px; line-height: 1; }
.lang-menu {
    display: none; position: absolute; bottom: calc(100% + 8px); right: 0;
    background: rgba(10,6,20,0.98); border: 1px solid rgba(251,191,36,0.25);
    border-radius: 10px; min-width: 165px;
    box-shadow: 0 -8px 40px rgba(0,0,0,0.7);
    -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
    animation: fadeInUp 0.45s ease; overflow: hidden;   /* было 0.18 [01.10]: смену экрана не успевали заметить */
}
.lang-menu.visible { display: block; }
.lang-menu-inner {
    max-height: 280px; overflow-y: auto; padding: 4px 0;
    scrollbar-width: thin; scrollbar-color: var(--accent-gold-dim) transparent;
}
.lang-menu-inner::-webkit-scrollbar { width: 4px; }
.lang-menu-inner::-webkit-scrollbar-thumb { background: var(--accent-gold-dim); border-radius: 4px; }
.lang-option {
    display: flex; align-items: center; gap: 10px; padding: 11px 14px;
    font-size: 14px; font-weight: 600; color: #cbd5e1; cursor: pointer;
    transition: background 0.15s, color 0.15s; border: none; background: none;
    width: 100%; text-align: left; font-family: inherit;
}
.lang-option:hover { background: rgba(251,191,36,0.1); color: var(--accent-gold); }
.lang-option.active { color: var(--accent-gold); font-weight: 800; }
.lang-opt-flag { font-size: 20px; flex-shrink: 0; }
.lang-opt-name { flex: 1; }
.lang-opt-check { font-size: 12px; color: var(--accent-gold); }

/* ============================================
   GAME HEADER — solid bg (no overlap with title)
   ============================================ */
#top-game-header {
    position: fixed; top: 0; left: 0; right: 0; height: 74px;
    background: linear-gradient(180deg, #0a0610 0%, #0a0610 88%, rgba(10,6,16,0) 100%);
    display: none; align-items: center; justify-content: center;
    padding: 0 8px; z-index: 200000;
    -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
    border-bottom: 1px solid rgba(251,191,36,0.12);
    gap: 6px;
}
/* ════════════════════════════════════════════════════════════════════════════
   ВЕРХНЯЯ ПАНЕЛЬ: ОДНА КНОПКА, ПОВТОРЁННАЯ ПЯТЬ РАЗ

   Владелец, 16.09, со снимком: «кнопки вверху — это какой-то хаос, а не кнопки
   в одном стиле. Все разнобойные. Должны быть все одинаковые, одинакового
   цвета, одинаковая подсветка».

   ОДНО ОПИСАНИЕ НА ВСЕХ, И ОНО ЗДЕСЬ. Раньше вид кнопок задавался в двух
   местах — тут по именам каждой, и ещё раз из net.js поверх. Два описания
   одного и того же неминуемо расходятся: там правишь, здесь забываешь, и ряд
   разъезжается. Теперь семья описана один раз, селектором по панели:
   «всё, что лежит в верхней панели, выглядит так». Появится шестая кнопка —
   попадёт в семью сама, дописывать нечего.

   ПОЧЕМУ ИМЕННО ПО ПАНЕЛИ, А НЕ ПО ИМЕНАМ. Кнопка «Правила» создаётся из
   net.js, её имени в этом файле взяться неоткуда. Перечисление по именам
   поэтому и не работало: она выпадала из семьи и выглядела чужой.
   ════════════════════════════════════════════════════════════════════════════ */
#top-game-header > * {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    /* ШИРИНА: ДЕЛИМ ПОРОВНУ, НО ДО ПОТОЛКА.
     *
     * Владелец, 16.09, со снимка компьютера: «ты растянула кнопки на весь
     * экран, это бред. Они должны быть строго по центру и такие же, как на
     * телефоне».
     *
     * Он прав: одно «поровну» без потолка ведёт себя по-разному на разных
     * экранах. На телефоне пятеро делят 390 точек — выходит по 73, красиво. На
     * компьютере те же пятеро делят 1400 — и каждая кнопка раздувается до 270
     * точек во всю ширину окна. Это не одинаковый вид, а одинаковая ФОРМУЛА,
     * дающая разный результат.
     *
     * Потолок в 118 точек убирает разницу: на компьютере все пятеро упираются
     * в него и панель собирается компактной группой по центру (панель сама
     * центрирует, justify-content:center). На телефоне до потолка не доходит —
     * там по-прежнему делят поровну. Вид один и тот же везде.
     *
     * 118 — это «БАЛЛЫ» и «ПРАВИЛА» со свободным полем по краям; уже — начнут
     * обрезаться словами, шире — снова поползут в стороны. */
    flex: 1 1 0; min-width: 0; max-width: 118px;
    height: 54px; margin: 0;   /* margin:0 — у <button> он свой в Safari, и ряд съезжал */
    gap: 2px; border-radius: 12px; cursor: pointer; padding: 5px 3px;
    background: linear-gradient(145deg, rgba(30,16,56,0.95), rgba(15,8,28,0.95));
    border: 1px solid rgba(251,191,36,0.28);
    box-shadow: 0 2px 12px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.07);
    color: var(--accent-gold); font-size: 17px; font-weight: 700; line-height: 1;
    transition: border-color 0.2s, box-shadow 0.2s, transform 0.15s;
    touch-action: manipulation; font-family: inherit; overflow: hidden;
    position: static; box-sizing: border-box;
}
/* Порядок слева направо: карта · вид стола · пауза · правила · баллы.
 * Задан через order, потому что «Правила» вставляются из net.js и в разметке
 * встают последними — без этого они оказывались между паузой и баллами. */
#my-secret-card-box { order: 0; }
#btn-view-toggle    { order: 1; }
#btn-pause          { order: 2; }
#net-help-btn       { order: 3; }
#my-score-box       { order: 4; }

/* Подписи под значками — тоже одни на всех. */
#top-game-header small, #top-game-header span {
    font-size: 9px; font-weight: 700; letter-spacing: 0.2px; text-transform: uppercase;
    color: rgba(251,191,36,0.75); margin: 0; display: block; line-height: 1.05;
    white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis;
}
.secret-front { font-size: 12px; }
#top-game-header > *:hover { border-color: var(--accent-gold); box-shadow: 0 2px 18px rgba(251,191,36,0.3); }
/* Нажатие описано ОДНИМ правилом на обе панели — выше, рядом с нижним рядом. */

/* ⚠ ПОСТОЯННОЙ ПОДСВЕТКИ СОСТОЯНИЯ БОЛЬШЕ НЕТ НИ У ОДНОЙ КНОПКИ.
 *
 * Владелец, 16.09: «3D и выход светятся сразу, как только открывается кабинет.
 * Подсветку забрать. Если на неё нажали — тогда выделяется».
 *
 * Он прав, и я дважды прошла мимо этого. Сначала «3D» красилась в синий, а
 * пауза в зелёный — я заменила чужие цвета на яркое золото и сочла задачу
 * решённой. Но беда была не в ЦВЕТЕ, а в самом факте: горящая кнопка посреди
 * спокойного ряда читается как нажатая, каким бы цветом ни горела. Человек
 * открывает стол — и видит две «включённые» кнопки, которых не трогал.
 *
 * Состояние при этом не теряется и показывать его нечем не стало: у кнопки
 * вида меняется и значок, и подпись (2D ↔ 3D), у паузы — значок и слово
 * («Пауза» ↔ «Продолжить»). Это и есть индикатор, причём читаемый словами, а
 * не догадкой по свечению.
 *
 * Выделение осталось ровно одно и одинаковое для всех кнопок обеих панелей:
 * под пальцем (:hover и :active). */

.secret-card-inner { position:relative; width:100%; height:100%; transition:transform 0.5s cubic-bezier(0.4,0,0.2,1); transform-style:preserve-3d; }
.secret-card-inner.revealed { transform:rotateY(180deg); }
.secret-face { position:absolute; width:100%; height:100%; -webkit-backface-visibility:hidden; backface-visibility:hidden; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:2px; border-radius:10px; font-size:18px; color:var(--accent-gold); background:transparent; }
.secret-front { transform:rotateY(180deg); font-size:15px; font-weight:900; color:#fff; text-align:center; line-height:1.2; padding:2px; }

/* ============================================
   CONTAINERS / SCREENS
   ============================================ */
.container {
    background: var(--panel-bg);
    -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
    padding: 24px 22px; border-radius: 14px;
    box-shadow: 0 20px 60px rgba(0,0,0,0.85), 0 0 0 1px var(--border-dim);
    width: 95%; max-width: 560px; text-align: center;
    position: relative; animation: fadeInUp 0.45s ease; margin-top: 8px;
}
.container::before { content:''; position:absolute; top:0; left:0; right:0; height:1px; background:linear-gradient(90deg,transparent,var(--accent-gold),transparent); border-radius:14px 14px 0 0; }
.screen { display: none; }
.screen.active { display: block; }
/* Setup / lobby / cabinet panels are one material: obsidian under a woven guilloché. The game
   screens deliberately opt out below (they float on the star field, no panel box). */
#screen-auth, #screen-login, #screen-topup, #screen-payment, #screen-join-type,
#screen-join-code, #screen-netlobby, #screen-admin, #screen-roles, #screen-lobby {
    background: var(--panel-weave), linear-gradient(180deg,#141416 0%,#080809 100%);
    border: 1px solid rgba(255,255,255,0.05);
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.15), 0 20px 40px rgba(0,0,0,0.8);
}
h1 { color: var(--accent-gold); margin-bottom: 6px; font-size: 28px; letter-spacing: 0.5px; }
h2 { color: #94a3b8; font-size: 18px; margin-top: 0; margin-bottom: 16px; }
/* display face for every title on the site */
.lux h1, #screen-auth h1, #screen-login h1, .container.screen h2, .logo-brand-genesis, .logo-brand-life {
    font-family:'Cormorant Garamond','Iowan Old Style','Palatino Linotype',Palatino,Georgia,serif; letter-spacing:0.02em;
}

/* LOGO + main title — poured gold that catches the same travelling light as the buttons */
.logo-brand { text-align:center; margin-bottom:18px; padding:14px 18px; border:1px solid rgba(251,191,36,0.2); border-radius:6px; background:linear-gradient(135deg,rgba(251,191,36,0.03),rgba(251,191,36,0.08),rgba(251,191,36,0.03)); position:relative; }
.logo-brand::before { content:''; position:absolute; top:0; left:0; right:0; height:1px; background:linear-gradient(90deg,transparent,var(--accent-gold),transparent); }
/* ⚠ Заголовок экрана входа в комнату добавлен В ТОТ ЖЕ СПИСОК, а не
   описан отдельно: иначе два одинаковых с виду заголовка разойдутся
   по кеглю при первой же правке. */
h1[data-i18n="auth_title"], #screen-join-code .nc-title, #screen-join-code .nc-cap,
.nas-word.on,
.logo-brand-genesis, .logo-brand-solar, .logo-brand-life {
    background:linear-gradient(120deg,#FDF2B2 0%,#F39C12 30%,#FFF8D1 50%,#F39C12 70%,#875A12 85%,#FDF2B2 100%);
    background-size:200% auto; background-position:calc(-1 * var(--net-sweepx,150%)) center;
    -webkit-background-clip:text; background-clip:text;
    -webkit-text-fill-color:transparent; color:transparent;
}
.logo-brand-genesis { font-family:Georgia,serif; font-size:10px; letter-spacing:7px; text-transform:uppercase; }
.logo-brand-solar { font-family:Georgia,serif; font-size:24px; letter-spacing:5px; font-weight:700; text-transform:uppercase; line-height:1.25; animation:logo-glow 3.5s ease-in-out infinite; }
.logo-brand-life { font-family:Georgia,serif; font-size:10px; letter-spacing:9px; text-transform:uppercase; }

/* INPUTS — obsidian field, gold frame, gold focus ring.
   ⚠ ТИПЫ ПЕРЕЧИСЛЕНЫ ПОИМЁННО, И НОВЫЙ ТИП НАДО ДОПИСЫВАТЬ СЮДА. Поле почты
   (type="email") добавили в форму и забыли добавить в этот список — браузер
   нарисовал его по умолчанию: белый прямоугольник посреди тёмной формы с
   золотыми рамками [владелец, 18.08, со снимком экрана]. */
input[type="text"], input[type="tel"], input[type="email"], input[type="number"],
input[type="password"], select, textarea {
    width:100%; padding:9px 11px; margin:6px 0; border-radius:10px;
    border:1.5px solid rgba(168,114,26,0.45); background:var(--field-bg); color:#F5E6C0;
    font-size:15px; font-family:inherit; touch-action:manipulation; outline:none;
    transition:border-color 0.15s, box-shadow 0.15s;
}
input:focus, select:focus, textarea:focus { border-color:var(--gold-line-lit); box-shadow:0 0 0 3px rgba(246,193,71,0.16); }

/* ТЕЛЕФОН: КОД СТРАНЫ И НОМЕР — ОДНА СТРОКА, КАК В TELEGRAM.
   Код прижат влево и занимает ровно столько, сколько нужно ему: номер длиннее и
   должен получить всю оставшуюся ширину. `flex:0 0 auto` у выбора и `flex:1` у
   номера — иначе на узком экране выбор растягивается и номер уезжает под него. */
.phone-row { display:flex; gap:8px; align-items:center; margin:6px 0; }
.phone-row select {
    flex:0 0 auto; width:auto; margin:0; padding-right:8px;
    font-variant-numeric:tabular-nums;
}
.phone-row input[type="tel"] { flex:1; min-width:0; margin:0; }
input::placeholder, textarea::placeholder { color:#7a725f; }
select option { background:#0d0d10; color:#F5E6C0; }
label { color:var(--gold-text); font-weight:600; }
input.filled, textarea.filled { border-color:rgba(251,191,36,0.5); box-shadow:0 0 8px rgba(251,191,36,0.18); }
.input-row { display:flex; align-items:center; justify-content:space-between; gap:12px; margin:6px 0; color:#fff; }
.input-row input[type="radio"], .input-row input[type="checkbox"] { width:22px; height:22px; cursor:pointer; accent-color:var(--accent-gold); flex-shrink:0; }

/* ============================================
   BUTTONS — two surfaces, reused everywhere
   GOLD     = the primary action, and any toggle that is currently ON.
   OBSIDIAN = secondary actions and toggles at rest; they ignite to gold when pressed.
   Add a family to the selector lists below and it inherits the whole look.
   ============================================ */
.btn:not(.btn-dim):not(.btn-green), .btn-gold-primary, #net-startnow,
.btn.btn-dim:active, #netlobby-bots:active, .btn-secondary:active, .btn-secondary.active,
.table-btn.active, .cloth-btn.active, .joker-btn.active, .lux-btn.active {
    background:var(--gold-3d); box-shadow:var(--gold-3d-shadow);
    color:var(--gold-ink); border-color:transparent; font-weight:800;
    text-shadow:0 1px 1px rgba(255,255,255,0.4);
}
/* the travelling highlight — one clock, so every gold surface flashes together */
.btn:not(.btn-dim):not(.btn-green)::after, .btn-gold-primary::after, #net-startnow::after,
.table-btn.active::after, .cloth-btn.active::after, .joker-btn.active::after, .lux-btn.active::after {
    content:''; position:absolute; top:0; left:var(--net-sweepx,-150%);
    width:60%; height:100%; z-index:1; pointer-events:none; transform:skewX(-25deg);
    background:linear-gradient(90deg,rgba(255,255,255,0) 0%,rgba(255,255,255,0.35) 50%,rgba(255,255,255,0) 100%);
}
#netlobby-bots::after { content:none; }   /* "fill with bots" stays calm until pressed */

.btn.btn-dim, #netlobby-bots, .btn-secondary,
.table-btn, .cloth-btn, .joker-btn, .lux-btn {
    background:var(--obsidian); border:1.5px solid var(--gold-line); color:var(--gold-text);
    box-shadow:var(--obsidian-shadow);
}
.btn.btn-dim:hover, #netlobby-bots:hover, .btn-secondary:hover,
.table-btn:hover, .cloth-btn:hover, .joker-btn:hover, .lux-btn:hover {
    color:var(--gold-text-lit); border-color:var(--gold-line-lit);
}

/* shared box: geometry and behaviour, no colour */
.btn, .btn-gold-primary, .btn-secondary, .table-btn, .cloth-btn, .joker-btn, .lux-btn {
    position:relative; overflow:hidden; cursor:pointer; font-family:inherit; touch-action:manipulation;
    transition:color 0.2s, border-color 0.2s, filter 0.25s, transform 0.15s;
}
.btn > *, .btn-gold-primary > *, .table-btn > *, .cloth-btn > *, .joker-btn > *, .lux-btn > * { position:relative; z-index:2; }
.btn {
    width:100%; margin-top:10px; padding:13px 18px; border:none; border-radius:12px;
    font-size:15px; font-weight:800; letter-spacing:0.5px; text-transform:uppercase;
}
.btn:not(.btn-dim):not(.btn-green):hover { filter:brightness(1.06) contrast(1.02); }
.btn:active { transform:translateY(1.5px); }
/* the money button is the ONE non-gold action — it must stay unmistakable */
.btn-green { background:linear-gradient(135deg,#15803d,#22c55e); color:#fff; box-shadow:0 4px 14px rgba(22,163,74,0.35); }
.btn-green:hover { box-shadow:0 4px 20px rgba(22,163,74,0.5); }

/* ADMIN / CABINET */
.admin-section { background:rgba(255,255,255,0.015); border:1px solid rgba(168,114,26,0.16); border-radius:14px; padding:14px; margin-bottom:12px; text-align:left; }
.admin-section-label, .lux-head { font-size:10px; font-weight:800; letter-spacing:0.04em; text-transform:uppercase; color:#F2C75E; margin-bottom:10px; }
.lux-lock { color:#8f887a; }
.cloth-picker { display:flex; gap:6px; flex-wrap:wrap; }
.table-picker, .joker-picker { display:flex; gap:8px; }
.table-btn, .cloth-btn, .joker-btn, .lux-btn { flex:1; border-radius:10px; }
.table-btn { padding:11px 8px; font-size:13px; font-weight:800; letter-spacing:0.3px; }
.cloth-btn { min-width:66px; padding:9px 5px; font-size:11px; font-weight:700; display:flex; flex-direction:column; align-items:center; gap:4px; }
.joker-btn { padding:9px; font-size:12px; font-weight:700; }
.cloth-btn.active { transform:scale(1.05); }
.cloth-btn.active .cloth-swatch { border-color:#fff; box-shadow:0 0 6px rgba(255,255,255,0.6); }
.cloth-swatch { width:28px; height:16px; border-radius:4px; border:1px solid rgba(255,255,255,0.1); }
.swatch-blue { background:radial-gradient(circle,#1e3a8a,#0c1a4a); }
.swatch-green { background:radial-gradient(circle,#064e3b,#022c1e); }
.swatch-red { background:radial-gradient(circle,#881337,#3f0d1a); }
.swatch-dark { background:radial-gradient(circle,#1f2937,#09090b); }

/* PAYMENT */
.payment-box { background:rgba(10,6,22,0.85); border:1px solid #475569; padding:18px; border-radius:10px; margin:12px 0; text-align:left; }
.pay-balance { color:#22c55e; font-size:17px; font-weight:bold; margin:0 0 8px 0; }
.pay-old { text-decoration:line-through; color:#94a3b8; font-size:15px; margin:0 0 4px 0; }
.pay-promo { color:#fbbf24; font-size:22px; font-weight:bold; margin:0 0 4px 0; }
.pay-hr { border:none; border-top:1px solid #334155; margin:10px 0; }
.pay-rest { color:#cbd5e1; font-size:15px; margin:0; }

/* CARDS */
.cards-grid { display:flex; flex-wrap:wrap; justify-content:center; gap:10px; margin:18px 0; }
.card { position:relative; width:62px; height:90px; cursor:pointer; perspective:1000px; touch-action:manipulation; }
.card.taken { opacity:0; transform:scale(0.3); pointer-events:none; transition:0.35s; }
.card-inner { position:relative; width:100%; height:100%; transition:transform 1.8s cubic-bezier(0.4,0,0.2,1); transform-style:preserve-3d; }
.card.facedown .card-inner { transform:rotateY(180deg); }
.card-face { position:absolute; width:100%; height:100%; -webkit-backface-visibility:hidden; backface-visibility:hidden; display:flex; flex-direction:column; align-items:center; justify-content:center; border-radius:8px; font-size:11px; font-weight:bold; border:1px solid #475569; }
.card-role { background:linear-gradient(135deg,#1e1030,#120820); color:#fff; gap:3px; }
.card-role span { font-size:24px; }
/* card back = the Golden Phoenix (replaced the old seed-of-life emblem) */
.card-cover { background:#0d0818 url('assets/logo-phoenix.svg') no-repeat center 46% / 78%; border-color:var(--accent-gold); transform:rotateY(180deg); }
.card.fly-to-header { position:fixed; z-index:10000; animation:flyAnimation 2s forwards cubic-bezier(0.25,1,0.5,1); }

/* ============================================
   GAME TABLE — labels OUTSIDE, more top margin
   ============================================ */
.table-circle {
    position:relative; z-index:5;
    height: min(76vh, 660px); width:auto; aspect-ratio:3 / 4; max-width:94vw;
    margin:16px auto 24px auto;
    box-sizing:border-box; overflow:visible;
    /* ⚠ ЗДЕСЬ БЫЛ `filter: drop-shadow(...)`, И ЕГО НЕЛЬЗЯ ВОЗВРАЩАТЬ [24.09].
       CSS-фильтр на этом элементе заставляет браузер отрисовать ВСЁ поддерево —
       сукно, обод, надписи, кружки мест, плашки с именами — в offscreen-буфер и
       показать растровый слепок вместо вектора. Слепок пересобирается при каждой
       смене состояния (выделение игрока, смена фазы, переключение вида), отчего
       стол «то чёткий, то мыло». Тень теперь рисует сам SVG отдельной фигурой под
       ободом (table.js, filter id="drop_…"): растеризуется только она, остальное
       остаётся вектором. Сторожит это tests/table-sharpness.test.js. */
}
.table-circle .tbl-svg { position:absolute; top:0; left:0; width:100%; height:100%; z-index:0; pointer-events:none; overflow:visible; }
body.table-neon .table-circle .tbl-led { animation:led-pulse 3.2s ease-in-out infinite; transform-origin:center; }
@keyframes led-pulse { 0%,100%{opacity:0.75;} 50%{opacity:1;} }

/* Table rim/felt/text now drawn by the SVG in table.js (no CSS border boxes) */
/* view-3d tilts the felt but keeps the names facing the camera at full size (like the 3D table).
   preserve-3d is REQUIRED so the label rotateX(-45deg) counter-rotation actually works.

   ⚠ ЗДЕСЬ СТОЯЛ `filter: drop-shadow(...)` НА .tbl-svg, И ЕГО ТОЖЕ БОЛЬШЕ НЕТ [24.09].
   Он появился как обход: фильтр на наклонённом элементе сбрасывает transform-style в
   `flat`, поэтому тень перенесли с контейнера на сам SVG. Но фильтр на SVG растеризует
   весь холст стола целиком — ровно то, на что жаловались («цифры и геометрия плывут, а
   плашки чёткие»: плашки лежат ПОВЕРХ и в растр не попадали). Тень теперь живёт внутри
   SVG отдельной фигурой (table.js), и CSS-фильтров на столе не осталось вовсе — вместе
   с ними ушла и причина обхода. */
body.view-3d .table-circle { transform:perspective(1100px) rotateX(45deg) scale(0.92); transform-origin:center 60%; transform-style:preserve-3d; filter:none; }
body.view-3d .outer-player-label { transform:translate(-50%,-50%) rotateX(-45deg) scale(1.06); }

/* YULA — now the SPIN BUTTON (pulsing, clickable) */
.yula {
    position:absolute; top:50%; left:50%; width:52px; height:52px; z-index:30;
    border-radius:50%; transform:translate(-50%,-50%);
    background:
        radial-gradient(circle at 36% 26%, #fffdf0 0%, #ffe88a 18%, #FFD700 42%, #c79a1e 72%, #6e500a 100%);
    border:2px solid #fff3c4;
    box-shadow:0 10px 22px rgba(0,0,0,0.9), 0 0 22px rgba(255,205,40,0.6),
        inset 0 3px 7px rgba(255,255,255,0.9), inset 0 -6px 11px rgba(110,80,10,0.85), inset 0 0 0 1px rgba(255,255,255,0.25);
    will-change:transform; transition:transform 0.4s ease, box-shadow 0.4s ease;
}
/* bright specular highlight (polished chrome-gold) */
.yula::before { content:''; position:absolute; top:14%; left:22%; width:38%; height:26%; border-radius:50%;
    background:radial-gradient(circle at 40% 40%, rgba(255,255,255,0.95), rgba(255,255,255,0) 72%); pointer-events:none; }
.yula-core { position:absolute; top:50%; left:50%; width:42%; height:42%; transform:translate(-50%,-50%); border-radius:50%;
    background:radial-gradient(circle at 38% 34%, #fff7d0, #FFD700 55%, #8a6a14 100%);
    box-shadow:inset 0 1px 2px rgba(255,255,255,0.8), inset 0 -2px 4px rgba(90,65,8,0.8), 0 1px 3px rgba(0,0,0,0.5); }
.yula-clickable { cursor:pointer; animation:yula-pulse 1.4s ease-in-out infinite; }
/* gold 3D arrow / pointer of the spinning-top */
.yula-arrow { position:absolute; top:-15px; left:50%; transform:translateX(-50%); width:0; height:0; z-index:31;
    border-left:8px solid transparent; border-right:8px solid transparent; border-bottom:18px solid #FFD700;
    filter:drop-shadow(0 0 5px rgba(255,205,40,0.9)) drop-shadow(0 2px 1px rgba(110,80,10,0.8)); }

/* CENTER TIMER — elite poker style countdown in the middle of the table */
.center-timer {
    position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
    width:132px; height:132px; border-radius:50%;
    display:flex; align-items:center; justify-content:center;
    font-size:44px; font-weight:900; color:var(--timer-red); z-index:28;
    background:radial-gradient(circle at 40% 34%, var(--cloth-lite,#243), var(--cloth-base,#05143C) 70%, #000 100%);
    border:3px solid #C9A34E;
    box-shadow:0 0 0 2px rgba(255,228,181,0.6), 0 8px 26px rgba(0,0,0,0.85), inset 0 0 24px rgba(0,0,0,0.55), 0 0 16px rgba(201,163,78,0.35);
    text-shadow:0 0 14px rgba(239,68,68,0.65); font-variant-numeric:tabular-nums; pointer-events:none;
    transition:background 0.4s ease;
}
/* Skip-night center button */
.center-skip-btn {
    position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
    width:96px; height:96px; border-radius:50%;
    display:flex; flex-direction:column; align-items:center; justify-content:center;
    font-size:13px; font-weight:800; color:#fff; z-index:32; cursor:pointer;
    background:radial-gradient(circle, #4c1d95, #2e1065); border:3px solid var(--accent-gold);
    box-shadow:0 0 25px rgba(251,191,36,0.4), inset 0 0 18px rgba(0,0,0,0.5);
    text-align:center; line-height:1.15; text-transform:uppercase; letter-spacing:0.5px;
    animation:yula-pulse 1.5s ease-in-out infinite; font-family:inherit;
}
.center-skip-btn:active { transform:translate(-50%,-50%) scale(0.93); }

/* TABLE ELEMENTS */
.question-env, .golden-sage, .player-score-circle, .speaker-node { position:absolute; transform:translate(-50%,-50%); touch-action:manipulation; }
.question-env { width:60px; height:42px; background:linear-gradient(155deg,#fdfdfb 0%,#efece4 55%,#dcd7ca 100%); border:1px solid #cbb98a; color:#1a1a1a; border-radius:4px; display:flex; flex-direction:column; align-items:center; justify-content:center; font-size:13px; font-weight:bold; box-shadow:0 3px 8px rgba(0,0,0,0.55), inset 0 1px 1px rgba(255,255,255,0.85), inset 0 -2px 3px rgba(0,0,0,0.08); cursor:pointer; z-index:8; }
.envelope-flap { position:absolute; top:0; left:0; width:0; height:0; border-left:29px solid transparent; border-right:29px solid transparent; border-top:16px solid #d8d8d8; opacity:0.9; pointer-events:none; }
.envelope-text { width:100%; text-align:center; line-height:1.2; padding-top:9px; font-size:13px; color:#2a2a2a; }
.question-env.opened { transform:translate(-50%,-50%) scale(2.4) !important; opacity:0; background:var(--accent-gold); transition:all 0.5s ease; z-index:100; }
.golden-sage { width:26px; height:26px; background:radial-gradient(circle,#fde68a,#f57f17); border:2px solid rgba(255,255,255,0.8); border-radius:50%; display:flex; justify-content:center; align-items:center; font-size:14px; box-shadow:0 0 12px #fbbf24; z-index:5; }

/* PLAYER CIRCLES */
/* ⚠ ШИРИНА И ВЫСОТА — ЧЁТНЫЕ, И ЭТО НЕ ПРИДИРКА [24.09]. Кружок центрируется
   через transform:translate(-50%,-50%); при нечётных 33px это сдвиг на 16.5px, то
   есть элемент ВСЕГДА стоит на пол-пикселя — рамка и цифра внутри размазываются
   между соседними пикселями. Вторая половина правки — округление координат в
   getCoords (table.js). Меняете размер — берите чётное число. */
.player-score-circle { width:34px; height:34px; border-radius:50%; display:flex; flex-direction:column; align-items:center; justify-content:center; font-size:12px; text-align:center; box-sizing:border-box; font-weight:bold; z-index:10; }
.player-score-circle.plus-one { background:#166534; border:2px solid #22c55e; color:#fff; }
.player-score-circle.zero-points { background:var(--purple-mid); border:2px solid #3b2f60; color:#a78bfa; }
.inactive-node { background:rgba(10,6,20,0.8) !important; border:1px dashed #4b5563 !important; color:#6b7280 !important; cursor:default !important; box-shadow:none !important; pointer-events:none !important; transform:translate(-50%,-50%) !important; filter:grayscale(80%); opacity:0.75; z-index:5; width:32px !important; height:32px !important; font-size:9px !important; }

/* OUTER NAME LABELS — font +2 (13px desktop) */
.outer-player-label { position:absolute; transform:translate(-50%,-50%); white-space:nowrap; font-size:13px; color:#e2e8f0; pointer-events:none; z-index:40; text-shadow:0 1px 6px #000, 0 0 14px rgba(0,0,0,0.95); text-align:center; line-height:1.2; background:rgba(5,3,12,0.7); padding:2px 7px; border-radius:5px; font-weight:600; }
.outer-player-label.elim { color:#4b5563; text-decoration:line-through; }
.outer-player-label.active-spkr { color:var(--accent-gold); font-weight:800; font-size:14px; }

/* VOTE BUTTONS */
.vote-dot-btn { cursor:pointer; z-index:12 !important; border:2px solid rgba(251,191,36,0.38) !important; transition:background 0.18s,border-color 0.18s,transform 0.18s !important; }
.vote-dot-btn:hover, .vote-dot-btn:active { background:#7c3aed !important; border-color:var(--accent-gold) !important; transform:translate(-50%,-50%) scale(1.16) !important; box-shadow:0 0 20px rgba(251,191,36,0.5) !important; z-index:15 !important; }
.vote-dot-btn.selected-vote { background:var(--accent-gold) !important; color:#08050c !important; border-color:#fde68a !important; box-shadow:0 0 16px rgba(251,191,36,0.75) !important; animation:gold-ring-pulse 1.2s ease-out 1; }
.vote-dot-btn.vote-was-elim { opacity:0.82; border-style:dashed !important; border-color:rgba(148,163,184,0.6) !important; filter:grayscale(35%); }
.vote-dot-btn.vote-was-elim:hover, .vote-dot-btn.vote-was-elim:active { filter:none; opacity:1; border-color:var(--accent-gold) !important; }
.tb-flex-container { display:flex; justify-content:center; align-items:center; gap:14px; flex-wrap:wrap; margin:14px 0; }
.tb-vote-btn { position:relative !important; left:auto !important; top:auto !important; transform:none !important; width:86px !important; height:86px !important; font-size:12px !important; cursor:pointer; border-radius:50% !important; display:flex !important; flex-direction:column !important; align-items:center !important; justify-content:center !important; background:var(--purple-mid) !important; border:2px solid rgba(251,191,36,0.32) !important; color:#fff !important; font-weight:bold !important; box-shadow:0 4px 12px rgba(0,0,0,0.5) !important; transition:background 0.18s,border-color 0.18s,transform 0.18s,box-shadow 0.18s !important; font-family:inherit !important; }
.tb-vote-btn:hover, .tb-vote-btn:active { transform:scale(1.1) !important; background:#7c3aed !important; border-color:var(--accent-gold) !important; box-shadow:0 0 20px rgba(251,191,36,0.4) !important; }
.tb-vote-btn.selected { background:var(--accent-gold) !important; color:#08050c !important; border-color:#fff !important; transform:scale(1.05) !important; }

/* SPEAKER NODES */
.speaker-node { width:34px; height:34px; border-radius:50%; background:var(--purple-mid); border:2px solid #4b5563; display:flex; align-items:center; justify-content:center; font-size:14px; font-weight:bold; color:#fff; z-index:10; transition:all 0.35s ease; }
.speaker-node.active { width:50px; height:50px; border-color:var(--accent-gold); background:#7c2d12; font-size:16px; z-index:12; animation:pulse-speaker 0.85s infinite alternate; }
.speaker-node.inactive-node { width:28px !important; height:28px !important; font-size:9px !important; }

.correct-voter-glow { border:3px solid var(--accent-gold) !important; background:rgba(120,60,9,0.4) !important; box-shadow:0 0 22px rgba(251,191,36,0.75), 0 0 45px rgba(251,191,36,0.25) !important; animation:gold-ring-pulse 1.5s ease-out infinite !important; color:var(--accent-gold) !important; }
.night-node { background:radial-gradient(circle at 38% 30%, #26262b, #0e0e12 75%, #000 120%); border:2px solid #3a3a42; color:#cbd5e1; box-shadow:0 4px 12px rgba(0,0,0,0.6); }
.blinking-joker { animation:joker-glow 1.15s infinite alternate; border:3px solid #ef4444 !important; cursor:pointer !important; z-index:999 !important; }
.sage-revealed { border:3px solid #2563eb !important; box-shadow:0 0 22px rgba(37,99,235,0.8) !important; background:#1e1b4b !important; }
.author-highlight { border:3px solid #22c55e !important; box-shadow:0 0 22px rgba(34,197,94,0.8) !important; }
.text-success { color:#22c55e !important; text-shadow:0 0 10px #22c55e; }
.text-danger { color:#ef4444 !important; text-shadow:0 0 10px #ef4444; }

/* ANSWERS */
.answer-btn { background:rgba(10,6,22,0.88); color:#fff; border:1px solid #3b2f60; padding:12px 13px; margin:6px 0; width:100%; border-radius:8px; cursor:pointer; text-align:left; font-size:15px; transition:0.18s; touch-action:manipulation; font-family:inherit; }
.answer-btn:hover { background:#1e1038; border-color:#7c3aed; }
.answer-btn.selected { background:#4c1d95; border-color:#7c3aed; }
.correct-glow { background:#166534 !important; color:#fff !important; font-weight:bold; border-color:#22c55e !important; box-shadow:0 0 12px #22c55e; }
.wrong-glow { background:#7f1d1d !important; color:#fff !important; border-color:#ef4444 !important; }

/* TIMER — RED (not pink) */
.big-timer { font-size:48px; color:var(--timer-red); font-weight:900; margin:10px 0; text-shadow:0 0 14px rgba(239,68,68,0.45); font-variant-numeric:tabular-nums; }

/* ============================================================================
   ИТОГИ РАУНДА — ПОЧЕМУ ЭКРАН НЕ МОТАЛСЯ [11.09]
   ----------------------------------------------------------------------------
   Владелец, 11.09, со скриншотом: «в игре так и не проматывается страница… в
   какой-то момент она просто вообще не мотается. Открывается экран, надо
   прочитать вопрос — пробуешь опустить страницу вниз, она не опускается».
   Жалоба повторялась, и предыдущие попытки чинили не то (полосу прокрутки).

   Причина была в разметке, одна строка. Накладка стояла так:

       display:flex; flex-direction:column; justify-content:center;
       height:100%; overflow-y:auto;

   `justify-content:center` вместе с прокруткой — известная поломка flexbox.
   Пока содержимое НИЖЕ экрана, всё хорошо и красиво по центру. Как только оно
   становится ВЫШЕ экрана — а таблица на девять ботов с пояснением всегда выше
   телефона, — центрирование выталкивает содержимое в ОБЕ стороны сразу. Низ
   ещё доступен прокруткой, а ВЕРХ уходит за начало блока, и достать его нельзя
   ничем: блок физически не умеет мотаться выше своей нулевой точки. Заголовок
   и первые строки таблицы становятся недостижимы, а палец, тянущий «вниз»,
   упирается в конец короткого диапазона — и это читается как «не мотается».

   Лечится не полосой прокрутки и не её шириной, а раскладкой:
     • justify-content:flex-start — начало содержимого всегда достижимо;
     • margin:auto 0 на внутренней обёртке — она СТАНОВИТСЯ по центру, когда
       место есть, и сама сжимается до нуля, когда его нет. То есть красивое
       центрирование сохраняется, а обрезания больше не бывает.

   Рядом стоящий #night-overlay уже был написан правильно (flex-start) — этот
   же приём просто не доехал до итогов раунда.
   ============================================================================ */
#screen-gong-result {
    position: fixed; inset: 0; z-index: 150000;
    background: rgba(5,3,12,0.97);
    display: none;            /* showGong() ставит flex инлайном */
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;   /* ⚠ НЕ center — см. объяснение выше */
    text-align: center;
    padding: 20px;
    box-sizing: border-box;
    overflow-y: auto;
    /* Прокрутка накладки не «протекает» на страницу под ней и не дёргает её. */
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
}
#screen-gong-result .gong-inner {
    width: 100%;
    max-width: 560px;
    /* Вот это и центрирует — но без обрезания: auto сжимается до нуля, когда
     * содержимое не помещается. Замена justify-content:center. */
    margin: auto 0;
}

/* NIGHT OVERLAY — full size table */
#night-overlay { position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(5,3,12,0.97); z-index:100000; display:none; flex-direction:column; align-items:center; justify-content:flex-start; padding:16px; overflow-y:auto; overscroll-behavior:contain; -webkit-overflow-scrolling:touch; }
#night-content { width:100%; max-width:520px; margin:0 auto; text-align:center; }

/* ════════════════════════════════════════════════════════════════════════════
   ПОД ПАНЕЛЯМИ НИЧЕГО НЕ ПРЯЧЕТСЯ

   Владелец, 16.09, со снимком экрана: «надпись находится под кнопками, она ниже
   не спускается, экран застревает. Не видно, что там за надпись. То же самое с
   нижними кнопками».

   ПОЧЕМУ ТАК ВЫХОДИЛО. Верхняя панель и нижний ряд — плавающие: они прибиты к
   краям экрана и лежат ПОВЕРХ всего (z-index 200000 и 300000). Накладка с
   итогом раунда лежит ниже (150000) и начинается от самого верха экрана, с
   отступом в 20 пикселей. То есть первые 74 пикселя её содержимого всегда
   закрыты кнопками — и достать их нельзя ничем: накладка уже прокручена в своё
   начало, а выше начала не бывает. Человек тянет экран, экран не идёт, надпись
   не показывается. Ровно то, что на снимке.

   ПОЧЕМУ «БОЛЬШЕ ХОД КОЛЕСИКА» НЕ ПОМОГЛО БЫ. Ход не кончился — кончилось само
   содержимое. Крутить можно сколько угодно: наверху не спрятанный текст, а
   край блока.

   РЕШЕНИЕ — ОСВОБОДИТЬ ПОЛОСЫ ПОД ПАНЕЛЯМИ. Накладка начинается не у края
   экрана, а ниже верхней панели, и кончается выше нижнего ряда. Тогда первая
   строка видна сразу, а последняя догоняется прокруткой до конца.

   Высота панелей задана здесь один раз, переменными: поменяется панель —
   отступы поедут за ней сами, и это место не придётся искать заново.
   ════════════════════════════════════════════════════════════════════════════ */
:root {
    --bar-top: 74px;      /* #top-game-header */
    --bar-bottom: 64px;   /* #lang-switcher-wrap: кнопки плюс его собственные отступы */
}
@media (max-width: 480px) { :root { --bar-top: 68px; } }

#screen-gong-result,
#night-overlay {
    /* ⚠ ПЕРВАЯ ПАРА — ЗАПАСНАЯ, НЕ УДАЛЯТЬ. Старые браузеры не понимают calc с
     * env() и переменными: для них вторая пара недействительна, и без первой
     * они остались бы с прежним отступом в 16–20 пикселей, то есть с той же
     * бедой. Новые браузеры просто перекроют её второй парой. */
    padding-top: 92px;
    padding-bottom: 82px;
    padding-top: calc(var(--bar-top) + 18px);
    padding-bottom: calc(var(--bar-bottom) + env(safe-area-inset-bottom, 0px) + 18px);
}

/* Night table FULL SIZE (no shrink) */

/* GONG explanation */
.gong-explanation-box { margin-top:12px; padding:12px 14px; background:rgba(251,191,36,0.06); border:1px solid rgba(251,191,36,0.22); border-left:3px solid var(--accent-gold); border-radius:8px; font-size:14px; color:#fbbf24; text-align:left; line-height:1.6; font-style:italic; }
.gong-explanation-label { font-size:10px; letter-spacing:1.5px; text-transform:uppercase; color:rgba(251,191,36,0.6); margin-bottom:4px; font-style:normal; font-weight:bold; }

/* FINAL TABLE */
.final-table { width:100%; border-collapse:collapse; margin-top:8px; background:rgba(8,5,18,0.65); border-radius:8px; overflow:hidden; border:1px solid rgba(251,191,36,0.18); }
.final-table th { background:var(--purple-dark); color:var(--accent-gold); padding:9px 7px; font-size:12px; border-bottom:1px solid rgba(251,191,36,0.28); }
.final-table td { padding:9px 7px; font-size:13px; color:#f8fafc; border-bottom:1px solid rgba(255,255,255,0.04); }
.row-winner { background:linear-gradient(90deg,rgba(120,53,9,0.38),transparent) !important; font-weight:bold; border-left:3px solid var(--accent-gold); }
.row-user { color:var(--accent-gold) !important; font-weight:bold; }
.row-correct-voter td { color:var(--accent-gold) !important; }
.row-wrong-voter td { color:#6b7280 !important; }

/* ============================================
   MOBILE
   ============================================ */
@media (max-width: 480px) {
    body { padding-top: 88px; font-size: 14px; }
    .container { padding: 13px; width: 98%; }
    h1 { font-size: 22px; } h2 { font-size: 16px; }
    #top-game-header { height: 68px; padding: 0 5px; gap: 4px; }
    #top-game-header > * { height: 48px; border-radius: 10px; font-size: 16px; padding: 3px 1px; }
    #top-game-header small, #top-game-header span { font-size: 8px; letter-spacing: 0; }
    .table-circle { width: min(74vw, 300px) !important; height:auto !important; aspect-ratio:3 / 4 !important; max-width:none !important; margin: 26px auto 24px auto !important; }
    body.view-3d .table-circle { transform: perspective(820px) rotateX(30deg) scale(0.92); }
    body.view-3d .outer-player-label { transform: translate(-50%,-50%) rotateX(-30deg) scale(1.06); }
    .player-score-circle { width: 25px !important; height: 25px !important; font-size: 9px !important; }
    .player-score-circle.inactive-node { width: 25px !important; height: 25px !important; }
    .inactive-node { width: 48px !important; height: 48px !important; }
    .outer-player-label { font-size: 10px; padding: 1px 4px; max-width: 64px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .outer-player-label.active-spkr { font-size: 12px; }
    .question-env { width: 48px; height: 34px; font-size: 11px; }
    .envelope-flap { border-left: 22px solid transparent; border-right: 22px solid transparent; border-top: 13px solid var(--accent-gold); }
    .big-timer { font-size: 40px; }
    .center-timer { width: 102px; height: 102px; font-size: 34px; }
    .center-skip-btn { width: 84px; height: 84px; font-size: 11px; }
    .yula { width: 46px; height: 46px; font-size: 8px; }
    .logo-brand-solar { font-size: 20px; letter-spacing: 3px; }
    .tb-vote-btn { width: 70px !important; height: 70px !important; font-size: 11px !important; }
    /* НИЖНИЙ РЯД УЖИМАЕТСЯ, ЧТОБЫ ВЛЕЗТЬ ЦЕЛИКОМ.
     * Пять кнопок — язык, звук, обновление, код стола, выход — в полный размер
     * не помещаются в ширину телефона, и крайняя уезжала за край экрана
     * (владелец, 16.09, снимок). Ужимаем отступы, зазор и шрифт; значки без
     * подписи становятся чуть меньше. Ничего не прячем: все кнопки на месте. */
    #lang-switcher-wrap { gap: 5px; padding-left: 4px; padding-right: 4px; }
    #lang-switcher-wrap > *:not(.lang-menu) { height: 32px; min-height: 32px; padding: 0 8px; font-size: 11px; gap: 4px; }
    #net-sync-btn, #net-narration-btn { width: 32px; padding: 0; }
    #net-room-code { letter-spacing: .08em; }
    #lang-flag { font-size: 15px; }
    #screen-game.container, #screen-round2-discuss.container, #screen-round3-discuss.container, #screen-tiebreaker.container { padding: 6px; max-width: 100%; }
    #display-question { font-size: 15px; padding: 10px; }
    /* extra room below table so bottom labels (players 5-6) are never covered */
    #game-phase-spin, #r1-speaking-phase, #r2-phase-speaking, #r3-phase-speaking, #tb-phase-speaking, #r2-phase-vote-action, #r3-phase-vote-action, #tb-phase-vote { margin-top: 6px; }

}


/* ============================================
   GAME PHASES FLOAT ON COSMIC BACKGROUND
   (no square panel box — table sits on stars)
   ============================================ */
#screen-game.container,
#screen-round2-discuss.container,
#screen-round3-discuss.container,
#screen-tiebreaker.container {
    background: transparent;
    -webkit-backdrop-filter: none; backdrop-filter: none;
    box-shadow: none; border: none;
    max-width: 640px;
}
#screen-game.container::before,
#screen-round2-discuss.container::before,
#screen-round3-discuss.container::before,
#screen-tiebreaker.container::before { display: none; }

/* Readability on stars: titles + status get glow/shadow */
#screen-game h2, #screen-round2-discuss h2, #screen-round3-discuss h2, #screen-tiebreaker h2,
#screen-game h3, #screen-round2-discuss h3, #screen-round3-discuss h3, #screen-tiebreaker h3,
#screen-game h4, #screen-round2-discuss h4, #screen-round3-discuss h4, #screen-tiebreaker h4,
#game-status, #r2-status-text, #r3-status-text, #tb-status-text, #tb-subtitle, #spin-hint {
    text-shadow: 0 2px 8px #000, 0 0 16px rgba(0,0,0,0.9);
}
/* Answer buttons keep their own bg, add a stronger shadow to pop on the stars.
   (.btn needs no such rule — var(--gold-3d-shadow) already carries a deep drop shadow.) */
#screen-game .answer-btn { box-shadow: 0 4px 18px rgba(0,0,0,0.7); }
/* The question text gets a subtle dark plate for readability */
#display-question {
    background: rgba(8,5,16,0.72); border-radius: 10px; padding: 12px 14px;
    -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
    border: 1px solid rgba(251,191,36,0.15);
}

/* NIGHT OVERLAY — let stars twinkle through (semi-transparent, not solid) */
#night-overlay {
    background: radial-gradient(ellipse at 50% 40%, rgba(8,5,18,0.86) 0%, rgba(3,2,8,0.94) 100%);
    padding-top: 92px; /* clear the fixed header so its buttons don't cover the title */
}
#night-title { max-width: 92%; }
#night-skip-btn { display: inline-block !important; }


/* ============================================
   TOUCH DEVICES — neutralize sticky :hover
   (on phones a tapped button keeps its :hover look,
    making selections appear "stuck"/unchanged)
   ============================================ */
@media (hover: none) {
    /* :not(:active) so a real press still ignites gold — only the LEFTOVER hover is neutralised */
    .btn:not(.btn-dim):not(.btn-green):hover { filter: none; }
    .btn-green:hover { box-shadow: 0 4px 14px rgba(22,163,74,0.35); }
    .btn.btn-dim:hover:not(:active), #netlobby-bots:hover:not(:active), .btn-secondary:hover:not(:active),
    .table-btn:hover:not(:active), .cloth-btn:hover:not(:active),
    .joker-btn:hover:not(:active), .lux-btn:hover:not(:active) {
        color: var(--gold-text); border-color: var(--gold-line);
    }
    .table-btn.active:hover, .cloth-btn.active:hover, .joker-btn.active:hover, .lux-btn.active:hover {
        color: var(--gold-ink); border-color: transparent;
    }
    .answer-btn:hover { background: rgba(10,6,22,0.88); border-color: #3b2f60; }
    /* :not(:active) — обязательно. Касание зажигает И :hover, И :active сразу;
     * без этой оговорки сброс «липкого» наведения гасил бы и подсветку нажатия,
     * и на телефоне кнопка не отзывалась бы вовсе (владелец, 16.09). */
    #top-game-header > *:hover:not(:active) { border-color: rgba(251,191,36,0.28); box-shadow: 0 2px 12px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.07); }
    #lang-switcher-wrap > *:not(.lang-menu):hover:not(:active) { border-color: rgba(251,191,36,0.28); box-shadow: 0 2px 12px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.07); }
    .lang-option:hover { background: none; color: #cbd5e1; }
    .lang-option.active:hover { color: var(--accent-gold); }
    .vote-dot-btn:hover { background: var(--purple-mid) !important; transform: translate(-50%,-50%) !important; box-shadow: 0 4px 12px rgba(0,0,0,0.65) !important; }
    .vote-dot-btn.selected-vote { background: var(--accent-gold) !important; }
    .tb-vote-btn:hover { transform: none !important; background: var(--purple-mid) !important; }
    .tb-vote-btn.selected { background: var(--accent-gold) !important; }
}


/* GSL gold-foil logo on the felt (2D table) — placed below the yula centre */
.felt-logo {
    position:absolute; left:50%; top:80%; transform:translate(-50%,-50%);
    font-family:Georgia,'Times New Roman',serif; font-weight:800;
    font-size:13px; letter-spacing:2px; white-space:nowrap; pointer-events:none;
    z-index:6; text-align:center; line-height:1.2;
    background:linear-gradient(180deg,#f7e7a6,#c9a227 55%,#8a6a14);
    -webkit-background-clip:text; background-clip:text;
    -webkit-text-fill-color:transparent; color:#c9a227;
    text-shadow:0 1px 2px rgba(0,0,0,0.35);
    opacity:0.92;
}
body.table-neon .felt-logo { filter:drop-shadow(0 0 6px rgba(255,180,55,0.4)); }
@media (max-width:480px){ .felt-logo { font-size:9px; letter-spacing:1px; top:82%; } }


/* small white envelopes around the Yula in the top zone */
.question-env.env-mini { width:24px; height:17px; font-size:9px; border-radius:3px; }
.question-env.env-mini .envelope-flap { border-left:11px solid transparent; border-right:11px solid transparent; border-top:7px solid #d8d8d8; }
.question-env.env-mini .envelope-text { padding-top:3px; font-size:9px; }
.question-env.env-mini.opened { transform:translate(-50%,-50%) scale(1.25) !important; box-shadow:0 0 14px rgba(255,215,0,0.8) !important; z-index:35; }


/* ===== cloth-driven colours (centre timer field + player buttons) ===== */
body.cloth-blue  { --cloth-base:#05143C; --cloth-lite:#1b3a78; }
body.cloth-green { --cloth-base:#003C14; --cloth-lite:#0a6b32; }
body.cloth-red   { --cloth-base:#640505; --cloth-lite:#a52222; }
body.cloth-dark  { --cloth-base:#0A0A0A; --cloth-lite:#3a3a3a; }

/* player circles look like premium buttons in the felt colour */
.player-score-circle.zero-points {
    background:radial-gradient(circle at 38% 30%, var(--cloth-lite,#2a2150), var(--cloth-base,#1a1340) 78%, #000 120%) !important;
    border:2px solid #C9A34E !important; color:#fff !important;
    box-shadow:0 0 0 1px rgba(255,240,192,0.45), 0 4px 9px rgba(0,0,0,0.6), inset 0 2px 4px rgba(255,255,255,0.22), inset 0 -3px 6px rgba(0,0,0,0.6) !important;
    transition:transform 0.35s ease, box-shadow 0.35s ease, background 0.4s ease;
}
.speaker-node {
    background:radial-gradient(circle at 38% 30%, var(--cloth-lite,#2a2150), var(--cloth-base,#1a1340) 75%, #000 120%);
    border:2px solid rgba(201,163,78,0.8); color:#fff;
    box-shadow:0 5px 12px rgba(0,0,0,0.6), inset 0 2px 4px rgba(255,255,255,0.16);
    transition:transform 0.35s ease, box-shadow 0.35s ease;
}
/* glow ONLY on active elements */
.speaker-node.active {
    border-color:var(--accent-gold);
    box-shadow:0 0 22px rgba(251,191,36,0.85), 0 6px 14px rgba(0,0,0,0.6);
    transform:translate(-50%,-50%) scale(1.12);
}
.vote-dot-btn { transition:transform 0.3s ease, box-shadow 0.3s ease, background 0.3s ease; }
.question-env, .golden-sage { transition:transform 0.4s ease, opacity 0.45s ease, box-shadow 0.4s ease; }


/* red felt: gold countdown digits */
body.cloth-red .center-timer { color:#FFD700; text-shadow:0 0 16px rgba(255,215,0,0.7), 0 2px 3px rgba(0,0,0,0.7); }
body.cloth-red .center-timer { border-color:#FFE4B5; box-shadow:0 0 0 2px rgba(255,215,0,0.7), 0 8px 26px rgba(0,0,0,0.85), inset 0 0 24px rgba(0,0,0,0.5), 0 0 20px rgba(255,205,40,0.4); }


/* keep system round/status text from overlapping the top player name */
#screen-game .table-circle, #screen-round2-discuss .table-circle,
#screen-round3-discuss .table-circle, #screen-tiebreaker .table-circle { margin-top:56px; }
#game-round-title, #game-status, #r2-title-text, #r3-title-text, #tb-title-text { position:relative; z-index:80; }
#game-status { margin-bottom:6px; }

/* ════════════════════════════════════════════════════════════════════════════
   СТРОКА РАУНДА — ПРИКЛЕЕНА ПОД ВЕРХНЕЙ ПАНЕЛЬЮ, ВИДНА ВСЕГДА

   Владелец, 16.09: «фразу „Круг 1 — Раунд 3 — кто Мудрец?" всегда не видно.
   Закрепить прозрачной прямо под кнопками, чтобы стол мог крутиться, а игрок в
   любой момент понимал, какой идёт раунд и что отгадывают».

   Строка стоит первой на экране, а под ней стол во весь рост: опустил экран к
   столу — и объяснение происходящего уехало за верхний край. Теперь оно
   приклеено на месте сразу под панелью и не двигается, что бы ни делал стол.

   ФОН РАСТВОРЯЕТСЯ КНИЗУ. Вверху почти непрозрачный — буквы читаются над любым
   сукном; книзу сходит на нет, поэтому отрезающей полосы на экране нет и стол
   виден целиком. Это и есть просьба «прозрачная, чтобы под ней всё двигалось».

   КАСАНИЯ ПРОХОДЯТ СКВОЗЬ (pointer-events:none). Полоса лежит над столом, и
   будь она «плотной», верхние места стола перестали бы нажиматься — а по ним
   голосуют. Это надпись, а не панель.

   Работает на ВСЕХ раундах: онлайн-игра держит все фазы на одном экране и
   переписывает эти две строки на каждой смене — круг, раунд, чья очередь,
   время ответа, ночь, голосование, пауза.
   ════════════════════════════════════════════════════════════════════════════ */
/* ⚠ ПРИБИТО К ЭКРАНУ, А НЕ «ПРИКЛЕЕНО». ЭТО И БЫЛА ПРИЧИНА ДЁРГАНЬЯ.
 *
 * Владелец, 17.09: «дёргание началось после того, как слово зафиксировали под
 * верхними кнопками. Сделай фиксацию мёртвой, как у верхних кнопок».
 *
 * Он назвал причину точно. Приклеенная строка (position:sticky) ЕДЕТ ВМЕСТЕ СО
 * СТРАНИЦЕЙ и на каждом кадре прокрутки пересчитывается заново — и делает это
 * не быстрый путь браузера, а основной поток, тот же, где работает вся игра.
 * На телефоне страницу двигает отдельный быстрый механизм, а приклеенная строка
 * за ним не поспевает: она отстаёт на кадр-другой, и рядом с ней так же
 * запаздывает накладка стола. Отсюда и «троится».
 *
 * Прибитая строка (position:fixed) ведёт себя ровно наоборот: браузер выносит
 * её на отдельный слой и при прокрутке НЕ ТРОГАЕТ ВООБЩЕ. Ноль работы на кадр —
 * ни дёрганья, ни расхода батареи. Именно так прибиты верхние кнопки, которые
 * не дёргались ни разу.
 *
 * ЧЕМ ПЛАТИМ И КАК ЗАКРЫВАЕМ. Прибитая строка выпадает из потока и перестаёт
 * держать под собой место. Поэтому рядом стоит распорка (#net-phase-gap) —
 * пустой блок той же высоты. Высоту ей задаёт наблюдатель за размером строки:
 * он молчит, пока текст не менялся, и срабатывает ровно тогда, когда фраза
 * стала длиннее или перенеслась на две строки (netPinPhaseLine в net.js). */
#net-phase-pin {
    position: fixed;
    top: var(--bar-top);
    left: 0; right: 0;
    z-index: 190000;              /* ниже обеих панелей (200000), выше стола */
    pointer-events: none;
    margin: 0;
    padding: 8px 12px 10px;
    /* ⚠ ЗАТЕМНЕНИЯ ПОД СТРОКОЙ БОЛЬШЕ НЕТ.
     *
     * Владелец, 16.09: «это затемнение рагульно смотрится. Там чёрный фон,
     * звёздное небо — и так будет читаться. Буквы золотистые, их и так хорошо
     * видно».
     *
     * Он прав, и я перестраховалась. Полоса задумывалась как защита от
     * нечитаемости над светлым сукном, но на экране она читается как серая
     * заплата поперёк стола — некрасиво и заметнее самой надписи.
     *
     * Вместо подложки — ТЕНЬ У БУКВ. Она не закрывает ничего: стол виден
     * целиком, а золото остаётся читаемым и над зелёным сукном, и над белым
     * барабаном. Это контраст самих букв, а не пятно на экране. */
    background: none;
    text-shadow: 0 1px 3px rgba(0,0,0,0.95), 0 0 10px rgba(0,0,0,0.8);
}
#net-phase-pin #game-round-title { margin: 0; font-size: 19px; line-height: 1.25; }
#net-phase-pin #game-status { margin: 4px 0 0; min-height: 0; }

/* РАСПОРКА — ПУСТОЕ МЕСТО РОВНО ПОД ПРИБИТОЙ СТРОКОЙ.
 * Она ничего не рисует и ни на что не реагирует: её единственная работа —
 * занять высоту, которую освободила прибитая строка, чтобы стол не полез под
 * неё. Высоту задаёт код, измерив настоящую строку (netPinPhaseLine). */
#net-phase-gap { width: 100%; pointer-events: none; }
@media (max-width: 480px) {
    #net-phase-pin { padding: 6px 8px 10px; }
    #net-phase-pin #game-round-title { font-size: 16px; }
    #net-phase-pin #game-status { font-size: 13px; }
}
/* ⚠ 24, НЕ 18 [01.10, найдено тестом qa/e2e/live-table на полном столе]. При 12 местах
   подпись верхнего места («Бот 6») стоит выше края стола и при 18 px заходила под
   заголовок круга на 3 px на всех телефонных ширинах — пилюля имени слипалась с
   нижней кромкой «Circle 1 — Round 1». Шесть пикселей запаса — и зазор есть всегда. */
@media (max-width:480px){
  #screen-game .table-circle, #screen-round2-discuss .table-circle,
  #screen-round3-discuss .table-circle, #screen-tiebreaker .table-circle { margin-top:24px !important; }
}


/* ===== player name fonts by table style ===== */
body.table-classic .outer-player-label {
    font-family:'Segoe UI','Helvetica Neue',system-ui,Arial,sans-serif; font-weight:600; letter-spacing:0.4px;
}
body.table-neon .outer-player-label {
    font-family:'Segoe UI','Helvetica Neue',system-ui,Arial,sans-serif; font-weight:300; letter-spacing:1.6px;
}

/* (casino-chip dashed edge removed per request) */


/* clean premium readable name font everywhere (overrides older serif) */
.outer-player-label { font-family:'Segoe UI','Helvetica Neue',system-ui,Arial,sans-serif; }

/* 4) yula CENTER pulse + hint pulses in yula-gold */
@keyframes core-pulse {
    0%,100% { box-shadow:inset 0 1px 2px rgba(255,255,255,.8), inset 0 -2px 4px rgba(90,65,8,.8), 0 0 6px rgba(255,205,40,.5); }
    50%     { box-shadow:inset 0 1px 2px rgba(255,255,255,.95), inset 0 -2px 4px rgba(90,65,8,.8), 0 0 18px rgba(255,205,40,1); }
}
.yula-clickable .yula-core { animation:core-pulse 1.3s ease-in-out infinite; }
/* always-on hint → opacity only (see the .twinkle-star note); the glow is a static shadow */
@keyframes hint-pulse { 0%,100% { opacity:0.80; } 50% { opacity:1; } }
#spin-hint {
    color:#FFCF6A; text-shadow:0 0 14px rgba(255,205,40,0.7), 0 2px 6px #000, 0 0 16px rgba(0,0,0,0.9);
    font-weight:600; animation:hint-pulse 1.3s ease-in-out infinite;
}

/* ===== floating MUSIC toggle — shown ONLY during the Night, styled to match the lang switcher family ===== */
#music-btn {
    position: fixed; bottom: 14px; left: 14px; z-index: 300001;
    border-radius: 10px;
    display: none; align-items: center; justify-content: center;
    padding: 8px 11px; font-size: 18px; line-height: 1; cursor: pointer; touch-action: manipulation;
    background: linear-gradient(145deg, rgba(30,16,56,0.95), rgba(15,8,28,0.95));
    border: 1px solid rgba(251,191,36,0.28);
    color: var(--accent-gold);
    box-shadow: 0 2px 12px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.07);
    transition: border-color 0.2s, box-shadow 0.2s, transform 0.15s;
}
/* only visible while the Night overlay is up */
#music-btn.night-visible { display: flex; animation: fadeInUp 0.45s ease; }
/* RED while a song is actually playing (clear "it's on, tap to stop" signal) */
#music-btn.playing {
    background: linear-gradient(180deg, #e0443a, #a4170e);
    border-color: #ff9183;
    color: #fff;
    box-shadow: 0 0 16px rgba(224,68,58,0.75), 0 2px 12px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.25);
}


#music-btn.off { opacity: 0.6; filter: grayscale(60%); }
#music-btn:hover { border-color: var(--accent-gold); box-shadow: 0 2px 18px rgba(251,191,36,0.3); }
#music-btn:active { transform: scale(0.88); }
@media (max-width:480px){ #music-btn { padding: 7px 10px; font-size: 16px; bottom: 12px; left: 12px; } }

/* referral code prefilled from an invite link */

/* ============================================================
   LUXURY TABLES — admin menu + in-game 3D background
   ============================================================ */
/* Colour/размер for .lux-head, .lux-btn and .lux-lock comes from the BUTTONS + ADMIN sections
   above — this block only adds their layout. It used to re-declare the whole old brown/yellow
   look here, which (being later in the file) quietly won over the gold system. */
.lux-head{ margin:14px 0 8px; display:flex; align-items:center; gap:6px; }
.lux-row{ margin-bottom:0; }
.lux-btn[disabled], .lux-btn.locked{ opacity:0.5; cursor:not-allowed; filter:grayscale(0.5); }
.lux-lock{ font-size:11px; text-align:center; margin-top:9px; letter-spacing:0.4px; }
.table-color-picker{ margin-top:4px; }

/* in-game: the 3D luxury table lives in a page-anchored overlay (#lux-host) aligned over this
   container — оно едет вместе со страницей само, код при прокрутке не участвует (luxuryTable.js) */
.table-circle.lux-table{ position:relative; }
/* the overlay aligns to the container's real layout box, so never let the 2D "3D view" tilt skew it */
body.view-3d .table-circle.lux-table{ transform:none !important; }
.table-circle .lux-frame{ position:absolute; inset:0; width:100%; height:100%; border:0; margin:0;
    background:transparent; pointer-events:auto; z-index:1; }   /* NEW: interactive 3D table (clicks reach it) */

/* NEW: Luxury Tables Logic — wherever the 3D table is mounted, hide the 2D overlay
   (numbers, names, yula, envelopes, centre timer). Table-less phases keep their nodes.
   The overlay nodes stay in the DOM (hidden) and act as click-proxies for the 3D table. */
.lux-table > *:not(.lux-frame){ display:none !important; }

/* ===== Cloth-colour picker popup (opens by TAPPING THE TABLE) ===== */
#cloth-popup{ position:fixed; inset:0; z-index:400000; display:none; align-items:center; justify-content:center; background:rgba(4,2,12,0.55); -webkit-backdrop-filter:blur(3px); backdrop-filter:blur(3px); }
#cloth-popup.show{ display:flex; animation:fadeInUp 0.45s ease; }
.cloth-popup-card{ background:linear-gradient(160deg, rgba(34,20,60,0.98), rgba(14,8,26,0.98)); border:1px solid rgba(251,191,36,0.45); border-radius:16px; padding:18px 18px 20px; box-shadow:0 18px 60px rgba(0,0,0,0.6), 0 0 26px rgba(251,191,36,0.18); max-width:90vw; }
.cloth-popup-title{ color:var(--accent-gold); font-weight:800; font-size:15px; text-align:center; margin-bottom:14px; }
.cloth-popup-row{ display:flex; gap:10px; flex-wrap:wrap; justify-content:center; }
.cloth-popup-sw{ flex:0 0 auto; min-width:74px; display:flex; flex-direction:column; align-items:center; gap:7px; padding:11px 10px; border-radius:11px; cursor:pointer; font-family:inherit; font-size:12px; font-weight:700; color:#cbb9e8; background:rgba(12,8,22,0.85); border:1px solid #3a2c55; transition:0.18s; }
.cloth-popup-sw:hover{ border-color:rgba(251,191,36,0.5); }
.cloth-popup-sw.active{ border-color:var(--accent-gold); color:#08050c; background:linear-gradient(135deg,#fbbf24,#d97706); box-shadow:0 0 16px rgba(251,191,36,0.55); transform:scale(1.05); }
.cloth-popup-dot{ width:42px; height:24px; border-radius:6px; border:2px solid rgba(255,255,255,0.25); box-shadow:inset 0 0 8px rgba(0,0,0,0.45); }
.cloth-popup-sw.active .cloth-popup-dot{ border-color:#fff; }



/* ===== networked lobby (Stage 2 multiplayer) ===== */
.netlobby-players{display:flex;flex-direction:column;gap:8px;margin:14px 0}
/* SOLID obsidian rows — translucent ones let the panel's guilloché bleed through the names */
.netlobby-player{display:flex;align-items:center;gap:9px;padding:10px 13px;border-radius:10px;
  background:var(--obsidian);border:1px solid rgba(168,114,26,0.30);font-size:15px}
.netlobby-player.me{background:linear-gradient(180deg,#241f16 0%,#0A0A0C 100%);border-color:var(--gold-line)}
.np-dot{width:9px;height:9px;border-radius:50%;background:#64748b;flex-shrink:0}
.np-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ── РАССАДКА: ЭКРАН ВЕДУЩЕГО ──────────────────────────────────────────────
   Классы вешает net.js (netSeatWire). У игрока их нет вовсе — он этих строк не
   трогает, и выглядят они у него ровно как раньше.

   ⚠ ЭТИ ПРАВИЛА СТОЯТ ПОСЛЕ базовых .netlobby-player НАМЕРЕННО. При равной
   специфичности побеждает последнее, и включающее правило, поднятое выше
   базового, не сработает никогда — на этом здесь уже обжигались (нижняя панель
   на телефоне не появлялась вовсе). */
.netlobby-player.seatable{cursor:grab;user-select:none;-webkit-user-select:none;
  -webkit-touch-callout:none;
  /* pan-y оставляет ПРОКРУТКУ списка пальцем: пока игрок не взят, жест обычный.
     Запрет прокрутки включается только на время перетаскивания, и делает это
     preventDefault в непассивном touchmove (net.js) — через CSS этого не выразить,
     потому что взятие происходит по удержанию, уже после начала касания. */
  touch-action:pan-y;
  transition:transform .12s ease, box-shadow .12s ease, border-color .12s ease}
.np-grip{color:var(--gold-line);opacity:.5;font-size:13px;line-height:1;flex-shrink:0;
  letter-spacing:-2px}

/* Взят касанием: человек выбран, ждём второго тапа. */
.netlobby-player.picked{border-color:var(--gold-line-lit,#F6C147);
  box-shadow:0 0 0 1px var(--gold-line-lit,#F6C147), 0 0 14px rgba(246,193,71,.25)}
.netlobby-player.picked .np-grip{opacity:1}

/* Взят пальцем и едет. pointer-events:none обязателен: без него
   elementFromPoint возвращал бы саму взятую строку, и «кто под пальцем» не
   определялось бы никогда. */
.netlobby-player.dragging{cursor:grabbing;pointer-events:none;position:relative;z-index:5;
  opacity:.9;transform-origin:center;transition:none;
  box-shadow:0 10px 26px rgba(0,0,0,.65), 0 0 0 1px var(--gold-line-lit,#F6C147)}

/* Строка под пальцем — сюда сядет тот, кого несут. */
.netlobby-player.over{border-color:var(--gold-line-lit,#F6C147);
  background:linear-gradient(180deg,#2a2317 0%,#0A0A0C 100%)}

@media(prefers-reduced-motion:reduce){
  .netlobby-player.seatable{transition:none}
}
.np-dot.on{background:#22c55e;box-shadow:0 0 6px #22c55e}
#netlobby-code{color:var(--accent-gold);font-weight:800}
/* #net-timer is a net.js-only element (not part of the Golden Master) — keep it
   hidden; the timer lives in the original table centre. No Golden Master overrides. */
#net-timer { display: none !important; }

/* ============================================================
   OVERLAYS BUILT BY net.js
   These few rules must beat a stylesheet net.js appends LATER (its own <style> wins ties on
   source order), or inline styles it sets on the element — hence the !important here and
   nowhere else in this file. Everything above is plain cascade.
   ============================================================ */
/* ⚠ КНОПКА «3D» СНОВА НАКЛОНЯЕТ СТОЛ [01.10, владелец: «при нажатии 3D ничего не происходит»].
   С июля здесь стояло `transform:none` — наклон для обычного стола был выключен НАГЛУХО после
   жалобы, что стол «лежит на боку»: тогда по умолчанию включался 3D с наклоном 45°. Кнопка
   осталась, но ничего не делала. Теперь иначе:
     • по умолчанию стол ПЛОСКИЙ (net.js при старте ставит selectedView='2d' — не тронуто);
     • «3D» мягко наклоняет его ВПЕРЁД на 28° (не 45°) с лёгким приближением — объём без
       ощущения заваленного стола;
     • переход плавный (0,9 с) в обе стороны, подписи мест стоят вертикально.
   Роскошный стол исключён — у него своя перспектива внутри iframe. */
body .table-circle:not(.lux-table) { transition: transform .9s cubic-bezier(.2,.7,.2,1); }
body .table-circle:not(.lux-table) .outer-player-label { transition: transform .9s cubic-bezier(.2,.7,.2,1); }
body.view-3d .table-circle:not(.lux-table) {
  transform: perspective(1000px) rotateX(28deg) scale(1.06) !important;
  transform-origin: center 62%;
}
/* Второй drop-shadow на .tbl-svg убран здесь же [24.09]: он накладывался поверх того,
   что стоял выше, и стол растеризовался дважды. Тень рисует SVG сам. */
body.view-3d .table-circle:not(.lux-table) .outer-player-label { transform: translate(-50%,-50%) rotateX(-28deg) scale(1.05) !important; }
@media (prefers-reduced-motion: reduce) {
  body .table-circle:not(.lux-table), body .table-circle:not(.lux-table) .outer-player-label { transition: none; }
}

@media (max-width: 700px) {
    /* the night god-view and the standings floated top-right/left and covered the whole table on a
       phone — dock them as full-width bottom sheets so the table stays visible above them */
    #net-obs-night, #net-tv-scores {
        left: 8px !important; right: 8px !important; top: auto !important; bottom: 8px !important;
        width: auto !important; max-width: none !important; max-height: 40vh !important;
    }
    #net-obs-night { font-size: 13px !important; padding: 10px 12px !important; }
    /* the 🗣️ narration button sat over the question text — move it under the header */
    #net-narration-btn { top: 120px !important; bottom: auto !important; left: 6px !important; width: 42px !important; height: 42px !important; font-size: 18px !important; }
    /* role cards are ~100px wide in the 3-up grid: every label must stay on ONE line */
    .nmr-cover-sub { white-space: nowrap !important; font-size: 7px !important; letter-spacing: 0.14em !important; }
    .nmr-name, #screen-roles .card-role {
        white-space: nowrap !important; font-size: clamp(11px,3.3vw,16px) !important;
        max-width: 100% !important; overflow: hidden !important; text-overflow: ellipsis !important;
    }
    .nmr-face { padding: 12px 5px !important; gap: 7px !important; }
    .nmr-select { font-size: 10px !important; letter-spacing: 0.5px !important; padding: 10px 4px !important; }
}

/* ============================================================
   WHO IS SPEAKING — one lit microphone, one source of truth
   net.js puts .net-speaking on the seat and on the name label of st.activeSpeaker (the server's
   own value) and takes it off the moment the floor passes on, so the mic follows the turn exactly
   as the phone's "ваше слово" does. Same look on every standard table and in every admin mode;
   the luxury 3D table draws its own copy of this mic inside its scene.
   !important because the seat's own .zero-points / .speaker-node colours are !important themselves.
   ============================================================ */
@keyframes mic-live { 0%,100% { opacity:0.72; transform:translateX(-50%) scale(1); } 50% { opacity:1; transform:translateX(-50%) scale(1.15); } }
.outer-player-label.net-speaking {
    color: var(--accent-gold) !important; font-weight: 800 !important;
    border: 1px solid rgba(251,191,36,0.55);
    box-shadow: 0 0 15px rgba(251,191,36,0.45);
}
/* the mic floats just ABOVE the name pill, on the felt, where it reads from across the room */
.outer-player-label.net-speaking::before {
    content: ''; position: absolute; left: 50%; top: -15px; width: 19px; height: 19px;
    background: var(--mic-lit) no-repeat center / contain;
    filter: drop-shadow(0 0 7px rgba(255,216,117,0.95));
    animation: mic-live 1.05s ease-in-out infinite;
    pointer-events: none;
}
/* and the seat itself gets the matching gold ring, so the pair is unmistakable */
.player-score-circle.net-speaking, .speaker-node.net-speaking {
    border-color: var(--accent-gold) !important;
    box-shadow: 0 0 0 1px rgba(255,240,192,0.5), 0 0 20px rgba(251,191,36,0.8) !important;
}

/* A device that asks for less motion gets no decorative loops at all. The mic keeps its glow —
   it carries meaning — it just stops pulsing. */
@media (prefers-reduced-motion: reduce) {
    .twinkle-star, .logo-brand-solar, #spin-hint,
    .yula-clickable, .yula-clickable .yula-core, .center-skip-btn { animation: none !important; }
    .outer-player-label.net-speaking::before { animation: none !important; opacity: 1; }
}


/* ============================================================================
   ИМЯ ИГРОКА — КЛИКАБЕЛЬНОЕ ПРИ ГОЛОСОВАНИИ
   Базовое .outer-player-label выключает события мыши (pointer-events:none), чтобы
   подпись не перехватывала касания стола. Во время выбора участника это мешало:
   нажать можно было только по кружку, мимо имени палец проваливался.
   Класс .vote-label-btn включает события обратно — но ТОЛЬКО для фазы голосования.
   Правило стоит в конце файла намеренно: при равной специфичности выигрывает
   последнее, иначе pointer-events:none из строки выше победил бы.
   ============================================================================ */
.outer-player-label.vote-label-btn {
    pointer-events: auto;
    cursor: pointer;
    touch-action: manipulation;
    border: 1px solid rgba(251,191,36,0.35);
    background: rgba(10,6,22,0.86);
    transition: border-color 0.15s, box-shadow 0.15s, transform 0.12s;
}
.outer-player-label.vote-label-btn:hover {
    border-color: var(--accent-gold);
    box-shadow: 0 0 12px rgba(251,191,36,0.35);
}
.outer-player-label.vote-label-btn:active { transform: translate(-50%,-50%) scale(0.94); }

/* выбранный участник — имя подсвечивается так же, как кружок */
.outer-player-label.vote-picked {
    color: var(--accent-gold);
    font-weight: 800;
    border-color: var(--accent-gold);
    box-shadow: 0 0 14px rgba(251,191,36,0.55);
}

/* заголовок фазы не должен уезжать под закреплённую шапку при автопрокрутке */
h1, h2, .round-title, #round-title { scroll-margin-top: 84px; }


/* ============================================================================
   ВОПРОС НЕ ПОДЛЕЗАЕТ ПОД СТОЛ [11.09]
   ----------------------------------------------------------------------------
   Владелец, 11.09: «вопрос идёт наслаиваться под стол, стол видно и вопрос
   видно, но вопрос — за столом».

   Две причины сложились. Первая: подписи игроков (.outer-player-label) стоят
   absolute ОТНОСИТЕЛЬНО стола и торчат наружу за его коробку — верхняя «БОТ 5»
   примерно на два десятка точек выше края. Поток вёрстки о них не знает, и
   когда я сжал верхний отступ стола до 6 точек, подпись налезла на то, что
   стоит выше. Отступ вернулся к 26 — при уменьшенном столе высота всё равно
   осталась в выигрыше.

   Вторая: у .table-circle стоит z-index:5, а у вопроса своего слоя не было,
   поэтому при любом пересечении стол рисовался ПОВЕРХ. Теперь у вопроса
   собственный слой выше стола: даже если они когда-нибудь пересекутся снова,
   читаемым останется текст, а не сукно.
   ============================================================================ */
#display-question {
    position: relative;
    z-index: 6;
}


/* ============================================================
   ЭКРАН ВХОДА: ВКЛАДКИ, ЯЧЕЙКИ КОДА, КВАДРАТНЫЙ СКАНЕР  [25.09]
   Разметку ставит net.js (netAuthTabs и первый шаг регистрации).
   ============================================================ */

/* ── ДВЕ ДОРОГИ — НА САМОМ ЗНАКЕ [утверждено владельцем 27.09] ────────────────
   Здесь стояли две вкладки-кнопки. Теперь выбор живёт на инь-яне: слово у
   ЗОЛОТОЙ половины горит, слово у тёмной приглушено. Разметку ставит
   netAuthTabs() в net.js — слова, переводы и обе дороги остались прежние.

   РАЗМЕРЫ СЧИТАЮТСЯ, А НЕ ПОДБИРАЮТСЯ.
     --cw   ширина содержимого .container (95%, не шире 560, поля по 22);
     --room место, оставшееся СБОКУ от знака на уровне точки;
     --word кегль слова: слово стоит В ОДНУ СТРОКУ, поэтому кегль не выбирают,
            а выводят из --room.

   ⚠ ДЕЛИТЕЛЬ 12.6 УЖЕ СОДЕРЖИТ РОСТ ОТ scale. «ВХОД В КАБИНЕТ» с разрядкой
   занимает ≈9.2 своих кегля, отступ от круга 1.4, рост при касании 1.2:
   1.4 + 1.2 × 9.2 = 12.44. Поставишь сюда 10 «чтобы покрупнее» — выросшее
   слово уедет за край экрана ровно в тот момент, когда на него нажали.

   ⚠ ПРОВЕРКА НА 360 px: --cw = 326.8, знак = 108, --room = 116.6, кегль = 9.25.
   Строка «слово — знак — слово» занимает 338 из 352.8 доступных: влезает, и
   горизонтальной прокрутки нет даже с выросшим словом. */
/* ⚠ РАЗМЕР ЗНАКА НЕ ЗАДАЁТСЯ, А ВЫВОДИТСЯ ИЗ ВОЗДУХА ВОКРУГ СЛОВ [28.09].
   Раньше числом задавался диаметр, а слова получали то, что осталось. Это
   ломалось на каждом втором экране: на телефоне слова жались к краю, на
   компьютере знак раздувало, и «НОВЫЙ ИГРОК» обрезало вместе с полосой
   прокрутки. Теперь наоборот — неизменны кегль слова и воздух вокруг него, а
   знак берёт ровно то, что после них осталось.

   ОТКУДА ЧИСЛА. Слову нужно сбоку: своя ширина (самый длинный ряд «КАБИНЕТ» —
   ≈4.9 кегля) × 1.2 (оно вырастает под пальцем) плюс воздух до кромки. Место
   сбоку от знака: room = cw/2 − 0.43301 × disc, где 0.43301 = √(50²−25²)/100 —
   полуширина круга на высоте точки. Приравняли и выразили disc:
       disc = 1.1547 × cw − 2.3094 × need
   Коэффициенты — это 1/0.43301 и 2/0.43301, а не подобранные множители.

   ⚠ ПОТОЛОК ОБЯЗАТЕЛЕН. Без него на широком экране формула отдаёт знаку
   шестьсот пикселей, и он перестаёт быть знаком. 72vmin держит его в рамках
   окна и по высоте тоже: на низком и широком окне знак иначе не влезал. */
.nauth-sign{
  --word:clamp(0.7rem, 3.1vw, 0.95rem);
  --air:calc(var(--word) * 0.9);        /* воздух до кромки — в буквах слова */
  --wordw:calc(var(--word) * 4.9);      /* «КАБИНЕТ» — самый длинный ряд */
  --need:calc(var(--wordw) * 1.2 + var(--air));
  --cw:calc(min(95vw,560px) - 44px);
  /* ⚠ ЗНАК МЕРЯЕТСЯ И ПО ВЫСОТЕ ОКНА, А НЕ ТОЛЬКО ПО ШИРИНЕ [28.09]. Формула
     ниже считает, сколько места осталось СБОКУ, и про высоту не знает ничего:
     на невысоком окне знак честно влезал по бокам и вылезал сверху и снизу —
     вместе с ним появлялась вертикальная полоса прокрутки. 500 — это
     постоянная часть экрана: логотип, заголовок, поля, согласие, действие,
     ссылка и поля карточки. Тем же приёмом ограничен знак на экране входа в
     комнату (там 360, потому что над ним нет логотипа и полей).
     max(150px, …) — предохранитель: на совсем низком окне знак не должен
     схлопнуться в точку. */
  --disc:max(150px, min(
      72vmin,
      430px,
      calc(100dvh - 500px),
      calc(1.1547 * var(--cw) - 2.3094 * var(--need))));
  --gap:var(--air);
  --room:calc((var(--cw) - var(--disc)) / 2 + var(--disc) * 0.06699);
  position:relative; width:var(--disc); height:var(--disc); margin:18px auto 20px}
/* Телефон: .container на этой ширине сама берёт 98% и поля по 13 (блок MOBILE).
   Повторяем ровно это — иначе слово считает место по настольным полям. */
@media (max-width:480px){ .nauth-sign{--cw:calc(min(98vw,560px) - 26px)} }

/* ⚠ НА КОМПЬЮТЕРЕ МЕНЯЕТСЯ ТОЛЬКО ШИРИНА КАРТОЧКИ И КЕГЛЬ СЛОВА. Размер знака
   здесь не трогаем вовсе: формула выше сама отдаст ему всё, что осталось от
   слов, и сама же упрётся в потолок. Задать его тут числом значит вернуть ту
   же беду — знак, не знающий, сколько места нужно тексту рядом. */
@media (min-width:760px){
  body.auth-full #screen-auth.container.active,
  body.auth-full #screen-login.container.active { max-width:760px }
  .nauth-sign{
    --cw:calc(min(95vw,760px) - 44px);
    --word:clamp(0.85rem, 1.5vw, 1.15rem);
    margin:24px auto 26px;
  }
}

/* Знак — родной: те же доли, тот же поворот на 90°, те же точки радиусом
   ND_EYE_R и тот же Феникс, что на дашборде и на экране входа в комнату. */
.nas-yy{position:absolute; inset:0; border-radius:50%; cursor:pointer}
.nas-yy:focus-visible{outline:none; box-shadow:0 0 0 3px rgba(246,193,71,0.7)}
.nas-yy svg{width:100%; height:100%; display:block; overflow:visible}
.nas-hit{cursor:pointer}
.nas-hit:focus{outline:none}
.nas-fill-a,.nas-fill-b,.nas-eye-a,.nas-eye-b{transition:fill .38s ease}

/* ⚠ transform-origin СМОТРИТ НА КРУГ: слово растёт ОТ знака наружу, поэтому
   зазор при увеличении сохраняется и на знак слово не налезает никогда.
   ⚠ МАСШТАБ ХОДИТ ЧЕРЕЗ --sc, А НЕ ЧЕРЕЗ ЦЕЛЫЙ transform: в transform уже
   живёт translateY(-50%), который держит слово на линии точки.
   25% и 75% — уровни точек знака (NC_EYE_SCAN [50,25] и NC_EYE_PHX [50,75]).
   93.301% — кромка круга НА ЭТОМ УРОВНЕ: √(50² − 25²) = 43.301, значит левая
   кромка стоит на 6.699% ширины знака, а правая на 93.301%. Числа вычислены. */
/* ⚠ СЛОВА СТОЯТ В ДВА РЯДА, А НЕ В СТРОКУ [28.09, владелец]. «НОВЫЙ ИГРОК» в
   строку — это девять с лишним кеглей ширины, и под такой хвост знаку места не
   остаётся. Два ряда — вчетверо компактнее по ширине, и знак вырос вдвое.
   Перенос НЕ ставится вручную: max-width ломает строку там, где она длиннее
   места, а balance делит на равные ряды. Так это работает и для «ВХОД В /
   КАБИНЕТ», и для «NEW / PLAYER», и для украинского — не заводя трёх разных
   разметок на три языка.
   ⚠ ДЕЛИТЕЛЬ 6.4 СЧИТАЕТ ПО САМОМУ ДЛИННОМУ РЯДУ: «КАБИНЕТ» — 7 знаков, это
   ≈4.8 кегля, при увеличении до 1.2 — 5.8, плюс отступ 0.6 = 6.4. */
.nas-word{position:absolute; top:var(--dot);
  transform:translateY(-50%) scale(var(--sc,1));
  white-space:normal; text-wrap:balance;
  max-width:calc(var(--room) - var(--gap));
  cursor:pointer; user-select:none;
  font-weight:800; font-size:var(--word); line-height:1.2;
  letter-spacing:0.07em; text-transform:uppercase;
  transition:transform .4s cubic-bezier(.2,.7,.2,1), filter .4s ease, opacity .35s ease}
.nas-w-new{--dot:25%; right:calc(93.301% + var(--gap)); text-align:right; transform-origin:100% 50%}
.nas-w-log{--dot:75%; left:calc(93.301% + var(--gap)); text-align:left;  transform-origin:0 50%}
/* Ни рамки, ни фона, ни контура: это текст в пространстве, а не кнопка.
   Рамку фокуса браузера заменяет рост самого слова. */
.nas-word:focus-visible{outline:none}

/* Золото активного слова дописано В ТОТ ЖЕ СПИСОК, что и заголовки экранов
   (см. h1[data-i18n="auth_title"] выше) — отдельного описания золота здесь нет
   намеренно. Тут только свечение активного и глухота приглушённого. */
.nas-word.on{filter:drop-shadow(0 0 0.32em rgba(246,193,71,0.55))
                    drop-shadow(0 0 1.1em rgba(234,168,44,0.28)); --sc:1.06}
.nas-word.off{background:linear-gradient(120deg,var(--gold-line) 0%,var(--gold-text) 50%,#7A4B11 100%);
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent; color:transparent; opacity:0.62}
.nas-word.off:hover{opacity:0.95; filter:drop-shadow(0 0 0.5em rgba(246,193,71,0.35))}
.nas-word:hover,.nas-word:focus-visible{--sc:1.16}
.nas-word.on:hover{--sc:1.2}

/* Первый шаг регистрации */
.nsg-step{display:flex;align-items:center;gap:6px;margin-bottom:14px}
.nsg-dot{width:22px;height:3px;border-radius:2px;background:rgba(201,163,78,0.28);
  display:inline-block}
.nsg-dot.on{background:#F6C147}
.nsg-step span{margin-left:6px;font-size:10px;letter-spacing:0.16em;color:#a89fb2;
  text-transform:uppercase}

.nsg-lbl{display:block;text-align:left;font-size:13px;color:#cbd5e1;margin:0 2px 8px}

/* ОДНА ПОЛОСКА, СКАНЕР ВНУТРИ НЕЁ СПРАВА.
   Знаки расставлены широко и читаются по одному, но границы у поля одни и те же
   для кода, имени продавца и вставленной ссылки — облик не меняется на восьмом
   знаке, как было у сетки из семи ячеек. */
.nsg-field{position:relative}
.nsg-field .nsg-code{box-sizing:border-box;width:100%;height:54px;margin:0;
  background:#0d0b13;border:1px solid rgba(201,163,78,0.3);border-radius:10px;
  color:#F6C147;caret-color:#F6C147;outline:none;
  font:600 17px/1 ui-monospace,SFMono-Regular,Menlo,monospace;
  letter-spacing:0.22em;text-indent:0.22em;padding:0 16px}
/* Место под сканер освобождает класс, а не догадка: без кнопки правый отступ
   такой же, как левый, и текст не висит у пустого края. */
.nsg-field.has-qr .nsg-code{padding-right:60px}
.nsg-field .nsg-code::placeholder{color:#6f6880;letter-spacing:0.06em;
  text-transform:none;font-size:14px}
.nsg-field .nsg-code:focus{border-color:var(--gold-line-lit,#F6C147);
  box-shadow:0 0 0 1px rgba(246,193,71,0.35)}

/* Сканер — не отдельное действие в столбце, а второй способ заполнить ту же
   строку, поэтому он живёт внутри поля и не занимает своей высоты. */
.nsg-qr{position:absolute;right:7px;top:50%;transform:translateY(-50%);width:40px;height:40px;
  background:#1d1828;border:1px solid rgba(201,163,78,0.3);border-radius:8px;
  color:#D39D3C;cursor:pointer;display:grid;place-items:center}
.nsg-qr:hover{border-color:var(--gold-line-lit,#F6C147);color:#F6C147}
.nsg-qr svg{width:19px;height:19px}

.nsg-where{background:none;border:none;padding:6px 2px;margin-top:2px;
  color:#a89fb2;font-size:12.5px;cursor:pointer;text-align:left;
  border-bottom:1px dotted rgba(168,159,178,0.5);align-self:flex-start}
.nsg-where:hover,.nsg-where.on{color:#D39D3C}

@media (prefers-reduced-motion:reduce){
  .nas-word,.nas-fill-a,.nas-fill-b,.nas-eye-a,.nas-eye-b{transition:none}
}

/* ── ФОРМА ВХОДА: НИ ОДНОЙ РАМКИ [утверждено владельцем 27.09] ────────────────
   Поле держится заливкой и внутренней тенью — как углубление в металле, а не
   как прямоугольник в рамке. Фокус тоже без кольца: заливка теплеет и вокруг
   разливается золотой свет.

   ⚠ border:0 ОБЯЗАТЕЛЕН И НА :focus. Выше в этом файле у input:focus своя
   border-color — без второй строки рамка возвращается при первом же касании.
   ⚠ ПОЛЕ КОДА (.nsg-code) СЮДА НЕ ВХОДИТ НАМЕРЕННО: у первого шага свой
   утверждённый вид от 26.09, и трогать его этой правкой не просили. */
#screen-auth input[type="text"], #screen-auth input[type="email"],
#screen-auth input[type="tel"],  #screen-auth input[type="password"],
#screen-login input[type="text"], #screen-login input[type="password"] {
    border:0; border-radius:13px; margin:0 0 12px; padding:15px 17px;
    background:var(--field-bg);
    box-shadow:inset 0 2px 6px rgba(0,0,0,0.72),
               inset 0 -1px 0 rgba(255,255,255,0.05),
               0 1px 0 rgba(255,255,255,0.03);
    transition:background .25s ease, box-shadow .25s ease;
}
#screen-auth input[type="text"]:focus, #screen-auth input[type="email"]:focus,
#screen-auth input[type="tel"]:focus,  #screen-auth input[type="password"]:focus,
#screen-login input[type="text"]:focus, #screen-login input[type="password"]:focus {
    border:0;
    background:linear-gradient(180deg,#221c12,#100e12);
    box-shadow:inset 0 2px 8px rgba(0,0,0,0.8),
               inset 0 -1px 0 rgba(246,193,71,0.22),
               0 0 24px rgba(246,193,71,0.13);
}
/* Первый шаг оставляем как был — возвращаем ему его собственные правила. */
#screen-auth .nsg-field .nsg-code{border:1px solid rgba(201,163,78,0.3);
    border-radius:10px; margin:0; padding:0 16px; background:#0d0b13;
    box-shadow:none}
#screen-auth .nsg-field.has-qr .nsg-code{padding-right:60px}
#screen-auth .nsg-field .nsg-code:focus{border-color:var(--gold-line-lit,#F6C147);
    background:#0d0b13; box-shadow:0 0 0 1px rgba(246,193,71,0.35)}

/* ── БЛОК МЕНЕДЖЕРА — ВТОРОЕ ПОЛЕ ФОРМЫ [27.09, утверждённый макет] ───────────
   Слева «Ваш менеджер» и кто он, справа «изменить». Выглядит точно как поле
   выше: та же заливка, та же внутренняя тень, то же скругление — потому что
   это и есть поле, только заполненное не рукой, а первым шагом.
   ⚠ ОПИСАНИЕ ЗДЕСЬ, И ТОЛЬКО ЗДЕСЬ. Раньше вид блока жил в netSellerEnsureCss;
   держать два описания одного поля в двух файлах — верный способ их развести. */
#net-seller-badge { margin:0 0 12px; text-align:left }
.nsg-mgr {
    display:flex; align-items:center; gap:12px; justify-content:space-between;
    border-radius:13px; padding:15px 17px; color:#cbd5e1; font-size:15px;
    background:var(--field-bg);
    box-shadow:inset 0 2px 6px rgba(0,0,0,0.72),
               inset 0 -1px 0 rgba(255,255,255,0.05),
               0 1px 0 rgba(255,255,255,0.03);
}
.nsg-mgr-t { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
.nsg-mgr-t b { color:#F5E6C0; font-weight:700 }
/* «Изменить» — надпись, а не кнопка: ни рамки, ни фона, как и всё остальное
   на этом экране. Не сжимается: уступит место — и пропадёт первой.
   ⚠ НАМЕРЕННО ТУСКЛОЕ. Золотым и полужирным оно спорило за внимание с самим
   менеджером и с главным действием внизу, хотя нужно меньшинству — тем, кому
   билет продал человек [владелец, 27.09]. Разгорается под пальцем и под
   фокусом: найти его можно, а лезть в глаза оно перестало. */
.nsg-mgr .nsg-edit {
    flex:0 0 auto; color:#6f6880; font-weight:400; font-size:12.5px;
    text-decoration:underline; text-underline-offset:2px;
    cursor:pointer; background:none; border:0;
    transition:color .2s ease;
}
.nsg-mgr .nsg-edit:hover, .nsg-mgr .nsg-edit:focus-visible {
    outline:none; color:var(--gold-text)
}
.nsg-mgr-s { display:block; margin:7px 4px 0; font-size:12px; line-height:1.45; color:#8b8296 }

/* Согласие — СТРОГО КУРСИВОМ. Курсив здесь не украшение: он отделяет
   юридическую строку от подписей к полям, набранных прямым. Ссылка курсивом
   тоже — иначе строка рвётся посередине. */
#reg-oferta-row span, #reg-oferta-row a { font-style:italic }

/* ── ГАЛОЧКА СОГЛАСИЯ: СОСТОЯНИЕ ВИДНО С ПЕРВОГО ВЗГЛЯДА [владелец, выкладка 159] ──
   Был системный квадратик 15 px и тусклая подпись: пустой и отмеченный почти не
   отличались, и человек не понимал, почему «Войти через Telegram» не работает.
   Пустая — красная рамка и красноватая подпись: обязательное и ещё не сделано.
   Отмеченная — золотой квадрат с тёмной галочкой и светлая подпись.
   Нажатие кнопки без галочки строку ещё и подсвечивает (netOfertaNudge). */
#reg-oferta-row {
  display:flex; align-items:flex-start; gap:8px;
  color:#d98b80; padding:4px 6px; border-radius:8px; transition:color .2s;
}
#reg-oferta-row:has(#reg-oferta:checked) { color:#d9cfbf }
#reg-oferta {
  -webkit-appearance:none; appearance:none; flex:0 0 auto; display:inline-grid; place-content:center;
  width:18px; height:18px; margin:0; border-radius:5px; cursor:pointer;
  border:1.5px solid #e0614f; background:transparent;
  transition:background .2s, border-color .2s;
}
/* Галочка — уголок из двух рамок, повёрнутый на 45°: ни шрифта, ни картинки. */
#reg-oferta::before {
  content:""; width:9px; height:5px; border:solid #1a1206; border-width:0 0 2px 2px;
  transform:translateY(-1px) rotate(-45deg); opacity:0; transition:opacity .15s;
}
#reg-oferta:checked { background:linear-gradient(145deg,#F9D976,#EAA82C 55%,#C18825); border-color:#F6C147 }
#reg-oferta:checked::before { opacity:1 }
#reg-oferta:focus-visible { outline:2px solid #F6C147; outline-offset:2px }

/* ── ГЛАВНОЕ ДЕЙСТВИЕ — НАДПИСЬ, А НЕ КНОПКА ─────────────────────────────────
   Ни фона, ни рамки, ни скругления: только текст со значком, крупно и с
   разрядкой. Что это нажимается, человек понимает по руке-курсору и по тому,
   что под курсором надпись подрастает и разгорается.

   ⚠ inline-flex, А НЕ flex ВО ВСЮ ШИРИНУ. Растянутая на строку надпись при
   scale(1.05) выросла бы на 5% ШИРИНЫ КАРТОЧКИ и полезла в поля. Так растёт
   только сама фраза, вокруг своей середины.
   ⚠ РАЗРЯДКА ЧЕРЕЗ clamp: «Зарегистрироваться через Telegram» — длинная фраза,
   и постоянные 0.2em кладут её в две строки на телефоне. */
#screen-auth .btn.btn-tg,
#screen-login button.btn[onclick*="netLogin"] {
    display:inline-flex; align-items:center; justify-content:center; gap:12px;
    width:auto; max-width:100%; margin:4px auto 0; padding:10px 6px;
    background:none; border:0; border-radius:0; box-shadow:none;
    color:#F6C147; line-height:1.25;
    /* ⚠ ВЕС 500 И КЕГЛЬ ПОМЕНЬШЕ. Заглавные буквы сами по себе читаются тяжелее
       строчных, и на восьмёрке фраза выходила жирным брусом [владелец, 27.09,
       дважды]. Лёгкое начертание с широкой разрядкой — обычный приём для
       заглавных: буква тоньше, строка длиннее, вес уходит. */
    font-weight:500;
    /* ⚠ КЕГЛЬ УМЕНЬШЕН РАДИ РАЗРЯДКИ, А НЕ САМ ПО СЕБЕ. Фраза стоит в одну
       строку, и её ширина — это кегль ПЛЮС просветы: не ужав первое, второе
       некуда растить. «ВОЙТИ ЧЕРЕЗ TELEGRAM» при 14 px и разрядке 0.5em
       занимает ~332 px из 364 доступных на экране в 360 px. */
    font-size:clamp(0.74rem,3.2vw,1.05rem);
    /* ⚠ ДВА ПРОСВЕТА ПРАВЯТСЯ ВМЕСТЕ, А НЕ ПО ОТДЕЛЬНОСТИ. Разрядка добавляется
       И между буквами, И к пробелу: растяни одну — разъедутся и слова. Просвет
       между словами владельца уже устраивал, поэтому word-spacing сведён к
       нулю ровно настолько, насколько вырос letter-spacing. */
    /* ⚠ ТРЕТИЙ ЗАХОД, И КАЖДЫЙ УПИРАЕТСЯ В ОДНО: ФРАЗА СТОИТ В ОДНУ СТРОКУ.
       На экране в 360 px «ВОЙТИ ЧЕРЕЗ TELEGRAM» со значком занимает ~326 из
       334 доступных — запас в восемь пикселей. Поэтому разрядку растят не
       отдельно, а ВМЕСТЕ с уменьшением кегля: 0.5em → 0.68em сверху и
       0.88rem → 0.74rem снизу. Просто увеличить просветы нельзя — фраза
       переломится на две строки, и это увидят не все, а только те, у кого
       телефон поуже. */
    --tg-track:clamp(0.36em,1.9vw,0.68em);
    letter-spacing:var(--tg-track);
    word-spacing:0;
    text-shadow:none; cursor:pointer;
    transition:transform .3s cubic-bezier(.2,.7,.2,1), filter .3s ease;
}
#screen-auth .btn.btn-tg:hover, #screen-auth .btn.btn-tg:focus-visible,
#screen-login button.btn[onclick*="netLogin"]:hover,
#screen-login button.btn[onclick*="netLogin"]:focus-visible {
    outline:none; transform:scale(1.05); background:none; box-shadow:none;
    filter:drop-shadow(0 0 0.45em rgba(246,193,71,0.55))
           drop-shadow(0 0 1.2em rgba(234,168,44,0.3));
}
/* ⚠ ОПТИЧЕСКОЕ ВЫРАВНИВАНИЕ. Разрядка добавляется и ПОСЛЕ последней буквы —
   фраза со значком уезжает влево на целый просвет, и чем шире разрядка, тем
   заметнее перекос. Снимаем этот хвост ровно той же величиной. */
#screen-auth .btn.btn-tg .btn-tg-t{margin-right:calc(-1 * var(--tg-track))}
#screen-auth .btn.btn-tg .btn-tg-i{width:1.35em; height:1.35em; transition:transform .25s ease}
#screen-auth .btn.btn-tg:hover .btn-tg-i{transform:translate(3px,-3px)}
/* Выключенная (занята запросом) или запертая (нет галочки согласия, netOfertaGate)
   — гаснет, а не становится серым прямоугольником: фона у неё больше нет.
   Запертая нажимается: нажатие подсвечивает галочку, а не уходит в пустоту. */
#screen-auth .btn.btn-tg:disabled{opacity:0.4; transform:none; filter:none; cursor:default}
#screen-auth .btn.btn-tg.is-locked{opacity:0.45; transform:none; filter:none; cursor:not-allowed}
/* ⚠ БЕГУЩИЙ БЛИК СНИМАЕТСЯ ВМЕСТЕ С ФОНОМ. Он рисуется псевдоэлементом поверх
   кнопки — белая полоса в 35% непрозрачности. На золотой плашке это блик, а на
   прозрачной надписи — серый прямоугольник, ездящий сам по себе: ровно его
   владелец и увидел под словом «ВОЙТИ» [28.09, со снимком]. */
#screen-auth .btn.btn-tg::after,
#screen-login button.btn[onclick*="netLogin"]::after{content:none}

/* ── «ВОЙТИ» — ГЛАВНОЕ СЛОВО ЭКРАНА ВХОДА ────────────────────────────────────
   Пять букв, и места у них вдоволь: кегль здесь вдвое крупнее, чем у длинной
   телеграмной фразы, разрядка шире. Ни рамки, ни фона — надпись, а не кнопка. */
#screen-login button.btn[onclick*="netLogin"]{
    --login-track:clamp(0.16em,1vw,0.26em);
    /* ⚠ МЕЛЬЧЕ ЗАГОЛОВКА, И ЭТО НАМЕРЕННО. Слово входа не должно перебивать
       «ИГРУ МУДРЕЦОВ» — у экрана один главный голос [владелец, 28.09]. */
    font-size:clamp(1.15rem,5vw,1.5rem);
    letter-spacing:var(--login-track);
    word-spacing:0;
    /* ⚠ ХВОСТ РАЗРЯДКИ ГАСИМ ОТСТУПОМ СЛЕВА, А НЕ ОТРИЦАТЕЛЬНЫМ ПОЛЕМ СПРАВА.
       Разрядка добавляется и после последней буквы, поэтому слово стоит не по
       оси. Отрицательное правое поле это чинило, но ломало само центрирование:
       кнопка держится на margin:auto с обеих сторон, и стоило одному из полей
       стать числом — слово уезжало вправо [владелец, 28.09, со снимком].
       Отступ слева добавляет столько же, сколько разрядка отняла справа, и
       auto-поля остаются нетронутыми. */
    margin:8px auto 0;
    padding:6px 0 6px var(--login-track);
    filter:drop-shadow(0 0 0.3em rgba(246,193,71,0.35));
}
/* ⚠ «ЗАБЫЛИ ПАРОЛЬ?» СТОИТ ВПЛОТНУЮ, И МЕШАЛА ЕМУ НЕ ССЫЛКА, А ПУСТАЯ СТРОКА
   ОШИБКИ МЕЖДУ НИМИ. #login-error держал восемнадцать пикселей высоты и восемь
   отступа, ничего при этом не показывая: три десятка пикселей пустоты, которых
   не видно в разметке. Пока текста нет — строки нет вовсе; появится ошибка,
   она вернётся в поток вместе со своей высотой. Тем же приёмом убрана пустая
   строка состояния на экране входа в комнату (.nc-say:empty). */
#screen-login #login-error{min-height:18px; margin-top:8px}
#screen-login #login-error:empty{min-height:0; margin:0; display:none}
#screen-login button.btn[onclick*="netLogin"]:hover,
#screen-login button.btn[onclick*="netLogin"]:focus-visible{
    filter:drop-shadow(0 0 0.5em rgba(246,193,71,0.6))
           drop-shadow(0 0 1.4em rgba(234,168,44,0.35));
}

/* ── СЛОВО РАССЫПАЕТСЯ В ПЫЛЬ ─────────────────────────────────────────
   Холст на весь экран: на нём netAuthDust() рисует песчинки, в которые
   осыпается надпись «ВОЙТИ». Он создаётся на время разлёта и убирается сам.

   ⚠ ЗДЕСЬ НАРОЧНО ПОЧТИ НЕТ СТИЛЯ. Двигаются не элементы, а точки внутри
   холста — это дело кода, а не CSS. Ни анимаций, ни will-change: холст и так
   перерисовывается каждый кадр целиком.

   ⚠ ПОТОЛОК z-index, А НЕ «ПОБОЛЬШЕ». Пыль обязана перекрыть ВСЁ: знак Инь-Ян,
   карточку формы, самолётик (200002) и прежние слои входа (200001). Число ниже
   любого из них — и облако уйдёт под картинку ровно там, где на него смотрят.

   ⚠ pointer-events:none ОБЯЗАТЕЛЕН. Секунду холст лежит поверх всей страницы;
   без этой строки он собирал бы нажатия на себя. Главная защита всё же не здесь,
   а в самом коде: узел снимается, а не прячется. */
.auth-dust{position:fixed; inset:0; width:100%; height:100%;
    z-index:2147483000; pointer-events:none}
/* Просили не двигать — не двигаем: разлёта нет вовсе, вход начинается сразу
   (netAuthDust выходит на первой же строке). */
@media (prefers-reduced-motion:reduce){ .auth-dust{display:none} }

/* ⚠ БУКВЫ РАЗЛЕТАЮТСЯ ТОЛЬКО У ТЕЛЕГРАМНОЙ ФРАЗЫ [30.09]. Раньше их резало на
   обоих экранах. Теперь слово «ВОЙТИ» рассыпается в пыль собственными пикселями
   (netAuthDust), и разлетающиеся поверх неё буквы были бы вторым, чужим
   движением. Здесь остался экран регистрации: у него самолётик, и буквы уходят
   вслед за ним. Направление считает netAuthGo (--cx/--cy). */
#screen-auth .btn.btn-tg.is-fly .auth-ch{
    /* ×1,6 [01.10, владелец]: было 700 мс и шаг 14 мс — на телефоне не видно. */
    animation:authChBurst 1150ms cubic-bezier(.15,.7,.3,1) forwards;
    animation-delay:calc(var(--i) * 22ms);
}

/* ── САМОЛЁТИК ЛЕТИТ ПОВЕРХ ВСЕГО ────────────────────────────────────────────
   ⚠ ОН ЛЕТИТ НЕ ВНУТРИ КНОПКИ, А В СОБСТВЕННОМ СЛОЕ НА ВЕСЬ ЭКРАН. Пока он
   оставался ребёнком кнопки, любой z-index был бессилен: карточка, поля и
   нижний ряд — соседи кнопки, и значок уходил ПОД них, потому что порядок
   слоёв решается между родителями, а не между их детьми. Поэтому netAuthGo
   переносит копию значка в этот слой, в те же экранные координаты, а
   настоящий на время прячет. Слой убирается сам. */
.auth-fly{position:fixed; inset:0; z-index:200002; pointer-events:none; overflow:hidden}
/* ⚠ МЕНЬШЕ И МЕДЛЕННЕЕ [владелец, выкладка 158: «слишком крупный и летит очень
   быстро»]. Было: 1,5 с через 80 % ширины экрана, к концу ×0,7. Стало: 2,6 с на
   коротком пути (40 % ширины), самолётик сразу уменьшается вдвое и тает вдали.
   Срок связан с net.js: netAuthPlane убирает слой, netAuthGo ждёт конца полёта. */
.auth-fly svg{
    position:absolute; width:var(--w,24px); height:var(--w,24px); color:#F6C147;
    filter:drop-shadow(0 0 5px rgba(246,193,71,0.5));
    will-change:transform,opacity;
    animation:authPlaneFly 2.6s cubic-bezier(.4,0,.6,1) forwards;
}
/* Настоящий значок на время полёта прячется — иначе их видно два. */
#screen-auth .btn.btn-tg.is-fly .btn-tg-i{visibility:hidden}
@media (prefers-reduced-motion:reduce){ .auth-fly{display:none} }

/* ── ОБЯЗАТЕЛЬНОЕ ПОЛЕ: КРАСНАЯ ПОДСКАЗКА ───────────────────────────────────
   Обязательность показывает ЦВЕТ САМОЙ ПОДСКАЗКИ, а не отдельный значок
   [30.09, владелец]. Пусто — подсказка красная; начал печатать — текст обычный,
   светлый (его цвет задан у input выше и подсказки не касается); стёр всё —
   подсказка снова красная. Всё это делает браузер сам: ::placeholder видно
   ровно тогда, когда поле пусто.

   ⚠ ЗДЕСЬ БЫЛИ ЗВЁЗДОЧКИ, И ИХ УБРАЛИ НЕ ЗА ВИД. Каждая звёздочка требовала
   завернуть поле в <span class="req-wrap">, а обёртка меняла полю родителя —
   и netSellerGate, который прячет поля первого шага через style.display,
   прятал ОДИН узел, а показывал ДРУГОЙ. Имя и почта исчезали с экрана совсем,
   оставалась висеть одна звёздочка [владелец, 30.09, со снимком]. Цвет
   подсказки не заводит ни узлов, ни обёрток и сломаться так не может.

   ⚠ ПРИЗНАК — АТРИБУТ required У САМОГО ПОЛЯ, А НЕ КЛАСС. Класс надо не забыть
   повесить; required стоит там же, где о поле думают, и его видит ещё и
   программа чтения с экрана. Новое обязательное поле получает пометку тем, что
   его таким объявили, — отдельного шага нет.
   ⚠ НАТИВНОЙ ПРОВЕРКИ БРАУЗЕРА ЭТО НЕ ВКЛЮЧАЕТ: на экране нет <form>, а кнопки
   объявлены type="button". Проверяют по-прежнему netRegister и сервер. */
input[required]::placeholder, textarea[required]::placeholder { color:#d9534f; opacity:1 }
/* Firefox до 51-го и старый WebKit — те же слова другими именами. */
input[required]::-moz-placeholder { color:#d9534f; opacity:1 }
input[required]::-webkit-input-placeholder { color:#d9534f }


@keyframes authChBurst{
    0%  {opacity:1; transform:translate(0,0) rotate(0) scale(1)}
    100%{opacity:0; transform:translate(var(--cx,0),var(--cy,-1.2em)) rotate(var(--cr,0deg)) scale(0.6)}
}

/* ── «РЕГИСТРАЦИЯ ЧЕРЕЗ БРАУЗЕР» ─────────────────────────────────────────────
   Запасная дорога для тех, у кого нет Telegram. Намеренно тихая: её ищут те,
   кому она нужна, а всем остальным она не должна спорить с главной кнопкой. */
/* ⚠ ВПЛОТНУЮ К КНОПКЕ TELEGRAM: это один блок, а не два соседних. Ссылка —
   продолжение той же мысли («а если Telegram нет»), и десять пикселей между
   ними читались как раздел экрана [владелец, 28.09]. */
.reg-browser-link{
    display:block; margin:0 auto; padding:2px 4px 6px;
    background:none; border:0; width:auto;
    color:#6f6880; font-size:11.5px; font-weight:400; letter-spacing:0.04em;
    text-transform:none; text-decoration:underline; text-underline-offset:2px;
    cursor:pointer; transition:color .2s ease;
}
.reg-browser-link:hover, .reg-browser-link:focus-visible{outline:none; color:var(--gold-text)}
.reg-browser-link::after{content:none}
/* Разворачивается плавно, а не появляется рывком. */
#reg-browser{display:grid; grid-template-rows:0fr; overflow:hidden;
    transition:grid-template-rows .42s ease, opacity .3s ease; opacity:0}
#reg-browser > *{min-height:0}
#reg-browser.open{grid-template-rows:1fr; opacity:1; margin-top:10px}

/* ── ФРАЗА УЛЕТАЕТ, И ТОЛЬКО ПОТОМ НАЧИНАЕТСЯ ВХОД ───────────────────────────
   Самолётик уходит за край, буквы разлетаются вверх по очереди. Буквы режет
   netAuthGo() в момент нажатия, задержку держит она же — 620 мс, чуть дольше
   самой длинной дорожки. Движутся только transform и opacity: их браузер
   считает без перерисовки. */
.auth-ch{display:inline-block; white-space:pre}
#screen-auth .btn.btn-tg.is-fly, #screen-login button.btn.is-fly{
    pointer-events:none; transform:none; filter:none
}
/* ⚠ ДЛИТЕЛЬНОСТИ И ЗАДЕРЖКА В net.js СВЯЗАНЫ. Буква 0,9 с, шаг 45 мс: последняя
   из двадцати трогается на 855 мс и садится на 1755. Самолётик (копия в слое
   .auth-fly, настоящий значок в это время скрыт) летит 2,6 с — netAuthGo ждёт
   его, а с ним и буквы. Поменяешь здесь — поменяй и там. */
#screen-auth .btn.btn-tg.is-fly .auth-ch{
    animation:authChBlow .9s cubic-bezier(.4,0,.6,1) forwards;
    animation-delay:calc(var(--i) * 45ms)
}
/* Сначала уменьшается вдвое, потом спокойно уходит вверх-вправо и тает. */
@keyframes authPlaneFly{
  15%{ transform:translate(1.5vw,-1vh) rotate(4deg) scale(.55); opacity:1 }
  to{ transform:translate(40vw,-26vh) rotate(12deg) scale(.4); opacity:0 }
}
@keyframes authChBlow{ to{opacity:0; transform:translate(0.4em,-1.1em) rotate(-8deg)} }
@media (prefers-reduced-motion:reduce){
  #screen-auth .btn.btn-tg.is-fly .auth-ch{ animation:none }
}

/* ⚠ СТРОКИ С НОМЕРОМ ВЫКЛАДКИ ЗДЕСЬ НЕТ НАМЕРЕННО. 28.09 она была добавлена
   внизу экрана входа и в тот же день убрана по слову владельца: номер
   показывается только во всплывающем окне после нажатия ⟳. Первый экран —
   вход в игру, а не приборная панель. */

/* ── ВХОД — ОДИН ЦЕЛЬНЫЙ ЭКРАН [27.09, утверждённый макет] ───────────────────
   Не карточка, лежащая на звёздном фоне, а сам экран: плита занимает окно
   целиком, без скруглений и полей по краям. Нижний ряд (язык и обновление)
   стоит фиксированно поверх него, и место под него плита оставляет сама.

   ⚠ МЕТКУ auth-full СТАВИТ И СНИМАЕТ netAuthFull() в net.js — по тому, какой
   экран сейчас активен. Без снятия за ним растянулись бы лобби и стол.
   ⚠ ОТСТУПЫ <body> ЗДЕСЬ ОБНУЛЯЮТСЯ. Верхние 96 px держат место под игровую
   панель, которой на входе нет, а нижние сложились бы с отступом плиты — и
   экран поехал бы на вторую прокрутку при полной своей пустоте.
   ⚠ «.active» В СЕЛЕКТОРЕ ОБЯЗАТЕЛЕН, И ЭТО НЕ ПРИДИРКА. Экраны прячет
   правило «.screen{display:none}» весом в один класс. Напиши здесь
   «#screen-auth.container» без .active — правило весом в три перебьёт
   пряталку, и второй экран входа покажется вместе с первым: регистрация и
   вход окажутся на странице одновременно. */
/* ⚠ ЗАМОК ПРОКРУТКИ — НА ОБОИХ УЗЛАХ. overflow:hidden только на <body> телефон
   обходит: страницу всё равно можно подтянуть пальцем. Тем же приёмом заперт
   экран входа в комнату (nc-lock). Снимается вместе с меткой — за столом и в
   лобби прокрутка нужна.
   ⚠ С 27.09 ПО 01.10 ЭТОГО ПРАВИЛА НЕ БЫЛО ВОВСЕ. Комментарий выше закрывался
   на строку раньше, остаток текста браузер читал как селектор — и выбрасывал
   правило целиком. Нашёл stylelint (qa/, «Cannot parse selector»). Правите
   длинное пояснение — прогоните после этого npm run lint:css в qa/. */
html.auth-full, body.auth-full { height:100%; overflow:hidden; overscroll-behavior:none }
/* ⚠ ОТКРЫТАЯ КЛАВИАТУРА — НИЖНИЙ РЯД УХОДИТ [01.10, найдено Playwright на 390×420].
   Когда клавиатура открыта, окно на Android сжимается до ~420 px, а ряд с языком
   и обновлением стоит фиксированно внизу — ровно поверх поля кода: палец попадал
   в переключатель языка, а не в поле. Поля не двигаем — просто убираем ряд,
   пока человек печатает (фокус в поле) или пока окно такое низкое. */
@media (max-height:450px){
  body.auth-full #lang-switcher-wrap,
  body.auth-full #net-sync-btn { display:none !important }
}
/* ⚠ ПРАВИЛО ПРО ФОКУС ЖИВЁТ ТОЛЬКО В НИЗКОМ ОКНЕ [владелец, 03.10: «захожу
   регистрироваться — кнопки обновления и языка не появляются, а потом сами
   приходят»]. Высоты у него не было вовсе, и ряд исчезал, едва курсор попадал в
   поле, — на телефоне в полный рост, на компьютере, где никакой клавиатуры нет
   и поверх поля ничего не стоит. Человек видел пустой низ экрана, щёлкал мимо
   поля, и кнопки «появлялись сами»: это и была жалоба.
   560 — та высота, ниже которой окно уже похоже на телефон с открытой
   клавиатурой (Android оставляет ~420); выше ряд никому не мешает. */
@media (max-height:560px){
  body.auth-full:has(#screen-auth input:focus, #screen-login input:focus) #lang-switcher-wrap,
  body.auth-full:has(#screen-auth input:focus, #screen-login input:focus) #net-sync-btn { display:none !important }
}
/* ⚠ ВЕС СЕЛЕКТОРА ЗДЕСЬ НЕ УКРАШЕНИЕ. Верхний отступ задан выше правилом
   body:not(.gos-hdr):not(.gos-bar-on) весом в три класса; напиши тут просто
   «body.auth-full» — и оно перебьёт, экран станет на восемь пикселей ниже
   окна, страница поедет и внизу покажется полоса подложки. */
body.auth-full:not(.gos-hdr):not(.gos-bar-on) { padding-top:0 }
body.auth-full { padding-bottom:0 }
body.auth-full #screen-auth.container.active,
body.auth-full #screen-login.container.active {
    width:100%; max-width:560px; margin:0; border-radius:0;
    /* Ровно окно, не больше и не меньше: при min-height лишний пиксель внизу
       сразу становится полосой чужого фона. */
    /* ⚠ ПО ГОРИЗОНТАЛИ — НИКОГДА. overflow-y:auto сам по себе включает и
       горизонтальную полосу, стоит содержимому вылезти вбок хоть на пиксель:
       браузер не умеет «авто по вертикали и видимо по горизонтали». Именно эта
       полоса и появилась внизу на компьютере [28.09, со снимком]. По вертикали
       прокрутка нужна: открытая форма регистрации честно выше окна. */
    height:100dvh; overflow-y:auto; overflow-x:hidden;
    /* ⚠ ПОЛОСЫ ПРОКРУТКИ НЕ ВИДНО ВОВСЕ [30.09, владелец: «ни горизонтальной,
       ни вертикальной»]. До этого она была тонкой и бронзовой — владелец решил,
       что и такой быть не должно: экран обязан читаться как приложение, а не как
       страница в браузере.

       ⚠ СКРЫТА ПОЛОСА, А НЕ ПРОКРУТКА. overflow-y выше остаётся auto, и это не
       полумера: открытая форма регистрации ЧЕСТНО выше окна, и заприте её
       overflow:hidden — нижние поля и кнопка станут недостижимы ничем. Пальцем и
       колесом экран прокручивается как прежде, просто молча. */
    scrollbar-width:none;
    -ms-overflow-style:none;
    /* ⚠ ПО ЦЕНТРУ — ЧЕРЕЗ auto-ПОЛЯ У КРАЙНИХ ДЕТЕЙ (ниже), А НЕ ВЫРАВНИВАНИЕМ
       ПО ГЛАВНОЙ ОСИ. Правило проекта, см. tests/overlay-scroll.test.js: у
       прокручиваемой колонки такое выравнивание выталкивает содержимое в ОБЕ
       стороны, и верх уходит за нулевую точку блока — достать его нельзя
       ничем, палец упирается в конец короткого диапазона. Владелец жаловался
       на это дважды. auto-поля дают ту же картинку, пока содержимое ниже
       окна, и честно схлопываются в ноль, когда выше.
       ⚠ Тот же тест читает и эту запись целиком, вместе с пояснением: не
       называй здесь запретное значение даже в рассказе о том, почему его
       нельзя, — проверка не отличает объяснение от кода. */
    display:flex; flex-direction:column; justify-content:flex-start;
    padding:calc(16px + env(safe-area-inset-top,0px)) 22px
            calc(var(--bar-bottom,64px) + env(safe-area-inset-bottom,0px) + 18px);
}
body.auth-full #screen-auth.container.active > *:first-child,
body.auth-full #screen-login.container.active > *:first-child { margin-top:auto }
body.auth-full #screen-auth.container.active > *:last-child,
body.auth-full #screen-login.container.active > *:last-child { margin-bottom:auto }
/* ⚠ МЕСТО ПОД НИЖНИЙ РЯД ДЕРЖИТ РАСПОРКА, А НЕ padding-bottom [30.09, найдено
   браузерной проверкой]. Отступ у колонки был и раньше — 82 px, — но в
   ПРОКРУЧИВАЕМОЙ flex-колонке нижний padding не попадает в область прокрутки:
   замерено на живой странице, scrollHeight 853 при кнопке, кончающейся на 830,
   то есть отступа в прокрутке нет вовсе. Из-за этого последняя кнопка экрана
   («Установить приложение») уходила под ряд с языком и обновлением: нажатие по
   её середине ловил переключатель языка.

   Распорка — настоящий flex-элемент, и в прокрутку она входит: та же страница
   после правки даёт scrollHeight 927, а кнопка встаёт на 701…745 — выше ряда,
   который начинается на 798.

   ⚠ ::after НЕ ПОПАДАЕТ ПОД ПРАВИЛО last-child ВЫШЕ: оно про элементы, а это
   псевдоэлемент. Прижим содержимого к середине auto-полями работает как прежде. */
body.auth-full #screen-auth.container.active::after,
body.auth-full #screen-login.container.active::after{
    content:''; display:block; flex:0 0 auto; width:100%;
    height:calc(var(--bar-bottom,64px) + env(safe-area-inset-bottom,0px) + 10px);
}
/* ⚠ НИЧТО В ЭТОЙ КОЛОНКЕ НЕ УЖИМАЕТСЯ. Высота экрана заперта, и по умолчанию
   flex отнимает недостающее у детей — они сжимаются по высоте, а не выходят за
   край. Отсюда сразу два дефекта, которые выглядели как разные [28.09]:
     • знак становился вдвое меньше, стоило открыть форму регистрации;
     • сама форма на компьютере показывала одну почту — остальное срезалось
       её собственным overflow:hidden.
   Причина одна, поэтому и лечение одно. Не влезло — колонка прокручивается
   (overflow-y:auto выше), а не давит содержимое. */
body.auth-full #screen-auth.container.active > *,
body.auth-full #screen-login.container.active > * { flex:0 0 auto }
/* Запасной вариант для браузеров без dvh — там окно меряется в vh. */
@supports not (height:100dvh){
  body.auth-full #screen-auth.container.active,
  body.auth-full #screen-login.container.active { height:100vh }
}
@media (max-width:480px){
  body.auth-full #screen-auth.container.active,
  body.auth-full #screen-login.container.active { padding-left:13px; padding-right:13px }
}
/* То же самое для браузеров на WebKit — они не знают scrollbar-width. Нулевая
   ширина именно скрывает полосу: прокрутка при этом работает полностью. */
body.auth-full #screen-auth.container.active::-webkit-scrollbar,
body.auth-full #screen-login.container.active::-webkit-scrollbar{width:0; height:0}

/* ── КНОПКА «ЗАРЕГИСТРИРОВАТЬСЯ ЧЕРЕЗ TELEGRAM» ──────────────────────────────
   Единственное действие на экране регистрации, поэтому она крупнее обычной
   золотой кнопки и несёт знак Telegram. Знак — вектор в разметке, а не эмодзи
   и не картинка: эмодзи на Android рисуется своим цветом и ломает золото, а
   картинка — ещё один запрос там, где человек и так ждёт. */
#screen-auth .btn.btn-tg {
    display:flex; align-items:center; justify-content:center; gap:10px;
    width:100%; margin-top:4px; padding:16px 18px;
    font-size:15.5px; font-weight:800; letter-spacing:0.01em; line-height:1.25;
}
#screen-auth .btn.btn-tg .btn-tg-i { flex:0 0 auto; }
/* Ожидание: кнопка гаснет и перестаёт нажиматься, текст меняет netRegister. */
#screen-auth .btn.btn-tg.is-busy { opacity:0.75; cursor:progress; }

/* ── «Уже есть кабинет» — одной строкой, только внутри Telegram ─────────────
   Блок намеренно тихий: он нужен меньшинству, но без него человек с готовым
   кабинетом заводит второй — молча. Раскрывается по нажатию. */
#reg-tglink { margin-top:12px; text-align:center; }
#reg-tglink .rtl-open {
    font-size:12.5px; color:#94a3b8; text-decoration:underline; cursor:pointer;
}
#reg-tglink .rtl-open:hover { color:var(--accent-gold); }
#reg-tglink .rtl-form { margin-top:10px; }
#reg-tglink .rtl-form input { width:100%; box-sizing:border-box; }
#reg-tglink .rtl-say { min-height:16px; margin:6px 0 0; font-size:12.5px; color:#94a3b8; }

/* Отступы экрана регистрации: поле, согласие и кнопка стоят на равном шаге.
   Экран короткий, и слипшиеся блоки на нём заметнее, чем на длинной форме. */
#screen-auth #reg-name { margin:14px 0 12px; }
#screen-auth #reg-oferta-row { margin:0 2px 14px; }
