/* ============================================
   KSLT — Dashboard (Личный кабинет)
   Prefix: db-
   ============================================ */

/* Hide body until auth check */
body.db-page {
    opacity: 0;
    transition: opacity 0.3s ease;
}
body.db-page.auth-ready {
    opacity: 1;
}

/* ---- Layout ---- */
.db-layout {
    min-height: 100vh;
    padding-top: var(--header-h);
}

/* ---- Header: using site header (floating-header header-hybrid) ---- */

/* ---- Sidebar ---- */
.db-sidebar {
    position: fixed;
    top: var(--header-h);
    left: 0;
    width: 260px;
    height: calc(100vh - var(--header-h));
    background: var(--bg-secondary);
    border-right: var(--bw-hair) solid var(--border-subtle);
    display: flex;
    flex-direction: column;
    padding: var(--space-md);
    overflow-y: auto;
}

.db-sidebar-user {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding-bottom: var(--space-md);
    margin-bottom: var(--space-md);
    border-bottom: var(--bw-hair) solid var(--border-subtle);
}

.db-sidebar-avatar {
    width: 80px;
    height: 80px;
    border-radius: 50%;
    object-fit: cover;
    border: var(--bw-heavy) solid var(--accent);
    margin-bottom: 12px;
    background: var(--bg-elevated);
}

.db-sidebar-avatar-placeholder {
    width: 80px;
    height: 80px;
    border-radius: 50%;
    border: var(--bw-heavy) solid var(--accent);
    margin-bottom: 12px;
    background: var(--bg-elevated);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--fs-2xl);
    color: var(--accent);
}

.db-sidebar-name {
    font-size: var(--fs-base);
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: 4px;
}


/* ТОЛЬКО ПОЛОЖЕНИЕ. Форму и краску даёт .kslt-role в style.css.
   Держала пятую копию бейджа: отступы 3·10 мимо шкалы, отступ сверху
   8 числом вместо --space-2, высота 21 против эталонных 24. */
.db-sidebar-role {
    margin-top: var(--space-2);
}

.db-sidebar-ratings {
    margin-top: 10px;
    display: flex;
    flex-direction: column;
    gap: 4px;
    width: 100%;
}

.db-sidebar-rating-row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    font-size: var(--fs-xs);
}

.db-sidebar-rating-label {
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--accent);
    min-width: 36px;
}

.db-sidebar-rating-value {
    color: var(--text-secondary);
}

.db-sidebar-qr-btn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-top: 10px;
    padding: 5px 12px;
    font-size: var(--fs-xs);
    color: #aaa;
    background: var(--alpha-white-04);
    border: var(--bw-hair) solid var(--alpha-white-12);
    border-radius: var(--radius-full);
    cursor: pointer;
    transition: all 0.2s;
}
.db-sidebar-qr-btn:hover {
    color: var(--accent);
    border-color: var(--accent-glow);
    background: var(--alpha-lime-06);
}
.db-sidebar-qr-btn svg {
    flex-shrink: 0;
}

/* Sidebar Nav */
.db-sidebar-nav {
    list-style: none;
    padding: 0;
    margin: 0;
    flex: 1;
}

.db-sidebar-item {
    margin-bottom: 4px;
}

.db-sidebar-link {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 16px;
    border-radius: var(--radius-md);
    color: var(--text-secondary);
    text-decoration: none;
    font-size: var(--fs-sm);
    transition: var(--transition-fast);
    cursor: pointer;
    border: none;
    background: none;
    width: 100%;
    text-align: left;
}

.db-sidebar-link:hover {
    background: var(--surface-glass-hover);
    color: var(--text-primary);
}

.db-sidebar-link.active {
    background: var(--alpha-lime-06);
    color: var(--accent);
}

.db-sidebar-link svg {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
}

.db-sidebar-link.active svg {
    stroke: var(--accent);
}

/* ---- Mobile Tabs ---- */
.db-mobile-tabs {
    display: none;
}

/* ---- Content ---- */
.db-content {
    margin-left: 260px;
    padding: var(--space-md) var(--space-lg);
    max-width: 900px;
}

.db-section {
    display: none;
}

.db-section.active {
    display: block;
}

.db-section-title {
    font-size: var(--fs-xl);
    font-weight: 700;
    /* Межстрочный ступенью, а не общими 1.5 от body: 26 x 1.5 = 39 — это
       межстрочный АБЗАЦА на заголовке. У публичных H2 ступень 1.1, и
       лестница 86:10 велит ту же */
    line-height: 1.1;
    /* КАПС СНЯТ — 11.10, по глазам Кости: «слишком большие буквы».
       Он приходил не отсюда, а от глобального `h2` (css/style.css:1667):
       кабинет его не задавал и не отменял. На лендинге капс уместен —
       заголовков мало и они крупные; в кабинете их восемь, они служебные
       («История платежей», «Настройки», «Баллы»), и капс там УЖЕ занят
       другим уровнем — подписью поля 12/400. Один приём на два уровня
       читается шумно. Кегль НЕ тронут: 21 на телефоне задаёт общее
       правило сайта (css/style.css:11745), одно на одиннадцать классов.
       Замер: строка «История платежей» 223 капсом против 188 без него. */
    text-transform: none;
    color: var(--text-primary);
    margin-bottom: var(--space-md);
}

/* ---- Card ---- */
.db-card {
    background: var(--bg-card);
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-lg);
    padding: var(--space-md);
    margin-bottom: var(--space-md);
}

.db-card-title {
    font-size: var(--fs-base);
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: var(--space-sm);
    padding-bottom: 12px;
    border-bottom: var(--bw-hair) solid var(--border-subtle);
}

/* ---- Form Fields ---- */
.db-field {
    margin-bottom: 20px;
}

.db-field-label {
    display: block;
    font-size: var(--fs-xs);
    color: var(--text-muted);
    margin-bottom: 4px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

/* INPUT 25:5 ВЕЛИТ ВЫСОТУ ЯВНО: «Height 44px… set explicitly — a select
   computes its height from the option font otherwise and drifts 2-4px».
   Наши поля стояли 43 — ровно тот дрейф, о котором описание предупреждает */
.db-field-input {
    width: 100%;
    min-height: 44px;
    padding: 0 16px;
    background: var(--bg-elevated);
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-sm);
    color: var(--text-primary);
    font-size: var(--fs-base);
    transition: var(--transition-fast);
    box-sizing: border-box;
}

.db-field-input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--alpha-lime-12);
}

/* Только для текстовых полей: псевдокласс :read-only срабатывает и на
   выпадающих списках — они не «редактируемый текст», — и «Пол» с датой
   рождения выглядели тусклыми, будто недоступны, хотя работают. */
input.db-field-input:read-only,
textarea.db-field-input:read-only {
    color: var(--text-muted);
    cursor: default;
    background: var(--bg-secondary);
}

.db-field-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
}

.db-field-row-3 {
    grid-template-columns: 1fr 1fr 1fr;
}

/* ---- Avatar Upload ---- */
.db-avatar-upload {
    display: flex;
    align-items: center;
    gap: 20px;
    margin-bottom: var(--space-md);
}

.db-avatar-preview {
    width: 100px;
    height: 100px;
    border-radius: 50%;
    object-fit: cover;
    border: var(--bw-heavy) solid var(--border-subtle);
    background: var(--bg-elevated);
}

.db-avatar-preview-placeholder {
    width: 100px;
    height: 100px;
    border-radius: 50%;
    border: var(--bw-heavy) solid var(--border-subtle);
    background: var(--bg-elevated);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--fs-3xl);
    color: var(--text-muted);
}

.db-avatar-actions {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.db-avatar-btn {
    padding: 8px 20px;
    border-radius: var(--radius-full);
    font-size: var(--fs-sm);
    cursor: pointer;
    border: var(--bw-hair) solid var(--border-subtle);
    background: var(--bg-elevated);
    color: var(--text-primary);
    transition: var(--transition-fast);
}

.db-avatar-btn:hover {
    border-color: var(--accent);
    color: var(--accent);
}

.db-avatar-hint {
    font-size: var(--fs-xs);
    color: var(--text-muted);
}

/* ---- Buttons ---- */
/* ВЫСОТУ ДЕРЖИТ СТУПЕНЬ, А НЕ ПОЛЯ — Button 17:82 говорит это сам:
   «Height is set by the height property, never by vertical padding».
   Было `padding: 12px 32px` и ни одной высоты: кнопка выходила 40 —
   числа, которого нет в шкале 28·36·44·52 и которое нигде не написано.
   В разметке кабинета это 45 кнопок, и все они чинятся здесь. */
.db-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: var(--btn-h-md, 44px);
    padding: 0 32px;
    border-radius: var(--radius-full);
    font-size: var(--fs-sm);
    font-weight: 600;
    line-height: var(--lh-none);
    cursor: pointer;
    border: none;
    /* ПОДЧЁРКИВАНИЕ ГАСИТСЯ В САМОЙ КНОПКЕ. `.db-btn` носят и `button`, и
       `a`: у ссылки подчёркивание своё, браузерное, и в ряду карточки
       профиля «QR код» выходил кнопкой, а «Моя страница» — ссылкой,
       хотя рамка и высота у них одни. Увидено глазами 12.10, померено:
       из двух ссылок-кнопок подчёркнута одна. Гасится там, где заведена
       кнопка, а не в каждом месте, где её ставят */
    text-decoration: none;
    transition: var(--transition-fast);
}

.db-btn-primary {
    background: var(--accent);
    color: var(--neutral-black);
}

.db-btn-primary:hover {
    background: var(--accent-hover);
    transform: translateY(-1px);
}

.db-btn-primary:disabled {
    opacity: 0.5;
    cursor: not-allowed;
    transform: none;
}

.db-btn-saved {
    background: var(--success);
    color: var(--neutral-white);
    transform: none;
}

.db-btn-saved:hover {
    background: var(--success);
    transform: none;
}

.db-btn-outline {
    background: transparent;
    color: var(--text-secondary);
    border: var(--bw-hair) solid var(--border-subtle);
}

.db-btn-outline:hover {
    border-color: var(--text-secondary);
    color: var(--text-primary);
}

.db-btn-danger {
    background: rgba(255, 59, 48, 0.1);
    color: var(--danger);
    border: var(--bw-hair) solid rgba(255, 59, 48, 0.2);
}

.db-btn-danger:hover {
    background: rgba(255, 59, 48, 0.2);
}

.db-btn-row {
    display: flex;
    gap: 12px;
    margin-top: var(--space-md);
}

/* ---- Toast Notification ---- */
.db-toast {
    position: fixed;
    top: 24px;
    left: 50%;
    transform: translateX(-50%) translateY(-100px);
    padding: 14px 28px;
    border-radius: var(--radius-sm);
    font-size: var(--fs-base);
    font-weight: 600;
    z-index: 9999;
    opacity: 0;
    transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.3s ease;
    pointer-events: none;
    text-align: center;
    max-width: 90vw;
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
}

.db-toast-success {
    background: rgba(34, 197, 94, 0.2);
    border: var(--bw-hair) solid rgba(34, 197, 94, 0.4);
    color: var(--success);
    box-shadow: 0 8px 32px rgba(34, 197, 94, 0.15);
}

.db-toast-error {
    background: rgba(255, 59, 48, 0.15);
    border: var(--bw-hair) solid rgba(255, 59, 48, 0.3);
    color: var(--danger);
    box-shadow: 0 8px 32px rgba(255, 59, 48, 0.15);
}

.db-toast-show {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
}

.db-toast-hide {
    opacity: 0;
    transform: translateX(-50%) translateY(-30px);
    transition: transform 0.3s ease, opacity 0.3s ease;
}

/* Card flash on save */
@keyframes db-card-flash {
    0%   { border-color: var(--success); box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.3); }
    50%  { border-color: var(--success); box-shadow: 0 0 12px 0 rgba(34, 197, 94, 0.15); }
    100% { border-color: var(--border-subtle); box-shadow: none; }
}

.db-card-flash {
    animation: db-card-flash 1.2s ease;
}

/* Legacy inline messages (kept for fallback) */


/* ---- Empty State ---- */
.db-empty {
    text-align: center;
    padding: var(--space-xl) var(--space-md);
}

.db-empty-icon {
    font-size: var(--fs-3xl);
    margin-bottom: 16px;
    opacity: 0.5;
}

.db-empty-title {
    font-size: var(--fs-md);
    color: var(--text-secondary);
    margin-bottom: 8px;
}

.db-empty-text {
    font-size: var(--fs-sm);
    color: var(--text-muted);
}

/* ---- Warning Banner ---- */
.db-banner-warning {
    background: rgba(255, 193, 7, 0.1);
    border: var(--bw-hair) solid rgba(255, 193, 7, 0.3);
    color: #ffc107;
    padding: 14px 20px;
    border-radius: var(--radius-md);
    font-size: var(--fs-sm);
    line-height: 1.5;
    margin-bottom: var(--space-md);
}

/* ---- Required ---- */
.db-required {
    color: var(--danger);
    font-weight: 700;
}

/* ---- Checkbox ---- */
/* СНЕСЕНО 14.10. Шесть правил `.db-checkbox` жили только здесь: имени нет
   ни в одном html, ни в одном js, ни в макетах — проверено поиском по
   всему дереву, кроме сборок android. Это была ТРЕТЬЯ копия чекбокса, и
   самая разъехавшаяся: коробка 13 при 16 в админке и 14 на входе, радиус
   2 при 3, штрих галочки 1.5 — ни одно из четырёх чисел не ступень.
   Живые две сведены в `style.css` одним определением. */

/* ---- Select styling ---- */
.db-field-input select,
select.db-field-input {
    appearance: none;
    -webkit-appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' fill='%23888'%3E%3Cpath d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 16px center;
    padding-right: 40px;
    cursor: pointer;
}

/* ---- Tournaments List ---- */



/* ---- Withdraw from a tournament ---- */



.db-withdraw-btn {
    flex-shrink: 0;
    white-space: nowrap;
    padding: 8px 14px;
    font-size: var(--fs-xs);
    font-weight: 500;
    color: var(--text-muted);
    background: none;
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: color 0.2s, border-color 0.2s;
}

.db-withdraw-btn:hover {
    color: var(--danger);
    border-color: rgba(255, 107, 107, 0.5);
}

@media (max-width: 640px) { /* было 560px: границы в медиа — только 640 и 992 */
    .db-withdraw-btn {
        width: 100%;
    }
}







.db-tournament-winner {
    color: var(--warning);
}

/* ---- Stats Grid ---- */
.kst-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 16px;
    margin-bottom: var(--space-md);
}

/* Парные и смешанные игры.

   Тот же язык, что у категорий: числа в ряд, процент справа, полоса под
   ними. Но без очков и без места — рейтинга в парах не существует, есть
   только сыгранное. */
.db-pair-block {
    margin-top: 22px;
}
.db-pair-head {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
}
.db-pair-title {
    font-size: var(--fs-base);
    font-weight: 700;
}
.db-pair-note {
    font-size: var(--fs-xs);
    color: var(--text-muted);
}
.db-pair-tag {
    font-size: var(--fs-2xs);
    font-weight: 700;
    padding: 1px 7px;
    border-radius: var(--radius-full);
    white-space: nowrap;
}
.db-pair-tag-dbl { background: var(--info-subtle); color: var(--info); }
.db-pair-tag-mix { background: rgba(168,85,247,0.18); color: var(--role-manager); }
/* Первая колонка у категорий отведена под очки — здесь её нет, колонок три */
.db-pair-grid {
    grid-template-columns: repeat(3, 96px);
}

/* Что именно посчитано. Стоит под заголовком раздела, а не под карточками:
   вопрос «почему победы не сходятся с историей» возникает до того, как
   человек дочитает до конца */
/* Сдвиг места под подписью — дом `js/kslt-plitki.js` умеет его рисовать.
   В кабинете он пока не зовётся, но правило стоит здесь, а не появится
   вместе с первым зовом: иначе первый зов нарисует неокрашенный текст. */
/* Отступ снизу у сетки платежей: раньше стоял прямо в разметке
   (`style="margin-bottom:16px"`), и такие размеры «жили отдельной
   жизнью». 16 — ступень шкалы. */
.kst-grid-pay { margin-bottom: var(--space-md); }

.kst-change { font-size: var(--fs-2xs); font-weight: 600; margin-top: 2px; }
.kst-change.up { color: var(--success, var(--success)); }
.kst-change.down { color: var(--danger, var(--danger)); }

.kst-note {
    margin: -8px 0 16px;
    color: var(--text-muted);
    font-size: var(--fs-sm);
}

/* Внутри категории карточки те же, но ниже: категорий у игрока до четырёх,
   и в полный рост они уводили «Статистику» на два экрана. Состав блока
   прежний — четыре числа и полоса побед с поражениями. */
.kst-grid-mini {
    /* Колонки постоянной ширины и прижаты влево.
       В четыре равные доли на всю ширину блока числа разъезжались по углам,
       и середина зияла пустотой. По содержимому — тоже плохо: ширина зависит
       от самого числа, и у категории с «209» подписи стояли не там, где у
       категории с «50». Категории сравнивают по вертикали, столбцы должны
       совпадать. */
    grid-template-columns: 92px repeat(3, 96px);
    justify-content: start;
    align-items: baseline;
    gap: 0 8px;
    margin-bottom: 0;
}

/* Внутри категории у чисел нет своей рамки: блок категории уже обведён, и
   получалась рамка в рамке. Без неё четыре числа читаются так же, а высота
   падает ещё на четверть */
.kst-grid-mini .kst-card {
    padding: 4px 0;
    background: none;
    border: none;
    /* Число по центру своей подписи: при выравнивании по левому краю
       короткий «0» уезжал влево от широкого слова «ПОРАЖЕНИЯ» */
    text-align: center;
}

.kst-grid-mini .kst-value {
    font-size: var(--fs-base);
    margin-bottom: 4px;
}

/* Очки — то, ради чего игрок открыл раздел. Победы, поражения и место при
   них: одинаковый размер у всех четырёх лишал блок главного числа */
/* Главное число категории — ТА ЖЕ единственная ступень кабинета.
   Соседние 16 остаются: это не «крупная цифра», а вторичные числа при
   ней, и разница между ними — то, ради чего блок так устроен */
.kst-grid-mini .kst-card:first-child .kst-value {
    font-size: var(--db-fs-cifra);
    line-height: var(--lh-tight);
}

/* Разрядка была рассчитана на крупную подпись под большим числом; на этом
   размере она только растягивала слова */
.kst-grid-mini .kst-label {
    font-size: var(--fs-2xs);
    letter-spacing: 0.2px;
}

.kst-card {
    background: var(--bg-elevated);
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 20px;
    text-align: center;
}

/* ОДНА СТУПЕНЬ НА ВСЕ КРУПНЫЕ ЧИСЛА КАБИНЕТА — слово Кости 12.10 по
   макету 765:12: «26». Было пять ступеней в шести объявлениях: xl здесь,
   lg в двух медиа, md в третьем, base у мини-плиток. Баланс баллов и
   плитки платежей уже стоят на этой переменной */
.kst-value {
    font-size: var(--db-fs-cifra);
    font-weight: 700;
    line-height: var(--lh-tight);
    color: var(--accent);
    margin-bottom: 4px;
}

