/* ТИХИХ УРОВНЕЙ ТЕКСТА ТРИ, А НЕ ЧЕТЫРЕ — решение Кости 06.10, путь «C».
   `--text-dim` это `var(--alpha-white-35)`, и на фоне карточки он даёт
   КОНТРАСТ 3.14 при норме WCAG AA 4.5. Померено в браузере 06.10; первый
   замер врал 19.8 всем уровням подряд, потому что отбрасывал альфу вместо
   того, чтобы смешать её с фоном.
   WCAG не делает скидки «тихому» тексту: 3.0 допустим только на крупном —
   от 24, или от 18.7 жирного. На мелком `--text-dim` незаконен по
   определению, а стоял он почти везде на мелком.
   Поднять сам токен нельзя: до порога это ~0.46, а `--text-muted` стоит на
   0.5 — два уровня схлопнулись бы в один во всех 138 местах продукта разом,
   включая каждый закрытый кусок. Поэтому токен уходит ИЗ ТЕКСТА по кускам,
   а не правкой значения: на этой странице он снят со всех шестнадцати мест,
   и правило не даёт ему вернуться. Для границ и фона он остаётся законным.
*/

/* ========================================
   TOURNAMENT DETAIL PAGE STYLES
   ======================================== */

/* === HERO === */
/* Высота общая для всех карточек: игрок, тренер, корт, турнир */
.td-hero {
    position: relative;
    z-index: 0;
    /* ПОЛ — ОБЩИЙ НА ВСЕ РАЗДЕЛЫ, css/tokens.css. Было 300 здесь, 360 в
       медиа 992, 320 в 768 и 280 в 480 — четыре числа на одно понятие.
       Воздух остаётся своим: обложка турнира прижата книзу (flex-end), и
       воздух у неё живёт на .td-hero-content, а не на самой обложке. */
    min-height: var(--oblozhka-pol);
    display: flex;
    align-items: flex-end;
    padding: 0 var(--pad-x);
    overflow: hidden;
}

.td-hero-bg {
    position: absolute;
    inset: 0;
    z-index: 0;
}

.td-hero-bg img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.td-hero-overlay {
    position: absolute;
    inset: 0;
    background: linear-gradient(to top, var(--bg-primary) 0%, var(--alpha-black-85) 50%, var(--alpha-black-50) 100%);
}

.td-hero-content {
    position: relative;
    z-index: 1;
    max-width: var(--container-max);
    width: 100%;
    margin: 0 auto;
    padding: var(--space-xl) 0 var(--space-lg);
}

.td-back-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--text-secondary);
    text-decoration: none;
    font-size: var(--fs-base);
    font-weight: 500;
    margin-bottom: var(--space-lg);
    transition: color 0.2s;
}

.td-back-link:hover {
    color: var(--accent);
}

/* Своя цена под шапкой: две суммы выше — это прайс, а здесь то,
   сколько платить именно этому человеку */
.td-fee-note {
    display: inline-block;
    margin-top: var(--space-sm);
    padding: 8px 16px;
    border-radius: var(--radius-full);
    background: var(--alpha-white-08);
    border: var(--bw-hair) solid var(--border-subtle);
    color: var(--text-secondary);
    font-size: var(--fs-sm);
    font-weight: 500;
}

.td-fee-note[hidden] {
    display: none;
}

.td-fee-note-member {
    background: var(--alpha-lime-12);
    border-color: var(--alpha-lime-30);
    color: var(--accent);
}

.td-hero-badges {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
    margin-bottom: var(--space-sm);
}

/* Метка категории. Раньше базовый вид лежал в tournaments.css и достался
   этой странице «в наследство» — на страницах категорий метку убрали
   (она дублировала заголовок), а здесь она нужна и живёт своей жизнью */
.tournament-category-badge {
    display: inline-block;
    padding: 8px 24px;
    background: var(--accent);
    color: var(--neutral-black);
    font-size: var(--fs-sm);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 2px;
    border-radius: var(--radius-full);
}

/* Uniform badge size in hero — all same padding/font/height */
.td-hero-badges .tournament-category-badge {
    padding: 5px 14px;
    font-size: var(--fs-xs);
    font-weight: 600;
    letter-spacing: 0.5px;
    margin-bottom: 0;
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
}

.td-hero-badges .tournament-gender-badge {
    background: var(--accent-subtle);
    border-color: var(--alpha-lime-30);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    padding: 5px 14px;
    font-size: var(--fs-xs);
    font-weight: 600;
    letter-spacing: 0.5px;
    margin-top: 0;
    margin-left: 0;
}

.td-status-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 14px;
    border-radius: var(--radius-full);
    font-size: var(--fs-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.td-status-badge.completed {
    background: var(--alpha-white-12);
    color: var(--text-secondary);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
}

.td-status-badge.live {
    background: rgba(255, 71, 87, 0.25);
    color: var(--danger);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    animation: pulse-glow 2s ease-in-out infinite;
}

.td-status-badge.upcoming {
    background: var(--alpha-lime-20);
    color: var(--accent);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
}

/* Registration open / ongoing / closed / cancelled */
.td-status-badge.registration_open,
.td-status-badge.open {
    background: var(--alpha-lime-20);
    color: var(--accent);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
}

/* «ИДЁТ» — ОДНА РОЛЬ НА ВЕСЬ ПРОДУКТ (круг 16, 18.10). Роль объявлена в
   `tokens.css` как «менеджер / идёт сейчас» ещё 17.10 — ПРИНЯТОЕ НЕ
   ПЕРЕОТКРЫВАЕТСЯ, его просто не донесли сюда. Было три краски на одно
   состояние: фиолетовая в списке админки (верная), лаймовая в её форме
   (ΔE 164 от верной) и голубой `#00A3FF` числом здесь (ΔE 37). Человек
   видел турнир лаймовым в форме и голубым на сайте. */
.td-status-badge.ongoing {
    background: var(--role-manager-subtle);
    color: var(--role-manager);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
}

.td-status-badge.closed {
    background: var(--alpha-white-12);
    color: var(--text-muted);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
}

.td-status-badge.cancelled {
    background: rgba(255, 71, 87, 0.2);
    color: var(--danger);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
}

/* Static data status classes (open/soon/past) */
.td-status-badge.soon {
    background: rgba(255, 159, 67, 0.18);
    color: var(--orange-500);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
}

.td-status-badge.past {
    background: var(--alpha-white-12);
    color: var(--text-muted);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
}

/* Свечение значка эфира. Упущено заходом 2б: правило смотрит блоки по
   СЕЛЕКТОРАМ, а `@keyframes` селектором не является, и старый красный
   остался здесь незамеченным. Правило дополнено — теперь оно читает и
   кадры анимаций, которые зовут селекторы роли. */
@keyframes pulse-glow {
    0%, 100% { box-shadow: 0 0 0 0 rgba(255, 71, 87, 0); }
    50% { box-shadow: 0 0 12px 2px var(--alpha-red-30); }
}

.td-hero h1 {
    /* СТУПЕНЬ, А НЕ ДОЛЯ ШИРИНЫ. Было clamp(1.8rem, 4vw, 3rem) и давало 48
       на десктопе, 40.96 на планшете боком, 33.76 на телефоне боком — ни
       одного числа со шкалы. Крутилка живёт в css/tokens.css и одна на все
       разделы: 40 · 40 · 32 · 32 · 26. */
    font-size: var(--fs-hero);
    font-weight: var(--fw-extrabold);
    color: var(--text-primary);
    margin-bottom: var(--space-sm);
    line-height: var(--lh-tight);
}

.td-hero-meta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-md);
    margin-bottom: var(--space-lg);
}

.td-meta-item {
    display: flex;
    align-items: center;
    gap: 6px;
    color: var(--text-secondary);
    font-size: var(--fs-sm);
}

.td-meta-item svg {
    flex-shrink: 0;
    opacity: 0.7;
}

.td-meta-reg {
    color: var(--accent);
}

.td-meta-reg svg {
    opacity: 1;
    stroke: var(--accent);
}

.td-hero-stats {
    display: flex;
    gap: var(--space-lg);
}

/* === STICKY TABS BAR === */
/* Полоса во всю ширину, содержимое — по сетке страницы: раньше вкладки
   центрировались сами по себе и не совпадали ни с заголовками разделов,
   ни со ссылкой «назад» слева от них */
.td-tabs-bar {
    position: sticky;
    top: var(--header-h);
    z-index: 50;
    background: var(--bg-primary, var(--neutral-950));
    border-bottom: var(--bw-hair) solid var(--border-subtle);
    /* Вкладки прижаты влево и начинаются там же, где заголовки разделов.
       Раньше полоса разносила ссылку «назад» и вкладки по краям, и вкладки
       висели правее всего содержимого страницы */
    display: flex;
    justify-content: flex-start;
    align-items: center;
    gap: 28px;
    padding: 0 var(--pad-x);
    /* ПОЛОСА НЕ НИЖЕ ЦЕЛИ НАЖАТИЯ, КОТОРУЮ НЕСЁТ. Замер 04.10 после правки:
       слой 44 у вкладки стоял, но на узких видах полоса была 39, слой вылезал
       за неё на 3 сверху — и эти 3 перекрывала обложка. Прибор показал это
       прямо: elementFromPoint над центром вкладки возвращал td-hero-content, а
       не td-tab. СЛОЙ, ВЫЛЕЗШИЙ ЗА СВОЮ ПОЛОСУ, ПЕРЕКРЫВАЕТСЯ СОСЕДОМ. */
    min-height: var(--btn-h-md);
    /* Override global nav styles */
    height: auto;
    max-width: none;
    margin: 0;
}

/* Back link inside tabs bar */
.td-tabs-back {
    display: none;
    align-items: center;
    gap: 6px;
    color: var(--text-secondary);
    text-decoration: none;
    font-size: var(--fs-sm);
    font-weight: 600;
    padding: 8px 0 4px;
    max-width: none;
    margin: 0;
    flex-shrink: 0;
    transition: color 0.2s;
}

.td-tabs-back:hover {
    color: var(--accent);
}

.td-tabs-back svg {
    flex-shrink: 0;
}

.td-tabs-back.visible {
    display: flex;
}

.td-tabs {
    display: flex;
    gap: 0;
    max-width: none;
    margin: 0;
    padding: 0;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}

.td-tabs::-webkit-scrollbar {
    display: none;
}

/* Первая вкладка начинается ровно там же, где заголовки разделов ниже */
.td-tab:first-child {
    padding-left: 0;
}

.td-tab {
    /* ЦЕЛЬ НАЖАТИЯ 44 — НАСТОЯЩЕЙ ВЫСОТОЙ, А НЕ СЛОЕМ. Замер 04.10: на
       планшете стоя и на телефоне стоя шесть вкладок были по 35 при норме 44,
       и это ГЛАВНАЯ навигация страницы.

       ПРОЗРАЧНЫЙ СЛОЙ ::after ЗДЕСЬ НЕ РАБОТАЕТ, и это поймал замер, а не
       чтение: у ленты .td-tabs стоит overflow-x: auto, а ОБЁРТКА С OVERFLOW —
       ЭТО ОКНО, А НЕ ОФОРМЛЕНИЕ. Слой, выросший за высоту ленты, ею же и
       обрезается. Прибор показал это прямо: elementFromPoint в крайней точке
       слоя возвращал td-tabs-bar, а не td-tab — то есть нажатие туда не
       доходило. Приём со слоем остаётся верным там, где обёртки-окна нет. */
    min-height: var(--btn-h-md);
    display: inline-flex;
    align-items: center;
    padding: var(--space-3) var(--space-6);
    background: none;
    border: none;
    border-bottom: var(--bw-thick) solid transparent;
    color: var(--text-secondary);
    font-family: var(--font-sans);
    font-size: var(--fs-sm);
    font-weight: 500;
    /* ОДНА СТРОКА, ВЫСОТУ ЗАДАЮТ ОТСТУПЫ. Было `normal` на всех пяти видах —
       оно считается ОТ МЕТРИК ШРИФТА, и у разных людей выходит своё число. */
    line-height: var(--lh-none);
    cursor: pointer;
    white-space: nowrap;
    transition: all 0.2s;
}

.td-tab:hover {
    color: var(--text-primary);
}

.td-tab.active {
    color: var(--accent);
    border-bottom-color: var(--accent);
}

/* === SECTIONS === */
.td-section {
    padding: var(--section-y) var(--pad-x);
}

.td-section-header {
    background: var(--bg-primary, var(--neutral-950));
    padding: var(--space-md) var(--pad-x) var(--space-sm);
    border-bottom: var(--bw-hair) solid var(--border-subtle);
    /* Куда прокручивать, когда прыгаем по ссылке на раздел: под шапкой ещё
       липнет полоса вкладок .td-tabs-bar, и заголовок должен встать под ней.
       Было 120 одним числом. Сегодня выходит ровно столько же (64 + 56), но
       теперь видно, из чего оно состоит, и первая половина поедет вместе с
       шапкой сама.
       ОСТОРОЖНО: 56 — НЕ ЗАМЕР. У .td-tabs-bar height: auto, её высота
       зависит от содержимого, а на странице без турнира полоса не рисуется
       вовсе — прибор 21.09 её не увидел ни на одной из пяти ширин. Пока это
       число не подтверждено замером на странице с данными, доверять ему
       нельзя. Задача заведена. */
    scroll-margin-top: calc(var(--header-h) + 56px);
}

