.app-shell {
    max-width: 1400px;
    margin: 0 auto;
    padding: 20px;
}

.app-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 18px 22px;
    margin-bottom: 14px;
    background: var(--surface-2);
    border-radius: 14px;
}

.app-header__left {
    display: flex;
    align-items: center;
    gap: 14px;
}

.app-logo {
    font-size: 28px;
    line-height: 1;
}

.app-title {
    margin: 0;
    font-size: 24px;
}

.app-subtitle {
    margin-top: 4px;
    font-size: 14px;
    color: var(--text-muted);
}

.author-link {
    color: var(--link);
    text-decoration: none;
    font-weight: 600;
}

.author-link:hover {
    text-decoration: underline;
}

.btn-save-scenario {
    padding: 8px 20px;
    background: var(--accent);
    color: #fff;
    border: none;
    border-radius: 6px;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    margin-left: 12px;
}

.btn-save-scenario:hover {
    background: var(--accent-hover);
}

.btn-save-scenario:disabled {
    background: #94d3a2;
    cursor: not-allowed;
}

.app-header-right {
    display: flex;
    align-items: center;
    gap: 12px;
}

.cabinet-shell {
    max-width: 1400px;
    margin: 0 auto;
    padding: 20px;
    display: flex;
    gap: 24px;
    min-height: 80vh;
}

.cabinet-sidebar {
    width: 220px;
    flex-shrink: 0;
}

/* min-width: 0 у обеих колонок кабинета — иначе флекс-элемент отказывается
   сжиматься ниже min-content своего содержимого (значение по умолчанию
   min-width: auto), и любая широкая строка внутри распирает не себя, а всю
   страницу: на окне 876px .cabinet-content вырастала до 938 и включала
   горизонтальную прокрутку документа. Виновата при этом выглядела та
   вкладка, которая была открыта, — списки банков, хотя сами строки в свою
   ширину помещались. */
.cabinet-sidebar,
.cabinet-content {
    min-width: 0;
}

.cabinet-nav {
    display: flex;
    flex-direction: column;
    gap: 4px;
    background: var(--surface-2);
    border-radius: 12px;
    padding: 12px;
}

.cabinet-nav-item {
    display: block;
    padding: 10px 14px;
    border-radius: 8px;
    color: var(--text);
    text-decoration: none;
    font-weight: 600;
    font-size: 14px;
}

.cabinet-nav-item:hover {
    background: var(--surface-3);
}

.cabinet-nav-item.active {
    background: #f1f3f5;
}

.cabinet-content {
    flex: 1;
    background: #f1f3f5;
    border-radius: 12px;
    padding: 24px;
}

@media (max-width: 768px) {
    .cabinet-shell {
        flex-direction: column;
    }
    .cabinet-sidebar {
        width: 100%;
    }
    /* Разделы кабинета — тоже переносом, а не скрытым скроллом: их
       четыре, в два ряда они помещаются целиком и видны все сразу. */
    .cabinet-nav {
        flex-direction: row;
        flex-wrap: wrap;
        gap: 8px;
    }
    /* Девять вкладок админки переносятся на несколько рядов, а не едут
       в горизонтальном скролле: скрытая вкладка, о которой не знаешь, —
       та же потеря, что вкладка за краем экрана, только без полосы
       прокрутки как намёка. */
    .admin-tabs {
        flex-wrap: wrap;
    }
}

.btn-new-scenario {
    display: inline-flex;
    align-items: center;
    min-height: 42px;
    padding: 0 20px;
    border: 0;
    border-radius: 10px;
    background: var(--accent);
    color: #fff;
    cursor: pointer;
    text-decoration: none;
    font-size: 14px;
    font-weight: 600;
    margin-bottom: 18px;
}

.btn-new-scenario:hover {
    background: var(--accent-hover);
}

.scenarios-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.scenario-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px;
    background: var(--surface);
    border-radius: 10px;
    padding: 12px 16px;
}

/* Вкладка «Пользователи» администратора (2026-09-29): четыре колонки
   данных плюс роль и кнопки. Одна сетка у шапки и у строк — иначе
   заголовки разъезжаются с содержимым при длинном логине или городе.
   Колонка кнопок — не auto (2026-09-30, по замечанию автора): в шапке
   она пуста, в строке шириной с «Профиль» и корзину, и доли fr считались
   от разного остатка — заголовки уезжали вправо тем сильнее, чем правее
   колонка. Фиксированный минимум делает остаток одинаковым. */