.kst-label {
    font-size: var(--fs-xs);
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

/* ---- Points by category ---- */
.kst-cat-block {
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 10px 12px 2px;
    margin-bottom: 8px;
}

.kst-cat-block:last-child {
    margin-bottom: 0;
}

.kst-cat-name {
    font-weight: 600;
    margin-bottom: 8px;
    display: flex;
    align-items: center;
    gap: 8px;
}

/* ТОЧКА РАВНА ТОЧКЕ ПРИЛОЖЕНИЯ (30.10). Было 10×10 при 8×8 в
   `mobile/www/css/app.css` — одно имя класса, разные числа: разметку
   рисует ОБЩИЙ дом `js/kslt-kategorii.js`, а вид расходился. 10 к тому
   же мимо шкалы. Сторож «одни классы в двух продуктах» этого не видел:
   он сверяет ИМЕНА, а не числа. */
.kst-cat-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
}

.kst-cat-home {
    font-size: var(--fs-2xs);
    font-weight: 500;
    color: var(--accent);
    margin-left: 8px;
}

/* Категория закрыта для новых заявок. Очки и матчи в ней остаются, поэтому
   она не прячется, а помечается */
.kst-cat-closed {
    margin-left: 8px;
    padding: 2px 8px;
    border-radius: var(--radius-full);
    background: var(--alpha-white-08);
    color: var(--text-muted);
    font-size: var(--fs-2xs);
    font-weight: 500;
}

/* ---- Полоса побед и поражений ----
   Четыре цифры одинакового размера не показывали главного — как игрок
   выступает. Цвета те же, что у значков W и L в таблице матчей, чтобы не
   заводить второй язык цветов */
/* ПОЛОСА РАВНА ПОЛОСЕ ПРИЛОЖЕНИЯ (30.10): было 6 против 8, отступ
   `8px 0 5px` (5 мимо ступеней) и радиус 3 числом вместо скругления
   «таблеткой». НАМЕРЕНО 8: высота полосы — графика, и она та же, что в
   приложении. */
.kst-cat-bar {
    display: flex;
    height: 8px;
    margin: 8px 0;
    border-radius: 999px;
    overflow: hidden;
    background: var(--alpha-white-08);
}

.kst-cat-bar-w,
.kst-cat-bar-l {
    display: block;
    height: 100%;
    transition: width 0.6s ease;
}

.kst-cat-bar-w { background: var(--success); }
.kst-cat-bar-l { background: var(--danger); }

.kst-cat-legend {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    padding-bottom: 8px;
    font-size: var(--fs-xs);
    color: var(--text-muted);
}

.kst-cat-record {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.kst-cat-mark {
    width: 8px;
    height: 8px;
    border-radius: 2px;
    flex-shrink: 0;
}

/* Второй квадратик отделяем от предыдущего числа */
.kst-cat-record .kst-cat-mark + * { margin-right: 2px; }
.kst-cat-mark-w { background: var(--success); }
.kst-cat-mark-l { background: var(--danger); margin-left: 6px; }

/* ---- Settings ---- */
.db-settings-section {
    margin-bottom: var(--space-lg);
}

.db-settings-section:last-child {
    margin-bottom: 0;
}

/* ---- Notification Toggles ---- */
.db-notif-toggle {
    position: relative;
    display: inline-block;
    width: 40px;
    height: 22px;
    cursor: pointer;
}

.db-notif-toggle input {
    opacity: 0;
    width: 0;
    height: 0;
}

.db-notif-slider {
    position: absolute;
    inset: 0;
    background: var(--alpha-white-12);
    border-radius: 11px;
    transition: background 0.2s;
}

.db-notif-slider::before {
    content: '';
    position: absolute;
    width: 16px;
    height: 16px;
    left: 3px;
    bottom: 3px;
    background: var(--neutral-white);
    border-radius: 50%;
    transition: transform 0.2s;
}

.db-notif-toggle input:checked + .db-notif-slider {
    background: var(--accent);
}

.db-notif-toggle input:checked + .db-notif-slider::before {
    transform: translateX(18px);
}

.db-danger-zone {
    border-color: rgba(255, 59, 48, 0.2);
}

.db-danger-zone .db-card-title {
    color: var(--danger);
}

/* ---- Password Fields ---- */
.db-pw-field {
    position: relative;
}

.db-pw-field .db-field-input {
    padding-right: 48px;
}

.db-pw-eye {
    position: absolute;
    right: 12px;
    top: 50%;
    transform: translateY(-50%);
    background: none;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    padding: 4px;
    display: flex;
    align-items: center;
    /* Кегля у кнопки не было вовсе, и браузер ставил свои 13.3333 с
       межстрочным normal — уровень мимо лестницы. Значок внутри меряется
       от кегля, поэтому число тут не украшение */
    font-size: var(--fs-sm);
    line-height: 1;
    transition: var(--transition-fast);
}

.db-pw-eye:hover {
    color: var(--accent);
}

/* Password Rules (same as auth page) */
.db-pw-rules {
    display: flex;
    flex-wrap: nowrap;
    gap: 0;
    font-size: var(--fs-2xs);
    margin-bottom: 20px;
    margin-top: -8px;
}

.db-pw-rule {
    color: var(--text-muted);
    transition: color var(--transition-fast);
    white-space: nowrap;
}

.db-pw-rule::before {
    content: '○ ';
}

.db-pw-rule.valid::before {
    content: '● ';
}

.db-pw-rule.valid {
    color: var(--accent);
}

.db-pw-rule + .db-pw-rule {
    margin-left: 4px;
    padding-left: 8px;
    border-left: var(--bw-hair) solid var(--border-subtle);
}

/* ---- Crop Modal ---- */
.db-crop-overlay {
    position: fixed;
    inset: 0;
    z-index: 10000;
    background: var(--alpha-black-85);
    display: flex;
    align-items: center;
    justify-content: center;
    animation: db-msg-in 0.2s ease;
}

.db-crop-modal {
    background: var(--bg-secondary);
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-lg);
    width: 90%;
    max-width: 500px;
    overflow: hidden;
}

.db-crop-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 20px;
    border-bottom: var(--bw-hair) solid var(--border-subtle);
}

.db-crop-title {
    font-size: var(--fs-base);
    font-weight: 600;
    color: var(--text-primary);
}

.db-crop-close {
    background: none;
    border: none;
    color: var(--text-muted);
    font-size: var(--fs-xl);
    cursor: pointer;
    padding: 0;
    line-height: 1;
}

.db-crop-close:hover {
    color: var(--text-primary);
}

.db-crop-body {
    padding: 20px;
    max-height: 60vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--neutral-black);
}

.db-crop-body img {
    max-width: 100%;
    display: block;
}

.db-crop-footer {
    display: flex;
    justify-content: flex-end;
    gap: 12px;
    padding: 16px 20px;
    border-top: var(--bw-hair) solid var(--border-subtle);
}

/* Cropper.js dark theme overrides */
.cropper-view-box {
    outline: var(--bw-thick) solid var(--accent);
    outline-color: var(--accent);
}

.cropper-line,
.cropper-point {
    background-color: var(--accent);
}

.cropper-modal {
    background-color: var(--neutral-black);
    opacity: 0.6;
}

/* ---- Membership Card ---- */
.db-membership-card {
    background: var(--bg-card);
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-lg);
    padding: var(--space-md);
    margin-bottom: var(--space-md);
}

.db-membership-active {
    border-color: rgba(34, 197, 94, 0.3);
}

.db-membership-expired {
    border-color: rgba(255, 59, 48, 0.2);
}

.db-membership-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 16px;
    padding-bottom: 12px;
    border-bottom: var(--bw-hair) solid var(--border-subtle);
}

.db-membership-header .db-card-title {
    margin-bottom: 0;
    padding-bottom: 0;
    border-bottom: none;
}

.db-membership-badge {
    font-size: var(--fs-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 1px;
    padding: 4px 12px;
    border-radius: var(--radius-full);
}

.db-membership-badge-active {
    background: rgba(34, 197, 94, 0.12);
    color: var(--success);
    border: var(--bw-hair) solid rgba(34, 197, 94, 0.25);
}

.db-membership-badge-expired {
    background: rgba(255, 59, 48, 0.1);
    color: var(--danger);
    border: var(--bw-hair) solid rgba(255, 59, 48, 0.2);
}

.db-membership-progress-wrap {
    margin-top: 4px;
}

.db-membership-progress {
    width: 100%;
    height: 6px;
    background: var(--bg-elevated);
    border-radius: 3px;
    overflow: hidden;
    margin-bottom: 10px;
}

.db-membership-progress-bar {
    height: 100%;
    background: var(--success);
    border-radius: 3px;
    transition: width 0.5s ease;
}

.db-membership-days {
    font-size: var(--fs-sm);
    color: var(--text-secondary);
}

.db-membership-days strong {
    color: var(--success);
}

.db-membership-text {
    color: var(--text-muted);
    font-size: var(--fs-sm);
    margin-bottom: 16px;
    line-height: 1.5;
}

.db-membership-btn {
    display: inline-block;
    text-decoration: none;
    text-align: center;
}

/* ---- Locked Overlay (membership required) ---- */
.db-locked-overlay {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 60px 24px;
    min-height: 300px;
}

.db-locked-icon {
    opacity: 0.4;
    margin-bottom: 16px;
    color: var(--text-muted);
}

.db-locked-title {
    font-size: var(--fs-lg);
    font-weight: 600;
    color: var(--text-primary);
    margin: 0 0 12px;
}

.db-locked-text {
    font-size: var(--fs-base);
    color: var(--text-muted);
    max-width: 400px;
    margin: 0 auto 24px;
    line-height: 1.5;
}

.db-locked-btn {
    display: inline-block;
    text-decoration: none;
    text-align: center;
}

/* Locked tab indicator */
.db-tab-locked {
    opacity: 0.5;
}

.db-sidebar-link.db-tab-locked::after {
    content: '\1F512';
    font-size: var(--fs-2xs);
    margin-left: auto;
}

.db-mobile-tab.db-tab-locked::after {
    content: '\1F512';
    font-size: var(--fs-2xs);
    margin-left: 4px;
}

/* ---- Games Subsections (accordion cards) ---- */
.db-subsection {
    background: var(--bg-elevated);
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-lg);
    margin-bottom: 12px;
    overflow: hidden;
}

.db-subsection:last-child {
    margin-bottom: 0;
}

.db-subsection-toggle {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 20px;
    background: transparent;
    border: none;
    color: var(--text-primary);
    font-size: var(--fs-base);
    font-weight: 600;
    cursor: pointer;
    transition: background 0.2s;
}

.db-subsection-toggle:hover {
    background: var(--alpha-lime-06);
}

.db-toggle-arrow {
    font-size: var(--fs-2xs);
    color: var(--text-muted);
}

.db-subsection-body {
    padding: 0 20px 16px;
}

/*
 * Заглушка на время загрузки держит высоту, чтобы карточки не прыгали.
 * Раньше высоту задавали самому телу раздела, и под единственной строкой
 * матча навсегда оставалась пустая полоса.
 */
.db-subsection-loading {
    display: flex;
    align-items: center;
    min-height: 72px;
    margin: 0;
    color: var(--text-muted);
    font-size: var(--fs-sm);
}

.db-subsection-collapsed {
    display: none;
}

/* ---- Matches Table ---- */
.db-matches-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--fs-sm);
}

.db-matches-table thead tr {
    border-bottom: var(--bw-hair) solid var(--alpha-white-08);
}

.db-matches-table th {
    text-align: left;
    padding: 6px 8px;
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.db-matches-table td {
    padding: 6px 8px;
    border-bottom: var(--bw-hair) solid var(--alpha-white-08);
}

.db-match-date {
    white-space: nowrap;
    color: var(--text-muted);
}

.db-match-opponent {
    display: flex;
    align-items: center;
    gap: 8px;
}

.db-match-avatar {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}

/* Пара: два кружка внахлёст. Второй перекрывает первый и обведён фоном
   строки, иначе они сливаются в одно пятно */
.db-match-avatars {
    display: flex;
    align-items: center;
    flex-shrink: 0;
}

.db-match-avatar-2nd {
    /* Нахлёст небольшой: при большем второй кружок съедал буквы первого */
    margin-left: -5px;
    box-shadow: 0 0 0 2px var(--bg-elevated);
}

.db-match-pair {
    display: flex;
    flex-direction: column;
    line-height: 1.3;
}

/* Имя не переносим: у пары наименьшая ширина ячейки падает до длины слова,
   и колонка сжималась, ломая имена во всех остальных строках */
.db-match-opponent > span,
.db-match-pair > span {
    white-space: nowrap;
}

.db-match-avatar-ph {
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--alpha-lime-12);
    color: var(--accent);
    font-size: var(--fs-2xs);
    font-weight: 700;
}

.db-match-score {
    text-align: center;
    /* то же, что на странице игрока: семейство одно, ширину цифр держит
       свойство. Голое `monospace` отдавало счёт системному моно — на
       разных машинах разный шрифт и разная ширина. */
    font-family: var(--font-sans);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

.db-match-result-cell {
    text-align: center;
}

/* Итог матча — самая читаемая колонка в таблице, поэтому не мельче
   соседей и с заметной подложкой */
.db-match-result {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 26px;
    padding: 3px 9px;
    border: var(--bw-hair) solid transparent;
    border-radius: var(--radius-sm, 6px);
    font-weight: 700;
    font-size: var(--fs-sm);
    line-height: 1.3;
}

.db-match-result.win {
    background: rgba(52,199,89,0.18);
    border-color: rgba(52,199,89,0.4);
    color: var(--success);
}

.db-match-result.loss {
    background: rgba(255,59,48,0.18);
    border-color: rgba(255,59,48,0.4);
    color: var(--danger);
}

.db-match-tournament {
    font-size: var(--fs-xs);
    color: var(--text-muted);
    max-width: 200px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.db-match-round {
    text-align: center;
    font-size: var(--fs-xs);
    font-weight: 600;
    color: var(--text-secondary);
    white-space: nowrap;
}

.db-match-h2h {
    background: transparent;
    border: var(--bw-hair) solid var(--accent-glow);
    color: var(--accent);
    font-size: var(--fs-2xs);
    font-weight: 700;
    /* Межстрочный был браузерный `normal` — единственный уровень строки
       мимо лестницы 1 · 1.1 · 1.3 · 1.5 · 1.65 (замер 12.10). 1 — ступень
       одиночной строки кнопки, та же, что у .db-btn и .db-chal-btn */
    line-height: 1;
    padding: 3px 8px;
    border-radius: 4px;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.2s, border-color 0.2s;
}

.db-match-h2h:hover {
    background: var(--alpha-lime-12);
    border-color: var(--accent);
}

/* ---- Show All / Collapse Button ---- */


/* ============================================
   RESPONSIVE
   ============================================ */

@media (max-width: 992px) {
    .db-content {
        margin-left: 0;
    }

    .db-sidebar {
        display: none;
    }

    /* Разделы кабинета — сеткой, все на виду. Лентой помещалось четыре из
       восьми, остальные уходили за правый край, и ничто не подсказывало,
       что её можно листать: человек думал, что кабинет пустой.

       В потоке, а не прибито к верху: три ряда плиток отъедали бы четверть
       экрана на каждой прокрутке */
    .db-mobile-tabs {
        display: grid;
        grid-template-columns: repeat(3, 1fr);
        gap: 8px;
        position: static;
        background: var(--bg-secondary);
        border-bottom: var(--bw-hair) solid var(--border-subtle);
        /* Сверху место под шапку сайта: она висит поверх страницы */
        padding: 76px 16px 12px;
    }

    /* Высота плитки — ступень шкалы кнопок 28 · 36 · 44 · 52, и держится
       свойством, а не суммой полей: замер 11.10 дал 60 — ни одной ступени.
       Межстрочный 1 — та же ступень для одиночной строки, что у кнопок
       ниже по файлу.

       ПОДПИСЬ В ОДНУ СТРОКУ — ЭТО СВОЙСТВО СЛОВА, А НЕ ПРАВИЛА, и первый
       раз я это назвал, не померив. Прогон 11.10 уронил кыргызский на
       375: «Менин оюндарым» просит 107 при 99 в плитке, переносится и
       даёт высоту 64 — не ступень. Слово сокращено до «Оюндарым»
       (`js/dashboard.js`, словарь kg): суффикс -ым и так значит «мои».
       Самая длинная подпись теперь «Уведомления» — 82 при 99. */
    .db-mobile-tab {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 4px;
        padding: 8px 4px;
        min-height: 52px;
        border-radius: var(--radius-md);
        border: var(--bw-hair) solid var(--border-subtle);
        background: transparent;
        color: var(--text-secondary);
        font-size: var(--fs-xs);
        line-height: 1;
        text-align: center;
        cursor: pointer;
        transition: var(--transition-fast);
    }

    .db-mobile-tab-icon {
        font-size: var(--fs-md);
        line-height: 1;
    }

    .db-mobile-tab.active {
        background: var(--alpha-lime-12);
        border-color: var(--accent);
        color: var(--accent);
    }


    /* Отступ сверху нужен только под шапку пользователя, если она прибита.
       Сетка разделов теперь в потоке и место под себя занимает сама */
    .db-content {
        padding: 16px;
    }

    .kst-grid {
        grid-template-columns: repeat(2, 1fr);
    }

}

@media (max-width: 640px) { /* было 768px: границы в медиа — только 640 и 992 */
    .db-field-row {
        grid-template-columns: 1fr;
    }

    .db-field-row-3 {
        grid-template-columns: 1fr 1fr 1fr;
    }

    .db-avatar-upload {
        flex-direction: column;
        text-align: center;
    }

    .db-btn-row {
        flex-direction: column;
    }

    /* ПРЯЧЕТСЯ ПАРА «ЗАГОЛОВОК + ЯЧЕЙКА», А НЕ НОМЕР КОЛОНКИ.
       Было `th:nth-child(5)` — правило писалось для таблицы матчей, где
       пятая колонка турнир. Но класс .db-matches-table несут ещё баттлы,
       приглашения, платежи и баллы, а у них пятая колонка своя: у баттлов
       это статус. Заголовок исчезал, ячейка оставалась — в шапке пять
       клеток, в теле шесть, и `table-layout: fixed`, который берёт ширины
       ИЗ ПЕРВОЙ СТРОКИ, раздавал их со сдвигом. Замер 12.10 на 390: шапка
       110 · 110 · 11 · 11 · 0 · 70, ячейки 110 · 110 · 11 · 11 · 70 · 11 —
       кнопка H2H шириной 40 стояла в ячейке 11 и торчала за край. */
    .db-matches-table th.db-th-tournament,
    .db-matches-table td.db-match-tournament {
        display: none;
    }

}

@media (max-width: 640px) { /* было 480px: границы в медиа — только 640 и 992 */
    .db-content {
        padding: 12px;
    }

    .db-section-title {
        font-size: var(--fs-md);
    }

    .db-card {
        padding: 16px;
    }

    .kst-grid {
        grid-template-columns: 1fr 1fr;
        gap: 8px;
    }

    .kst-card {
        padding: 12px;
    }

    /* Кегля здесь больше нет: цифра одна на всех видах, --db-fs-cifra */
}

/* ============================================
   GAME INVITES LIST
   ============================================ */

.db-invite-list {
    display: flex;
    flex-direction: column;
    gap: 0;
}

.db-invite-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 0;
    border-bottom: var(--bw-hair) solid var(--alpha-white-08);
}

.db-invite-item:last-child {
    border-bottom: none;
}

.db-invite-avatar {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}

.db-invite-avatar-ph {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--alpha-lime-12);
    color: var(--accent);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--fs-xs);
    font-weight: 700;
    flex-shrink: 0;
}