.td-section-header h2 {
    max-width: var(--container-max);
    margin: 0 auto;
    font-size: var(--fs-xl);
    font-weight: 700;
    /* МЕЖСТРОЧНЫЙ ЗАДАЁТСЯ СТУПЕНЬЮ, А НЕ НАСЛЕДУЕТСЯ. Здесь его не было
       вовсе, и заголовок брал 1.5 от `body` — межстрочный основного
       текста. Замер 06.10: 26 / 700 / 1.5 на десктопе против 20 / 700 /
       1.2 на телефоне, то есть ОДИН УРОВЕНЬ С ДВУМЯ РАЗНЫМИ ЗНАЧЕНИЯМИ,
       и ни одно из них не ступень: заголовку положен `--lh-tight`. */
    line-height: var(--lh-tight);
    color: var(--text-primary);
}

                                                       
                                                                        
                                                                    
                                                                      
                                                                      
                                                         
                                                               
                                                                         
                                                                  
.td-section-header h3,
.td-section h3{
    font-size: var(--fs-lg);
    font-weight: var(--fw-bold);
    line-height: var(--lh-tight);
}

/* Подзаголовок блока сетки — «Групповой этап» · «Плей-офф» ·
   «Дополнительные матчи». Класс несёт ТОЛЬКО краску и отступ: кегль, вес
   и межстрочный приходят от правила выше, одним определением. */
.td-bracket-subtitle {
    color: var(--accent);
    margin-bottom: var(--space-sm);
}

/* Название группы внутри сетки — заголовок КАРТОЧКИ по лестнице 86:10:
   16 на широких, 14 на узких. Было 15.2 инлайном, такой ступени нет. */
.td-group-name {
    font-size: var(--fs-base);
    font-weight: var(--fw-bold);
    line-height: var(--lh-snug);
    color: var(--text-primary);
    margin-bottom: var(--space-xs);
}


/* ───────── ПОЛОСА «ПОКАЗАТЬ СЫГРАННЫЕ» ─────────

   Сверка фигмы 05.10, компонент `Utility button 56:101`. Его описание
   решает, кнопка это или утилита, прямым текстом: «If the click sends
   something or changes state for real, it is a Button. If it only
   rearranges what is already on screen, it is a Utility button». Полоса
   только перекладывает показанное — значит утилита: радиус 8, никогда не
   таблетка, ступени ТОЛЬКО 28 и 36.

   Берём 36 и слой цели 44, как у чипов ленты сетки. */
.td-sched-ranshe {
    display: inline-flex;
    align-items: center;
    gap: var(--space-sm);
    position: relative;
    min-height: var(--btn-h-sm);
    margin-bottom: var(--space-md);
    padding: 0 var(--space-md);
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text-secondary);
    font-size: var(--fs-sm);
    font-weight: 500;
    line-height: var(--lh-none);
    cursor: pointer;
    transition: color 0.2s, border-color 0.2s;
}

.td-sched-ranshe::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    height: var(--btn-h-md);
    transform: translateY(-50%);
}

.td-sched-ranshe:hover {
    color: var(--text-primary);
    border-color: var(--border-light);
}

.td-sched-ranshe:focus-visible {
    outline: var(--bw-thick) solid var(--accent);
    outline-offset: 2px;
}

/* Знак раскрывашки — та же механика, что у `Accordion 38:63`: плюс
   переворачивается в минус. Рисуется поворотом одной палочки, а не
   подменой символа: подменённый символ диктор прочитает вслух. */
.td-sched-ranshe-znak {
    position: relative;
    width: 10px;
    height: 10px;
    flex: none;
}

.td-sched-ranshe-znak::before,
.td-sched-ranshe-znak::after {
    content: '';
    position: absolute;
    left: 0;
    top: 50%;
    width: 10px;
    height: 1.5px;
    background: currentColor;
    transform: translateY(-50%);
    transition: transform 0.2s;
}

.td-sched-ranshe-znak::after {
    transform: translateY(-50%) rotate(90deg);
}

.td-sched-ranshe.is-open .td-sched-ranshe-znak::after {
    transform: translateY(-50%) rotate(0deg);
}


/* ───────── ЛЕНТА ВЫБОРА БЛОКА СЕТКИ ─────────

   Сверка фигмы 05.10, компонент `Filter chip 49:77`, его описание решает
   спор прямым текстом: «A chip filters a list in place. A Tab switches the
   whole view. They look similar and must not be swapped». Лента выбирает,
   какой блок показан НА МЕСТЕ, и ничего больше — значит чип, не вкладка.

   Числа взяты из описания компонента, а не придуманы: высота 36
   (control/btn-h-sm), поля 16, радиус полный, подпись 14.

   ЦЕЛЬ НАЖАТИЯ — ПРОЗРАЧНЫМ СЛОЕМ, А НЕ РОСТОМ: «кнопка 36 законна, если у
   неё есть прозрачный слой цели 44» — принято 04.10. Слой принадлежит самой
   кнопке, поэтому он ничего не перекрывает: беда 04.10 случилась на
   обёртке вокруг `input`, а не на кнопке. */
.td-fic-lenta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-sm);
    max-width: var(--container-max);
    margin: 0 auto var(--space-md);
    /* Поля нет: лента встаёт по левому краю своего раздела, тем же, что у
       заголовка. ПАРА, КОТОРАЯ ЧИТАЕТСЯ КАК ПАРА, ЕДЕТ ОДНОЙ ГРАНИЦЕЙ. */
}

.td-fic-chip {
    position: relative;
    display: inline-flex;
    align-items: center;
    min-height: var(--btn-h-sm);
    padding: 0 var(--space-md);
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-full);
    background: var(--alpha-white-08);
    color: var(--text-secondary);
    font-size: var(--fs-sm);
    font-weight: 500;
    line-height: var(--lh-none);
    white-space: nowrap;
    cursor: pointer;
    transition: background-color 0.2s, color 0.2s, border-color 0.2s;
}

/* Слой цели 44 — по центру кнопки, высоты кнопки он не знает */
.td-fic-chip::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    height: var(--btn-h-md);
    transform: translateY(-50%);
}

.td-fic-chip:hover {
    background: var(--alpha-white-12);
    color: var(--text-primary);
}

.td-fic-chip.is-active {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-on);
    font-weight: 600;
}

.td-fic-chip:focus-visible {
    outline: var(--bw-thick) solid var(--accent);
    outline-offset: 2px;
}

/* Скрытый блок: `display: none`, а не нулевая высота. Прятать высотой
   значило бы оставить его в раскладке, и `выровнятьМатчиЗаМеста` считала бы
   по нему настоящие числа при невидимом содержимом. */
.td-fic-section.td-fic-skryt {
    display: none;
}


/* === SINGLE ELIMINATION BRACKET === */
.td-bracket-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    padding: var(--space-md) 0;
    max-width: var(--container-max);
    margin: 0 auto;
}

.td-bracket {
    display: flex;
    align-items: stretch;
    min-width: max-content;
    gap: 0;
}

.td-bracket-round {
    display: flex;
    flex-direction: column;
    min-width: 210px;
    /* Матч за третье место висит под колонкой финала на отдельном слое */
    position: relative;
}

/* Матч за третье место — под финалом, в его же колонке.
   Абсолютно, а не в потоке: в потоке он отнимал высоту у списка матчей,
   финал уезжал вверх и переставал попадать на свою соединительную линию. */
.td-third-place {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    margin-top: 14px;
}

/* Блок за третье место висит отдельным слоем и места в потоке не занимает —
   иначе он отнимал бы высоту у колонки и финал сходил со своей линии.
   Поэтому сетке нужен нижний отступ под него, иначе он наезжает на
   следующий раздел. */
.td-bracket:has(.td-third-place) {
    padding-bottom: 130px;
}

.td-third-place .td-round-title {
    margin-bottom: 6px;
}

.td-round-title {
    text-align: center;
    font-size: var(--fs-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--text-muted);
    padding-bottom: var(--space-sm);
    margin-bottom: var(--space-sm);
    border-bottom: var(--bw-hair) solid var(--border-subtle);
}

.td-bracket-matches {
    display: flex;
    flex-direction: column;
    justify-content: space-around;
    flex: 1;
}

/* === MATCH CARD === */
.td-match {
    background: var(--bg-card);
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-sm);
    margin: 4px 0;
    overflow: hidden;
    width: 200px;
    transition: border-color 0.2s;
}

.td-match:hover {
    border-color: var(--border-light);
}

.td-match-highlight,
.td-ig-match.td-match-highlight {
    border-color: var(--accent);
    box-shadow: 0 0 10px var(--alpha-lime-20);
}

.td-match-dimmed,
.td-ig-match.td-match-dimmed {
    opacity: 0.3;
}

tr.td-rr-row-highlight {
    background: var(--alpha-lime-06) !important;
    outline: var(--bw-hair) solid var(--accent);
    outline-offset: -1px;
}

.td-match.live {
    border-color: rgba(255, 71, 87, 0.5);
    box-shadow: 0 0 12px rgba(255, 71, 87, 0.1);
}

.td-match-player {
    display: flex;
    align-items: center;
    padding: 6px 10px;
    gap: 6px;
    font-size: var(--fs-xs);
    border-bottom: var(--bw-hair) solid var(--border-subtle);
}

.td-match-player:last-child {
    border-bottom: none;
}

.td-match-player.winner {
    background: var(--alpha-lime-06);
}

.td-match-player.winner .td-player-name {
    color: var(--text-primary);
    font-weight: 600;
}

.td-match-player.loser .td-player-name {
    color: var(--text-muted);
}

.td-seed {
    color: var(--text-muted);
    font-size: var(--fs-2xs);
    min-width: 24px;
}

/* МЕТКА ГРУППЫ В КЛЕТКЕ СЕТКИ: откуда человек приехал — `A1`, `G2`.
   Приём и размеры взяты у `.ad-brk-grp-label` в админке, а не придуманы
   заново: одно понятие рисуется одинаково на обоих экранах. Ширина 22
   фиксированная — метки `A1` и `G2` обязаны занимать одно место, иначе
   имена в соседних клетках встают разными левыми краями.
   Кегль — та же ступень `--fs-2xs`, что у посева строкой выше. */
.td-grp-label {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 22px;
    margin-right: 4px;
    background: var(--accent-subtle);
    color: var(--accent);
    font-size: var(--fs-2xs);
    font-weight: 700;
    line-height: 1.3;
    border-radius: 3px;
    text-align: center;
}

/* ПУСТАЯ КЛЕТКА, КОТОРАЯ ГОВОРИТ, КОГО ЖДЁТ: `A1`, `IG1`, `Q1`.
   Это не фамилия, а адрес, и читаться должен иначе: вес из системы и
   разрядка, чтобы короткий код не слипался в одно пятно. Цвет —
   `--text-secondary`: на фоне клетки он даёт 9.98 и проходит WCAG 1.4.3,
   а `--text-dim`, которым рисуется «TBD», даёт 3.21 и проваливает. */
.td-slot-wait {
    color: var(--text-secondary);
    font-weight: 600;
    letter-spacing: 0.04em;
}

.td-player-name {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--text-secondary);
}

.td-match-score {
    font-weight: 600;
    min-width: 14px;
    text-align: center;
    color: var(--text-muted);
    font-size: var(--fs-xs);
}

.td-match-player.winner .td-match-score {
    color: var(--accent);
}

.td-match-score.live-score {
    color: var(--danger);
    font-size: var(--fs-xs);
}

.td-match-outcome {
    font-size: var(--fs-2xs);
    font-weight: 700;
    padding: 1px 4px;
    border-radius: 3px;
    background: var(--accent-subtle);
    color: var(--accent);
    margin-left: 2px;
    vertical-align: middle;
}

/* === BRACKET CONNECTORS === */
.td-connector-column {
    display: flex;
    flex-direction: column;
    width: 36px;
    flex-shrink: 0;
}

.td-connector-inner {
    flex: 1;
    display: flex;
    flex-direction: column;
}

.td-connector-pair {
    flex: 1;
    display: flex;
    flex-direction: column;
    position: relative;
}

.td-conn-top,
.td-conn-bottom {
    flex: 1;
    position: relative;
}

.td-conn-top::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 0;
    right: 50%;
    height: 1px;
    background: var(--border-light);
}

.td-conn-top::before {
    content: '';
    position: absolute;
    top: 50%;
    bottom: 0;
    left: 50%;
    width: 1px;
    background: var(--border-light);
}

.td-conn-bottom::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 0;
    right: 50%;
    height: 1px;
    background: var(--border-light);
}

.td-conn-bottom::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 50%;
    left: 50%;
    width: 1px;
    background: var(--border-light);
}

.td-conn-mid {
    position: relative;
    height: 0;
}

.td-conn-mid::after {
    content: '';
    position: absolute;
    top: 0;
    left: 50%;
    right: 0;
    height: 1px;
    background: var(--border-light);
}

/* === FIC SECTIONS === */
.td-fic-section {
    margin-bottom: 28px;
    padding-bottom: 20px;
    border-bottom: var(--bw-hair) solid var(--border, var(--alpha-white-08));
}
.td-fic-section:last-child {
    border-bottom: none;
    margin-bottom: 0;
}
.td-fic-section-title {
    font-size: var(--fs-sm);
    font-weight: 700;
    color: var(--accent);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-bottom: 12px;
    padding-left: 4px;
}

/* === ROUND ROBIN === */






