/* =========================================================================
   ИГРОВЫЕ РЕЖИМЫ — единый визуальный слой (Мины / Bubbles / Dice)
   Подключается ПОСЛЕ styles.css и намеренно переопределяет старые правила.
   Все имена классов с префиксом gx-, кроме тех, от которых зависит JS
   (.cell, .revealed-*, .mines-coefficient-item, .bubbles-*, .dice-*).
   ========================================================================= */

/* ---------------------------------------------------------------- ТОКЕНЫ */

.game-wrapper.gx {
    --gx-ink: #0b1a33;
    --gx-ink-2: #33507a;
    --gx-muted: #7e94b3;
    --gx-line: #e3ecf9;
    --gx-line-2: #eff4fd;
    --gx-bg: #ffffff;
    --gx-bg-2: #f7faff;
    --gx-bg-3: #edf3ff;
    --gx-blue: #2563eb;
    --gx-blue-d: #1d4ed8;
    --gx-blue-l: #60a5fa;
    --gx-sky: #38bdf8;
    --gx-win: #0ea472;
    --gx-win-bg: #e9fbf4;
    --gx-lose: #e23b52;
    --gx-lose-bg: #fef0f2;

    /* ОБЪЁМ КЛЕТОК. Эти четыре штуки и делают вид «выпуклой плитки»:
       gloss — блик по верхней кромке, edge — тень-подставка снизу.
       В тёмной теме они ОБЯЗАНЫ переворачиваться: блик в 0.9 белого и
       светло-голубая подставка на тёмном фоне выглядят как грязь. */
    --gx-gloss: rgba(255, 255, 255, .9);
    --gx-edge: #dbe6f8;
    --gx-edge-ok: #93e3c9;
    --gx-edge-lose: #f7b5bf;

    /* «Монетка» на закрытой клетке — три стопа радиального градиента. */
    --gx-coin-a: #c2d7fa;
    --gx-coin-b: #a3c1f4;
    --gx-coin-c: #8db3f0;

    --gx-shadow: 0 8px 26px rgba(37, 99, 235, .07);
    --gx-shadow-lg: 0 18px 44px rgba(37, 99, 235, .16);
    --gx-r: 20px;
    --gx-r-sm: 12px;
    --gx-ease: cubic-bezier(.34, 1.4, .64, 1);

    /* сброс старых правил .game-wrapper */
    background: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    padding: 0;
    margin: 0 auto;
    overflow: visible;

    container-type: inline-size;
    container-name: gx;
    color: var(--gx-ink);
}

/* ------------------------------------------------------ ТЁМНАЯ ТЕМА

   У игровых режимов свой набор токенов (--gx-*) — он был здесь задолго до
   общей темы. Не переписываем его на общие токены целиком: светлая тема
   выверена и трогать её незачем. Вместо этого в тёмной ПЕРЕНАЦЕЛИВАЕМ
   --gx-* на общую палитру, и весь игровой слой темнеет вместе с сайтом.

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

   Специфичность :root[data-theme] + .game-wrapper.gx выше, чем у самого
   .game-wrapper.gx, — поэтому переопределение срабатывает.

   Оттенки, которых нет в общей палитре (--gx-blue-l, --gx-sky), заданы
   числом: они играют роль подсветки и в тёмной теме должны быть ярче.  */

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .game-wrapper.gx {
        --gx-ink:     var(--text);
        --gx-ink-2:   var(--text-2);
        --gx-muted:   var(--text-dim);
        --gx-line:    var(--border);
        --gx-line-2:  var(--border-soft);
        --gx-bg:      var(--surface);
        --gx-bg-2:    var(--surface-2);
        --gx-bg-3:    var(--surface-3);
        --gx-blue:    var(--accent);
        --gx-blue-d:  var(--accent-hover);
        --gx-blue-l:  #7cb4ff;
        --gx-sky:     #5fccff;
        --gx-win:     var(--ok);
        --gx-win-bg:  var(--ok-soft);
        --gx-lose:    var(--danger);
        --gx-lose-bg: var(--danger-soft);

        /* Блик почти гасим, подставку делаем ТЕМНЕЕ поверхности — только так
           плитка читается выпуклой на тёмном. Монетка приглушённая стальная:
           прежние светло-голубые стопы горели на тёмной клетке пятном. */
        --gx-gloss:    rgba(255, 255, 255, .05);
        --gx-edge:     #0a0f16;
        --gx-edge-ok:  #14513c;
        --gx-edge-lose:#5a2028;
        --gx-coin-a:   #5a76a6;
        --gx-coin-b:   #46608a;
        --gx-coin-c:   #374d70;
    }
}

:root[data-theme="dark"] .game-wrapper.gx {
    --gx-ink:     var(--text);
    --gx-ink-2:   var(--text-2);
    --gx-muted:   var(--text-dim);
    --gx-line:    var(--border);
    --gx-line-2:  var(--border-soft);
    --gx-bg:      var(--surface);
    --gx-bg-2:    var(--surface-2);
    --gx-bg-3:    var(--surface-3);
    --gx-blue:    var(--accent);
    --gx-blue-d:  var(--accent-hover);
    --gx-blue-l:  #7cb4ff;
    --gx-sky:     #5fccff;
    --gx-win:     var(--ok);
    --gx-win-bg:  var(--ok-soft);
    --gx-lose:    var(--danger);
    --gx-lose-bg: var(--danger-soft);

    --gx-gloss:    rgba(255, 255, 255, .05);
    --gx-edge:     #0a0f16;
    --gx-edge-ok:  #14513c;
    --gx-edge-lose:#5a2028;
    --gx-coin-a:   #5a76a6;
    --gx-coin-b:   #46608a;
    --gx-coin-c:   #374d70;
}

.game-wrapper.gx::before { display: none; }

.gx *,
.gx *::before,
.gx *::after { box-sizing: border-box; }

.gx button { font-family: 'Inter', sans-serif; }

.gx .gx-num-font {
    font-variant-numeric: tabular-nums;
    letter-spacing: -.02em;
}

/* ------------------------------------------------------------- ШАПКА ИГРЫ */

.gx-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    flex-wrap: wrap;
    margin-bottom: 16px;
    padding: 0;
    border: 0;
}

.gx-head::after { display: none; }

.gx-id {
    display: flex;
    align-items: center;
    gap: 12px;
    cursor: pointer;
    border-radius: 16px;
    padding: 6px 12px 6px 6px;
    transition: background .2s ease;
    min-width: 0;
}

.gx-id:hover { background: var(--gx-bg-3); }

.gx-id__back {
    width: 26px;
    height: 26px;
    flex: none;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: var(--surface);
    border: 1px solid var(--gx-line);
    color: var(--gx-blue);
    font-size: 17px;
    font-weight: 700;
    line-height: 1;
    padding-bottom: 2px;
    transition: transform .2s var(--gx-ease), border-color .2s ease;
}

.gx-id:hover .gx-id__back {
    transform: translateX(-3px);
    border-color: var(--gx-blue-l);
}

.gx-id__icon {
    width: 46px;
    height: 46px;
    flex: none;
    display: grid;
    place-items: center;
    border-radius: 15px;
    background: linear-gradient(150deg, var(--surface) 0%, var(--gx-bg-3) 100%);
    border: 1px solid var(--gx-line);
    box-shadow: 0 6px 16px rgba(37, 99, 235, .08);
    overflow: hidden;
}

/* Правила под <img> здесь больше нет: все шесть режимов держат в шапке svg
   из общего набора GAME_ICONS. Мёртвое правило под картинку тем и опасно,
   что молча ждёт — вернёт кто-нибудь png, и он подхватит чужие размеры
   вместо того, чтобы сразу выглядеть неправильно. */
.gx-id__icon svg {
    width: 22px;
    height: 22px;
    color: var(--gx-blue);
}

.gx-id__txt { min-width: 0; }

.gx-id__txt h1 {
    font-size: 21px;
    font-weight: 800;
    letter-spacing: -.4px;
    color: var(--gx-ink);
    line-height: 1.15;
    margin: 0;
}

.gx-id__txt p {
    margin: 2px 0 0;
    font-size: 12px;
    font-weight: 600;
    color: var(--gx-muted);
}

.gx-head__side {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.gx-tag {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 30px;
    padding: 0 12px;
    border-radius: 999px;
    background: var(--gx-bg-3);
    border: 1px solid var(--border);
    color: var(--gx-blue-d);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    white-space: nowrap;
}

.gx-tag__dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--ok);
    box-shadow: 0 0 0 3px rgba(34, 197, 94, .18);
    animation: gxPulse 2s ease-in-out infinite;
}

@keyframes gxPulse {
    0%, 100% { opacity: 1; }
    50% { opacity: .45; }
}

.gx-balance {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 16px;
    border-radius: 999px;
    background: var(--surface);
    border: 1px solid var(--gx-line);
    box-shadow: var(--gx-shadow);
    white-space: nowrap;
}

.gx-balance__label {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--gx-muted);
}

.gx-balance .game-balance-num {
    display: block;
    font-size: 16px;
    font-weight: 800;
    color: var(--gx-ink);
    letter-spacing: -.02em;
    font-variant-numeric: tabular-nums;
    background: none;
    -webkit-text-fill-color: currentColor;
}

/* ------------------------------------------------------------ РАСКЛАДКА */

.gx-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 16px;
    align-items: start;
}

/* ПАНЕЛЬ В ДВЕ КОЛОНКИ, ПОКА СТОЛ ЕЩЁ ОДНОКОЛОНОЧНЫЙ.
 *
 * Ниже 640px панель идёт над доской, и блоки в столбик вытягивали её так, что
 * доска уезжала за нижний край экрана: игрок видел ставку, число мин, будущий
 * выигрыш, кнопку — и ни одной клетки. Ради чего он пришёл, оказывалось ниже
 * сгиба.
 *
 * Блоки настроек встают парами, а выплата, кнопка и подсказка остаются во всю
 * ширину: они итоговые, делить их пополам незачем. Панель становится вдвое
 * ниже, и доска поднимается на экран. */
@container gx (min-width: 520px) and (max-width: 639.98px) {
    .gx-panel {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 14px;
        align-items: start;
    }

    .gx-panel > .gx-payout,
    .gx-panel > .gx-action,
    .gx-panel > .gx-hint {
        grid-column: 1 / -1;
    }
}

/* ДВЕ КОЛОНКИ С 640px, А НЕ С 720.
 *
 * Прежний порог в 720 казался безобидным, но средняя колонка макета доходит
 * до него только при окне от 1440px. То есть на любом обычном ноутбуке —
 * 1280, 1366, 1400 — стол разворачивался в один столб, панель занимала всю
 * ширину, а доска уходила под сгиб. Ровно то, что видно на скриншоте с
 * макбука.
 *
 * При 640 колонка стола складывается уже с окна 1221px. Панели остаётся
 * 268px, доске — около 350: для поля 5×5 это клетка в 60px, читаемо. */
@container gx (min-width: 640px) {
    .gx-layout {
        grid-template-columns: minmax(268px, 310px) minmax(0, 1fr);
        gap: 18px;
        align-items: stretch;
    }

    /* Bubbles — «дорожка» широкая, поэтому она живёт на всю ширину,
       а панель раскладывается в два столбца над ней. */
    .gx[data-game="bubbles"] .gx-layout { grid-template-columns: minmax(0, 1fr); }

    .gx[data-game="bubbles"] .gx-panel {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 16px;
        align-items: stretch;
    }

    .gx[data-game="bubbles"] .gx-panel > .gx-tiles { grid-column: 1 / -1; }

    /*
     * Тумблер — во всю ширину, как плитки.
     *
     * В одной ячейке двухколоночной панели он выглядел половинкой: сам
     * элемент узкий, а рядом пустовала вторая половина строки. Растянутый
     * во всю ширину он читается как строка настройки, а не как обрубок.
     */
    .gx[data-game="bubbles"] .gx-panel > .gx-switch {
        grid-column: 1 / -1;
        /* Подпись слева, переключатель у правого края — привычный порядок
           для строки настроек. */
        justify-content: space-between;
        /* Отступ здесь даёт gap сетки (16px); свой margin сложился бы с
           ним в двойной. */
        margin-top: 0;
    }

    /* order, а не перестановка в разметке: селектор input:checked + track
       требует, чтобы дорожка шла сразу за полем, иначе включённое
       состояние перестанет применяться. */
    .gx[data-game="bubbles"] .gx-panel > .gx-switch .gx-switch__text { order: 1; }
    .gx[data-game="bubbles"] .gx-panel > .gx-switch .gx-switch__track { order: 2; }
}

/* Карточка-основа */
.gx-card {
    background: var(--gx-bg);
    border: 1px solid var(--gx-line);
    border-radius: var(--gx-r);
    box-shadow: var(--gx-shadow);
}

.gx-panel {
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding: 16px;
}

.gx-stage {
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 18px;
    overflow: hidden;
}

.gx-stage::before {
    content: '';
    position: absolute;
    inset: 0;
    /* ВНИМАНИЕ: это накладка на ВСЮ площадь карточки (inset: 0), и красит её
       именно она, а не фон под ней. Пока стопы были заданы числами
       (#fbfdff → #ffffff), поле мин и дайса оставалось белым в тёмной теме,
       сколько ни правь --gx-bg: накладка перекрывала его сверху. */
    background:
        radial-gradient(120% 70% at 50% -10%, rgba(96, 165, 250, .16) 0%, transparent 62%),
        linear-gradient(180deg, var(--gx-bg-2) 0%, var(--gx-bg) 55%);
    pointer-events: none;
}

.gx-stage > * { position: relative; }

/* -------------------------------------------------------------- ПОЛЯ */

.gx-block {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.gx-block__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
}

.gx-label {
    font-size: 10.5px;
    font-weight: 800;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--gx-muted);
}

.gx-label__val {
    font-size: 13px;
    font-weight: 800;
    color: var(--gx-blue-d);
    font-variant-numeric: tabular-nums;
}

/* Степпер (− значение +) */
.gx-stepper {
    display: grid;
    grid-template-columns: 42px minmax(0, 1fr) 42px;
    align-items: center;
    height: 50px;
    background: var(--gx-bg-2);
    border: 1px solid var(--gx-line);
    border-radius: 14px;
    overflow: hidden;
    transition: border-color .2s ease, box-shadow .2s ease;
}

.gx-stepper:focus-within,
.gx-stepper:hover {
    border-color: var(--accent-border);
    box-shadow: 0 0 0 4px rgba(37, 99, 235, .07);
}

.gx-stepper__btn {
    height: 100%;
    border: 0;
    background: transparent;
    color: var(--gx-blue);
    font-size: 21px;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
    transition: background .15s ease, color .15s ease, transform .1s ease;
}

.gx-stepper__btn:hover { background: rgba(37, 99, 235, .09); }
.gx-stepper__btn:active { transform: scale(.9); }

.gx-stepper__val {
    text-align: center;
    font-size: 19px;
    font-weight: 800;
    color: var(--gx-ink);
    font-variant-numeric: tabular-nums;
    letter-spacing: -.02em;
    user-select: none;
}

.gx-stepper__val--input {
    width: 100%;
    height: 100%;
    border: 0;
    background: transparent;
    outline: none;
    text-align: center;
    font-family: 'Inter', sans-serif;
    -moz-appearance: textfield;
}

.gx-stepper__val--input::-webkit-inner-spin-button,
.gx-stepper__val--input::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

/* Поле с приписанной единицей («2.00» + «x»): само значение остаётся
   числом, поэтому input может быть type="number" и правиться руками. */
.gx-stepper__field {
    display: flex;
    align-items: baseline;
    justify-content: center;
    gap: 2px;
    width: 100%;
    height: 100%;
    min-width: 0;
    cursor: text;        /* поле кликабельно целиком, включая суффикс */
}

.gx-stepper__field .gx-stepper__val--input {
    flex: 0 1 auto;
    width: 5ch;          /* хватает на «99.99» и на «9500» */
    min-width: 0;
    height: 100%;
    text-align: right;
}

.gx-stepper__field .gx-stepper__val--input::selection {
    background: rgba(37, 99, 235, .18);
}

.gx-stepper__suffix {
    flex: none;
    font-size: 15px;
    font-weight: 800;
    color: var(--gx-muted);
    user-select: none;
}

/* Чипы быстрых значений — всегда одна строка равных долей */
.gx-chips {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    gap: 6px;
}

.gx-chip {
    min-width: 0;
    height: 32px;
    padding: 0 10px;
    border-radius: 10px;
    border: 1px solid var(--gx-line);
    background: var(--surface);
    color: var(--gx-ink-2);
    font-size: 12px;
    font-weight: 700;
    cursor: pointer;
    transition: all .16s ease;
    font-variant-numeric: tabular-nums;
}

.gx-chip:hover {
    border-color: var(--gx-blue-l);
    color: var(--gx-blue-d);
    background: var(--gx-bg-3);
}

.gx-chip:active { transform: scale(.95); }

.gx-chip.is-active {
    background: linear-gradient(140deg, var(--gx-blue) 0%, var(--gx-blue-d) 100%);
    border-color: transparent;
    color: #fff;
    box-shadow: 0 6px 14px rgba(37, 99, 235, .28);
}