.db-invite-info {
    flex: 1;
    min-width: 0;
}

.db-invite-name {
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.db-invite-meta {
    font-size: var(--fs-xs);
    color: var(--text-muted);
    margin-top: 2px;
}

/* Статус и H2H стоят одной колонкой справа: H2H появляется только у
   сыгранных, и без общей колонки строка прыгала бы по ширине */
.db-invite-side {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 6px;
    flex-shrink: 0;
}

.db-invite-status {
    font-size: var(--fs-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    flex-shrink: 0;
    padding: 3px 8px;
    border-radius: 6px;
}

/* ТОНА ЗНАЧКА — ИЗ ШКАЛЫ, И ФОН НЕПРОЗРАЧНЫЙ.
   Стояли #4caf50 и #f44336 — Material, которого в палитре КСЛТ нет вовсе,
   на прозрачном тинте 10 %. Замер 12.10 на СМЕШАННОМ фоне: красный давал
   **4.10** при норме AA 4.5. ПРОЗРАЧНАЯ КРАСКА СМЕШИВАЕТСЯ С ФОНОМ, и у
   значка в кабинете подложек три — #0A0A0A, #111111 и #1E1E1E, — то есть
   контраст у одного значка получался разный в разных местах.
   Посчитано на #1E1E1E: --red-500 на тинте 15 % даёт 4.19, на 13 % — 4.33,
   на 10 % — 4.49. Ни один не проходит, и дело не в тоне: тинт поднимает
   фон быстрее, чем краску.
   Поэтому фон берётся непрозрачной ступенью шкалы — `--danger-surface` и
   `--success-surface`. Контраст перестаёт зависеть от подложки:
   **5.16** и **7.25**. */
.db-invite-accepted {
    color: var(--green-500);
    background: var(--success-surface);
}

.db-invite-declined {
    color: var(--red-500);
    background: var(--danger-surface);
}

.db-invite-pending {
    color: var(--accent);
    background: var(--alpha-lime-06);
}

/* ---- Badges Card in Stats ---- */
.db-badges-progress {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 16px;
}

.db-badges-progress-bar {
    flex: 1;
    height: 6px;
    background: var(--bg-elevated);
    border-radius: 3px;
    overflow: hidden;
}

.db-badges-progress-fill {
    height: 100%;
    background: var(--accent);
    border-radius: 3px;
    transition: width 0.6s ease;
}

.db-badges-progress-text {
    color: var(--accent);
    font-size: var(--fs-sm);
    font-weight: 700;
    white-space: nowrap;
}

.db-badges-earned {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 12px;
}

.db-badge-item {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background: var(--surface-glass);
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-full);
    padding: 4px 12px;
    font-size: var(--fs-sm);
    font-weight: 600;
    transition: border-color var(--transition-fast), transform 0.25s ease;
}

.db-badge-earned {
    border-color: var(--alpha-lime-20);
    color: var(--text-primary);
}

.db-badge-locked {
    opacity: 0.5;
    color: var(--text-muted);
}

.db-badge-icon {
    font-size: var(--fs-base);
    display: inline-block;
    transition: transform 0.25s ease, filter 0.25s ease;
}

/* Оживает под курсором — так же, как в перебивке на главной.
   Закрытые тоже: пусть человек видит, что до них можно дотянуться */
.db-badge-item:hover {
    border-color: var(--alpha-lime-30);
    transform: translateY(-2px);
}

.db-badge-item:hover .db-badge-icon {
    transform: scale(1.2);
    filter: drop-shadow(0 0 8px var(--alpha-lime-50));
}

.db-badge-item:hover .db-badge-icon-locked {
    filter: grayscale(0.4) drop-shadow(0 0 8px var(--alpha-lime-30));
    opacity: 0.85;
}

.db-badge-icon-locked {
    filter: grayscale(1);
    opacity: 0.5;
}

.db-badge-name {
    white-space: nowrap;
}

.db-badges-next-label {
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    font-weight: 600;
    margin-bottom: 8px;
}

/* ---- All Badges Grid (expandable) ---- */
/* UTILITY BUTTON 56:101: «Если клик только ПЕРЕСТАВЛЯЕТ то, что уже на
   экране, — это Utility, размеры xs 28 и sm 36». Кнопка разворачивает
   список значков, ничего не отправляя: ступень sm 36, а высота 39 была
   суммой полей 10/16 */
.db-badges-expand-btn {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 36px;
    width: 100%;
    background: none;
    border: var(--bw-hair) dashed var(--border-subtle);
    border-radius: var(--radius-md);
    color: var(--accent);
    font-size: var(--fs-sm);
    font-weight: 600;
    line-height: var(--lh-none);
    padding: 0 16px;
    margin-top: 8px;
    cursor: pointer;
    transition: all var(--transition-fast);
    text-align: center;
}

.db-badges-expand-btn:hover {
    border-color: var(--accent);
    background: var(--alpha-lime-06);
}

.db-badges-expand-btn.active {
    border-style: solid;
    border-color: var(--accent);
    background: var(--alpha-lime-06);
}

.db-badges-all {
    display: grid;
    grid-template-columns: 1fr;
    gap: 4px;
    margin-top: 12px;
}

.db-badge-all-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 12px;
    border-radius: var(--radius-md);
    background: var(--surface-glass);
    border: var(--bw-hair) solid var(--border-subtle);
    transition: border-color var(--transition-fast), transform 0.25s ease;
}

.db-badge-all-item:hover {
    border-color: var(--alpha-lime-30);
    transform: translateY(-2px);
}

.db-badge-all-item:hover .db-badge-all-icon {
    transform: scale(1.2);
    filter: drop-shadow(0 0 8px var(--alpha-lime-50));
}

.db-badge-all-item:hover .db-badge-icon-locked {
    filter: grayscale(0.4) drop-shadow(0 0 8px var(--alpha-lime-30));
    opacity: 0.85;
}

.db-badge-all-earned {
    border-color: var(--alpha-lime-20);
}

.db-badge-all-locked {
    opacity: 0.5;
}

.db-badge-all-icon {
    font-size: var(--fs-lg);
    flex-shrink: 0;
    width: 32px;
    text-align: center;
    transition: transform 0.25s ease, filter 0.25s ease;
}

.db-badge-all-info {
    flex: 1;
    min-width: 0;
}

.db-badge-all-name {
    font-weight: 600;
    font-size: var(--fs-sm);
    margin-bottom: 4px;
}

.db-badge-all-desc {
    font-size: var(--fs-xs);
    color: var(--text-muted);
}

.db-badge-all-earned .db-badge-all-desc {
    color: var(--accent);
}

.db-badge-all-check {
    color: var(--accent);
    font-weight: 700;
    font-size: var(--fs-base);
    flex-shrink: 0;
}

/* ---- Badge Toast ---- */
.db-badge-toast {
    position: fixed;
    bottom: 24px;
    right: 24px;
    background: rgba(20, 20, 20, 0.95);
    border: var(--bw-hair) solid var(--accent);
    border-radius: var(--radius-lg);
    padding: 12px 24px;
    color: var(--text-primary);
    font-size: var(--fs-sm);
    font-weight: 600;
    z-index: 9999;
    transform: translateY(20px);
    opacity: 0;
    transition: all 0.4s ease;
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    box-shadow: 0 4px 20px var(--alpha-black-50);
}

.db-badge-toast.visible {
    transform: translateY(0);
    opacity: 1;
}

.db-badge-toast-icon {
    font-size: var(--fs-md);
    margin-right: 4px;
}

/* ============================================
   H2H MODAL (dashboard)
   ============================================ */
.h2h-overlay {
    position: fixed;
    inset: 0;
    z-index: 9999;
    background: var(--alpha-black-60);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    opacity: 0;
    transition: opacity 0.3s ease;
}
.h2h-overlay.visible { opacity: 1; }

/* ОКНО H2H СТОИТ В ДВУХ ФАЙЛАХ: тот же блок есть в css/player.css. До
   куска 185 здесь был литерал #1a1a1a — ровно значение --neutral-800, но
   написанное мимо системы, и потому молча расходившееся бы с ним. */
.h2h-modal {
    background: var(--neutral-800);
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-xl);
    padding: 36px 32px;
    max-width: 520px;
    width: 100%;
    position: relative;
    transform: translateY(20px);
    transition: transform 0.3s ease;
    max-height: 90vh;
    overflow-y: auto;
}
.h2h-overlay.visible .h2h-modal { transform: translateY(0); }

.h2h-close {
    position: absolute;
    top: 14px; right: 14px;
    background: none; border: none;
    color: var(--text-muted);
    font-size: var(--fs-xl);
    cursor: pointer;
    width: 36px; height: 36px;
    display: flex; align-items: center; justify-content: center;
    border-radius: 50%;
    transition: color 0.2s, background 0.2s;
}
.h2h-close:hover { color: var(--text-primary); background: var(--surface-glass); }

.h2h-loading, .h2h-empty {
    text-align: center;
    padding: 48px 20px;
    color: var(--text-muted);
    font-size: var(--fs-base);
}

.h2h-header {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 24px;
    margin-bottom: 28px;
}

.h2h-player {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    flex: 1;
    min-width: 0;
}

.h2h-photo {
    width: 72px; height: 72px;
    border-radius: 50%;
    object-fit: cover;
    border: var(--bw-heavy) solid var(--border-subtle);
}

.h2h-photo-ph {
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--alpha-lime-12);
    color: var(--accent);
    font-size: var(--fs-xl);
    font-weight: 700;
}

.h2h-name {
    font-size: var(--fs-sm);
    font-weight: 600;
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 140px;
}

.h2h-center { text-align: center; flex-shrink: 0; }
.h2h-score {
    font-size: var(--fs-2xl);
    font-weight: 700;
    color: var(--accent);
    letter-spacing: 2px;
}
.h2h-label {
    font-size: var(--fs-xs);
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 1px;
    margin-top: 4px;
}

.h2h-stats {
    background: var(--bg-elevated);
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 16px;
    margin-bottom: 24px;
}
.h2h-stat-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 8px 0;
}
.h2h-stat-row + .h2h-stat-row { border-top: var(--bw-hair) solid var(--border-subtle); }
.h2h-stat-val { font-size: var(--fs-md); font-weight: 700; min-width: 40px; }
.h2h-val-left { text-align: left; color: var(--accent); }
.h2h-val-right { text-align: right; color: var(--text-primary); }
.h2h-stat-label {
    font-size: var(--fs-xs);
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    text-align: center;
}

.h2h-matches { margin-bottom: 20px; }
.h2h-matches-title {
    font-size: var(--fs-xs);
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-bottom: 12px;
}
.h2h-match {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 0;
    border-bottom: var(--bw-hair) solid var(--alpha-white-08);
}
.h2h-match:last-child { border-bottom: none; }
.h2h-match-date { color: var(--text-muted); font-size: var(--fs-xs); min-width: 70px; }
/* Название турнира и метка рода матча. Окно узкое: имя укорачиваем
   многоточием, метку — никогда, иначе от «дружеский» останется «дру…» */
.h2h-match-tournament { display: flex; align-items: center; flex: 1; min-width: 0; color: var(--text-muted); font-size: var(--fs-xs); }
.h2h-tname { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.h2h-match-tournament .db-match-tag { flex: none; }
.h2h-match-score { font-weight: 700; font-family: var(--font-sans); font-variant-numeric: tabular-nums; }
.h2h-match-result {
    font-size: var(--fs-xs); font-weight: 700;
    padding: 2px 8px; border-radius: 3px;
}
/* Победа и проигрыш — роль, решённая 15.10 и 19.10: системные --success и
   --danger. Сторож «отказ одной краской» смотрел только copy в player.css
   и этих двух строк не видел — краски iOS (#34c759, #ff3b30) жили здесь
   десять дней. ΔE до системных 8.5 и 19.5, контраст 6.03→6.76 и 4.21→4.39. */
.h2h-match-result.win { background: var(--success-subtle); color: var(--success); }
.h2h-match-result.loss { background: var(--danger-subtle); color: var(--danger); }

.h2h-profile-btn {
    display: block;
    text-align: center;
    padding: 12px;
    background: var(--alpha-lime-06);
    border: var(--bw-hair) solid var(--accent-subtle);
    border-radius: var(--radius-md);
    color: var(--accent);
    font-size: var(--fs-sm);
    font-weight: 600;
    text-decoration: none;
    transition: background 0.2s;
}
.h2h-profile-btn:hover { background: var(--alpha-lime-12); }

@media (max-width: 640px) { /* было 480px: границы в медиа — только 640 и 992 */
    .h2h-modal { padding: 24px 16px; }
    .h2h-photo { width: 56px; height: 56px; }
    .h2h-header { gap: 16px; }
    .h2h-score { font-size: var(--fs-xl); }
    .h2h-name { font-size: var(--fs-xs); max-width: 100px; }
}

/* ---- Onboarding Welcome Modal ---- */
.db-onboarding-overlay {
    position: fixed;
    inset: 0;
    z-index: 10000;
    background: var(--alpha-black-60);
    backdrop-filter: blur(8px);
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transition: opacity 0.3s ease;
    padding: 16px;
}
.db-onboarding-overlay.visible { opacity: 1; }

.db-onboarding-modal {
    background: #1a1a1a;
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: 16px;
    max-width: 440px;
    width: 100%;
    padding: 40px 32px 32px;
    text-align: center;
    position: relative;
    opacity: 0;
    transform: translateY(24px);
    transition: opacity 0.3s ease, transform 0.3s ease;
}
.db-onboarding-modal.visible {
    opacity: 1;
    transform: translateY(0);
}

.db-onboarding-close {
    position: absolute;
    top: 12px;
    right: 16px;
    background: none;
    border: none;
    color: var(--text-muted);
    font-size: var(--fs-xl);
    cursor: pointer;
    line-height: 1;
    padding: 4px;
    transition: color 0.2s;
}
.db-onboarding-close:hover { color: var(--text-primary); }

.db-onboarding-emoji {
    font-size: var(--fs-3xl);
    margin-bottom: 16px;
    line-height: 1;
}

.db-onboarding-title {
    font-size: var(--fs-lg);
    font-weight: 700;
    color: var(--text-primary);
    margin: 0 0 12px;
}

.db-onboarding-text {
    font-size: var(--fs-base);
    color: var(--text-secondary);
    line-height: 1.65;
    margin: 0 0 20px;
}

.db-onboarding-dots {
    display: flex;
    justify-content: center;
    gap: 8px;
    margin-bottom: 24px;
}

.db-onboarding-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--border-subtle);
    transition: background 0.3s, transform 0.3s;
}
.db-onboarding-dot.active {
    background: var(--accent);
    transform: scale(1.25);
}

.db-onboarding-btn {
    display: inline-block;
    background: var(--accent);
    color: var(--neutral-black);
    border: none;
    border-radius: 8px;
    padding: 12px 40px;
    font-size: var(--fs-base);
    font-weight: 600;
    cursor: pointer;
    transition: opacity 0.2s;
}
.db-onboarding-btn:hover { opacity: 0.85; }

.db-onboarding-tg-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: #2AABEE;
    color: var(--neutral-white);
    border-radius: 8px;
    padding: 10px 20px;
    font-size: var(--fs-sm);
    font-weight: 600;
    text-decoration: none;
    margin-bottom: 16px;
    transition: opacity 0.2s;
}
.db-onboarding-tg-btn:hover { opacity: 0.85; }

@media (max-width: 640px) { /* было 480px: границы в медиа — только 640 и 992 */
    .db-onboarding-modal { padding: 32px 20px 24px; }
    .db-onboarding-emoji { font-size: var(--fs-3xl); }
    .db-onboarding-title { font-size: var(--fs-md); }
    .db-onboarding-btn { width: 100%; }
}



.db-reenter-btn {
    display: inline-flex;
    align-items: center;
    text-decoration: none;
    color: var(--accent);
    border-color: var(--alpha-lime-50);
}

.db-reenter-btn:hover {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--alpha-lime-06);
}

/* Телефон: страна списком, номер рядом. Код страны подставляется по выбору,
   а не угадывается по номеру — у России и Казахстана он общий, +7. */
.db-phone-row {
    display: flex;
    gap: 8px;
}

.db-phone-country {
    flex: 0 0 auto;
    width: 260px;
    max-width: 45%;
    min-width: 0;
    padding-right: 34px;
    cursor: pointer;
    text-overflow: ellipsis;
}

.db-phone-number {
    flex: 1 1 auto;
    min-width: 0;
}

@media (max-width: 640px) { /* было 520px: границы в медиа — только 640 и 992 */
    /* Страна и номер остаются в одной строке. Раньше они вставали друг под
       друга, и одно поле «Телефон» занимало две строки — вместе с WhatsApp
       это четыре строки на два номера.

       Поле страны узкое: видно флаг и код, название обрезается — оно
       нужно только при выборе, а в списке место есть */
    .db-phone-country {
        flex: 0 0 122px;
        width: 122px;
        max-width: 122px;
        padding-left: 10px;
        padding-right: 22px;
    }
}

/* Подставленные браузером значения приходят со своим белым фоном и чёрным
   текстом — на тёмной теме поле превращается в белое пятно. Этот фон не
   перекрывается обычным background, только тенью внутрь; бесконечный
   transition не даёт браузеру вернуть свой цвет после клика по полю. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active,
textarea:-webkit-autofill,
select:-webkit-autofill {
    -webkit-text-fill-color: var(--text-primary);
    -webkit-box-shadow: 0 0 0 1000px var(--bg-elevated) inset;
    box-shadow: 0 0 0 1000px var(--bg-elevated) inset;
    caret-color: var(--text-primary);
    transition: background-color 100000s ease-in-out 0s;
}

/* Переключатель «показывать» у каждого способа связи: телеграм может быть
   рабочий, WhatsApp личный — открывать их по одной галочке неправильно */
.db-contact-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

.db-show-toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}

.db-show-label {
    font-size: var(--fs-sm);
    font-weight: 500;
    color: var(--text-muted);
    transition: color 0.2s;
}

.db-show-toggle:has(input:checked) .db-show-label { color: var(--accent); }

/* Ползунок: по галочке не прочитать, что произойдёт, а по нему видно
   состояние сразу */
.db-switch {
    position: relative;
    display: inline-block;
    width: 34px;
    height: 18px;
    flex: 0 0 auto;
}

.db-switch input {
    opacity: 0;
    width: 0;
    height: 0;
}

.db-switch-slider {
    position: absolute;
    inset: 0;
    background: var(--alpha-white-12);
    border-radius: 9px;
    transition: background 0.2s;
}

.db-switch-slider::before {
    content: '';
    position: absolute;
    width: 12px;
    height: 12px;
    left: 3px;
    bottom: 3px;
    background: var(--neutral-white);
    border-radius: 50%;
    transition: transform 0.2s;
}

.db-switch input:checked + .db-switch-slider { background: var(--accent); }
.db-switch input:checked + .db-switch-slider::before { transform: translateX(16px); }

/* Кому видны контакты — вопрос неочевидный, и без объяснения человек не
   понимает, что открывает и кому */