/* ШАПКА ТАБЛИЦЫ — `--text-muted`, А НЕ `--text-dim`. Замер 04.10 на боевой:
   шапка таблицы группы давала контраст 3.21 при норме WCAG AA 4.5 — это
   ТЕКСТ, и правило для текста к нему применяется целиком. `--text-muted`
   даёт 5.33. Та же правка, что сделана в админке 03.10: ПРАВИЛО, ВЫВЕДЕННОЕ
   НА ОДНОМ КУСКЕ, ПРИМЕНЯЕТСЯ К СЛЕДУЮЩЕМУ БЕЗ НОВОГО РАЗБОРА.
   Шапок на странице ЧЕТЫРЕ — круговая, заявки, группа и расписание, — и все
   четыре стояли на одной и той же краске.
   `--text-dim` остаётся там, где он ОСМЫСЛЕН: им рисуется «TBD», и про это
   уже записано выше, у .td-slot-wait. */


















/* Round Robin Knockout */





/* === VENUE === */
.td-venue-card {
    max-width: var(--container-max);
    margin: 0 auto;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-lg);
    background: var(--bg-card);
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-lg);
    overflow: hidden;
}

.td-venue-info {
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
    padding: var(--space-lg);
    justify-content: center;
}

.td-venue-name {
    font-size: var(--fs-lg);
    font-weight: 700;
    color: var(--accent);
    text-decoration: none;
    transition: opacity 0.2s;
}

/* Корт-черновик: имя текстом, а не ссылкой — вести некуда. Наведение
   и курсор тоже снимаем, иначе обещают переход, которого не будет */
.td-venue-name-plain {
    cursor: default;
}

.td-venue-name-plain:hover {
    color: var(--text-primary);
}

.td-venue-name:hover {
    opacity: 0.8;
}

.td-venue-address {
    display: flex;
    align-items: center;
    gap: 6px;
    color: var(--text-secondary);
    font-size: var(--fs-sm);
}

.td-venue-address svg {
    flex-shrink: 0;
    opacity: 0.7;
}

.td-venue-links {
    display: flex;
    gap: var(--space-sm);
    margin-top: var(--space-xs);
}

.td-venue-link {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 6px 14px;
    background: var(--bg-elevated);
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-full);
    color: var(--text-secondary);
    font-size: var(--fs-xs);
    font-weight: 500;
    text-decoration: none;
    transition: all 0.2s;
}

.td-venue-link:hover {
    border-color: var(--accent);
    color: var(--accent);
}

/* КАРТА — ОРИЕНТИР, А НЕ ИНСТРУМЕНТ ИЗУЧЕНИЯ РАЙОНА.
   07.10, слово Кости: «карту сделать размером меньше, она слишком большая».
   Замерено на 1512: колонка карты 900, отношение 16/10 давало высоту 562 —
   больше, чем вся левая колонка со сведениями. Отношение остаётся (на узких
   видах оно и работает), но сверху появляется потолок: 360 — девять ступеней
   по 40, и на широком виде карта перестаёт быть выше своей карточки. */
.td-venue-map {
    aspect-ratio: 16 / 10;
    min-height: 220px;
    max-height: 360px;
}

.td-venue-map iframe {
    width: 100%;
    height: 100%;
    border: 0;
    display: block;
}

/* ПЛАНШЕТ И УЖЕ: карточка площадки и карта ужимаются уже на планшете. */
@media (max-width: 992px) {
    .td-venue-card {
        grid-template-columns: 1fr;
    }
    .td-venue-map {
        aspect-ratio: 16 / 9;
        min-height: 200px;
    }
}


















/* === PARTICIPANTS === */
.td-participants-grid {
    max-width: var(--container-max);
    margin: 0 auto;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 10px;
}

/* When registration tables are inside, remove grid/max-width constraints */
.td-participants-grid:has(.td-reg-columns) {
    display: block;
    max-width: none;
}







.td-participants-subtitle {
    /* Кегль и вес НЕ объявлены: подзаголовок раздела определён один раз,
       правилом `.td-section h3` выше. Здесь остаётся только то, что
       свойственно именно этому подзаголовку. */
    color: var(--text-primary);
    margin-bottom: var(--space-sm);
    display: flex;
    align-items: center;
    gap: 8px;
}

.td-participants-count {
    font-size: var(--fs-xs);
    font-weight: 500;
    color: var(--text-muted);
    background: var(--bg-elevated);
    padding: 2px 10px;
    border-radius: var(--radius-full);
}

.td-reg-columns {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-xl);
}

/* ТОЛЬКО ТЕЛЕФОН: списки записи в одну колонку. На планшете стоя две по
   368 читаются — это список имён, а не карточка. */
@media (max-width: 640px) {
    .td-reg-columns {
        grid-template-columns: 1fr;
    }
}

.td-reg-table-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.td-reg-table {
    width: 100%;
    /* Без минимальной ширины таблица ужималась по контейнеру, и обёртка с
       прокруткой не срабатывала: колонки сжимались, «ВРЕМЯ» уезжало за край.
       У соседней таблицы групп такой минимум был с самого начала. */
    min-width: 620px;
    border-collapse: collapse;
}

.td-reg-table th,
.td-reg-table td {
    padding: 8px 6px;
    border-bottom: var(--bw-hair) solid var(--border-subtle);
    font-size: var(--fs-sm);
    text-align: left;
}

.td-reg-table thead th {
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    white-space: nowrap;
}

.td-reg-table tbody tr {
    transition: background 0.15s;
}

.td-reg-table tbody tr:hover {
    background: var(--surface-glass-hover);
}

.td-reg-table tbody tr:last-child td {
    border-bottom: none;
}

.td-reg-table td:first-child {
    width: 24px;
    color: var(--text-muted);
    font-weight: 600;
    text-align: center;
}

.td-reg-table td:nth-child(2) {
    width: 32px;
}

.td-reg-table td:nth-child(5),
.td-reg-table td:nth-child(6) {
    white-space: nowrap;
    color: var(--text-secondary);
}

.td-reg-photo {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    object-fit: cover;
    display: block;
}

.td-reg-photo-empty {
    background: var(--bg-elevated);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--fs-2xs);
    color: var(--text-muted);
}

.td-reg-table-waitlist {
    opacity: 0.6;
}

.td-reg-table-waitlist tbody tr:hover {
    opacity: 1;
}

/* === DESCRIPTION === */
/* ───────── МЕРА АБЗАЦА ЗАДАЁТСЯ В ЗНАКАХ, А НЕ В ПИКСЕЛЯХ ─────────

   Костя нашёл глазами 05.10: «не понятно как расположен текст». Замер
   пяти видов назвал причину: `max-width: 900px` плюс `margin: 0 auto`
   центровали блок в контейнере 1400, и левый край текста уезжал на 306
   при заголовке на 56 — РАЗРЫВ 250. На 1024 то же слабее (23 против 62),
   на 768 и ниже края совпадали, потому что 900 туда не влезает.

   ПАРА, КОТОРАЯ ЧИТАЕТСЯ КАК ПАРА, ЕДЕТ ОДНОЙ ГРАНИЦЕЙ: заголовок раздела
   и его текст — это пара, и общий левый край у них теперь один.

   900 пикселей при кегле 16 давали 94 знака в строке. Читаемая мера
   45–75, и на 94 глаз теряет начало следующей строки. Мера задана в `ch`
   — единице, которая считает знаки, а не пиксели, и потому не врёт при
   другом кегле. Замерено по видам: 84 · 84 · 76 · 84 · 37.

   80ch, А НЕ 66ch — слово Кости 05.10: «можно будет шире». 88ch отвергнуто
   замером: даёт 92 знака, то есть практически прежние 94.

   ОГОВОРКА: `ch` считается по ширине нуля, а кириллица в Inter уже, и
   80ch выходит 84 знака. Число снято замером, а не посчитано. */
.td-description-text {
    max-width: 80ch;
    font-size: var(--fs-base);
    line-height: var(--lh-relaxed);
    color: var(--text-secondary);
}

/* === RESULTS / PODIUM (Olympic: 3-1-2) === */
/* ───────── РАСКЛАДКА ОЧКОВ ПОД ПЬЕДЕСТАЛОМ ─────────
   Пьедестал показывает троих, а сыграли все: таблица досказывает, кто на
   каком этапе остановился и сколько получил. Отступы со шкалы
   8 · 12 · 16 · 24 · 32 · 40, кегли со шкалы 11 · 12 · 14 · 16 · 18. */
.td-results-table {
    max-width: 720px;
    margin: 32px auto 0;
    padding: 0 16px;
}
.td-results-title {
    font-size: var(--fs-sm);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-secondary);
    margin-bottom: 16px;
    text-align: center;
}
.td-results-table table {
    width: 100%;
    border-collapse: collapse;
}
.td-results-table th {
    font-size: var(--fs-2xs);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    /* `--text-secondary`, а не `--text-dim`: при кегле 11 тусклый цвет
       проваливает WCAG 1.4.3 */
    color: var(--text-secondary);
    text-align: left;
    padding: 8px 12px;
    border-bottom: var(--bw-hair) solid var(--alpha-white-12);
}
.td-results-table td {
    font-size: var(--fs-sm);
    padding: 12px;
    border-bottom: var(--bw-hair) solid var(--alpha-white-08);
}
.td-res-place { width: 72px; text-align: center; font-weight: 600; }
.td-results-table th.td-res-place { text-align: center; }
.td-res-stage { color: var(--text-secondary); }
.td-res-pts { width: 80px; text-align: right; font-weight: 700; color: var(--accent); }
.td-results-table th.td-res-pts { text-align: right; }
.td-res-winner td { background: var(--alpha-lime-06); font-weight: 600; }

/* ПОБЕДИТЕЛЬ НЕ ТЕРЯЕТ ВЕС В КОЛОНКЕ ОЧКОВ.
   Замер 02.10 прогоном: в строке победителя очки шли весом 600, а у всех
   остальных — 700. `.td-res-winner td` весит (0,1,1), `.td-res-pts` —
   (0,1,0), и правило строки перебивало правило колонки ровно там, где
   число важнее всего. Поймала проверка лестницы, а не глаз. */
.td-results-table .td-res-winner .td-res-pts { font-weight: 700; }

/* ───────── ОТСЕЧКА ДЛИННОЙ ТАБЛИЦЫ ─────────
 *
 * Ограничения не было вовсе: запрос итогов идёт без `limit`
 * (`js/tournament-detail.js`), и строки рисуются все до одной. На восьми
 * это читается, на 64 — стена в треть экрана, и смотрят её с телефона.
 *
 * СТРОКА СКРЫТА, А НЕ ВЫБРОШЕНА: она есть в разметке, её находит поиск по
 * странице и читает диктор. Кнопка лишь ставит класс на свою коробку.
 * ОДНА ОТСЕЧКА НА ТРИ ТАБЛИЦЫ: итоги, заявки и расписание прячут хвост
 * одним правилом, а не тремя похожими.
 */
/* Правила отсечки уехали в css/kslt-otsechka.css — там же, где её
   механизм: одно понятие, одно место. Классы теперь kslt-skryta и
   kslt-more. */

/* Две лиги идут одна под другой: заголовок каждой — её собственный. */
.td-results-table + .td-results-table { margin-top: var(--space-6); }

/* Телефон: этап уходит — он производное от места, а место остаётся */
@media (max-width: 640px) {
    /* Название группы — ступень ниже, как велит лестница card title. */
    .td-group-name {
        font-size: var(--fs-sm);
    }

    .td-res-stage { display: none; }
    .td-res-place { width: 56px; }
}

.td-podium {
    max-width: 800px;
    margin: 0 auto;
    display: flex;
    justify-content: center;
    align-items: flex-end;
    gap: 0;
    position: relative;
    padding-bottom: 1px;
}

/* ДВА ПЬЕДЕСТАЛА ПОДРЯД — ПОЛОСА МЕЖДУ НИМИ (07.11). В турнире с двумя
   лигами результата два: у верхней сетки свой пьедестал, у утешительной
   свой. Стояли они встык, и подпись второй лиги читалась как подпись к
   карточкам первой.
   ПАРА, КОТОРАЯ ЧИТАЕТСЯ КАК ПАРА, ЕДЕТ ОДНОЙ ГРАНИЦЕЙ: подпись лиги и её
   пьедестал — одно целое, и отступ ставится ПЕРЕД подписью, а не после
   карточек. 32 — ступень шкалы, на ступень крупнее отступа внутри блока. */
.td-podium + .td-league-title { margin-top: 32px; }

.td-podium-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: var(--space-md) var(--space-sm) 0;
    transition: all 0.2s;
    /* Три равные колонки, как в рейтинге: ширина не зависит от длины фамилии */
    flex: 1 1 0;
    min-width: 0;
    max-width: 230px;
    position: relative;
}

/* Тумба. Она подложка под именем, а не отдельный блок под ним: раньше
   имя стояло над пустой тумбой, и пьедестал выглядел иначе, чем в рейтинге */
.td-podium-card::after {
    content: '';
    display: block;
    width: 100%;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 0;
}

.td-podium-medal,
.td-podium-photo-wrap,
.td-podium-place,
.td-podium-name,
.td-podium-country,
.td-podium-prize {
    position: relative;
    z-index: 1;
}

/* Порядок как в рейтинге: слева второе, в середине первое, справа третье.
   Здесь было наоборот — второе справа, третье слева, — и одно и то же место
   на двух страницах оказывалось по разные стороны */
.td-podium-1 { order: 1; z-index: 3; margin: 0 -8px; }
.td-podium-2 { order: 0; z-index: 2; }
.td-podium-3 { order: 2; z-index: 1; }

.td-podium-1 { padding-bottom: var(--space-lg); }
.td-podium-2,
.td-podium-3 { padding-bottom: var(--space-md); }