/* Горизонтальная лента чипов (кол-во мин) */
.gx-chips--scroll {
    grid-auto-columns: 44px;
    overflow-x: auto;
    padding-bottom: 4px;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 26px), transparent 100%);
    mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 26px), transparent 100%);
}

.gx-chips--scroll .gx-chip { scroll-snap-align: center; }

.gx-scroll::-webkit-scrollbar { height: 5px; }
.gx-scroll::-webkit-scrollbar-track { background: transparent; }
.gx-scroll::-webkit-scrollbar-thumb {
    background: var(--border);
    border-radius: 99px;
}
.gx-scroll::-webkit-scrollbar-thumb:hover { background: var(--border-strong); }
.gx-scroll {
    scrollbar-width: thin;
    scrollbar-color: #dbe6f7 transparent;
}

.gx-hidden { display: none !important; }

/* Плитка возможного выигрыша */
.gx-payout {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 13px 16px;
    border-radius: 14px;
    background: linear-gradient(135deg, var(--surface-accent) 0%, var(--surface-accent) 100%);
    border: 1px solid var(--accent-border);
}

.gx-payout__label {
    font-size: 10.5px;
    font-weight: 800;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.gx-payout__val {
    display: block;
    margin-top: 3px;
    font-size: 22px;
    font-weight: 800;
    color: var(--gx-blue-d);
    letter-spacing: -.5px;
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
}

.gx-payout__mult {
    flex: none;
    padding: 5px 11px;
    border-radius: 9px;
    background: linear-gradient(140deg, var(--gx-blue) 0%, var(--gx-blue-d) 100%);
    color: #fff;
    font-size: 12.5px;
    font-weight: 800;
    box-shadow: 0 5px 12px rgba(37, 99, 235, .3);
    font-variant-numeric: tabular-nums;
}

/* Главная кнопка действия */
.gx-action {
    width: 100%;
    min-height: 56px;
    padding: 9px 20px;
    border: 0;
    border-radius: 16px;
    background: linear-gradient(140deg, #3b82f6 0%, var(--gx-blue-d) 100%);
    color: #fff;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1px;
    box-shadow: 0 10px 24px rgba(37, 99, 235, .3);
    transition: transform .16s var(--gx-ease), box-shadow .2s ease, filter .2s ease;
    position: relative;
    overflow: hidden;
}

.gx-action::after {
    content: '';
    position: absolute;
    top: 0;
    left: -60%;
    width: 40%;
    height: 100%;
    background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .35), transparent);
    transform: skewX(-18deg);
    animation: gxSheen 3.6s ease-in-out infinite;
}

@keyframes gxSheen {
    0%, 62% { left: -60%; }
    100% { left: 130%; }
}

.gx-action:hover {
    transform: translateY(-2px);
    box-shadow: var(--gx-shadow-lg);
    filter: saturate(1.08);
}

.gx-action:active { transform: translateY(0) scale(.985); }

/* Ставка отправлена, ждём ответ. Кнопка гаснет на эти доли секунды — иначе
   по ней успевали нажать второй раз и заводился второй раунд. Блик тоже
   останавливаем: бегущая полоса на неактивной кнопке читается как «грузится
   и вот-вот оживёт», хотя жать уже нельзя. */
.gx-action:disabled {
    cursor: not-allowed;
    filter: grayscale(.55);
    opacity: .65;
    box-shadow: none;
}

.gx-action:disabled::after { animation: none; }

.gx-action:disabled:hover {
    transform: none;
    box-shadow: none;
    filter: grayscale(.55);
}

.gx-action__main {
    font-size: 15.5px;
    font-weight: 800;
    letter-spacing: -.01em;
}

.gx-action__sub {
    font-size: 11.5px;
    font-weight: 600;
    opacity: .82;
    font-variant-numeric: tabular-nums;
}

.gx-action--cash {
    background: linear-gradient(140deg, #10b981 0%, #059669 100%);
    box-shadow: 0 10px 24px rgba(5, 150, 105, .3);
}

.gx-action--cash:hover { box-shadow: 0 18px 44px rgba(5, 150, 105, .28); }

/* Плитки статистики */
.gx-tiles {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 8px;
}

.gx-tiles--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }

.gx-tile {
    padding: 11px 8px;
    border-radius: 13px;
    background: var(--gx-bg-2);
    border: 1px solid var(--gx-line-2);
    text-align: center;
}

.gx-tile__label {
    display: block;
    font-size: 9.5px;
    font-weight: 800;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--gx-muted);
    line-height: 1.25;
}

.gx-tile__val {
    display: block;
    margin-top: 5px;
    font-size: 17px;
    font-weight: 800;
    color: var(--gx-ink);
    font-variant-numeric: tabular-nums;
    letter-spacing: -.02em;
}

.gx-tile__val.highlight {
    background: linear-gradient(135deg, var(--gx-blue) 0%, #6366f1 100%);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
}

.gx-hint {
    font-size: 11px;
    color: var(--gx-muted);
    text-align: center;
    line-height: 1.45;
    font-weight: 500;
}

/* =====================================================================
   МИНЫ
   ===================================================================== */

.gx-board-wrap {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 16px;
}

.gx .game-board {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: clamp(7px, 2cqw, 13px);
    width: 100%;
    max-width: 460px;
    margin: 0;
    padding: clamp(8px, 1.6cqw, 14px);
    border-radius: 22px;
    background: linear-gradient(165deg, var(--surface-2) 0%, var(--surface-accent) 100%);
    border: 1px solid var(--border);
    box-shadow: inset 0 1px 0 var(--gx-gloss), 0 10px 30px rgba(37, 99, 235, .08);
}

.gx .cell {
    aspect-ratio: 1 / 1;
    border-radius: 15px;
    background: linear-gradient(160deg, var(--surface) 0%, var(--surface-accent) 100%);
    border: 1px solid var(--border);
    box-shadow: 0 2px 0 var(--gx-edge), inset 0 1px 0 var(--gx-gloss);
    display: grid;
    place-items: center;
    cursor: pointer;
    font-size: clamp(20px, 5cqw, 30px);
    line-height: 1;
    user-select: none;
    position: relative;
    transition: transform .18s var(--gx-ease), box-shadow .2s ease, border-color .2s ease, background .2s ease;
}

/* «монетка» на закрытой клетке */
/* размер в px, а не в %: процент от auto-трека грида схлопывается в 0 */
.gx .cell::before {
    content: '';
    width: clamp(9px, 2.4cqw, 18px);
    height: clamp(9px, 2.4cqw, 18px);
    border-radius: 50%;
    background: radial-gradient(circle at 35% 30%,
        var(--gx-coin-a) 0%, var(--gx-coin-b) 60%, var(--gx-coin-c) 100%);
    box-shadow: 0 1px 2px rgba(37, 99, 235, .22);
    opacity: 1;
    transition: opacity .2s ease, transform .2s var(--gx-ease);
}

.gx .cell:hover:not(.revealed-bomb):not(.revealed-safe) {
    transform: translateY(-3px);
    border-color: var(--gx-blue-l);
    box-shadow: 0 8px 18px rgba(37, 99, 235, .18), inset 0 1px 0 var(--gx-gloss);
}

.gx .cell:hover:not(.revealed-bomb):not(.revealed-safe)::before {
    transform: scale(1.25);
    opacity: 1;
}

.gx .cell:active:not(.revealed-bomb):not(.revealed-safe) {
    transform: translateY(-1px) scale(.96);
}

/* открыта — безопасная */
.gx .cell.revealed-safe {
    background: linear-gradient(160deg, var(--ok-soft) 0%, var(--ok-soft) 100%);
    border-color: var(--ok-border);
    box-shadow: 0 2px 0 var(--gx-edge-ok), 0 0 0 3px rgba(14, 164, 114, .1), inset 0 1px 0 var(--gx-gloss);
    cursor: default;
    animation: gxReveal .42s var(--gx-ease) both;
}

.gx .cell.revealed-safe::before { display: none; }

.gx .cell.revealed-safe::after {
    content: '💎';
    font-size: clamp(19px, 4.6cqw, 28px);
    color: var(--ok-strong);
    filter: drop-shadow(0 3px 5px rgba(5, 122, 85, .28));
    animation: gxPop .38s var(--gx-ease) both;
}

/* открыта — мина */
.gx .cell.revealed-bomb {
    background: linear-gradient(160deg, var(--danger-soft) 0%, var(--danger-soft) 100%);
    border-color: var(--danger-border);
    box-shadow: 0 2px 0 var(--gx-edge-lose), 0 0 0 3px rgba(226, 59, 82, .12);
    cursor: default;
    animation: gxShake .45s ease-in-out both;
}

.gx .cell.revealed-bomb::before { display: none; }

@keyframes gxReveal {
    0% { transform: rotateY(-82deg) scale(.9); opacity: .35; }
    100% { transform: rotateY(0) scale(1); opacity: 1; }
}

@keyframes gxPop {
    0% { transform: scale(0) rotate(-25deg); opacity: 0; }
    65% { transform: scale(1.22) rotate(6deg); opacity: 1; }
    100% { transform: scale(1) rotate(0); }
}

@keyframes gxShake {
    0%, 100% { transform: translateX(0); }
    18% { transform: translateX(-5px) rotate(-2deg); }
    38% { transform: translateX(5px) rotate(2deg); }
    58% { transform: translateX(-3px); }
    78% { transform: translateX(3px); }
}

/* лента коэффициентов */
.gx-coeffs {
    width: 100%;
    max-width: 560px;
}

.gx-coeffs__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 8px;
}

.gx .mines-coefficients-table {
    display: flex;
    gap: 7px;
    overflow-x: auto;
    padding: 2px 2px 6px;
    scroll-snap-type: x proximity;
}

.gx .mines-coefficient-item {
    flex: 0 0 auto;
    min-width: 62px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    padding: 8px 10px;
    border-radius: 12px;
    background: var(--surface);
    border: 1px solid var(--gx-line);
    color: var(--gx-muted);
    font-size: 10.5px;
    font-weight: 700;
    cursor: pointer;
    scroll-snap-align: center;
    transition: transform .45s var(--gx-ease), background .2s ease, border-color .2s ease, box-shadow .2s ease;
}

.gx .mines-coefficient-item:hover {
    border-color: var(--gx-blue-l);
    box-shadow: 0 5px 14px rgba(37, 99, 235, .12);
}

.gx .mines-coefficient-item .coeff-val {
    font-size: 14px;
    font-weight: 800;
    color: var(--gx-ink);
    font-variant-numeric: tabular-nums;
}

.gx .mines-coefficient-item.active {
    background: linear-gradient(140deg, var(--gx-blue) 0%, var(--gx-blue-d) 100%);
    border-color: transparent;
    color: rgba(255, 255, 255, .78);
    box-shadow: 0 7px 16px rgba(37, 99, 235, .28);
}