.db-contacts-note {
    font-size: var(--fs-sm);
    line-height: var(--lh-relaxed);
    color: var(--text-secondary);
    background: var(--alpha-white-04);
    border-left: var(--bw-thick) solid var(--accent);
    border-radius: 0 8px 8px 0;
    padding: 8px 12px;
    margin-bottom: 16px;
}

/* Подключение телеграма: раньше здесь была одна строка про членство, и
   человек не понимал, зачем нажимать. Перечисляем, что придёт, до нажатия */
.db-tg-block {
    margin-top: 12px;
    padding-top: 12px;
    border-top: var(--bw-hair) solid var(--alpha-white-08);
}

.db-tg-title {
    font-size: var(--fs-base);
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: 8px;
}

.db-tg-list {
    margin: 0 0 16px;
    padding-left: 20px;
    font-size: var(--fs-sm);
    line-height: 1.65;
    color: var(--text-secondary);
}

.db-tg-list li::marker { color: var(--accent); }

/* Ступень sm 36 вместо высоты 36 из полей 9/18 — число то же, но теперь
   оно НАЗВАНО. Краска синяя фирменная, её не трогаем: контраст 4.16
   поднят фоном, а не осветлением чужого цвета */
.db-tg-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 36px;
    gap: 8px;
    padding: 0 16px;
    font-size: var(--fs-sm);
    font-weight: 600;
    line-height: var(--lh-none);
    background: rgba(0, 136, 204, 0.28);
    color: #2AA3DC;
    border: var(--bw-hair) solid rgba(0, 136, 204, 0.3);
    border-radius: 8px;
    text-decoration: none;
    transition: background 0.2s;
}

.db-tg-btn:hover { background: rgba(0, 136, 204, 0.25); }



.db-tg-note {
    font-size: var(--fs-sm);
    line-height: 1.65;
    color: var(--text-muted);
}

.db-tg-state {
    font-size: var(--fs-sm);
    font-weight: 500;
    color: var(--success);
    white-space: nowrap;
}

.db-tg-off {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    color: var(--text-muted);
    text-decoration: underline;
    cursor: pointer;
}

.db-tg-off:hover { color: var(--danger); }

/* Столбец телеграма без подключённого бота: ползунок бесполезен, и это
   должно быть видно, а не выясняться отсутствием сообщений */
.db-notif-off { opacity: 0.35; cursor: not-allowed; }
.db-notif-off input { cursor: not-allowed; }

.db-notif-hint {
    margin-top: 12px;
    font-size: var(--fs-sm);
    line-height: 1.65;
    color: var(--text-secondary);
}

/* ============================================
   Модальное окно — одно на весь кабинет
   ============================================
   Раньше каждое окно собиралось со своими стилями прямо в коде, а
   подтверждения показывались системным confirm() — чужой рамкой посреди
   тёмной страницы. */

.db-modal-back {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: var(--alpha-black-60);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    animation: dbModalBack 0.2s ease;
}

@keyframes dbModalBack { from { opacity: 0; } }

.db-modal {
    width: 100%;
    max-width: 420px;
    max-height: 85vh;
    overflow-y: auto;
    /* Один вид у всех окон сайта: приподнятая подложка и рамка цветом
       клуба, приглушённым — обводка не должна спорить с содержимым */
    background: var(--bg-elevated);
    border: var(--bw-hair) solid var(--alpha-lime-30);
    border-radius: var(--radius-lg);
    padding: 26px 24px;
    box-shadow: 0 20px 60px var(--alpha-black-50);
    animation: dbModalIn 0.22s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes dbModalIn { from { opacity: 0; transform: translateY(14px) scale(0.97); } }

.db-modal-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 16px;
}

/* Размер окна — как в админке: 1.1rem заголовок, 0.9rem текст. Прежние
   1.3 и 1 были крупнее всего остального на сайте, и окно выпирало */
.db-modal-title {
    margin: 0;
    font-size: var(--fs-md);
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text-primary);
}

.db-modal-close {
    background: none;
    border: none;
    color: var(--text-muted);
    font-size: var(--fs-md);
    line-height: 1;
    padding: 2px 6px;
    cursor: pointer;
}

.db-modal-close:hover { color: var(--text-primary); }

.db-modal-text {
    font-size: var(--fs-sm);
    line-height: 1.65;
    color: var(--text-primary);
    margin: 0 0 18px;
}

.db-modal-steps {
    margin: 0 0 20px;
    padding-left: 22px;
    font-size: var(--fs-base);
    line-height: 1.65;
    color: var(--text-secondary);
}

.db-modal-steps li::marker { color: var(--accent); font-weight: 700; }

.db-modal-actions {
    display: flex;
    gap: 10px;
    justify-content: flex-end;
    flex-wrap: wrap;
}

/* Кнопки окна — в одну ступень с кнопками админки: 10×20 при кегле 0.85.
   Общие 12×32 при 0.9 делали окно шире содержимого */
.db-modal-actions .db-btn {
    padding: 8px 20px;
    font-size: var(--fs-sm);
}

.db-modal-wait {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 18px;
    padding-top: 16px;
    border-top: var(--bw-hair) solid var(--border-subtle);
    font-size: var(--fs-sm);
    color: var(--text-secondary);
}

.db-modal-spinner {
    width: 14px;
    height: 14px;
    border: var(--bw-thick) solid var(--alpha-white-12);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: dbSpin 0.8s linear infinite;
    flex: 0 0 auto;
}

@keyframes dbSpin { to { transform: rotate(360deg); } }

.db-modal-done { color: var(--success); }

/* Ключевые слова в окнах выделяем — сплошной серый текст не читается */
.db-modal-done { font-weight: 600; }
.db-modal-steps li { padding-left: 4px; }
.db-modal-steps li::marker { font-size: var(--fs-base); }
.db-tg-list li::marker { font-size: var(--fs-md); }

/* Ссылка на платежи в карточке членства: сам список живёт в своём разделе,
   повторять его здесь незачем */
/* Сумма платежа — один вес на все виды. На широком она шла 400 и ничем
   не отличалась от способа и примечания в той же строке */
.db-pay-amount {
    font-weight: 600;
}

/* ШИРИНЫ КОЛОНОК ОБЩЕЙ ТАБЛИЦЫ НАПИСАНЫ ДЛЯ МАТЧЕЙ, А НЕСУТ ИХ ПЯТЬ
   РАЗНЫХ ТАБЛИЦ. `.db-matches-table td:nth-child(2)` отдаёт второй
   колонке 34 % — у матчей это соперник, которому ширина нужна, а у
   платежей это СУММА: на 1024 она получала 200 при нужных 90, а
   примечание сжималось до 93 и просило 123. Таблица переливалась на
   619 при 589, и это была настоящая прокрутка вбок на десктопе.
   Отвечаем тем же весом — удвоением класса, как в куске 59, — и
   правим ПАРУ «заголовок + клетка», а не половину:
   сумма по содержимому, примечание забирает остаток. */
.db-pay-table.db-matches-table th:nth-child(2),
.db-pay-table.db-matches-table td:nth-child(2) {
    /* Раскладка таблицы жёсткая (`table-layout: fixed`, :3237), и процент
       здесь буквален: первая редакция правила поставила 1 % и колонка
       схлопнулась в 7 пикселей — замер поймал это сразу. Ширина названа
       числом: «1 000 000 KGS» просит 95 при кегле 14 и весе 600 */
    width: 140px;
    white-space: nowrap;
}

.db-pay-table.db-matches-table th:nth-child(5),
.db-pay-table.db-matches-table td:nth-child(5) {
    width: auto;
}

/* Примечание пишет человек, и в нём встречается слово длиннее колонки:
   на 1024 «Мастерс-этап», просило 123 при 113, и таблица снова просила
   599 при 589. Перенос внутри слова — а не отсечка: примечание читают
   целиком, в отличие от имени в строке баттла */
.db-pay-note {
    overflow-wrap: anywhere;
}

/* ПЛИТКИ ПЛАТЕЖЕЙ БЕРУТ ТУ ЖЕ ЕДИНСТВЕННУЮ СТУПЕНЬ, что баланс и окно
   обмена. Было 26 с межстрочным 1.5 на широком и 18 с 1.1 на узком —
   один уровень с двумя межстрочными и двумя кеглями */
#db-payments .kst-value {
    font-size: var(--db-fs-cifra);
    line-height: var(--lh-tight);
}

.db-pay-link {
    margin-top: 14px;
    padding-top: 12px;
    border-top: var(--bw-hair) solid var(--border-subtle);
}

.db-pay-link-btn {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    font-size: var(--fs-sm);
    color: var(--accent);
    cursor: pointer;
}

.db-pay-link-btn:hover { text-decoration: underline; }


/* ---- Вызовы: исход сыгранного матча и отметка баттла ---- */
.db-invite-score {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: var(--fs-xs);
    font-weight: 700;
}

.db-invite-outcome {
    font-size: var(--fs-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 2px 7px;
    border-radius: var(--radius-sm, 6px);
}

.db-invite-outcome.win {
    background: rgba(52,199,89,0.18);
    color: var(--success);
}

.db-invite-outcome.loss {
    background: rgba(255,59,48,0.18);
    color: var(--danger);
}

.db-invite-battle {
    display: inline-flex;
    align-items: center;
    margin-top: 4px;
    font-size: var(--fs-xs);
    font-weight: 600;
    color: var(--accent);
    text-decoration: none;
}

.db-invite-battle:hover {
    text-decoration: underline;
}

/* ---- Достижения: сколько осталось до значка ---- */
.db-badge-progress {
    margin-left: 4px;
    padding: 4px 8px;
    border-radius: var(--radius-full);
    background: var(--alpha-white-08);
    color: var(--text-secondary, var(--text-muted));
    font-size: var(--fs-xs);
    font-weight: 700;
    white-space: nowrap;
}

/* Метка рода матча.

   В историю попадает всё сыгранное, а в зачёт — только рейтинговые одиночные.
   Без метки это выглядит как ошибка: матчей одиннадцать, побед в статистике
   четыре. Цветом выделен только «рейтинг» — остальные метки поясняют, а не
   спорят за внимание. Раньше здесь стояла одна иконка на парные матчи. */
.db-match-tag {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 7px;
    border-radius: var(--radius-full);
    font-size: var(--fs-2xs);
    font-weight: 700;
    white-space: nowrap;
    vertical-align: middle;
}
.db-tag-rating   { background: var(--alpha-lime-12); color: var(--accent); }
.db-tag-friendly { background: var(--alpha-white-12); color: var(--text-muted); }
.db-tag-battle   { background: rgba(255,159,67,0.16); color: #FF9F43; }
.db-tag-doubles  { background: var(--info-subtle); color: var(--info); }
.db-tag-mixed    { background: rgba(168,85,247,0.18); color: var(--role-manager); }

/* С кем игрок был в паре: своя половина пары в матче не записана, и без
   этой строки за ней приходилось идти в сетку турнира.
   Подпись остаётся тусклой, а имя берёт акцент: строка стоит под названием
   турнира, и двумя оттенками серого они сливались в одно пятно */
.db-match-mate {
    margin-top: 3px;
    font-size: var(--fs-xs);
    color: var(--text-muted);
}

.db-match-mate-name {
    /* Тот же лайм, что у заглушки фото и кнопки H2H: в одной таблице
       третий оттенок только дробил бы картинку */
    color: var(--accent);
    font-weight: 600;
}


/* Запись, добавленную админом руками, открывать нечего: такого турнира в
   базе нет. Строка выглядит как остальные, но не ведёт себя как ссылка */



/* ---- Уведомления в кабинете ----
   Колокольчик показывает последние двадцать и годится, чтобы заметить новое.
   Здесь — вся история, чтобы можно было вернуться к старому сообщению */

/* Каждое уведомление — своя карточка. Одним списком с тонкими линиями они
   сливались в сплошной текст */


/* Отметить всё сразу — по кнопке, а не молча при открытии раздела */
.db-notif-all-read {
    background: none;
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-full);
    padding: 4px 12px;
    color: var(--text-muted);
    font-size: var(--fs-xs);
    cursor: pointer;
}

.db-notif-all-read:hover {
    color: var(--text-primary);
    border-color: var(--alpha-white-20);
}





/* Заголовок и дата — одной строкой, дата прижата вправо */

.db-notif-title {
    font-weight: 600;
    font-size: var(--fs-sm);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Текст в одну строку с обрезкой: целиком он открывается в окне */
.db-notif-msg {
    font-size: var(--fs-sm);
    color: var(--text-secondary);
    line-height: 1.3;
    margin-top: 4px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}


/* ---- Турниры в «Моих играх»: та же таблица, что и матчи ----
   Раньше это были высокие карточки с фотографией турнира, а кнопка «Снять
   заявку» стояла за пределами карточки, справа от неё. Строк по десять на
   странице — карточками блок уезжал на два экрана. */
/* Название целиком: у клуба два турнира с общим началом и разным хвостом
   («…Futures» и «…Masters»). В обрезанном виде строки неразличимы, и человек
   снимает заявку не с того турнира */
.db-tour-table .db-match-tournament {
    max-width: none;
    white-space: normal;
    overflow: visible;
}

/* Слой цели 44 ниже требует своей системы координат */
.db-tour-link {
    position: relative;
    color: var(--text-primary, var(--neutral-white));
    text-decoration: none;
}

.db-tour-link:hover {
    color: var(--accent);
}

.db-tour-status {
    white-space: nowrap;
    font-size: var(--fs-xs);
    color: var(--text-muted);
}

/* Статус заявки берём общий (.kslt-status), только ужимаем под строку таблицы */
.db-tour-table .kslt-status {
    padding: 5px 11px;
    font-size: var(--fs-xs);
    gap: 6px;
}

.db-tour-table .kslt-status::before {
    width: 6px;
    height: 6px;
}

.db-tour-pts {
    text-align: right;
    white-space: nowrap;
    font-weight: 600;
    color: var(--accent);
}

.db-tour-table .db-withdraw-btn {
    padding: 4px 10px;
    font-size: var(--fs-xs);
}

/* СВОЙ ПОСТРАНИЧНЫЙ ПЕРЕКЛЮЧАТЕЛЬ УБРАН — 11.10.
   Кабинет рисовал свою полосу страниц (.db-pager, .db-pager-btn): третью
   копию того, что с 07.10 живёт одним определением в js/polosa-stranic.js.
   После перехода на общую полосу эти правила не рисовал уже никто:
   проверено поиском по js/, css/, pages/ и mobile/www/ — единственным
   местом, где оставалось имя, был этот файл. */

/* ---- Значок статуса ----
   Классы цвета стояли прямо на ячейке таблицы, и «Принято» заливало всю
   её высоту зелёным прямоугольником во всю колонку. Цвет живёт на значке. */
.db-status-badge {
    display: inline-block;
    padding: 4px 8px;
    border-radius: 6px;
    font-size: var(--fs-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    white-space: nowrap;
}

/* Соперник ведёт на свою страницу */
/* Слой цели 44 ниже требует своей системы координат */
.db-opp-link {
    position: relative;
    color: inherit;
    text-decoration: none;
}

.db-opp-link:hover {
    color: var(--accent);
}


/* ---- Строка чисел категории ----
   Числа слева, итог справа. Раньше всё жалось к левому краю, и правая
   половина блока пустовала — глазу не за что было зацепиться. */
.kst-cat-top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
}

.kst-cat-rate {
    text-align: right;
    flex-shrink: 0;
}

.kst-cat-rate b {
    display: block;
    font-size: var(--fs-xl);
    line-height: 1.1;
    font-weight: 700;
    color: var(--accent);
}

.kst-cat-rate span {
    display: block;
    margin-top: 1px;
    font-size: var(--fs-2xs);
    letter-spacing: 0.2px;
    text-transform: uppercase;
    color: var(--text-muted);
}

/* Матчей ещё не было — процента нет, но столбец остаётся на месте */
.kst-cat-rate-empty b {
    color: var(--text-muted);
}

/* ---- Ответ на вызов прямо в строке ----
   Раньше принять вызов можно было только из Telegram: на сайте кнопок не
   было вовсе. */
/* ОДНО ОБЪЯВЛЕНИЕ НА ОДИН КЛАСС. `justify-content: center` стоял вторым
   блоком строкой ниже по файлу (12.10) — два объявления на одно понятие,
   и выигрывало второе. Сведено сюда. */
.db-chal-actions {
    display: flex;
    justify-content: center;
    gap: 6px;
    flex-wrap: wrap;
}

.db-chal-btn {
    /* Высота — ступень xs 28 по `Button 17:82`, и держится СВОЙСТВОМ:
       «Height is set by the height property, never by vertical padding».
       Замер 12.10 дал 25 — поля считали от межстрочного, и кнопка уезжала
       при каждой правке кегля. Межстрочный 1 — ступень одиночной строки */
    display: inline-flex;
    align-items: center;
    min-height: 28px;
    line-height: 1;
    padding: 4px 10px;
    font-size: var(--fs-xs);
    font-weight: 500;
    white-space: nowrap;
    color: var(--text-muted);
    background: none;
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-sm, 6px);
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
}

.db-chal-btn:disabled {
    opacity: 0.5;
    cursor: default;
}

.db-chal-yes:hover:not(:disabled) {
    color: #0A0A0A;
    background: var(--accent);
    border-color: var(--accent);
}

.db-chal-no:hover:not(:disabled),
.db-chal-off:hover:not(:disabled) {
    color: var(--danger);
    border-color: rgba(255, 107, 107, 0.5);
}

/* Сообщение вызова в строке: курсивом, чтобы отличалось от названий */
.db-chal-msg {
    font-style: italic;
}

/* Кнопки ответа стоят по центру своей колонки, как и значок статуса */
/* Вызов уже отвечен — вместо кнопок строка с исходом */
.db-modal-answered {
    padding: 14px 20px 18px;
    text-align: right;
    font-size: var(--fs-sm);
    color: var(--text-muted);
}

/* Направление вызова: свои и чужие лежат в одном списке */
.db-chal-dir {
    text-transform: lowercase;
    opacity: 0.75;
}

/* Соперник — самая содержательная колонка: имя, направление, сообщение и
   место. Правило только для баттлов: в таблице матчей оно раздувало вторую
   колонку и выталкивало H2H за край */
/* Первые две колонки одной ширины во всех трёх таблицах «Моих игр» —
   матчи, баттлы, приглашения. Разделы стоят друг под другом, и столбцы,
   начинающиеся в разных местах, читаются как сбитая вёрстка */
/* Жёсткая раскладка: при обычной браузер считает ширины пожеланием и
   перекраивает колонки под содержимое — длинное название турнира ужимало
   «Дату», и разделы разъезжались по вертикали */
.db-matches-table {
    table-layout: fixed;
}

.db-matches-table td:nth-child(1),
.db-matches-table th:nth-child(1) {
    width: 110px;
}

.db-matches-table td:nth-child(2),
.db-matches-table th:nth-child(2) {
    width: 34%;
}

/* Закрытая категория — приглушённой строкой: игрок в ней не выступает */
.db-sidebar-rating-off {
    opacity: 0.45;
}

/* Таблицу лучше прокрутить, чем обрезать: на узком экране столбцы всё
   равно не влезут, а молча пропавший H2H выглядит как поломка */
.db-subsection-body {
    overflow-x: auto;
}



/* Длинному содержимому в жёсткой раскладке нужен явный перенос */
.db-matches-table .db-match-tournament,
.db-matches-table .db-chal-msg {
    overflow: hidden;
    text-overflow: ellipsis;
}

.db-matches-table .db-match-opponent {
    min-width: 0;
}

.db-matches-table .db-match-opponent > span {
    min-width: 0;
    overflow: hidden;
}


/* ---- Уведомления: та же таблица, что в «Моих играх» ----
   Раздел жил своей вёрсткой — карточками с датой справа — и выглядел
   чужим среди остальных разделов кабинета. */
.db-notif-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}