.td-podium-1::after {
    height: 120px;
    background: linear-gradient(to top, var(--alpha-lime-06), var(--alpha-lime-06));
    border: var(--bw-hair) solid var(--alpha-lime-20);
    border-bottom: none;
}

.td-podium-2::after {
    height: 80px;
    background: linear-gradient(to top, rgba(192, 192, 192, 0.06), transparent);
    border: var(--bw-hair) solid var(--border-subtle);
    border-bottom: none;
    border-right: none;
}

.td-podium-3::after {
    height: 56px;
    background: linear-gradient(to top, rgba(205, 127, 50, 0.06), transparent);
    border: var(--bw-hair) solid var(--border-subtle);
    border-bottom: none;
    border-left: none;
}

.td-podium-photo-wrap {
    margin-bottom: var(--space-sm);
}

.td-podium-photo {
    border-radius: 50%;
    object-fit: cover;
    transition: transform 0.3s;
}

.td-podium-card:hover .td-podium-photo {
    transform: scale(1.05);
}

.td-podium-1 .td-podium-photo {
    width: 110px;
    height: 110px;
    border: var(--bw-heavy) solid var(--accent);
    box-shadow: 0 0 20px var(--accent-subtle);
}

.td-podium-2 .td-podium-photo {
    width: 80px;
    height: 80px;
    border: var(--bw-heavy) solid rgba(192, 192, 192, 0.4);
}

.td-podium-3 .td-podium-photo {
    width: 72px;
    height: 72px;
    border: var(--bw-heavy) solid rgba(205, 127, 50, 0.4);
}

.td-podium-medal { margin-bottom: var(--space-xs); }
.td-podium-1 .td-podium-medal { font-size: var(--fs-3xl); }
.td-podium-2 .td-podium-medal { font-size: var(--fs-2xl); }
.td-podium-3 .td-podium-medal { font-size: var(--fs-xl); }

.td-podium-place {
    font-size: var(--fs-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--text-muted);
    margin-bottom: var(--space-xs);
}

.td-podium-name {
    font-weight: 700;
    color: var(--text-primary);
    margin-bottom: 4px;
    line-height: 1.3;
    overflow-wrap: break-word;
    max-width: 100%;
}

.td-podium-1 .td-podium-name { font-size: var(--fs-md); }
.td-podium-2 .td-podium-name,
.td-podium-3 .td-podium-name { font-size: var(--fs-base); }

.td-no-results {
    max-width: var(--container-max);
    margin: 0 auto;
    text-align: center;
    padding: var(--space-xl) 0;
    color: var(--text-muted);
}

/* ПУСТОЕ СОСТОЯНИЕ НЕ МОЛЧИТ: строка называет, чего нет, подпись — когда
   появится. `Empty state 37:53`: «never just no data». Два уровня, а не
   один: заголовок несёт вес, подсказка — тише и мельче, и обе стоят на
   ступенях шкалы (16 и 14), а не на числах. */
.td-empty-title {
    font-size: var(--fs-base);
    font-weight: 600;
    line-height: var(--lh-snug);
    color: var(--text-primary);
    margin: 0 0 var(--space-xs);
}

.td-empty-hint {
    font-size: var(--fs-sm);
    line-height: var(--lh-normal);
    color: var(--text-secondary);
    margin: 0;
}

/* === LOCKED PAGE === */
/* Дорога назад на экране «турнир не найден»: отступ ступенью, а не инлайном.
   У экрана «нужен вход» он стоял числом в атрибуте style — туда же, куда
   пакетная правка потом не доберётся */
.td-notfound-back {
    margin-top: var(--space-4);
}

.td-hero-locked {
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: var(--space-2xl) 0 var(--space-xl);
}

.td-locked-subtitle {
    color: var(--text-secondary);
    font-size: var(--fs-lg);
    line-height: 1.5;
    margin-bottom: var(--space-xl);
    max-width: 600px;
}

.td-locked-buttons {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
    justify-content: center;
}

.td-locked-btn {
    display: inline-flex;
    align-items: center;
    padding: 16px 44px;
    background: var(--accent);
    color: var(--neutral-black);
    font-weight: 700;
    font-size: var(--fs-md);
    border-radius: var(--radius-full);
    text-decoration: none;
    transition: all 0.2s;
}

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

.td-locked-btn-secondary {
    display: inline-flex;
    align-items: center;
    padding: 16px 44px;
    background: transparent;
    color: var(--text-primary);
    font-weight: 600;
    font-size: var(--fs-md);
    border: var(--bw-hair) solid var(--border-light);
    border-radius: var(--radius-full);
    text-decoration: none;
    transition: all 0.2s;
}

.td-locked-btn-secondary:hover {
    border-color: var(--accent);
    color: var(--accent);
}

/* === GROUP TABLES 2-COLUMN GRID === */
.td-groups-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 24px 32px;
    max-width: var(--container-max);
    margin: 0 auto;
}

/* ТАБЛИЦЫ ГРУПП — ДВЕ В РЯД ТОЛЬКО ОТ 993 — 16.10, кусок 75.
   Было: одна колонка до 768, две выше, а сжатие ячеек под две в ряд
   включалось с 900. Полоса 769–899 оставалась с двумя НЕсжатыми
   таблицами — именно там колонка геймов и не помещалась. Теперь пара и
   её сжатие стоят на одной границе, домашней. */
@media (max-width: 992px) {
    .td-groups-grid {
        grid-template-columns: 1fr;
    }
}

/* === GROUP CROSS-TABLE (Round Robin) === */
.td-group-table {
    width: 100%;
    border-collapse: collapse;
    background: var(--bg-card);
    border: var(--bw-hair) solid var(--border-subtle);
    border-radius: var(--radius-sm);
}

.td-group-table th,
.td-group-table td {
    padding: 8px 6px;
    border-bottom: var(--bw-hair) solid var(--border-subtle);
    font-size: var(--fs-sm);
}

/* Две таблицы в ряд: после колонки геймов они перестали помещаться в свою
   половину, и правая обрезалась по краю */
@media (min-width: 993px) {
    .td-groups-grid .td-group-table th,
    .td-groups-grid .td-group-table td {
        padding: 7px 4px;
        font-size: var(--fs-xs);
    }
}

/* Кто проводил турнир: строка под подиумом, как подпись в протоколе */




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

   В одну строку, и перенос запрещён: «личная встреча» переносилась на
   вторую строку, а «жребий» оставался на одной, и строки в таблице
   выходили разной высоты. Рядом две группы, и разнобой был виден сразу */
.td-grp-lot {
    display: block;
    margin-top: 3px;
    font-size: var(--fs-2xs);
    font-weight: 500;
    line-height: 1;
    color: var(--text-muted);
    cursor: help;
    white-space: nowrap;
}

/* Две группы в ряд — подпись ужимаем, чтобы длинная не растянула колонку */
@media (min-width: 993px) {
    .td-groups-grid .td-grp-lot {
        font-size: var(--fs-2xs);
    }
}

.td-group-table thead th {
    background: var(--bg-elevated);
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.td-group-table tbody tr {
    transition: background 0.15s;
}

.td-group-table tbody tr:hover {
    background: var(--surface-glass-hover);
}

.td-group-table tbody tr:last-child td {
    border-bottom: none;
}

/* === RESPONSIVE === */
@media (max-width: 992px) {
    /* РЕШЕНИЕ СТРАНИЦЫ СОХРАНЕНО, ТОЛЬКО ВЫРАЖЕНО СТУПЕНЬЮ И НА СВОЕЙ
       ГРАНИЦЕ. Причина прежняя и записана автором правила ниже: три
       показателя в одну строку рядом с названием турнира кричали громче
       него. Обложка турнира КРУТИТ общую ручку у себя — ступенью ниже
       стандарта раздела, — а не заводит собственный размер долями ширины.
       Было clamp(1rem, 4.4vw, 1.35rem): 21.6 на планшете и 17.16 на
       телефоне, чисел со шкалы среди них нет.

       ГРАНИЦА 992, А НЕ 768. Первый заход 04.10 поставил крутилку в медиа
       768 — и телефон БОКОМ (844) в неё не попал: цифры там остались 32,
       крупнее, чем на планшете стоя. ВИД НЕ ДЕЛИТСЯ ОДНОЙ ШИРИНОЙ, а
       границы у нас две — 640 и 992.

       ПРЕДПОЛОЖЕНИЕ: ступень ниже — это --fs-md (18) и --fs-2xs (11).
       Угадан размер, не причина. */
    .td-hero {
        /* 06.10: ЧЕТЫРЕ СТУПЕНИ ЗА ОДИН ШАГ — лестница померила, чего стоила
           догадка 04.10. Крутилка идёт 40 → 32 → 21, а страница пришпиливала
           18 на ВЕСЬ диапазон ниже 992: средняя ступень крутилки до этой
           страницы не доезжала никогда, и выходило 40 → 18.
           Замысел был верен, число — нет. «Ступень ниже крутилки» на 992
           это 26, а не 18. Проверено отношением к названию турнира: на 768
           название 32 — цифра 26 тише его, как и хотел Костя; на десктопе
           40 против 26 — то же отношение. Ниже 640 крутилка даёт 21, и
           ступень ниже — это и есть 18. */
        --fs-hero-num: var(--fs-xl);   /* 26 — ступень ниже крутилки (32) */
        --fs-hero-cap: var(--fs-2xs);
    }

    .td-hero-stats {
        gap: var(--space-md);
    }


}


/* ЦИФРА ОБЛОЖКИ НИЖЕ 640 — ступень ниже крутилки и здесь.
   Порядок правил решает: блок 640 выше по файлу стоит ДО блока 992, и
   на 390 выигрывал бы 992-й. Поэтому ступень для узких объявлена здесь,
   после него, а не там. Крутилка на 640 даёт 21 — ступень ниже это 18. */
@media (max-width: 640px) {
    .td-hero {
        --fs-hero-num: var(--fs-md);   /* 18 — ступень ниже крутилки (21) */
    }
}

/* ПЛАНШЕТ И УЖЕ: секции, вкладки, цифры обложки, расписание, участники
   и подиум — всё это сжатие под узкий экран, а планшет узкий тоже. */
@media (max-width: 992px) {
    .td-section {
        padding: var(--section-y-sm) var(--pad-x);
    }

    .td-section-header {
        padding: var(--space-sm) var(--pad-x) var(--space-xs);
    }

    .td-tabs-bar {
        padding: 0 var(--pad-x);
    }

    /* Первая вкладка на телефоне не прижимается вплотную к краю: базовое
       правило снимает ей левый отступ, и без этой строки она упиралась в
       границу экрана */
    .td-tab:first-child {
        padding-left: 16px;
    }

    .td-hero-stats {
        flex-wrap: wrap;
        gap: var(--space-sm);
    }

    .td-hero-meta {
        flex-direction: column;
        gap: var(--space-xs);
    }




    .td-participants-grid {
        grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    }

    .td-podium-card { padding: var(--space-sm) var(--space-md) 0; }
    .td-podium-1 .td-podium-photo { width: 80px; height: 80px; }
    .td-podium-2 .td-podium-photo { width: 65px; height: 65px; }
    .td-podium-3 .td-podium-photo { width: 58px; height: 58px; }
    .td-podium-1 .td-podium-medal { font-size: var(--fs-2xl); }
    .td-podium-2 .td-podium-medal { font-size: var(--fs-xl); }
    .td-podium-3 .td-podium-medal { font-size: var(--fs-lg); }
    .td-podium-1::after { height: 100px; }
    .td-podium-2::after { height: 65px; }
    .td-podium-3::after { height: 45px; }
    .td-podium-1 .td-podium-name { font-size: var(--fs-base); }
    .td-podium-2 .td-podium-name,
    .td-podium-3 .td-podium-name { font-size: var(--fs-sm); }


}

@media (max-width: 640px) {
    /* ШИРИНА КОЛОНКИ СЕТКИ И КЛЕТКИ ЖИВЁТ ОДНИМ МЕСТОМ НИЖЕ (:2627).
       10.11: она была объявлена в этом файле ПЯТЬ раз — 210, clamp(160..180),
       1 1 0, 0 0 200 и 0 0 150. Побеждало последнее, и на телефоне колонка
       выходила 150: имени доставалось 62 пикселя, и из 160 фамилий в сетке
       ОБРЕЗАЛОСЬ 28 — «Мирбек Дюшеналиев», «Улукбек Бекбосунов».
       ОДНО ОПРЕДЕЛЕНИЕ НА ОДНО ПОНЯТИЕ: беда родилась ровно на шве. */

    .td-tabs {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }


    .td-participants-grid {
        grid-template-columns: 1fr;
    }

    .td-locked-subtitle {
        font-size: var(--fs-md);
    }

    .td-locked-btn,
    .td-locked-btn-secondary {
        padding: 14px 28px;
        font-size: var(--fs-base);
        width: 100%;
        justify-content: center;
    }

    .td-locked-buttons {
        flex-direction: column;
        width: 100%;
        max-width: 300px;
    }
}

@media (max-width: 640px) {
    .td-hero-meta {
        font-size: var(--fs-sm);
    }
}

/* === PREDICTION BAR === */
.td-prediction {
    padding: 6px 8px 4px;
    border-top: var(--bw-hair) solid var(--alpha-white-08);
}

.td-pred-names {
    display: flex;
    justify-content: space-between;
    font-size: var(--fs-2xs);
    font-weight: 600;
    color: var(--text-secondary);
    margin-bottom: 2px;
    letter-spacing: 0.2px;
}

.td-pred-bar {
    display: flex;
    height: 18px;
    border-radius: 9px;
    overflow: hidden;
    background: var(--alpha-white-04);
    cursor: help;
}

.td-pred-fill {
    display: flex;
    align-items: center;
    justify-content: center;
    transition: width 1.2s cubic-bezier(0.22, 1, 0.36, 1);
    font-size: var(--fs-2xs);
    font-weight: 700;
    letter-spacing: 0.3px;
    min-width: 0;
    overflow: hidden;
}

.td-pred-fav {
    background: linear-gradient(90deg, var(--accent), var(--alpha-lime-70));
    color: var(--neutral-black);
}

.td-pred-dog {
    background: var(--alpha-white-12);
    color: var(--text-muted);
}

.td-pred-pct {
    white-space: nowrap;
}

.td-pred-label {
    text-align: center;
    font-size: var(--fs-2xs);
    color: var(--text-muted);
    margin-top: 3px;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    opacity: 0.7;
}

/* Hide pct text when bar is too narrow */
.td-pred-fill[data-width="5%"] .td-pred-pct,
.td-pred-fill[data-width="6%"] .td-pred-pct,
.td-pred-fill[data-width="7%"] .td-pred-pct,
.td-pred-fill[data-width="8%"] .td-pred-pct,
.td-pred-fill[data-width="9%"] .td-pred-pct,
.td-pred-fill[data-width="10%"] .td-pred-pct,
.td-pred-fill[data-width="11%"] .td-pred-pct,
.td-pred-fill[data-width="12%"] .td-pred-pct {
    font-size: 0;
}

/* Responsive prediction */
@media (max-width: 640px) {
    .td-prediction {
        padding: 4px 6px 3px;
    }
    .td-pred-bar {
        height: 14px;
    }
    .td-pred-fill {
        font-size: var(--fs-2xs);
    }
    .td-pred-label {
        font-size: var(--fs-2xs);
    }
}

/* === COUNTDOWN TIMER === */
.td-countdown {
    margin-top: var(--space-md);
    text-align: center;
}

.td-cd-title {
    font-size: var(--fs-2xs);
    font-weight: 600;
    letter-spacing: 2px;
    color: var(--alpha-white-50);
    margin-bottom: var(--space-sm);
}

.td-cd-boxes {
    display: flex;
    gap: 10px;
    justify-content: center;
}

.td-cd-box {
    background: var(--alpha-white-08);
    backdrop-filter: blur(8px);
    border: var(--bw-hair) solid var(--alpha-white-12);
    border-radius: 10px;
    padding: 10px 14px;
    min-width: 60px;
    text-align: center;
    transition: background 0.3s, border-color 0.3s;
}

.td-cd-num {
    display: block;
    font-size: var(--fs-xl);
    font-weight: 700;
    color: var(--accent);
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
    transition: color 0.3s;
}

.td-cd-label {
    display: block;
    font-size: var(--fs-2xs);
    font-weight: 500;
    color: var(--alpha-white-50);
    text-transform: uppercase;
    letter-spacing: 1px;
    margin-top: 2px;
}

/* Urgent: < 10 min — red numbers */
.td-cd-urgent .td-cd-num {
    color: #ff4444;
}
.td-cd-urgent {
    border-color: rgba(255,68,68,0.3);
    background: rgba(255,68,68,0.08);
}

/* Pulse: < 1 hour — gentle animation */
.td-cd-pulse .td-cd-boxes {
    animation: cdPulse 2s ease-in-out infinite;
}

@keyframes cdPulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.7; }
}