.gx .mines-coefficient-item.active .coeff-val { color: #fff; }

.gx .mines-coefficient-item.rotated { transform: rotate(360deg); }

/* =====================================================================
   DICE
   ===================================================================== */

.gx-dice-stage {
    display: flex;
    flex-direction: column;
    gap: 18px;
    align-items: center;
}

.gx-roll {
    width: 100%;
    max-width: 520px;
    text-align: center;
    padding: 26px 20px 22px;
    border-radius: 22px;
    background: linear-gradient(165deg, var(--surface-2) 0%, var(--surface-accent) 100%);
    border: 1px solid var(--border);
    box-shadow: inset 0 1px 0 var(--gx-gloss);
    position: relative;
    overflow: hidden;
}

.gx-roll__label {
    font-size: 10.5px;
    font-weight: 800;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.gx .dice-number-value {
    display: block;
    margin-top: 8px;
    font-size: clamp(40px, 13cqw, 66px);
    font-weight: 900;
    line-height: 1;
    letter-spacing: -3px;
    color: var(--gx-ink);
    font-variant-numeric: tabular-nums;
    transition: color .25s ease;
}

.gx .dice-number-value.rolling {
    color: var(--gx-blue);
    animation: gxRoll .09s infinite alternate;
}

.gx .dice-number-value.settled { animation: gxSettle .4s var(--gx-ease); }

.gx .dice-number-value.win {
    color: var(--gx-win);
    text-shadow: 0 6px 30px rgba(14, 164, 114, .35);
}

.gx .dice-number-value.lose {
    color: var(--gx-lose);
    text-shadow: 0 6px 30px rgba(226, 59, 82, .28);
}

@keyframes gxRoll {
    0% { transform: translateY(0); opacity: 1; }
    100% { transform: translateY(-3px); opacity: .68; }
}

@keyframes gxSettle {
    0% { transform: scale(1.2); opacity: .3; }
    60% { transform: scale(.96); opacity: 1; }
    100% { transform: scale(1); }
}

/* шкала 0 … 999 999 с зонами выигрыша */
.gx-scale { width: 100%; margin-top: 20px; }

.gx-scale__track {
    position: relative;
    height: 14px;
    border-radius: 999px;
    background: var(--border);
    box-shadow: inset 0 1px 3px rgba(15, 40, 80, .1);
    overflow: visible;
}

.gx-scale__zone {
    position: absolute;
    top: 0;
    bottom: 0;
    width: calc(var(--p, 50) * 1%);
    transition: width .25s ease;
}

.gx-scale__zone--less {
    left: 0;
    border-radius: 999px 0 0 999px;
    background: linear-gradient(90deg, #7dd3fc 0%, #38bdf8 100%);
}

.gx-scale__zone--more {
    right: 0;
    border-radius: 0 999px 999px 0;
    background: linear-gradient(90deg, var(--gx-blue) 0%, var(--gx-blue-d) 100%);
}

/* При шансе > 50% интервалы «меньше» и «больше» пересекаются:
   середина принадлежит обоим, показываем её отдельной штриховкой. */
.gx-scale__zone--both {
    left: calc((100 - var(--p, 50)) * 1%);
    right: auto;
    width: max(0%, calc((2 * var(--p, 50) - 100) * 1%));
    background: repeating-linear-gradient(
        135deg,
        #7dd3fc 0 6px,
        var(--gx-blue-d) 6px 12px);
    transition: left .25s ease, width .25s ease;
}

.gx-scale__note {
    display: none;
    margin: 8px 0 0;
    font-size: 10.5px;
    line-height: 1.4;
    font-weight: 600;
    color: var(--gx-muted);
    text-align: left;
}

.gx-scale.is-overlap .gx-scale__note { display: block; }

.gx-scale__marker {
    position: absolute;
    top: 50%;
    left: var(--m, 50%);
    width: 4px;
    height: 26px;
    margin-left: -2px;
    border-radius: 3px;
    background: var(--gx-ink);
    transform: translateY(-50%) scaleY(0);
    transform-origin: center;
    opacity: 0;
    transition: left .45s var(--gx-ease), opacity .2s ease, transform .3s var(--gx-ease), background .2s ease;
    box-shadow: 0 3px 10px rgba(11, 26, 51, .3);
}

.gx-scale__marker.is-on {
    opacity: 1;
    transform: translateY(-50%) scaleY(1);
}

.gx-scale__marker.win { background: var(--gx-win); }
.gx-scale__marker.lose { background: var(--gx-lose); }

.gx-scale__legend {
    display: flex;
    justify-content: space-between;
    margin-top: 9px;
    font-size: 10.5px;
    font-weight: 700;
    color: var(--gx-muted);
    font-variant-numeric: tabular-nums;
}

/* ползунок шанса */
.gx-range {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: 26px;
    background: transparent;
    cursor: pointer;
    margin: 0;
}

.gx-range::-webkit-slider-runnable-track {
    height: 8px;
    border-radius: 999px;
    background: linear-gradient(90deg,
        var(--gx-blue) 0%,
        var(--gx-blue) calc(var(--p, 50) * 1%),
        var(--gx-line) calc(var(--p, 50) * 1%),
        var(--gx-line) 100%);
}

.gx-range::-moz-range-track {
    height: 8px;
    border-radius: 999px;
    background: linear-gradient(90deg,
        var(--gx-blue) 0%,
        var(--gx-blue) calc(var(--p, 50) * 1%),
        var(--gx-line) calc(var(--p, 50) * 1%),
        var(--gx-line) 100%);
}

.gx-range::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 22px;
    height: 22px;
    margin-top: -7px;
    border-radius: 50%;
    background: var(--surface);
    border: 3px solid var(--gx-blue);
    box-shadow: 0 4px 12px rgba(37, 99, 235, .32);
    transition: transform .15s var(--gx-ease);
}

.gx-range::-moz-range-thumb {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--surface);
    border: 3px solid var(--gx-blue);
    box-shadow: 0 4px 12px rgba(37, 99, 235, .32);
}

.gx-range:hover::-webkit-slider-thumb { transform: scale(1.12); }
.gx-range:active::-webkit-slider-thumb { transform: scale(.95); }

.gx-range:focus-visible::-webkit-slider-thumb {
    box-shadow: 0 0 0 6px rgba(37, 99, 235, .16);
}

/* кнопки выбора */
.gx .dice-choices {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
    width: 100%;
    max-width: 520px;
    margin: 0;
}

.gx .dice-choice-btn {
    flex: none;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 15px 12px;
    border: 0;
    border-radius: 18px;
    color: #fff;
    cursor: pointer;
    overflow: hidden;
    transition: transform .16s var(--gx-ease), box-shadow .2s ease, filter .2s ease;
}

.gx .dice-choice-btn::before { content: none; }

.gx .dice-less-btn {
    background: linear-gradient(140deg, #7dd3fc 0%, #0ea5e9 100%);
    box-shadow: 0 10px 22px rgba(14, 165, 233, .28);
}

.gx .dice-more-btn {
    background: linear-gradient(140deg, #3b82f6 0%, var(--gx-blue-d) 100%);
    box-shadow: 0 10px 22px rgba(37, 99, 235, .3);
}

.gx .dice-choice-btn:hover:not(.dice-choice-disabled) {
    transform: translateY(-3px);
    filter: saturate(1.1);
}

.gx .dice-less-btn:hover:not(.dice-choice-disabled) { box-shadow: 0 16px 34px rgba(14, 165, 233, .32); }
.gx .dice-more-btn:hover:not(.dice-choice-disabled) { box-shadow: 0 16px 34px rgba(37, 99, 235, .34); }

.gx .dice-choice-btn:active:not(.dice-choice-disabled) { transform: translateY(0) scale(.97); }

.gx .dice-choice-btn.dice-choice-disabled {
    opacity: .45;
    filter: grayscale(.35);
    pointer-events: none;
}

.gx .dice-choice-btn.dice-choice-pressed { transform: scale(.94); }

.gx-choice__top {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 15.5px;
    font-weight: 800;
}

.gx-choice__arrow {
    display: grid;
    place-items: center;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: rgba(255, 255, 255, .22);
    font-size: 13px;
    line-height: 1;
}

.gx .dice-interval {
    flex: none;
    padding: 0;
    border: 0;
    background: transparent;
    color: rgba(255, 255, 255, .85);
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .01em;
    font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------------
   ИТОГ РАУНДА

   Итог раунда в дайсе. НЕ заменяйте всплывающим уведомлением: раунд
   длится секунду, играют подряд, и уведомления копятся стопкой поверх
   поля, закрывая и выпавшее число, и шапку с балансом.

   Блок держит своё место в раскладке ВСЕГДА, во всех трёх состояниях
   (idle / rolling / итог). Спрячете пустой через display: none — при
   появлении результата кнопки под пальцем прыгнут вниз.
   --------------------------------------------------------------------- */
.gx .gx-result-card {
    /* Ширина как у остальных блоков поля: .gx-roll и .dice-choices тоже
       100% при max-width 520px. Без этого карточка сжималась по своему
       тексту — родитель центрирует содержимое (align-items: center), и
       блок получался узкой плашкой посреди пустого поля. */
    width: 100%;
    max-width: 520px;

    /* Место занято ВСЕГДА, даже когда результата ещё нет.
       Появление из display: none сдвигало кнопки вниз на каждом броске:
       нажал — и всё под пальцем прыгнуло. Пустая карточка прозрачна и
       невидима, но высоту держит, поэтому раскладка не шевелится. */
    display: grid;
    gap: 2px;
    align-content: center;
    min-height: 88px;

    padding: 12px 16px;
    border-radius: 14px;
    text-align: center;
    border: 1px solid transparent;
    background: transparent;

    /* По центру своей колонки. В дайсе это делает родитель (align-items:
       center), а в bubbles блок лежит в обычном .bx шириной во всю
       колонку — без auto карточка прижалась бы влево. */
    margin-inline: auto;
}

/* Отступа сверху карточке не нужно: единственный, кто ей пользуется, —
   дайс, а там его даёт gap игрового поля (18px). В bubbles такой блок
   пробовали и убрали: итог там и так виден строкой состояния и бейджем
   у множителя. */

/* Анимируем содержимое, а не сам блок: он никуда не появляется, меняется
   только то, что внутри. */
.gx .gx-result-card.is-shown > * {
    animation: diceResultIn .22s ease-out both;
}

@keyframes diceResultIn {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: translateY(0); }
}

.gx .gx-result-card__title {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    opacity: .75;
}

.gx .gx-result-card__sum {
    font-size: 24px;
    font-weight: 800;
    line-height: 1.1;
    letter-spacing: -.02em;
    font-variant-numeric: tabular-nums;
}

.gx .gx-result-card__note {
    font-size: 12px;
    font-weight: 600;
    opacity: .7;
    font-variant-numeric: tabular-nums;
}

/* До первого броска и во время него — приглушённо: это не итог, а
   состояние ожидания, и спорить за внимание с полем оно не должно. */
.gx .gx-result-card.is-idle,
.gx .gx-result-card.is-rolling {
    background: var(--gx-bg-2, var(--surface-2));
    border-color: var(--gx-line, var(--border));
    color: var(--gx-muted, #94a3b8);
}

/* Сумму в этих состояниях не выделяем размером: там прочерк и многоточие,
   набирать их крупным жирным — всё равно что кричать «ничего нет». */
.gx .gx-result-card.is-idle .gx-result-card__sum,
.gx .gx-result-card.is-rolling .gx-result-card__sum {
    font-size: 18px;
    font-weight: 700;
}

.gx .gx-result-card.is-win {
    background: linear-gradient(135deg, var(--ok-soft), var(--ok-soft));
    border-color: var(--ok-border);
    color: var(--ok-strong);
}

.gx .gx-result-card.is-lose {
    background: linear-gradient(135deg, var(--danger-soft), var(--danger-soft));
    border-color: var(--danger-border);
    color: var(--danger);
}
/* =====================================================================
   BUBBLES
   Поле переписано под саму механику: сервер разыгрывает краш равномерно
   на [1.01, 10], игрок ставит цель — значит, показываем шкалу 1x…10x,
   флажок цели и рост множителя по ней. Ни пузыря, ни эмодзи.
   ===================================================================== */

.gx .bubbles.game-field {
    width: 100%;
    max-width: 620px;
    height: auto;
    min-height: 0;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 30px 26px 26px;
    border-radius: 22px;
    /* Стопы токенами, а не числами: поле баблов иначе остаётся белым в
       тёмной теме — см. такую же накладку у .gx-stage::before выше. */
    background:
        radial-gradient(90% 70% at 50% 0%, rgba(56, 189, 248, .14) 0%, transparent 65%),
        linear-gradient(165deg, var(--gx-bg-2) 0%, var(--gx-bg-3) 100%);
    border: 1px solid var(--border);
    /* Блик по верхней кромке. В светлой теме это белый, в тёмной — цвет
       поверхности, то есть блика попросту нет. Так и надо: на тёмном белая
       полоска выглядела бы царапиной. */
    box-shadow: inset 0 1px 0 var(--gx-bg);
    position: relative;
    overflow: hidden;
}

.bx {
    width: 100%;
    position: relative;
    z-index: 1;
}

/* ---------- крупный счётчик ---------- */

.bx__head {
    text-align: center;
    margin-bottom: 30px;
}

.bx__cap {
    display: block;
    font-size: 10.5px;
    font-weight: 800;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.bx__live {
    margin-top: 6px;
    font-size: clamp(38px, 11cqw, 60px);
    font-weight: 900;
    line-height: 1;
    letter-spacing: -2px;
    color: var(--gx-ink);
    font-variant-numeric: tabular-nums;
    transition: color .25s ease;
}

.bx.is-reached .bx__live {
    color: var(--gx-win);
    text-shadow: 0 6px 28px rgba(14, 164, 114, .3);
}

.bx.is-lost .bx__live {
    color: var(--gx-lose);
    text-shadow: 0 6px 28px rgba(226, 59, 82, .26);
    animation: gxShake .45s ease-in-out;
}

/* ---------- шкала 1x … 10x ---------- */

.bx__track {
    position: relative;
    height: 16px;
    border-radius: 999px;
    background: var(--border);
    box-shadow: inset 0 1px 3px rgba(15, 40, 80, .12);
}

.bx__fill {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: var(--fill, 0%);
    min-width: 16px;
    border-radius: 999px;
    background: linear-gradient(90deg, #7dd3fc 0%, var(--gx-blue) 100%);
    box-shadow: 0 2px 10px rgba(37, 99, 235, .3);
}

.bx.is-reached .bx__fill {
    background: linear-gradient(90deg, #6ee7c0 0%, var(--gx-win) 100%);
    box-shadow: 0 2px 10px rgba(14, 164, 114, .3);
}

.bx.is-lost .bx__fill {
    background: linear-gradient(90deg, #fca5b5 0%, var(--gx-lose) 100%);
    box-shadow: 0 2px 10px rgba(226, 59, 82, .28);
}

/* бегущий блик, пока множитель растёт */
.bx.is-live .bx__fill::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: 999px;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .55), transparent);
    animation: gxFlow 1s linear infinite;
}

@keyframes gxFlow {
    0% { transform: translateX(-100%); }
    100% { transform: translateX(100%); }
}

/* ---------- флажок цели ---------- */

.bx__flag {
    position: absolute;
    left: var(--target, 11%);
    top: 50%;
    width: 3px;
    height: 30px;
    margin-left: -1.5px;
    border-radius: 3px;
    background: var(--gx-ink);
    transform: translateY(-50%);
    transition: left .25s ease, background .25s ease;
    z-index: 3;
}

.bx.is-reached .bx__flag { background: var(--gx-win); }

.bx__flag-cap {
    position: absolute;
    bottom: calc(100% + 7px);
    left: 50%;
    transform: translateX(-50%);
    padding: 3px 9px;
    border-radius: 7px;
    background: var(--gx-ink);
    color: #fff;
    font-size: 11px;
    font-weight: 800;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
    transition: background .25s ease;
}

.bx__flag-cap::after {
    content: '';
    position: absolute;
    top: 100%;
    left: 50%;
    margin-left: -4px;
    border: 4px solid transparent;
    border-top-color: var(--gx-ink);
    transition: border-top-color .25s ease;
}

.bx.is-reached .bx__flag-cap { background: var(--gx-win); }
.bx.is-reached .bx__flag-cap::after { border-top-color: var(--gx-win); }

/* у краёв шкалы подпись прижимается внутрь, иначе половина уезжает за границу */
.bx__flag.is-right .bx__flag-cap,
.bx__pin.is-right .bx__pin-cap {
    left: auto;
    right: 0;
    transform: none;
}

.bx__flag.is-left .bx__flag-cap,
.bx__pin.is-left .bx__pin-cap {
    left: 0;
    transform: none;
}

.bx__flag.is-right .bx__flag-cap::after { left: auto; right: 5px; margin-left: 0; }
.bx__flag.is-left .bx__flag-cap::after { left: 5px; margin-left: 0; }

/* ---------- метка краша ---------- */

.bx__pin {
    position: absolute;
    left: var(--crash, 0%);
    top: 50%;
    width: 3px;
    height: 26px;
    margin-left: -1.5px;
    border-radius: 3px;
    background: var(--gx-lose);
    transform: translateY(-50%) scaleY(0);
    opacity: 0;
    transition: opacity .2s ease, transform .3s var(--gx-ease);
    z-index: 4;
}

.bx.is-reached .bx__pin { background: var(--text-dim); }

.bx__pin.is-on {
    opacity: 1;
    transform: translateY(-50%) scaleY(1);
}

/* хлопок в точке краша */
.bx__pin.is-on::before {
    content: '';
    position: absolute;
    left: 50%;
    top: 50%;
    width: 40px;
    height: 40px;
    margin: -20px 0 0 -20px;
    border-radius: 50%;
    border: 3px solid currentColor;
    color: var(--gx-lose);
    animation: gxBurst .5s ease-out both;
}

.bx.is-reached .bx__pin.is-on::before { color: var(--text-dim); }

/* подпись краша идёт под шкалой, где стоят деления, поэтому у неё
   собственная подложка — иначе она сливается с ближайшим делением */
.bx__pin-cap {
    position: absolute;
    top: calc(100% + 6px);
    left: 50%;
    transform: translateX(-50%);
    padding: 2px 8px;
    border-radius: 7px;
    background: var(--surface);
    border: 1px solid var(--border);
    box-shadow: 0 3px 8px rgba(15, 40, 80, .08);
    font-size: 11px;
    font-weight: 800;
    color: var(--gx-lose);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.bx.is-reached .bx__pin-cap { color: var(--text-dim); }

/* ---------- деления и статус ---------- */

.bx__ticks {
    position: relative;
    height: 14px;
    margin-top: 38px;
}

.bx__ticks span {
    position: absolute;
    transform: translateX(-50%);
    font-size: 10.5px;
    font-weight: 700;
    color: var(--gx-muted);
    font-variant-numeric: tabular-nums;
}

.bx__ticks span:first-child { transform: none; }
.bx__ticks span:last-child { transform: translateX(-100%); }

.bx__status {
    margin: 14px 0 0;
    text-align: center;
    font-size: 12.5px;
    font-weight: 700;
    color: var(--gx-muted);
    min-height: 18px;
    transition: color .25s ease;
}

.bx.is-reached .bx__status { color: var(--gx-win); }
.bx.is-lost .bx__status { color: var(--gx-lose); }

/* ---------- плашка результата ---------- */

/* Плашка живёт в потоке под счётчиком: как оверлей она закрывала и число,
   и подпись флажка. Высота слота задана всегда, поэтому шкала не прыгает. */
.gx .bubbles-result {
    position: static;
    height: auto;
    min-height: 42px;
    margin-top: 12px;
    padding: 0;
    display: grid;
    place-items: center;
    pointer-events: none;
}

.gx .bubbles-result_wrap {
    position: relative;
    width: auto;
    height: auto;
    padding: 9px 22px;
    border-radius: 999px;
    font-size: 19px;
    font-weight: 800;
    backdrop-filter: blur(6px);
    box-shadow: 0 14px 34px rgba(11, 26, 51, .14);
    opacity: 0;
    transform: scale(.8);
}

.gx .bubbles-result_wrap:after { content: none; }

/* плашка вставляется в DOM сразу с классом .show, поэтому здесь нужна
   анимация, а не transition — переходить не от чего */
.gx .bubbles-result_wrap.show { animation: gxBadgePop .52s var(--gx-ease) both; }

@keyframes gxBadgePop {
    0% { opacity: 0; transform: scale(.55) translateY(10px); }
    55% { opacity: 1; transform: scale(1.12) translateY(0); }
    100% { opacity: 1; transform: scale(1); }
}

@keyframes gxBurst {
    0% { transform: scale(.35); opacity: .6; }
    100% { transform: scale(2.2); opacity: 0; }
}

.gx .bubbles-result_wrap p {
    margin: 0;
    font-size: inherit;
    font-weight: inherit;
    color: inherit;
}

.gx .bubbles-result_wrap.win {
    background: rgba(233, 251, 244, .94);
    border: 1px solid var(--ok-border);
    color: var(--gx-win);
}

.gx .bubbles-result_wrap.lost {
    background: rgba(254, 240, 242, .94);
    border: 1px solid var(--danger-border);
    color: var(--gx-lose);
}

/* =====================================================================
   АДАПТИВ
   ===================================================================== */

@media (max-width: 560px) {
    .gx-head { gap: 10px; }

    .gx-id__icon {
        width: 40px;
        height: 40px;
        border-radius: 13px;
    }

    .gx-id__txt h1 { font-size: 18px; }

    .gx-panel,
    .gx-stage { padding: 14px; }

    .gx-payout__val { font-size: 20px; }

    .gx-action { min-height: 52px; }

    .gx-tile__val { font-size: 15px; }
}

@media (max-width: 380px) {
    .gx-chip { min-width: 44px; font-size: 11.5px; }
    .gx .dice-choices { gap: 8px; }
    .gx-choice__top { font-size: 14px; }
}

/* =====================================================================
   ИТОГ РАУНДА: РАСКРЫТИЕ МИН И ОКНО ПОВЕРХ ПОЛЯ

   Когда раунд закончился — забрал игрок выигрыш или подорвался — доска
   перестаёт быть тайной и досказывает расклад:

     .revealed-bomb  мина, на которую наступили — красная, трясётся;
     .revealed-mine  остальные мины — те же цвета, но приглушённо: это
                     справка «куда ты не наступил», а не событие;
     .is-dimmed      нетронутые безопасные клетки — гаснут, чтобы мины
                     читались с первого взгляда.

   Три состояния, а не одно на все мины: иначе клетка, которая взорвала
   раунд, теряется среди десятка одинаковых.
   ===================================================================== */

.gx .cell.revealed-mine {
    background: linear-gradient(160deg, var(--danger-soft) 0%, var(--danger-soft) 100%);
    border-color: var(--danger-border);
    box-shadow: 0 2px 0 var(--gx-edge-lose), inset 0 1px 0 var(--gx-gloss);
    cursor: default;
    /* Задержку проставляет minesRevealMines() — мины проявляются волной,
       а не все разом. */
    animation: gxRevealSoft .4s var(--gx-ease) both;
    animation-delay: var(--gx-delay, 0s);
}

.gx .cell.revealed-mine::before { display: none; }

.gx .cell.revealed-mine::after {
    content: '💣';
    font-size: clamp(17px, 4.2cqw, 26px);
    line-height: 1;
    opacity: .6;
    filter: grayscale(.3);
}

.gx .cell.is-dimmed {
    opacity: .4;
    filter: saturate(.45);
    cursor: default;
    transition: opacity .35s ease, filter .35s ease;
}

.gx .cell.is-dimmed:hover {
    transform: none;
    box-shadow: 0 2px 0 var(--gx-edge), inset 0 1px 0 var(--gx-gloss);
    border-color: var(--border);
}

@keyframes gxRevealSoft {
    0%   { transform: scale(.82); opacity: 0; }
    70%  { transform: scale(1.04); opacity: 1; }
    100% { transform: scale(1); opacity: 1; }
}

/* ------------------------------------------------------------ ОКНО ИТОГА */

.gx-board-holder {
    position: relative;
    width: 100%;
    max-width: 460px;
    display: flex;
    justify-content: center;
}

.gx-result {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    z-index: 4;
    /* Клики проходят мимо подложки к доске — ловит их только карточка */
    pointer-events: none;

    /* Светлой карточке нужна подложка: на белой доске она иначе сливается
       с фоном. Тёмная выделялась сама, за счёт контраста, но выпадала из
       светлой темы сайта — вместо неё гасим и размываем доску. */
    border-radius: 22px;
    background: rgba(240, 246, 255, .72);
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
    animation: gxScrimIn .3s ease both;
}

/* display выше перебил бы UA-правило hidden, поэтому явно */
.gx-result[hidden] { display: none; }

.gx-result__card {
    /* Акцент задаётся одной парой переменных, поэтому вариантов всего два
       и они не расходятся между цифрой, суммой и обводкой. Оттенки взяты
       из --gx-win / --gx-lose: неон вроде #24d17e на белом не читается. */
    --gx-res-accent: var(--gx-win);
    --gx-res-halo: rgba(14, 164, 114, .18);

    pointer-events: auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    min-width: min(76%, 268px);
    padding: 22px 32px 24px;
    border: 0;
    border-radius: 24px;
    cursor: pointer;
    text-align: center;
    background: linear-gradient(168deg, var(--surface) 0%, var(--surface-2) 100%);
    box-shadow:
        0 0 0 3px var(--gx-res-accent),
        0 0 0 9px var(--gx-res-halo),
        0 22px 44px rgba(11, 26, 51, .16);
    animation: gxResultIn .45s var(--gx-ease) both;
    transition: transform .2s var(--gx-ease);
}

.gx-result__card:hover  { transform: translateY(-2px); }
.gx-result__card:active { transform: translateY(0) scale(.98); }

.gx-result__card:focus-visible {
    outline: 3px solid #fff;
    outline-offset: -3px;
}

.gx-result__mult {
    font-size: clamp(34px, 9cqw, 54px);
    font-weight: 900;
    line-height: 1;
    letter-spacing: -1.5px;
    color: var(--gx-res-accent);
    font-variant-numeric: tabular-nums;
}

.gx-result__rule {
    width: 74px;
    height: 3px;
    border-radius: 999px;
    background: var(--gx-line);
}

.gx-result__sum {
    font-size: clamp(19px, 4.4cqw, 27px);
    font-weight: 800;
    line-height: 1;
    letter-spacing: -.5px;
    color: var(--gx-res-accent);
    font-variant-numeric: tabular-nums;
}

/* Подпись под суммой — «Забрано», «Ставка сгорела», «Поле пройдено» */
.gx-result__note {
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .3px;
    color: var(--gx-muted);
}

.gx-result--lose .gx-result__card {
    --gx-res-accent: var(--gx-lose);
    --gx-res-halo: rgba(226, 59, 82, .16);
}

/* ==========================================================================
   ИТОГ РАУНДА В МИНАХ: РАСКЛАД ОБЯЗАН ОСТАВАТЬСЯ ВИДЕН

   Раунд заканчивается раскрытым полем: minesRevealMines() красит все мины
   красным с 💣, а нетронутые безопасные клетки гасит, чтобы расклад читался
   сразу. Взорвавшись, игрок смотрит именно на него — по нему видно,
   насколько близко было к выигрышу. Забрав выигрыш, тоже: там интересно,
   на какой клетке остановился и что ждало дальше.

   Но поверх всего этого ложился итог — подложка белая на три четверти с
   размытием в 3 пикселя, а на ней карточка шириной в три четверти доски.
   От расклада оставались цветные пятна: работа делалась и тут же пряталась.

   ЧИНИМ ДВУМЯ ПРАВКАМИ, И ОБЕ НУЖНЫ:

     1. Подложка почти прозрачная и без размытия. Затемнение оставляем
        символическим — оно отделяет законченный раунд от нового, но
        читать сквозь него не мешает.

     2. Плашка компактная: множитель и сумма в одну строку вместо трёх,
        поля вчетверо меньше. Стоит она по центру поля, как и раньше, но
        накрывает пару клеток вместо девяти из двадцати пяти.

   ОДИНАКОВО ДЛЯ ВЫИГРЫША И ПРОИГРЫША. Раскрытый расклад показывается в
   обоих случаях, значит и прятать его нельзя в обоих; а два разных итога
   в одной игре читались бы как два разных механизма. Цвет обводки при
   этом по-прежнему свой у каждого исхода — его задаёт .gx-result--lose.

   ТОЛЬКО ДЛЯ МИН, через #minesResult: .gx-result общий с кено и рыбалкой,
   а там за подложкой нет никакого расклада — прятать ей нечего, и трогать
   их незачем.

   Плашку можно убрать нажатием (minesHideResult), и подпись об этом
   добавлена ниже: без неё возможность знали бы только те, кто ткнул
   наугад.
   ========================================================================== */

#minesResult {
    background: rgba(240, 246, 255, .14);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

#minesResult .gx-result__card {
    min-width: 0;
    padding: 10px 18px 11px;

    /* СЕТКА, А НЕ FLEX-ПЕРЕНОС — иначе плашка растягивается во всю доску.
     *
     * Раскладка нужна такая: множитель и сумма рядом, подписи под ними во
     * всю ширину. На flex это делается через flex-basis: 100% у подписей,
     * и на первый взгляд работает — но ширина у самой плашки не задана, она
     * считается по содержимому. Получается замкнутый расчёт: подписи просят
     * сто процентов от ширины, которой ещё нет, и браузер разрешает его в
     * пользу максимума. Плашка раздувалась почти на всю доску, хотя текста
     * в ней на треть строки.
     *
     * У grid этой петли нет: колонки auto меряются по содержимому, а
     * подписи растягиваются на «сколько получилось» (1 / -1), а не требуют
     * ширину вперёд. Плашка выходит ровно по самой длинной своей строке. */
    display: grid;
    grid-template-columns: auto auto;
    justify-content: center;

    /* По базовой линии, а не по центру: числа стоят рядом в одной строке,
       и ровняться им положено по низу цифр, как обычному тексту. При почти
       равных кеглях (21 и 19) разница с центрированием в полпикселя, зато
       «×» и «₽» гарантированно на одном уровне. */
    align-items: baseline;

    /* Зазор меньше прежнего: у равных по весу чисел широкий просвет
       разбивал пару на два отдельных числа. */
    column-gap: 8px;
    row-gap: 3px;

    /* Ореол вдвое уже прежнего: у крупной карточки он был рамкой в девять
       точек вокруг четверти доски, у компактной такой же перевесил бы саму
       плашку. */
    box-shadow:
        0 0 0 2px var(--gx-res-accent),
        0 0 0 6px var(--gx-res-halo),
        0 10px 26px rgba(11, 26, 51, .28);
}

/* МНОЖИТЕЛЬ И СУММА — ОДНА ПАРА, А НЕ ДВА РАЗНЫХ ЧИСЛА.
 *
 * На большой карточке они стояли друг под другом, и разнобой был уместен:
 * множитель — заголовок (54px, вес 900, трекинг −1.5), сумма под ним
 * подписью (27px, вес 800, трекинг −0.5). Втянув их в одну строку, этот
 * же разнобой стал читаться как сбой вёрстки: рядом оказались числа
 * разной величины, плотности и жирности, да ещё и по общей базовой линии —
 * мелкое выглядело приваленным к крупному.
 *
 * Здесь они равноправны: коэффициент и деньги — две половины одного
 * ответа «чем кончился раунд». Поэтому вес и трекинг общие, а размеры
 * почти равны: 21 и 19 вместо 21 и 15. Небольшой перевес у множителя
 * оставлен намеренно — он всё-таки первый по смыслу, — но это оттенок
 * иерархии, а не разные роли.
 *
 * Цвет у обоих и так один (--gx-res-accent), менять нечего. */
#minesResult .gx-result__mult,
#minesResult .gx-result__sum {
    grid-row: 1;
    font-weight: 900;
    letter-spacing: -.5px;
}

#minesResult .gx-result__mult { grid-column: 1; font-size: 21px; }
#minesResult .gx-result__sum  { grid-column: 2; font-size: 19px; }

/* Разделительная черта между множителем и суммой в строке не нужна. */
#minesResult .gx-result__rule { display: none; }

/* Подписи — под числами, во всю ширину сетки, какой бы она ни вышла. */
#minesResult .gx-result__note {
    grid-column: 1 / -1;
    font-size: 10.5px;
}

/* Подсказка про нажатие — здесь, а не в тексте итога: note приходит из
   mines.js вместе с суммой и описывает исход раунда, а это подпись к
   элементу управления. */
#minesResult .gx-result__card::after {
    content: 'нажмите, чтобы убрать';
    grid-column: 1 / -1;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: .2px;
    color: var(--gx-muted);
    opacity: .75;
}

@keyframes gxScrimIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@keyframes gxResultIn {
    0%   { transform: scale(.72) translateY(14px); opacity: 0; }
    62%  { transform: scale(1.05) translateY(0);   opacity: 1; }
    100% { transform: scale(1) translateY(0);      opacity: 1; }
}

@media (max-width: 480px) {
    .gx-result__card {
        min-width: min(82%, 220px);
        padding: 18px 24px 20px;
        border-radius: 20px;
    }

    .gx-result__rule { width: 58px; }
}

@media (prefers-reduced-motion: reduce) {
    .gx *,
    .gx *::before,
    .gx *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
    }
}

/* =====================================================================
   ТУМБЛЕР В БОКОВОЙ КОЛОНКЕ

   Настоящий <input type="checkbox"> внутри <label>: он и с клавиатуры
   переключается, и озвучивается скринридером, и по подписи кликается —
   всё это пришлось бы писать руками, будь на его месте <div>. Сам
   квадратик спрятан, вместо него рисуются дорожка и бегунок.
   ===================================================================== */

.gx-switch {
    /* Обязателен: скрытое поле внутри абсолютное, и без этого оно
       отсчитывалось бы от постороннего предка выше по дереву. */
    position: relative;
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 12px;
    padding: 12px 14px;
    border: 1px solid var(--gx-line);
    border-radius: var(--gx-r);
    background: var(--gx-bg-2);
    cursor: pointer;
    user-select: none;
    transition: border-color .15s ease, background .15s ease;
}

.gx-switch:hover { border-color: var(--gx-line-2); }

/* Не display: none — так элемент выпадает из обхода по Tab и перестаёт
   принимать фокус. Прячем визуально, оставляя доступным. */
.gx-switch input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.gx-switch__track {
    position: relative;
    flex: none;
    width: 42px;
    height: 24px;
    border-radius: 999px;
    background: var(--border-strong);
    transition: background .2s ease;
}

.gx-switch__knob {
    position: absolute;
    top: 3px;
    left: 3px;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: var(--surface);
    box-shadow: 0 1px 3px rgba(15, 23, 42, .28);
    transition: transform .2s cubic-bezier(.4, 0, .2, 1);
}

.gx-switch input:checked + .gx-switch__track {
    background: var(--gx-blue);
}

.gx-switch input:checked + .gx-switch__track .gx-switch__knob {
    transform: translateX(18px);
}

/* Обводка по фокусу с клавиатуры — на дорожке, а не на скрытом поле:
   :focus-visible не срабатывает по клику мышью, поэтому лишнего кольца
   при обычном переключении не будет. */
.gx-switch input:focus-visible + .gx-switch__track {
    box-shadow: 0 0 0 3px rgba(37, 99, 235, .28);
}

.gx-switch__text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.gx-switch__title {
    font-size: 13.5px;
    font-weight: 700;
    color: var(--gx-ink);
    line-height: 1.2;
}

.gx-switch__sub {
    font-size: 11.5px;
    font-weight: 600;
    color: var(--gx-muted);
    line-height: 1.2;
}


/* ==========================================================================
   ICE FISHING — ОБЩИЙ ОНЛАЙН-РАУНД

   Раунд один на всех, поэтому экран устроен иначе, чем у остальных режимов:
   фаза и таймер всегда на виду, ставок за раунд несколько, поля показывают
   чужие ставки.

   ЦВЕТ ПОЛЯ ЗАДАЁТСЯ ОДНОЙ ПЕРЕМЕННОЙ --ice-tint по data-kind. Иконка,
   рамка, подложка и кружок в истории берут её. Красить элементы по
   отдельности нельзя — разъедутся при первой правке.

   Все цвета через токены: режим обязан работать в обеих темах с рождения.
   ========================================================================== */

/* ЦВЕТ ПОЛЯ — ОДНОЙ ПЕРЕМЕННОЙ. Всё остальное (полоска, значок, рамка,
   плашка своей ставки, кружок в истории) берёт её отсюда. Красить элементы
   по отдельности нельзя: разъедутся при первой правке.

   Белое и серое поля отличаются ТОЛЬКО цветом: выплата у обоих ×1, просто
   на колесе дольки чередуются, и ставка на них раздельная. */
.ice [data-kind="white"]   { --ice-tint: #7f9dbe; --ice-bg: #ffffff; --ice-fg: #12304e; --ice-line: #c3d5e6; }
.ice [data-kind="grey"]    { --ice-tint: #4d6b8c; --ice-bg: #9fb6cc; --ice-fg: #10283f; --ice-line: #8199b1; }
.ice [data-kind="blues"]   { --ice-tint: #2b7fd4; --ice-bg: #2b7fd4; --ice-fg: #ffffff; --ice-line: #1c62aa; }
.ice [data-kind="oranges"] { --ice-tint: #e08a1e; --ice-bg: #e2870f; --ice-fg: #ffffff; --ice-line: #b96c07; }
.ice [data-kind="reds"]    { --ice-tint: #d4413c; --ice-bg: #d83b34; --ice-fg: #ffffff; --ice-line: #ad2a24; }

/* --------------------------------------------------------------- ФАЗА */

.ice-phase {
    display: flex;
    flex-direction: column;
    gap: 7px;
    padding: 11px 14px;
    border-radius: var(--gx-r-sm);
    background: var(--gx-bg-2);
    border: 1px solid var(--gx-line);
}

.ice-phase__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
}

.ice-phase__label {
    font-size: 12px;
    font-weight: 800;
    letter-spacing: .5px;
    text-transform: uppercase;
    color: var(--gx-muted);
}

/* Цифра моноширинная: секунды тикают, и без табличных знаков строка
   дёргалась бы по ширине на каждой смене разряда. */
.ice-phase__time {
    font-size: 19px;
    font-weight: 900;
    color: var(--gx-ink);
    font-variant-numeric: tabular-nums;
}

.ice-phase__track {
    height: 6px;
    border-radius: 999px;
    background: var(--gx-line);
    overflow: hidden;
}

.ice-phase__track i {
    display: block;
    height: 100%;
    border-radius: inherit;
    background: linear-gradient(90deg, var(--gx-blue) 0%, var(--gx-sky) 100%);
    transition: width .95s linear;
}

/* ЦВЕТ — ПО ФАЗЕ, И НЕ ТОЛЬКО У ПОЛОСЫ.
 *
 * Раньше менялась одна полоска, а подпись и цифра оставались серыми в любой
 * фазе. Полоска в шесть пикселей — это слишком тихо для события «ставки
 * закрылись»: игрок смотрит на колесо, а не на неё.
 *
 * Теперь фаза красит всю плашку: рамку, фон, подпись и секунды. Приём
 * ставок синий, вращение янтарное, результат зелёный. Читать при этом
 * ничего не нужно — цвет виден боковым зрением. */
.ice-phase.is-betting {
    --ice-ph: var(--gx-blue);
    --ice-ph-soft: rgba(43, 127, 212, .10);
}

.ice-phase.is-spinning {
    --ice-ph: var(--warn);
    --ice-ph-soft: rgba(226, 135, 15, .12);
}

.ice-phase.is-settled {
    --ice-ph: var(--gx-win);
    --ice-ph-soft: rgba(14, 164, 114, .12);
}

.ice-phase {
    border-color: color-mix(in srgb, var(--ice-ph, var(--gx-line)) 38%, transparent);
    background: var(--ice-ph-soft, var(--gx-bg-2));
    transition: background .3s ease, border-color .3s ease;
}

.ice-phase.is-betting .ice-phase__label,
.ice-phase.is-spinning .ice-phase__label,
.ice-phase.is-settled .ice-phase__label { color: var(--ice-ph); }

.ice-phase.is-betting .ice-phase__time,
.ice-phase.is-spinning .ice-phase__time,
.ice-phase.is-settled .ice-phase__time { color: var(--ice-ph); }

.ice-phase.is-spinning .ice-phase__track i {
    background: linear-gradient(90deg, var(--warn) 0%, var(--danger) 100%);
}

.ice-phase.is-settled .ice-phase__track i {
    background: linear-gradient(90deg, var(--gx-win) 0%, var(--gx-sky) 100%);
}

/* Последние секунды приёма ставок — плашка дышит. Это единственный момент
   раунда, где промедление стоит игроку раунда: дальше ставку уже не
   принять. Пульс включает JS, добавляя is-hurry. */
.ice-phase.is-hurry {
    animation: icePhasePulse 1s ease-in-out infinite;
}

@keyframes icePhasePulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(226, 59, 82, 0); }
    50%      { box-shadow: 0 0 0 4px rgba(226, 59, 82, .16); }
}

/* -------------------------------------------------------------- КОЛЕСО */

/* Видна только ВЕРХНЯЯ ДУГА: диск шире стола, контейнер обрезает всё ниже
   своей высоты. Все 56 долек целиком читаться и не должны — на любом экране
   они превратились бы в ниточки.

   overflow: hidden здесь несущий, а не косметический — уберёте, и диск
   вылезет на пол-страницы.

   Фон тёмный и ОДИНАКОВЫЙ в обеих темах: это не подложка интерфейса, а
   пространство, в котором лежит предмет. На светлой теме он нужен даже
   больше — иначе светлый обод колеса сливается с карточкой стола. */
/* ОБЁРТКА-КОНТЕЙНЕР. Все размеры колеса считаются от ЕЁ ширины, а не от
   ширины всей игры: между ними разница в панель ставок и отступы стола, на
   средних экранах больше трёхсот пикселей. Без этого колесо на ноутбуке
   оказывалось мельче, чем на телефоне. */
.ice-wheel-box {
    container-type: inline-size;
    container-name: icew;
    margin-top: 12px;
}

.ice-wheel {
    position: relative;
    height: clamp(150px, 62.4cqw, 300px);
    overflow: hidden;
    border-radius: var(--gx-r-sm);
    background: radial-gradient(80% 120% at 50% 112%, #1b3a5e 0%, #0c1b2c 100%);
    border: 1px solid rgba(148, 180, 220, .1);
}

/* Неподвижный свет поверх вращающегося диска.
   В самой картинке колеса свет только радиально-симметричный — иначе блик
   поехал бы по кругу вместе с дольками. Направленный живёт здесь. */
.ice-wheel::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 2;
    pointer-events: none;
    background:
        radial-gradient(48% 62% at 50% -12%, rgba(190, 224, 255, .14) 0%, rgba(190, 224, 255, 0) 70%),
        linear-gradient(180deg, rgba(6, 16, 28, 0) 68%, rgba(6, 16, 28, .38) 100%);
}

/* ШИРИНА ДИСКА — ГЛАВНАЯ НАСТРОЙКА ЭТОГО ЭКРАНА, и она не про «побольше».
   Чем шире диск, тем ПЛОЩЕ дуга и тем МЕНЬШЕ долек влезает в кадр:

       200%  —  9 долек из 56
       145%  — 14 долек, дуга упирается в боковые стены стола
       132%  — 15 долек, купол крупный и целиком в кадре
       118%  — 18 долек, но колесо мелкое

   Проценты, а не пиксели: на телефоне колесо ужимается вместе со столом,
   иначе видимая дуга становится почти прямой линией. */
.ice-wheel__disc {
    position: absolute;
    left: 50%;
    top: 0;
    width: 240%;
    aspect-ratio: 1 / 1;
    transform: translateX(-50%);
    transform-origin: 50% 50%;
    will-change: transform;
}

/* Поворот СКЛАДЫВАЕТСЯ с центровкой: translateX обязан остаться, иначе
   колесо при вращении уедет вбок. Порядок множителей менять нельзя. */
.ice-wheel.is-spinning .ice-wheel__disc {
    transition: transform 6s cubic-bezier(.12, .72, .06, 1);
}

/* Картинка колеса и слой фишек лежат друг на друге в одних координатах и
   вращаются вместе: крутится их общий родитель. */
.ice-w__img,
.ice-w__over {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
}

.ice-w__img {
    user-select: none;
    -webkit-user-drag: none;
}

.ice-w__over { pointer-events: none; }

/* ДОП-МНОЖИТЕЛЬ. Выпадает случайно каждый раунд, поэтому в картинку его не
   запечь. На колесе от него только МЕТКА — полоса у кромки дольки; само
   число живёт в полосе «Множители раунда» под колесом. Почему так, а не
   надписью на дольке, разобрано в multsMarkup() в js.

   МАЛИНОВЫЙ — единственный цвет, которого на колесе нет нигде: дольки
   белые, серые, синие, оранжевые и красные, обод ледяной. Любой другой
   оттенок спорил бы с каким-нибудь сектором. Тот же цвет несут числа в
   полосе — по нему глаз и связывает одно с другим. */
.ice-w__mx-mark {
    fill: #f5197f;
    stroke: #ffffff;
    stroke-width: 2;
}

/* Ореол под меткой: полупрозрачная подложка чуть шире. Даёт метке
   свечение, не пряча дольку под ней. */
.ice-w__mx-glow {
    fill: #f5197f;
    opacity: .22;
}

/* Число прямо на метке. Белое: малиновый достаточно тёмный, обводки под
   буквы не нужно — а контур вокруг текста и есть тот приём, от которого
   всё это оформление уходило.

   Кегль приходит из js атрибутом font-size, потому что зависит от числа
   знаков: «×3» и «×100» одним кеглем либо болтаются, либо не влезают. */
.ice-w__mx-txt {
    fill: #ffffff;
    font-family: 'Inter', sans-serif;
    font-weight: 800;
    letter-spacing: -.02em;
    text-anchor: middle;
    dominant-baseline: central;
}

/* «×» мельче и приглушён — значение несёт число, знак только поясняет. */
.ice-w__mx-sign {
    font-size: .68em;
    fill-opacity: .72;
}

/* --------------------------------------------------- ПОЛОСА МНОЖИТЕЛЕЙ */

/* Место числа. На колесе оно всегда наклонено и чаще всего за кадром, а
   здесь стоит ровно, набрано шрифтом сайта и видно всегда. */
.ice-mults {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 12px;
    padding: 9px 12px;
    border-radius: 12px;
    background: var(--gx-bg-2);
    border: 1px solid var(--gx-line);
}

.ice-mults[hidden] { display: none; }

.ice-mults__label {
    font-size: 10.5px;
    font-weight: 800;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--gx-muted);
}

.ice-mults__list {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.ice-mx {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 4px 10px 4px 8px;
    border-radius: 999px;
    background: var(--gx-bg);
    border: 1px solid var(--gx-line);
    font-size: 12.5px;
    font-weight: 700;
    color: var(--gx-ink);
}

/* Кружок цветом дольки — та же связь «поле = сектор = цвет», что на
   плитках ставок и в истории. */
.ice-mx__dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--ice-bg);
    border: 1px solid var(--ice-line);
}

/* Название дольки — второстепенное: главное здесь число, а название только
   отвечает на вопрос «где». Поэтому вес меньше и тон приглушён. */
.ice-mx__name {
    font-weight: 600;
    color: var(--gx-muted);
}

.ice-mx__val {
    font-size: 14px;
    font-weight: 900;
    color: #f5197f;
    font-variant-numeric: tabular-nums;
}

/* УКАЗАТЕЛЬ. Неподвижен, крутится колесо под ним. Остриё картинки внизу по
   центру — оно и должно смотреть на дольку. */
/* Указатель ЗАХОДИТ НА КОЛЕСО, а не висит над ним. Обод при ширине диска
   132% занимает около 57 px по вертикали; чтобы остриё дотянулось до
   долек, картинке нужно быть выше этого. При соотношении файла 108×149
   ширина 52 px даёт высоту 72 px — остриё оказывается на 15 px внутри
   поля долек.

   Меняете ширину диска — пересчитайте: обод это ровно 0.10 радиуса. */
.ice-wheel__pointer {
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    z-index: 4;
    width: clamp(30px, 11.8cqw, 56px);
    filter: drop-shadow(0 5px 10px rgba(0, 0, 0, .5));
}

.ice-wheel__pointer img {
    display: block;
    width: 100%;
    height: auto;
}

/* ИТОГ РАУНДА ПОВЕРХ КОЛЕСА.
 *
 * Компонент целиком минный (.gx-result выше по файлу), здесь только то, что
 * обязано отличаться из-за подложки.
 *
 * ПЕЛЕНА ТЁМНАЯ, А НЕ СВЕТЛАЯ. В минах под ней белая доска, и светлая
 * пелена гасит её как надо. Здесь под ней колесо на почти чёрном фоне:
 * светлая пелена на нём выглядела бы туманом поверх ночи — светлее фона, но
 * темнее самих долек, то есть контраст не гасила бы, а путала. Тёмная
 * уводит колесо назад одним движением.
 *
 * Z-INDEX 7, ВЫШЕ БОНУСНОЙ СЦЕНЫ (6). Одновременно они не показываются —
 * на рыбьей дольке карточка ждёт конца сцены, — но порядок задан явно:
 * порядок в разметке тут не спасёт, у сцены свой z-index. */
.ice-result {
    z-index: 7;
    border-radius: inherit;
    background: rgba(7, 18, 32, .66);
}

/* Название выпавшего поля. Цвет берётся из --ice-tint, которую даёт
   атрибут data-kind — та же переменная, что красит плитку ставки и кружок
   в истории. JS ставит только сам атрибут, цвет он не знает и знать не
   должен: иначе оттенков стало бы два и они разошлись бы. */
.ice-result__name {
    font-size: clamp(12px, 3cqw, 15px);
    font-weight: 800;
    letter-spacing: .6px;
    text-transform: uppercase;
    color: var(--ice-tint, var(--gx-muted));
}

/* Зритель без ставок. Ни выигрыша, ни проигрыша — значит и акцента быть не
   должно: зелёная обводка на пустой сумме читается как «вы выиграли». */
.ice-result--idle .gx-result__card {
    --gx-res-accent: var(--gx-muted);
    --gx-res-halo: rgba(126, 148, 179, .16);
}

.ice-result--idle .gx-result__sum { display: none; }

/* КАРТОЧКА ПОДЖАТА ПОД ОКНО КОЛЕСА.
 *
 * В минах она стоит на квадратной доске и места там вдоволь. Здесь окно
 * колеса высотой 62.4cqw — на телефоне это около 225 px, и минная карточка
 * в свои ~180 px упиралась бы в края.
 *
 * Все размеры в cqw, от ширины .ice-wheel-box: карточка обязана ужиматься
 * ровно вместе с окном, в котором лежит, иначе на узких экранах она из него
 * вылезет, а на широких потеряется. */
.ice-result .gx-result__card {
    gap: clamp(4px, 1.2cqw, 10px);
    padding: clamp(12px, 3cqw, 22px) clamp(18px, 5cqw, 32px);
    border-radius: 20px;
}

.ice-result .gx-result__mult { font-size: clamp(28px, 7.4cqw, 46px); }
.ice-result .gx-result__sum  { font-size: clamp(16px, 3.6cqw, 24px); }
.ice-result .gx-result__rule { width: clamp(46px, 12cqw, 74px); height: 2px; }

/* --------------------------------------------------------------- ПОЛЯ */

/* ПОЛЯ СТАВОК — ЦВЕТНЫЕ БЛОКИ, а не карточки с полоской сверху.

   Раньше поле было белой карточкой с тонкой цветной чертой над ней, значком
   и тремя строчками мелкого текста. Выглядело это формой, а не столом, и
   связь с колесом держалась на трёхпиксельной полоске.

   Теперь плитка целиком того цвета, что и её долька на колесе. Игрок
   связывает поле с сектором мгновенно и без подписи — работает то же
   правило, по которому устроено само колесо.

   Значков внутри больше нет намеренно. Синяя рыбка на синей плитке
   невидима, а сажать её на белый кружок значит вернуть ту же пестроту,
   от которой уходим. Цвет плитки говорит всё, что говорил значок.

   Цвета ФИКСИРОВАННЫЕ в обеих темах — как у колеса. «Белое» поле обязано
   оставаться белым: это его имя, а не оттенок интерфейса. */
/* ПЯТЬ ПОЛЕЙ не делятся ни на 2, ни на 3 без остатка, поэтому сетка на
   шесть дорожек, а плитки занимают по нескольку. Так пустых клеток не
   остаётся ни при какой ширине — а пустая клетка в ряду ставок читается
   как «здесь что-то не загрузилось». */
.ice-board {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: 10px;
    margin-top: 12px;
}

/* Узко: по две в ряд, последняя во всю ширину. */
.ice-spot { grid-column: span 3; }
.ice-spot:nth-child(5) { grid-column: span 6; }

.ice-spot {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 12px 13px;
    border-radius: 14px;
    background: var(--ice-bg);
    color: var(--ice-fg);
    border: 1px solid var(--ice-line);
    text-align: left;
    cursor: pointer;
    font-family: inherit;
    box-shadow: 0 1px 0 rgba(9, 24, 44, .06);
    transition: transform .16s var(--gx-ease), box-shadow .16s ease, filter .16s ease;
}

.ice-spot:hover {
    transform: translateY(-2px);
    filter: brightness(1.05);
    box-shadow: 0 8px 18px rgba(9, 24, 44, .18);
}

.ice-spot:active { transform: translateY(0); }

.ice-spot__name {
    font-size: 13px;
    font-weight: 700;
    line-height: 1.2;
    opacity: .88;
}

/* Выплата — главное число на плитке, ради него сюда и смотрят. */
.ice-spot__mult {
    font-size: 21px;
    font-weight: 900;
    line-height: 1.15;
    letter-spacing: -.02em;
    font-variant-numeric: tabular-nums;
}

.ice-spot__head {
    display: flex;
    flex-direction: column;
    gap: 1px;
}

/* ЖИВОЙ СТОЛ — вторая строка плитки.
 *
 * Слева сколько игроков, справа сколько денег. Прижаты к разным краям
 * намеренно: это два независимых числа, и поставь их рядом, глаз читал бы
 * «12 игроков 4 300 ₽» как одну фразу и спотыкался.
 *
 * margin-top: auto прибивает строку к низу карточки. Без него плитки с
 * разной длиной названия («Белый» в одну строку, «Big Oranges» в две)
 * разъезжались бы по вертикали, и цифры в ряду стояли бы на разной высоте. */
.ice-spot__live {
    margin-top: auto;
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    font-size: 11px;
    font-weight: 700;
    line-height: 1.2;
}

.ice-spot__players { opacity: .58; }

.ice-spot__pot {
    opacity: .9;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Своя ставка — белая фишка в углу. Пустая места НЕ занимает, иначе поля
   без ставки были бы ниже остальных и сетка поехала бы. */
.ice-spot__mine:empty { display: none; }

/* СВОЯ СТАВКА — ПОЛОСОЙ ВНИЗУ, а не фишкой в углу.
 *
 * В углу помещалось одно число, и это была вся информация: «стоит 200 ₽».
 * Сколько с них вернётся, игрок считал сам — на белом ×1 ещё ладно, а на
 * красной рыбе с потолком ×10000 это уже не устный счёт.
 *
 * Полосе хватает места на «200 ₽ → 400 ₽», и фишка из угла никуда не
 * делась: она стоит в начале полосы тем же значком. Кружок
 * нейтрально-тёмный, а не цвета номинала — ставка может состоять из
 * нескольких фишек разного достоинства, и красить его в одно из них было бы
 * враньём. */
.ice-spot__mine {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px 4px 4px;
    border-radius: 999px;
    background: rgba(255, 255, 255, .96);
    color: #10283f;
    font-size: 12px;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    box-shadow: 0 1px 3px rgba(9, 24, 44, .25);
}

/* Стрелка и будущая выплата бледнее самой ставки: поставленное — факт,
   выплата — возможность, и весом они равны быть не должны. */
.ice-spot__mine i {
    font-style: normal;
    opacity: .38;
}

.ice-spot__mine span {
    opacity: .72;
    white-space: nowrap;
}

.ice-spot__mine b { font-weight: 800; }

/* Та же формула, что у большой фишки: узкие вставки 18° из 45° и лицо
   поверх. Формулы должны совпадать — иначе значок на поле и фишка в стойке
   выглядят разными предметами. */
.ice-spot__mine::before {
    content: '';
    flex: none;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background:
        radial-gradient(circle,
            #ffffff 0 54%,
            rgba(9, 24, 44, .24) 54% 60%,
            transparent 60%),
        repeating-conic-gradient(from -9deg,
            #ffffff 0 18deg,
            #2b4a6b 18deg 45deg);
    box-shadow:
        inset 0 0 0 1px rgba(9, 24, 44, .28),
        0 1px 2px rgba(9, 24, 44, .25);
}

/* Со ставкой — светлое кольцо по контуру. На цветной плитке рамка уже
   занята цветом, поэтому отметка идёт наружу, а не внутрь. */
.ice-spot.is-mine {
    box-shadow: 0 0 0 3px var(--gx-bg), 0 0 0 5px var(--ice-tint);
}

.ice-spot:disabled {
    opacity: .5;
    cursor: default;
    transform: none;
    filter: none;
}

/* ------------------------------------------------------------ ИСТОРИЯ */

.ice-history {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 12px;
    min-width: 0;
}

.ice-history__label {
    flex: none;
    font-size: 11px;
    font-weight: 800;
    letter-spacing: .4px;
    text-transform: uppercase;
    color: var(--gx-muted);
}

/* Прокрутка по горизонтали: история растёт, а переносить её на вторую
   строку нельзя — стол и так высокий. */
.ice-history__list {
    display: flex;
    gap: 6px;
    margin: 0;
    padding: 0;
    list-style: none;
    overflow-x: auto;
    scrollbar-width: none;
}
.ice-history__list::-webkit-scrollbar { display: none; }

/* Кружки истории залиты ТЕМ ЖЕ цветом, что плитка поля и долька колеса.
   Три места, один цвет — серия читается без подписей. */
.ice-history__list li {
    flex: none;
    min-width: 34px;
    padding: 5px 7px;
    border-radius: 999px;
    text-align: center;
    font-size: 11.5px;
    font-weight: 800;
    color: var(--ice-fg);
    background: var(--ice-bg);
    border: 1px solid var(--ice-line);
    font-variant-numeric: tabular-nums;
}

/* РЫБЬИ РАУНДЫ ВЫДЕЛЕНЫ. Обычных долек 49 из 56, поэтому лента почти всегда
   белая с серым, и в этом ряду взгляду не за что зацепиться. А следят
   именно за рыбой: когда она была, сколько принесла и давно ли.

   Отличие даёт кольцо и вес, а не другой цвет: цвет уже занят — он говорит,
   КАКАЯ это рыба. */
.ice-history__list li.is-fish {
    font-size: 12.5px;
    padding: 5px 9px;
    box-shadow: 0 0 0 2px var(--gx-bg), 0 0 0 3.5px var(--ice-tint);
}

/* Сегодняшний раунд крайний слева и чуть ярче: лента идёт от свежего к
   старому, и без этого непонятно, с какого конца читать. */
.ice-history__list li:first-child {
    outline: 2px solid var(--gx-ink);
    outline-offset: -2px;
}

/* --------------------------------------------------- СТОЛ ВО ВСЮ ШИРИНУ */

/* У Ice Fishing НЕТ боковой колонки. Она отнимала у стола треть ширины, а
   колесо к ширине чувствительнее всего: диск задан в процентах от стола, и
   каждый отданный панели пиксель уменьшал дольки.

   Правило перебивает две колонки из @container gx (min-width: 640px) не
   порядком, а весом: .ice .gx-layout это (0,2,0) против (0,1,0). Порядок
   тут не помог бы — контейнерный запрос идёт ниже по файлу. */
.ice .gx-layout {
    grid-template-columns: minmax(0, 1fr);
}

/* Полей шесть; пока стол был узким, они шли в три колонки в два ряда.
   Во всю ширину помещаются в один ряд, и стол читается одним взглядом. */
/* Средне: три в ряд, потом две по половине. */
@container gx (min-width: 560px) {
    .ice-spot { grid-column: span 2; }
    .ice-spot:nth-child(4),
    .ice-spot:nth-child(5) { grid-column: span 3; }
}

/* Широко: все пять в один ряд, стол читается одним взглядом. */
@container gx (min-width: 900px) {
    .ice-board { grid-template-columns: repeat(5, 1fr); }
    .ice-spot,
    .ice-spot:nth-child(4),
    .ice-spot:nth-child(5) { grid-column: auto; }
}

/* Временная кнопка показа сцены. Помечена явно: демонстрационные кнопки,
   которые выглядят как обычные, доживают до продакшена. */
.ice-demo {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    align-self: flex-start;
    margin-top: 10px;
    padding: 7px 12px;
    border-radius: 10px;
    background: var(--gx-bg-2);
    border: 1px dashed var(--warn);
    color: var(--gx-ink);
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 700;
    cursor: pointer;
}

.ice-demo span {
    padding: 1px 6px;
    border-radius: 999px;
    background: var(--warn-soft);
    color: var(--warn);
    font-size: 10px;
    font-weight: 800;
    letter-spacing: .08em;
    text-transform: uppercase;
}

/* СЦЕНА УЛОВА ПЕРЕЕХАЛА В public/css/ice-scene.css.

   Здесь было четыре с половиной сотни строк: слои растровой сцены, позы
   рыбака, брызги, полёт рыбы. Сцену переписали вектором с эскалацией техники
   по ступеням улова, и держать её посреди общего файла игр стало незачем —
   она касается одного режима и одного события.

   Разметку строит public/js/ice-scene.js. */

/* ------------------------------------------------------- ВСЕ БОНУСЫ */

/* ДЕЙСТВИЕ, А НЕ ПОЛЕ, и выглядеть обязано иначе. Плитки ставок — сплошные
   цветные блоки; эта кнопка светлая, с пунктирной рамкой и в одну строку.
   Сделай её шестой плиткой — игрок счёл бы «все бонусы» отдельным исходом
   и искал бы такую дольку на колесе. Её там нет. */
.ice-allbet {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    margin-top: 10px;
    padding: 11px 14px;
    border-radius: 14px;
    background: var(--gx-bg-2);
    border: 1px dashed var(--gx-line);
    color: var(--gx-ink);
    text-align: left;
    cursor: pointer;
    font-family: inherit;
    transition: border-color .16s ease, background .16s ease, transform .12s var(--gx-ease);
}

.ice-allbet:hover {
    background: var(--gx-bg-3);
    border-color: var(--gx-blue);
    transform: translateY(-1px);
}

.ice-allbet:active { transform: translateY(0); }

/* Три кружка цветами рыб показывают, куда ляжет фишка. Наложены внахлёст —
   так читается «одним движением», а не «три отдельные кнопки». */
.ice-allbet__dots {
    flex: none;
    display: flex;
}

.ice-allbet__dots i {
    width: 17px;
    height: 17px;
    border-radius: 50%;
    background: var(--ice-bg);
    box-shadow: 0 0 0 2px var(--gx-bg-2);
}

.ice-allbet__dots i + i { margin-left: -6px; }

.ice-allbet__txt {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    line-height: 1.25;
}

.ice-allbet__txt b { font-size: 13.5px; font-weight: 800; }
.ice-allbet__txt span { font-size: 11.5px; color: var(--gx-muted); }

.ice-allbet__sum {
    flex: none;
    padding: 4px 10px;
    border-radius: 999px;
    background: var(--gx-bg);
    border: 1px solid var(--gx-line);
    font-size: 12px;
    font-weight: 800;
    color: var(--gx-blue-d);
    font-variant-numeric: tabular-nums;
}

/* ----------------------------------------------- ПОЛОСА СТАВКИ ВНИЗУ */

.ice-bar {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 14px;
    margin-top: 12px;
    padding: 14px;
    border-radius: var(--gx-r-sm);
    background: var(--gx-bg-2);
    border: 1px solid var(--gx-line);
}

/* Стойка фишек занимает ровно своё, действия забирают остаток и прижимают
   содержимое вправо. Ячеек теперь две, а не три: список ставок убран —
   суммы стоят на самих плитках. */
@container gx (min-width: 760px) {
    .ice-bar {
        grid-template-columns: auto minmax(0, 1fr);
        gap: 22px;
        align-items: end;
    }
}

.ice-bar__cell {
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-width: 0;
}

.ice-rack__head {
    display: flex;
    align-items: baseline;
    gap: 10px;
    flex-wrap: wrap;
}

.ice-rack__hint {
    font-size: 11.5px;
    color: var(--gx-muted);
}

/* РЕЙКА ПОД ФИШКАМИ.
 *
 * Шесть кружков просто в ряд на общем фоне читались незаконченной заготовкой:
 * у фишек есть объём и тень, а лежали они в пустоте, и было неясно, где
 * кончается «стойка номиналов» и начинается панель. Углубление отвечает на это
 * одной фигурой — как в настоящей рейке за бортом стола.
 *
 * ВЕРХНЕЕ ПОЛЕ БОЛЬШЕ НИЖНЕГО, И ЭТО НЕ ОПЕЧАТКА. Выбранная фишка приподнята
 * на 6px и окружена кольцом ещё в 5px, а overflow-x: auto ниже делает
 * контейнер прокручиваемым ПО ОБЕИМ осям — это правило CSS, а не причуда
 * браузера: стоит одной оси перестать быть visible, вторая тоже становится
 * auto. С равными полями подъём и кольцо срезало бы верхним краем, и выбранная
 * фишка отличалась бы от прочих на пару пикселей вместо целого движения. */
.ice-rack {
    display: flex;
    align-items: center;
    gap: 9px;

    /* В строку и с прокруткой, а не в перенос: шесть номиналов на второй ряд
       переносить некуда — панель и так последняя перед нижним краем. */
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;

    padding: 13px 14px 11px;
    border-radius: 999px;
    background: linear-gradient(180deg, var(--gx-bg-3) 0%, var(--gx-bg-2) 100%);
    box-shadow:
        inset 0 0 0 1px var(--gx-line),
        inset 0 3px 6px rgba(9, 24, 44, .10);
}

.ice-rack::-webkit-scrollbar { display: none; }

/* ==========================================================================
   ДЕЙСТВИЯ НАД СТАВКОЙ — «Повторить», «Удвоить», «Очистить»

   Раньше это были общие .gx-chip: контур цвета --gx-line на светлом фоне
   панели и подпись цветом --gx-ink-2. Рядом с фишками, у которых объём,
   блики и тени, они читались не как кнопки, а как выключенные подписи —
   особенно «Очистить», у которой и текст, и рамка бледно-розовые.

   Правило простое и оно же объясняет разницу между тремя: КНОПКА ДОЛЖНА
   ИМЕТЬ ПОВЕРХНОСТЬ. Две обычные её получают, опасная остаётся контурной —
   но контур у неё в полный цвет, а не в треть.

   Правило живёт здесь, а не в общем .gx-chip, чтобы не перекрасить те же
   кнопки в минах, дайсе и кено: там их никто не просил трогать. */

.ice-acts .gx-chip {
    height: 38px;
    padding: 0 14px;
    border-radius: 11px;
    font-size: 12.5px;
    font-weight: 800;
    letter-spacing: .2px;

    color: var(--gx-ink);
    background: var(--gx-bg-3);
    border: 1px solid var(--gx-line);
    /* Светлая грань сверху и тень снизу: тот же приём, которым набран объём у
       фишек. Без него кнопка остаётся плоской наклейкой рядом с ними. */
    box-shadow:
        inset 0 1px 0 var(--gx-gloss),
        0 1px 2px rgba(9, 24, 44, .07);
}

.ice-acts .gx-chip:hover:not(:disabled) {
    background: var(--gx-bg-2);
    border-color: var(--gx-edge);
    transform: translateY(-1px);
    box-shadow:
        inset 0 1px 0 var(--gx-gloss),
        0 4px 10px rgba(9, 24, 44, .12);
}

.ice-acts .gx-chip:active:not(:disabled) {
    transform: translateY(0);
    box-shadow: inset 0 2px 4px rgba(9, 24, 44, .12);
}

.ice-acts .gx-chip:disabled {
    opacity: .45;
    cursor: default;
}

/* «Очистить» снимает все ставки раунда разом, поэтому она единственная без
   поверхности: контурная кнопка не приглашает нажать. Но контур и подпись —
   в полный цвет опасности, а не в бледную его треть, иначе она читается не
   как «осторожно», а как «недоступно». */
.ice-acts .gx-chip--danger {
    color: var(--gx-lose);
    background: transparent;
    border-color: color-mix(in srgb, var(--gx-lose) 55%, transparent);
    box-shadow: none;
}

.ice-acts .gx-chip--danger:hover:not(:disabled) {
    color: #fff;
    background: var(--gx-lose);
    border-color: var(--gx-lose);
    box-shadow: 0 4px 12px color-mix(in srgb, var(--gx-lose) 35%, transparent);
}

/* Фишка, которой сейчас ставят, отличается не только кольцом: соседи
   приглушены. Разница между «выбрана» и «не выбрана» должна ловиться
   боковым зрением — во время приёма ставок смотрят на поля, а не сюда. */
.ice-chip:not(.is-active):not(:disabled) {
    filter: saturate(.72) brightness(.97);
    opacity: .82;
}

.ice-chip:not(.is-active):not(:disabled):hover {
    filter: none;
    opacity: 1;
}

/* ФИШКА.

   Собрана из трёх слоёв, как настоящая:

     сам элемент — ОБОД. Восемь вставок по кругу repeating-conic-gradient:
                   именно они делают фишку фишкой, без них выходит просто
                   цветной кружок. Вставки узкие — 18° из 45°, как на
                   настоящих; широкие превращают обод в шахматку.
     ::before     — ЛИЦО, отдельный кружок в 62% диаметра. Именно элементом,
                   а не ещё одним фоном: на лице свой венчик рисок, а
                   конический градиент заливает круг целиком, и одним фоном
                   его по радиусу не остановить — риски полезли бы на обод.
     span         — номинал поверх всего.

   Объём даётся тенями, а не картинкой: свет сверху, тень снизу изнутри,
   тёмный кант по краю и падающая тень под фишкой. Четыре тени вместо
   одной, но именно их сумма читается как «предмет лежит на столе». */
.ice-chip {
    position: relative;
    flex: none;
    width: 56px;
    height: 56px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    cursor: pointer;
    font-family: inherit;
    background:
        radial-gradient(circle at 50% 28%,
            rgba(255, 255, 255, .45) 0, rgba(255, 255, 255, 0) 64%),
        repeating-conic-gradient(from -9deg,
            var(--chip-ins) 0 18deg,
            var(--chip) 18deg 45deg);
    box-shadow:
        0 3px 8px rgba(9, 24, 44, .34),
        inset 0 0 0 1.5px rgba(9, 24, 44, .3),
        inset 0 2px 3px rgba(255, 255, 255, .4),
        inset 0 -4px 6px rgba(9, 24, 44, .22);
    transition: transform .14s var(--gx-ease), box-shadow .14s ease;
}

/* Лицо. Венчик коротких рисок по краю — деталь, по которой фишку узнают
   даже боковым зрением. */
.ice-chip::before {
    content: '';
    position: absolute;
    inset: 19%;
    border-radius: 50%;
    background:
        radial-gradient(circle, var(--chip-face) 0 74%, transparent 74%),
        repeating-conic-gradient(from 11deg,
            rgba(9, 24, 44, .16) 0 4deg,
            transparent 4deg 22.5deg),
        var(--chip-face);
    box-shadow:
        0 0 0 1.5px rgba(9, 24, 44, .22),
        inset 0 2px 3px rgba(255, 255, 255, .75),
        inset 0 -2px 4px rgba(9, 24, 44, .14);
}

.ice-chip span {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    font-size: 13px;
    font-weight: 900;
    color: var(--chip-ink);
    font-variant-numeric: tabular-nums;
    letter-spacing: -.02em;
    text-shadow: 0 1px 0 rgba(255, 255, 255, .55);
}

.ice-chip:hover { transform: translateY(-3px); }

/* Выбранная приподнята и обведена — как вынутая из стойки. Тени обода
   приходится повторить: box-shadow заменяется целиком, а не дополняется. */
.ice-chip.is-active {
    transform: translateY(-6px);
    box-shadow:
        0 0 0 3px var(--gx-bg-2),
        0 0 0 5px var(--gx-blue),
        0 8px 16px rgba(9, 24, 44, .34),
        inset 0 0 0 1.5px rgba(9, 24, 44, .3),
        inset 0 2px 3px rgba(255, 255, 255, .4),
        inset 0 -4px 6px rgba(9, 24, 44, .22);
}

/* НОМИНАЛЫ. Красная, зелёная, синяя и чёрная стоят там же, где в казино:
   достоинство читается цветом, цифру разбирать не нужно.

     --chip      обод
     --chip-ins  вставки. У светлых фишек белые, у тёмных — своего оттенка:
                 белое на чёрном и на фиолетовом выглядит дёшево, у чёрной
                 вставки золотые, как на настоящих чипах высокого номинала
     --chip-face лицо
     --chip-ink  цифра

   ЦВЕТ ПРИВЯЗАН К МЕСТУ В РЯДУ, А НЕ К СУММЕ. Раньше ключом был data-val:
   «10» серая, «50» красная и так далее. Но номиналы теперь подбираются под
   баланс игрока (chipLadder в icefishing.js), и на стойке может оказаться
   и 1 с 5, и 50к со 100к — у такой фишки не было бы ни обода, ни лица, один
   пустой круг.

   Место в ряду для этого и подходит лучше суммы: рейка всегда идёт от
   дешёвой к дорогой, а привычный порядок цветов казино ровно это и
   означает. Игрок с балансом в сто рублей и игрок с миллионом видят одну и
   ту же лестницу цветов, каждый в своём масштабе. */
.ice-chip[data-tier="0"] { --chip: #7e97b1; --chip-ins: #ffffff; --chip-face: #f4f8fc; --chip-ink: #16324e; }
.ice-chip[data-tier="1"] { --chip: #cf2f26; --chip-ins: #ffffff; --chip-face: #fff4f3; --chip-ink: #7d1a14; }
.ice-chip[data-tier="2"] { --chip: #17924c; --chip-ins: #ffffff; --chip-face: #f0fbf5; --chip-ink: #0b5029; }
.ice-chip[data-tier="3"] { --chip: #2464c7; --chip-ins: #ffffff; --chip-face: #f0f5fd; --chip-ink: #123a6e; }
.ice-chip[data-tier="4"] { --chip: #6f3fe0; --chip-ins: #ece2ff; --chip-face: #f6f1ff; --chip-ink: #38197a; }
.ice-chip[data-tier="5"] { --chip: #1d2938; --chip-ins: #d8b45a; --chip-face: #edf1f6; --chip-ink: #16202b; }

/* НЕ ПО КАРМАНУ. Фишку дороже баланса не убираем со стойки, а гасим: убери
   её — и лестница номиналов дёргалась бы после каждой ставки, а игрок терял
   бы из виду, куда вообще может дорасти. Погашенная показывает и то и
   другое: и что номинал есть, и что сейчас он недоступен. */
.ice-chip:disabled {
    cursor: not-allowed;
    filter: grayscale(.85);
    opacity: .34;
    box-shadow: inset 0 0 0 1.5px rgba(9, 24, 44, .18);
}

.ice-chip:disabled:hover { filter: grayscale(.85); opacity: .34; transform: none; }

/* --------------------------------------------------- ИТОГ И ДЕЙСТВИЯ */

/* Итог и кнопки в одной строке.
 *
 * Прежде под «мои ставки» отводился отдельный столбец панели со списком
 * полей и суммой. В пустом раунде — а это каждый раунд до первой фишки —
 * он занимал треть полосы ради надписи «Всего 0 ₽».
 *
 * Суммы по полям написаны на самих плитках, и второй их список рядом не
 * помогал, а требовал сверять два места. Остался итог одним числом; когда
 * ставок нет, он прячется целиком и кнопки просто занимают строку. */
.ice-acts {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: 10px 14px;
}

.ice-total {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin-right: auto;
}

.ice-total[hidden] { display: none; }

.ice-total__label {
    font-size: 11px;
    font-weight: 800;
    letter-spacing: .4px;
    text-transform: uppercase;
    color: var(--gx-muted);
}

.ice-total__sum {
    font-size: 19px;
    font-weight: 900;
    color: var(--gx-ink);
    font-variant-numeric: tabular-nums;
}

/* «Очистить» — единственная кнопка, которая забирает, а не добавляет.
   Красной её делать незачем (это не опасная операция, деньги возвращаются),
   но и наравне с «Повторить» она стоять не должна. */
.gx-chip--danger {
    color: var(--gx-lose);
    border-color: color-mix(in srgb, var(--gx-lose) 32%, transparent);
}

.gx-chip--danger:hover:not(:disabled) {
    background: color-mix(in srgb, var(--gx-lose) 10%, transparent);
    border-color: var(--gx-lose);
}

/* Выключенная кнопка действия. «Удвоить» без ставок и «Повторить» без
   прошлого раунда обещают то, чего нажатие не сделает. */
.gx-chip:disabled {
    opacity: .42;
    cursor: default;
    transform: none;
}

/* --------------------------------------------------------------- УЗКИЙ */

/* Container query, а не @media: стол перестраивается по ширине СВОЕГО
   блока, как и остальные режимы. На узкой колонке это тоже работает. */
/* УЗКИЕ ЭКРАНЫ: ДИСК СТАНОВИТСЯ ШИРЕ, А НЕ УЖЕ.
 
   Ширина диска задана в процентах от стола, поэтому на телефоне колесо
   ужимается вместе с ним — и долька у кромки падает с 57 пикселей до 33.
   Это и читается как «мелко».
 
   Лечится увеличением процента: чем шире диск, тем площе дуга и тем меньше
   долек в кадре, зато каждая крупнее. На телефоне это верный размен —
   пятнадцать долек там всё равно не рассмотреть.
 
       стол 500px    132% -> R 330, 15 долек, долька 33px
                     190% -> R 475, 10 долек, долька 48px
 
   Высота тоже в cqw, а не в пикселях: она обязана следовать за радиусом,
   иначе на широком телефоне обрежется полоса долек, а на узком в кадр
   вылезет белая ступица. 43cqw держит срез примерно на 0.55 радиуса — как
   раз по внутреннему краю долек. */
/* РАЗМЕР КОЛЕСА ПО ЕГО СОБСТВЕННОЙ ШИРИНЕ.
 
   Ширина диска задана в процентах от колеса, поэтому на узком экране оно
   ужимается вместе с ним — и долька у кромки падает с 57 пикселей до 33.
   Это и читается как «мелко».
 
   Лечится увеличением процента: чем шире диск, тем площе дуга и тем меньше
   долек в кадре, зато каждая крупнее. На телефоне это верный размен —
   пятнадцать долек там всё равно не рассмотреть.
 
       колесо 340px   132% -> R 224, 15 долек, долька 23px
                      190% -> R 323, 10 долек, долька 33px
                      240% -> R 408,  8 долек, долька 41px
 
   ЧЕТЫРЕ ЗОНЫ, А НЕ ДВЕ, — из-за стыков. На границе долька скачком меняет
   размер, и скачок равен отношению процентов. Пара 240/158 дала бы прыжок
   в полтора раза: телефон 520px выглядел бы заметно крупнее планшета
   530px. Лестница 240/195/160/132 держит все три стыка около 1.22.
 
   Высота тоже в cqw: она обязана следовать за радиусом, иначе на широком
   экране обрежется полоса долек, а на узком в кадр вылезет белая ступица.
   Коэффициент свой на каждую зону — везде 0.52 радиуса, отсюда срез ровно
   на 0.48 R при любой ширине: видна вся полоса долек и краешек ступицы.
 
   Указатель тем же порядком: его высота держится на 1.35 толщины обода,
   чтобы остриё всегда заходило на дольки на одну и ту же долю. */
@container icew (min-width: 430px) {
    .ice-wheel { height: clamp(170px, 50.7cqw, 300px); }
    .ice-wheel__disc { width: 195%; }
    .ice-wheel__pointer { width: clamp(30px, 9.6cqw, 56px); }
}

@container icew (min-width: 560px) {
    .ice-wheel { height: clamp(180px, 41.6cqw, 300px); }
    .ice-wheel__disc { width: 160%; }
    .ice-wheel__pointer { width: clamp(30px, 7.8cqw, 56px); }
}

/* Потолок высоты здесь ВЫШЕ, чем в узких зонах, и не для красоты: макет
   страницы по ширине ничем не ограничен, и на большом мониторе колесо
   доходит до полутора тысяч пикселей. С потолком 300px срез уползал на
   0.70 радиуса и отрезал рыбам значки. 420px держат срез в допуске до
   ширины колеса около 1500px, а это уже монитор под 2700. */
@container icew (min-width: 730px) {
    .ice-wheel { height: clamp(200px, 34.3cqw, 420px); }
    .ice-wheel__disc { width: 132%; }
    .ice-wheel__pointer { width: clamp(30px, 6.5cqw, 56px); }
}

/* Пятая зона появилась вместе со столом во всю ширину: без боковой панели
   колесо на большом мониторе доходит до 1200 пикселей, и при 132% дольки
   вырастали до 70 пикселей при пятнадцати в кадре. Лишнюю ширину правильнее
   тратить на КОЛИЧЕСТВО долек, а не на их размер — восемнадцать в кадре
   ближе к настоящему столу. Скачок 132/112 = 1.18, в ряду со всеми. */
@container icew (min-width: 1000px) {
    .ice-wheel { height: clamp(240px, 29.1cqw, 420px); }
    .ice-wheel__disc { width: 112%; }
    .ice-wheel__pointer { width: clamp(30px, 5.5cqw, 56px); }
}

@container gx (max-width: 560px) {
    .ice-board { grid-template-columns: repeat(2, 1fr); }

    /* СБРОС ПРОЛЁТОВ ОБЯЗАТЕЛЕН, и без него сетка врала.
     *
     * Базовые правила рассчитаны на шесть дорожек: плитка занимает span 3
     * (половину), пятая — span 6 (всю ширину). Здесь дорожек ДВЕ, а пролёты
     * от них никто не отменял, и grid поступает по спецификации — достраивает
     * неявные колонки, чтобы вместить пролёт в шесть.
     *
     * Получается ряд из шести дорожек, где первые две — 1fr, а четыре
     * достроенные — auto. Левая плитка садится на 1fr + 1fr + auto, правая
     * на три auto, и ширины у них РАЗНЫЕ: на телефоне правая выходила на
     * пятую часть шире левой. Сетка при этом выглядела правдоподобно —
     * по две в ряд, — поэтому перекос читался не как поломка вёрстки, а как
     * теснота: в узкой левой плитке сумма ставки рвалась пополам, в широкой
     * правой та же строка помещалась целиком.
     *
     * Теперь пролёты сброшены под двухдорожечную сетку: четыре плитки по
     * одной дорожке, пятая на обе. Ряд ровный при любой ширине экрана. */
    .ice-spot { grid-column: auto; }
    .ice-spot:nth-child(5) { grid-column: span 2; }

    .ice-spot__mult { font-size: 15px; }

    /* Фишки мельче, но не меньше 46px: ниже сорока четырёх начинается зона
       промахов пальцем, а ошибиться номиналом дороже, чем полем.
       Пропорции внутри держатся сами — лицо задано в процентах. */
    .ice-chip { width: 46px; height: 46px; }
    .ice-chip span { font-size: 12px; }
    .ice-rack { gap: 7px; }
}

/* ==========================================================================
   КЕНО

   Поле — сетка из 40 или 80 клеток. Число колонок задаётся не медиазапросом,
   а container query по ширине СВОЕГО блока: у режима есть боковая панель, и
   на среднем экране поле уже, чем окно, вдвое. Считай мы по окну — на
   планшете клетки оказались бы вдвое мельче, чем нужно.

   Четыре состояния клетки, и они обязаны различаться с одного взгляда:

     обычная    ждёт
     is-picked  отмечена игроком
     is-drawn   выпала, но игрок её не отмечал
     is-hit     отмечена И выпала — это и есть попадание

   Попадание выделено сильнее всех остальных вместе взятых: ради него игра и
   идёт, и искать его глазами по сетке из восьмидесяти клеток игрок не должен.
   ========================================================================== */

.keno-board {
    display: grid;
    gap: clamp(4px, 1.1cqw, 8px);
    margin-bottom: 14px;
}

/* Сорок чисел — восемь в ряд, восемьдесят — десять. Не поровну: у большого
   поля клетки и так мельче, и лишние две колонки экономят две строки. */
.keno-board[data-total="40"] { grid-template-columns: repeat(8, 1fr); }
.keno-board[data-total="80"] { grid-template-columns: repeat(10, 1fr); }

@container gx (max-width: 460px) {
    .keno-board[data-total="80"] { grid-template-columns: repeat(8, 1fr); }
}

.keno-cell {
    position: relative;
    aspect-ratio: 1 / 1;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: clamp(8px, 1.8cqw, 14px);
    border: 1px solid var(--gx-line);
    background: var(--gx-bg-2);
    color: var(--gx-ink);
    font-family: inherit;
    font-size: clamp(11px, 2.2cqw, 15px);
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    cursor: pointer;
    transition: transform .14s var(--gx-ease), background .15s ease,
                border-color .15s ease, color .15s ease, box-shadow .15s ease;
}

.keno-cell:hover {
    border-color: var(--gx-blue);
    background: var(--gx-bg-3);
    transform: translateY(-2px);
}

.keno-cell:active { transform: translateY(0) scale(.94); }

/* ОТМЕЧЕННАЯ КЛЕТКА — синяя, как и остальной интерфейс режима.
 *
 * Внутренний блик сверху и тень снизу: без них плоская заливка выглядит
 * дыркой в сетке, а не выпуклой фишкой. */
.keno-cell.is-picked {
    background: linear-gradient(155deg, #3b82f6 0%, var(--gx-blue-d) 100%);
    border-color: transparent;
    color: #fff;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .3),
        0 4px 12px -5px rgba(29, 78, 216, .8);
    animation: kenoPick .26s var(--gx-ease) both;
}

/* КОЛЬЦО ПРИ ОТМЕТКЕ — отдельным слоем, а не тенью самой клетки.
 *
 * У .is-picked уже занят и transform (штамп), и box-shadow (блик с тенью).
 * Расходящееся кольцо на том же элементе затирало бы их. Псевдоэлемент даёт
 * ему собственный слой и позволяет уйти за границы клетки, не растягивая
 * сетку.
 *
 * АНИМАЦИЯ ВИСИТ НА КЛАССЕ, А НЕ ЗАПУСКАЕТСЯ ИЗ СКРИПТА, и это важно:
 * renderPicks() зовётся на каждый клик по любой клетке и переставляет классы
 * всем сразу. Но classList.toggle(c, true) на уже отмеченной ничего не
 * меняет, класс не переустанавливается — и анимация не перезапускается.
 * Дёргай мы её из кода, всё поле вздрагивало бы на каждый клик. */
.keno-cell.is-picked::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    animation: kenoPickRing .45s var(--gx-ease) both;
}

@keyframes kenoPick {
    0%   { transform: scale(.76); }
    55%  { transform: scale(1.1); }
    100% { transform: scale(1); }
}

@keyframes kenoPickRing {
    0%   { box-shadow: 0 0 0 0 rgba(59, 130, 246, .55); }
    100% { box-shadow: 0 0 0 9px rgba(59, 130, 246, 0); }
}

/* ВЫПАВШЕЕ, НО НЕ ОТМЕЧЕННОЕ — «шар лёг мимо».
 *
 * Прежде это была пунктирная серая рамка с блёклой цифрой. На поле в
 * восемьдесят клеток такое почти не читалось: числа выходили одно за другим,
 * а на экране будто ничего не происходило. Между тем это ЕДИНСТВЕННОЕ, что
 * показывает ход розыгрыша, — попадания редки, и в большинстве раундов игрок
 * не видит ни одного.
 *
 * Теперь это заполненная фишка с чёткой цифрой: событие видно, но цветом оно
 * нейтрально и зелёному попаданию не конкурент. Тёмная цифра, а не блёклая —
 * выпавшее число настоящий результат, а не примечание мелким шрифтом. */
.keno-cell.is-drawn {
    background: var(--gx-bg-3);
    /* --gx-ink-2, а не --gx-muted: обводка обязана быть видна в ОБЕИХ темах.
       Приглушённый серый на светлой ещё читался, на тёмной сливался с самой
       клеткой, и вся правка сводилась бы к нулю там, где играет большинство. */
    border-color: var(--gx-ink-2);
    color: var(--gx-ink);
    /* Просвет между обводкой и заливкой — клетка читается кольцом, а не
       просто перекрашенным квадратом. */
    box-shadow: inset 0 0 0 2px var(--gx-bg);
    animation: kenoDrop .3s var(--gx-ease) both;
}

/* ПАДЕНИЕ, А НЕ ШТАМП — и это нарочно.
 *
 * У отметки движение на месте — «штамп» с перелётом по масштабу (kenoPick),
 * у попадания такой же толчок, только сильнее и с кольцом (kenoHit). Дай мы
 * выпавшему третий вариант того же самого, три разных события слились бы, а
 * различать их надо с одного взгляда: «я отметил», «машина вытянула»,
 * «совпало». Поэтому здесь единственное движение ПО МЕСТУ, а не по размеру.
 *
 * Шар именно падает: приходит сверху, слегка сплющивается о клетку и
 * встаёт. Длительность короче шага показа (90–140 мс), поэтому соседние
 * числа чуть накладываются — розыгрыш читается как поток, а не как
 * череда отдельных вспышек. */
@keyframes kenoDrop {
    0%   { transform: translateY(-30%) scale(.9); opacity: 0; }
    60%  { transform: translateY(0) scale(1.06); opacity: 1; }
    100% { transform: translateY(0) scale(1); opacity: 1; }
}

/* ПОПАДАНИЕ. Заливка, кольцо и короткий толчок: это единственное событие
   раунда, и оно обязано находиться взглядом мгновенно. */
.keno-cell.is-hit {
    background: linear-gradient(150deg, #10b981 0%, #059669 100%);
    border-color: transparent;
    color: #fff;
    box-shadow: 0 0 0 3px var(--gx-bg), 0 0 0 5px #10b981;
    animation: kenoHit .32s var(--gx-ease) both;
}

/* Попадание ставится ПОВЕРХ отметки — класс is-picked с клетки не снимается.
   Кольцо отметки под ним гасим, иначе фиолетовый ободок остаётся висеть
   вокруг зелёной клетки. */
.keno-cell.is-hit::after { display: none; }

@keyframes kenoHit {
    0%   { transform: scale(.72); }
    60%  { transform: scale(1.12); }
    100% { transform: scale(1); }
}

/* ----------------------------------------------------- ПЕРЕКЛЮЧАТЕЛЬ */

.keno-formats {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
}

.keno-fmt {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    padding: 9px 6px;
    border-radius: 12px;
    border: 1px solid var(--gx-line);
    background: var(--gx-bg-2);
    color: var(--gx-ink);
    font-family: inherit;
    cursor: pointer;
    transition: border-color .15s ease, background .15s ease;
}

.keno-fmt b { font-size: 18px; font-weight: 900; font-variant-numeric: tabular-nums; }
.keno-fmt span { font-size: 11px; font-weight: 600; color: var(--gx-muted); }

.keno-fmt:hover { border-color: var(--gx-muted); }

.keno-fmt.is-on {
    border-color: var(--gx-blue);
    background: color-mix(in srgb, var(--gx-blue) 10%, transparent);
}

.keno-fmt.is-on span { color: var(--gx-blue-d); }

/* Риск — три кнопки в строку. Отдельная сетка, а не .keno-formats: там две
   колонки, и третья кнопка ушла бы на вторую строку одна. Сам вид кнопки
   общий с форматом (.keno-fmt), чтобы переключатели читались как родня. */
.keno-risks {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 8px;
}

/* Риск — это цена промаха, и цвет об этом говорит: чем выше, тем горячее.
   Одинаковый синий на всех трёх не отличал бы «часто и понемногу» от
   «редко и много», а разница между ними здесь главная. */
/* Пояснения под названием больше нет — кнопка в одну строку, поэтому ей
   нужен свой отступ: общий с форматом рассчитан на две строки, и без второй
   кнопка выглядела бы приплюснутой. */
.keno-risks .keno-fmt {
    padding: 11px 6px;
}

.keno-risks .keno-fmt b { font-size: 13px; font-weight: 800; }

.keno-fmt--medium.is-on {
    border-color: #f59e0b;
    background: color-mix(in srgb, #f59e0b 12%, transparent);
}

.keno-fmt--high.is-on {
    border-color: #ef4444;
    background: color-mix(in srgb, #ef4444 12%, transparent);
}

.keno-fmt--medium.is-on span,
.keno-fmt--high.is-on span { color: inherit; }

/* Пока стол тесный, панель отдаёт доске всё, что может: 268px — её нижняя
   граница из общего правила выше, здесь опускаем до 236. Разница в тридцать
   пикселей на доске заметнее, чем на поле ввода ставки. */
@container gx (min-width: 640px) and (max-width: 779.98px) {
    .gx-layout {
        grid-template-columns: minmax(236px, 268px) minmax(0, 1fr);
        gap: 14px;
    }
}

/* Поле на восемьдесят чисел в тесном столе — восемь колонок вместо десяти.
   Десять при доске в 350px дают клетку в 31px: цифры ещё читаются, а попасть
   пальцем уже трудно. Тот же приём, что и в @container gx (max-width: 460px)
   ниже, только для двухколоночной раскладки. */
@container gx (min-width: 640px) and (max-width: 779.98px) {
    .keno-board[data-total="80"] { grid-template-columns: repeat(8, 1fr); }
}

/* --------------------------------------------------------- ВЫПЛАТЫ */

.keno-pay {
    padding: 11px 13px;
    border-radius: var(--gx-r-sm);
    background: var(--gx-bg-2);
    border: 1px solid var(--gx-line);
}

.keno-pay__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 8px;
}

/* Строки в ряд с переносом, а не столбцом: их до шести, и столбцом они
   вытягивали бы экран ровно там, где игрок хочет видеть поле. */
.keno-pay__rows {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.keno-pay__row {
    flex: 1 1 96px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    padding: 7px 8px;
    border-radius: 10px;
    background: var(--gx-bg);
    border: 1px solid var(--gx-line);
}

.keno-pay__hit { font-size: 10.5px; font-weight: 700; color: var(--gx-muted); }

.keno-pay__mult {
    font-size: 15px;
    font-weight: 900;
    color: var(--gx-ink);
    font-variant-numeric: tabular-nums;
}

/* Правила .keno-pay__odds здесь больше нет: подпись с вероятностью убрана из
   таблицы. Сами вероятности никуда не делись и отдаются ручкой
   /api/keno/paytable вместе с множителями — посчитать их по-прежнему может
   кто угодно. */

/* Правил под .is-zero здесь больше нет: «catch 0» убран вместе со старым
   порогом выплат, отдельной строки под «мимо всех» в таблице не бывает.
   Мёртвое правило тем и опасно, что молча ждёт своего часа. */

/* Держатель поля: по нему выравнивается окно итога. Без него .gx-result
   растянулся бы на всю секцию, вместе с таблицей выплат под полем, и
   карточка встала бы ниже клеток — то есть мимо того, куда смотрит игрок. */
.keno-holder {
    position: relative;
}

/* =====================================================================
   СЛОТЫ — ОКНО С ИГРОЙ

   Игра провайдера открывается в iframe поверх всего. Не модалка сайта:
   слоты рассчитаны на весь экран, и запертые в карточку они рисуют свои
   кнопки за её краями.

   iframe в разметке НЕТ — его создаёт и удаляет slots.js. Спрятанный
   iframe продолжает жить: звук, таймеры и сессия провайдера остаются, и
   следующий запуск открывался бы поверх ещё идущей игры.
   ===================================================================== */

.slot-player {
    position: fixed;
    inset: 0;
    z-index: 2600;
    display: flex;
    flex-direction: column;
    background: #05080d;
}

.slot-player[hidden] { display: none; }

.slot-player__bar {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 14px;
    background: #0b1420;
    border-bottom: 1px solid rgba(148, 180, 220, .12);
}

.slot-player__name {
    font-size: 14px;
    font-weight: 800;
    color: #eaf2fd;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Крупная цель для пальца: на телефоне это единственный выход из игры. */
.slot-player__close {
    flex: none;
    width: 40px;
    height: 40px;
    border: 0;
    border-radius: 12px;
    background: rgba(255, 255, 255, .08);
    color: #eaf2fd;
    font-size: 18px;
    cursor: pointer;
    transition: background .15s ease;
}

.slot-player__close:hover { background: rgba(255, 255, 255, .16); }

.slot-player__frame {
    flex: 1 1 auto;
    min-height: 0;
    position: relative;
}

/* Рамка без границ и на всю площадь: любые отступы вокруг игры читаются
   как поломка вёрстки, а не как оформление. */
.slot-player__frame iframe {
    display: block;
    width: 100%;
    height: 100%;
    border: 0;
}

.slot-player__wait {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    padding: 20px;
    text-align: center;
    color: #8ba6c4;
    font-size: 14px;
    font-weight: 600;
}

/* ==========================================================================
   МИНЫ: РУЛЕТКА С ИКСАМИ

   Лента едет под неподвижным указателем — как на джекпот-рулетках. Набор
   секторов повторяется много раз, лента разгоняется и плавно тормозит так,
   что выпавший икс встаёт ровно под указателем.

   Появляется, только если множитель в этом раунде выпал; решает это сервер
   при заведении игры, из той же пары сидов, что и расклад мин. Показывается
   ВЕСЬ набор секторов, а не один выпавший: игрок должен видеть, из чего
   тянули.
   ========================================================================== */

.mines-roul {
    /* ШИРИНА ЗАДАЁТСЯ ЯВНО, И ЭТО НЕ УКРАШЕНИЕ.
     *
     * Родитель .gx-board-wrap — колоночный flex с align-items: center, то
     * есть ширина ребёнка по умолчанию НЕ тянется на всю строку, а равна
     * ширине его содержимого. А содержимое здесь — лента из повторённого
     * набора секторов: на розыгрыше её строят в 6-20 витков, и max-content
     * доходит до нескольких тысяч пикселей.
     *
     * Без этих двух строк выходило так: панель раздувалась по ленте,
     * overflow: hidden у окна не обрезал ничего (окно было ровно шириной
     * ленты), карточка с рамкой и фоном уезжала за правый край, а сектора
     * шли сплошной полосой во всю страницу. Хуже того, ломалась и
     * остановка: смещение считается от view.clientWidth, и при ширине окна
     * в 4000px нужный икс вставал далеко за экраном.
     *
     * Заметно это было только на розыгрыше. При возврате на страницу с
     * недоигранным раундом лента строится в ОДИН виток (instant), её
     * max-content мал, и панель случайно получалась правильного размера —
     * отсюда и «после перезагрузки выглядит нормально».
     *
     * 460px — ширина игрового поля (.gx-board-holder, .game-board): панель
     * стоит прямо под ним и обязана быть с ним по одной линии. Тот же
     * приём у соседей по .gx-board-wrap — у .gx-board-holder и .gx-coeffs
     * ширина проставлена ровно так же и по той же причине. */
    width: 100%;
    max-width: 460px;

    margin-top: 12px;
    padding: 10px 12px 12px;
    border-radius: var(--gx-r-sm);
    background: var(--gx-bg-2);
    border: 1px solid var(--gx-line);
}

.mines-roul__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 8px;
}

/* ОКНО. Лента внутри него шире его самой и обрезается по краям — этим и
   создаётся барабан. overflow: hidden здесь обязателен: без него лента
   вылезет за карточку и растянет страницу вбок. */
.mines-roul__view {
    position: relative;
    overflow: hidden;
    border-radius: 10px;
    padding: 6px 0;
    background: var(--gx-bg-3);
    box-shadow: inset 0 1px 4px rgba(9, 24, 44, .12);
}

/* Затемнение к краям — лента должна уходить в тень, а не обрываться по
   линейке. pointer-events: none, иначе слой перехватывал бы касания. */
.mines-roul__view::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 2;
    pointer-events: none;
    background: linear-gradient(90deg,
        var(--gx-bg-3) 0%, rgba(0, 0, 0, 0) 14%,
        rgba(0, 0, 0, 0) 86%, var(--gx-bg-3) 100%);
}

/* УКАЗАТЕЛЬ — неподвижная черта по центру окна. Под ней и останавливается
   выпавший сектор. */
.mines-roul__view::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: 50%;
    width: 2px;
    margin-left: -1px;
    z-index: 3;
    pointer-events: none;
    background: var(--gx-blue);
    box-shadow: 0 0 0 1px rgba(255, 255, 255, .55), 0 0 10px rgba(37, 99, 235, .55);
}

/* ЛЕНТА. Ширину не задаём — её набирают сами сектора; двигает ленту
   transform из mines.js. Именно transform, а не left или margin: он не
   вызывает пересчёт раскладки на каждом кадре и потому не дёргается на
   слабых телефонах. */
.mines-roul__strip {
    display: flex;
    gap: 6px;
    padding: 0 6px;
    width: max-content;
    will-change: transform;
}

/* Ширина фиксированная и одинаковая у всех секторов: ×2 и ×10000 должны
   занимать поровну, иначе лента визуально «спотыкается» на длинных числах.
   flex: none обязателен — без него flex сжал бы сектора под ширину окна и
   барабан бы исчез. */
.mines-roul__x {
    flex: none;
    width: 62px;
    padding: 9px 4px;
    border-radius: 9px;
    border: 1px solid var(--gx-line);
    background: var(--gx-bg-2);
    color: var(--gx-ink-2, var(--gx-muted));
    font-size: 14px;
    font-weight: 800;
    text-align: center;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ВЫПАВШИЙ СЕКТОР — зелёный, как выигрыш. Подсвечивается только после
   остановки: во время прокрутки подсветка выдала бы результат заранее. */
.mines-roul__x.is-win {
    background: linear-gradient(160deg, var(--gx-win) 0%, #0b8a60 100%);
    border-color: transparent;
    color: #fff;
    box-shadow:
        0 0 0 2px var(--gx-bg-3),
        0 0 0 4px var(--gx-win),
        0 6px 16px rgba(14, 164, 114, .38);
    animation: minesRoulWin .38s var(--gx-ease) both;
}

@keyframes minesRoulWin {
    0%   { transform: scale(.9); }
    60%  { transform: scale(1.1); }
    100% { transform: scale(1); }
}


/* --------------------------------------------------------------------------
   ОБЪЯВЛЕНИЕ БОНУСНОЙ ИГРЫ

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

     объявили   «Бонусная игра · разыгрывается множитель раунда»
     крутим     та же строка пульсирует, лента едет
     назвали    «Множитель ×3 · выигрыш раунда умножится на 3»

   Состояние задаётся классом на .mines-roul (is-rolling / is-done), а не
   переписыванием разметки: так текст меняется без перерисовки узлов, и
   анимация не срывается на середине.

   ПОЛНОЭКРАННОЙ ЗАСТАВКИ ЗДЕСЬ НЕТ НАМЕРЕННО. Она перекрыла бы поле ровно
   тогда, когда игрок собрался открывать клетки, а приём ставок в минах
   ничем не ограничен по времени — человек просто ждал бы, пока его пустят
   играть. Объявление обязано быть заметным, но не поперёк дороги.
   -------------------------------------------------------------------------- */

/* Сама панель приезжает снизу и подсвечивается — это и есть «что-то
   случилось». Без появления полоса просто возникала бы в разметке, и
   заметить её можно было только случайно. */
.mines-roul {
    animation: minesRoulIn .34s var(--gx-ease) both;
}

@keyframes minesRoulIn {
    from { opacity: 0; transform: translateY(10px) scale(.98); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}

.mines-roul__banner {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 4px 8px;
    margin-bottom: 9px;
    padding: 8px 11px;
    border-radius: 10px;
    border: 1px solid rgba(37, 99, 235, .28);
    background: linear-gradient(120deg,
        rgba(37, 99, 235, .14) 0%, rgba(56, 189, 248, .10) 100%);
}

/* Звёздочка — единственная картинка в объявлении. Значок вместо иконки,
   потому что ради одного символа тянуть шрифт или svg незачем. */
.mines-roul__spark {
    flex: none;
    font-size: 14px;
    line-height: 1;
    color: var(--gx-sky);
}

.mines-roul__title {
    font-size: 14px;
    font-weight: 900;
    letter-spacing: -.2px;
    color: var(--gx-blue);
}

/* Пояснение занимает всю строку под заголовком, если не влезло рядом:
   flex-basis тут безопасен, ширина у баннера своя (он растянут по панели),
   и замкнутого расчёта, как у плашки итога, не возникает. */
.mines-roul__sub {
    font-size: 11.5px;
    font-weight: 600;
    color: var(--gx-muted);
}

/* ВО ВРЕМЯ ПРОКРУТКИ баннер дышит — этим он и отличается от статичной
   подписи, которую глаз пропускает. Пульсация только на фоне и рамке:
   мигающий текст читать невозможно. */
.mines-roul.is-rolling .mines-roul__banner {
    animation: minesRoulPulse 1.1s ease-in-out infinite;
}

@keyframes minesRoulPulse {
    0%, 100% { border-color: rgba(37, 99, 235, .28); background-position: 0% 50%; }
    50%      { border-color: rgba(56, 189, 248, .65); background-position: 100% 50%; }
}

.mines-roul.is-rolling .mines-roul__spark {
    display: inline-block;
    animation: minesRoulSpin 1.1s linear infinite;
}

@keyframes minesRoulSpin {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}

/* РЕЗУЛЬТАТ — зелёный, как выигрыш, и без движения: розыгрыш кончился,
   дёргаться больше нечему. Цвет тот же, что у выпавшего сектора ленты, —
   объявление и барабан должны читаться как одно событие. */
.mines-roul.is-done .mines-roul__banner {
    border-color: rgba(14, 164, 114, .45);
    background: linear-gradient(120deg,
        rgba(14, 164, 114, .16) 0%, rgba(62, 207, 142, .10) 100%);
}

.mines-roul.is-done .mines-roul__title { color: var(--gx-win); }
.mines-roul.is-done .mines-roul__spark { color: var(--gx-win); }

/* На телефоне объявление плотнее: панель и так отнимает высоту у поля. */
@container gx (max-width: 560px) {
    .mines-roul__banner { margin-bottom: 8px; padding: 7px 9px; }
    .mines-roul__title { font-size: 13px; }
    .mines-roul__sub { font-size: 11px; }
}

/* «Уменьшить движение» — гасим и пульсацию, и вращение звёздочки, и
   выезд панели. Смысл объявления держится на цвете и тексте, они
   остаются. */
@media (prefers-reduced-motion: reduce) {
    .mines-roul,
    .mines-roul.is-rolling .mines-roul__banner,
    .mines-roul.is-rolling .mines-roul__spark { animation: none; }
}

/* На телефоне сектора мельче: полоса не должна отнимать высоту у поля. */
@container gx (max-width: 560px) {
    .mines-roul { margin-top: 8px; padding: 8px 10px 10px; }
    .mines-roul__x { width: 54px; padding: 8px 4px; font-size: 13px; }
}

/* Системная настройка «уменьшить движение» — прокрутки нет.
   Основную работу делает скрипт: он проверяет ту же настройку через
   matchMedia и сразу ставит ленту на выпавший сектор вместе с подсветкой.
   Правило здесь — страховка на случай, если разметку однажды начнут
   двигать мимо этой ветки. */
@media (prefers-reduced-motion: reduce) {
    .mines-roul__strip { transition: none !important; }
}