.users-head,
.scenario-row.users-row {
    display: grid;
    grid-template-columns: minmax(120px, 1.2fr) minmax(120px, 1.4fr) minmax(90px, 1fr) 104px 160px minmax(124px, max-content);
    align-items: center;
    gap: 12px;
}
.users-head {
    padding: 0 16px 6px;
    color: var(--text-dim);
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.users-col {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.users-sort {
    display: flex;
    align-items: center;
    gap: 5px;
    border: 0;
    background: none;
    padding: 0;
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    color: inherit;
    cursor: pointer;
    text-align: left;
}
.users-sort:hover { color: var(--text); }
.users-sort.is-active { color: var(--accent-text); }
/* Значок держит своё место всегда, чтобы заголовок не дёргался при смене
   направления. */
.users-sort-mark {
    flex: 0 0 auto;
    width: 10px;
    font-size: 11px;
    line-height: 1;
}
.users-col--joined { color: var(--text-dim); font-size: 13px; }
.users-col--actions { display: flex; align-items: center; gap: 8px; }
@media (max-width: 860px) {
    /* На узком экране колонки не держатся — строка складывается, а шапка
       с сортировкой прячется: сортировать нечего показывать. */
    .users-head { display: none; }
    .scenario-row.users-row {
        grid-template-columns: minmax(0, 1fr) auto;
        row-gap: 6px;
    }
    .users-col--role { grid-column: 1; }
    .users-col--actions { grid-column: 2; grid-row: 1 / span 4; align-self: start; }
}

.scenario-link {
    flex: 1;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    text-decoration: none;
    color: var(--text);
}

.scenario-link:hover .scenario-title {
    text-decoration: underline;
}

.scenario-title {
    font-weight: 600;
    font-size: 15px;
}

.scenario-date {
    font-size: 13px;
    color: var(--text-dim);
}

.scenario-delete {
    width: 36px;
    height: 36px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    cursor: pointer;
    font-size: 18px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.scenario-delete:hover {
    background: rgba(198, 40, 40, 0.08);
}

.scenario-link {
    flex: 1;
    display: flex;
    align-items: center;
    gap: 16px;
    text-decoration: none;
    color: var(--text);
}

.scenario-link:hover .scenario-title {
    text-decoration: underline;
}

.scenario-title {
    font-weight: 600;
    font-size: 15px;
    min-width: 180px;
}

.scenario-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 14px;
    flex: 1;
}

.scenario-meta-item {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 13px;
    color: var(--text-muted);
    white-space: nowrap;
}

.scenario-icon {
    width: 18px;
    height: 18px;
    fill: var(--text-dim);
    flex-shrink: 0;
}

.scenario-date {
    font-size: 13px;
    color: var(--text-dim);
    white-space: nowrap;
}

.scenario-status {
    display: inline-block;
    padding: 3px 10px;
    border-radius: 12px;
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
}

.scenario-status--editing {
    background: #dbeafe;
    color: #1e40af;
}

/* «Модерация» (2026-09-24) — прежний «Тестирование», тот же жёлтый. */
.scenario-status--moderation,
.scenario-status--testing {
    background: #fef3c7;
    color: #92400e;
}

.scenario-status--published {
    background: #d1fae5;
    color: #065f46;
}

/* Список сценариев сеткой (2026-09-24, по запросу автора): каждый
   параметр в своём столбце — тип с жанром, название, автор, игроки,
   сложность, время партии, статус, дата. Шапка повторяет колонки строк;
   у списка автора справа место под кнопку удаления (36px + отступ),
   у администратора в столбце статуса — выбор статуса и «Бесплатно». */
.scenarios-grid-list {
    --sg-cols: 165px minmax(150px, 1fr) 130px 60px 76px 80px 130px 104px;
}
.scenarios-grid-list--admin {
    --sg-cols: 165px minmax(150px, 1fr) 130px 60px 76px 80px 150px 104px;
}
.sg-head {
    display: grid;
    grid-template-columns: var(--sg-cols);
    gap: 10px;
    padding: 0 16px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: var(--text-dim);
}
.scenarios-grid-list:not(.scenarios-grid-list--admin) .sg-head { padding-right: 64px; }
.sg-row { flex-wrap: nowrap; }
.sg-link {
    display: grid;
    grid-template-columns: var(--sg-cols);
    gap: 10px;
    align-items: center;
    min-width: 0;
}
.sg-title { min-width: 0; overflow-wrap: anywhere; }
.sg-type { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.sg-kind {
    display: inline-block; font-size: 10px; font-weight: 700; padding: 1px 6px; border-radius: 5px;
    background: #e5e7eb; color: #374151; white-space: nowrap;
}
.sg-kind--qa { background: #ede9fe; color: #6d28d9; }
.sg-genre { font-size: 12px; color: var(--text-muted); white-space: nowrap; }
.sg-author { font-size: 13px; color: var(--text-muted); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sg-author-link { cursor: pointer; text-decoration: underline dotted; text-underline-offset: 3px; }
.sg-author-link:hover { color: var(--accent, #2da44e); }
.sg-center { justify-content: center; text-align: center; }
.sg-status { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* У администратора: выбор статуса, под ним «Бесплатно» — в одну колонку. */
.sg-status--admin { flex-direction: column; align-items: flex-start; gap: 4px; }
.sg-status--admin .admin-status-select { width: 100%; }
.sg-delete-gap { width: 36px; height: 36px; flex-shrink: 0; }
@media (max-width: 960px) {
    .sg-head { display: none; }
    .sg-row { flex-wrap: wrap; }
    .sg-link { display: flex; flex-wrap: wrap; gap: 8px 14px; }
    .sg-title { flex: 1 1 100%; }
}

/* Группы кнопок создания в «Моих сценариях» (2026-09-24): «Ролевой
   квест» — ручной режим и генерация, «Детектив» — генерация. */
.create-groups { display: flex; gap: 14px; flex-wrap: wrap; margin-bottom: 18px; }
.create-group {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    padding: 10px 14px; border: 1px solid #e5e7eb; border-radius: 12px; background: #fafbfc;
}
.create-group-title { font-size: 13px; font-weight: 700; color: #656d76; margin-right: 4px; }

/* Сыгранные игры в игротеке (2026-09-24): раскрываемый список, колонки
   сеткой — кнопка «Награда» больше не сдвигает соседние строки. */
.played-block { margin-top: 6px; }
.played-summary { cursor: pointer; font-size: 14px; font-weight: 700; margin-bottom: 8px; list-style: none; display: flex; align-items: center; gap: 8px; }
.played-summary::-webkit-details-marker { display: none; }
.played-summary::before { content: '▸'; display: inline-block; font-size: 13px; color: #656d76; transition: transform .15s; }
.played-block[open] > .played-summary::before { transform: rotate(90deg); }
.played-count { font-size: 12px; font-weight: 600; color: #6d28d9; background: #ede9fe; padding: 1px 8px; border-radius: 10px; }
/* Колонки: название | дата | исход | «Лучший игрок» | «Награда». Счёт
   «Вопросов верно: N/M» снят 2026-10-16 по решению автора. */
.played-grid {
    display: grid;
    grid-template-columns: minmax(180px, 1fr) 130px 110px 120px 96px;
    align-items: center; gap: 12px;
}
.played-date { font-size: 13px; color: #656d76; }
.played-result { font-size: 13px; font-weight: 700; white-space: nowrap; }
.played-tag {
    display: inline-block; font-size: 12px; font-weight: 700; padding: 5px 12px; border-radius: 6px;
    white-space: nowrap; text-decoration: none;
}
.played-tag--mvp { background: #fff7ed; color: #b45309; border: 1.5px solid #f59e0b; }
.played-tag--award { background: #6d28d9; color: #fff; }
@media (max-width: 760px) {
    .played-grid { grid-template-columns: 1fr 1fr; }
    .played-title { grid-column: 1 / -1; }
}

/* Скобки со счётчиком на плашках администратора (2026-09-24). */
.tab-badge { color: #b91c1c; font-weight: 700; }

/* Вкладка «Промпты» администратора (2026-09-24): режим → стадия → текст. */
.prompt-modes { display: flex; gap: 6px; margin-bottom: 14px; }
.prompt-stage-tabs { display: flex; gap: 4px; margin-bottom: 14px; flex-wrap: wrap; }
.prompt-stage-tab {
    padding: 5px 13px; border: 1.5px solid #d1d5db; border-radius: 6px; background: #fff;
    font-size: 12px; font-weight: 600; color: #6b7280; cursor: pointer;
}
.prompt-stage-tab:hover { border-color: #a78bfa; color: #7c3aed; }
.prompt-stage-tab--active { border-color: #7c3aed; background: #f5f3ff; color: #7c3aed; }
.prompt-stage-panel { display: none; flex-direction: column; gap: 6px; }
.prompt-stage-panel--active { display: flex; }
.prompt-textarea {
    width: 100%; box-sizing: border-box; min-height: 80px; font-family: monospace; font-size: 12px;
    line-height: 1.5; border: 1.5px solid #d8b4fe; border-radius: 6px; padding: 10px; resize: vertical;
    color: #374151; background: #fff;
}
.prompt-textarea--appendix { border-color: #c4b5fd; }
.prompt-temp-row { display: flex; align-items: center; gap: 8px; font-size: 12px; color: #6b7280; }
.prompt-temp { width: 80px; border: 1.5px solid #d1d5db; border-radius: 6px; padding: 4px 8px; font-size: 13px; }

.admin-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin-bottom: 18px;
}

.admin-tab-btn {
    border: 0;
    border-radius: 8px;
    background: var(--surface-2);
    color: var(--text);
    padding: 8px 16px;
    cursor: pointer;
    font-size: 14px;
    font-weight: 600;
}

.admin-tab-btn:hover {
    background: var(--surface-3);
}

.admin-tab-btn.active {
    background: var(--surface);
}

.admin-status-select {
    padding: 4px 8px;
    border: 1px solid var(--border-strong);
    border-radius: 6px;
    font-size: 12px;
    background: #fff;
    cursor: pointer;
    min-width: 140px;
}

    /* Шапка (раскладка 2026-10-23, по запросу автора): ключ без вордмарка,
       вплотную к нему пункты сайта (.public-nav--main), справа имя и «Выйти»
       (.public-nav--user, margin-left: auto). На телефоне — два ряда, см.
       медиазапрос ниже. */
    .public-header {
        display: flex;
        align-items: center;
        justify-content: flex-start;
        gap: 12px 14px;
        flex-wrap: wrap;
        padding: 16px 24px;
        background: var(--surface-2);
        border-radius: 14px;
        margin-bottom: 24px;
        position: sticky;
        top: 12px;
        z-index: 100;
        transition: background .25s ease, box-shadow .25s ease;
    }
    .public-header-left {
        display: flex;
        align-items: center;
        gap: 12px;
    }
    .public-logo {
        display: flex;
        align-items: center;
        gap: 8px;
        font-size: 28px;
        font-weight: 700;
        color: var(--text);
        text-decoration: none;
        transition: color .25s ease;
    }
    .public-logo-icon {
        flex-shrink: 0;
        width: 28px;
        height: 28px;
    }
    /* Вордмарк в шапке скрыт на любой ширине (2026-10-23, по запросу
       автора: «пункты меню прижать к ключику, а не к Квестотеке») — в шапке
       только ключ-ссылка на главную, пункты сайта сразу за ним. В подвале
       тот же партиал логотипа остаётся с текстом. До того текст прятался
       только на телефоне (≤480px). */
    .public-header .public-logo-text {
        display: none;
    }
    @media (max-width: 480px) {
        .public-header {
            padding: 12px 16px;
        }
    }
    .public-header--on-hero {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        width: 100%;
        margin-bottom: 0;
        border-radius: 0;
        padding: 20px 32px;
        background: transparent;
        box-shadow: none;
    }
    .public-header--on-hero.is-scrolled {
        background: rgba(13, 18, 24, 0.88);
        backdrop-filter: blur(10px);
        -webkit-backdrop-filter: blur(10px);
        border-bottom: 1px solid var(--border);
        box-shadow: 0 2px 18px rgba(0, 0, 0, 0.45);
    }
    .public-header--on-hero .public-logo,
    .public-header--on-hero .public-nav a,
    .public-header--on-hero .public-nav .logout-btn {
        color: #fff;
        text-shadow: 0 1px 6px rgba(0, 0, 0, 0.5);
    }
    .public-header--on-hero:not(.is-scrolled) .public-logo-text {
        opacity: 0;
        transform: translateX(-6px);
        pointer-events: none;
    }
    .public-logo-text {
        transition: opacity .25s ease, transform .25s ease;
    }
    .public-header--on-hero.is-scrolled .public-logo,
    .public-header--on-hero.is-scrolled .public-nav a,
    .public-header--on-hero.is-scrolled .public-nav .logout-btn {
        text-shadow: none;
    }
    @media (max-width: 480px) {
        .public-header--on-hero {
            padding: 14px 16px;
        }
    }
    body.landing-page .app-shell {
        padding-top: 0;
    }
    .public-nav {
        display: flex;
        gap: 16px;
        align-items: center;
        min-width: 0;
    }
    .public-nav--user {
        margin-left: auto;
    }
    /* «КВЕСТОТЕКА» по центру шапки на мониторе (2026-10-23, по запросу
       автора): абсолютно, от середины, чтобы не зависеть от ширины левой
       и правой групп; на экранах у́же 760px скрыто — там наезжало бы на
       пункты. Вместо вордмарка логотипа, который стоял между ключом и
       пунктами. */
    .public-header-brand {
        display: none;
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
        font-size: 24px;
        font-weight: 700;
        letter-spacing: .02em;
        color: var(--text);
        text-decoration: none;
        white-space: nowrap;
        transition: color .25s ease, opacity .25s ease;
    }
    @media (min-width: 760px) {
        .public-header-brand {
            display: block;
        }
    }
    /* Узкие контейнеры — лобби и экран партии (600px): слово по центру
       наезжало бы на пункты (2026-10-23, по замечанию автора) — его нет. */
    .public-header--narrow .public-header-brand {
        display: none;
    }
    /* Шапка на экране партии (2026-10-23): лежит в общей закреплённой зоне
       .qa-top вместе с таймером, сама не липнет. */
    .public-header--game {
        position: static;
        margin-bottom: 8px;
        padding: 10px 14px;
    }
    .qa-top {
        position: sticky;
        top: 0;
        z-index: 20;
        background: #e6e8eb;
        margin: 0 -16px;
        padding: 0 16px;
    }
    .public-header--on-hero .public-header-brand {
        color: #fff;
        text-shadow: 0 1px 6px rgba(0, 0, 0, 0.5);
    }
    .public-header--on-hero:not(.is-scrolled) .public-header-brand {
        opacity: 0;
        pointer-events: none;
    }
    .public-header--on-hero.is-scrolled .public-header-brand {
        text-shadow: none;
    }
    /* Имя вошедшего вместо «Кабинет» (2026-10-23): длинное — с
       многоточием, чтобы не ломать ряд. */
    .public-nav-name {
        max-width: 180px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    /* Кнопка ☰ (2026-10-23): видна только когда шапка на телефоне свёрнута
       (.is-burger), см. скрипт в public_header.html. */
    .public-burger {
        display: none;
        margin-left: auto;
        width: 36px;
        height: 32px;
        padding: 6px 8px;
        border: 1px solid var(--border);
        border-radius: 8px;
        background: transparent;
        cursor: pointer;
        flex-direction: column;
        justify-content: space-between;
        flex-shrink: 0;
    }
    .public-burger span {
        display: block;
        height: 2px;
        border-radius: 1px;
        background: var(--text);
    }
    .public-header--on-hero .public-burger span { background: #fff; }
    .public-header--on-hero .public-burger { border-color: rgba(255, 255, 255, 0.5); }
    @media (max-width: 560px) {
        /* Одна строка (2026-10-23, по запросу автора: «собери обе части меню
           в одну строку; не помещается — сокращать имя; сокращено до 10
           символов и всё равно не влезает — ЛК; и после этого не влезает —
           три полоски»). Пункты сайта не сжимаются (flex-shrink: 0), имя
           ужимается многоточием до 10ch; дальше решает скрипт по
           переполнению. Утром 2026-10-23 здесь было два ряда. */
        /* Уплотнение (2026-10-23, по решению автора «делай без спорных
           мер»): поля шапки 16 → 10 и выход за поля страницы на 10px с
           каждой стороны, промежутки 12/10/8 → 8/8/6, кегль 13 → 12, ключ
           28 → 24. На ширине 390 имени достаётся ~100px (≈15 знаков)
           вместо 44 — большинство имён помещаются в строку целиком. */
        .public-header {
            flex-wrap: nowrap;
            gap: 8px;
            padding: 10px;
            margin: 0 -10px 24px;
        }
        .public-header--on-hero {
            margin: 0;
            padding: 10px;
        }
        .public-header .public-logo-icon {
            width: 24px;
            height: 24px;
        }
        .public-nav--main {
            gap: 8px;
            flex-shrink: 0;
        }
        .public-nav--user {
            gap: 6px;
            flex: 0 1 auto;
            min-width: 0;
        }
        .public-nav--user .logout-form,
        .public-nav--user .logout-btn { flex-shrink: 0; }
        /* С родителем .public-header — базовое правило «.public-nav a { 14px }»
           стоит в файле ниже этого блока и при равной специфичности
           перебивало мобильный кегль (так прежние 13px на телефоне никогда
           не действовали; найдено 2026-10-23 диагностикой ширин). */
        .public-header .public-nav a,
        .public-header .public-nav .logout-btn {
            font-size: 12px;
        }
        .public-nav-name {
            flex: 0 1 auto;
            max-width: none;
            min-width: 10ch;
        }
        /* «ЛК» не сжимается: иначе ужалось бы до нуля вместо переполнения, и ☰ не включился бы. */
        .public-header.is-lk .public-nav-name { flex-shrink: 0; min-width: 0; }
        .public-burger[hidden] { display: none; }
        .public-header.is-burger .public-burger { display: flex; }
        .public-header.is-burger .public-nav--main,
        .public-header.is-burger .public-nav--user { display: none; }
        /* Раскрытое меню: шапка снова переносит ряды, обе группы столбиком
           под первой строкой. */
        .public-header.is-burger.is-open { flex-wrap: wrap; }
        .public-header.is-burger.is-open .public-nav--main,
        .public-header.is-burger.is-open .public-nav--user {
            display: flex;
            flex-direction: column;
            align-items: flex-start;
            flex: 1 1 100%;
            min-width: 100%;
            gap: 10px;
            margin: 0;
            padding-top: 10px;
            border-top: 1px solid var(--border);
        }
        .public-header.is-burger.is-open .public-nav--main { order: 4; }
        .public-header.is-burger.is-open .public-nav--user { order: 5; }
        .public-header.is-burger.is-open .public-nav a,
        .public-header.is-burger.is-open .public-nav .logout-btn { font-size: 15px; }
        .public-header.is-burger.is-open .public-nav-name { max-width: 100%; }
    }
    /* «Выйти» — форма POST (2026-09-24, аудит), кнопка выглядит ссылкой.
       Форма — flex с центрированием (2026-10-23, по замечанию автора «имя
       и Выйти немного на разном уровне»): строчная форма ставила кнопку по
       базовой линии своей строки, и «Выйти» сидело на пару пикселей ниже
       имени. */
    .logout-form { display: flex; align-items: center; margin: 0; }
    .logout-btn {
        background: none; border: 0; padding: 0; margin: 0;
        font: inherit; line-height: inherit; cursor: pointer; text-shadow: inherit;
    }
    .public-nav a,
    .public-nav .logout-btn {
        color: var(--link);
        text-decoration: none;
        font-weight: 600;
        font-size: 14px;
    }
    .public-nav a:hover,
    .public-nav .logout-btn:hover {
        text-decoration: underline;
    }
    body.landing-page {
        --hero-key-w: 128px;
        --hero-key-gap: 24px;
        --hero-indent: calc(var(--hero-key-w) + var(--hero-key-gap));
        --hero-col: 560px;
    }
    /* Контент лендинга живёт в той же колонке, что и первый экран: левая
       граница — левый край ключа, правая — правый край заголовка. +40px —
       собственный горизонтальный padding .app-shell (20px с каждой стороны),
       без него колонка секций оказалась бы на 40px у́же строки заголовка. */
    body.landing-page .app-shell {
        max-width: calc(var(--hero-indent) + var(--hero-col) + 40px);
    }
    /* ── Первый экран (2026-10-15, по макету и замечаниям автора) ──────
       Фон — фотография автора как есть (make_hero_image --file, холст
       1376×768), во весь экран: cover по центру. Автор оставил в кадре
       запас фона со всех сторон, так что на мониторе обрезаются стены и
       пол, а на телефоне (кадр 9:19) — бока комнаты, в середине остаётся
       стоящий в центре с соседями. Прежние варианты того же дня —
       достроенный холст и фон по ширине окна с формулами положения —
       сняты по решению автора («без доработок во весь экран»).
       Под картинкой — цвет страницы (background-color), на случай, если
       она ещё не загрузилась. */
    body.landing-page {
        /* Кегль имени сайта — от него считается и кегль слогана
           (0.43), чтобы слоган был не шире имени на любом экране. */
        --hero-name: clamp(40px, 7vw, 84px);
    }
    .hero {
        text-align: center;
        /* Имя сайта — на самом верху, ЗА прозрачной шапкой (2026-10-15, по
           запросу автора): шапка фиксированная и на первом экране
           прозрачная, её логотип слева и ссылки справа, имя по центру —
           на мониторе шире 1000px они не встречаются. На окне у́же
           ссылки «Каталог · Войти · Регистрация» легли бы на буквы, и там
           имя опускается под шапку (медиазапросы ниже). Низ — небольшой:
           кнопки стоят на крае стола, как в макете. */
        padding: 12px 20px clamp(24px, 6vh, 64px);
        width: 100vw;
        margin-left: calc(50% - 50vw);
        margin-right: calc(50% - 50vw);
        margin-bottom: 32px;
        background-color: #0d1218;
        /* WebP с откатом на JPEG (2026-09-07): первый экран на мобильном
           интернете грузится именно этой картинкой. Браузер без
           image-set() возьмёт первое объявление и скачает JPEG. */
        background-image: url("../img/hero-bg.jpg");
        background-image: image-set(url("../img/hero-bg.webp") type("image/webp"), url("../img/hero-bg.jpg") type("image/jpeg"));
        background-repeat: no-repeat;
        background-size: cover;
        background-position: center;
        border-radius: 0;
        /* Высота первого экрана — svh, самая малая из возможных: она НЕ
           меняется, когда браузер телефона прячет или показывает свою
           нижнюю панель (2026-10-12, по запросу автора). У dvh высота
           следует за панелью, и весь экран дёргался при каждом её
           появлении. Первая строка — откат для браузеров без svh. */
        min-height: 100vh;
        min-height: 100svh;
        box-sizing: border-box;
        position: relative;
        display: flex;
        flex-direction: column;
        align-items: center;
        color: #fff;
    }
    /* Затемнение низа фотографии (2026-10-16, по запросу автора: «от края
       стола до низа картинки»): кромка стола на снимке — примерно на двух
       третях высоты, ниже — ноги и ковёр, на них стоят плашки и кнопки.
       Градиент от прозрачного на уровне стола к почти чёрному внизу, тем же
       цветом, что фон .hero, — переход в тёмную страницу без ступеньки.
       Лежит под содержимым (.hero-stack выше по z-index); боковые полосы
       (.hero::before ниже) рисуются под ним и складываются с ним в углах. */
    .hero::after {
        content: "";
        position: absolute;
        left: 0;
        right: 0;
        bottom: 0;
        height: 38%;
        pointer-events: none;
        background: linear-gradient(to bottom, rgba(13, 18, 24, 0) 0%, rgba(13, 18, 24, 0.6) 45%, rgba(13, 18, 24, 0.94) 100%);
    }
    /* Затемнение боков (2026-10-16, по запросу автора: «от людей влево и
       вправо»): две полосы от левого и правого края экрана к группе за
       столом, от rgba(13,18,24,.85) у края до прозрачного у людей. Ширина
       полосы — не на глаз, а из геометрии cover-фона: снимок 1376×768
       (1.79:1), люди со стульями занимают в нём от 16.7 % до 88.3 % ширины.
       При cover по центру ширина картинки на экране W = max(100vw,
       179svh) (по ширине окна на широком, по высоте — на узком), и видимый
       пустой бок слева = 0.167·W − (W − 100vw)/2 = 50vw − 0.333·W, справа
       0.117·W − (W − 100vw)/2 = 50vw − 0.383·W. Полоса в 1.2 раза шире
       пустого бока, чтобы прозрачный хвост градиента ложился на стул, а не
       обрывался у него: слева 60vw − 0.4·W, справа 60vw − 0.46·W. На
       мониторе 16:9 это ≈20vw и ≈14vw, на 16:10 ≈15vw и ≈9vw, на телефоне
       (кадр 9:19, бока снимка за экраном, люди во всю ширину) выражение
       отрицательно — полосы нет, лица не затемняются. Первая пара
       background-size — откат в vh для браузеров без svh; через custom
       property откат не сделать: невалидное значение при подстановке
       обнулило бы размер и полоса растянулась бы на весь экран. */
    .hero::before {
        content: "";
        position: absolute;
        inset: 0;
        pointer-events: none;
        background-image:
            linear-gradient(to right, rgba(13, 18, 24, 0.85) 0%, rgba(13, 18, 24, 0.4) 45%, rgba(13, 18, 24, 0) 100%),
            linear-gradient(to left, rgba(13, 18, 24, 0.85) 0%, rgba(13, 18, 24, 0.4) 45%, rgba(13, 18, 24, 0) 100%);
        background-repeat: no-repeat;
        background-position: left top, right top;
        background-size: max(0px, 60vw - 0.4 * max(100vw, 179vh)) 100%, max(0px, 60vw - 0.46 * max(100vw, 179vh)) 100%;
        background-size: max(0px, 60vw - 0.4 * max(100vw, 179svh)) 100%, max(0px, 60vw - 0.46 * max(100vw, 179svh)) 100%;
    }
    /* Одна колонка на весь экран шириной по содержимому: самое широкое —
       имя сайта одной строкой, и плашка с кнопками (width 100%)
       растягиваются ровно на его ширину — «по ширине равны названию».
       Магических чисел нет: ширину задаёт сам текст. */
    .hero-stack {
        display: flex;
        flex-direction: column;
        flex: 1 1 auto;
        width: fit-content;
        max-width: 100%;
        margin: 0 auto;
        position: relative;
        z-index: 1;
    }
    /* Имя и слоган — без подложки (2026-10-15, по решению автора: «оставь
       без фона»; полупрозрачная фиолетовая полоса во всю ширину окна
       стояла тут несколько часов). Читаемость на фото держит тень
       текста. */
    .hero-lockup {
        position: relative;
    }
    /* Имя сайта — крупно, одной строкой, плотно (разрядка 0.04em; прежние
       0.12em автор счёл разреженными). Отрицательный правый отступ
       убирает хвост разрядки после последней буквы, иначе строка
       визуально уезжает влево. */
    .hero-wordmark-text {
        display: block;
        font-size: var(--hero-name);
        font-weight: 800;
        line-height: 1;
        letter-spacing: 0.04em;
        margin: 0 -0.04em 0 0;
        white-space: nowrap;
        color: #fff;
        text-shadow: 0 2px 18px rgba(0, 0, 0, 0.6), 0 1px 3px rgba(0, 0, 0, 0.35);
    }
    /* Слоган — строчными (по макету), одной строкой под именем, кегль
       0.40 имени: при 0.43 строка «детективные блицы для компании»
       выходила на 40px шире имени и колонку задавала она; при 0.40 слоган
       чуть у́же имени, и ширину плашек с кнопками задаёт имя. */
    .hero h1 {
        font-size: calc(var(--hero-name) * 0.40);
        line-height: 1.12;
        max-width: none;
        white-space: nowrap;
        /* Вплотную к имени (2026-10-15, по запросу автора «подними выше к
           КВЕСТОТЕКА»): у капса нет выносных элементов, и при line-height
           1 под буквами остаётся пустая полоса — отрицательный отступ в
           долях кегля имени её съедает. */
        margin: calc(var(--hero-name) * -0.06) 0 0;
        text-shadow: 0 2px 12px rgba(0, 0, 0, 0.4);
    }
    /* Нижняя группа (абзац о формате + кнопки) — общая зона с
       бледно-розовой подложкой (2026-10-22, по запросу автора: «бледно
       розовый фон, как акцентные цвета на фоновом фото, общей зоной с
       кнопками» — на снимке розово-сиреневая подсветка лампы и стены).
       Почти непрозрачная, чтобы цвет читался розовым, а не серым поверх
       затемнённого низа фото; текст внутри тёмный. К низу экрана зону
       выталкивает auto-margin — устойчиво к любой высоте экрана;
       середина кадра остаётся лицам. */
    .hero-bottom {
        position: relative;
        margin-top: auto;
        width: 100%;
        box-sizing: border-box;
        padding: clamp(16px, 2.6vh, 24px) clamp(16px, 1.8vw, 24px);
        border-radius: 18px;
        background: rgba(243, 216, 230, 0.92);
        box-shadow: 0 8px 30px rgba(0, 0, 0, 0.25);
    }
    /* Гирлянда по кромке зоны (2026-10-22, по запросу автора: «рамку по
       периметру обрами плавно мигающей тёплой одноцветной гирляндой»):
       два псевдоэлемента чуть шире зоны (inset −9px) с прозрачной рамкой
       18px и border-image из SVG-плитки 72×72, нарезанной по 18: углы —
       пустые клетки, рёбра — плитка с одним огоньком (янтарное ядро и
       свечение радиальным градиентом, цвет как у гирлянды на фото).
       border-image-repeat: round укладывает на каждое ребро целое число
       плиток, чуть подгоняя шаг, — огоньки не режутся на концах рёбер и
       не накладываются в углах (первый вариант того же дня рисовал
       полосы background-ом с repeat-x/repeat-y на всю ширину и высоту:
       в прямых углах полосы верха и боков накладывались, а крайние
       плитки обрезались — замечание автора). Центры огоньков — ровно на
       кромке зоны (середина рамки). У ::before огонёк в первой половине
       плитки ребра, у ::after — во второй: ряды чередуются, а
       противофазная пульсация прозрачности даёт плавное мерцание
       «через один», без JS. На скруглённых углах огоньков нет — пустая
       клетка. При prefers-reduced-motion гирлянда горит ровно. */
    .hero-bottom::before,
    .hero-bottom::after {
        content: '';
        position: absolute;
        inset: -9px;
        pointer-events: none;
        border: 18px solid transparent;
        border-image-slice: 18;
        border-image-width: 18px;
        border-image-repeat: round;
        animation: hero-garland 2.6s ease-in-out infinite;
    }
    .hero-bottom::before {
        border-image-source: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='72' height='72'%3E%3Cdefs%3E%3CradialGradient id='g'%3E%3Cstop offset='0' stop-color='%23ffd27a'/%3E%3Cstop offset='.34' stop-color='%23ffd27a'/%3E%3Cstop offset='.5' stop-color='%23ffc86e' stop-opacity='.55'/%3E%3Cstop offset='1' stop-color='%23ffbe5a' stop-opacity='0'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='27' cy='9' r='9' fill='url(%23g)'/%3E%3Ccircle cx='63' cy='27' r='9' fill='url(%23g)'/%3E%3Ccircle cx='27' cy='63' r='9' fill='url(%23g)'/%3E%3Ccircle cx='9' cy='27' r='9' fill='url(%23g)'/%3E%3C/svg%3E");
    }
    .hero-bottom::after {
        border-image-source: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='72' height='72'%3E%3Cdefs%3E%3CradialGradient id='g'%3E%3Cstop offset='0' stop-color='%23ffd27a'/%3E%3Cstop offset='.34' stop-color='%23ffd27a'/%3E%3Cstop offset='.5' stop-color='%23ffc86e' stop-opacity='.55'/%3E%3Cstop offset='1' stop-color='%23ffbe5a' stop-opacity='0'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='45' cy='9' r='9' fill='url(%23g)'/%3E%3Ccircle cx='63' cy='45' r='9' fill='url(%23g)'/%3E%3Ccircle cx='45' cy='63' r='9' fill='url(%23g)'/%3E%3Ccircle cx='9' cy='45' r='9' fill='url(%23g)'/%3E%3C/svg%3E");
        animation-delay: -1.3s;
    }
    @keyframes hero-garland {
        0%, 100% { opacity: 0.35; }
        50% { opacity: 1; }
    }
    @media (prefers-reduced-motion: reduce) {
        .hero-bottom::before,
        .hero-bottom::after {
            animation: none;
            opacity: 0.85;
        }
    }
    /* Абзац о формате (2026-10-22, по запросам автора; с 2026-10-15 по
       2026-10-22 — три фиолетовые плашки столбиком, с 2026-09-18 — пилюли
       в ряд): две строки крупно жирным (прямым — курсив снят по запросу
       автора), тёмным по бледно-розовой зоне, по центру, как имя и
       слоган. Кегль подобран так, чтобы первая строка «3–7 человек с
       телефонами, 20 мин.» входила в ширину имени целиком (≈18.5em при
       жирном начертании: 26px × 18.5 ≈ 480px при колонке ≈ 540px без
       отступов зоны). Ширину колонки абзац не задаёт: width: 0 с
       min-width: 100% не даёт вклада в ширину колонки (проценты при
       расчёте ширины по содержимому считаются нулём), а в раскладке
       абзац занимает всю ширину — иначе при width: 100% колонка
       (fit-content) растягивалась бы по длинной строке, а имя уезжало в
       центр. */
    .hero-format {
        width: 0;
        min-width: 100%;
        box-sizing: border-box;
        margin: 0;
        font-size: clamp(18px, 2vw, 26px);
        font-weight: 800;
        line-height: 1.3;
        text-align: center;
        color: #2a1a33;
        /* Капсом (2026-10-22, по запросу автора «буквы надписи и кнопок
           сделай заглавными»); в разметке текст строчными — капс только
           на виде. Капс на ~20 % шире строчных: первая строка ≈ 17.8em
           ≈ 463px при 26px, в зону (≈540px) входит. */
        text-transform: uppercase;
        letter-spacing: 0.02em;
    }
    /* Кнопки под строкой формата в ряд по центру, одной умеренной
       ширины (2026-10-22, по запросу автора «нормализуй по ширине без
       растягивания»; с 2026-10-15 делили ширину колонки пополам по
       макету): min-width даёт обеим одну ширину — текст короче, —
       и ни одна не тянется на половину колонки. На телефоне — столбиком
       во всю ширину (ниже). */
    .hero .hero-actions {
        display: flex;
        flex-wrap: nowrap;
        gap: 14px;
        width: 100%;
        /* Отступ от абзаца — по запросу автора «отступи от кнопок»
           (2026-10-22; прежде clamp(16px, 3vh, 28px)). */
        margin: clamp(22px, 4vh, 36px) 0 0;
        justify-content: center;
    }
    .hero .hero-actions .hero-btn {
        flex: 0 0 auto;
        min-width: 220px;
        box-sizing: border-box;
        text-align: center;
        box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
        /* Капсом (2026-10-22, по запросу автора) — только кнопки первого
           экрана; та же пара внизу страницы остаётся строчными. */
        text-transform: uppercase;
        letter-spacing: 0.03em;
    }
    /* «Каталог» — нейтральная (2026-10-15, по запросу автора): тёмно-серая
       полупрозрачная со светлой рамкой, белый текст — не спорит с зелёной
       «Начать игру» и читается и на светлом фото, и на тёмном фоне
       «Начнём играть?» (тот же класс у второй пары кнопок). Прежние
       варианты: прозрачная с белой рамкой терялась на фото, фиолетовая
       из макета стояла несколько часов. Селектор усилен: правило
       .hero-btn объявлено ниже и при равной специфичности перебивало бы
       цвет. */
    .hero-btn.hero-btn--neutral {
        background: rgba(31, 41, 55, 0.72);
        border: 1px solid rgba(255, 255, 255, 0.4);
        color: #fff;
        backdrop-filter: blur(6px);
        -webkit-backdrop-filter: blur(6px);
    }
    .hero-btn.hero-btn--neutral:hover {
        background: rgba(55, 65, 81, 0.85);
    }
    /* Низкое окно (ноутбук 1366×768, телефон 360×640): нижняя группа
       компактнее — плашки и кнопки ниже, зазоры меньше, — иначе на
       экране такой высоты плашка формата ложится на подбородок
       сидящего слева. Лица на фото при фоне по ширине окна занимают
       середину, и единственный запас высоты — в самой группе. */
    @media (max-height: 820px) {
        .hero-format { font-size: clamp(17px, 1.8vw, 22px); }
        .hero .hero-actions { margin-top: 18px; }
        .hero .hero-actions .hero-btn { padding-top: 10px; padding-bottom: 10px; }
        .hero-bottom { padding-top: 14px; padding-bottom: 14px; }
    }
    /* У́же 1000px имя сайта (51vw при 7vw кегля) по горизонтали доходит до
       ссылок шапки — опускаем его под шапку (её высота ~60px). */
    @media (max-width: 1000px) {
        .hero {
            padding-top: 64px;
        }
    }
    @media (max-width: 860px) {
        body.landing-page {
            --hero-key-w: 96px;
            --hero-key-gap: 18px;
            --hero-col: 440px;
        }
    }
    @media (max-width: 480px) {
        .hero {
            /* 60px — высота шапки на телефоне (14px + логотип + 14px):
               имя сразу под строкой меню. */
            padding: 60px 16px clamp(20px, 5vh, 48px);
        }
        body.landing-page {
            --hero-key-w: 84px;
            --hero-indent: 0px;
            --hero-col: 100%;
            /* На телефоне колонка во всю ширину окна, и 7vw дают мелко —
               имя считается от ширины экрана с запасом побольше: 12vw ×
               7.3 em ≈ 88vw, в колонку с отступами 16px входит. */
            --hero-name: clamp(30px, 12vw, 50px);
        }
        /* Имя, плашка и кнопки — по ширине телефона с небольшим отступом
           от края (по замечанию автора): колонка во всю ширину. */
        .hero-stack {
            width: 100%;
        }
        /* Абзац о формате на телефоне — от ширины экрана, без разрядки,
           и зона у́же по бокам: первая строка капсом ≈ 20.3em (по снимку
           390px: при 17px переносилось «МИН.»), при 4vw — 15.6px на
           390px → ≈317px в 334px доступных, 14.4px на 360px → ≈292px в
           304px. Мелковато для «крупно», но строка целая — по запросу
           автора «на следующей строчке». */
        .hero-bottom {
            padding-left: 12px;
            padding-right: 12px;
        }
        .hero-format {
            font-size: clamp(13px, 4vw, 20px);
            letter-spacing: 0;
        }
        /* Кнопки на телефоне — каждая на своей строке, во всю ширину
           колонки (2026-10-22, по запросу автора: «на мобильной пусть
           остаются выровнены и каждая на своей строке»). */
        .hero .hero-actions {
            flex-direction: column;
            gap: 10px;
        }
        .hero .hero-actions .hero-btn {
            width: 100%;
            min-width: 0;
        }
    }
    .hero-btn {
        display: inline-block;
        padding: 12px 28px;
        background: var(--accent);
        color: #fff;
        border-radius: 8px;
        text-decoration: none;
        font-weight: 600;
        font-size: 16px;
    }
    .hero-btn:hover {
        background: var(--accent-hover);
    }
    .cards-grid {
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
        gap: 20px;
    }
    /* Карточка — колонка (2026-10-12, по замечанию автора: «у карточек
       боевых сценариев и обучающих немного разная высота, у обучающих
       кнопка Играть чуть выше»). Карточки в ряду тянутся до высоты
       самой длинной, а содержимое — нет, и кнопка вставала там, где
       кончался текст: у обучающих анонс короче, и она поднималась.
       Теперь нижнюю строку с кнопкой прижимает к низу auto-margin у
       .card-meta, и у всех карточек ряда они на одной линии. */
    .card {
        background: var(--surface);
        border: 1px solid transparent;
        border-radius: 12px;
        overflow: hidden;
        text-decoration: none;
        color: var(--text);
        transition: box-shadow 0.2s;
        display: flex;
        flex-direction: column;
    }
    .card:hover {
        box-shadow: 0 4px 16px rgba(0,0,0,0.12);
    }
    /* Тонкая рамка цвета сложности (2026-10-26, по запросу автора): та же
       шкала, что у пламени; уровень в data-diff на корне карточки, без
       уровня рамка прозрачная. :hover в правиле — чтобы серая рамка
       наведения публичных страниц (body.public-page .card:hover) её не
       перебивала; у сыгранного grayscale карточки приглушает и рамку. */
    .card[data-diff="1"], .card[data-diff="1"]:hover { border-color: #4ade80; }
    .card[data-diff="2"], .card[data-diff="2"]:hover { border-color: #a3e635; }
    .card[data-diff="3"], .card[data-diff="3"]:hover { border-color: #facc15; }
    .card[data-diff="4"], .card[data-diff="4"]:hover { border-color: #fb923c; }
    .card[data-diff="5"], .card[data-diff="5"]:hover { border-color: #f87171; }
    .card[data-diff="6"], .card[data-diff="6"]:hover { border-color: #f43f5e; }
    .card-image {
        /* relative — опора для пометки «СЫГРАНО»; у лендинга такое
           правило уже было своё. flex: none — карточка стала колонкой, и
           без него обложка сжималась бы, когда текста много. */
        position: relative;
        flex: none;
        width: 100%;
        height: 180px;
        background: var(--surface-3);
        display: flex;
        align-items: center;
        justify-content: center;
        color: var(--text-dim);
        font-size: 14px;
    }
    .card-image img {
        width: 100%;
        height: 100%;
        object-fit: cover;
    }
    .card-body {
        padding: 16px;
        display: flex;
        flex-direction: column;
        flex: 1 1 auto;
    }
    .card-title {
        font-size: 18px;
        font-weight: 700;
        margin: 0 0 8px;
    }
    .card-desc {
        font-size: 14px;
        color: var(--text-muted);
        margin: 0 0 12px;
        display: -webkit-box;
        -webkit-line-clamp: 3;
        -webkit-box-orient: vertical;
        overflow: hidden;
    }
    /* Нижняя строка карточки (2026-09-22): состав, сложность, оценка —
       и в правом углу цена партии или «Бесплатно» (.card-price, прежде
       мелкая плашка в углу картинки). */
    .card-meta {
        display: flex;
        align-items: center;
        flex-wrap: wrap;
        /* Прижата к низу карточки вместе с кнопкой под ней: см. .card. */
        margin-top: auto;
        gap: 10px 14px;
        font-size: 13px;
        color: var(--text-dim);
    }
    .card-meta-item {
        display: inline-flex;
        align-items: center;
        gap: 4px;
    }
    .card-price {
        margin-left: auto;
        display: inline-flex;
        align-items: center;
        padding: 6px 14px;
        border-radius: 999px;
        font-size: 15px;
        font-weight: 800;
        line-height: 1;
        letter-spacing: 0.01em;
        color: var(--text);
        background: var(--surface-3);
        white-space: nowrap;
    }
    .card-price--free {
        color: #fff;
        background: #16a34a;
    }
    /* Сложность — пламя, которое с уровнем растёт и «раскаляется»: от
       зелёного на 1 до пурпурно-красного на 6; число рядом того же цвета.
       Уровень 0 — сложность не задана, серое пламя. */
    .card-diff {
        font-weight: 700;
        color: var(--text-dim);
    }
    .card-diff-icon {
        width: 15px;
        height: 15px;
        fill: currentColor;
        flex: none;
    }
    .card-diff.diff-1 { color: #4ade80; }
    .card-diff.diff-2 { color: #a3e635; }
    .card-diff.diff-3 { color: #facc15; }
    .card-diff.diff-4 { color: #fb923c; }
    .card-diff.diff-5 { color: #f87171; }
    .card-diff.diff-6 { color: #f43f5e; }
    .card-diff.diff-2 .card-diff-icon { width: 16px; height: 16px; }
    .card-diff.diff-3 .card-diff-icon { width: 17px; height: 17px; }
    .card-diff.diff-4 .card-diff-icon { width: 18px; height: 18px; }
    .card-diff.diff-5 .card-diff-icon { width: 20px; height: 20px; filter: drop-shadow(0 0 3px rgba(248, 113, 113, 0.6)); }
    .card-diff.diff-6 .card-diff-icon { width: 22px; height: 22px; filter: drop-shadow(0 0 4px rgba(244, 63, 94, 0.75)); }
    .card-play-btn {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 10px;
        margin: 16px -16px -16px;
        padding: 16px 0;
        background: var(--accent);
        color: #fff;
        text-align: center;
        font-weight: 700;
        font-size: 20px;
    }
    /* Кнопка цвета пламени сложности (2026-10-26, по запросу автора): та же
       шкала, что у .card-diff; в кнопке белое пламя и цифра уровня.
       Лёгкая тень текста — чтобы белое читалось на салатовом и жёлтом. */
    .card-play-btn.diff-1 { background: #4ade80; }
    .card-play-btn.diff-2 { background: #a3e635; }
    .card-play-btn.diff-3 { background: #facc15; }
    .card-play-btn.diff-4 { background: #fb923c; }
    .card-play-btn.diff-5 { background: #f87171; }
    .card-play-btn.diff-6 { background: #f43f5e; }
    .card-play-btn[class*="diff-"] { text-shadow: 0 1px 2px rgba(0, 0, 0, 0.28); }
    .card-play-diff {
        display: inline-flex;
        align-items: center;
        gap: 3px;
        font-size: 17px;
        line-height: 1;
    }
    .card-play-flame {
        width: 22px;
        height: 22px;
        fill: #fff;
        flex: none;
        filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.28));
    }
    /* Сыгранный сценарий в каталоге (2026-09-24): карточка приглушена,
       вместо «Играть» — «Сценарий сыгран». */
    .card.card--played { opacity: 0.55; filter: grayscale(0.35); }
    .card.card--played:hover { opacity: 0.8; }
    .card.card--played .card-play-btn { background: #9aa0a6; }
    .card.new-game-card {
        background: linear-gradient(160deg, #7c3aed 0%, #4c1d95 100%);
        display: flex;
        flex-direction: column;
    }
    .card.new-game-card:hover {
        box-shadow: 0 6px 22px rgba(124, 58, 237, 0.45);
    }
    .new-game-card-visual {
        height: 180px;
        display: flex;
        align-items: center;
        justify-content: center;
    }
    .new-game-card .card-body {
        text-align: center;
    }
    .new-game-card-title {
        font-size: 18px;
        font-weight: 700;
        color: #fff;
        margin: 0 0 8px;
    }
    .new-game-card-cta {
        font-size: 13px;
        font-weight: 600;
        color: #ddd6fe;
        margin: 0;
    }
    .catalog-filters {
        display: flex;
        align-items: flex-end;
        gap: 16px;
        flex-wrap: wrap;
        background: var(--surface);
        border-radius: 12px;
        padding: 16px 20px;
        margin-bottom: 24px;
    }
    .catalog-filters .filter-group {
        display: flex;
        flex-direction: column;
        gap: 4px;
    }
    .catalog-filters label {
        font-size: 12px;
        color: var(--text-dim);
    }
    .catalog-filters select {
        padding: 8px 10px;
        border: 1px solid var(--border);
        border-radius: 8px;
        font-size: 14px;
        background: var(--surface-input);
        color: var(--text);
        min-width: 160px;
    }
    .catalog-filters button {
        padding: 9px 20px;
        background: var(--accent);
        color: #fff;
        border: none;
        border-radius: 8px;
        font-weight: 600;
        font-size: 14px;
        cursor: pointer;
    }
    .catalog-filters .filter-reset {
        font-size: 13px;
        color: var(--text-dim);
        text-decoration: none;
    }
    .filters-toggle-btn {
        display: none;
        margin-bottom: 16px;
        padding: 9px 16px;
        background: var(--surface);
        color: var(--text);
        border: 1px solid var(--border);
        border-radius: 8px;
        font-weight: 600;
        font-size: 14px;
        cursor: pointer;
    }
    @media (max-width: 480px) {
        .filters-toggle-btn {
            display: block;
        }
        .catalog-filters {
            display: none;
        }
        .catalog-filters.open {
            display: flex;
        }
    }
    .section-title {
        font-size: 34px;
        margin: 0 0 20px;
    }
    @media (max-width: 860px) {
        .section-title {
            font-size: 28px;
        }
        .section {
            margin-bottom: 36px;
        }
    }
    @media (max-width: 480px) {
        .section-title {
            font-size: 24px;
            margin-bottom: 16px;
        }
        .section {
            margin-bottom: 30px;
        }
    }

    /* Лента «Новые игры каждую неделю!» в каталоге (2026-09-22, по запросу
       автора). На десктопе стоит в строке заголовка — справа от «Каталог
       игр», в пустом месте, и не добавляет странице высоты; на телефоне
       ложится полосой под заголовком. Заметность — акцентный градиент и
       «живая» точка; текст короткий, чтобы лента не спорила с карточками. */
    .catalog-head {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 12px 20px;
        flex-wrap: wrap;
        margin: 0 0 20px;
    }
    .catalog-head .section-title {
        margin: 0;
    }
    .new-weekly {
        display: inline-flex;
        align-items: center;
        gap: 10px;
        padding: 9px 18px 9px 14px;
        border-radius: 999px;
        background: linear-gradient(100deg, var(--accent) 0%, var(--accent-hover) 100%);
        color: #fff;
        font-size: 15px;
        font-weight: 700;
        letter-spacing: 0.01em;
        white-space: nowrap;
        box-shadow: 0 6px 18px var(--accent-soft), 0 0 0 1px rgba(255, 255, 255, 0.08) inset;
    }
    .new-weekly-dot {
        position: relative;
        width: 10px;
        height: 10px;
        border-radius: 50%;
        background: #fff;
        flex: 0 0 auto;
    }
    .new-weekly-dot::after {
        content: "";
        position: absolute;
        inset: -4px;
        border-radius: 50%;
        border: 2px solid rgba(255, 255, 255, 0.85);
        animation: new-weekly-pulse 1.8s ease-out infinite;
    }
    @keyframes new-weekly-pulse {
        0% { transform: scale(0.6); opacity: 0.9; }
        100% { transform: scale(1.8); opacity: 0; }
    }
    @media (prefers-reduced-motion: reduce) {
        .new-weekly-dot::after { animation: none; display: none; }
    }
    /* Баннер пакетного предложения вверху каталога (2026-10-30, по
       запросу автора): «Пакет из 5 игр за 599 ₽». Фиолетовый градиент —
       не спорит с зелёной лентой новинок и зелёными кнопками карточек;
       кнопка белая. На телефоне текст и кнопка — столбиком. */
    .package-banner {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 14px 24px;
        flex-wrap: wrap;
        margin: 0 0 20px;
        padding: 18px 22px;
        border-radius: 16px;
        background: linear-gradient(100deg, #4f46e5 0%, #7c3aed 100%);
        color: #fff;
        box-shadow: 0 8px 24px rgba(79, 70, 229, 0.28);
    }
    .package-banner-text { flex: 1 1 320px; min-width: 0; }
    .package-banner-title { font-size: 22px; font-weight: 800; line-height: 1.2; letter-spacing: 0.01em; }
    .package-banner-note { margin: 6px 0 0; font-size: 15px; line-height: 1.5; color: rgba(255, 255, 255, 0.92); }
    .package-banner-cta { flex: 0 0 auto; display: flex; flex-direction: column; align-items: stretch; gap: 6px; }
    .package-banner-form { margin: 0; }
    .package-banner-btn {
        display: inline-block; width: 100%; box-sizing: border-box;
        padding: 12px 22px; border: 0; border-radius: 10px; cursor: pointer;
        background: #fff; color: #4f46e5; font-size: 16px; font-weight: 800; text-align: center;
        text-decoration: none; white-space: nowrap;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
    }
    .package-banner-btn:hover { background: #eef2ff; }
    .package-banner-btn:disabled { opacity: 0.7; cursor: wait; }
    .package-banner-error { font-size: 13px; line-height: 1.4; color: #fde68a; max-width: 280px; }
    .package-banner-error:empty { display: none; }
    @media (max-width: 860px) {
        .package-banner { padding: 16px; gap: 12px; }
        .package-banner-title { font-size: 19px; }
        .package-banner-note { font-size: 14px; }
        .package-banner-cta { flex: 1 1 100%; }
        .package-banner-error { max-width: none; }
    }
    @media (max-width: 860px) {
        .catalog-head {
            margin-bottom: 16px;
        }
        .new-weekly {
            flex: 1 1 100%;
            justify-content: center;
            padding: 10px 14px;
            font-size: 14px;
            border-radius: 12px;
            white-space: normal;
            text-align: center;
        }
    }
    .section-more {
        display: inline-block;
        margin-top: 20px;
        color: var(--link);
        font-weight: 600;
        text-decoration: none;
    }
    .section-more:hover {
        text-decoration: underline;
    }
    .hero-actions {
        display: flex;
        gap: 14px;
        justify-content: center;
        flex-wrap: wrap;
        margin-bottom: 16px;
    }
    .hero-btn--ghost {
        background: transparent;
        border: 2px solid var(--accent);
        color: var(--accent-text);
        padding: 10px 26px;
    }
    .hero-btn--ghost:hover {
        background: var(--accent-soft);
    }
    .hero .hero-btn--ghost {
        color: #fff;
        border-color: #fff;
    }
    .hero .hero-btn--ghost:hover {
        background: rgba(255, 255, 255, 0.16);
    }
    .section {
        margin-bottom: 48px;
    }
    /* Ссылки на посадочные страницы внутри текста лендинга (2026-10-12):
       подчёркнуты цветом акцента, сам текст — как у абзаца, иначе
       подзаголовок полки читался бы как три кнопки. */
    .showcase-lead a,
    .about-card a {
        color: inherit;
        text-decoration: underline;
        text-decoration-color: var(--accent);
        text-decoration-thickness: 1px;
        text-underline-offset: 3px;
    }
    .showcase-lead a:hover,
    .about-card a:hover {
        color: var(--accent-text);
    }
    .section-lead {
        font-size: 15px;
        color: var(--text-muted);
        margin: -8px 0 20px;
        max-width: 720px;
    }
    /* Секция «Что внутри?» — три блока-абзаца разного тона вместо одной серой
       плашки. Тона взяты те же, что у плиток страницы сценария:
       полупрозрачная заливка поверх фона плюс рамка того же цвета — работают
       на любом фоне, потому что берут его собственный тон.

       Блоки — горизонтальные полосы одна под другой, а не колонки в ряд.
       Колонками они были до того, как контент лендинга сузился до колонки
       первого экрана: в 712px три колонки дают по 224px, и текст в 19px
       рвётся там на два-три слова в строку, вытягивая блок в узкий столбик.
       Полоса во всю ширину укладывает тот же текст в одну-две строки.

       Текст здесь заметно крупнее обычного (19px против 15px в остальных
       карточках лендинга): блоков всего три, они несут суть платформы, и
       читают их с той же дистанции, что и заголовок секции. */
    .about-grid {
        display: grid;
        grid-template-columns: minmax(0, 1fr);
        gap: 14px;
    }
    /* Шахматный порядок: строка — полоса плюс иконка, и чётные строки
       разворачиваются (row-reverse), поэтому иконка встаёт то справа, то
       слева, а полоса — наоборот. Ступенька заодно делает видимым порядок
       чтения, которого у трёх одинаковых полос во всю ширину нет. */
    .about-row {
        display: flex;
        align-items: center;
        gap: 22px;
    }
    .about-row:nth-child(even) {
        flex-direction: row-reverse;
    }
    .about-icon {
        flex: 0 0 auto;
        width: 104px;
        height: 104px;
    }
    .about-card {
        flex: 1 1 auto;
        min-width: 0;
        border-radius: 14px;
        padding: 22px 26px;
        border: 1px solid var(--about-border, var(--border));
        background: var(--about-bg, var(--surface));
    }
    .about-card p {
        font-size: 19px;
        line-height: 1.55;
        margin: 0;
        color: var(--text);
    }
    /* Первая фраза каждой полосы — жирная: у блоков нет заголовков, и она
       работает вместо него, давая взгляду точку входа. */
    .about-card strong {
        font-weight: 700;
    }
    /* Три стикера — второй экран главной (2026-10-22, по запросу автора;
       тексты его). Три бумажные записки в ряд: пастельные цвета — жёлтый
       стикер, розовый в тон зоны первого экрана и мятный, — каждая чуть
       повёрнута в свою сторону, с мягкой тенью на тёмной странице и
       полупрозрачным «кусочком скотча» сверху (::before): так три
       одинаковых прямоугольника читаются записками, а не карточками.
       Текст тёмный, крупный, первая фраза жирная. У́же 860px — столбиком,
       наклон вдвое меньше: на узком экране повёрнутый блок во всю ширину
       заметно выходит за края. */
    .stickers-screen {
        padding-top: clamp(28px, 6vh, 56px);
    }
    /* Заголовки «Как проходит игра» и «Вечер под контролем» — на две
       строки выше наклеек (2026-10-22, по запросам автора: «подними
       заголовки на 2 строчки выше», затем «заголовок поднять выше, а
       стикеры оставить на месте»): сдвиг — transform на две строки
       заголовка (2.4em при line-height 1.2), он не меняет раскладку,
       и записки стоят там же, где с обычным отступом 20px. Первый
       вариант добавлял те же 2.4em в margin-bottom — секция центрирует
       содержимое, и наклейки уезжали вниз. */
    .stickers-screen > .section-title,
    .stripes-screen > .section-title {
        transform: translateY(-2.4em);
    }
    .stickers {
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: clamp(18px, 3vw, 40px);
        align-items: stretch;
    }
    .sticker {
        position: relative;
        box-sizing: border-box;
        padding: clamp(26px, 2.6vw, 36px) clamp(20px, 2.2vw, 30px) clamp(22px, 2.4vw, 32px);
        border-radius: 6px;
        color: #2a1a33;
        background: var(--sticker-bg);
        box-shadow: 0 14px 34px rgba(0, 0, 0, 0.35), 0 2px 6px rgba(0, 0, 0, 0.2);
        transform: rotate(var(--sticker-tilt));
    }
    .sticker::before {
        content: '';
        position: absolute;
        top: -11px;
        left: 50%;
        width: 88px;
        height: 24px;
        transform: translateX(-50%) rotate(-3deg);
        background: rgba(255, 255, 255, 0.42);
        border: 1px solid rgba(255, 255, 255, 0.55);
        box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    }
    .sticker p {
        margin: 0;
        font-size: clamp(18px, 1.5vw, 22px);
        line-height: 1.45;
    }
    .sticker strong {
        font-weight: 800;
    }
    .sticker--yellow { --sticker-bg: #fde68a; --sticker-tilt: -2deg; }
    .sticker--pink   { --sticker-bg: #f6cfe3; --sticker-tilt: 1.5deg; }
    .sticker--mint   { --sticker-bg: #bfe9d3; --sticker-tilt: -1deg; }
    /* Полоски «Вечер под контролем» — третий экран (2026-10-22, по запросу
       автора: «горизонтальные вытянутые наклеенные цветные полоски в
       стиле второго экрана, приклеены не совсем ровно»): три широкие
       наклейки одна под другой, тот же язык, что у стикеров, — пастель,
       тень, тёмный текст с жирной первой фразой, — но наклон меньше
       (−0.8°, 0.6°, −0.5°: у полосы во всю ширину даже градус уводит
       концы на десятки пикселей) и скотч не по центру сверху, а
       кусочками на обоих концах (::before слева, ::after справа), как
       клеят ленту. Цвета — другая тройка пастелей (голубой, персиковый,
       лавандовый), чтобы экраны не сливались. */
    .stripes {
        display: flex;
        flex-direction: column;
        gap: clamp(18px, 2.6vh, 28px);
    }
    .stripe {
        position: relative;
        box-sizing: border-box;
        padding: clamp(16px, 1.8vw, 22px) clamp(28px, 3vw, 44px);
        border-radius: 6px;
        color: #2a1a33;
        background: var(--stripe-bg);
        box-shadow: 0 12px 30px rgba(0, 0, 0, 0.35), 0 2px 6px rgba(0, 0, 0, 0.2);
        transform: rotate(var(--stripe-tilt));
    }
    .stripe::before,
    .stripe::after {
        content: '';
        position: absolute;
        top: -10px;
        width: 64px;
        height: 22px;
        background: rgba(255, 255, 255, 0.42);
        border: 1px solid rgba(255, 255, 255, 0.55);
        box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    }
    .stripe::before { left: 26px; transform: rotate(-6deg); }
    .stripe::after { right: 26px; transform: rotate(5deg); }
    .stripe p {
        margin: 0;
        font-size: clamp(18px, 1.5vw, 22px);
        line-height: 1.45;
        text-align: center;
    }
    .stripe strong {
        font-weight: 800;
    }
    .stripe--sky      { --stripe-bg: #bae6fd; --stripe-tilt: -0.8deg; }
    .stripe--peach    { --stripe-bg: #fed7aa; --stripe-tilt: 0.6deg; }
    .stripe--lavender { --stripe-bg: #ddd6fe; --stripe-tilt: -0.5deg; }
    @media (max-width: 860px) {
        .stripe { padding-left: 18px; padding-right: 18px; }
        .stripe::before { left: 12px; }
        .stripe::after { right: 12px; }
    }
    @media (max-width: 860px) {
        .stickers {
            grid-template-columns: minmax(0, 1fr);
            gap: 26px;
        }
        .sticker {
            transform: rotate(calc(var(--sticker-tilt) / 2));
        }
    }
    /* Секция «Что внутри» снята 2026-10-22 (по запросу автора), но её
       устройство живёт дальше: .about-grid/.about-row/.about-card с
       иконками шахматкой — разметка «Кому подходит», а переменные
       --about-bg/--about-border цветовых классов — ещё и у карточек
       ленты шагов (.how-card). Правила ниже не мёртвые. */
    .about-card--green  { --about-bg: rgba(47, 174, 83, 0.14);  --about-border: rgba(47, 174, 83, 0.34); }
    .about-card--violet { --about-bg: rgba(124, 58, 237, 0.16); --about-border: rgba(167, 139, 250, 0.36); }
    .about-card--amber  { --about-bg: rgba(202, 138, 4, 0.18);   --about-border: rgba(250, 204, 21, 0.34); }
    .about-card--blue   { --about-bg: rgba(59, 130, 246, 0.16);  --about-border: rgba(96, 165, 250, 0.34); }
    .about-card--orange { --about-bg: rgba(234, 88, 12, 0.16);   --about-border: rgba(251, 146, 60, 0.34); }
    .about-card--teal   { --about-bg: rgba(13, 148, 136, 0.18);  --about-border: rgba(45, 212, 191, 0.34); }
    /* Лентой с прокруткой эта секция не оборачивается ни на какой ширине:
       текста в блоках много, и листать абзац по горизонтали неудобно —
       в отличие от коротких карточек-преимуществ ниже по странице. */
    @media (max-width: 860px) {
        .about-row {
            gap: 16px;
        }
        .about-icon {
            width: 72px;
            height: 72px;
        }
        .about-card {
            padding: 20px 18px;
        }
        .about-card p {
            font-size: 17px;
        }
    }
    @media (max-width: 480px) {
        .about-row {
            gap: 12px;
        }
        /* Иконка ужимается, но не исчезает: она несёт смысл блока, а при
           ширине экрана в 360px полоса и так занимает почти всё. */
        .about-icon {
            width: 48px;
            height: 48px;
        }
    }
    /* Посадочные страницы под поисковые запросы (2026-09-30,
       editor/seo_landing.html). Текста здесь заметно больше, чем на
       остальных страницах витрины, поэтому у колонки свой предел ширины:
       строка длиннее примерно 75 знаков читается тяжело — глаз теряет
       начало следующей. Остальное берётся у лендинга: заголовки секций,
       карточки сценариев, вопросы, полоса призыва. */
    /* Шапка посадочной страницы (2026-09-30): полоса из обложек во всю
       ширину контейнера. Высота растёт с шириной окна, но не больше 320px
       — шапка не должна отодвигать текст за первый экран; на телефоне
       держится нижний предел, и видно середину полосы (background-position
       по центру). */
    .seo-hero {
        height: clamp(190px, 25vw, 320px);
        border-radius: 16px;
        overflow: hidden;
        background-size: cover;
        background-position: center;
        display: flex;
        align-items: flex-end;
        margin: 0 0 24px;
    }
    /* Заголовок выровнен по колонке текста, а не по краю картинки: иначе
       он висит сам по себе, отдельно от всего, что идёт ниже. */
    .seo-hero-inner {
        width: 100%;
        max-width: 760px;
        margin: 0 auto;
        padding: 0 0 26px;
    }
    .seo-hero-title {
        margin: 0;
        font-size: 38px;
        line-height: 1.15;
        color: #f2f6fb;
        text-shadow: 0 2px 14px rgba(0, 0, 0, 0.65);
    }
    @media (max-width: 860px) {
        .seo-hero-inner {
            padding: 0 16px 20px;
        }
        .seo-hero-title {
            font-size: 28px;
        }
    }
    @media (max-width: 480px) {
        .seo-hero {
            border-radius: 12px;
        }
        .seo-hero-title {
            font-size: 24px;
        }
    }

    /* Предел ширины стоит на ТЕКСТОВЫХ блоках, а не на всей странице:
       подборка сценариев и вопросы должны занимать ту же ширину, что на
       лендинге, — в колонке под текст три карточки в ряд не помещаются. */
    .seo-page-title,
    .seo-page-lead,
    .seo-section {
        max-width: 760px;
        margin-left: auto;
        margin-right: auto;
    }
    .seo-page-title {
        font-size: 34px;
        line-height: 1.2;
        margin: 8px auto 16px;
        color: var(--text);
    }
    .seo-page-lead {
        font-size: 18px;
        line-height: 1.6;
        color: var(--text);
        margin: 0 auto 40px;
    }
    .seo-section-title {
        font-size: 22px;
        line-height: 1.3;
        margin: 0 0 12px;
        color: var(--text);
    }
    .seo-text {
        font-size: 16px;
        line-height: 1.65;
        color: var(--text-muted);
        margin: 0 0 14px;
    }
    /* Заголовки широких секций (подборка, вопросы, «смотрите также»)
       центрируются, как на лендинге: колонка текста у́же страницы, и
       прижатый к левому краю окна заголовок висел бы отдельно от всего
       остального. */
    .seo-page .section-title {
        text-align: center;
    }
    .seo-cards-note {
        text-align: center;
        margin: -8px auto 20px;
        max-width: 640px;
    }
    .seo-related {
        display: flex;
        flex-wrap: wrap;
        gap: 10px;
        justify-content: center;
    }
    .seo-related-link {
        padding: 10px 16px;
        border-radius: 10px;
        border: 1px solid var(--border);
        background: var(--surface);
        color: var(--text);
        text-decoration: none;
        font-size: 15px;
    }
    .seo-related-link:hover {
        border-color: var(--accent);
        color: var(--accent-text);
    }

    /* Раздел «Идеи» (2026-10-12): статьи-подборки под информационные
       запросы. Пункты подборки — карточки в той же колонке 760px, наш
       формат отмечен рамкой цвета акцента, но устроен как остальные. */
    .idea-meta {
        max-width: 760px;
        margin: -8px auto 28px;
        font-size: 14px;
        color: var(--text-muted);
    }
    .idea-meta a {
        color: inherit;
    }
    .idea-item {
        margin: 0 0 14px;
        padding: 14px 16px 12px;
        border: 1px solid var(--border);
        border-radius: 12px;
        background: var(--surface);
    }
    .idea-item--ours {
        border-color: var(--accent);
    }
    .idea-item-title {
        margin: 0 0 6px;
        font-size: 18px;
        line-height: 1.3;
        color: var(--text);
    }
    .idea-item-title span {
        color: var(--text-muted);
        font-weight: 400;
        margin-right: 8px;
    }
    .idea-item-facts {
        margin: 0 0 10px;
        font-size: 14px;
        color: var(--text-muted);
    }
    .idea-item .seo-text {
        margin-bottom: 8px;
    }
    .idea-item-link {
        display: inline-block;
        margin-top: 2px;
        font-size: 15px;
        color: var(--accent-text);
        text-decoration: none;
    }
    .idea-item-link:hover {
        text-decoration: underline;
    }
    .ideas-list {
        display: grid;
        gap: 14px;
        max-width: 760px;
        margin: 0 auto 40px;
    }
    .ideas-list-item {
        display: block;
        padding: 16px 18px;
        border: 1px solid var(--border);
        border-radius: 12px;
        background: var(--surface);
        color: var(--text);
        text-decoration: none;
    }
    .ideas-list-item:hover {
        border-color: var(--accent);
    }
    .ideas-list-item h2 {
        margin: 0 0 6px;
        font-size: 20px;
        line-height: 1.3;
    }
    .ideas-list-item p {
        margin: 0;
        color: var(--text-muted);
        font-size: 15px;
        line-height: 1.5;
    }
    @media (max-width: 480px) {
        .seo-page-title {
            font-size: 27px;
        }
        .seo-page-lead {
            font-size: 16px;
            margin-bottom: 28px;
        }
        .seo-section-title {
            font-size: 20px;
        }
    }

    /* Частые вопросы (2026-09-30). Две колонки на широком экране: вопросов
       шесть, и одной колонкой секция вытягивается на полтора экрана перед
       самым призывом к действию. Ширина колонки взята с запасом — ответ
       занимает три-четыре строки, при более узкой колонке он рвался бы в
       столбик из двух слов. */
    .faq-grid {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
        gap: 14px;
        text-align: left;
    }
    .faq-item {
        border-radius: 14px;
        padding: 18px 22px;
        border: 1px solid var(--border);
        background: var(--surface);
    }
    /* Вопрос — заголовок третьего уровня: он несёт запрос, по которому
       страницу ищут, и поисковику важна его роль в структуре, а не только
       начертание. */
    .faq-item h3 {
        margin: 0 0 8px;
        font-size: 17px;
        line-height: 1.35;
        color: var(--text);
    }
    .faq-item p {
        margin: 0;
        font-size: 15px;
        line-height: 1.55;
        color: var(--text-muted, var(--text));
    }
    @media (max-width: 480px) {
        .faq-item {
            padding: 16px 16px;
        }
    }
    /* Вкладка «Сервер» в кабинете (2026-09-30). Блоки в ряд, пока
       помещаются: диск, база, память и нагрузка читаются вместе — беда
       обычно видна по сочетанию, а не по одной цифре. */
    .sys-block {
        border: 1px solid var(--border);
        border-radius: 12px;
        padding: 16px 18px;
        margin-bottom: 14px;
        background: var(--surface);
    }
    .sys-block-title {
        margin: 0 0 12px;
        font-size: 15px;
        font-weight: 700;
        color: var(--text);
    }
    .sys-row {
        margin-bottom: 10px;
    }
    /* Абзацы внутри блока прижаты к своим полосам: у .panel-text общий
       отступ рассчитан на самостоятельный текст, и рядом с полосой
       заполнения строки слипались. */
    .sys-block .panel-text {
        margin: 0 0 12px;
    }
    .sys-block .panel-text:last-child {
        margin-bottom: 0;
    }
    .sys-row-head {
        display: flex;
        justify-content: space-between;
        gap: 12px;
        flex-wrap: wrap;
        font-size: 13px;
        margin-bottom: 6px;
    }
    .sys-row-title {
        font-weight: 600;
        color: var(--text);
    }
    .sys-row-note {
        color: var(--text-dim);
    }
    .sys-bar {
        height: 10px;
        border-radius: 5px;
        background: var(--surface-3);
        overflow: hidden;
    }
    .sys-bar-fill {
        height: 100%;
        border-radius: 5px;
    }
    /* Три уровня приходят с сервера (sysinfo._level): пороги заданы там
       одним местом, разметка только красит. */
    .sys-bar-fill.sys-ok { background: var(--accent); }
    .sys-bar-fill.sys-warn { background: #d97706; }
    .sys-bar-fill.sys-alert { background: var(--danger); }
    /* Таблица размеров: имена длинные, цифры короткие — имя жмётся, число
       держит место и остаётся у правого края. */
    .sys-table {
        margin-top: 10px;
        font-size: 13px;
    }
    .sys-table-row {
        display: flex;
        justify-content: space-between;
        gap: 12px;
        padding: 4px 0;
        border-top: 1px solid #e5e7eb;
        color: var(--text-muted);
    }
    .sys-table-row span:first-child {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    .sys-table-row span:last-child {
        flex: 0 0 auto;
    }

    .card-image {
        position: relative;
    }
    .card-type-badge {
        display: inline-block;
        background: var(--violet-soft);
        color: var(--violet-text);
        font-size: 11px;
        font-weight: 700;
        padding: 2px 8px;
        border-radius: 6px;
        margin-bottom: 6px;
    }
    /* Ряды карточек лендинга — ЛЕНТА С ПРОКРУТКОЙ ТОЛЬКО НА УЗКИХ
       ЭКРАНАХ (2026-09-07, по прямому запросу пользователя: «листание
       вправо каждого блока не нужно на десктопе»). Раньше это правило
       действовало на любой ширине, и на широком экране шесть карточек
       «Нового вида досуга» приходилось листать вместо того, чтобы просто
       увидеть их все двумя рядами. Выше 860px работают обычные grid-
       правила, объявленные выше по файлу (auto-fill/auto-fit с переносом),
       — здесь они не переопределяются вовсе.

       Переопределение именно через .landing-page: .cards-grid/.card
       используются и на /catalog/, где нужен обычный wrap-grid. */
    @media (max-width: 860px) {
        .landing-page .cards-grid {
            display: flex;
            flex-wrap: nowrap;
            overflow-x: auto;
            -webkit-overflow-scrolling: touch;
            scrollbar-width: none;
            -ms-overflow-style: none;
            /* Лента — контейнер прокрутки, и всё, что торчит за край
               карточки (мигающая рамка разминки, 3px box-shadow), она
               обрезала со всех сторон (2026-10-15, замечание автора:
               «показывает только часть мигания»). Внутренний отступ
               даёт рамке место, отрицательный внешний возвращает
               карточки на прежнюю линию. */
            padding: 5px;
            margin: -5px;
        }
        .landing-page .cards-grid::-webkit-scrollbar {
            display: none;
        }
    }
    /* Обёртка ряда карточек — держит стрелки прокрутки поверх контента.
       Видимость стрелок считает JS (landing.html): скрыты, если ряд не
       переполнен, и на начале/конце ряда — своей стороной. */
    .landing-page .landing-carousel {
        position: relative;
    }
    .landing-page .carousel-arrow {
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
        z-index: 2;
        width: 40px;
        height: 40px;
        padding: 0;
        border: 1px solid var(--border);
        border-radius: 50%;
        background: var(--surface-2);
        color: var(--text);
        display: flex;
        align-items: center;
        justify-content: center;
        cursor: pointer;
        box-shadow: 0 2px 10px rgba(0, 0, 0, 0.14);
        transition: background 0.2s, box-shadow 0.2s;
    }
    .landing-page .carousel-arrow:hover {
        background: var(--surface-3);
        box-shadow: 0 4px 14px rgba(0, 0, 0, 0.2);
    }
    .landing-page .carousel-arrow svg {
        width: 18px;
        height: 18px;
    }
    .landing-page .carousel-arrow[hidden] {
        display: none;
    }
    .landing-page .carousel-arrow--prev {
        left: -8px;
    }
    .landing-page .carousel-arrow--next {
        right: -8px;
    }
    .landing-page .carousel-arrow--prev svg {
        transform: scaleX(-1);
    }
    .landing-page .carousel-swipe-hint {
        display: none;
    }
    /* Выше 860px лента осталась одна (2026-10-12): «Как проходит игра»
       листается на любой ширине, и стрелки ей там нужны — без них на
       широком экране не видно, что вправо есть продолжение. Прежде такой
       же была снятая теперь секция «Какую игру выбрать». У остальных
       секций карточки раскладывает grid, листать нечего, и стрелка была
       бы кнопкой в никуда.

       Подсказка-рука скрыта везде: она про свайп пальцем. */
    @media (min-width: 861px) {
        .landing-page .section:not(.how-screen) .carousel-arrow,
        .landing-page .carousel-swipe-hint {
            display: none;
        }
        /* Ровные ряды вместо «сколько влезет»: auto-fill раскладывал шесть
           карточек как 5 + 1 — сирота в последнем ряду читается как обрыв
           списка. Две колонки дают ровные пары в любой секции и держат
           карточку читаемой шириной: контент лендинга сужен до колонки
           первого экрана (712px), и в три колонки на карточку остаётся
           224px — текст рвётся на два-три слова в строку. */
        .landing-page .cards-grid {
            grid-template-columns: repeat(2, minmax(0, 1fr));
        }
    }
    /* Чем листать — решает УСТРОЙСТВО ВВОДА, а не ширина окна
       (2026-09-07): у мыши нет жеста свайпа, у пальца нет курсора, чтобы
       целиться в кнопку. Планшет в портретной ориентации попадает в ленту
       по ширине и получает именно подсказку-жест, а не стрелки, — раньше
       порог стоял на 480px, и планшету доставались кнопки под мышь. */
    @media (max-width: 860px) and (hover: none), (max-width: 860px) and (pointer: coarse) {
        .landing-page .carousel-arrow {
            display: none;
        }
        /* Подсказка — анимированная стрелка вправо (JS показывает её
           только на ряду, ближайшем к центру экрана, см.
           updateFocusedHint() в landing.html). Не гаснет по таймеру —
           только когда этот ряд проскроллили или в фокус попал другой
           блок. */
        .landing-page .carousel-swipe-hint {
            display: flex;
            position: absolute;
            top: 50%;
            right: 10px;
            transform: translateY(-50%);
            align-items: center;
            justify-content: center;
            width: 34px;
            height: 34px;
            color: var(--accent-text);
            pointer-events: none;
            z-index: 2;
            filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.25));
            animation: carousel-swipe-hint 1.6s ease-in-out infinite;
        }
        .landing-page .carousel-swipe-hint svg {
            width: 100%;
            height: 100%;
        }
        .landing-page .carousel-swipe-hint[hidden] {
            display: none;
        }
    }
    /* Наведение — только там, где есть курсор: на тач-экране :hover
       залипает после касания, и карточка остаётся «подсвеченной» до
       следующего тапа по другому месту. */
    @media (hover: none) {
        .landing-page .card:hover,
        .landing-page .carousel-arrow:hover {
            box-shadow: none;
            background: var(--surface-2);
        }
        .landing-page .card:hover {
            background: var(--surface);
        }
    }
    @keyframes carousel-swipe-hint {
        0% { opacity: 0; transform: translateY(-50%) translateX(0); }
        20% { opacity: 1; transform: translateY(-50%) translateX(0); }
        80% { opacity: 1; transform: translateY(-50%) translateX(16px); }
        100% { opacity: 0; transform: translateY(-50%) translateX(16px); }
    }
    @media (prefers-reduced-motion: reduce) {
        .landing-page .carousel-swipe-hint {
            animation: none;
            opacity: 0.9;
            transform: translateY(-50%);
        }
    }
    /* Ширина карточки в ленте: на планшете видно две с половиной (третья
       обрезана — это и есть подсказка, что ряд листается), на телефоне —
       одна и краешек следующей. На десктопе правило не действует вовсе:
       там карточки раскладывает grid. */
    @media (max-width: 860px) {
        .landing-page .card {
            flex: 0 0 calc((100% - 2 * 20px) / 2.4);
            min-width: 220px;
        }
    }
    @media (max-width: 480px) {
        .landing-page .card {
            flex-basis: 86%;
            min-width: 0;
        }
    }
    .cta-band {
        text-align: center;
        background:
            radial-gradient(120% 140% at 50% 0%, rgba(47, 174, 83, 0.18) 0%, rgba(47, 174, 83, 0) 62%),
            var(--surface-2);
        border: 1px solid var(--border);
        color: var(--text);
        border-radius: 14px;
        padding: 48px 24px;
        margin-bottom: 20px;
    }
    .cta-band h2 {
        font-size: 32px;
        margin: 0 0 10px;
    }
    @media (max-width: 860px) {
        .cta-band {
            padding: 36px 20px;
        }
        .cta-band h2 {
            font-size: 26px;
        }
    }
    @media (max-width: 480px) {
        .cta-band {
            padding: 28px 16px;
        }
        .cta-band h2 {
            font-size: 22px;
        }
    }
    .cta-band p {
        font-size: 15px;
        color: var(--text-muted);
        margin: 0 0 20px;
    }
    /* Единый подвал публичных страниц (2026-09-24). Полоса на всю ширину
       окна — тем же приёмом, что hero лендинга (100vw и отрицательные
       поля): на лендинге .app-shell сужен до колонки контента, и подвал
       внутри неё ломался. Контейнер внутри — ширины шапки. */
    .public-footer {
        width: 100vw;
        margin-left: calc(50% - 50vw);
        margin-right: calc(50% - 50vw);
        /* Зазор от контента — паддингом: на публичных страницах margin-top
           у подвала занят прижимом к низу экрана (auto, см. ниже). */
        padding-top: 56px;
        font-size: 13px;
        color: var(--text-dim);
    }
    .public-footer-band {
        border-top: 1px solid #e5e7eb;
        background: var(--surface);
    }
    .public-footer-inner {
        max-width: 1326px;
        margin: 0 auto;
        padding: 32px 20px 20px;
        box-sizing: border-box;
    }
    .public-footer-top {
        display: flex;
        justify-content: space-between;
        align-items: flex-start;
        gap: 32px;
    }
    .public-footer-brand {
        flex: 0 1 300px;
        display: flex;
        flex-direction: column;
        gap: 10px;
    }
    .public-footer-brand .public-logo { align-self: flex-start; font-size: 20px; }
    .public-footer-brand .public-logo-icon { width: 22px; height: 22px; }
    .public-footer-tagline {
        margin: 0;
        font-size: 13px;
        line-height: 1.5;
        color: var(--text-dim);
    }
    .public-footer-cols {
        display: flex;
        gap: 48px;
        flex-wrap: wrap;
    }
    .public-footer-col {
        display: flex;
        flex-direction: column;
        gap: 8px;
        min-width: 150px;
    }
    .public-footer-col-title {
        font-size: 12px;
        font-weight: 700;
        letter-spacing: .04em;
        text-transform: uppercase;
        color: var(--text-muted);
        margin-bottom: 2px;
    }
    .public-footer-col a {
        color: var(--text-dim);
        text-decoration: none;
        line-height: 1.4;
    }
    .public-footer-col a:hover {
        color: var(--text);
        text-decoration: underline;
    }
    .public-footer-bottom {
        margin-top: 24px;
        padding-top: 14px;
        border-top: 1px solid #e5e7eb;
        display: flex;
        justify-content: space-between;
        gap: 12px;
        flex-wrap: wrap;
    }
    @media (max-width: 760px) {
        .public-footer-top {
            flex-direction: column;
            gap: 24px;
        }
        .public-footer-brand { flex-basis: auto; }
        .public-footer-cols { gap: 24px 36px; }
    }
    /* Плашки способов оплаты на странице «Способы оплаты» (2026-09-21):
       банк-эквайер требует видеть способы на странице. Логотип + название;
       фон плашки всегда белый — синий Visa и подписи логотипов на тёмной
       теме сайта не читаются, а бренды рассчитаны на светлый фон. */
    .pay-badges {
        display: flex;
        gap: 10px;
        flex-wrap: wrap;
        margin: 0 0 22px;
    }
    .pay-badge {
        display: inline-flex;
        align-items: center;
        gap: 10px;
        padding: 7px 14px 7px 10px;
        border: 1px solid #d0d7de;
        border-radius: 8px;
        font-size: 14px;
        font-weight: 600;
        letter-spacing: 0.02em;
        color: #1f2937;
        background: #ffffff;
    }
    .pay-badge-logo {
        display: block;
        height: 22px;
        width: auto;
    }
    .info-page {
        max-width: 760px;
        margin: 0 auto;
        padding-bottom: 20px;
    }
    .info-page h1 {
        font-size: 30px;
        margin: 0 0 20px;
    }
    .info-page-text {
        font-size: 15px;
        line-height: 1.7;
        color: var(--text-muted);
    }
    .info-page-text p {
        margin: 0 0 14px;
    }
    .info-page-empty {
        font-size: 14px;
        color: var(--text-dim);
    }
    .info-page-text--protected {
        -webkit-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
    }
    /* Форма «Сообщение фаундеру проекта» на странице контактов
       (2026-09-18). Поля стилизует правило по тегу для body.public-page
       (base.css), здесь — только раскладка. */
    .contact-form {
        display: flex;
        flex-direction: column;
        gap: 12px;
        margin-top: 28px;
        padding-top: 24px;
        border-top: 1px solid #e5e7eb;
    }
    .contact-form-title {
        font-size: 20px;
        margin: 0 0 4px;
    }
    .contact-form textarea {
        min-height: 140px;
        resize: vertical;
        font: inherit;
    }
    .contact-form .hero-btn {
        align-self: flex-start;
    }
    .contact-form-ok {
        color: var(--accent-text);
        font-size: 14px;
        background: rgba(47, 174, 83, 0.14);
        border: 1px solid rgba(47, 174, 83, 0.35);
        border-radius: 8px;
        padding: 10px 14px;
    }
    /* Приманки формы контактов: за экраном, без display:none и opacity —
       их headless-боты читают как «скрыто» и поле не заполняют. */
    .contact-form-trap {
        position: absolute;
        left: -9999px;
        top: 0;
        width: 1px;
        height: 1px;
        overflow: hidden;
    }
    /* Баннер согласия — компактная полоса (2026-09-18): прежний занимал
       на десктопе ~90px и накрывал кнопки первого экрана, а на телефоне
       растягивался на весь экран — flex-basis 420px у текста в колонке
       задавал ему ВЫСОТУ. */
    .consent-banner {
        position: fixed;
        left: 16px;
        right: 16px;
        bottom: 12px;
        z-index: 300;
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 14px;
        flex-wrap: wrap;
        background: var(--surface-2);
        color: var(--text);
        border: 1px solid var(--border);
        padding: 10px 16px;
        border-radius: 12px;
        box-shadow: 0 4px 24px rgba(0, 0, 0, 0.45);
    }
    .consent-banner-text {
        margin: 0;
        font-size: 12px;
        line-height: 1.45;
        flex: 1 1 360px;
        min-width: 0;
    }
    .consent-banner-text a {
        color: var(--accent-text);
        text-decoration: underline;
    }
    .consent-banner-btn {
        flex: 0 0 auto;
        padding: 8px 18px;
        background: var(--accent);
        color: #fff;
        border: 0;
        border-radius: 8px;
        font-weight: 600;
        font-size: 14px;
        cursor: pointer;
        white-space: nowrap;
    }
    .consent-banner-btn:hover {
        background: var(--accent-hover);
    }
    @media (max-width: 480px) {
        .consent-banner {
            left: 10px;
            right: 10px;
            bottom: 10px;
            padding: 12px 14px;
            flex-direction: column;
            align-items: stretch;
            gap: 10px;
        }
        /* В колонке flex-basis — это высота: текст берёт столько, сколько
           нужно строкам, и не больше. */
        .consent-banner-text {
            flex: 0 0 auto;
        }
        .consent-banner-btn {
            width: 100%;
        }
    }
/* ── Тёмная витрина: рамки и правки, не сводимые к замене токена ──────────
   На тёмном фоне разница между --bg и --surface (#0d1218 против #161d26)
   куда тоньше, чем между светлыми #e6e8eb и #e9edf1, и карточка без рамки
   расплывается в фоне. Рамка проставлена только блокам публичных страниц —
   списки кабинета (.scenario-row, .cabinet-*) сюда не входят и остаются
   такими же, как были. */
body.public-page .card,
body.public-page .catalog-filters,
body.public-page .stat-tile {
    border: 1px solid var(--border);
}

body.public-page .card {
    background: var(--surface);
}

body.public-page .card:hover {
    border-color: var(--border-strong);
}

body.public-page .public-footer-col a {
    color: var(--text-dim);
}

body.public-page .section-more {
    color: var(--accent-text);
}

/* Типографика вордмарка — теми же тремя ступенями, что и hero h1. */

/* ── Страница сценария (scenario_detail.html) ─────────────────────────────
   До 2026-09-14 вся страница была набрана inline-стилями с захардкоженными
   светлыми пастельными подложками (#eff6ff, #fff7ed, …). На тёмной витрине
   такие плашки светились белыми прямоугольниками, поэтому разметка
   переведена на классы, а подложки — на полупрозрачные акценты: они
   работают поверх любого фона, потому что берут его собственный тон. */
.detail-page {
    max-width: 800px;
    margin: 0 auto;
}

.detail-cover {
    border-radius: 14px;
    overflow: hidden;
    max-height: 400px;
}

.detail-cover img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.detail-cover-note {
    font-size: 12px;
    color: var(--text-dim);
    margin: 6px 0 24px;
    text-align: right;
}

.detail-cover-gap {
    margin-bottom: 24px;
}

.detail-title {
    font-size: 28px;
    line-height: 1.4;
    margin: 0 0 20px;
}

.detail-intro {
    background: var(--violet-soft);
    border: 1px solid var(--violet-border);
    border-radius: 12px;
    padding: 18px 22px;
    margin-bottom: 24px;
}

.detail-intro-badge {
    display: inline-block;
    background: var(--violet);
    color: #fff;
    font-size: 12px;
    font-weight: 700;
    padding: 3px 10px;
    border-radius: 6px;
    margin-bottom: 10px;
}

.detail-intro-text {
    color: var(--text);
    font-size: 15px;
    line-height: 1.6;
}

.stat-row {
    display: flex;
    gap: 24px;
    margin-bottom: 32px;
    flex-wrap: wrap;
}

.stat-row--last {
    margin-bottom: 40px;
}

.stat-tile {
    flex: 1;
    min-width: 130px;
    padding: 16px 24px;
    border-radius: 10px;
    text-align: center;
    background: var(--tile-bg, var(--surface));
}

.stat-tile-label {
    font-size: 13px;
    line-height: 1.4;
    color: var(--text-dim);
}

.stat-tile-value {
    font-size: 24px;
    line-height: 1.3;
    font-weight: 700;
    color: var(--tile-fg, var(--text));
}

.stat-tile-value--sm {
    font-size: 18px;
    line-height: 1.45;
    margin-top: 3px;
}

.stat-tile-note {
    font-size: 12px;
    line-height: 1.4;
    color: var(--text-dim);
    margin-top: 2px;
}

/* Оттенки плиток. Подложка — полупрозрачная заливка того же тона, что и
   цифра: на светлой теме получается прежняя пастель, на тёмной — мягкое
   свечение, а не белый прямоугольник. */
.stat-tile--blue   { --tile-bg: rgba(59, 130, 246, 0.14);  --tile-fg: #60a5fa; }
.stat-tile--orange { --tile-bg: rgba(234, 88, 12, 0.14);   --tile-fg: #fb923c; }
.stat-tile--teal   { --tile-bg: rgba(13, 148, 136, 0.16);  --tile-fg: #2dd4bf; }
.stat-tile--green  { --tile-bg: rgba(22, 163, 74, 0.16);   --tile-fg: #4ade80; }
.stat-tile--cyan   { --tile-bg: rgba(8, 145, 178, 0.16);   --tile-fg: #22d3ee; }
.stat-tile--amber  { --tile-bg: rgba(202, 138, 4, 0.18);   --tile-fg: #facc15; }
.stat-tile--violet { --tile-bg: rgba(124, 58, 237, 0.18);  --tile-fg: #a78bfa; }

.detail-section-title {
    font-size: 20px;
    margin: 0 0 14px;
}

.detail-legend {
    margin-bottom: 32px;
    font-size: 16px;
    line-height: 1.8;
    color: var(--text);
}

.detail-legend p {
    margin: 0 0 16px;
}

.detail-authors {
    margin-bottom: 24px;
    color: var(--text-muted);
    font-size: 15px;
    line-height: 1.6;
}

/* Отзывы и оценки на странице сценария (2026-09-24). */
.detail-feedback { display: flex; flex-direction: column; gap: 10px; margin: 0 0 24px; }
.detail-feedback-row { background: var(--surface, #fff); border: 1px solid var(--border, #e5e7eb); border-radius: 10px; padding: 12px 16px; }
.detail-feedback-head { display: flex; gap: 14px; flex-wrap: wrap; align-items: baseline; font-size: 13px; }
.detail-feedback-date { color: var(--text-muted); }
.detail-feedback-player { font-weight: 700; }
.detail-feedback-city { font-weight: 400; color: var(--text-muted); }
.detail-feedback-team { color: var(--text-muted); }
.detail-feedback-rating { color: #ca8a04; font-weight: 700; margin-left: auto; }
.detail-feedback-text { margin-top: 6px; font-size: 14px; line-height: 1.55; white-space: pre-line; }

.detail-notice {
    border-radius: 10px;
    padding: 16px 20px;
    margin-bottom: 8px;
    font-size: 15px;
    line-height: 1.6;
}

.detail-notice a {
    font-weight: 600;
    text-decoration: underline;
    color: inherit;
}

.detail-notice--warning {
    background: rgba(180, 83, 9, 0.16);
    border: 1px solid rgba(240, 217, 154, 0.35);
    color: #fbbf24;
}

/* Памятка ступени (2026-10-10) — не предупреждение и не отказ, а
   подсказка «что сделать перед игрой», поэтому цвет спокойный, в тон
   акцента платформы, а не жёлтый и не красный. */
.detail-notice--info {
    background: rgba(124, 58, 237, 0.16);
    border: 1px solid rgba(167, 139, 250, 0.34);
    color: #ddd6fe;
    margin-bottom: 18px;
}

.detail-notice--danger {
    background: rgba(185, 28, 28, 0.16);
    border: 1px solid rgba(243, 184, 177, 0.35);
    color: #fca5a5;
}

.detail-actions {
    display: flex;
    gap: 16px;
    justify-content: center;
    padding: 32px 0;
    border-top: 1px solid #e5e7eb;
    flex-wrap: wrap;
}

/* Крупная кнопка действия — используется на странице сценария и в лобби. */
.btn-lg {
    display: inline-block;
    padding: 14px 32px;
    border: 0;
    border-radius: 10px;
    color: #fff;
    text-decoration: none;
    font-weight: 600;
    font-size: 16px;
    cursor: pointer;
    text-align: center;
}

.btn-lg--primary {
    background: var(--accent);
}

.btn-lg--primary:hover {
    background: var(--accent-hover);
}

.btn-lg--secondary {
    background: var(--surface-3);
    color: var(--text);
}

.btn-lg--secondary:hover {
    background: var(--border-strong);
}

.btn-lg--danger {
    background: var(--danger-bg);
    color: #fff;
}

/* ── Вход, регистрация, вход в комнату ───────────────────────────────────
   Поля этих форм рендерит Django без CSS-классов, поэтому их внешний вид
   задан правилом по тегу в base.css (body.public-page input/select/…). */
/* Рейтинг команд (2026-10-19): таблица на всю ширину, на телефоне —
   горизонтальная прокрутка обёртки, колонки не ломаются. */
.team-rating-lead { color: var(--text-dim); font-size: 15px; max-width: 720px; margin: 0 0 18px; }
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.team-rating-table { width: 100%; border-collapse: collapse; font-size: 14px; min-width: 640px; }
.team-rating-table th, .team-rating-table td { padding: 10px 12px; border-bottom: 1px solid var(--border); text-align: left; white-space: nowrap; }
.team-rating-table th { color: var(--text-dim); font-weight: 600; font-size: 13px; }
.team-rating-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.team-rating-table .team-rating-name { font-weight: 700; white-space: normal; }
.team-rating-table tr.is-mine td { background: rgba(109, 40, 217, 0.12); }
.team-rating-table .team-rating-empty { text-align: center; color: var(--text-dim); padding: 28px 12px; white-space: normal; }
.team-pending { font-size: 12px; color: #f0ad4e; font-weight: 600; }
.team-rating-note { color: var(--text-dim); font-size: 13px; margin-top: 12px; line-height: 1.5; }
.team-rank { display: inline-block; font-size: 12px; font-weight: 700; padding: 2px 8px; border-radius: 6px; background: rgba(109, 40, 217, 0.16); color: #c4b5fd; }
.team-rank--elite { background: rgba(240, 173, 78, 0.2); color: #f0ad4e; }
.team-hall-title { margin: 28px 0 10px; font-size: 20px; }
.team-hall-table { min-width: 520px; }
/* Рейтинг на телефоне (2026-10-23, по запросу автора): при прокрутке
   таблицы вправо колонки «Место» (1-я) и «Команда» (3-я) закреплены слева
   (sticky), остальные уезжают под них и видны настолько, насколько
   хватает экрана. «Рейтинг» (2-я) прокручивается как все: закреплённая
   3-я встаёт сразу за 1-й (left = ширина 1-й, она задана жёстко).
   Фон закреплённых ячеек непрозрачный — иначе под ними просвечивали бы
   уезжающие колонки; у своей команды подсветка смешана с фоном. Зал
   славы не трогаем: там другие колонки. */
@media (max-width: 640px) {
    .team-rating-table:not(.team-hall-table) th:nth-child(1),
    .team-rating-table:not(.team-hall-table) td:nth-child(1),
    .team-rating-table:not(.team-hall-table) th:nth-child(3),
    .team-rating-table:not(.team-hall-table) td:nth-child(3) {
        position: sticky;
        background: var(--bg);
        z-index: 1;
    }
    .team-rating-table:not(.team-hall-table) th:nth-child(1),
    .team-rating-table:not(.team-hall-table) td:nth-child(1) {
        left: 0;
        width: 56px;
        min-width: 56px;
        max-width: 56px;
        box-sizing: border-box;
        padding-right: 8px;
    }
    .team-rating-table:not(.team-hall-table) th:nth-child(3),
    .team-rating-table:not(.team-hall-table) td:nth-child(3) {
        left: 56px;
        max-width: 150px;
        box-shadow: 2px 0 0 var(--border);
    }
    .team-rating-table:not(.team-hall-table) th:nth-child(1),
    .team-rating-table:not(.team-hall-table) th:nth-child(3) {
        z-index: 2;
    }
    .team-rating-table:not(.team-hall-table) tr.is-mine td:nth-child(1),
    .team-rating-table:not(.team-hall-table) tr.is-mine td:nth-child(3) {
        background: color-mix(in srgb, var(--bg) 88%, #6d28d9 12%);
    }
    .team-rating-table .team-rating-empty { position: static; }
}

.auth-card {
    max-width: 400px;
    margin: 40px auto;
}

.auth-title {
    margin: 0;
}

.auth-form {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-top: 20px;
}

.auth-error {
    color: var(--danger);
    font-size: 13px;
    margin-top: 4px;
}

.auth-alert {
    color: var(--danger);
    font-size: 14px;
    background: rgba(198, 40, 40, 0.14);
    border: 1px solid rgba(198, 40, 40, 0.35);
    border-radius: 8px;
    padding: 10px 14px;
    margin-top: 16px;
}
/* Зелёный вариант — «почта подтверждена» на странице входа (2026-09-21). */
.auth-alert--ok {
    color: #047857;
    background: rgba(4, 120, 87, 0.14);
    border-color: rgba(4, 120, 87, 0.35);
}

.auth-switch {
    margin-top: 16px;
    font-size: 14px;
}

.auth-switch a {
    color: var(--accent-text);
}

/* Гостевой вход по дороге в комнату (2026-10-12): рамка цвета акцента —
   это главный путь за стол, форма своего аккаунта идёт ниже за чертой. */
.guest-entry {
    margin-top: 16px;
    padding: 16px;
    border: 1px solid var(--accent);
    border-radius: 12px;
    background: var(--surface);
}
.guest-entry-title {
    margin: 0;
    font-size: 18px;
}
.guest-entry-text {
    margin: 6px 0 0;
    font-size: 14px;
    color: var(--text-muted);
}
.guest-entry-form {
    margin-top: 12px;
}
.guest-entry-note {
    margin: 10px 0 0;
    font-size: 12px;
    line-height: 1.45;
    color: var(--text-muted);
}
.guest-entry-note a {
    color: var(--accent-text);
}
.auth-divider {
    margin: 20px 0 4px;
    font-size: 13px;
    color: var(--text-muted);
    text-align: center;
}

/* Вход одним касанием (2026-09-28): кнопки VK ID и Яндекс ID над формой.
   Фирменные цвета заданы прямо, а не переменными темы: узнаваемость
   кнопки провайдера и держится на его цвете, он одинаков в обеих темах. */
.social-auth {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 20px;
}
.social-btn {
    display: block;
    text-align: center;
    text-decoration: none;
    font-size: 15px;
    font-weight: 600;
    color: #fff;
    border-radius: 8px;
    padding: 12px 16px;
    transition: filter .2s ease;
}
.social-btn:hover { filter: brightness(1.08); }
.social-btn--vk { background: #0077ff; }
.social-btn--yandex { background: #fc3f1d; }
/* Разделитель «или»: линия с подписью посередине — форма ниже остаётся
   равноправной, а не выглядит запасным путём. */
.social-auth-or {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 4px 0 -6px;
    color: var(--text-dim);
    font-size: 13px;
}
.social-auth-or::before,
.social-auth-or::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--border);
}

/* Строка согласия под кнопкой регистрации (2026-09-18): регистрация —
   обработка персональных данных, и согласие фиксируется здесь, а не
   только баннером. */
.auth-consent {
    margin: 0;
    font-size: 12px;
    line-height: 1.5;
    color: var(--text-dim);
}
.auth-consent a {
    color: var(--accent-text);
}
/* Чекбокс согласия на регистрации (2026-09-24). */
.auth-consent--check {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    cursor: pointer;
    text-align: left;
}
.auth-consent--check input {
    margin: 3px 0 0;
    flex-shrink: 0;
    accent-color: var(--accent, #2da44e);
}
/* Галочка информационных материалов (2026-10-28, по запросу автора):
   заметна — отдельной строкой с чекбоксом, но тише условий — чуть
   бледнее, чтобы взгляд на ней не задерживался. */
.auth-consent--soft {
    opacity: .8;
}

/* Кнопка отправки формы: <button> с классом .hero-btn ещё несёт браузерную
   рамку и стрелку курсора по умолчанию. */
.hero-btn--submit {
    border: 0;
    cursor: pointer;
}

/* Хаб входа в игру /join/ (2026-10-23): три раздела один под другим,
   между ними линия; подписи серые. В «Создать игру» (2026-10-26, по
   запросу автора) — заголовок «Рекомендуем для Вас:», список прямых
   ссылок на игры и ссылка «Каталог игр» (до того — серая кнопка во всю
   ширину, .join-hub-btn). */
.join-hub {
    max-width: 460px;
}
.join-hub-section + .join-hub-section {
    margin-top: 28px;
    padding-top: 24px;
    border-top: 1px solid #e5e7eb;
}
.join-hub-hint {
    margin: 8px 0 0;
    color: #656d76;
    font-size: 14px;
    line-height: 1.45;
}
.join-hub-reco-title {
    margin: 14px 0 6px;
    font-weight: 700;
    color: var(--text);
}
.join-hub-reco {
    list-style: none;
    margin: 0 0 12px;
    padding: 0;
}
.join-hub-reco li { margin: 4px 0; }
.join-hub-reco a,
.join-hub-link {
    color: var(--accent-text);
    font-weight: 600;
    text-decoration: none;
}
.join-hub-reco a:hover,
.join-hub-link:hover { text-decoration: underline; }
.join-hub-link { display: inline-block; margin-top: 4px; }

.room-code-input {
    text-transform: uppercase;
    font-size: 24px;
    text-align: center;
    letter-spacing: 4px;
}

/* ── Лобби комнаты (lobby.html, qa_lobby.html) ───────────────────────────
   Оба шаблона держат собственный <html> и не наследуют base_public.html,
   поэтому класс public-page на <body> у них проставлен вручную, а шапка
   подключается тем же партиалом public_header.html. Разметка обоих лобби
   совпадает почти дословно, поэтому классы общие: правка одного правила
   меняет оба режима сразу. */
.lobby-shell {
    max-width: 600px;
    margin: 0 auto;
}

.lobby-title {
    margin: 0 0 16px;
}

.lobby-subtitle {
    margin: 0 0 8px;
}

.lobby-code {
    background: var(--surface);
    border: 1px solid var(--border);
    padding: 16px;
    border-radius: 10px;
    margin-bottom: 20px;
    text-align: center;
    cursor: pointer;
}

.lobby-code-label {
    font-size: 13px;
    color: var(--text-dim);
}

.lobby-code-value {
    font-size: 36px;
    font-weight: 700;
    letter-spacing: 6px;
    /* Разрядка дописывает пустоту после последнего знака — без
       компенсации код визуально уезжает влево от центра плашки. */
    margin-right: -6px;
}

.lobby-note {
    background: rgba(202, 138, 4, 0.14);
    border: 1px solid rgba(202, 138, 4, 0.3);
    color: var(--text);
    padding: 12px 16px;
    border-radius: 10px;
    margin-bottom: 20px;
    font-size: 13px;
    line-height: 1.55;
}

.lobby-field {
    margin-bottom: 16px;
}

.lobby-field .form-select {
    width: 100%;
}

.lobby-actions {
    display: flex;
    gap: 12px;
    margin-top: 24px;
}

.lobby-action {
    flex: 1;
}

.lobby-enter {
    margin-top: 24px;
}

.btn-lg--block {
    display: block;
}

.lobby-panel {
    text-align: center;
    padding: 24px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    margin-top: 24px;
}

.lobby-panel--waiting {
    padding: 14px;
    color: var(--text-dim);
    font-size: 14px;
}

.lobby-panel-title {
    font-size: 18px;
    font-weight: 600;
    margin: 0 0 12px;
}

.lobby-panel-link {
    color: var(--accent-text);
    text-decoration: none;
    font-weight: 600;
    font-size: 14px;
}

/* Комната закрыта из-за снятия сценария с публикации (2026-09-30):
   рамка предупреждения, а не обычной панели, — команда собралась и
   играть сегодня не будет, это не рядовое «игра завершена». */
.lobby-panel--suspended {
    background: rgba(180, 83, 9, 0.16);
    border-color: rgba(240, 217, 154, 0.35);
}

/* Цвета те же, что у предупреждения на странице сценария
   (.detail-notice--warning), — плашка «игра отменена» из того же ряда. */
.lobby-panel--suspended .lobby-panel-title {
    color: #fbbf24;
}

.lobby-panel-text {
    margin: 0 0 10px;
    font-size: 15px;
    line-height: 1.5;
    color: var(--text);
}

.btn-lg--danger:hover {
    background: var(--danger-bg-hover);
}

/* Кнопка «Начать игру» до готовности: погашена и не кликается. */
#start-game-btn:disabled {
    opacity: 0.5;
    pointer-events: none;
}

/* ── Фоновый слой лендинга: лента обложек, проступающих из темноты ───────
   Обложки берутся из базы (см. landing() → _landing_background_images) в
   случайном порядке и идут ПОДРЯД сверху вниз, деля высоту страницы поровну.
   Картинка показывается целиком (background-size: contain) — не обрезана ни
   рамкой блока, ни краем экрана.

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

   Слой лежит внутри .app-shell (шаблонный блок content — его потомок), а тот
   на лендинге сужен до колонки первого экрана. Поэтому по вертикали фон
   привязан к контенту, а по горизонтали вырван на всю ширину окна тем же
   приёмом, что и hero: left: calc(50% - 50vw) + width: 100vw.
   z-index: -1, а не 0: .app-shell со своим z-index создаёт контекст
   наложения, и нулевой слой оказался бы ПОВЕРХ непозиционированных секций.
   Отрицательный уводит фон под контент, но не под саму страницу — у
   .app-shell нет заливки, и фон остаётся виден. */
body.landing-page {
    position: relative;
    overflow-x: hidden;
    overflow-x: clip;
}

.landing-bg {
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(50% - 50vw);
    width: 100vw;
    z-index: -1;
    overflow: hidden;
    pointer-events: none;
}

body.landing-page .app-shell,
body.cover-strip .app-shell {
    position: relative;
    z-index: 1;
}
/* Посадочные и статьи «Идей» (2026-10-23, по запросу автора): та же лента
   фоном. Слой .landing-bg лежит в .seo-page — она и задаёт ему высоту;
   body.cover-strip даёт .app-shell свой контекст наложения, как на
   главной. */
.seo-page {
    position: relative;
}

/* Обёртка всего, что ниже первого экрана. Существует ради фона: слой
   привязан к ней, поэтому лента начинается ровно под hero. Пока слой шёл от
   верха документа, hero высотой в экран накрывал его начало, и первая же
   видимая картинка была подрезана сверху. */
.landing-body {
    position: relative;
}

/* Картинка ровно во всю ширину окна, высота — по её собственной пропорции:
   ничего не обрезается, а обложка у́же экрана растягивается до него. Именно
   поэтому здесь <img> с height: auto, а не background: background-size даёт
   выбор только между «обрезать» (cover) и «оставить поля» (contain).

   Высоту ленты никто не подгоняет под страницу — она набирается из реальных
   высот картинок, а лишнее срезает overflow контейнера. */
.landing-bg-item {
    display: block;
    width: 100%;
    height: auto;
    opacity: 0.22;
    filter: blur(1px) saturate(0.8);
    /* Переход через чёрный: верх и низ каждой картинки гаснут в фон, поэтому
       стыка между соседними не видно. */
    -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 20%, #000 80%, transparent 100%);
    mask-image: linear-gradient(to bottom, transparent 0%, #000 20%, #000 80%, transparent 100%);
}

@media (max-width: 860px) {
    .landing-bg-item {
        opacity: 0.16;
    }
}

/* Кому трудно от шума — оставляем фон почти чистым. */
@media (prefers-reduced-motion: reduce) {
    .landing-bg-item {
        opacity: 0.1;
    }
}

/* ── Прокрутка лендинга: без привязки (2026-10-09, по запросу автора) ────
   Привязки к началу секций здесь больше нет. Была мягкая (proximity) плюс
   scroll-snap-stop: always у каждой секции — страница останавливалась у
   каждого заголовка, и длинный лендинг листался лестницей. Автор просил
   оставить «только базовое минимальное»: прокрутка целиком в руках
   человека.

   Осталось ровно одно — scroll-padding-top: это НЕ притягивание, а отступ
   для переходов по якорям (#about, #how-it-goes, ссылки подвала). Без него
   браузер ставит цель ровно под край окна, и заголовок закрывает
   фиксированная шапка лендинга.

   Привязка снята и у ГОРИЗОНТАЛЬНЫХ лент (2026-10-10, по замечанию
   автора: «в районе блока Сценарии по-прежнему срабатывает притягивание
   по вертикальной прокрутке»). Вертикальной привязки на странице к тому
   моменту уже не было — проверено и по CSSOM, и опытом: прокрутка,
   поставленная рядом с началом секции, никуда не уезжала. Притягивало
   именно `scroll-snap-type: x proximity` у лент карточек: при вертикальном
   свайпе палец почти всегда уводит и вбок, лента защёлкивает ближайшую
   карточку, и рывок читается как «страницу подтянуло». Листание лент это
   не сломало — они прокручиваются и стрелками, и пальцем. */
html:has(body.landing-page) {
    scroll-padding-top: 96px;
}

/* ── Полка сценариев: шире колонки, но не во всю страницу ────────────────
   Контент лендинга сведён к колонке первого экрана, но карточкам сценариев
   в ней тесно: в 712px помещается две, а обложки — главное на странице.
   Секция выходит за колонку тем же приёмом, что hero и фоновый слой
   (margin съедает половину разницы), но берёт не всю ширину окна, а 3/4.

   Ширина выбрана не на глаз: три карточки в 75vw при экране 1400px дают по
   336px — тот же размер, что у карточки в колонке контента (712px на две).
   Карточки по всей странице выглядят одинаковыми, хотя лежат в блоках
   разной ширины; меняя одно из чисел, надо сверять второе. */
body.landing-page .section--wide {
    --showcase-w: 75vw;
    width: var(--showcase-w);
    margin-left: calc(50% - var(--showcase-w) / 2);
    margin-right: calc(50% - var(--showcase-w) / 2);
}

/* 861–1100px: 3/4 экрана дают карточку у́же 230px, и анонс в ней сжимается
   в колонку из двух слов. Там полка занимает почти всю ширину. */
@media (max-width: 1100px) {
    body.landing-page .section--wide {
        --showcase-w: 94vw;
    }
}

/* ── Призыв над полкой «Актуальные игры» (2026-10-12, текст автора) ─────
   Две строки: вопрос, с которым человек и пришёл («Друзья собрались. Во
   что поиграть?»), и ответ форматом. Вопрос крупнее заголовка секции
   намеренно — заголовок называет раздел, а читают здесь вопрос; ответ
   тише, тоном подписи. Ширина у́же полки: строка в 3/4 экрана растянулась
   бы на всю ширину и перестала читаться репликой. */
/* Заголовок полки — обычный .section-title (2026-10-22, по запросу
   автора «уменьши до стандартного, как все остальные»; с 2026-10-12 по
   2026-10-22 крупный .showcase-title clamp(30px, 4vw, 46px) под вопрос в
   две строки «Друзья собрались. Во что поиграть?»). Подзаголовок — одной
   строкой на мониторе, на телефоне по естественному переносу. */
body.landing-page .showcase-lead {
    max-width: 760px;
    margin: 0 auto 28px;
    text-align: center;
    font-size: clamp(15px, 1.6vw, 19px);
    line-height: 1.45;
    color: var(--text-muted);
}
@media (max-width: 480px) {
    body.landing-page .showcase-lead {
        margin-bottom: 22px;
    }
}

/* Заголовок широкой секции — по центру (2026-10-09, по запросу автора:
   «заголовок блока Сценарии выровнять по центру»). Прежде он держался на
   левой линии h1 первого экрана ради единой вертикали у всей страницы, но
   над лентой карточек, уходящей в обе стороны, левая привязка читалась
   сбоем вёрстки, а не линией. Теперь так же, как у секций в колонке. */
body.landing-page .section--wide > .section-title {
    text-align: center;
}

/* Три карточки в ряд вместо общих для лендинга двух — правило перекрывает
   их, поэтому селектор длиннее. Число карточек в выборке (`landing()`)
   кратно трём: иначе последний ряд остаётся наполовину пустым. */
@media (min-width: 861px) {
    body.landing-page .section--wide .cards-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

/* Заголовки секций в колонке контента — по центру; у широких секций то же
   самое задано выше своим правилом. */
body.landing-page .section--centered > .section-title {
    text-align: center;
}

/* ── Секция лендинга не короче экрана (2026-10-02) ───────────────────────
   Прокрутка лендинга привязана к началам секций, но высота у секций
   своя: та, что короче окна, оставляла снизу полосу, и в ней читалось
   начало СЛЕДУЮЩЕЙ — «Как проходит игра» выглядывал из-под «Какую игру
   выбрать», «Кому подходит» из-под «Попробуйте бесплатно». Пара меняется
   от ширины окна: на мониторе выглядывал один сосед, на телефоне другой,
   поэтому лечится не каждый случай по отдельности, а общим правилом.

   Минимум — свободная часть окна (те же 96px вычета, что в
   scroll-padding-top привязки), содержимое по центру. Секция выше окна
   остаётся как есть: её контент не ужать, и сосед под ней появится
   только когда её саму дочитают.

   Экран «Теперь у вас есть КВЕСТОТЕКА» (.pitch-screen) со своей высотой
   был исключён из правила; он снят 2026-10-15 вместе с «Частыми
   вопросами», по запросу автора. */
body.landing-page .landing-body > .section {
    /* svh (с откатом на vh для старых браузеров) — высота окна при
       показанных панелях браузера, постоянная: см. .hero, высоты на
       лендинге не следуют за панелью телефона. */
    min-height: calc(100vh - 96px);
    min-height: calc(100svh - 96px);
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

/* ── Подвал прижат к низу экрана на коротких страницах ───────────────────
   Страницы входа, регистрации и пустой инфо-страницы короче экрана, и
   подвал на них поднимался к середине, оставляя под собой пустое полотно.
   .app-shell становится flex-колонкой высотой не меньше экрана, а подвал
   забирает весь свободный остаток через margin-top: auto.

   Только для публичных страниц: у кабинета своя оболочка (.cabinet-shell),
   и подвала там нет вовсе. */
body.public-page .app-shell {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    /* Подвал — последний элемент оболочки и сам полоса до края окна:
       нижний отступ оболочки оставлял бы под ним пустую полоску фона. */
    padding-bottom: 0;
}
/* Подвал растянут на 100vw (см. .public-footer), а 100vw включает полосу
   прокрутки — без clip на каждой прокручиваемой странице появлялся бы
   горизонтальный скролл на её ширину. Лендинг делает то же для hero. */
body.public-page {
    overflow-x: hidden;
    overflow-x: clip;
}

body.public-page .app-shell > .public-footer {
    margin-top: auto;
}

/* Удаление аккаунта в «Личных данных» (2026-09-24): раскрываемый блок,
   чтобы опасное действие не стояло рядом с «Сохранить». */
.account-delete-summary {
    cursor: pointer; font-size: 13px; color: #c62828; list-style: none;
    display: inline-flex; align-items: center; gap: 6px;
}
.account-delete-summary::-webkit-details-marker { display: none; }
.account-delete-summary::before { content: '▸'; display: inline-block; font-size: 12px; transition: transform .15s; }
.account-delete[open] > .account-delete-summary::before { transform: rotate(90deg); }
.account-delete-body {
    margin-top: 10px; padding: 14px 16px; border: 1px solid #f3c2c2; border-radius: 10px; background: #fff7f7;
}
.btn-danger {
    padding: 8px 16px; border: 1px solid #c62828; border-radius: 8px; background: #fff;
    color: #c62828; font-weight: 600; cursor: pointer; font: inherit;
}
.btn-danger:hover { background: #c62828; color: #fff; }
.btn-danger:disabled { opacity: .6; cursor: default; }

/* Фидбэки админки (2026-09-24): пометка замечаний тестировщика, ссылка на сценарий. */
.fb-test-badge {
    display: inline-block; font-size: 10px; font-weight: 700; padding: 2px 7px; border-radius: 5px;
    background: #fff7ed; color: #b45309; border: 1px solid #f59e0b; letter-spacing: .03em; white-space: nowrap;
}
.fb-scenario-link { color: inherit; text-decoration: none; }
.fb-scenario-link:hover { text-decoration: underline; color: var(--accent, #2da44e); }

/* Раздел «Тестирование» тестировщика (2026-09-24). */
.scenarios-grid-list--testing {
    --sg-cols: 165px minmax(150px, 1fr) 70px 90px 84px 96px 116px;
}
.scenarios-grid-list--testing .sg-head { padding-right: 16px; }
.testing-row { flex-direction: column; align-items: stretch; }
.testing-row > .sg-link { width: 100%; }
.testing-title-link { color: inherit; text-decoration: none; }
.testing-title-link:hover { text-decoration: underline; }
.testing-mark { display: inline-block; padding: 3px 10px; border-radius: 12px; font-size: 12px; font-weight: 600; white-space: nowrap; }
.testing-mark--new { background: #fef3c7; color: #92400e; }
.testing-mark--passed { background: #d1fae5; color: #065f46; }
.testing-btn { padding: 6px 12px; font-size: 13px; text-decoration: none; }
.testing-remarks { margin-top: 10px; padding-top: 10px; border-top: 1px dashed #d0d7de; }
.testing-remarks-summary { cursor: pointer; font-size: 13px; font-weight: 600; color: #656d76; list-style: none; display: inline-flex; gap: 6px; align-items: center; }
.testing-remarks-summary::-webkit-details-marker { display: none; }
.testing-remarks-summary::before { content: '▸'; display: inline-block; font-size: 12px; transition: transform .15s; }
.testing-remarks[open] > .testing-remarks-summary::before { transform: rotate(90deg); }
.testing-remarks .rv-remarks-body { margin-top: 8px; display: flex; flex-direction: column; gap: 8px; }
.testing-remarks .rv-remark { padding: 8px 10px; background: #f6f8fa; border-radius: 6px; }
.testing-remarks .rv-remark-label { font-size: 13px; font-weight: 700; margin-bottom: 4px; }
.testing-remarks .rv-remark-giveup { font-size: 11px; font-weight: 700; color: #c62828; background: #fdecec; padding: 1px 6px; border-radius: 5px; margin-left: 6px; }
.testing-remarks .rv-remark-text { font-size: 14px; line-height: 1.5; white-space: pre-line; }
.testing-remark-date { font-weight: 400; color: #656d76; font-size: 12px; margin-left: 6px; }

/* Лента наблюдений «Детектива» (2026-09-25) — рука игрока показывается не
   сплошным абзацем, а отдельными записями: каждая читается как одна
   мысль, и на каждой может стоять личная пометка. Рендер общий для игры и
   тест-режима, см. static/editor/js/qa_facts.js. Игровые экраны светлые
   (body без .public-page), поэтому цвета здесь константами, как у
   соседних игровых стилей. */
.qa-note {
    position: relative;
    background: #ffffff;
    border: 1px solid #dfe3e8;
    border-left: 3px solid #cbd2d9;
    border-radius: 8px;
    padding: 10px 12px;
    margin-bottom: 8px;
    cursor: pointer;
    transition: padding-right 0.15s ease, border-left-color 0.15s ease;
}
.qa-note:last-child { margin-bottom: 0; }
.qa-note-text { line-height: 1.5; white-space: pre-line; }
.qa-note-text--plain { margin-top: 4px; }
.qa-note-body { transition: opacity 0.15s ease; }
.qa-note--key { border-left-color: #2da44e; }
/* «Шум» не зачёркиваем: запись придётся перечитывать, когда версия
   команды поменяется. Гасим — остаётся читаемой, но не тянет взгляд. */
.qa-note--noise .qa-note-body { opacity: 0.5; }
.qa-note--noise { border-left-color: #c62828; }
/* Значков на записи нет (2026-10-13): касание переключает пометку по
   кругу — нет → важное → шум → нет (см. qa_facts.js). */
.qa-notes-hint {
    font-size: 12px;
    color: #8a94a0;
    margin: 0 0 8px;
    line-height: 1.4;
}
.qa-adapt-badge { line-height: 1; margin-bottom: 3px; }
.qa-adapt-name { font-weight: 600; margin-bottom: 4px; }

/* Архив «Все истории» (2026-10-13, по запросу автора): лента пройденных
   раундов в сером блоке без полосы слева (полоса внешнего блока снята в
   тот же день по запросу автора — полосы остались только у блоков фактов
   рук); плашка, пройденный вопрос и блоки фактов — одна серая тема, чтобы
   архив читался единым целым и не спорил с синей «Новой историей» выше.
   Показывается, только когда пройденные раунды есть (см. QAHistory.render). */
.qa-past-block {
    background: #eceff3; border-radius: 4px;
    padding: 8px 10px; margin-top: 14px;
}
.qa-past-block > .qa-adapt-badge { margin-bottom: 10px; }
/* Архив — <details>, свёрнут по умолчанию (2026-10-23, по запросу автора);
   заголовок-summary — та же серая плашка «Все истории» со стрелкой. */
.qa-past-block > summary.qa-adapt-badge { cursor: pointer; list-style: none; margin-bottom: 0; }
.qa-past-block > summary.qa-adapt-badge::-webkit-details-marker { display: none; }
.qa-past-block > summary.qa-adapt-badge::after { content: " ▸"; font-size: 11px; color: #6b7280; }
.qa-past-block[open] > summary.qa-adapt-badge { margin-bottom: 10px; }
.qa-past-block[open] > summary.qa-adapt-badge::after { content: " ▾"; }
/* Название игры — заголовком вкладки «Легенда» (2026-10-23, по запросу
   автора; прежде стояло в шапке экрана). */
.qa-game-title { margin: 0 0 10px; font-size: 20px; font-weight: 700; color: #1f2933; }
.qa-past-badge {
    display: inline-block; font-size: 10px; font-weight: 700; color: #374151;
    background: #d1d5db; padding: 1px 7px; border-radius: 9px;
    text-transform: uppercase; letter-spacing: 0.4px; vertical-align: middle;
}
/* Пройденный вопрос — отдельный серый блок «Вопрос N — название»;
   взятая подсказка под ним — такой же серый блок (2026-10-13, по запросу
   автора: оранжевая карточка подсказки выделялась в архиве). */
.qa-past-question, .qa-past-hint {
    background: #dfe3e8; border-radius: 8px; padding: 12px 16px; margin-bottom: 8px;
}
/* Рука пройденного вопроса — серые блоки фактов лежат в раунде прямо
   под блоком вопроса: без общей обёртки руки (серый блок `.qa-past-hand`
   жил несколько часов 2026-10-13 и снят по запросу автора) и без светлой
   карточки .game-item вокруг каждого блока (снята в тот же день по тому
   же запросу — обёртка .qa-past-story держит только отступ). Блок фактов
   каждой руки — .qa-adapt-past: как синяя .qa-adapt-highlight, только
   серым, с плашкой «История вопроса N» чуть темнее своего фона. */
.qa-past-story { margin-bottom: 8px; }
.qa-adapt-past { background: #dde1e6; border-left: 3px solid #9ca3af; border-radius: 4px; padding: 8px 10px; }
.qa-adapt-past .qa-past-badge { background: #c9cfd6; }
/* Руки ролей одной лентой (разминка, 2026-10-13): блок на роль без
   заголовка — имя роли стоит на карточке. */
.qa-hand-block { margin-bottom: 10px; }
.qa-story-owner { font-size: 12px; font-weight: 700; color: #6b7280; margin-bottom: 4px; }
/* Секция «Как проходит игра» (2026-09-28) — ролик о пути команды от
   кнопки «Играть» до диплома. До клика на месте плеера стоит постер,
   поэтому .video-facade и держит соотношение сторон сам (aspect-ratio
   задаёт шаблон по размерам файла): место занято сразу, и страница не
   прыгает, когда картинка дойдёт. */
.video-facade {
    position: relative;
    width: 100%;
    max-width: 880px;
    margin: 0 auto;
    border: 1px solid var(--border);
    border-radius: 14px;
    overflow: hidden;
    background: #000;
    cursor: pointer;
}
/* Вертикальная запись экрана телефона считается от ВЫСОТЫ экрана, а не
   от ширины колонки: у кадра 9:20 ширина в 100% дала бы высоту больше
   окна — на телефоне ролик закрыл бы и заголовок секции, и шаги. Высоту
   задаёт шаблон (min(66vh, 620px)), ширину — соотношение сторон файла,
   поэтому чёрных полей нет ни при какой пропорции. Мелкий текст внутри
   читают не здесь, а в полноэкранном режиме плеера. */
.video-facade--portrait { max-width: 100%; }
/* Собственного scroll-margin-top у секции больше нет (2026-10-02). Он
   стоял здесь ради перехода по ссылке «Как проходит игра» со страницы
   сценария и из лобби — чтобы заголовок не уезжал под липкую шапку, — но
   то же самое делает scroll-padding-top на лендинге, и два отступа
   складывались: привязка прокрутки останавливалась на 92px ВЫШЕ начала
   секции, и сверху в кадр лезли карточки предыдущей. */
.video-facade img,
.video-facade video {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
}
.video-play {
    position: absolute;
    inset: 0;
    margin: auto;
    width: 64px;
    height: 64px;
    padding: 0;
    border: 0;
    background: transparent;
    cursor: pointer;
    transition: transform 0.15s ease;
}
.video-facade:hover .video-play { transform: scale(1.06); }
.section--centered .section-lead { margin-left: auto; margin-right: auto; }
/* ── «Как проходит игра»: лента карточек по высоте ролика (2026-10-09) ──
   Прежде ролик стоял слева, а пять шагов — списком справа; блок лежал в
   середине страницы. Теперь это ВТОРОЙ экран, и шаги читаются карточками:
   крупный значок, крупная строка, лента листается вправо, последняя
   карточка — тот же ролик в тех же размерах.

   Высота ленты — одна переменная на все карточки, включая ролик: он
   вертикальный (720×1602), и это он задаёт меру. Ширина карточки-ролика
   считается из этой высоты по пропорции кадра (--video-ratio приходит из
   разметки, значения — из файла), поэтому чёрных полей нет ни при какой
   записи. 62vh, а не 100%: над лентой стоит заголовок секции, и карточка
   во всю высоту окна выдавила бы его за край. */
.how-cards {
    --how-h: min(62vh, 560px);
    --how-card-w: 290px;
    display: flex;
    flex-wrap: nowrap;
    gap: 20px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    -ms-overflow-style: none;
    padding: 4px 0;
}
.how-cards::-webkit-scrollbar { display: none; }
.how-card {
    flex: 0 0 var(--how-card-w);
    height: var(--how-h);
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 28px;
    padding: 28px 24px;
    border-radius: 20px;
    border: 1px solid var(--about-border, var(--border));
    background: var(--about-bg, var(--surface));
    text-align: center;
}
.how-card p {
    margin: 0;
    font-size: 22px;
    line-height: 1.35;
    font-weight: 700;
    color: var(--text);
}
/* Значок крупный намеренно: карточку узнают по нему раньше, чем прочтут
   строку, — на это и расчёт при листании. */
.how-card-icon {
    width: 132px;
    height: 132px;
    flex: 0 0 auto;
}
/* Карточка-ролик (2026-10-14, по запросу автора) — такая же, как карточки
   шагов: значок и строка, без постера. Фасад занимает всю карточку и
   ловит клик; после клика (.is-open, video_facade.js) он растягивается
   поверх карточки чёрным полем, и ролик идёт внутри целиком (contain —
   запись вертикальная, карточка нет). Прежде карточкой был сам постер с
   шириной по пропорции кадра. */
.how-card--video {
    position: relative;
    overflow: hidden;
    cursor: pointer;
}
.how-card--video .video-facade {
    position: static;
    width: 100%;
    height: 100%;
    max-width: none;
    aspect-ratio: auto;
    margin: 0;
    border: 0;
    border-radius: 0;
    background: none;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 28px;
}
.how-card--video .video-facade.is-open {
    position: absolute;
    inset: 0;
    background: #000;
    border-radius: 20px;
}
.how-card--video .video-facade video {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}
@media (max-width: 860px) {
    .how-cards { --how-card-w: 76%; --how-h: min(58vh, 520px); }
    .how-card { gap: 20px; padding: 22px 18px; }
    .how-card p { font-size: 19px; }
    .how-card-icon { width: 104px; height: 104px; }
}
@media (max-width: 480px) {
    .how-cards { --how-card-w: 84%; }
}
/* Ссылка на ролик со страницы сценария и из лобби (2026-09-28) —
   скромная строка, не соперничает с «Создать игру». */
.how-it-goes-link {
    display: inline-block;
    margin: 4px 0 18px;
    color: var(--accent-text);
    text-decoration: none;
    font-size: 14px;
    font-weight: 600;
}
.how-it-goes-link:hover { text-decoration: underline; }


/* ───────── Разбор вопроса и лента раундов «Детектива» ─────────
   Разбор появился в тест-режиме (2026-09-24) и с 2026-09-29 работает в
   живой партии тоже, поэтому стили переехали из qa_test_game.html сюда —
   иначе их пришлось бы держать в двух шаблонах. */
/* «🏳️ Пропустить вопрос» (до 2026-10-30 «Сдаюсь!», утром 2026-10-30 —
   «Следующий вопрос») — того же размера, что «Подсказка» (.qa-hint-btn в
   qa_game.html/qa_test_game.html), по запросу автора. */
.qa-giveup-btn {
    padding: 8px 14px; border: 1.5px solid #c8ccd1; border-radius: 8px;
    background: #fff; color: #8a94a0; font-weight: 600; font-size: 13px; cursor: pointer;
}
.qa-giveup-btn:hover { color: #c62828; border-color: #e0a0a0; }
/* Кнопки внизу легенды (2026-10-30, по запросу автора): «Вопрос N» и «Факты». */
.qa-legend-nav { margin-top: 14px; }
.qa-legend-go {
    padding: 12px 16px; border: 1.5px solid #6366f1; border-radius: 10px;
    background: #fff; color: #4f46e5; font-weight: 700; font-size: 15px; cursor: pointer;
}
.qa-legend-go--q { background: #6366f1; color: #fff; }
.qa-legend-go:hover { filter: brightness(0.96); }
.qa-review { border: 2px solid #2da44e; }
.qa-review--giveup { border-color: #c62828; }
.qa-review-head {
    font-size: 12px; font-weight: 700; text-transform: uppercase;
    letter-spacing: .04em; color: #2da44e; margin-bottom: 4px;
}
.qa-review--giveup .qa-review-head { color: #c62828; }
.qa-review-sub { font-size: 12px; font-weight: 700; color: #5f6f82; margin: 12px 0 4px; }
.qa-review-text { line-height: 1.5; white-space: pre-line; }
.qa-review-parts { margin: 0; padding-left: 18px; line-height: 1.5; }
/* Новое время партии с учётом ставки последнего ответа: команда снимает
   паузу, уже зная, с чем возвращается в игру. */
.qa-review-time { margin-top: 10px; font-size: 13px; color: #5f6f82; }
.qa-review-note {
    width: 100%; min-height: 80px; margin-top: 6px; padding: 8px 10px;
    border: 1px solid #c8ccd1; border-radius: 8px; font: inherit;
    resize: vertical; box-sizing: border-box;
}
.qa-review-actions { margin-top: 8px; }
/* «Продолжить» — обратное действие паузы, поэтому зелёная и со стрелкой
   воспроизведения (2026-09-29, по запросу автора): раньше это была
   неприметная серая «Пропустить», и её принимали за отказ от разбора. */
.qa-review-go {
    padding: 10px 16px; border: 0; border-radius: 8px;
    background: #2da44e; color: #fff; font-weight: 600; font-size: 14px; cursor: pointer;
}
.qa-review-go:hover { background: #218838; }
.qa-review-go:disabled { opacity: .6; cursor: default; }

/* Раунд в ленте: вопрос, подсказка, наблюдения и ответы одним блоком. */
.qa-round { margin-bottom: 18px; }
.qa-log { margin: 0 2px 4px; }
.qa-log-row {
    display: flex; gap: 8px; align-items: baseline;
    padding: 6px 10px; border-radius: 8px; margin-bottom: 4px;
    font-size: 14px; line-height: 1.45;
}
.qa-log-row--ok { background: rgba(45, 164, 78, 0.12); }
.qa-log-row--no { background: rgba(198, 40, 40, 0.10); }
.qa-log-name { flex: 0 0 auto; font-weight: 600; }
.qa-log-row--ok .qa-log-name { color: #1a7f37; }
.qa-log-row--no .qa-log-name { color: #c62828; }
.qa-log-text { min-width: 0; word-break: break-word; }


/* Ряд кнопок (2026-09-29, правило проекта — .rules/RULES.md §7): не
   помещаются в ряд — переносятся; у́же ряда — растягиваются по ширине,
   и перенесённая кнопка занимает строку целиком. Поле ввода в таком
   ряду получает min-width: 0, иначе не сжимается ниже своей внутренней
   ширины и выталкивает кнопку за рамку. */
.btn-row {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
}
.btn-row > * {
    flex: 1 1 auto;
    min-width: 0;
}
/* Элемент, которому растягиваться не нужно (значок, счётчик). */
.btn-row > .btn-row-fixed { flex: 0 0 auto; }

/* Подпись ступени на карточке сценария. С 2026-10-09 она стоит ВМЕСТО
   жанровой плашки, а не рядом с ней, поэтому и отступы у неё теперь те же
   (2026-10-10, по замечанию автора: «плашка для ступеней и обычная
   отличается по положению относительно названия»): прежний margin-left
   сдвигал её вправо, а без нижнего отступа заголовок подходил к ней
   ближе, чем в обычной карточке, — два ряда карточек не выстраивались. */
.card-step-badge {
    display: inline-block;
    margin: 0 0 6px;
    font-size: 11px;
    font-weight: 700;
    color: var(--violet-text, #6d28d9);
    background: var(--violet-soft, #ede9fe);
    padding: 2px 8px;
    border-radius: 6px;
    vertical-align: middle;
}

/* Подписка на паблик ВК (2026-10-13, по запросу автора): блок на финале
   разминки и в «Личных данных» кабинета — заголовок, два виджета ВК
   (подписаться, разрешить сообщения) и запасная ссылка на паблик.
   Цвет — фирменный синий ВК, чтобы блок читался как «это ВКонтакте». */
.vk-subscribe {
    background: #eef4ff; border: 1px solid #c7d7f7; border-radius: 12px;
    padding: 16px 18px; margin: 20px 0; text-align: left; max-width: 560px;
}
.vk-subscribe-title { font-weight: 700; font-size: 16px; color: #0b3d91; margin-bottom: 6px; }
.vk-subscribe-text { font-size: 14px; line-height: 1.5; color: #2b3a55; margin-bottom: 12px; }
/* Кнопки-ссылки в приложение ВК (2026-10-14, по замечанию автора: вход в
   виджете на телефоне не проходит): «Подписаться» ведёт в паблик,
   «Разрешить сообщения» — в диалог с ним (vk.me); на телефоне ссылки
   открывает приложение ВК, где человек уже вошёл. Виджетов ВК больше нет
   (2026-10-15): на мониторе они дублировали кнопки. */
.vk-subscribe-btns { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.vk-subscribe-btn {
    display: inline-block; padding: 9px 16px; border-radius: 8px; font-size: 14px; font-weight: 600;
    background: #0077ff; color: #fff; text-decoration: none;
}
.vk-subscribe-btn--ghost { background: #fff; color: #0077ff; border: 1px solid #0077ff; }
.vk-subscribe-note { font-size: 12px; color: #4b5563; margin-bottom: 8px; }
.vk-subscribe-link { display: inline-block; font-size: 14px; font-weight: 600; color: #0077ff; text-decoration: none; }
.vk-subscribe-link:hover { text-decoration: underline; }
#game-finished-block .vk-subscribe { margin: 20px auto 0; }
/* Плашки финала (2026-10-30, по запросу автора): бонусы за обучение
   начислены этой партией (зелёная) и просьба гостю авторизоваться
   (янтарная). Текст — с сервера (onboarding.BONUS_GRANTED_HTML,
   guest_merge.FINAL_NOTE_HTML), ссылки внутри наследуют цвет. */
.qa-final-note {
    margin-top: 16px; padding: 14px 16px; border-radius: 10px;
    text-align: left; line-height: 1.6; font-size: 15px;
}
.qa-final-note a { color: inherit; font-weight: 700; text-decoration: underline; }
.qa-final-note--bonus { background: #ecfdf5; border: 1px solid #6ee7b7; color: #065f46; }
.qa-final-note--guest { background: #fff7ed; border: 1px solid #fdba74; color: #7c2d12; }
/* Вкладка «Паблик ВК» в кабинете администратора: посты и рассылка. */
.vk-panel-status { display: flex; flex-wrap: wrap; gap: 8px 18px; font-size: 13px; color: #4b5563; margin: 6px 0 14px; }
.vk-panel-status b { color: #111827; }
.vk-notice { border-radius: 8px; padding: 10px 14px; margin: 0 0 14px; font-size: 14px; background: #dcfce7; color: #14532d; }
.vk-notice--error { background: #fee2e2; color: #7f1d1d; }
.vk-post-form { max-width: 640px; display: flex; flex-direction: column; gap: 10px; margin-bottom: 22px; }
.vk-post-form textarea { min-height: 160px; }
.vk-post-form .vk-post-row { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.vk-post-preview { max-width: 220px; border-radius: 8px; display: block; }
.vk-post-list { display: flex; flex-direction: column; gap: 10px; margin-bottom: 22px; }
.vk-post-item { background: #f3f4f6; border-radius: 10px; padding: 12px 14px; }
/* POS-материалы (2026-10-26): список QR-кодов — превью слева, подпись,
   адрес, кнопки скачивания и удаления. */
.pos-list { display: flex; flex-direction: column; gap: 10px; max-width: 760px; }
.pos-item { display: flex; gap: 14px; align-items: flex-start; background: #f3f4f6; border-radius: 10px; padding: 12px 14px; }
.pos-item-qr { flex: none; width: 112px; height: 112px; background: #fff; border-radius: 8px; padding: 6px; box-sizing: border-box; }
.pos-item-qr svg { width: 100%; height: 100%; display: block; }
.pos-item-body { min-width: 0; flex: 1; }
.pos-item-title { font-weight: 700; }
.pos-item-url { font-size: 13px; word-break: break-all; color: var(--accent-text); }
.pos-item-meta { font-size: 12px; color: #6b7280; margin-top: 2px; }
.pos-item form { display: inline; }
@media (max-width: 560px) {
    .pos-item { flex-direction: column; }
}
.vk-post-item--error { background: #fee2e2; }
.vk-post-item--published { background: #ecfdf5; }
.vk-post-item--scheduled { background: #eff6ff; }
.vk-post-meta { font-size: 12px; color: #6b7280; margin-bottom: 6px; display: flex; flex-wrap: wrap; gap: 6px 14px; }
.vk-post-text { white-space: pre-wrap; font-size: 14px; line-height: 1.45; }
.vk-post-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.vk-post-actions form { display: inline; }

/* Вкладки администратора группами (2026-10-13, по запросу автора): на
   узком экране — группы кнопок с заголовками над панелями; на широком
   (>768px) кнопки спрятаны, а то же дерево живёт в сайдбаре под пунктом
   «Администратор» (.cabinet-subnav) и открыто, пока он активен. */
.admin-tab-group { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; width: 100%; margin-bottom: 6px; }
.admin-tab-group-title { width: 100%; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; color: #6b7280; margin: 4px 0 2px; }
.cabinet-subnav { display: none; margin: 2px 0 4px 8px; padding-left: 8px; border-left: 2px solid #d1d5db; }
.cabinet-nav-item.active[data-section="admin"] + .cabinet-subnav { display: block; }
.cabinet-subnav-group { margin-bottom: 8px; }
.cabinet-subnav-title { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; color: #6b7280; padding: 6px 8px 2px; }
.cabinet-subnav-item { display: block; padding: 6px 10px; border-radius: 6px; font-size: 13px; font-weight: 600; color: var(--text); text-decoration: none; }
.cabinet-subnav-item:hover { background: var(--surface-3); }
.cabinet-subnav-item.active { background: #f1f3f5; color: #1d4ed8; }
@media (min-width: 769px) {
    .admin-tabs { display: none; }
}
@media (max-width: 768px) {
    .cabinet-subnav { display: none !important; }
}
/* Черновик статьи «Идей» — плашка над шапкой для администратора. */
.idea-draft-note { background: #fef3c7; color: #78350f; border-radius: 8px; padding: 10px 14px; margin: 0 0 12px; font-size: 14px; }

/* ── Окно перед созданием комнаты (2026-10-13, по решению автора) ────────
   Страница сценария: «необходимо пройти Разминку» (Пройти / Отказаться)
   или «Вы прошли не все бесплатные игры» (Бесплатный сценарий / Играть
   выбранный). Затемнение на весь экран, карточка по центру, кнопки в ряд
   на десктопе и столбиком на телефоне — те же .btn-lg, что под анонсом. */
.gate-modal-overlay {
    position: fixed; inset: 0; z-index: 1000;
    background: rgba(17, 24, 39, 0.55);
    display: flex; align-items: center; justify-content: center;
    padding: 16px;
}
.gate-modal {
    background: var(--surface, #fff); color: var(--text, #1f2937);
    border-radius: 14px; padding: 24px 22px; max-width: 440px; width: 100%;
    box-shadow: 0 18px 50px rgba(0, 0, 0, 0.28);
}
.gate-modal-text { margin: 0 0 18px; font-size: 17px; line-height: 1.45; font-weight: 600; }
.gate-modal-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.gate-modal-actions .btn-lg { flex: 1 1 160px; text-align: center; }

/* ── Полоса с названием над игровым экраном (2026-10-14, по запросу
   автора): во всю ширину окна, в самом верху теста и разминки —
   «КВЕСТОТЕКА — детективные блицы». Это не ссылка: уводить с игрового
   экрана незачем. На экране живой партии полосы нет с 2026-10-23
   (по запросу автора). */
.qa-brand-bar {
    width: 100%; box-sizing: border-box; padding: 7px 12px; margin: 0 0 8px;
    background: #1b2430; color: #fff; text-align: center;
    font-size: 13px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
}

/* Приглашение на разминку (2026-10-14): страница /qa/warmup/ для гостя —
   разметка страницы сценария, под кнопками короткое примечание. */
.warmup-invite-note { margin: 18px 0 0; font-size: 14px; line-height: 1.5; color: var(--text-muted); }
.warmup-invite-note a { color: var(--accent-text); }

/* ── Блок «Обучающие игры» (2026-10-14, по запросу автора): заголовком
   стоит лозунг «Пройди обучающий сценарий и получи N бонусов на игры» —
   он длиннее обычных названий секций, поэтому ограничен по ширине, чтобы
   на широкой полке (75vw) не растягивался в одну строку через весь экран.
   Отдельный призыв с кнопкой перед блоком снят в тот же день. */
.landing-page .training-title { width: fit-content; max-width: 100%; margin: 0 auto 14px; }
/* Заголовок «N бонусов за обучение» мигает рамкой так же, как карточка
   разминки (2026-10-15, по запросу автора): та же анимация
   card-training-blink, заголовок обёрнут в скруглённую плашку по ширине
   текста, чтобы рамке было что обводить. При 0 бонусов заголовок обычный. */
.landing-page .training-title--blink {
    padding: 6px 22px;
    border-radius: 14px;
    box-shadow: 0 0 0 3px var(--accent);
    animation: card-training-blink 1.6s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
    .landing-page .training-title--blink { animation: none; }
}

/* Карточка разминки мигает рамкой по периметру (2026-10-14, по запросу
   автора: «карточка обучения пусть по периметру мигает») — и в блоке
   обучающих игр на лендинге, и в каталоге, где она стоит первой. Рамка —
   box-shadow поверх скруглённого края, цвет кнопки «Обучение»; мигание
   плавное, полтора такта в секунду не нужно — 1.6s туда-обратно, чтобы
   привлекать, а не раздражать. Анимируемое свойство перебивает обычные
   правила box-shadow (наведение, тач-экран). При prefers-reduced-motion
   рамка стоит неподвижно. */
.card.card--training {
    box-shadow: 0 0 0 3px var(--accent);
    animation: card-training-blink 1.6s ease-in-out infinite;
}
@keyframes card-training-blink {
    0%, 100% { box-shadow: 0 0 0 3px var(--accent); }
    50% { box-shadow: 0 0 0 3px rgba(47, 174, 83, 0); }
}
@media (prefers-reduced-motion: reduce) {
    .card.card--training { animation: none; }
}