/* Live indicator */
.td-countdown-live {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
}

.td-cd-pulse-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--accent);
    animation: cdLivePulse 1.5s ease-in-out infinite;
}

@keyframes cdLivePulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50% { opacity: 0.4; transform: scale(0.8); }
}

.td-cd-live-text {
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--accent);
    letter-spacing: 1px;
    text-transform: uppercase;
}

/* Registration warning */
.td-cd-reg-warning {
    margin-top: var(--space-sm);
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    font-size: var(--fs-xs);
    font-weight: 500;
    color: var(--warning);
}

.td-countdown-reg {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    font-size: var(--fs-xs);
    font-weight: 500;
    color: var(--warning);
}

@media (max-width: 640px) {
    .td-cd-box {
        min-width: 50px;
        padding: 8px 10px;
    }
    .td-cd-num {
        font-size: var(--fs-lg);
    }
    .td-cd-label {
        font-size: var(--fs-2xs);
    }
    .td-cd-boxes {
        gap: 6px;
    }
}

/* ========================================
   DUAL LEAGUE LAYOUT (Group + Leagues)
   ======================================== */

.td-dual-league {
    display: flex;
    gap: 32px;
    margin-bottom: 32px;
}

.td-league-bracket {
    flex: 1;
    min-width: 0;
    overflow-x: auto;
}

/* ОДНО ПОНЯТИЕ ОПРЕДЕЛЕНО ДВАЖДЫ — И ПОБЕЖДАЛО НЕ ЗАДУМАННОЕ (07.11).
   В этом правиле стоит 16, а на экране подпись была 21: её перебивало
   `.td-section-header h3, .td-section h3` с `--fs-lg` —
   0,0,1,1 против 0,0,1,0. Замер 07.11 на 1512 показал лестницу раздела
   «Места»: заголовок 26/700, подпись лиги 21/700, имя победителя 18/700,
   мета 12/600 — то есть РАМКА КРИЧАЛА ГРОМЧЕ СОДЕРЖАНИЯ: название лиги
   было крупнее имени тех, кто её выиграл.
   МОРОЗИТСЯ ОТНОШЕНИЕ, А НЕ ЧИСЛО: подпись лиги строго мельче заголовка
   раздела и строго мельче имени победителя на пьедестале, и строго
   крупнее подписи круга. 26 > 18 > 16 > 12 — все четыре на ступенях.
   Селектор поднят до `h3.td-league-title`, а не обёрнут в
   родителя: подпись лиги ВСЕГДА h3 — и над сеткой, и на пьедестале. */
h3.td-league-title {
    display: inline-block;
    padding: 8px 16px;
    border-radius: var(--radius-sm, 6px);
    font-size: var(--fs-base);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-bottom: 16px;
}

/* ПОДПИСЬ ЛИГИ ОПУСКАЕТСЯ ВМЕСТЕ С ИМЕНЕМ ПОБЕДИТЕЛЯ (08.11).
   На широком лестница стоит 26 → 18 → 16 → 12. На узких видах имя
   победителя опускается до 16 (`@media (max-width: 992px)` выше), а
   подпись оставалась 16 — РАВНЫЕ, и отношение рушилось: рамка
   сравнялась с содержанием. Поймала не пара глаз и не чтение css, а
   ПРОБА на пяти видах: десктоп и планшет лёжа прошли, планшет, телефон
   и телефон лёжа упали.
   ОТНОШЕНИЕ МОРОЗИТСЯ НА ВСЕХ ВИДАХ, А НЕ НА ТОМ, ГДЕ ЕГО МЕРИЛИ.

   ПОЧЕМУ ЗДЕСЬ, А НЕ В ТОМ МЕДИА-БЛОКЕ ВЫШЕ. Первая попытка поставила
   это правило там — и не сработала вовсе: четыре правила на подпись лиги
   имеют ОДИНАКОВУЮ специфичность 0,0,1,1, а общее `h3.td-league-title`
   стоит НИЖЕ медиа-блока и перебивало его на всех ширинах.
   РАВНАЯ СПЕЦИФИЧНОСТЬ РЕШАЕТСЯ ПОРЯДКОМ, И ЭТО НЕ РЕШЕНИЕ — третий раз
   за день одна и та же беда. Поэтому селекторы названы местами, где
   подпись живёт: у пьедестала (1,0,1,1) и над сеткой лиги (0,0,2,1).
   Оба выше 0,0,1,1 и от порядка не зависят. */
@media (max-width: 992px) {
    #resultsPodium h3.td-league-title,
    .td-league-bracket h3.td-league-title { font-size: var(--fs-sm); }
}

.td-league-title-premier {
    color: var(--accent);
    background: var(--alpha-lime-06);
    border: var(--bw-hair) solid var(--alpha-lime-20);
}

.td-league-title-consolation {
    color: var(--text-secondary);
    background: var(--alpha-white-04);
    border: var(--bw-hair) solid var(--alpha-white-12);
}

@media (max-width: 992px) {
    .td-dual-league {
        flex-direction: column;
    }
}

/* ---- Doubles: team names ---- */
/* ОДНО ОПРЕДЕЛЕНИЕ НА ОДНО ИМЯ (08.11). `white-space` стоял отдельным
   правилом ниже — второе объявление `.td-team-name` решало бы порядком
   в файле, и чужой сторож «договор имени» это назвал числом. */
.td-team-name {
    font-size: var(--fs-sm);
    line-height: 1.3;
    /* ПЕРЕНОС ЗАДАЁТСЯ ЗДЕСЬ, А НЕ НАСЛЕДУЕТСЯ. Родитель
       `.td-player-name` на десктопе стоит в `nowrap` с многоточием: это
       верно для одиночки, у которого имя одно, и неверно для пары, где
       обрезалось ВТОРОЕ ИМЯ ЦЕЛИКОМ. */
    white-space: normal;
}

/* ПАРА ПИШЕТСЯ ДВУМЯ СТРОКАМИ — КАК В АДМИНКЕ (07.11).
   Admin: `.ad-brk-name .ad-team-p { display: block }` и
   `.ad-team-sep { display: none }`. Одно понятие — одно оформление.

   ПЕРЕНОС ЗАДАЁТСЯ БЛОКОМ, А НЕ НАДЕЖДОЙ НА СЛОВО. Родитель
   `.td-player-name` на десктопе стоит в `nowrap` с многоточием: это
   верно для одиночки, у которого имя одно, и неверно для пары, где
   обрезалось ВТОРОЕ ИМЯ ЦЕЛИКОМ. Своё `normal` перебивает
   унаследованное, а два `display: block` делают перенос ровно по
   разделителю, а не «как попало» посреди фамилии. */
/* ЯКОРЬ НА САМИХ ЧАСТЯХ, А НЕ НА ОБЁРТКЕ (07.11). Сперва правило стояло
   как `.td-team-name .td-team-p` — и не сработало на пьедестале,
   где обёртки `.td-team-name` нет: там своя карточка со своим
   кеглем, и надеть на неё обёртку значило бы отдать ей чужой размер
   букв. Понятие «часть пары» живёт в самом `.td-team-p`; где он
   стоит — решает зовущий. */
.td-team-p { display: block; }
.td-team-sep { display: none; }

/* КЕГЛЬ ПАРЫ ЗАДАЁТСЯ ПО МЕСТУ, А НЕ ОБЁРТКОЙ В РАЗМЕТКЕ (08.11).
   Прежде двоих оборачивали в `.td-team-name` ради её `--fs-sm`. Обёртка
   ушла: разметка перестала решать за оформление, и склеек стало меньше.
   В клетке сетки кегль прежний — замер 07.11 дал 14, и он не изменился.
   На карточке пьедестала части кегля не задают вовсе: там свой размер
   букв у каждого места, и чужой отнял бы его. */
.td-player-name .td-team-p { font-size: var(--fs-sm); line-height: 1.3; }

/* КЛЕТКИ ДЕРЖАТ ОДНУ ВЫСОТУ ЗАРАНЕЕ. Пара занимает две строки, `BYE` и
   `TBD` — одну; без общей высоты соединители сетки разъезжаются, и
   линия приходит мимо клетки. Приём и число взяты у админки
   (`.ad-brk-player { min-height: 2.6em }`) — две строки при 1.3. */
.td-bracket .td-match-player { min-height: 2.6em; }


/* Мест осталось мало — цифра в шапке турнира становится предупреждением */
.hero-stat-tight .hero-stat-value {
    color: var(--warning);
}