.db-notif-filters {
    display: flex;
    gap: 4px;
    flex-wrap: wrap;
}

/* СЛОЙ ЦЕЛИ 44 У ВСЕГО, ЧТО СТОИТ НА СТУПЕНИ НИЖЕ 44 — решение 04.10,
   применяется без нового разбора: ступень красится, палец получает 44 */
.db-chip::after,
.db-badges-expand-btn::after,
.db-tg-btn::after,
.db-btn-sm::after,
.db-matches-table .db-opp-link::after,
.db-matches-table .db-tour-link::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    min-width: 44px;
    min-height: 44px;
    width: 100%;
    height: 100%;
    transform: translate(-50%, -50%);
}

/* Фильтр — теми же таблетками, что и остальные переключатели кабинета */
/* FILTER CHIP 49:77: 36 высотой, поля 16. Чип фильтрует список НА МЕСТЕ —
   вкладка переключает весь вид, и описание прямо запрещает их путать.
   Было 32 из полей 5/14 */
.db-chip {
    position: relative;
    display: inline-flex;
    align-items: center;
    min-height: 36px;
    padding: 0 16px;
    line-height: var(--lh-none);
    font-size: var(--fs-xs);
    color: var(--text-muted);
    background: none;
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-full);
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
}

.db-chip:hover {
    color: var(--text-primary);
    border-color: var(--alpha-white-20);
}

.db-chip.active {
    color: #0A0A0A;
    background: var(--accent);
    border-color: var(--accent);
    font-weight: 600;
}

.db-notif-table td:nth-child(1) { width: 110px; }
.db-notif-table td:nth-child(2) { width: 44px; text-align: center; }

/* Межстрочный назван ЗДЕСЬ, в базе уровня: в медиа стояло 1.3, а база
   наследовала 1.5 — один уровень с двумя значениями */
.db-notif-type {
    font-size: var(--fs-base);
    line-height: var(--lh-snug);
}

.db-notif-row {
    cursor: pointer;
}

.db-notif-row:hover td {
    background: var(--alpha-white-04);
}

/* Непрочитанное — лаймовой полосой слева, а не рамкой вокруг строки */
.db-notif-row.unread td:first-child {
    box-shadow: inset 3px 0 0 var(--accent);
}

.db-notif-row.unread .db-notif-title {
    color: var(--text-primary);
}

.db-notif-table .db-notif-title {
    font-weight: 600;
    font-size: var(--fs-sm);
    white-space: normal;
}

.db-notif-table .db-notif-msg {
    font-size: var(--fs-sm);
    color: var(--text-secondary);
    margin-top: 4px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}


/* Столбцы статуса и H2H в баттлах: значок статуса налезал на кнопку, когда
   подпись длинная. Даём каждому свою ширину и запрещаем перенос */
.db-battles-table td:nth-child(5),
.db-battles-table th:nth-child(5) {
    width: 150px;
    white-space: nowrap;
}

.db-battles-table td:nth-child(6),
.db-battles-table th:nth-child(6) {
    width: 70px;
}


/* Значок статуса не вылезает за свою ячейку, какой бы ни была подпись */
.db-battles-table .db-status-badge {
    display: inline-block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    vertical-align: middle;
}

/* ЗНАЧОК ПРИВЕДЁН К КОМПОНЕНТУ `Badge 27:53`, И РАСХОЖДЕНИЙ БЫЛО ТРИ.
   Описание: «Height 24px, horizontal padding 8px, radius/full, 11px Medium
   uppercase with 0.5px letter spacing». В коде стояли вес 700, радиус 6 и
   высота 23 — ни одно из трёх не бралось из компонента.
   Краска: контраст подписи был 4.10 при норме AA 4.5 — посчитано на
   СМЕШАННОМ фоне rgb(51,34,32), а не на прозрачном слое. Тон danger взят из
   шкалы (--red-500 #FF4757) вместо местного #F44336: он светлее, и подпись
   поднимается выше порога. Прозрачная краска смешивается с фоном, а не
   отбрасывается. */
.db-status-badge {
    min-height: 24px;
    padding: 0 8px;
    border-radius: 999px;
    font-weight: 500;
    letter-spacing: 0.5px;
}


/* ============================================
   Приглашения поиграть
   ============================================
   Принять или отклонить можно прямо в кабинете. Раньше это делалось
   только кнопками в Телеграм-боте, и человек без Телеграма ответить
   не мог вовсе. */

/* Ширины заданы явно, иначе имя соперника растягивает таблицу за край
   карточки. Колонка действий шире содержимого и прижата вправо — так
   кнопка стоит у края, а не посреди строки */
.db-invites-table {
    table-layout: fixed;
    width: 100%;
}

.db-invites-table th:nth-child(1),
.db-invites-table td:nth-child(1) { width: 76px; }

.db-invites-table th:nth-child(3),
.db-invites-table td:nth-child(3) { width: 104px; }

.db-invites-table th:nth-child(4),
.db-invites-table td:nth-child(4) { width: 210px; }

.db-invites-table .db-match-opponent span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Направление — стрелкой у имени: вошло вместо целой колонки */
.db-inv-dir {
    flex-shrink: 0;
    margin-left: 6px;
    color: var(--text-muted);
    font-size: var(--fs-sm);
}

/* Кнопки кабинета выложены как блоки, поэтому выравнивание текстом на
   них не действует — прижимаем саму строку кнопок к правому краю */
.db-inv-actions {
    white-space: nowrap;
}

.db-inv-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
}

/* Межстрочный управляющего элемента один: в блоке целей ниже стоял 1,
   здесь 1.3 — и выигрывал тот, что ниже по файлу */
.db-btn-sm {
    position: relative;
    padding: 0 12px;
    font-size: var(--fs-xs);
    border-radius: var(--radius-full);
    line-height: 1.3;
}

/* Согласие — цветом клуба, отказ — тихой обводкой: одинаково заметные
   кнопки заставляли выбирать между двумя равными, хотя выбор неравный */
.db-invites-table .db-inv-accept {
    background: var(--accent);
    color: var(--neutral-black);
    border: var(--bw-hair) solid var(--accent);
    font-weight: 600;
}

.db-btn-ghost {
    background: transparent;
    border: var(--bw-hair) solid var(--border-light);
    color: var(--text-muted);
}

.db-btn-ghost:hover {
    border-color: var(--border-light);
    color: var(--text-secondary);
}

@media (max-width: 640px) { /* было 720px: границы в медиа — только 640 и 992 */
    /* На узком экране колонки делит браузер: жёсткие ширины не помещаются */
    .db-invites-table { table-layout: auto; }
    .db-invites-table th:nth-child(3),
    .db-invites-table td:nth-child(3),
    .db-invites-table th:nth-child(4),
    .db-invites-table td:nth-child(4) { width: auto; }
    .db-inv-actions { justify-content: flex-start; }
    .db-btn-sm { padding: 4px 8px; font-size: var(--fs-xs); }
}

/* Карточка контактов собеседника — то, чем обменялись по согласию */
.db-inv-contacts {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.db-inv-contacts-name {
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: 4px;
}

.db-inv-contact {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--bg-elevated);
    color: var(--text-primary);
    text-decoration: none;
    font-size: var(--fs-sm);
    transition: border-color 0.2s;
}

/* Каждая сеть подсвечивается своим цветом, а не общим лаймом: строка
   узнаётся по цвету раньше, чем прочитана. Так же сделано на карточке
   игрока и в уведомлении из колокольчика */
.db-inv-contact:hover { border-color: var(--accent); }
.db-inv-contact:hover .db-inv-contact-label { color: var(--accent); }

.db-inv-contact--wa:hover { border-color: #25D366; }
.db-inv-contact--wa:hover .db-inv-contact-label { color: #25D366; }

.db-inv-contact--tg:hover { border-color: #2AABEE; }
.db-inv-contact--tg:hover .db-inv-contact-label { color: #2AABEE; }

.db-inv-contact--ig:hover { border-color: #E1306C; }
.db-inv-contact--ig:hover .db-inv-contact-label { color: #E1306C; }

/* Раньше слева стояла подпись «Telegram», «Instagram» — колонка текста
   рядом с самим именем читалась тяжело. Теперь знак сети */
.db-inv-contact-label {
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
    color: var(--text-muted);
}

.db-inv-contact-label svg {
    width: 18px;
    height: 18px;
}

@media (max-width: 640px) {
    .db-inv-actions { text-align: left; }
    .db-inv-contact-label svg { width: 16px; height: 16px; }
}

/* ---- «Мои матчи»: что ждёт ответа ----
   Полоса над архивом матчей. Появляется, только когда есть дела: пустая
   рамка ради пустоты хуже, чем её отсутствие. */
.mma-box {
    border: var(--bw-hair) solid var(--accent-glow);
    border-radius: 12px;
    padding: 14px 16px;
    margin-bottom: 16px;
    background: var(--alpha-lime-06);
}

.mma-title {
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.6px;
    color: var(--accent);
    font-weight: 700;
    margin-bottom: 10px;
}

.mma-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 0;
    flex-wrap: wrap;
}

.mma-row + .mma-row { border-top: var(--bw-hair) solid var(--border-subtle); }

.mma-when {
    font-size: var(--fs-xs);
    color: var(--text-muted);
    flex: 0 0 58px;
}

.mma-main { flex: 1 1 200px; min-width: 0; }

.mma-name {
    color: var(--text-primary);
    font-weight: 600;
    font-size: var(--fs-sm);
}

.mma-sub {
    color: var(--text-muted);
    font-size: var(--fs-xs);
    margin-top: 2px;
}

.mma-sub b { color: var(--text-secondary); }

.mma-btn {
    padding: 8px 14px;
    border-radius: 8px;
    border: 0;
    cursor: pointer;
    background: var(--accent);
    color: #0a0a0a;
    font-weight: 700;
    font-size: var(--fs-xs);
}

.mma-wait {
    font-size: var(--fs-xs);
    color: var(--text-muted);
}

/* ---- Привязка карточки игрока ----
   Стоит первым блоком в «Играх», пока карточка не найдена: без неё в
   кабинете нет ни рейтинга, ни истории. */
.db-link-card {
    background: var(--alpha-lime-06);
    border: var(--bw-hair) solid var(--alpha-lime-20);
    border-radius: 12px;
    padding: 18px 20px;
    margin-bottom: 20px;
}

.db-link-card-pending {
    background: rgba(255, 179, 0, 0.06);
    border-color: rgba(255, 179, 0, 0.3);
}

.db-link-title {
    font-weight: 700;
    font-size: var(--fs-base);
    margin-bottom: 6px;
    color: var(--text-primary);
}

.db-link-text {
    color: var(--text-secondary);
    font-size: var(--fs-sm);
    line-height: 1.5;
    margin-bottom: 12px;
}

.db-link-input {
    width: 100%;
    max-width: 340px;
}

.db-link-results {
    margin-top: 10px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    max-width: 480px;
}

.db-link-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 8px 12px;
    border-radius: 8px;
    background: var(--alpha-white-04);
}

.db-link-name {
    font-weight: 600;
    color: var(--text-primary);
}

.db-link-meta {
    font-size: var(--fs-xs);
    color: var(--text-muted);
    margin-top: 2px;
}

/* Две кнопки в ряд: «это я» и «не я» */
.db-link-actions {
    display: flex;
    gap: 10px;
    margin-top: 12px;
    flex-wrap: wrap;
}

.db-link-take,
.db-link-other {
    padding: 6px 14px;
    border-radius: 6px;
    border: var(--bw-hair) solid var(--accent);
    background: var(--alpha-lime-12);
    color: var(--accent);
    font-weight: 600;
    font-size: var(--fs-sm);
    cursor: pointer;
    white-space: nowrap;
}

.db-link-take:disabled {
    opacity: 0.5;
    cursor: default;
}

.db-link-other {
    margin-top: 4px;
    border-color: var(--alpha-white-20);
    background: transparent;
    color: var(--text-secondary);
}

.db-link-empty {
    color: var(--text-muted);
    font-size: var(--fs-sm);
    padding: 6px 2px;
}

/* Заявку отклонили — человек должен это увидеть, а не пустой поиск */
.db-link-rejected {
    background: rgba(255, 59, 48, 0.1);
    border: var(--bw-hair) solid rgba(255, 59, 48, 0.3);
    color: var(--danger);
    border-radius: 8px;
    padding: 8px 12px;
    font-size: var(--fs-sm);
    margin-bottom: 12px;
}

/* ========================================
   Карточка игрока в «Моём профиле»
   Фото, имя, очки с местом, NTRP, счёт побед, QR и ссылка на публичную
   страницу. Раньше это жило только в боковом меню, а на телефоне оно
   скрыто — и человек видел в профиле одну форму редактирования
   ======================================== */

.db-profile-card {
    margin-bottom: 16px;
}

.db-profile-card-head {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 12px;
}

.db-profile-card-photo {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}

.db-profile-card-initials {
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--alpha-lime-12);
    color: var(--accent);
    font-weight: 700;
}

.db-profile-card-name {
    font-size: var(--fs-base);
    font-weight: 700;
    color: var(--text-primary);
    /* 1.25 не ступень лестницы 1 · 1.1 · 1.3 · 1.5 · 1.65 — замер 11.10 */
    line-height: 1.3;
}

.db-profile-card-rows {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 12px;
}

.db-profile-card-row {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    font-size: var(--fs-sm);
    color: var(--text-secondary);
}

.db-profile-card-row span:last-child {
    color: var(--accent);
    font-weight: 600;
    white-space: nowrap;
}

/* Закрытая категория: игрок в ней больше не выступает */
.db-profile-card-row.is-off {
    opacity: 0.45;
}

.db-profile-card-actions {
    display: flex;
    gap: 8px;
}

.db-profile-card-actions .db-btn {
    flex: 1;
    text-align: center;
}

.db-profile-card-empty {
    font-size: var(--fs-sm);
    color: var(--text-muted);
}

/* ЗДЕСЬ БЫЛ БЛОК НА <=640 — СНЯТ 12.10 КАК МЁРТВЫЙ, ПОСЛЕ ЗАМЕРА.
   Он просил `.db-profile-card-actions { flex-direction: column }` и
   `.db-profile-card-row { font-size: var(--fs-sm) }`. Первое перебивал
   блок на <=992 НИЖЕ по файлу — при равном весе побеждает последний,
   и замер на 390 давал `row`, а не `column`. Второе повторяло базовое
   правило слово в слово. Мёртвым названо по замеру, а не по виду */

/* ============================================
   ВОЗДУХ НАД РАЗДЕЛОМ НА ТЕЛЕФОНЕ
   ============================================ */

/* От плиток до заголовка раздела набегало 117 пикселей пустоты, и она
   складывалась из трёх отступов сразу:

   — .db-layout отодвигал содержимое на 64 пикселя под шапку сайта, хотя
     плитки стоят выше макета и место под шапку занимают сами;
   — .db-content добавлял 12;
   — общее правило для section из style.css — ещё 40 сверху.

   Разделы кабинета не публичные секции сайта, отступ «как у блока
   лендинга» им не нужен: наверху и так плитки с рамкой. */

@media (max-width: 992px) {
    .db-layout {
        padding-top: 0;
    }

    .db-content {
        padding: 12px 12px 24px;
    }

    /* Фон у самой секции — значит и воздух над заголовком должен быть
       внутри неё. Когда отступ стоял у .db-content, он уходил наружу
       чёрной полосой, а заголовок так и лежал на верхнем крае блока.

       По бокам полей нет: карточки подразделов идут во всю ширину блока,
       а от края их отделяет сам блок. Отступ нужен только заголовку */
    .db-section {
        padding: 12px 0;
    }

    .db-section-title {
        padding: 0 12px;
        margin-bottom: 10px;
    }
}

/* ============================================
   СТРОКА ВМЕСТО КАРТОЧКИ
   ============================================ */

/* Когда в блоке одно значение — категория игрока без очков, — заголовок
   с чертой и абзац под ним съедали 113 пикселей ради одного слова */
.db-card-line {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 16px;
}

.db-card-line-label {
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--text-primary);
}

.db-card-line-value {
    font-size: var(--fs-base);
    font-weight: 600;
    color: var(--accent);
}

/* ============================================
   ПЛИТКИ ЦИФР НА ТЕЛЕФОНЕ
   ============================================ */

/* Плитка была 82 пикселя при 34 пикселях содержимого: больше половины
   пустой. Ужимаем поля и кегль — четыре плитки становятся короче на
   сорок пикселей в сумме */

@media (max-width: 992px) {
    .kst-card {
        padding: 8px;
    }


    .kst-label {
        font-size: var(--fs-2xs);
    }
}

/* ============================================
   УВЕДОМЛЕНИЯ НА ТЕЛЕФОНЕ
   ============================================ */

/* Таблица из трёх колонок телефону не по размеру: на дату уходило 110
   пикселей из 366, и текст уведомления сжимался в одну строку с
   многоточием — «Менеджер отклонил заявку…» и всё, сути не видно.

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

@media (max-width: 992px) {
    .db-notif-table,
    .db-notif-table tbody,
    .db-notif-row td {
        display: block;
        width: auto;
    }

    .db-notif-table td:nth-child(1),
    .db-notif-table td:nth-child(2) {
        width: auto;
    }

    .db-notif-row {
        display: grid;
        grid-template-columns: 24px 1fr;
        grid-template-areas:
            "icon text"
            ".    date";
        column-gap: 8px;
        padding: 12px 12px;
        border-bottom: var(--bw-hair) solid var(--border-subtle);
    }

    .db-notif-row td {
        padding: 0;
        border: none;
    }

    /* Полоса непрочитанного переезжает с ячейки даты на всю запись:
       колонок больше нет, и красить первую ячейку стало нечего */
    .db-notif-row.unread {
        box-shadow: inset 3px 0 0 var(--accent);
    }

    .db-notif-row.unread td:first-child {
        box-shadow: none;
    }

    .db-notif-row .db-notif-type {
        grid-area: icon;
        text-align: left;
        font-size: var(--fs-base);
    }

    .db-notif-row td:last-child {
        grid-area: text;
    }

    .db-notif-row .db-match-date {
        grid-area: date;
        margin-top: 4px;
        font-size: var(--fs-2xs);
        color: var(--text-muted);
    }

    .db-notif-table .db-notif-title {
        font-size: var(--fs-sm);
    }

    /* Две строки вместо одной с многоточием */
    .db-notif-table .db-notif-msg {
        font-size: var(--fs-xs);
        white-space: normal;
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        overflow: hidden;
    }
}

/* ============================================
   ПОЛОСА ФИЛЬТРОВ УВЕДОМЛЕНИЙ — ОДНИМ РЯДОМ
   ============================================ */

/* Две подписи в одной кнопке: широкому экрану — полная, телефону —
   короткая. Иначе «Отметить все прочитанными» не влезало рядом с
   фильтрами и уезжало во второй ряд */
/* Узкая подпись шапки таблицы — межстрочный назван, был браузерный */
.db-label-narrow {
    display: none;
    line-height: var(--lh-snug);
}

@media (max-width: 992px) {
    .db-label-wide {
        display: none;
    }

    .db-label-narrow {
        display: inline;
    }

    /* Ряд один и не переносится: фильтры слева, кнопка справа */
    .db-notif-bar {
        flex-wrap: nowrap;
        gap: 8px;
    }

    .db-notif-filters {
        flex-wrap: nowrap;
        min-width: 0;
    }

    .db-notif-bar .db-chip {
        padding: 4px 8px;
        font-size: var(--fs-xs);
        white-space: nowrap;
    }

    .db-notif-all-read {
        flex: 0 0 auto;
        padding: 4px 8px;
        font-size: var(--fs-xs);
        white-space: nowrap;
    }
}

/* Типов уведомлений бывает больше трёх — вызовы, турниры, членство.
   Тогда фильтры не помещаются в строку, и лишние листаются пальцем,
   а кнопка остаётся на месте справа */
@media (max-width: 992px) {
    .db-notif-filters {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
    }

    .db-notif-filters::-webkit-scrollbar {
        display: none;
    }
}

/* ============================================
   ПОЛЯ ВВОДА НА ТЕЛЕФОНЕ
   ============================================ */

/* Ровно 16 пикселей: при меньшем кегле Safari на iPhone увеличивает
   страницу, как только человек ставит курсор в поле, и обратно она уже
   не возвращается. В кабинете поля были 15.2 — профиль и смена пароля */

@media (max-width: 992px) {
    .db-section input:not([type="checkbox"]):not([type="radio"]),
    .db-section select,
    .db-section textarea {
        font-size: var(--fs-base);
        /* Межстрочный у select и textarea оставался браузерным (normal) —
           единственные два уровня кабинета мимо лестницы. 1.3 — та же
           ступень, что у .db-field-input */
        line-height: 1.3;
    }
}

/* ============================================
   ДОСТИЖЕНИЯ И ЦИФРЫ — ПЛОТНЕЕ НА ТЕЛЕФОНЕ
   ============================================ */

/* «Следующие цели» занимали 156 пикселей из 330: три таблетки шли
   в столбик, потому что каждая была шире половины карточки.
   Ужимаем — и две встают в ряд.

   Цифры статистики: плитка в 72 пикселя держалась на межстрочье 30
   при кегле 20. Убираем запас в самом тексте */

@media (max-width: 992px) {
    .db-badges-next-label {
        font-size: var(--fs-2xs);
        margin-bottom: 6px;
    }

    #dbBadgesNext .db-badges-earned {
        gap: 4px;
        margin-bottom: 8px;
    }

    #dbBadgesNext .db-badge-item {
        padding: 4px 8px;
        font-size: var(--fs-xs);
        gap: 4px;
    }

    #dbBadgesNext .db-badge-progress {
        font-size: var(--fs-2xs);
        padding: 4px 4px;
    }

    .kst-value {
        /* кегль один на все виды; здесь остаётся только межстрочный,
           и он тот же, что в базе уровня */
        line-height: var(--lh-tight);
        margin-bottom: 4px;
    }

    .kst-label {
        font-size: var(--fs-2xs);
    }
}