/* ---- Свой матч в сетке: кнопка счёта или подпись ---- */
.td-match-act {
    display: block;
    width: 100%;
    padding: 6px;
    border: 0;
    font-size: var(--fs-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.4px;
}

.td-match-enter {
    background: var(--accent);
    color: var(--neutral-950);
    cursor: pointer;
}

.td-match-enter:hover { filter: brightness(1.08); }

.td-match-wait {
    text-align: center;
    color: var(--text-muted);
    background: var(--alpha-white-04);
    font-weight: 500;
    text-transform: none;
    letter-spacing: 0;
}

/* Своя клетка в таблице группы: нажимается и ведёт в окно счёта.
   Фамилия соперника стоит подложкой — приглушённой, второй строкой: в
   турнирной таблице фамилий не пишут, но без неё две соседние свои клетки
   сливаются в одну полосу, и непонятно, где какой матч. */
.td-grp-mine {
    cursor: pointer;
    background: var(--alpha-lime-12);
    border: var(--bw-hair) solid var(--accent);
    padding: 4px 6px !important;
    line-height: 1.1;
}

.td-grp-mine:hover { background: var(--alpha-lime-20); }

.td-grp-act {
    display: block;
    color: var(--accent);
    font-size: var(--fs-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.3px;
}

.td-grp-opp {
    display: block;
    color: var(--text-muted);
    font-size: var(--fs-2xs);
    opacity: 0.7;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Пустой столбец вместо недостающего круга: блоки за места начинаются не
   с первого круга, и без него каждый блок съезжал к левому краю, вместо
   того чтобы стоять под своим кругом основной сетки. */
.td-round-spacer {
    flex-shrink: 0;
}

/* Вид полосы прокрутки задан один раз для всего сайта — см. style.css,
   раздел «Полосы прокрутки». Здесь ничего объявлять не нужно. */


/* ---- Одинаковая высота блоков сетки ----
   Та же беда, что в админке: блок с W/O был выше, блок без счёта ниже,
   и позиция матчей следующего круга накапливала расхождение. */
.td-match {
    min-height: 62px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    position: relative;
}
.td-match .td-match-player {
    min-height: 30px;
    box-sizing: border-box;
}
/* W/O и прочие исходы: стояли поверх блока и налезали на фамилию.
   Возвращаем в строку счёта — высоту это не меняет, потому что строка
   там есть всегда. */
.td-match-outcome {
    position: static;
    font-size: var(--fs-2xs);
    margin-left: 4px;
    white-space: nowrap;
}

/* ---- Сетка на 64: помещается на экран ----
   Шесть колонок и тридцать две пары в первом круге по высоте не влезают.
   Заголовки кругов закрепляем, чтобы при прокрутке было видно, где ты
   находишься, а саму сетку уменьшаем — иначе её не увидеть целиком. */
.td-bracket-scroll {
    overflow-x: auto;
    overflow-y: visible;
}
.td-bracket-round .td-round-title {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--bg-primary);
    padding: 6px 0;
}

/* ---- Сетка помещается на экран ----
   Прокрутки вдоль не нужно: сетка ужимается по ширине окна. Блоки уже,
   шрифт мельче, промежутки короче — целиком видно и на ноутбуке.

   Только на широких экранах. На телефоне ужимать нечего: четыре круга в
   390 пикселей не влезают, колонки наезжали друг на друга, а прокрутку это
   же правило и запрещало — сетку нельзя было ни прочитать, ни сдвинуть. */
@media (min-width: 993px) {
.td-bracket-scroll {
    /* Не hidden, а visible: при hidden по одной оси браузер сам делает
       вторую прокручиваемой — внутри сетки появлялась вертикальная полоса,
       и сетка жила в собственном окошке. Прокрутки здесь не нужно вовсе:
       по ширине колонки ужимаются сами, по высоте листается страница */
    overflow: visible;
    max-width: 100%;
}
.td-bracket {
    min-width: 0;
    width: 100%;
}
.td-bracket-round {
    min-width: 0;
    flex: 1 1 0;
}

/* ПАРНЫЕ ЛИГИ СТАНОВЯТСЯ ДРУГ ПОД ДРУГОМ (07.11) — ЧТОБЫ ПОМЕСТИЛИСЬ,
   А НЕ ЧТОБЫ ЛИСТАТЬСЯ.
   В клетке парного турнира имён два, и замер 07.11 на 1512 показал:
   две лиги делят ширину пополам, колонка выходит 157,5, имени достаётся
   76, а `Трофимушкина` требует 85 — второе имя обрезалось краем.
   ПЕРВЫМ ДЕЛОМ ПОПРОБОВАЛИ ПРОКРУТКУ, КАК В АДМИНКЕ, И ОТКАЗАЛИСЬ:
   колонка встала 190, имена влезли, но финал высшей лиги уехал за правый
   край и соединительные линии разошлись. Прокрутка — цена за то, чего
   можно не платить: лигам тесно не по своей вине, а потому что их двое
   в ряд. Поставленные друг под другом, каждая получает всю ширину, и
   колонка выходит около 336 — имени достаётся 266 при нужных 85.
   ПОСЧИТАНО: 1400 − 3 связки по 18 = 1346 на 4 круга. Проверено в
   браузере на 1512.
   ПРИЗНАК БЕРЁТСЯ ИЗ СОДЕРЖИМОГО, А НЕ ЗАВОДИТСЯ ВТОРЫМ ПОНЯТИЕМ:
   `:has(.td-team-p)` значит «в этих сетках есть пара» — ровно
   то, что рисует клетка. Отдельный класс пришлось бы ставить из JS и
   держать с ним в согласии.
   Одиночных турниров это не касается вовсе: у них пар нет, лиги остаются
   в ряд, как стояли.
   Браузер без `:has` правило не применит и останется на прежнем
   виде — ничего не ломается. */
.td-dual-league:has(.td-team-p) {
    flex-direction: column;
}

.td-bracket .td-match {
    width: 100%;
    max-width: 100%;
    font-size: var(--fs-xs);
}
.td-bracket .td-player-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ПЕРЕНОС ПАРЫ СТОИТ РЯДОМ С ЗАПРЕТОМ, А НЕ ВЫШЕ ПО ФАЙЛУ (08.11).
   Первая редакция записала его как `.td-player-name:has(.td-team-p)` —
   0,0,2,0, ровно столько же, сколько у запрета строкой выше, и побеждал
   тот, кто НИЖЕ В ФАЙЛЕ. Замер в браузере: 31 обрезанное имя при нуле
   ожидаемых. Та же беда, что с подписью лиги, пойманная тем же способом —
   не чтением css, а замером на экране.
   РАВНАЯ СПЕЦИФИЧНОСТЬ РЕШАЕТСЯ ПОРЯДКОМ, И ЭТО НЕ РЕШЕНИЕ: правило
   стоит здесь, на ступень выше (0,0,3,0), и от порядка не зависит.
   Многоточие верно для одиночки, у которого имя одно, и неверно для
   пары, где обрезалось ВТОРОЕ ИМЯ ЦЕЛИКОМ. */
.td-bracket .td-player-name:has(.td-team-p) {
    white-space: normal;
    text-overflow: clip;
}
.td-connector-column {
    width: 18px;
    flex: 0 0 18px;
}
}   /* /min-width: 1024px */

/* ---- Сетка на телефоне ----
   Ужимать её некуда, поэтому листаем вбок: блоки нормальной ширины, имена
   целиком, круги не наезжают друг на друга. */
@media (max-width: 992px) {
    .td-bracket-scroll {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        max-width: 100%;
    }

    .td-bracket-round {
        min-width: 200px;
        flex: 0 0 200px;
    }

    .td-bracket .td-match {
        width: 100%;
    }

    /* Имя не обрезаем: на телефоне пара — это два человека, и «Владисла…»
       никому ничего не говорит */
    .td-bracket .td-player-name {
        white-space: normal;
        overflow-wrap: break-word;
    }
}

/* Рамка при нажатии на вкладку была системной, синей — чужой цвет посреди
   чёрно-лаймовой страницы. Оставляем её только для тех, кто ходит с
   клавиатуры: им она нужна, чтобы видеть, где находишься. */
.td-tab:focus { outline: none; }
.td-tab:focus-visible {
    outline: var(--bw-thick) solid var(--accent);
    outline-offset: 2px;
    border-radius: 6px;
}

/* ---- Расписание запусков ----
   Тот же порядок, что у ведущего в админке: одна очередь, от раннего запуска
   к позднему. Корт известен только у первых — дальше прочерк, его поставят по
   ходу дня. Здесь только смотрят, поэтому ни полей, ни кнопок. */
/* Расписание идёт по той же ширине, что таблицы групп: 1400 и по левому
   краю. Раньше оно было в колонке 1000 пикселей по центру — и на широком
   экране висело отдельным островом, сдвинутым вправо от всего остального. */
.td-sched-court {
    max-width: var(--container-max);
    margin: 0 0 var(--space-xl);
}

.td-sched-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--fs-sm);
}

.td-sched-court-cell {
    white-space: nowrap;
    color: var(--text-secondary);
}

.td-sched-table th {
    text-align: left;
    font-size: var(--fs-2xs);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-muted);
    font-weight: 600;
    padding: 8px 10px;
    border-bottom: var(--bw-hair) solid var(--border-subtle);
}

.td-sched-table td {
    padding: 10px;
    border-bottom: var(--bw-hair) solid var(--border-subtle);
    color: var(--text-primary);
    vertical-align: middle;
}

.td-sched-num {
    width: 36px;
    color: var(--text-muted);
    text-align: center;
}

.td-sched-time {
    width: 64px;
    font-weight: 700;
}

.td-sched-round {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 4px;
    background: var(--alpha-white-08);
    color: var(--text-secondary);
    font-size: var(--fs-xs);
    white-space: nowrap;
}

.td-sched-p {
    /* Пара пишется целиком, поэтому строка переносится, а не обрезается */
    line-height: 1.3;
}

.td-sched-vs {
    width: 34px;
    text-align: center;
    color: var(--text-muted);
    font-size: var(--fs-xs);
}

.td-sched-status {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 4px;
    font-size: var(--fs-xs);
    font-weight: 600;
    white-space: nowrap;
}

.td-sched-done { background: rgba(52, 199, 89, 0.12); color: var(--success); }
.td-sched-live { background: rgba(255, 179, 0, 0.15); color: #ffb300; }
.td-sched-soon { background: var(--alpha-white-08); color: var(--text-secondary); }

.td-sched-row-live td { background: rgba(255, 179, 0, 0.04); }

/* ПЛАНШЕТ И УЖЕ — 16.10, кусок 75: таблица расписания вписывается в
   экран, и планшету это нужно так же, как телефону. */
@media (max-width: 992px) {
    .td-sched-table { font-size: var(--fs-sm); }
    .td-sched-table td, .td-sched-table th { padding: 8px 6px; }
    .td-sched-vs { width: 24px; }
}

/* ============================================================
   Телефон и планшет
   ============================================================
   Правила живут здесь, рядом со своим компонентом: файл подключается
   после style.css, и правка выигрывает по порядку — без подпорок.

   ГРАНИЦА 768 ЗАМЕНЕНА НА ДОМАШНЮЮ 992 — 16.10, кусок 75. Всё, что
   ниже, — сжатие обложки, вкладок и САМОЙ СЕТКИ под узкий экран. Сетка
   и без того листается вбок от 1023 и ниже; её блоки, подписи и
   соединители ужимаются здесь, и планшету это нужно ровно так же, как
   телефону: на 844 обложка занимала 645 пикселей из 844. */

@media (max-width: 992px) {

    /* Обложка занимала 645 пикселей из 844 — три четверти экрана, хотя
       содержимого в ней на 388. Остальное давали отступы: 80 сверху,
       40 снизу и по 40 между блоками.

       Сверху оставляем 74 — под шапку, которая висит поверх страницы */
    .td-hero-content {
        padding: 74px 0 18px;
    }

    .td-back-link { margin-bottom: 14px; }
    .td-hero-badges { margin-bottom: 10px; }

    /* Кегль и межстрочный пришли из крутилки обложки — здесь остаётся
       только отступ, и он встал на ступень (было 10) */
    .td-hero-content h1 {
        margin-bottom: var(--space-3);
    }

    .td-hero-meta { margin-bottom: 14px; }

    /* Возврат к категории остаётся на месте — прижат слева и не уезжает.
       Чтобы он занимал меньше из 390 пикселей, оставляем стрелку и короткую
       подпись, а ленте вкладок отдаём всё остальное */
    .td-tabs-back {
        flex: 0 0 auto;
        font-size: var(--fs-xs);
        gap: 4px;
        padding-right: 10px;
        margin-right: 2px;
        border-right: var(--bw-hair) solid var(--alpha-white-12);
    }

    .td-tabs-back span {
        max-width: 62px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /* Лента вкладок прокручивается вбок, а активная сама подъезжает в
       середину — за это отвечает tournament-detail.js */
    .td-tabs {
        flex: 1 1 auto;
        min-width: 0;
        gap: 14px;
        scrollbar-width: none;
        scroll-behavior: smooth;
    }

    .td-tabs::-webkit-scrollbar { display: none; }

    /* ОДНО ОБЪЯВЛЕНИЕ НА УЗКИЕ ВИДЫ. Было ЧЕТЫРЕ: 12/16 в медиа 768, 10/12 в
       480, 8/10 в 375 и 10/2 здесь же — и выигрывало последнее. Ни одно из
       восьми чисел не стоит на шкале отступов 8 · 12 · 16 · 24 · 32 · 40.
       Горизонтальный отступ вырос с 2 до 8: лента вкладок прокручивается
       вбок, а активная сама подъезжает в середину, поэтому ширина полосы
       здесь ничего не ломает. */
    .td-tab {
        flex: 0 0 auto;
        font-size: var(--fs-xs);
        padding: var(--space-3) var(--space-2);
    }

    /* ─── Сетка плей-офф на телефоне ───
       Клетки были рассчитаны на широкий экран: колонка 200 пикселей,
       строка игрока 32, шрифт 12.8. В 390 помещались полтора круга,
       остальное — прокруткой вбок. Ужимаем, чтобы за раз читалось два
       круга целиком */
    /* Ширину колонки держит flex-basis, одного width мало.

       192, А НЕ 150 — ЗАМЕР 10.11 НА 390, ВСЕ ВАРИАНТЫ ОДНИМ ПРОГОНОМ:
         150 -> обрезано 28 фамилий, за раз видно 2.39 круга
         176 -> обрезано  6
         192 -> обрезано  0, видно 1.86 круга
         200 -> обрезано  0, видно 1.79
       192 — ПЕРВАЯ ступень, где не обрезана ни одна фамилия; дальше
       платим шириной без выигрыша. Ступени 8 × 24 и 8 × 23.

       ЦЕНА НАЗВАНА: за раз читается не два круга, а один и восемь
       десятых. Прежний комментарий ужимал колонку ради двух кругов —
       но сетка без фамилий не сетка, а правило «имя не обрезаем» в этом
       же файле (:2425) и в приложении (app.css:5085, кусок 216) уже
       принято. Правило переносится без нового разбора. */
    .td-bracket-round {
        flex: 0 0 192px;
        width: 192px;
        min-width: 192px;
    }

    /* А вот заголовку круга flex-basis задавать нельзя: внутри колонки
       он означает высоту, и «РАУНД 1» превращался в чёрный блок на 150
       пикселей. Заголовку — только ширина */
    .td-round-title {
        flex: 0 0 auto;
        width: 192px;
        min-width: 192px;
    }

    /* Клетки одной высоты — иначе соединительные линии разъезжаются.
       Пара занимает две строки, одиночка одну; чтобы высота у всех была
       общей, имя ограничиваем двумя строками и обрезаем многоточием.
       До этого длинные пары вытягивали карточку на три строки, соседние
       оставались низкими, и линии проходили прямо по именам */
    .td-match-player {
        padding: 4px 7px;
        font-size: var(--fs-2xs);
        /* Высота задана жёстко, а не минимумом: у одиночного имени строка
           выходила 30 пикселей, у пары — 37, и клетки всё равно разъезжались */
        height: 38px;
        min-height: 38px;
        align-items: center;
    }

    .td-match-player .td-player-name {
        line-height: 1.1;
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        overflow: hidden;
    }

    .td-round-title {
        font-size: var(--fs-2xs);
        margin-bottom: 6px;
    }

    .td-bracket-match { margin-bottom: 8px; }

                                                                            
                                                                        
                         
                                                                         
                                                                           
                                                    
    .td-match{
        box-sizing: border-box;
        width: 184px;
        min-width: 184px;
        max-width: 184px;
    }

    .td-connector-column {
        width: 18px;
        min-width: 18px;
        flex: 0 0 18px;
    }

    /* Дополнительные матчи — по две карточки в ряд.
       В разметке у сетки минимум колонки 220 пикселей, поэтому на 390
       помещалась одна, и два матча занимали пол-экрана */
    .td-ig-grid {
        grid-template-columns: 1fr 1fr !important;
        gap: 8px !important;
    }

    .td-ig-match{
        font-size: var(--fs-2xs);
    }

    .td-ig-match > div:first-child {
        padding: 4px 8px !important;
        font-size: var(--fs-2xs);
    }

    .td-ig-match .td-match-player,
    .td-ig-match > div:not(:first-child) {
        padding: 5px 8px !important;
    }

    /* Текст описания турнира — 0.88rem, как имена в списке участников.
       Был 16 пикселей и выглядел заметно крупнее соседних разделов */
    #descriptionContent,
    #descriptionContent p,
    #descriptionContent li {
        font-size: var(--fs-sm);
        line-height: 1.5;
    }

    /* Полоса прокрутки у таблиц групп была светлой, системной — в тёмной
       теме она выглядела чужой заплаткой. Делаем тонкой и приглушённой */
    /* Между последней строкой расписания и спонсорами зияло 120 пикселей */
    .td-section:has(#scheduleContainer),
    .td-section:has(#participantsGrid) {
        padding-bottom: 14px;
    }

    .td-sched-court { margin-bottom: 0; }

    /* ─── Участники и лист ожидания ───
       Таблица была 620 пикселей при 390 доступных. Убираем номер строки и
       время подачи, оставляем фото, имя, категорию и дату. Фото остаётся
       в раскладке заранее: когда появятся настоящие снимки, менять ничего
       не придётся */
    .td-reg-table td:first-child,
    .td-reg-table thead th:first-child,
    .td-reg-table td:last-child,
    .td-reg-table thead th:last-child {
        display: none;
    }

    /* Обёртки ограничиваем по ширине экрана: без этого таблица растягивала
       их до 620 пикселей и просто уходила за край — ровно как было с
       таблицами групп */
    .td-reg-columns,
    .td-reg-columns > div,
    .td-reg-table-wrap {
        min-width: 0;
        max-width: 100%;
    }

    .td-reg-table {
        width: 100%;
        /* Снимаем min-width: 620 из основных правил — из-за него таблица
           держала свою ширину и уходила за край экрана */
        min-width: 0;
        table-layout: fixed;
    }

    /* Основной текст на телефоне — 0.82rem (см. стандарт размеров).
       Размер задан ячейкам, а не таблице: правило на ячейках сильнее */
    .td-reg-table th,
    .td-reg-table td {
        font-size: var(--fs-sm);
    }

    /* NTRP и дата — подписи: 0.72rem по стандарту */
    .td-reg-table td:nth-child(4),
    .td-reg-table td:nth-child(5) {
        font-size: var(--fs-xs);
    }

    /* Шапка на месте — без неё непонятно, что за цифры в колонках.
       Раньше она налезала сама на себя из-за длинного слова «Категория»;
       теперь там короткое «NTRP», и подписи помещаются */
    .td-reg-table thead th {
        font-size: var(--fs-2xs);
        letter-spacing: 0.02em;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        padding: 4px 5px;
    }

    .td-reg-table td,
    .td-reg-table th {
        padding: 6px 5px;
    }

    .td-reg-photo {
        width: 30px;
        height: 30px;
        min-width: 30px;
    }

    /* Имя пары занимает две строки — это его нормальный вид, а не перенос */
    .td-reg-table td:nth-child(3) {
        line-height: 1.3;
        word-break: break-word;
    }

    /* При fixed ширину задают заголовки */
    .td-reg-table thead th:nth-child(2) { width: 38px; }
    .td-reg-table thead th:nth-child(4) { width: 62px; }
    .td-reg-table thead th:nth-child(5) { width: 54px; }

    /* NTRP и дата: размер задан выше, здесь только ширина и цвет.
       Раньше размер повторялся и тут — и держал 9.6 пикселя */
    .td-reg-table td:nth-child(4) {
        width: 62px;
        color: var(--text-secondary);
    }

    .td-reg-table td:nth-child(5) {
        width: 54px;
        white-space: nowrap;
        color: var(--text-muted);
    }

    .td-participants-subtitle {
        /* Кегль приходит от правила подзаголовка раздела: 16 на ≤768. */
        margin-bottom: 8px;
    }

    .td-reg-columns {
        display: block;
    }

    .td-reg-columns > div + div {
        margin-top: 18px;
    }

    /* Между последней группой и «Расписанием запусков» зияло 64 пикселя:
       нижний отступ раздела плюс промежуток сетки групп */
    .td-section:has(.td-groups-grid) {
        padding-bottom: 14px;
    }

    .td-groups-grid {
        gap: 14px;
    }

    /* Таблицы групп прокручиваются вбок.
       Обёртка с прокруткой сама была шире экрана — 563 пикселя при 390, —
       поэтому прокручивать было нечего: таблица просто уходила за край */
    .td-groups-grid { min-width: 0; }

    .td-groups-grid > div,
    .td-groups-grid > div > div {
        min-width: 0;
        max-width: 100%;
    }

    .td-group-table {
        min-width: 480px;
    }

    /* ─── Расписание запусков вписано в экран ───
       Таблица была 500 пикселей при 390 доступных: номер, время, раунд,
       две пары, корт и состояние. Убираем то, что можно узнать иначе, и
       сокращаем подписи — прокрутка не нужна, весь игровой день виден */

    /* Номер строки и состояние: номер ничего не добавляет, состояние видно
       по счёту и подсветке живого матча */
    .td-sched-table .td-sched-num,
    .td-sched-table thead th:first-child,
    .td-sched-table .td-sched-status,
    .td-sched-table thead th:last-child,
    .td-sched-table td:last-child {
        display: none;
    }

    /* «Группа A» → «A», «Корт 1» → «1»: полные подписи в узкой колонке
       переносились по слогам */
    .td-sched-round,
    .td-sched-court-cell {
        font-size: 0;
    }

    .td-sched-round::after,
    .td-sched-court-cell::after {
        content: attr(data-short);
        font-size: var(--fs-2xs);
    }

    /* Без table-layout: fixed — заголовок «Игра» объединяет три ячейки,
       и нумерация столбцов с ним не совпадает. Ширины задаём ячейкам,
       остаток забирают имена */
    .td-sched-table {
        width: 100%;
    }

    .td-sched-table .td-sched-time {
        width: 44px;
        font-size: var(--fs-2xs);
        white-space: nowrap;
    }

    .td-sched-table .td-sched-vs {
        width: 16px;
        font-size: var(--fs-2xs);
        text-align: center;
    }

    .td-sched-table .td-sched-court-cell {
        width: 22px;
        text-align: center;
    }

    .td-sched-table td:nth-child(3) { width: 24px; text-align: center; }

    .td-sched-table .td-sched-p {
        font-size: var(--fs-2xs);
        line-height: 1.1;
        word-break: break-word;
    }

    .td-sched-table td,
    .td-sched-table th {
        padding: 6px 4px;
    }

    /* Карточка места занимала 477 пикселей — больше половины экрана, хотя
       в ней всего название, адрес и две ссылки. Виноваты отступы в 40
       пикселей по кругу и крупное название */
    /* У карточки промежуток между колонками 40 пикселей. На широком экране
       он разводит текст и карту, а на телефоне колонка одна — и он падает
       вниз пустой полосой между кнопками и картой */
    .td-venue-card {
        gap: 10px;
    }

    .td-venue-info {
        padding: 12px 14px 0;
        /* Свой промежуток между строками в 16 пикселей — поверх него ещё
           и отступы у самих строк. На четыре строки текста выходило
           больше пустоты, чем букв */
        gap: 2px;
    }

    .td-venue-name {
        font-size: var(--fs-base);
        line-height: 1.3;
        margin-bottom: 4px;
    }

    .td-venue-address {
        font-size: var(--fs-xs);
        margin-bottom: 0;
    }

    /* Между адресом и ссылками было 26 пикселей, между названием и
       адресом — 16: на четыре строки текста приходилось больше отступов,
       чем самого текста */
    .td-venue-links { margin-top: 8px; gap: 8px; }

    .td-venue-links a {
        padding: 6px 12px;
        font-size: var(--fs-xs);
    }

    /* Карта на телефоне нужна для ориентира, а не для изучения района.
       Высота держалась на min-height, поэтому одной height было мало */
    .td-venue-map,
    .td-venue-map iframe {
        height: 150px;
        min-height: 150px;
    }

                                                                         
                                                                         
                                                                          
    .td-section-header h2,
    .td-section h2{
        /* БЫЛО `clamp(1.15rem, 5vw, 1.32rem)` — плавающий кегль, который на
           390 давал 20: такого числа на шкале 11 · 12 · 14 · 16 · 18 · 21 ·
           26 нет. Ступень `--fs-lg` это 21, то есть верх прежнего clamp:
           заголовок не вырос и не сжался, он просто перестал гулять.
           Межстрочный был 1.2 — тоже мимо лестницы; стал той же ступенью,
           что и на широких видах. ОДИН УРОВЕНЬ — ОДИН МЕЖСТРОЧНЫЙ. */
        font-size: var(--fs-lg);
        font-weight: 700;
        letter-spacing: -0.03em;
        line-height: var(--lh-tight);
    }

    /* ПАРА, КОТОРАЯ ЧИТАЕТСЯ КАК ПАРА, ЕДЕТ ОДНОЙ ГРАНИЦЕЙ. Заголовок
       раздела на узких опустился с 26 до 21, а подзаголовок блока остался
       на 21 — и пара схлопнулась: «Плей-офф» стал ростом с «Сетка». На
       широких отношение держится (26 против 21, одна ступень), на узких
       его не было вовсе. Подзаголовок опускается на ту же одну ступень:
       21 -> 18. Поймала проба `95-poryadok-blokov` — девять падений, по
       три на каждый узкий вид. */
    .td-section-header h3,
    .td-section h3 {
        font-size: var(--fs-md);
    }

                                                                    
    .td-section-header h3,
    .td-section h3,

    /* Показатели по центру обложки: прижатые к левому краю, они спорили
       с названием турнира, которое тоже идёт слева */
    .td-hero-stats {
        justify-content: center;
        text-align: center;
    }

    /* Цифры под названием — 25 пикселей по общему стандарту обложек. Но
       стандарт рассчитан на две колонки в два ряда, а здесь три показателя
       в одну строку, и один из них — длинная пара «2 500 / 3 000». Рядом с
       названием турнира они кричали громче него, поэтому мельче */
    .td-hero-stats { gap: 10px; }

    /* Счётчик до старта: цифры в трёх коробках по 60 пикселей занимали
       125 пикселей высоты — столько же, сколько название турнира */
    .td-countdown { margin-top: 12px; }
    .td-cd-title { margin-bottom: 6px; font-size: var(--fs-2xs); letter-spacing: 1.5px; }
    .td-cd-boxes { gap: 6px; }

    .td-cd-box {
        padding: 6px 10px;
        min-width: 48px;
        border-radius: 8px;
    }
}

/* ЭКРАН ДЕЛИТСЯ ВЫСОТОЙ И ПОВОРОТОМ, А НЕ НОВОЙ ШИРИНОЙ.
   Замер 06.10 на 844 × 390: обложка 465 при экране 390 — первый экран
   целиком съеден шапкой турнира, а пришли за сеткой. Порог по высоте, а не
   по ширине: беда ровно в том, что экран низкий. */
@media (max-height: 500px) and (orientation: landscape) {
    .td-hero { min-height: 0; }
    /* Высоту держит не поле, а ВОЗДУХ МЕЖДУ СТРОКАМИ шапки: замер дал
       содержимое 352 при сумме строк 208 — 144 ушли в зазоры. Сжимаем
       зазор, а не выбрасываем строки: на низком экране нужен тот же
       турнир, просто плотнее. */
    .td-hero-content { padding-top: var(--space-4); padding-bottom: var(--space-4); gap: var(--space-2); }
    .td-hero-stats { gap: var(--space-3); }
}

/* ═══ ЗАПИСЬ НА ТУРНИР ═══════════════════════════════════════════════
   ИМЯ `td-zapis-`, А НЕ `td-reg-`: второе уже занято ДРУГИМ понятием —
   таблицей заявок участников (`.td-reg-table`, `.td-reg-columns`,
   `.td-reg-photo`, 43 места в этом файле). Первая редакция села в чужое
   пространство имён, и поймал это прибор: он насчитал два медиазапроса
   на «мои» классы, которых я не писал. ИМЯ, УЖЕ ЗАНЯТОЕ ДРУГИМ ПОНЯТИЕМ,
   НЕ ПЕРЕИСПОЛЬЗУЕТСЯ.
   Ветка C, круг 2, 16.10. Блок записи рисовался ДВАДЦАТЬЮ инлайнами в
   `js/tournament-detail.js`, и классы `.td-registration-area`,
   `.td-register-btn`, `.td-reg-status` стояли в разметке, не имея в css
   НИ ОДНОГО объявления: они выглядели точкой входа, а не красили ничего.

   ПОЧЕМУ ЭТО БЫЛО НЕ ТОЛЬКО НЕОПРЯТНО. Плашки отказа и внимания стояли
   на прозрачном тинте 10 % — а блок живёт на ОБЛОЖКЕ турнира под
   градиентом, то есть фон у него разный от турнира к турниру. Замер
   16.10: на тёмном низу градиента отказ давал 5.18, на светлой обложке
   под чёрным 85 % — 3.88, в середине градиента — 1.15. Текст пропадал.
   ПРОЗРАЧНАЯ КРАСКА СМЕШИВАЕТСЯ С ФОНОМ — ровно вывод 12.10 из кабинета,
   где ту же беду нашли на трёх подложках. Непрозрачная ступень роли даёт
   5.16 и 7.96 НЕЗАВИСИМО от обложки.

   Числа — ступени: отступы 4 · 8 · 12 · 16 · 24, цель нажатия 44. */

.td-zapis-area {
    margin-top: var(--space-md);
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-3);
}

/* Пояснение под статусом: мера чтения своя, а не на всю ширину обложки */
.td-zapis-note {
    flex-basis: 100%;
    /* 520 — ширина формы средней длины (`--modal-w-md`), та же мера
       чтения: строка не растягивается на всю обложку */
    max-width: var(--modal-w-md);
    color: var(--text-muted);
    font-size: var(--fs-sm);
    line-height: 1.5;
}

/* Строка под заголовком плашки — причина отказа, требуемый NTRP */
.td-zapis-note-pod {
    color: var(--text-muted);
    font-size: var(--fs-sm);
    margin-top: var(--space-1);
}

/* ---- Плашка: одно понятие, две роли ---- */
.td-zapis-plashka {
    padding: var(--space-3) var(--space-6);
    border-radius: var(--radius-sm);
    border: var(--bw-hair) solid transparent;
}

.td-zapis-plashka-title {
    font-weight: 500;
    margin-bottom: var(--space-1);
}

/* Заголовок без строки под ним не несёт нижнего воздуха */
.td-zapis-plashka-title:last-child { margin-bottom: 0; }

.td-zapis-plashka-otkaz {
    background: var(--danger-surface);
    border-color: var(--danger-subtle);
}
.td-zapis-plashka-otkaz .td-zapis-plashka-title { color: var(--danger); }

.td-zapis-plashka-vnimanie {
    background: var(--warning-surface);
    border-color: var(--warning-subtle);
}
.td-zapis-plashka-vnimanie .td-zapis-plashka-title { color: var(--warning); }

.td-zapis-plashka-link {
    color: var(--accent);
    font-size: var(--fs-sm);
}

/* ---- Кнопки ---- */
.td-zapis-btns {
    display: flex;
    gap: var(--space-3);
    flex-wrap: wrap;
}

/* ЦЕЛЬ НАЖАТИЯ 44 — обе кнопки её не держали: контурная давала 34 по
   высоте, главная 45 по случаю, а не по решению. */
.td-register-btn,
.td-zapis-btn-kontur {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* ВЫСОТА — ТОКЕН, А НЕ ЧИСЛО. Я написал было `44px`, решив, что
       токена нет; он есть и стоит строкой выше в этом же файле у
       `.td-tab` — `--btn-h-md`. Искать ответ в продукте ДО того, как
       писать своё. */
    min-height: var(--btn-h-md);
    border-radius: var(--radius-sm);
    /* МЕЖСТРОЧНЫЙ У КНОПОК ОСТАЁТСЯ `normal` НАМЕРЕННО: высота задана
       токеном `--btn-h-md`, текст центрируется флексом, и отношение
       межстрочного тут ничего не держит. Замер 16.10 на 1280, 768 и 375:
       высота 44 на всех трёх. Лестница межстрочных (заход B4) работает
       там, где строк больше одной.
       ШРИФТ НЕ ПОВТОРЯЕТСЯ ПОШТУЧНО: общее правило живёт в
       `css/tokens.css` и доезжает всем управлениям (заход B5, 79 копий
       снято). Своя строка здесь была бы восьмидесятой. */
    cursor: pointer;
}

.td-register-btn {
    padding: var(--space-3) var(--space-6);
    border: none;
    background: var(--accent);
    color: var(--accent-on);
    font-weight: 600;
    font-size: var(--fs-base);
}

.td-zapis-btn-kontur {
    padding: var(--space-2) var(--space-4);
    border: var(--bw-hair) solid var(--border);
    background: transparent;
    color: var(--text-muted);
    font-weight: 500;
    font-size: var(--fs-sm);
}

.td-zapis-btn-kontur-accent {
    border-color: var(--accent);
    color: var(--accent);
}

/* Статус «зарегистрирован как партнёр» — не кнопка, нажимать нечего */
.td-reg-status {
    display: inline-block;
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-sm);
    background: var(--accent-subtle);
    color: var(--accent);
    font-weight: 500;
}