/* ============================================
   ФОРМА ПРОФИЛЯ НА ТЕЛЕФОНЕ
   ============================================ */

/* Форма занимала 1203 пикселя — четыре с лишним экрана вместе с соцсетями.
   Разбор по строкам показал, что место съедает не сама форма, а текст
   вокруг полей: подсказки под полями шли шестнадцатым кеглем (правило им
   никто не задавал, наследовалось от карточки), а примечание про контакты
   при кегле 14.4 и межстрочье 1.6 занимало 112 пикселей.

   Кегли — по общей шкале телефона: подписи 0.72rem. */

@media (max-width: 992px) {
    .db-field {
        margin-bottom: 14px;
    }


    .db-contacts-note {
        font-size: var(--fs-xs);
        /* межстрочный один на все виды — он назван в базе уровня */
        padding: 8px 12px;
        margin-bottom: 12px;
    }

    .db-avatar-upload {
        gap: 8px;
    }
}

/* ============================================
   КАРТОЧКА ИГРОКА В ПРОФИЛЕ
   ============================================ */

/* Подсветка была «браузерная»: серая рамка и белый текст. У нас она
   лаймовая везде, кабинет не исключение */
.db-btn-outline:hover {
    border-color: var(--accent);
    color: var(--accent);
}

@media (max-width: 992px) {
    /* Карточка собирается как в поиске игрока: фото сверху, имя под ним —
       и то и другое по центру. Раньше фото и имя стояли в строку, и имя
       читалось как приписка сбоку */
    .db-profile-card-head {
        flex-direction: column;
        gap: 8px;
        text-align: center;
    }

    .db-profile-card-photo {
        width: 60px;
        height: 60px;
        object-fit: cover;
    }

    /* ИМЯ НЕ ОПУСКАЕТСЯ ДО СТУПЕНИ СТРОК. Было --fs-sm (14), и строки
       карточки тоже 14 (`.db-profile-card-row`): пара «имя + строки»
       схлопывалась в одну ступень, а на широких видах она отстоит
       (16 против 14). Замер 12.10 на 390: имя при 16 занимает 129 из
       332 доступных — влезает в одну строку на всех трёх языках */
    .db-profile-card-name {
        font-size: var(--fs-base);
    }

    /* Две кнопки в ряд: в столбик они занимали 91 пиксель на двоих при
       ширине в 332 — вдвое больше, чем нужно */
    .db-profile-card-actions {
        flex-direction: row;
        gap: 8px;
    }

    /* ПОДПИСЬ КНОПКИ — 14 ПО `Button 17:82`, А НЕ 12. Кнопка высотой 44
       носила подпись мелкой ступени. Замер 12.10 на 390: самая длинная
       подпись, кыргызская «Менин баракчам», при 14 занимает 119.3 из
       146 доступных — запас 27, в одну строку.
       Поле только горизонтальное: высоту держит `min-height` у
       `.db-btn`, вертикальное поле её не задаёт — `Button 17:82`,
       «height is set by the height property, never by vertical padding» */
    .db-profile-card-actions .db-btn {
        padding: 0 12px;
        font-size: var(--fs-sm);
    }
}

/* ============================================
   ВЫБОР ИЗ ДВУХ — КНОПКАМИ
   ============================================ */

/* Пол: список из двух значений требовал трёх касаний — открыть, выбрать,
   закрыть. Кнопки отвечают за одно, и видно ответ не раскрывая */
.db-choice-row {
    display: flex;
    gap: 8px;
}

/* Высота ступенью: блок целей ниже уже даёт 36, но межстрочный оставался
   браузерным normal — ссылка на шрифт, а не значение */
.db-choice {
    padding: 0 16px;
    font-size: var(--fs-xs);
    font-weight: 600;
    line-height: var(--lh-none);
    color: var(--text-secondary);
    background: var(--bg-elevated);
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: var(--transition-fast);
}

.db-choice:hover {
    border-color: var(--accent);
    color: var(--accent);
}

.db-choice.active {
    background: var(--alpha-lime-12);
    border-color: var(--accent);
    color: var(--accent);
}

/* «Тот же, что телефон» — не кнопка действия, а подсказка, по которой
   можно нажать: стоит под полем на месте прежней пояснительной строки */


@media (max-width: 992px) {
    /* Поле страны под флаг и код — название из списка убрано.
       96 пикселей не хватило: у списка свои поля и стрелка справа, под
       текст оставалось 64, и «+996» обрезалось многоточием */
    .db-phone-country {
        flex: 0 0 116px;
        width: 116px;
        max-width: 116px;
        padding-left: 10px;
        padding-right: 18px;
    }
}

/* Метка слева, кнопки справа — «Пол» занимает одну строку вместо двух */
.db-field-inline {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

.db-field-inline .db-field-label {
    margin-bottom: 0;
}

/* ============================================
   ПОЛЕ ГОДА
   ============================================ */

/* Стрелки вверх-вниз браузер рисует сам, своим белым по тёмному полю, и
   год ими всё равно не набирают — крутить от 1940 до 1990 никто не станет.
   Клавиатура на телефоне остаётся цифровой: inputmode у поля */
#profileBirthYear::-webkit-outer-spin-button,
#profileBirthYear::-webkit-inner-spin-button {
    -webkit-appearance: none;
    appearance: none;
    margin: 0;
}

#profileBirthYear {
    -moz-appearance: textfield;
    appearance: textfield;
}

@media (max-width: 992px) {
    /* Три поля даты — по 100 пикселей на каждое, подписи в них не влезали.
       Кегль и поля поменьше: «Июль» и «Год» помещаются целиком */
    .db-field-row-3 .db-field-input {
        padding: 10px 8px;
        font-size: var(--fs-base);
    }

    .db-field-row-3 select.db-field-input {
        padding-right: 20px;
    }
}

/* ============================================
   БЛОК ТЕЛЕГРАМ-БОТА НА ТЕЛЕФОНЕ
   ============================================ */

/* Перечень того, что придёт в Телеграм, занимал 256 пикселей: пять строк
   девятым кеглем с межстрочьем 1.8. Читают его один раз — до подключения,
   и место под себя он забирал навсегда */

@media (max-width: 992px) {
    .db-tg-block {
        margin-top: 12px;
        padding-top: 12px;
    }

    .db-tg-title {
        font-size: var(--fs-sm);
        margin-bottom: 4px;
    }

    .db-tg-list {
        margin-bottom: 8px;
        padding-left: 16px;
        font-size: var(--fs-xs);
    }

    .db-tg-btn {
        padding: 8px 12px;
        font-size: var(--fs-xs);
    }
}

/* ============================================
   СОХРАНЁННОЕ ПОЛЕ
   ============================================ */

/* Заполненное и уже сохранённое поле показываем приглушённо: видно, что
   там лежит готовый ответ. Стоит поменять значение — поле снова обычное,
   и на фоне остальных сразу заметно, что уйдёт в базу при сохранении */
.db-field-input.is-saved {
    background: var(--alpha-white-04);
    border-color: rgba(255, 255, 255, 0.05);
    color: var(--text-secondary);
}

.db-field-input.is-saved:focus {
    background: var(--bg-elevated);
    color: var(--text-primary);
}

/* ============================================
   ПЛАТЕЖИ НА ТЕЛЕФОНЕ
   ============================================ */

/* Пять колонок — дата, сумма, способ, статус, заметка — в 366 пикселей не
   помещались, и таблица ездила вбок. Как в уведомлениях: строка становится
   карточкой. Сумма крупно справа, остальное подписями слева */

@media (max-width: 992px) {
    .db-pay-table thead {
        display: none;
    }

    .db-pay-table,
    .db-pay-table tbody,
    .db-pay-row td {
        display: block;
    }

    /* То же, что в баллах: проценты из общей таблицы сильнее `auto` */
    .db-pay-table.db-matches-table td {
        width: auto;
    }

    .db-pay-row {
        display: grid;
        grid-template-columns: 1fr auto;
        grid-template-areas:
            "date   amount"
            "method status"
            "note   note";
        gap: 4px 8px;
        padding: 12px 4px;
        border-bottom: var(--bw-hair) solid var(--border-subtle);
    }

    .db-pay-row td {
        padding: 0;
        border: none;
    }

    .db-pay-date {
        grid-area: date;
        font-size: var(--fs-xs);
        color: var(--text-secondary);
    }

    /* АКЦЕНТ НЕСЁТ ВЕС, А КЕГЛЬ — РЕЖИМ СТРОКИ. Было: на широком виде
       сумма 14/400, то есть не выделена вовсе, а здесь 16/700 — выделение
       существовало только на телефоне. Вес теперь один на все виды (600,
       как у очков баллов и счёта баттлов), а кегль ступенью выше соседей
       ТОЛЬКО там, где строка стала списком: в списке сумма — товар */
    .db-pay-amount {
        grid-area: amount;
        text-align: right;
        font-size: var(--fs-base);
        /* Без этого «2 500 KGS» ломалось на три строки: колонка сжималась
           по содержимому, а пробелы разрешали перенос */
        white-space: nowrap;
    }

    .db-pay-method {
        grid-area: method;
        font-size: var(--fs-xs);
        color: var(--text-muted);
    }

    .db-pay-status {
        grid-area: status;
        text-align: right;
        font-size: var(--fs-xs);
    }

    .db-pay-note {
        grid-area: note;
        margin-top: 4px;
        font-size: var(--fs-xs);
        color: var(--text-muted);
    }

    /* Пустое состояние: 80 пикселей отступа сверху и снизу плюс значок в
       48 давали карточку на 282 — почти треть экрана ради одной строки */
    .db-empty {
        padding: 28px 16px;
    }

    .db-empty-icon {
        font-size: var(--fs-2xl);
    }

    .db-empty-title {
        font-size: var(--fs-sm);
    }
}

/* ============================================
   КУПОН СО СКИДКОЙ
   ============================================ */

/* Размеры были вписаны прямо в разметку — те же, что здесь. Разница в
   одном: теперь их видит телефон */
.db-voucher-item {
    padding: 12px 16px;
}

.db-voucher-row {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: flex-start;
    gap: 12px;
}

.db-voucher-main {
    flex: 1;
}

.db-voucher-name {
    font-weight: 600;
    color: var(--alpha-white-72);
    margin-bottom: 4px;
}

.db-voucher-service {
    font-size: var(--fs-xs);
    color: var(--alpha-white-50);
}

/* На широком экране срок стоит под названием, как и стоял: сетку из двух
   колонок держит .db-voucher-row, а этот блок занимает всю первую */
/* Контраст был 3.21 при норме AA 4.5: краска стояла своей альфой 0.35.
   Срок действия не украшение — по нему человек решает, успеет он или нет */
.db-voucher-dates {
    order: 3;
    flex: 1 1 100%;
    margin-top: 4px;
    font-size: var(--fs-xs);
    color: var(--text-muted);
}

.db-voucher-side {
    display: flex;
    align-items: center;
    gap: 8px;
}

.db-voucher-status {
    font-size: var(--fs-xs);
    padding: 4px 8px;
    border-radius: 6px;
}

.db-voucher-qr-btn {
    padding: 4px 8px;
    font-size: var(--fs-xs);
    font-weight: 600;
    color: var(--neutral-black);
    background: var(--accent);
    border: none;
    border-radius: 6px;
    cursor: pointer;
}

@media (max-width: 992px) {
    /* Статус и кнопка «Показать QR» отнимали у левой части половину
       ширины, и «Собутыльник · -100%» ломалось на две строки, а срок —
       на три. Строим в два ряда: сверху имя и статус, снизу срок и QR */
    /* От правого края экрана до кнопки набегало 43 пикселя: поля карточки
       раздела плюс поля самого купона. Прижимаем — статус и кнопка встают
       к краю, как в остальных списках */
    #db-vouchers .db-card {
        padding: 12px;
    }

    .db-voucher-item {
        padding: 12px 8px;
    }

    .db-voucher-row {
        display: grid;
        grid-template-columns: 1fr auto;
        grid-template-areas:
            "main   side"
            "dates  side";
        align-items: start;
        gap: 4px 8px;
    }

    .db-voucher-main {
        grid-area: main;
        min-width: 0;
    }

    /* Статус и кнопка — столбиком в правой колонке: кнопка встаёт прямо
       под «Активный», а срок поднимается к ней слева, а не вплотную */
    .db-voucher-side {
        grid-area: side;
        display: flex;
        flex-direction: column;
        align-items: flex-end;
        gap: 4px;
    }

    .db-voucher-status {
        font-size: var(--fs-2xs);
        padding: 4px 8px;
    }

    .db-voucher-qr-btn {
        padding: 4px 8px;
        font-size: var(--fs-xs);
        white-space: nowrap;
    }

    .db-voucher-name {
        font-size: var(--fs-sm);
        margin-bottom: 4px;
    }

    .db-voucher-service {
        font-size: var(--fs-xs);
    }

    /* Срок уезжает в свой ряд — рядом с кнопкой, а не под именем */
    .db-voucher-dates {
        grid-area: dates;
        align-self: center;
        margin-top: 0;
        font-size: var(--fs-2xs);
    }
}

/* ============================================
   БАЛЛЫ
   ============================================ */

/* Те же размеры, что стояли в разметке — вынесены, чтобы телефон мог их
   переопределить */
/* КРУПНАЯ ЦИФРА КАБИНЕТА — ОДНА СТУПЕНЬ НА ВСЕ ТРИ МЕСТА ЭТОГО КУСКА.
   Было пять ступеней на одно понятие: 3xl у баланса, 2xl в окне обмена,
   xl и md у плиток. Слово Кости 12.10 по макету 749:12 — «сумма одинаково
   сделай, они больше чем другие»: один кегль у баланса, окна и плиток,
   и он крупнее подписей рядом (26 против 11 и 12) и заголовка раздела 21.
   Шесть остальных объявлений .kst-value живут в статистике и админских
   карточках — они сводятся вместе с их куском, здесь не трогаются. */
:root {
    --db-fs-cifra: var(--fs-xl);
}

.db-loy-balance {
    text-align: center;
    padding: 24px 16px 20px;
    background: linear-gradient(135deg, var(--alpha-lime-06) 0%, var(--alpha-lime-06) 100%);
    border: var(--bw-hair) solid var(--accent-subtle);
    border-radius: 12px;
}

/* Контраст был 3.82 при норме AA 4.5 — краска взята из шкалы, а не
   из своей альфы. Разрядка одна на оба вида: было 1.5 и 1 */
.db-loy-balance-label {
    font-size: var(--fs-2xs);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-muted);
    margin-bottom: 8px;
}

.db-loy-balance-value {
    font-size: var(--db-fs-cifra);
    font-weight: 700;
    line-height: var(--lh-none);
    color: var(--accent);
}

.db-loy-balance-unit {
    margin-top: 4px;
    font-size: var(--fs-xs);
    color: var(--text-muted);
}

.db-loy-expiry {
    margin-top: 8px;
    font-size: var(--fs-xs);
    color: var(--text-muted);
}

/* ВЫСОТА СВОЙСТВОМ, А НЕ ПОЛЯМИ — Button 17:82. Было 10/28 и высота
   складывалась сама */
.db-loy-redeem {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    margin-top: 20px;
    padding: 0 24px;
    font-size: var(--fs-sm);
    font-weight: 700;
    line-height: var(--lh-none);
    color: var(--neutral-black);
    background: var(--accent);
    border: none;
    border-radius: 8px;
    cursor: pointer;
}

.db-loy-history-title {
    margin: 20px 0 12px;
    font-size: var(--fs-base);
    line-height: var(--lh-tight);
}

.db-loy-type {
    padding: 4px 8px;
    font-size: var(--fs-xs);
    background: var(--alpha-white-08);
    border-radius: 4px;
}

.db-loy-date {
    font-size: var(--fs-xs);
    color: var(--text-muted);
}

/* ПРИХОД И РАСХОД — КРАСКА СИСТЕМЫ. В разметке стояли #4caf50 и #f44336
   инлайном (js:6131, :6134): Material мимо палитры, и инлайн бьёт правило
   по весу — починить его из файла стилей нельзя вовсе. Те же два тона уже
   носят значки статуса, сведённые куском 59 */
.db-loy-points {
    font-weight: 600;
}

.db-loy-points.is-plus  { color: var(--green-500); }
.db-loy-points.is-minus { color: var(--red-500); }

@media (max-width: 992px) {
    /* Баланс: цифра в 48 пикселей с полями по 28 занимала треть экрана */
    .db-loy-balance {
        padding: 16px 12px 16px;
    }

    .db-loy-balance-label {
        font-size: var(--fs-2xs);
        margin-bottom: 4px;
    }

    /* Кегля у плитки здесь больше нет: цифра одна на всех видах */

    .db-loy-balance-unit {
        font-size: var(--fs-2xs);
    }

    .db-loy-expiry {
        font-size: var(--fs-2xs);
        margin-top: 6px;
    }

    .db-loy-redeem {
        margin-top: 12px;
        padding: 0 16px;
        font-size: var(--fs-xs);
    }

    .db-loy-history-title {
        margin: 16px 0 8px;
        font-size: var(--fs-sm);
    }

    /* История — как платежи и уведомления: строка становится записью,
       а не таблицей, которую надо возить вбок */
    .db-loy-table thead {
        display: none;
    }

    .db-loy-table,
    .db-loy-table tbody,
    .db-loy-row td {
        display: block;
    }

    /* Ширину колонок общая таблица задаёт процентами
       (`td:nth-child(2) { width: 34% }`), и это правило весомее простого
       `width: auto` — описание сжималось до 96 пикселей и ломалось на три
       строки. Отвечаем тем же весом */
    .db-loy-table.db-matches-table td {
        width: auto;
    }

    .db-loy-row {
        display: grid;
        grid-template-columns: 1fr auto;
        grid-template-areas:
            "action points"
            "date   points";
        align-items: center;
        gap: 4px 8px;
        padding: 8px 4px;
        border-bottom: var(--bw-hair) solid var(--border-subtle);
    }

    .db-loy-row td {
        padding: 0;
        border: none;
    }

    .db-loy-action {
        grid-area: action;
        font-size: var(--fs-xs);
    }

    .db-loy-type {
        font-size: var(--fs-2xs);
    }

    .db-loy-date {
        grid-area: date;
        font-size: var(--fs-2xs);
    }

    .db-loy-points {
        grid-area: points;
        align-self: center;
        text-align: right;
        font-size: var(--fs-base);
        white-space: nowrap;
    }
}

/* ============================================
   ОКНО QR — КАРТОЧКА ИГРОКА
   ============================================ */

/* Было собрано целиком инлайном: окно девятью свойствами, крестик шестью,
   кегли в rem мимо шкалы (1.5 · 1.15 · 0.88 · 0.78 · 0.75) и свой фон
   rgba(26,26,30). Размеры телефона жили веткой `if (innerWidth <= 560)` —
   то есть граница 560, которой нет среди разрешённых 640 и 992 */
.db-qr-modal {
    position: relative;
    max-width: 340px;
    width: 90%;
    padding: 32px;
    text-align: center;
    background: var(--bg-card);
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: 16px;
}

.db-qr-close {
    position: absolute;
    top: 8px;
    right: 8px;
}

.db-qr-brand {
    font-size: var(--fs-xl);
    font-weight: 700;
    line-height: var(--lh-tight);
    color: var(--accent);
    margin-bottom: 4px;
}

.db-qr-sub {
    font-size: var(--fs-xs);
    color: var(--text-muted);
    margin-bottom: 20px;
}

.db-qr-code {
    display: inline-block;
    padding: 12px;
    background: var(--neutral-white);
    border-radius: 8px;
    margin-bottom: 16px;
}

.db-qr-name {
    font-size: var(--fs-base);
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: 4px;
}

.db-qr-cat {
    font-size: var(--fs-xs);
    color: var(--accent);
    margin-bottom: 20px;
}

.db-qr-actions {
    display: flex;
    gap: 8px;
    justify-content: center;
}

/* Телефон: граница своя была 560 — её нет среди разрешённых. Квадрат кода
   камере хватает и меньше, поля окна тоже уменьшаются */
@media (max-width: 640px) {
    .db-qr-modal { padding: 20px; }
    .db-qr-brand { font-size: var(--fs-md); }
    .db-qr-sub,
    .db-qr-cat { margin-bottom: 12px; }
    .db-qr-code { padding: 8px; margin-bottom: 12px; }
    .db-qr-name { font-size: var(--fs-sm); }
    .db-qr-actions .db-btn { padding: 0 16px; font-size: var(--fs-xs); white-space: nowrap; }
}

/* ============================================
   ОКНО ОБМЕНА БАЛЛОВ
   ============================================ */

/* MODAL 26:143 ВЫБИРАЕТ ШИРИНУ ПО ЧИСЛУ ЭЛЕМЕНТОВ, А НЕ ПО ВКУСУ:
   список наград — это 1–3 пункта, то есть ступень sm 400. Стояло 420 —
   числа, которого нет ни в одной ступени (400 · 520 · 720). Поля 20 на
   шапку, тело и подвал — было 28/24 и 18/16 на телефоне. Потолок 90vh */
.db-redeem-modal {
    background: #111111;
    border: var(--bw-hair) solid var(--alpha-white-08);
    border-radius: 16px;
    padding: 20px;
    max-width: 400px;
    width: 90%;
    max-height: 90vh;
    overflow-y: auto;
    box-shadow: 0 20px 60px var(--alpha-black-50);
}

.db-redeem-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
}

.db-redeem-title {
    margin: 0;
    font-size: var(--fs-md);
    line-height: var(--lh-tight);
    color: var(--neutral-white);
}

/* ICON BUTTON 40:93: «Only lg (44) meets the 44pt minimum touch target»,
   и закрытие окна названо там прямо среди заменяемого. Было 30x26 */
.db-loy-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    padding: 0;
    font-size: var(--fs-md);
    line-height: var(--lh-none);
    color: var(--text-muted);
    background: none;
    border: none;
    border-radius: 999px;
    cursor: pointer;
}

.db-redeem-balance {
    text-align: center;
    padding: 16px 0 20px;
    margin-bottom: 16px;
    border-bottom: var(--bw-hair) solid var(--alpha-white-08);
}

.db-redeem-balance-value {
    font-size: var(--db-fs-cifra);
    font-weight: 700;
    line-height: var(--lh-none);
    color: var(--accent);
}

.db-redeem-balance-label {
    margin-top: 4px;
    font-size: var(--fs-xs);
    color: var(--text-muted);
}

.db-redeem-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    padding: 12px 16px;
    margin-bottom: 8px;
    background: var(--alpha-white-04);
    border: var(--bw-hair) solid var(--alpha-white-08);
    border-radius: 10px;
}

.db-redeem-item-title {
    font-size: var(--fs-base);
    font-weight: 600;
    color: var(--neutral-white);
}

.db-redeem-item-cost {
    margin-top: 4px;
    font-size: var(--fs-xs);
    color: var(--text-muted);
}

/* BUTTON 17:82 xs 28, высота свойством. Было 96x31 на широком и 84x29 на
   узком — два размера, и ни одного на шкале 28·36·44·52. Ступень xs
   законна только с прозрачным слоем цели 44 — решение 04.10 */
.db-loy-redeem-item {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 28px;
    padding: 0 16px;
    font-size: var(--fs-xs);
    font-weight: 700;
    line-height: var(--lh-none);
    color: var(--neutral-black);
    background: var(--accent);
    border: none;
    border-radius: 8px;
    cursor: pointer;
    white-space: nowrap;
}

.db-loy-redeem-item::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    min-width: 44px;
    min-height: 44px;
    width: 100%;
    height: 100%;
    transform: translate(-50%, -50%);
}

/* Баллов не хватает — кнопка гаснет, но остаётся на месте: видно, сколько
   ещё копить. Краска неактивного — ИМЕНЕМ: стояло число 0.3, это 2.59 при
   норме AA 4.5. Кабинет не берёт --text-dim вовсе (решение куска «общий
   слой кабинета»), поэтому здесь --text-muted: 5.33, выше порога, а
   «выключено» несут фон, вес и cursor: not-allowed, а не слепота текста */
.db-loy-redeem-item.is-off {
    background: var(--alpha-white-08);
    color: var(--text-muted);
    font-weight: 600;
    cursor: not-allowed;
}

@media (max-width: 992px) {
    /* Поля, ширина и потолок — ОДНО определение выше: 20, 400, 90vh */
    .db-redeem-modal {
        width: 92%;
    }

    .db-redeem-head {
        margin-bottom: 12px;
    }

    .db-redeem-title {
        font-size: var(--fs-base);
    }

    /* Баланс здесь тот же, что в разделе: 35 пикселей цифры в окне
       шириной 351 занимали больше места, чем сам список наград */
    .db-redeem-balance {
        padding: 12px 0 16px;
        margin-bottom: 12px;
    }

    .db-redeem-balance-label {
        font-size: var(--fs-2xs);
    }

    .db-redeem-item {
        padding: 12px;
        margin-bottom: 8px;
    }

    .db-redeem-item-title {
        font-size: var(--fs-sm);
    }

    .db-redeem-item-cost {
        font-size: var(--fs-xs);
    }

    .db-loy-redeem-item {
        padding: 0 12px;
    }
}

/* ============================================
   НАСТРОЙКИ: ТАБЛИЦА УВЕДОМЛЕНИЙ
   ============================================ */

/* Размеры были в разметке, повторяю их один в один — меняется только то,
   что ниже, в мобильном блоке */
.db-notify-table {
    width: 100%;
    border-collapse: collapse;
}

/* Межстрочный назван в базе уровня: в медиа стоял 1.3, а здесь
   наследовалось 1.5 */
.db-notify-head {
    padding: 4px 12px;
    text-align: center;
    line-height: var(--lh-snug);
    font-size: var(--fs-xs);
    font-weight: 500;
    color: var(--text-muted);
}

.db-notify-name {
    padding: 8px 12px;
    font-size: var(--fs-sm);
    color: var(--text-secondary);
}

.db-notify-cell {
    padding: 8px 12px;
    text-align: center;
}

@media (max-width: 992px) {
    /* Четыре колонки на 366 пикселях: подписи столбцов шли шестнадцатым
       кеглем, названия — четырнадцатым. По шкале телефона это подписи */
    .db-notify-head {
        padding: 4px 4px;
        font-size: var(--fs-2xs);
    }

    /* Колонки одной ширины. «Сайт и приложение» занимало 135 пикселей из
       332 — вдвое больше соседних, и переключатель стоял посреди пустоты.
       Название категории берёт остаток, три столбца делят его поровну */
    .db-notify-table {
        table-layout: fixed;
    }

    .db-notify-table thead th:first-child {
        width: 34%;
    }

    /* Перенос только по словам: break-word рвал «приложение» пополам.
       Кегль мельче, чтобы «приложение» помещалось в строку целиком */
    .db-notify-head {
        width: 22%;
        font-size: var(--fs-2xs);
        word-break: normal;
        hyphens: none;
    }

    .db-notify-name {
        padding: 8px 4px 8px 0;
        font-size: var(--fs-xs);
    }

    .db-notify-cell {
        padding: 8px 4px;
    }

    .db-notif-hint {
        font-size: var(--fs-xs);
    }

    /* Кнопки раздела: общие поля 12×32 при кегле 0.9 — на телефоне это
       половина ширины экрана ради одного слова */
    #db-settings .db-btn {
        padding: 8px 16px;
        font-size: var(--fs-xs);
    }

}

/* Текст «Опасной зоны»: размер стоял прямо в теге, и файл стилей его не
   перебивал вовсе — та же ловушка, что была в оферте */
.db-danger-text {
    margin-bottom: 16px;
    font-size: var(--fs-sm);
    color: var(--text-muted);
}

@media (max-width: 992px) {
    .db-danger-text {
        font-size: var(--fs-xs);
    }
}

/* Достижения в кабинете — тот же размер, что на странице игрока */
@media (max-width: 992px) {
    .db-badge-all-item {
        padding: 10px 12px;
        gap: 10px;
    }

    .db-badge-all-icon {
        font-size: var(--fs-lg);
    }

    .db-badge-all-name {
        font-size: var(--fs-sm);
    }

    .db-badge-all-desc {
        font-size: var(--fs-xs);
    }
}

/* ============================================
   УСТРОЙСТВА И ВХОД
   ============================================ */

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

/* С кем игрок выходит в паре — строкой под названием турнира. Раньше этого
   не было видно вовсе, и человек шёл смотреть состав на страницу турнира */
/* Поле с ошибкой: раньше человек видел только всплывашку у шапки и не
   понимал, к какому полю она относится */
.db-field-input.db-field-bad {
    border-color: var(--danger);
    box-shadow: 0 0 0 2px rgba(244, 67, 54, 0.15);
}

.db-tour-partner {
    margin-top: 2px;
    font-size: var(--fs-xs);
    color: var(--text-muted);
}

.db-devices {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-bottom: 14px;
}

.db-device {
    display: grid;
    grid-template-columns: 24px 1fr auto;
    align-items: center;
    gap: 10px;
    padding: 10px 2px;
    border-bottom: var(--bw-hair) solid var(--border-subtle);
}

.db-device:last-child {
    border-bottom: none;
}

.db-device-icon {
    font-size: var(--fs-base);
    line-height: 1;
}

.db-device-name {
    font-size: var(--fs-sm);
    color: var(--text-primary);
}

/* Отметка «это устройство»: без неё человек пугается, что кнопка выхода
   оборвёт сессию где-то ещё, но не здесь */
.db-device-current {
    color: var(--accent);
    font-weight: 600;
}

.db-device-time {
    font-size: var(--fs-xs);
    color: var(--text-muted);
    white-space: nowrap;
}

/* Вход был больше восьми часов назад — сеанс там точно оборвался.
   Приглушаем, чтобы живое было видно сразу */
.db-device.is-idle .db-device-name,
.db-device.is-idle .db-device-icon {
    color: var(--text-muted);
    opacity: 0.65;
}

/* Шестое и дальше — до нажатия «Показать все» */
.db-device.is-more {
    display: none;
}

.db-devices-more {
    margin-bottom: 14px;
}

/* Раскрытие списка — мелкая ступень, а не действие в полную ширину.
   Через идентификатор: в блоке настроек кнопки размером правит
   #db-settings .db-btn, и обычный класс его не перебивает */
#db-settings .db-devices-more {
    min-height: 32px;
    padding: 8px 12px;
    font-size: var(--fs-xs);
}

.db-devices-empty {
    margin-bottom: 14px;
    font-size: var(--fs-sm);
    color: var(--text-muted);
}

.db-devices-logout {
    width: 100%;
}

/* ============================================
   ОКНО ПОДТВЕРЖДЕНИЯ НА ТЕЛЕФОНЕ
   ============================================ */

/* Заголовок 1.3rem ломался на две строки, а кнопки жались к правому краю
   узкими плашками. На телефоне подтверждение читают и нажимают быстро:
   заголовок в строку, кнопки во всю ширину, главная сверху */

@media (max-width: 992px) {
    .db-modal {
        padding: 18px 16px;
    }

    .db-modal-title {
        font-size: var(--fs-base);
    }

    .db-modal-text {
        font-size: var(--fs-sm);
    }

    .db-modal-actions {
        flex-direction: column;
        gap: 8px;
        margin-top: 14px;
    }

    /* 40 — не ступень шкалы кнопок, и к тому же МЕНЬШЕ 44, которые этому
       же .db-btn даёт блок целей нажатия ниже по файлу: селектор здесь
       весомее, и на телефоне кнопка окна оставалась 40. Поле 11 тоже мимо
       любой шкалы; высоту держит свойство, а не сумма полей */
    .db-modal-actions .db-btn {
        width: 100%;
        min-height: 44px;
        padding: 12px 16px;
        font-size: var(--fs-sm);
    }
}

/* ══════════════════════════════════════════════════════════════════════
   ВИДИМЫЙ ФОКУС, ОТКАЗ ОТ ДВИЖЕНИЯ И ЦЕЛИ НАЖАТИЯ — 11.10
   ══════════════════════════════════════════════════════════════════════

   ЗАМЕР ДО ПРАВКИ: `:focus-visible` в этом файле — НОЛЬ при 57 `:hover`.
   Глобальные правила в `css/style.css` покрывают шапку и карточки
   «Услуг», кабинет они не покрывают. То есть человек, идущий по кабинету
   с клавиатуры, не видел, где находится, ни на одном из восьми разделов.

   `prefers-reduced-motion` — НОЛЬ при пяти анимациях.

   Нажимаемых мест двадцать, из них ДЕВЯТЬ своих были ниже 44: листалка
   28x28, глазок пароля 28x28, чип 52x27, «прочитать все» 201x27,
   «отключить ТГ» 75x21, выбор 90x33, кнопка аватара 142x34, «потратить
   баллы» 175x37, кнопка формы 139x40. Листалка в этот список больше не
   входит: своя полоса страниц кабинета ушла целиком, и кнопку цели
   считает уже общий компонент.

   БЛОК СТОИТ В КОНЦЕ НАМЕРЕНО: правила фокуса обязаны перебивать всё, что
   выше, а порядок в файле решает. */

/* ---- 1. ФОКУС ВИДЕН, И У КАЖДОГО ЭЛЕМЕНТА ПОИМЁННО ----
   :focus-visible, а НЕ :focus: второй срабатывает и на мышь, и тогда
   обводка висит после каждого нажатия. Переходы здесь перечислены, а не
   «all»: `transition: all` анимирует и outline-width, и контур у
   клавиатурного пользователя наезжает от нуля. */
.db-sidebar-link:focus-visible,
.db-mobile-tab:focus-visible,
.db-subsection-toggle:focus-visible,
.db-btn:focus-visible,
.db-btn-sm:focus-visible,
.db-chip:focus-visible,
.db-notif-all-read:focus-visible,
.db-avatar-btn:focus-visible,
.db-choice:focus-visible,
.db-tg-off:focus-visible,
.db-pw-eye:focus-visible,
.db-loy-redeem:focus-visible,
.db-loy-redeem-item:focus-visible,
.db-modal-close:focus-visible,
.db-opp-link:focus-visible,
.db-match-h2h:focus-visible {
    outline: var(--bw-thick) solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
    /* ПЕРЕХОДЫ ПЕРЕЧИСЛЕНЫ, А НЕ «ALL» — правило 09.10 со страницы игрока,
       и кабинету оно нужно ровно так же. `--transition-fast` объявлен в
       tokens.css:233 как «0.2s ease», БЕЗ имени свойства, то есть
       `transition: var(--transition-fast)` означает `all`. Контур при этом
       наезжает от нуля: прогон 11.10 померил шесть элементов подряд —
       `.db-sidebar-link`, `.db-avatar-btn`, `.db-field-input`,
       `.db-choice`, `.db-pw-eye`, `.db-btn` — у всех «контур 0 solid»
       в первый миг после клавиши. Человеку с клавиатуры это читается как
       «фокуса нет», и он нажимает Tab дальше.
       Здесь перечисляем, что контуру МОЖНО анимировать: толщина не может. */
    transition: outline-color var(--transition-fast),
                outline-offset var(--transition-fast);
}

/* Поле ввода держит свой лаймовый ореол, но теперь и на клавиатуру:
   было только `:focus`, то есть и на мышь, и без обводки вовсе */
.db-field-input:focus-visible {
    outline: var(--bw-thick) solid var(--accent);
    outline-offset: 1px;
    transition: outline-color var(--transition-fast),
                outline-offset var(--transition-fast);
}