/* ═══ ТАБЛИЦА ГРУПП: ЯЧЕЙКИ ═══════════════════════════════════════════
   Ветка C, круг 3, 17.10. Каркас таблицы (`.td-group-table`) в css был с
   самого начала, а ЯЧЕЙКИ красились инлайном из js — 23 раза
   `text-align:center`, 11 раз `white-space:nowrap`, ширины колонок
   числами. И красились ДВАЖДЫ: обычные группы и группы лиги собираются
   двумя разными кусками кода (`группы` и `группыЛиг`, сходство 41.7 %),
   каждый со своей копией инлайнов.

   ПРОЗРАЧНОСТИ ПОСТАВЛЕНЫ НА СТУПЕНИ. Диагональ таблицы стояла на
   `rgba(255,255,255,0.03)`, граница карточки доп. матча — на 0.06;
   лестница белых прозрачностей знает 0.04 · 0.08 · 0.12
   (`--surface-glass`, `--border-subtle`, `--alpha-white-12`). Соседняя
   строка того же файла уже брала `--border-subtle` — то есть одно
   понятие держалось двух значений на расстоянии одной строки. */

/* Обёртка: таблица шире экрана едет пальцем, а не ломает страницу */
.td-gt-scroll { overflow-x: auto; }

/* Блок одной группы в столбце групп */
.td-gt-block { margin-bottom: var(--space-md); }

/* Колонки ПО НАЗНАЧЕНИЮ, а не по номеру: номер колонки зависит от числа
   игроков в группе, назначение — нет */
.td-group-table .td-gt-opp { text-align: center; min-width: 48px; white-space: nowrap; }
.td-group-table .td-gt-wins { text-align: center; width: 32px; }
.td-group-table .td-gt-games { text-align: center; width: 44px; white-space: nowrap; }
.td-group-table .td-gt-place { text-align: center; width: 40px; }

/* Ячейки строки */
.td-group-table .td-gt-num { text-align: center; font-weight: 600; }
.td-group-table .td-gt-name { white-space: nowrap; }
.td-group-table .td-gt-score { text-align: center; white-space: nowrap; }

/* Сам против себя — клетка, которой не бывает */
.td-group-table /* 28.10, КУСОК 216: строка прошедшего дальше. Была инлайном в коде
   (`style="background:var(--alpha-lime-06)"`) — вид в разметке, а не в
   стилях; при переезде рисунка в общий дом стала классом. */
.td-gt-qualified { background: var(--alpha-lime-06); }

.td-gt-self {
    text-align: center;
    background: var(--surface-glass);
    color: var(--text-muted);
}

/* Матч не сыгран */
.td-group-table .td-gt-empty { text-align: center; color: var(--text-muted); }

/* Прошёл дальше — подсветка строки */
.td-group-table .td-gt-qual { background: var(--alpha-lime-06); }

/* Номер посева у имени */
.td-gt-seed { color: var(--accent); font-size: var(--fs-2xs); }

/* Счёт встречи в клетке: выиграл — акцентом, проиграл — тише.
   Состояние живёт КЛАССОМ, а не веткой в строке разметки: до правки
   краска победителя писалась тернарником прямо в `style=`, по два раза
   на каждую из двух таблиц. */
.td-group-table .td-gt-score { font-size: var(--fs-xs); color: var(--text-secondary); }
.td-group-table .td-gt-score.td-gt-win { color: var(--accent); font-weight: 600; }

/* Итоговые колонки строки */
.td-group-table .td-gt-total { text-align: center; font-weight: 600; }
.td-group-table .td-gt-ratio {
    text-align: center;
    font-size: var(--fs-xs);
    color: var(--text-secondary);
    white-space: nowrap;
}
.td-group-table .td-gt-pos { text-align: center; font-weight: 700; white-space: nowrap; }
.td-group-table .td-gt-pos.td-gt-win { color: var(--accent); }

/* Доля геймов второй строкой — тише основного числа. То же значение и
   то же понятие, что у менеджера: `.ad-grp-dolya` в `css/admin.css:1043`.
   Я было написал `var(--alpha-text-70, 0.7)` — такого токена нет, и с
   запасным значением вид получился бы верный при вранье в имени. Ровно
   то, подо что 16.10 заведено правило «каждая переменная где-то
   объявлена»; поймал себя до прогона, но поймало бы и оно. */
.td-gt-ratio-pct { opacity: 0.7; }

/* ═══ КАРТОЧКА ДОП. МАТЧА ═══════════════════════════════════════════════
   Ветка C, круг 3, 17.10. Карточка рисовалась восемью инлайнами, а её
   внутренние блоки не имели имён вовсе — поэтому css в медиа 992 цеплялся
   за СТРУКТУРУ (`> div:first-child`, `> div:not(:first-child)`) и держался
   на шести `!important`: структурный селектор слабее инлайна, и иначе он
   не выигрывал. Имена дают правилам ту же силу без подпорок. */

.td-ig-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: var(--space-3);
    margin-bottom: var(--space-8);
}

.td-ig-match {
    background: var(--bg-card);
    border: var(--bw-hair) solid var(--border);
    border-radius: var(--radius-sm);
    overflow: hidden;
}

/* Шапка карточки — номер доп. матча */
.td-ig-head {
    font-size: var(--fs-2xs);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-muted);
    padding: var(--space-2) var(--space-3);
    background: var(--surface-glass);
    border-bottom: var(--bw-hair) solid var(--border-subtle);
}

/* Строка игрока */
.td-ig-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
}
.td-ig-row + .td-ig-row { border-top: var(--bw-hair) solid var(--border-subtle); }
.td-ig-row.td-ig-won { background: var(--alpha-lime-06); }

/* ДЛИННОЕ ИМЯ НЕ ВЫТАЛКИВАЕТ СЧЁТ. Найдено ГЛАЗАМИ на стенде 17.10, а
   не числами: у «Сыдыков Эрмек-Бакытбекович» имя упиралось в счёт вплотную,
   а у «Токтосунов Жаныбек-Асанович» счёт 6:7 5:7 разрывался на две строки.
   Имя сжимается и переносится (`min-width: 0` — иначе flex не даёт
   элементу стать уже содержимого), счёт не сжимается и не переносится. */
.td-ig-name {
    font-size: var(--fs-sm);
    color: var(--text-primary);
    min-width: 0;
    overflow-wrap: anywhere;
}
.td-ig-score {
    font-size: var(--fs-xs);
    font-weight: 600;
    color: var(--text-secondary);
    flex-shrink: 0;
    white-space: nowrap;
    margin-left: var(--space-2);
}
.td-ig-won .td-ig-name { color: var(--accent); font-weight: 700; }
.td-ig-won .td-ig-score { color: var(--accent); }

/* Распорка на месте заголовка круга: место держим, текст не показываем */
.td-round-title-spacer { visibility: hidden; }

/* Матч за место под сеткой. Правила живут на САМОМ `.td-place-match`:
   отдельный класс-обёртка был лишней сущностью — элемент тот же, и из-за
   него в разметке оказалось два атрибута `class`. */
.td-place-match { margin-top: var(--space-3); max-width: 200px; }

/* Распорка между блоком доп. матчей и сеткой. Пустой элемент ради
   отступа — не лучший приём; отступ правильнее отдать соседу, но это уже
   раскладка, а не оформление. Имя дано, находка записана. */
.td-razdelitel-blokov { margin-bottom: var(--space-8); }

/* Пусто — «нет участников в листе ожидания» */
.td-no-results { padding: var(--space-md) 0; }