/* ---- 2. ЦЕЛЬ НАЖАТИЯ 44, ДАЖЕ КОГДА КРАСИТСЯ МЕЛЬЧЕ ----
   Решение 04.10: кнопка 36 законна, если у неё есть прозрачный слой цели
   44. Красить мелкие управляющие элементы крупнее значило бы развести их с
   соседями по строке, поэтому растёт не краска, а цель. */
/* ГЛАЗКА В ЭТОМ СПИСКЕ НЕТ НАРОЧНО. 11.10 он сюда попал, и это сломало
   экран: `.db-pw-eye` объявлен `position: absolute` выше по файлу
   (строка 1009) и висит справа ВНУТРИ поля. Общее `relative` победило по
   порядку, глазок выпал в поток и встал СЛЕВА ПОД полем — замер на 375:
   поле 29,293 317x47, глазок 17,326. Поймал Костя глазами.
   Слою цели это не мешает: absolute — тоже позиционирование, и `::after`
   считается от самого глазка. */
.db-chip,
.db-notif-all-read,
.db-choice,
.db-tg-off {
    position: relative;
}

.db-chip::after,
.db-notif-all-read::after,
.db-choice::after,
.db-tg-off::after,
.db-pw-eye::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 100%;
    height: 100%;
    min-width: 44px;
    min-height: 44px;
    transform: translate(-50%, -50%);
}

/* ---- 3. КНОПКИ ВСТАЮТ НА СТУПЕНИ ШКАЛЫ 28 · 36 · 44 · 52 ----
   Высота держится свойством, а не суммой полей: поля считают от кегля, и
   высота уезжала при каждой правке текста. Межстрочный 1 — ступень
   лестницы для одиночной строки кнопки. */
.db-btn,
.db-avatar-btn,
.db-loy-redeem,
.db-sidebar-link,
.db-subsection-toggle {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    line-height: 1;
}

.db-sidebar-link,
.db-subsection-toggle {
    display: flex;
}

.db-chip,
.db-notif-all-read,
.db-choice,
.db-btn-sm {
    display: inline-flex;
    align-items: center;
    min-height: 36px;
}

.db-toggle-arrow,
.db-field-input {
    line-height: 1.3;
}

/* SELECT И TEXTAREA — ТЕ ЖЕ УРОВНИ, ЧТО У ПОЛЯ ВВОДА.
   Замер 11.10 по пяти видам: на видах шире 992 этим двум не доставалось
   ни кегля, ни межстрочного, и браузер ставил свои 13.3333 / normal —
   единственные два уровня кабинета мимо лестницы. На узких видах кегль
   уже стоял (блок выше, 16 против зума iOS), а межстрочный — нет.
   Начертание тоже наследуется: иначе select рисуется системным шрифтом. */
.db-section select,
.db-section textarea {
    font-size: var(--fs-base);
    line-height: 1.3;
}


/* ======================================================================
   РАЗДЕЛЫ КАБИНЕТА НА КОРОТКОМ ВИДЕ — 11.10

   Замер 11.10: полоса разделов занимает 149 из 390 на телефоне лёжа —
   38 % экрана до первой прокрутки, из них 77 уходит на просвет под висящей
   шапкой (64) и границу. Ширины в медиа — только 640 и 992; короткий вид
   делится ВЫСОТОЙ, а не шириной: лёжа мало именно высоты, а ширины 844
   хватает на подпись рядом со значком.

   Плитка кладётся строкой: 52 → 44, тоже ступень шкалы кнопок.
   ====================================================================== */
@media (max-width: 992px) and (max-height: 480px) {
    .db-mobile-tabs {
        padding: 76px 16px 8px;
    }

    .db-mobile-tab {
        flex-direction: row;
        gap: 8px;
        padding: 8px 12px;
        min-height: 44px;
    }
}

/* ======================================================================
   БАТТЛЫ И ПРИГЛАШЕНИЯ НА ТЕЛЕФОНЕ — СТРОКА СПИСКА, А НЕ СТРОКА ТАБЛИЦЫ
   12.10, по слову Кости «прокрутка вбок не годится»

   ЗАМЕР ДО ПРАВКИ на 390: таблица 324 на экране и просит 361; под имя
   остаётся 110, под счёт и итог — по 11; у приглашений хуже — 542 при 309.
   Шесть колонок на 375 не помещаются ни при каких ширинах, и прокрутка
   вбок внутри вертикального списка — не решение, а перенос беды.

   ТАБЛИЦА ОПРАВДАНА, КОГДА КОЛОНКИ СРАВНИВАЮТ МЕЖДУ СТРОКАМИ — цены,
   очки, места. Здесь сравнивать нечего: это лента событий, и ленты на
   телефоне строят списком (Gmail, Linear, Stripe, «Почта» на iOS).
   На видах шире 640 таблица остаётся ровно такой, какой была.

   СОБРАНО ИЗ ГОТОВОГО, СВОЕГО НЕ НАРИСОВАНО НИ ОДНОГО:
     • карточка — `Card 29:65`, «Compact padding 16 for dense lists»;
     • кружок   — `Avatar 29:82`, «sm 32 for lists» (было xs 24 — размер
       строки ТАБЛИЦЫ, и это был верный размер, пока строка ею была);
     • значок   — `Badge 27:53`, высота 24, поля 8, радиус full;
     • кнопки   — `Button 17:82` xs 28 плюс прозрачный слой цели 44.

   ЧТО ВО ЧТО ВЛЕЗАЕТ — ПОСЧИТАНО МЕРОЙ ТЕКСТА, 324 доступно:
     линия 1  кружок 32 + имя + значок; при самом длинном значке
              COUNTER-PROPOSAL (143 вместе с капсом, разрядкой и полями)
              под имя остаётся 133, а «Адиль Валимамедов» просит 142 —
              поэтому имя стоит в ОДНУ строку и обрезается многоточием,
              а значок не сжимается. Так устроен Gmail: тема обрезается,
              дата справа держит место. Имя целиком доступно всегда — оно
              ссылка на страницу игрока.
     линия 2  дата 57 + счёт 51 + итог 16 + H2H 40 с зазорами = 200.
              Кнопки ответа 147 и счёт НЕ ВСТРЕЧАЮТСЯ: кнопки бывают у
              активного вызова, счёт — у сыгранного.
   ====================================================================== */
/* ======================================================================
   ТАБЛИЦА МАТЧЕЙ НА ТЕЛЕФОНЕ — ТОТ ЖЕ ПРИЁМ, ЧТО У БАТТЛОВ, 12.10

   Прогон 12.10 нашёл то, чего не видел замер: у аккаунта с матчами
   таблица просит 336 при 309 — семь колонок (дата, соперник, счёт, итог,
   турнир, раунд, H2H), из которых на ≤640 прячется только турнир.
   У Кости матчей в этом разделе нет, и беда не показывалась вовсе.

   ПРАВИЛО, ВЫВЕДЕННОЕ НА БАТТЛАХ, ПРИМЕНЯЕТСЯ БЕЗ НОВОГО РАЗБОРА: это та
   же лента событий, и сравнивать между строками в ней нечего.
     линия 1  соперник, справа H2H
     линия 2  дата · счёт · итог · раунд
   ====================================================================== */
@media (max-width: 640px) {
    .db-games-table,
    .db-games-table tbody {
        display: block;
        width: 100%;
    }

    .db-games-table thead {
        display: none;
    }

    .db-games-table tbody tr {
        display: grid;
        grid-template-columns: auto auto auto 1fr auto;
        grid-template-areas:
            "who  who   who  who  h2h"
            "date score res  round .";
        align-items: center;
        gap: 4px 8px;
        padding: 12px 4px;
        border-bottom: var(--bw-hair) solid var(--border-subtle);
    }

    /* Тем же весом, что общие проценты колонок: удвоение класса */
    .db-games-table.db-matches-table tbody td {
        display: block;
        width: auto;
        padding: 0;
        border: none;
    }

    .db-games-table tbody td:nth-child(2) { grid-area: who; }
    .db-games-table .db-match-date       { grid-area: date; }
    .db-games-table .db-match-score      { grid-area: score; text-align: left; }
    .db-games-table .db-match-result-cell { grid-area: res; text-align: left; }
    .db-games-table .db-match-round      { grid-area: round; text-align: left; }
    .db-games-table td:last-child        { grid-area: h2h; text-align: right; }

    /* Имя соперника в одну строку с отсечкой — решение куска 59 слово в слово */
    .db-games-table .db-opp-link {
        display: inline-block;
        max-width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        vertical-align: bottom;
    }
}

@media (max-width: 640px) {
    .db-battles-table,
    .db-battles-table tbody,
    .db-invites-table,
    .db-invites-table tbody {
        display: block;
        width: 100%;
    }

    /* Шапка таблицы в списке не нужна: каждое значение и так названо своим
       видом — кружок, значок, счёт. Прятать её от ДИКТОРА нельзя, но и
       читать «Дата Соперник Счёт» перед каждой строкой он не должен,
       поэтому она уходит целиком вместе со своей ролью */
    .db-battles-table thead,
    .db-invites-table thead {
        display: none;
    }

    .db-battles-table tbody tr,
    .db-invites-table tbody tr {
        display: grid;
        column-gap: 12px;
        row-gap: 10px;
        align-items: center;
        margin-bottom: 8px;
        padding: 12px 16px;
        border: var(--bw-hair) solid var(--border-subtle);
        border-bottom: var(--bw-hair) solid var(--border-subtle);
        border-radius: var(--radius-lg);
        background: var(--bg-card);
    }

    /* Клетки становятся блоками. ПРАВИЛО 08.10: клетка таблицы, оставшаяся
       `display: table-cell` внутри грида, не растягивается на свою дорожку —
       помогает только блок с названной областью, а не номер дорожки */
    .db-battles-table tbody td,
    .db-invites-table tbody td {
        display: block;
        min-width: 0;
        padding: 0;
        border: 0;
        text-align: left;
    }

    /* ── баттлы: шесть клеток в две линии ── */
    .db-battles-table tbody tr {
        grid-template-columns: auto auto auto 1fr auto;
        grid-template-areas:
            "who  who   who  who status"
            "date score res  act  h2h";
    }

    .db-battles-table tbody td:nth-child(1) { grid-area: date; }
    .db-battles-table tbody td:nth-child(2) { grid-area: who; }
    .db-battles-table tbody td:nth-child(3) { grid-area: score; }
    .db-battles-table tbody td:nth-child(4) { grid-area: res; }
    .db-battles-table tbody td.db-cell-status  { grid-area: status; justify-self: end; }
    .db-battles-table tbody td.db-cell-actions { grid-area: act; justify-self: end; }
    .db-battles-table tbody td:nth-child(6) { grid-area: h2h; justify-self: end; }

    /* ── приглашения: четыре клетки в две линии ── */
    .db-invites-table tbody tr {
        grid-template-columns: auto 1fr auto;
        grid-template-areas:
            "who  who  status"
            "date .    act";
    }

    .db-invites-table tbody td:nth-child(1) { grid-area: date; }
    .db-invites-table tbody td:nth-child(2) { grid-area: who; }
    .db-invites-table tbody td:nth-child(3) { grid-area: status; justify-self: end; }
    .db-invites-table tbody td:nth-child(4) { grid-area: act; justify-self: end; }

    /* ШИРИНЫ КОЛОНОК СНИМАЮТСЯ ЦЕЛИКОМ, И КЛАСС УДВОЕН НЕ ДЛЯ КРАСОТЫ.
       Наборов ширин три — 110 и 34 % у .db-matches-table, 150 и 70 у
       .db-battles-table, 76 · 104 · 210 у .db-invites-table, — и все они
       записаны через `td:nth-child(N)`, то есть весят (0,3,1). Простое
       `.db-battles-table tbody td` весит (0,1,2) и проигрывает: замер 12.10
       после первой редакции показал клетку имени 44 вместо 140 и значок,
       который держал свои прежние 150. Удвоение класса уравнивает вес —
       тот же приём, что у витрины новостей 08.10.
       В списке ширину клетке даёт её ОБЛАСТЬ, а не прежняя колонка. */
    .db-battles-table.db-battles-table tbody td:nth-child(1),
    .db-battles-table.db-battles-table tbody td:nth-child(2),
    .db-battles-table.db-battles-table tbody td:nth-child(3),
    .db-battles-table.db-battles-table tbody td:nth-child(4),
    .db-battles-table.db-battles-table tbody td:nth-child(5),
    .db-battles-table.db-battles-table tbody td:nth-child(6),
    .db-invites-table.db-invites-table tbody td:nth-child(1),
    .db-invites-table.db-invites-table tbody td:nth-child(2),
    .db-invites-table.db-invites-table tbody td:nth-child(3),
    .db-invites-table.db-invites-table tbody td:nth-child(4) {
        width: auto;
    }

    /* Имя в одну строку с отсечкой — см. расчёт в шапке блока */
    .db-battles-table .db-match-opponent > span,
    .db-invites-table .db-match-opponent > span {
        min-width: 0;
    }

    .db-battles-table .db-opp-link,
    .db-invites-table .db-opp-link {
        display: block;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /* Кружок — ступень списка, а не строки таблицы */
    .db-battles-table .db-match-avatar,
    .db-battles-table .db-match-avatar-ph,
    .db-invites-table .db-match-avatar,
    .db-invites-table .db-match-avatar-ph {
        width: 32px;
        height: 32px;
        flex-shrink: 0;
    }

    /* Пустые клетки не занимают место: у вызова без счёта строка не должна
       раздвигаться пустотой. НОЛЬ — ЗАКОННОЕ ЗНАЧЕНИЕ, а пусто — нет */
    .db-battles-table tbody td:empty {
        display: none;
    }
}

/* ЦЕЛЬ НАЖАТИЯ 44 ПРОЗРАЧНЫМ СЛОЕМ — решение 04.10 без нового разбора.
   Замер 12.10: H2H красится 40x21, кнопка ответа 28 высотой. Красить их
   крупнее значило бы развести со строкой, поэтому растёт не краска, а цель */
.db-match-h2h,
.db-chal-btn {
    position: relative;
}

.db-match-h2h::after,
.db-chal-btn::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 100%;
    height: 100%;
    min-width: 44px;
    min-height: 44px;
    transform: translate(-50%, -50%);
}

/* Закрытие окна H2H: `Icon button 40:93` — «Only lg (44) meets the 44pt
   minimum touch target». Замер 12.10 дал 36 — это размер md, desktop-only */
@media (max-width: 992px) {
    .h2h-close {
        min-width: 44px;
        min-height: 44px;
    }
}

/* ---- Заблокированные в кабинете (Apple 1.2), 04.11 ----
   СТРОКА, А НЕ КАРТОЧКА: это перечень, из которого выходят одним
   нажатием. Цель нажатия 44 — та же, что у всех остальных. */
.db-blk-row {
    display: flex; align-items: center; gap: var(--space-3);
    padding: var(--space-3) 0;
    border-bottom: var(--bw-hair) solid var(--border-subtle);
}
.db-blk-row:last-of-type { border-bottom: none; }
.db-blk-text { flex: 1 1 auto; min-width: 0; }
.db-blk-name {
    font-size: var(--fs-base); font-weight: 600; color: var(--text-primary);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.db-blk-date { font-size: var(--fs-xs); color: var(--text-muted); }
.db-blk-off { flex: 0 0 auto; min-height: 44px; }
.db-blk-off:disabled { opacity: .5; cursor: progress; }
.db-blk-note {
    margin: var(--space-4) 0 0;
    font-size: var(--fs-xs); line-height: 1.5; color: var(--text-muted);
}

/* ─────────────────────────────────────────────────────────────────
   СОГЛАСИЯ (Ю2/Ю3) — ОДИН ВИД НА ДВА МЕСТА: экран после регистрации
   и раздел кабинета. Переключатель берётся готовый — `db-notif-*`,
   тот же, что у уведомлений: ОДНО ОПРЕДЕЛЕНИЕ НА ОДНО ПОНЯТИЕ, и
   рука у человека уже обучена.
   Все числа — ступени шкалы, своих нет.
   ───────────────────────────────────────────────────────────────── */
.db-consent-intro {
    color: var(--text-secondary);
    font-size: var(--fs-sm);
    line-height: 1.5;
    margin: 0 0 var(--space-4);
}

.db-consent-list { list-style: none; margin: 0; padding: 0; }

.db-consent-item {
    display: flex;
    gap: var(--space-3);
    align-items: flex-start;
    padding: var(--space-4) 0;
    border-bottom: var(--bw-hair) solid var(--border-subtle);
}

.db-consent-item:last-child { border-bottom: none; }

.db-consent-text { flex: 1; min-width: 0; }

.db-consent-name {
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
    color: var(--text-primary);
    margin-bottom: var(--space-1);
}

.db-consent-desc {
    font-size: var(--fs-sm);
    line-height: 1.5;
    color: var(--text-secondary);
}

.db-consent-meta {
    font-size: var(--fs-xs);
    color: var(--text-muted);
    margin-top: var(--space-2);
}

.db-consent-footnote {
    font-size: var(--fs-xs);
    line-height: 1.5;
    color: var(--text-muted);
    margin: var(--space-4) 0 var(--space-4);
}

.db-consent-minor {
    font-size: var(--fs-sm);
    line-height: 1.5;
    color: var(--text-secondary);
    margin: 0;
}

/* НА ТЕЛЕФОНЕ переключатель уезжает под текст: в строку он не
   помещается, и подпись дробится на три слова в столбик. */
@media (max-width: 640px) {
    .db-consent-item { flex-direction: column; gap: var(--space-2); }
}

/* Предупреждение «писем не будет» — показывается, когда первая цель
   выключена. Прячется классом, а не перерисовкой экрана. */
.db-consent-nomail {
    font-size: var(--fs-sm);
    line-height: 1.5;
    color: var(--warning);
    margin: var(--space-3) 0 0;
}

.db-consent-hidden { display: none; }

/* Окно согласий после первого входа. Оболочка общая с приветствием
   (`db-onboarding-overlay`), здесь только ширина и прокрутка: целей
   четыре, и на невысоком экране карточка должна прокручиваться сама,
   а не уезжать кнопкой за край. */
.db-consent-overlay { overflow-y: auto; padding: var(--space-4); }

.db-consent-overlay .db-consent-card {
    max-width: 640px;
    width: 100%;
    margin: auto;
}

/* МОИ ДАННЫЕ (Ю4). Сводка «что о вас хранится»: раздел и число
   записей. Сами значения человек видит в скачанном файле — полтора
   десятка таблиц на экране не показывают ничего. */
.db-mydata-intro {
    color: var(--text-secondary);
    font-size: var(--fs-sm);
    line-height: 1.5;
    margin: 0 0 var(--space-4);
}

.db-mydata-list { list-style: none; margin: 0 0 var(--space-4); padding: 0; }

.db-mydata-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--space-3);
    padding: var(--space-2) 0;
    border-bottom: var(--bw-hair) solid var(--border-subtle);
}

.db-mydata-row:last-child { border-bottom: none; }

.db-mydata-name { font-size: var(--fs-sm); color: var(--text-secondary); }

/* ЧИСЛО — ЦИФРАМИ ОДНОЙ ШИРИНЫ: столбец справа не должен прыгать от
   строки к строке. */
.db-mydata-count {
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}
