/* ============================================================
   DantesW — базовые стили и дизайн-токены
   Один источник правды для цветов, типографики и общих компонентов.
   ============================================================ */

/* ---------- Токены: тёмная тема (по умолчанию) ---------- */
:root {
    --bg:            #0a0a2a;
    --bg-2:          #1a1a3a;
    --surface:       #1a1a3a;
    --surface-2:     #2d2b55;
    --surface-3:     rgba(10, 10, 30, 0.62);
    /* На ступень светлее третьей поверхности. Нужна там, где плашка лежит
       поверх другой плашки или поверх фона профиля и должна от неё
       отделяться (форма «Добавить по юзернейму», строки заявок в друзья). */
    --surface-4:     #4d49a3;
    --surface-soft:  rgba(74, 71, 128, 0.30);
    --border:        #4a4780;
    --border-soft:   rgba(138, 132, 255, 0.18);

    /* Тонкая полупрозрачная полоса прокрутки — см. правило на *::-webkit-scrollbar-thumb выше. */
    --scroll-thumb:       rgba(138, 132, 255, 0.28);
    --scroll-thumb-hover: rgba(138, 132, 255, 0.45);

    --text:          #e0e0ff;
    --text-muted:    #b8b8ff;
    --text-dim:      #a0a0ff;

    --accent:        #6c63ff;
    --accent-2:      #8a84ff;
    --accent-soft:   rgba(108, 99, 255, 0.12);
    --accent-ring:   rgba(108, 99, 255, 0.30);
    --on-accent:     #ffffff;

    /* Кнопки самой шапки (.header .nav-link и соседние) намеренно не
       зависят от --accent/темы (см. комментарий у этих правил ниже) —
       поэтому у них свой набор токенов. Меняются только выбором палитры
       сайта (см. :root[data-palette="…"] ниже), не темой. */
    --header-pill-bg:          #524dbb;
    --header-pill-text:        #d8d6ff;
    --header-pill-hover-bg:    rgba(108, 99, 255, 0.16);
    --header-pill-hover-text:  #ffffff;
    --header-pill-active-grad: linear-gradient(90deg, #6c63ff 0%, #8a84ff 100%);
    --header-pill-active-text: #ffffff;

    --success:       #4cd964;
    --warning:       #ffc107;
    --danger:        #ff4757;
    --info:          #5ac8fa;
    --gold:          #ffd700;

    --game-brawl:        #ffc93c;
    --game-standoff2:    #ff7043;
    --game-clash_royale: #4aa3f0;
    --game-mlbb:         #b060d0;
    --game-other:        #8a84ff;

    /* Ниже — цвета игр, убранных из списка (платформа теперь только
       мобильная). Переменные оставлены: на них опираются редкости,
       категории новостей и постов в pages.css. */
    --game-cs2:      #f4634a;
    --game-dota:     #4aa3f0;
    --game-valorant: #66bb6a;
    --game-fortnite: #b060d0;
    --game-lol:      #d4af37;

    --cat-esports:     #ff7a3d;
    --cat-mygame:      #4cd9a0;
    --cat-tournaments: #ffd700;
    --cat-talk:        #5ac8fa;

    --shadow-xs: 0 2px 8px rgba(0, 0, 0, 0.20);
    --shadow-sm: 0 4px 12px rgba(0, 0, 0, 0.25);
    --shadow-md: 0 10px 24px rgba(0, 0, 0, 0.28);
    --shadow-lg: 0 20px 40px rgba(0, 0, 0, 0.35);

    --radius-sm: 8px;
    --radius-md: 12px;
    --radius-lg: 16px;
    --radius-xl: 20px;

    --container: 1200px;

    --font-head: 'Montserrat', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --font-body: 'Roboto', system-ui, -apple-system, 'Segoe UI', sans-serif;

    --transition: 0.25s cubic-bezier(0.4, 0, 0.2, 1);

    /* ---------- Шкала типографики ----------
       Раньше h1-h4 не задавали себе font-size вовсе (только браузерный
       дефолт), а каждая карточка/страница придумывала свой похожий, но
       чуть другой размер (1.2rem тут, 1.05rem там, 0.94rem — описание,
       0.88rem — тоже описание, но в другом месте). Шкала — не замена
       всем этим значениям разом (их в проекте тысячи, менять все —
       отдельная большая работа), а общий словарь: новые и переписанные
       под неё места теперь берут размер отсюда, а не выдумывают заново. */
    --text-xs:   0.75rem;                        /* 12px — мелкие бейджи, подписи */
    --text-sm:   0.875rem;                        /* 14px — подсказки, мета-текст */
    --text-base: 1rem;                            /* 16px — обычный текст */
    --text-md:   1.05rem;                         /* ~17px — чуть крупнее обычного */
    --text-lg:   1.2rem;                          /* заголовок карточки */
    --text-xl:   1.5rem;                          /* подзаголовок секции */
    --text-2xl:  clamp(1.4rem, 3vw, 2.1rem);      /* заголовок секции (было в .section-title) */
    --text-3xl:  clamp(1.75rem, 4.5vw, 2.75rem);  /* крупный заголовок / герой */

    --leading-tight:   1.2;
    --leading-snug:    1.35;
    --leading-normal:  1.5;
    --leading-relaxed: 1.65;

    /* ---------- Шкала отступов ---------- */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 20px;
    --space-6: 24px;
    --space-7: 32px;
    --space-8: 48px;

    /* ---------- Движение ----------
       --transition — основной, уже используется по всему сайту, не
       трогаем. Добавились только более быстрый (мелкие микро-реакции —
       нажатие, переключатель) и более медленный (переливы, свечение). */
    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
    --transition-fast: 0.15s var(--ease-out);
    --transition-slow: 0.45s var(--ease-out);

    /* ---------- Свечение ----------
       Собраны из уже существующих цветовых токенов (--accent-ring,
       --gold), поэтому отдельно переопределять их в светлой теме не
       нужно — при смене темы сами токены внутри пересчитаются. */
    --glow-accent:  0 0 0 1px var(--accent-ring), 0 10px 28px -8px var(--accent-ring);
    --glow-premium: 0 0 0 1px color-mix(in srgb, var(--gold) 45%, transparent),
                    0 10px 30px -8px color-mix(in srgb, var(--gold) 55%, transparent);

    color-scheme: dark;
}

/* ---------- Токены: светлая тема ---------- */
:root[data-theme="light"] {
    --bg:            #f4f5fb;
    --bg-2:          #ffffff;
    --surface:       #ffffff;
    --surface-2:     #ffffff;
    --surface-3:     #f3f2fd;
    /* В светлой теме «светлее» значит белее — иначе плашка утонула бы
       в общем светлом фоне. */
    --surface-4:     #fbfbff;
    --surface-soft:  rgba(108, 99, 255, 0.07);
    --border:        #ddddf0;
    --border-soft:   rgba(108, 99, 255, 0.16);

    --scroll-thumb:       rgba(91, 82, 232, 0.22);
    --scroll-thumb-hover: rgba(91, 82, 232, 0.38);

    --text:          #1e1e38;
    --text-muted:    #5b5b7a;
    --text-dim:      #6f6f92;

    --accent:        #5b52e8;
    --accent-2:      #7b73f5;
    --accent-soft:   rgba(91, 82, 232, 0.09);
    --accent-ring:   rgba(91, 82, 232, 0.25);
    --on-accent:     #ffffff;

    --success:       #1fa64a;
    --warning:       #b07600;
    --danger:        #d92c3c;
    --info:          #0b7fbe;
    --gold:          #b8860b;

    --game-brawl:        #b98600;
    --game-standoff2:    #d9501f;
    --game-clash_royale: #1f7fd0;
    --game-mlbb:         #8b3fb0;
    --game-other:        #5b52e8;

    /* См. комментарий в тёмной теме — переменные убранных игр нужны
       редкостям и категориям. */
    --game-cs2:      #d94b32;
    --game-dota:     #1f7fd0;
    --game-valorant: #2e8b3a;
    --game-fortnite: #8b3fb0;
    --game-lol:      #96780f;

    --cat-esports:     #d9591f;
    --cat-mygame:      #1f9d6e;
    --cat-tournaments: #b8860b;
    --cat-talk:        #0b7fbe;

    --shadow-xs: 0 2px 8px rgba(30, 30, 56, 0.06);
    --shadow-sm: 0 4px 12px rgba(30, 30, 56, 0.08);
    --shadow-md: 0 10px 24px rgba(30, 30, 56, 0.10);
    --shadow-lg: 0 20px 40px rgba(30, 30, 56, 0.14);

    color-scheme: light;
}

/* ---------- Токены: палитра сайта ----------
   Выбор человека в «⋮ → Тема» (js/layout.js initPalette(),
   dp-palette в localStorage, атрибут data-palette на <html> ставится
   в js/theme-init.js ещё до отрисовки — как и с data-theme). Без
   атрибута (по умолчанию) ничего не меняется — сайт остаётся тем же
   фиолетовым, что и был. Каждая палитра переопределяет ровно те же
   токены, что уже расходятся между тёмной/светлой темой выше, плюс
   --header-pill-* (те у кнопок шапки не зависят от темы — см. их
   определение в начале файла — поэтому палитра переопределяет их
   одним блоком на обе темы, без вложенности в data-theme). */
:root[data-palette="aurora"] {
    --header-pill-bg:          #7d428d;
    --header-pill-text:        #ffe0f2;
    --header-pill-hover-bg:    rgba(255, 95, 162, 0.18);
    --header-pill-hover-text:  #ffffff;
    --header-pill-active-grad: linear-gradient(90deg, #ff5fa2 0%, #4fc3ff 100%);
    --header-pill-active-text: #14142b;
}
:root[data-palette="fire"] {
    --header-pill-bg:          #a65417;
    --header-pill-text:        #ffe6cc;
    --header-pill-hover-bg:    rgba(255, 147, 48, 0.18);
    --header-pill-hover-text:  #ffffff;
    --header-pill-active-grad: linear-gradient(90deg, #ff9330 0%, #ffb35c 100%);
    --header-pill-active-text: #14142b;
}
:root[data-palette="mint"] {
    --header-pill-bg:          #218075;
    --header-pill-text:        #d3fbf2;
    --header-pill-hover-bg:    rgba(41, 224, 196, 0.18);
    --header-pill-hover-text:  #ffffff;
    --header-pill-active-grad: linear-gradient(90deg, #29e0c4 0%, #5cf0d8 100%);
    --header-pill-active-text: #14142b;
}

/* Три яркие палитры (аврора/огонь/мятный) заметно светлее классической
   фиолетовой (--accent там уже не тёмно-фиолетовый, а розовый/оранжевый/
   бирюзовый сам по себе) — белый текст поверх них (--on-accent, кнопки,
   активные вкладки, бейджи) читался плохо. На светлой версии темы у этих
   же палитр акцент снова достаточно тёмный (см. блоки
   :root[data-theme="light"][data-palette="…"] ниже) — там белый текст
   возвращаем обратно отдельным правилом. */
:root[data-palette="aurora"],
:root[data-palette="fire"],
:root[data-palette="mint"] {
    --on-accent: #14142b;
}

:root[data-palette="aurora"] {
    --accent:        #ff5fa2;
    --accent-2:      #4fc3ff;
    --accent-soft:   rgba(255, 95, 162, 0.12);
    --accent-ring:   rgba(255, 95, 162, 0.30);
    --border-soft:   rgba(79, 195, 255, 0.18);
    --scroll-thumb:       rgba(79, 195, 255, 0.28);
    --scroll-thumb-hover: rgba(79, 195, 255, 0.45);
}
:root[data-theme="light"][data-palette="aurora"] {
    --accent:        #d6317f;
    --accent-2:      #0e86c9;
    --accent-soft:   rgba(214, 49, 127, 0.09);
    --accent-ring:   rgba(214, 49, 127, 0.25);
    --border-soft:   rgba(14, 134, 201, 0.16);
    --scroll-thumb:       rgba(214, 49, 127, 0.22);
    --scroll-thumb-hover: rgba(214, 49, 127, 0.38);
    --on-accent: #ffffff;
}

:root[data-palette="fire"] {
    --accent:        #ff9330;
    --accent-2:      #ffb35c;
    --accent-soft:   rgba(255, 147, 48, 0.12);
    --accent-ring:   rgba(255, 147, 48, 0.30);
    --border-soft:   rgba(255, 179, 92, 0.18);
    --scroll-thumb:       rgba(255, 179, 92, 0.28);
    --scroll-thumb-hover: rgba(255, 179, 92, 0.45);
}
:root[data-theme="light"][data-palette="fire"] {
    --accent:        #c65a12;
    --accent-2:      #a86a10;
    --accent-soft:   rgba(198, 90, 18, 0.09);
    --accent-ring:   rgba(198, 90, 18, 0.25);
    --border-soft:   rgba(168, 106, 16, 0.16);
    --scroll-thumb:       rgba(198, 90, 18, 0.22);
    --scroll-thumb-hover: rgba(198, 90, 18, 0.38);
    --on-accent: #ffffff;
}

:root[data-palette="mint"] {
    --accent:        #29e0c4;
    --accent-2:      #5cf0d8;
    --accent-soft:   rgba(41, 224, 196, 0.12);
    --accent-ring:   rgba(41, 224, 196, 0.30);
    --border-soft:   rgba(92, 240, 216, 0.18);
    --scroll-thumb:       rgba(92, 240, 216, 0.28);
    --scroll-thumb-hover: rgba(92, 240, 216, 0.45);
}
:root[data-theme="light"][data-palette="mint"] {
    --accent:        #0f9c82;
    --accent-2:      #0e7c9b;
    --accent-soft:   rgba(15, 156, 130, 0.09);
    --accent-ring:   rgba(15, 156, 130, 0.25);
    --border-soft:   rgba(14, 124, 155, 0.16);
    --scroll-thumb:       rgba(15, 156, 130, 0.22);
    --scroll-thumb-hover: rgba(15, 156, 130, 0.38);
    --on-accent: #ffffff;
}

/* Системную светлую тему больше не подхватываем.
   Тёмная — тема сайта по умолчанию (см. js/theme-init.js), и раньше
   человек с системной светлой видел сайт совсем не таким, каким его
   рисовали. Светлая включается переключателем в шапке и живёт в
   :root[data-theme="light"] выше. Заодно это убирает случай, когда
   атрибут говорит одно, а системная настройка перекрашивает в другое. */

/* ---------- Сброс ---------- */
*, *::before, *::after { box-sizing: border-box; }

/* Полоса прокрутки — везде на сайте тонкая и полупрозрачная (переменные
   --scroll-thumb/--scroll-thumb-hover в токенах темы выше), а не системная
   и не скрытая: так видно, что список длиннее экрана, но шкала не спорит
   с интерфейсом. scrollbar-color — для Firefox, псевдоэлементы ниже — для
   Chrome/Safari/Edge. Раньше полосу прятали почти везде — теперь стиль
   один на весь сайт, а прятать её нужно осознанно (см. .captain-links-list
   в pages.css — единственное место, где скролл остаётся без шкалы). */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--scroll-thumb) transparent;
}
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-corner { background: transparent; }
*::-webkit-scrollbar-thumb {
    background: var(--scroll-thumb);
    border-radius: 999px;
    border: 2px solid transparent;
    background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover { background: var(--scroll-thumb-hover); }

html { overflow-x: hidden; overflow-y: scroll; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after {
        animation-duration: 0.001ms !important;
        transition-duration: 0.001ms !important;
    }
}

body {
    margin: 0;
    font-family: var(--font-body);
    background: var(--bg);
    color: var(--text);
    line-height: 1.6;
    min-height: 100vh;
    min-height: 100dvh;
    overflow-x: hidden;
    transition: background-color var(--transition), color var(--transition);
}
/* html уже обрезает горизонталь (overflow-x: hidden). Второй overflow-x:
   hidden на body делает body отдельным контейнером прокрутки — из-за этого
   страница иногда «залипала» и выше нельзя было листать. clip обрезает,
   но контейнера прокрутки не создаёт. */
@supports (overflow: clip) {
    body { overflow-x: clip; }
}

/* Атрибут hidden в UA-стилях слабее любого класса: без !important
   элементы с display из класса (.btn и др.) не прячутся. */
[hidden] { display: none !important; }

/* Раньше у заголовков не было своего font-size вовсе — размер зависел
   от браузерного дефолта и от того, переопределила ли его конкретная
   карточка/страница (переопределяли не все и не одинаково). Правило
   ниже — только подложка: у любого h1-h4, для которого нет более
   специфичного селектора (а он почти всегда есть — .tournament-card h3,
   .stream-details h4 и так далее сильнее по специфичности и не
   меняются), теперь предсказуемый размер по шкале вместо browser-default. */
h1, h2, h3, h4 {
    font-family: var(--font-head);
    margin: 0;
    line-height: var(--leading-tight);
}
h1 { font-size: var(--text-2xl); }
h2 { font-size: var(--text-xl); }
h3 { font-size: var(--text-lg); }
h4 { font-size: var(--text-md); }
p { margin: 0; }
ul { margin: 0; padding: 0; }
img, video, iframe, canvas, svg { max-width: 100%; height: auto; }

a { color: var(--accent-2); }

:focus-visible {
    outline: 2px solid var(--accent-2);
    outline-offset: 2px;
    border-radius: 4px;
}

/* Наведение и фокус для всего интерактивного, что само явно не
   отключает transform/transition (карточки, кнопки и т. п. уже задают
   своё) — прежде многие мелкие элементы (значки, ссылки в тексте)
   реагировали на hover рывком, без перехода вовсе. */
a, button, [role="button"], [role="tab"], summary {
    transition: color var(--transition-fast), background-color var(--transition-fast),
                border-color var(--transition-fast), box-shadow var(--transition-fast),
                transform var(--transition-fast);
}

/* Текст только для экранного диктора. clip — приём старый и держится
   на position:absolute: стоит родителю сбить позиционирование, и текст
   вылезает на страницу. clip-path делает то же самое современным
   способом и не зависит от позиционирования — держим оба. */
.sr-only {
    position: absolute; width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%);
    white-space: nowrap; border: 0;
}

.skip-link {
    position: absolute; left: 12px; top: -60px;
    background: var(--accent); color: var(--on-accent);
    padding: 10px 18px; border-radius: var(--radius-sm);
    z-index: 3000; transition: top var(--transition);
}
.skip-link:focus { top: 12px; }

.container {
    max-width: var(--container);
    margin: 0 auto;
    padding: 0 20px;
    width: 100%;
}

/* ---------- Шапка ---------- */
.header {
    background: var(--surface);
    /* --surface близок по яркости к --bg (особенно в тёмной теме), и
       на глаз шапка сливалась с фоном страницы — box-shadow один эту
       грань не обозначал. Явная граница по всему периметру шапки. */
    border: 1px solid var(--border);
    box-shadow: var(--shadow-sm);
    position: sticky;
    top: 12px;
    z-index: 1000;
    margin: 12px 16px 0;
    border-radius: var(--radius-lg);
}

.header .container { max-width: 1440px; }

.header-content {
    display: flex;
    align-items: center;
    gap: 14px;
    /* Кнопки выросли на несколько пикселей — отступ уменьшили ровно
       настолько же, чтобы высота шапки не изменилась. */
    padding: 16px 0;
    min-width: 0;
}

.logo {
    display: flex;
    align-items: center;
    gap: 10px;
    font-family: var(--font-head);
    font-size: 1.35rem;
    font-weight: 700;
    /* Чуть светлее основного --accent — тот же оттенок, что уже
       используется у иконки рядом (.logo i) и у ссылок (a), так что
       название и значок теперь одного тона. */
    color: var(--accent-2);
    text-decoration: none;
    flex: 0 0 auto;
    white-space: nowrap;
    position: relative;
    z-index: 1;
}
.logo i { font-size: 1.45rem; color: var(--accent-2); }
.logo-mark { height: 1.45rem; width: 1.45rem; flex: none; color: var(--accent-2); }

/* min-width:0 обязателен: без него flex-элемент отказывается сжиматься
   и меню начинает вылезать поверх логотипа и кнопок справа.
   overflow:hidden — страховка: если места всё-таки не хватило,
   меню аккуратно обрежется, а не наедет на соседей. */
.main-nav {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    justify-content: center;
    overflow: hidden;
}

.nav-list {
    display: flex;
    list-style: none;
    gap: 8px;
    flex-wrap: nowrap;
    justify-content: center;
}

.nav-link {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--text-muted);
    text-decoration: none;
    padding: 10px 16px;
    border-radius: var(--radius-md);
    transition: background-color var(--transition-fast), color var(--transition-fast), box-shadow var(--transition-fast), transform var(--transition-fast);
    font-weight: 500;
    font-size: var(--text-sm);
    white-space: nowrap;
    /* Каждый пункт — самостоятельная кнопка, а не общий ряд с рамкой:
       своя тень задаёт границу вместо border, поэтому кнопки читаются
       по отдельности даже без явной обводки друг у друга.
       Пункты-модалки (см. js/layout.js, navMarkup) — <button>, а не <a>,
       поэтому сбрасываем нативные стили кнопки браузера. */
    background: var(--surface-3);
    border: none;
    box-shadow: 0 1px 0 rgba(255, 255, 255, 0.04) inset, 0 2px 6px -2px rgba(0, 0, 0, 0.5);
    font-family: inherit;
    line-height: inherit;
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
}
.nav-link:hover { background: var(--accent-soft); color: var(--accent-2); transform: translateY(-1px); }
.nav-link:active { transform: translateY(0) scale(0.97); }
.nav-link[aria-current="page"] {
    background: linear-gradient(90deg, var(--accent) 0%, var(--accent-2) 100%);
    color: var(--on-accent);
    box-shadow: 0 4px 12px var(--accent-ring);
}

.header-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 0 0 auto;
    margin-left: auto;
}
.header-actions .btn { padding: 12px 18px; font-size: 0.95rem; }

/* Кнопка профиля — всегда самая правая, независимо от того, в каком
   порядке скрипты добавили свои кнопки. Порядок вставки в DOM тут
   ненадёжен: меню «⋮», пропуск и корзину дописывают три разных
   файла (search.js/friends.js, dantes-pass.js, store.js), и кто успеет
   первым — зависит от того, что раньше загрузилось. order решает это
   раз и навсегда, не заставляя эти файлы договариваться между собой. */
.header-actions > * { order: 0; }
.header-actions .btn-profile-link { order: 10; }

/* Круглые кнопки (календарь, колокольчик, тема) в этой панели —
   крупнее, чем базовый .btn-icon/.theme-toggle везде на сайте
   (там своя разметка: чат, корзина, карточки постов и т. д.). */
.header-actions .btn-icon,
.header-actions .theme-toggle {
    width: 50px;
    height: 50px;
    font-size: 1.22rem;
}

/* ---------- Кнопки шапки: цвет не зависит от темы ----------
   .nav-link и .btn-icon берут фон из --surface-3 / --accent-soft, а эти
   токены в светлой теме почти белые — шапка на светлой теме теряла
   свои тёмно-фиолетовые кнопки-таблетки. Кнопки шапки — часть фирменного
   стиля, а не поверхность страницы, поэтому фиксируем им цвета тёмной
   темы напрямую (в обход переменных) на любой теме сайта. */
.header .nav-link {
    background: var(--header-pill-bg);
    color: var(--header-pill-text);
}
.header .nav-link:hover { background: var(--header-pill-hover-bg); color: var(--header-pill-hover-text); }
.header .nav-link[aria-current="page"] {
    background: var(--header-pill-active-grad);
    color: var(--header-pill-active-text);
}
/* Круглая кнопка календаря — единственная, что осталась вне «⋮»
   (см. блок «Кнопка «⋮»» ниже: поиск и колокольчик уже объединены).
   Раньше была своим, более светлым акцентным цветом — теперь того же
   тёмно-фиолетового тона, что и остальные кнопки шапки, чтобы шапка
   не пестрила разными оттенками одной и той же кнопки. */
.header-actions .btn-icon,
.header-actions .theme-toggle {
    background: var(--header-pill-bg);
    color: var(--header-pill-text);
}
.header-actions .btn-icon:hover,
.header-actions .theme-toggle:hover { background: var(--header-pill-hover-bg); color: var(--header-pill-hover-text); }

/* ---------- Кнопка «⋮»: поиск, Dantes AI и Pass под одной кнопкой ----------
   Уведомления сюда больше не входят — отдельный круглый значок в
   headerActions, перед этой самой кнопкой (см. .notif ниже и
   js/friends.js mountBell). Остальные три — общий пункт-таблетка того
   же цвета, что и остальные кнопки навигации (наследует стиль от
   .nav-link), а не круглый значок: открывает список из нескольких
   действий, а не сразу один конкретный экран. */
.header-more { position: relative; }
.header-more-btn { position: relative; padding-left: 14px; padding-right: 14px; }

.header-more-panel {
    position: absolute;
    top: calc(100% + 12px);
    /* Кнопка «⋮» стоит у самого правого края шапки — если открывать
       панель от левого края кнопки (left:0), она уезжает за пределы
       экрана и справа обрезается (это и было на скриншоте). Правый
       край панели вместо этого совпадает с правым краем кнопки, а
       сама панель растёт влево — так она остаётся в границах экрана
       на любой ширине, без отдельных подстроек под мобильные. */
    right: 0;
    left: auto;
    min-width: 210px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    z-index: 1200;
    overflow: visible;
    padding: 6px;
    display: grid;
    gap: 2px;
    animation: slide-up 0.18s ease;
}

.header-more-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 10px 12px;
    border: none;
    border-radius: var(--radius-md);
    background: none;
    color: var(--text);
    font-family: inherit;
    font-size: 0.9rem;
    font-weight: 500;
    text-align: left;
    /* Пункты — в основном <button>, но встречаются и ссылки (например
       Dantes Pass раньше вёл на страницу) — на всякий случай, чтобы
       ссылка не подчёркивалась, как обычный текст. */
    text-decoration: none;
    cursor: pointer;
    position: relative;
}
.header-more-item:hover { background: var(--accent-soft); color: var(--accent-2); }
.header-more-item i { width: 1.1em; text-align: center; color: var(--text-dim); }
.header-more-item:hover i { color: var(--accent-2); }

/* Порядок пунктов внутри «⋮» — не DOM (кто из трёх скриптов успел
   смонтироваться первым, от раза к разу разное, и пункты дёргались бы
   местами при каждой перезагрузке), а явный css order, тем же приёмом,
   что и .header-actions > * ниже для кнопки профиля. По просьбе поиск —
   всегда первым пунктом списка. */
.header-more-panel > * { order: 5; }
.header-more-panel [data-open-search] { order: 1; }
.header-more-panel #palettePicker { order: 2; }
.header-more-panel #passBtn { order: 3; }
.header-more-panel [data-open-ai] { order: 4; }

/* Тема — пункт меню «⋮» (js/layout.js initPalette()). Сама
   кнопка — обычный .header-more-item, а кружки палитр — не строка
   внутри списка (сдвигала бы соседние пункты), а всплывающая панель
   под кнопкой, тем же приёмом, что и .notif-panel у колокольчика —
   работает одинаково что в поповере «⋮» на десктопе, что в
   планшетной раскладке-строке (@media 769–1240px чуть выше), где
   .header-more-panel уже сама превращена в ряд круглых кнопок. */
.palette-picker { position: relative; display: flex; }
.palette-swatches {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    left: auto;
    display: flex;
    gap: 8px;
    padding: 10px 12px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    z-index: 1210;
}
.palette-swatch {
    width: 26px; height: 26px;
    border-radius: 50%;
    border: 2px solid transparent;
    cursor: pointer;
    padding: 0;
    transition: transform var(--transition-fast), border-color var(--transition-fast);
}
.palette-swatch:hover { transform: scale(1.1); }
.palette-swatch.is-active { border-color: var(--text); }
.palette-swatch[data-choice="classic"] { background: linear-gradient(135deg, #6c63ff, #8a84ff); }
.palette-swatch[data-choice="aurora"]  { background: linear-gradient(135deg, #ff5fa2, #4fc3ff); }
.palette-swatch[data-choice="fire"]    { background: linear-gradient(135deg, #ff9330, #ffb35c); }
.palette-swatch[data-choice="mint"]    { background: linear-gradient(135deg, #29e0c4, #5cf0d8); }
/* Светлая — фон почти белый, поэтому кружок обводим всегда (не только
   в is-active), иначе на тёмной панели мог потеряться край. */
.palette-swatch[data-choice="light"] {
    background: linear-gradient(135deg, #ffffff, #ded9ff);
    border-color: var(--border);
}
.palette-swatch[data-choice="light"].is-active { border-color: var(--text); }


/* ============================================================
   Пульс платформы — первая полоса главной (js/home-pulse.js)

   Смысл полосы: за первые две секунды человек должен увидеть, что
   здесь живые люди и ближайший турнир, а не слоган. Поэтому полоса
   стоит ВЫШЕ баннера с новостями: баннер — про то, что мы хотим
   рассказать, пульс — про то, что человеку нужно знать.
   ============================================================ */
.pulse {
    max-width: calc(var(--container) - 40px);
    width: calc(100% - 40px);
    margin: 18px auto 0;
}
.pulse[hidden] { display: none; }

/* Раньше — три блока в ряд (турнир / цифры / призыв завести аккаунт),
   сейчас только цифры, растянутые на всю ширину полосы. */
.pulse-inner {
    padding: 0;
    max-width: none;
}

/* Живые цифры сообщества — шесть штук в ряд на широком экране */
.pulse-stats {
    display: grid;
    grid-template-columns: repeat(6, minmax(0, 1fr));
    gap: 10px;
}
.pulse-stat {
    display: flex; flex-direction: column; gap: 2px;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--surface-2);
}
.pulse-stat i { color: var(--accent-2); font-size: 0.85rem; }
.pulse-stat b {
    font-family: var(--font-head);
    font-size: 1.05rem;
    color: var(--text);
    line-height: 1.1;
}
.pulse-stat span { font-size: 0.72rem; color: var(--text-dim); }

@media (max-width: 900px) {
    .pulse-stats { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
    .pulse { margin-top: 12px; }
    .pulse-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}


/* ---------- Кнопки ---------- */
.btn {
    padding: 10px 18px;
    border: 2px solid transparent;
    border-radius: var(--radius-sm);
    font-family: var(--font-head);
    font-weight: 600;
    font-size: 0.93rem;
    cursor: pointer;
    transition: transform var(--transition-fast), box-shadow var(--transition-fast),
                background-color var(--transition-fast), color var(--transition-fast),
                border-color var(--transition-fast);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    text-decoration: none;
    background: transparent;
    color: var(--accent-2);
    line-height: 1.2;
    /* Кнопка не резиновая по содержимому: когда flexbox прижимает её уже,
       чем нужно её надписи (например, две кнопки в ряд на средней ширине
       экрана, или произвольное название команды/турнира в тексте кнопки),
       длинное неразрывное слово раньше просто вылезало за скруглённые
       края кнопки — text-align тут не помогал, а overflow:hidden у
       карточки-контейнера превращал вылезший текст в обрубленную кашу.
       overflow-wrap:anywhere — это последний рубеж: пока слово помещается
       целиком, ничего не меняется, а когда не помещается, кнопка честно
       переносит текст на вторую строку вместо того, чтобы съезжать за
       собственные границы. */
    overflow-wrap: anywhere;
    text-align: center;
}

.btn:hover:not(:disabled) { transform: translateY(-2px); }
/* Нажатие — чуть проседает и чуть сжимается: раньше кнопка на клике
   просто возвращалась в исходное место (translateY(0)), без разницы с
   состоянием покоя, — нечем было подтвердить, что клик вообще
   засчитался, кроме результата действия чуть позже. */
.btn:active:not(:disabled) { transform: translateY(0) scale(0.97); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: 0.55; cursor: not-allowed; transform: none; }

.btn-primary {
    background: linear-gradient(90deg, var(--accent) 0%, var(--accent-2) 100%);
    color: var(--on-accent);
}
.btn-primary:hover:not(:disabled) { box-shadow: 0 8px 20px var(--accent-ring); }

.btn-secondary, .btn-login, .btn-small {
    background: transparent;
    color: var(--accent-2);
    border-color: var(--accent);
}
.btn-secondary:hover:not(:disabled),
.btn-login:hover:not(:disabled),
.btn-small:hover:not(:disabled) { background: var(--accent-soft); }

.btn-small { padding: 8px 16px; font-size: 0.87rem; }
.btn-block { width: 100%; }

.btn-danger { border-color: var(--danger); color: var(--danger); background: transparent; }
/* Зелёная кнопка подтверждения. Заливка, а не контур: это единственное
   действие в жёлтом баннере чек-ина, и оно должно читаться как «нажми
   сюда», а не как ещё одна пилюля рядом с текстом. */
.btn-success {
    border-color: var(--success);
    background: var(--success);
    color: #06210f;
    font-weight: 700;
}
.btn-success:hover { filter: brightness(1.08); }
.btn-danger:hover:not(:disabled) { background: var(--danger); color: #fff; }

/* Продвижение товара, другие Premium-действия — тот же золотой акцент,
   что у короны/титулов, чтобы визуально считывалось как «премиум». */
.btn-premium {
    background: linear-gradient(90deg, var(--gold, #f7c948) 0%, #b8860b 100%);
    color: #1a1408; border-color: transparent;
}
.btn-premium:hover:not(:disabled) { box-shadow: var(--glow-premium); }

.btn-lg { padding: 14px 28px; font-size: 1.05rem; }

.btn.is-loading { position: relative; color: transparent !important; pointer-events: none; }
.btn.is-loading::after {
    content: '';
    position: absolute;
    width: 18px; height: 18px;
    border: 2px solid rgba(255, 255, 255, 0.55);
    border-top-color: transparent;
    border-radius: 50%;
    animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.btn-icon {
    width: 44px; height: 44px; padding: 0;
    border-radius: 50%;
    background: var(--accent-soft);
    color: var(--accent-2);
    border: none;
    display: inline-flex; align-items: center; justify-content: center;
    cursor: pointer;
    font-size: 1.05rem;
    transition: background-color var(--transition-fast), transform var(--transition-fast), box-shadow var(--transition-fast);
}
.btn-icon:hover { background: var(--accent-ring); transform: translateY(-1px); }
.btn-icon:active { transform: translateY(0) scale(0.94); }

.theme-toggle {
    width: 44px; height: 44px;
    background: var(--accent-soft);
    border: none;
    border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    cursor: pointer;
    font-size: 1.1rem;
    color: var(--accent-2);
    transition: background-color var(--transition-fast), transform var(--transition-fast);
}
.theme-toggle:hover { background: var(--accent-ring); transform: rotate(12deg) scale(1.06); }
.theme-toggle:active { transform: rotate(12deg) scale(0.94); }

.btn-profile {
    max-width: 170px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    border-color: var(--accent);
    min-width: 0;
}
.btn-profile span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.btn-profile.active {
    background: linear-gradient(135deg, var(--accent) 0%, var(--accent-2) 100%);
    color: var(--on-accent);
}

/* Кнопка мобильного меню — раньше вовсе не имела своего hover/active,
   только общий :focus-visible контур на клавиатурном фокусе. */
.mobile-menu-btn {
    display: none;
    background: var(--accent-soft);
    border: none;
    border-radius: var(--radius-sm);
    font-size: 1.2rem;
    color: var(--accent-2);
    cursor: pointer;
    width: 42px; height: 42px;
    align-items: center; justify-content: center;
    flex-shrink: 0;
    transition: background-color var(--transition-fast), transform var(--transition-fast);
}
.mobile-menu-btn:hover { background: var(--accent-ring); }
.mobile-menu-btn:active { transform: scale(0.94); }

/* ---------- Баннер на главной (в стиле витрины Steam) ----------
   Слайды с картинкой на весь блок (обновления/новый сезон/новости
   платформы), листаются автоматически и точками/стрелками вручную. */
.home-banner {
    position: relative;
    border: 2px solid #fff;
    background: var(--surface-2);
    overflow: hidden;
    /* Ширина — ровно как у «Активных турниров» и остальных разделов.
       Баннер лежит вне .container (он идёт до <main>), поэтому те же
       ограничения приходится повторить здесь: было margin: 20px 16px,
       и на широком экране баннер вылезал за общий край страницы
       сотнями пикселей, ломая единственную вертикаль, по которой
       выровнено всё остальное. */
    /* Минус 40px — это внутренние поля .container (по 20px с каждой
       стороны): у него ограничена коробка, а у баннера — сама
       картинка, и без вычитания он оказался бы на 40px шире. */
    max-width: calc(var(--container) - 40px);
    width: calc(100% - 40px);
    margin: 20px auto 0;
    border-radius: var(--radius-lg);
}

.home-banner-slides {
    position: relative;
    height: clamp(260px, 42vw, 460px);
}

.home-banner-slide {
    position: absolute;
    inset: 0;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.5s ease;
    background-size: cover;
    background-position: center;
    display: flex;
    align-items: flex-end;
}
.home-banner-slide.is-active { opacity: 1; visibility: visible; }

.home-banner-slide::before {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(0deg, rgba(0,0,0,0.78) 0%, rgba(0,0,0,0.25) 55%, rgba(0,0,0,0.05) 100%);
}

.home-banner-caption {
    position: relative;
    z-index: 1;
    padding: 32px clamp(20px, 5vw, 56px);
    max-width: 720px;
    color: #fff;
    opacity: 0;
    transform: translateY(14px);
    transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.1s, transform 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.1s;
}
.home-banner-slide.is-active .home-banner-caption { opacity: 1; transform: translateY(0); }
.home-banner-caption h2 { font-size: clamp(1.4rem, 3.2vw, 2.2rem); margin-bottom: 8px; }
.home-banner-caption p { color: rgba(255,255,255,0.85); font-size: 0.98rem; margin-bottom: 14px; }

.home-banner-empty {
    height: clamp(200px, 30vw, 320px);
    display: flex; align-items: center; justify-content: center;
    color: var(--text-muted);
    background:
        radial-gradient(1200px 400px at 50% -10%, var(--accent-soft), transparent 70%),
        linear-gradient(135deg, var(--surface) 0%, var(--surface-2) 100%);
}

.home-banner-arrow {
    position: absolute;
    top: 50%; transform: translateY(-50%);
    z-index: 2;
    width: 40px; height: 40px;
    border-radius: 50%;
    border: none;
    background: rgba(0,0,0,0.45);
    color: #fff;
    cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    transition: background-color var(--transition);
}
.home-banner-arrow:hover { background: rgba(0,0,0,0.7); }
.home-banner-arrow.prev { left: 16px; }
.home-banner-arrow.next { right: 16px; }

.home-banner-dots {
    position: absolute;
    z-index: 2;
    bottom: 16px; right: clamp(20px, 5vw, 56px);
    display: flex; gap: 8px;
}
.home-banner-dot {
    width: 9px; height: 9px; border-radius: 50%;
    border: none; padding: 0; cursor: pointer;
    background: rgba(255,255,255,0.4);
    transition: background-color var(--transition), transform var(--transition);
}
.home-banner-dot.is-active { background: #fff; transform: scale(1.2); }

.home-banner-admin { position: absolute; z-index: 2; top: 16px; right: 16px; }

/* ---------- Прелоадер ---------- */
.site-preloader {
    position: fixed; inset: 0; z-index: 9999;
    display: flex; align-items: center; justify-content: center; flex-direction: column; gap: 16px;
    background: var(--bg);
    transition: opacity 0.35s ease;
}
.site-preloader.is-hidden { opacity: 0; pointer-events: none; }

/* Значок бренда вместо безликого кольца: тот же квадрат с «D» и жёлтой
   точкой, что и в favicon/иконке приложения, только живой — дышит
   масштабом, а вокруг него крутится дуга (как индикатор загрузки на iOS
   поверх иконки). Цвета — через переменные темы (--accent/--on-accent),
   чтобы подстраивались под выбранную палитру (см. .palette-swatch). */
.site-preloader-mark {
    position: relative;
    width: 64px; height: 64px;
    display: flex; align-items: center; justify-content: center;
}
.site-preloader-logo {
    position: relative;
    z-index: 1;
    width: 100%; height: 100%;
    border-radius: 20px;
    display: flex; align-items: center; justify-content: center;
    background: linear-gradient(135deg, var(--accent-2), var(--accent));
    color: var(--on-accent);
    font-family: var(--font-head);
    font-weight: 700;
    font-size: 1.8rem;
    box-shadow: 0 8px 24px color-mix(in srgb, var(--accent) 45%, transparent);
    animation: preloader-breathe 1.6s ease-in-out infinite;
}
.site-preloader-dot {
    position: absolute;
    top: 0; right: 0;
    width: 11px; height: 11px;
    border-radius: 50%;
    background: #ffd93d;
    z-index: 2;
    animation: preloader-dot-pulse 1.6s ease-in-out infinite;
}
.site-preloader-ring {
    position: absolute;
    inset: -9px;
    border-radius: 26px;
    border: 3px solid transparent;
    border-top-color: var(--accent);
    border-right-color: var(--accent);
    animation: preloader-spin 0.9s linear infinite;
}
.site-preloader-label {
    display: flex; align-items: flex-end;
    color: var(--text-muted); font-size: 0.92rem;
}
/* Три точки после «загружается» бегут волной, а не висят статично —
   мелкая деталь, но именно из-за неё текст тоже выглядит живым. */
.site-preloader-ellipsis { display: inline-flex; gap: 3px; margin-left: 2px; }
.site-preloader-ellipsis i {
    width: 4px; height: 4px;
    border-radius: 50%;
    background: currentColor;
    opacity: 0.25;
    animation: preloader-ellipsis 1.2s ease-in-out infinite;
}
.site-preloader-ellipsis i:nth-child(2) { animation-delay: 0.15s; }
.site-preloader-ellipsis i:nth-child(3) { animation-delay: 0.3s; }

@keyframes preloader-spin { to { transform: rotate(360deg); } }
@keyframes preloader-breathe {
    0%, 100% { transform: scale(1); }
    50% { transform: scale(1.08); }
}
@keyframes preloader-dot-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(255, 217, 61, 0.55); }
    50% { box-shadow: 0 0 0 5px rgba(255, 217, 61, 0); }
}
@keyframes preloader-ellipsis {
    0%, 100% { opacity: 0.25; transform: translateY(0); }
    50% { opacity: 1; transform: translateY(-3px); }
}

/* ---------- Список слайдов в модалке управления баннером ---------- */
.banner-manager-row {
    display: flex; align-items: center; gap: 14px;
    padding: 10px 14px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-md, 10px);
}
.banner-manager-thumb {
    width: 56px; height: 44px; flex-shrink: 0; border-radius: 8px;
    background-color: var(--surface);
    background-size: cover; background-position: center;
    display: flex; align-items: center; justify-content: center;
    color: var(--text-muted);
}
.banner-manager-info { flex: 1; min-width: 0; }
.banner-manager-info strong { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); }
.banner-manager-info span { color: var(--text-muted); font-size: 0.85rem; }

/* ---------- Список возможностей в приветственной модалке ---------- */
.welcome-feature-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 12px; }
.welcome-feature-list li { display: flex; align-items: center; gap: 12px; color: var(--text); font-size: 0.96rem; }
.welcome-feature-list i {
    width: 32px; height: 32px; flex-shrink: 0;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--accent) 0%, var(--accent-2) 100%);
    color: var(--on-accent);
    display: flex; align-items: center; justify-content: center;
    font-size: 0.85rem;
}

/* Перенос строки внутри ряда чипов: в ленте вторым рядом идёт выбор игры,
   и он должен начинаться с новой строки, а не липнуть к категориям. */
.chip-break { flex-basis: 100%; height: 0; margin: 0; }

/* ---------- Выбор игры ----------
   Окно «Добавьте игру» при регистрации (js/auth.js) и список игр в
   настройках профиля (js/profile.js) используют одни и те же плитки. */
.game-pick-intro { color: var(--text-muted); font-size: var(--text-sm); margin: 0 0 16px; line-height: 1.5; }

.game-pick-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 12px;
}

.game-pick {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 10px;
    padding: 18px 12px;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--surface-2);
    color: var(--text);
    cursor: pointer;
    text-align: center;
    transition: border-color .15s, transform .15s, background .15s;
}
.game-pick:hover:not(:disabled) { border-color: var(--accent); transform: translateY(-2px); }
.game-pick:disabled { opacity: .5; cursor: default; }
.game-pick.is-chosen { border-color: var(--accent); background: var(--accent-soft); opacity: 1; }
.game-pick.is-current { border-color: var(--accent); background: var(--accent-soft); }

.game-pick-icon { font-size: 1.7rem; line-height: 1; }
.game-pick-art { display: inline-flex; align-items: center; justify-content: center; height: 48px; }
.game-pick-art img { width: 44px; height: 44px; object-fit: contain; border-radius: 10px; }
/* Цвет иконки берётся из палитры игры: те же значения, что у ярлыков
   .tag-game в списках турниров и трансляций. */
.game-pick-icon.brawl        { color: var(--game-brawl); }
.game-pick-icon.standoff2    { color: var(--game-standoff2); }
.game-pick-icon.clash_royale { color: var(--game-clash_royale); }
.game-pick-icon.mlbb         { color: var(--game-mlbb); }

.game-pick-label { font-size: var(--text-sm); font-weight: 600; }
.game-pick-sub   { font-size: var(--text-xs); color: var(--text-muted); }
.game-pick-note  { min-height: 20px; margin: 12px 0 0; font-size: var(--text-sm); color: var(--text-muted); text-align: center; }

@media (max-width: 380px) {
    .game-pick-grid { grid-template-columns: 1fr; }
}

/* ---------- Общие секции ---------- */
.main-content { padding: 56px 0; }

.section-title {
    font-size: var(--text-2xl);
    font-weight: 700;
    margin-bottom: var(--space-7);
    color: var(--text);
    text-align: center;
}

.section-head {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: 16px; margin-bottom: 28px; flex-wrap: wrap;
}
.section-head .section-title { margin-bottom: 0; text-align: left; }

.featured-section { margin-bottom: 72px; }

/* ---------- Карточки турниров (компактные) ---------- */
.tournament-grid, .stream-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
    gap: 22px;
}

.tournament-card {
    background: var(--surface-2);
    padding: var(--space-6);
    border-radius: var(--radius-lg);
    border: 1px solid var(--border);
    /* Раньше карточка была плоской в состоянии покоя — тень появлялась
       только на hover. Лёгкая тень с самого начала (--shadow-xs) даёт ей
       вес на фоне страницы уже сейчас, а не только в момент наведения —
       общая витрина читается по слоям, а не единым плоским полотном. */
    box-shadow: var(--shadow-xs);
    transition: transform var(--transition-slow), box-shadow var(--transition-slow), border-color var(--transition);
    display: flex;
    flex-direction: column;
}
.tournament-card:hover { transform: translateY(-4px) scale(1.012); box-shadow: var(--shadow-md); border-color: var(--accent); }
.tournament-card:focus-within { border-color: var(--accent); }

.tournament-header { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 14px; }

/* Карточка турнира с загруженным баннером: картинка занимает шапку во всю
   ширину, поэтому у самой карточки убираем верхний отступ и обрезаем углы,
   иначе баннер торчал бы за скруглением. Тег дисциплины и сумма ложатся
   поверх картинки, и им нужна тень — на светлом баннере жёлтая сумма без
   неё пропадала. */
/* Карточка сохраняет свои 24px внутреннего поля для текста, а баннер
   выходит за них отрицательным отступом — так он занимает всю ширину, а
   кнопка «Подробнее» (width: 100%) остаётся ровно по полям и не вылезает
   за край, как было бы, раздвинь мы содержимое margin'ами. */
.tournament-card.has-photo { overflow: hidden; }
.tournament-header.has-photo {
    min-height: 96px;
    align-items: flex-start;
    padding: 16px 20px;
    margin: -24px -24px 16px;
    background-image: linear-gradient(180deg, rgba(10, 10, 42, 0.15) 0%, rgba(10, 10, 42, 0.78) 100%), var(--bg-url);
    background-size: cover;
    background-position: center;
}
.tournament-header.has-photo .tag-game,
.tournament-header.has-photo .tournament-prize { text-shadow: 0 1px 4px rgba(0, 0, 0, 0.7); }

.tag-game {
    padding: 4px 12px;
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    border: 1px solid currentColor;
    background: color-mix(in srgb, currentColor 14%, transparent);
    white-space: nowrap;
    color: var(--game-other);
}
.tag-game.brawl        { color: var(--game-brawl); }
.tag-game.standoff2    { color: var(--game-standoff2); }
.tag-game.clash_royale { color: var(--game-clash_royale); }
.tag-game.mlbb         { color: var(--game-mlbb); }
.tag-game.other        { color: var(--game-other); }

/* Категория трансляции — тот же приём, что у тега игры: своя рамка
   и заливка через currentColor, цвет только меняется классом. */
.tag-type {
    padding: 4px 12px;
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 600;
    border: 1px solid currentColor;
    background: color-mix(in srgb, currentColor 14%, transparent);
    white-space: nowrap;
    color: var(--accent-2);
}
.tag-type { letter-spacing: 0.02em; }
.tag-type.esports     { color: var(--cat-esports); }
.tag-type.mygame      { color: var(--cat-mygame); }
.tag-type.tournaments { color: var(--cat-tournaments); }
.tag-type.talk        { color: var(--cat-talk); }

.tournament-prize { color: var(--gold); font-weight: 700; font-size: var(--text-md); white-space: nowrap; }
/* h3 с font-weight не задан явно нигде здесь — раньше наследовался
   браузерный bold через h1-h4, теперь то же самое явно, плюс заголовок
   карточки в этой иерархии на ступень крупнее описания и мета-строки
   под ним (var(--text-lg) → --text-sm → --text-xs, а не 1.2 → 0.94 →
   0.88rem вразнобой). */
.tournament-card h3 { font-size: var(--text-lg); font-weight: 700; margin-bottom: var(--space-2); color: var(--text); }
.tournament-desc { color: var(--text-dim); margin-bottom: var(--space-4); font-size: var(--text-sm); font-weight: 600; line-height: var(--leading-snug); flex: 1; }

.tournament-info {
    display: flex; justify-content: space-between; gap: 12px; flex-wrap: wrap;
    margin-bottom: var(--space-5); color: var(--text-muted); font-size: var(--text-xs);
}
.tournament-info span { display: inline-flex; align-items: center; gap: 6px; }

/* ---------- Карточки стримов (компактные) ---------- */
.stream-card {
    background: var(--surface-2);
    border-radius: var(--radius-lg);
    border: 1px solid var(--border);
    box-shadow: var(--shadow-xs);
    overflow: hidden;
    transition: transform var(--transition-slow), box-shadow var(--transition-slow), border-color var(--transition);
}
.stream-card:hover { transform: translateY(-4px) scale(1.012); box-shadow: var(--shadow-md); border-color: var(--accent); }
.stream-card:focus-within { border-color: var(--accent); }

.stream-thumbnail {
    height: 170px;
    background: linear-gradient(135deg, var(--accent) 0%, var(--accent-2) 100%);
    position: relative;
    display: flex; align-items: flex-end; justify-content: flex-end;
    padding: 14px;
}

.stream-live, .stream-live-badge {
    position: absolute; top: 14px; left: 14px;
    background: var(--danger); color: #fff;
    padding: 4px 10px; border-radius: 4px;
    font-weight: 700; font-size: 0.75rem; letter-spacing: 0.04em;
}
.stream-live.offline, .stream-live-badge.offline { background: var(--text-dim); }

.stream-views, .stream-viewers {
    background: rgba(0, 0, 0, 0.65);
    color: #fff;
    padding: 4px 10px;
    border-radius: 4px;
    font-size: 0.85rem;
    display: inline-flex; align-items: center; gap: 6px;
}

.stream-info { padding: 18px; display: flex; gap: 14px; align-items: center; }

.streamer-avatar {
    width: 46px; height: 46px;
    background: linear-gradient(135deg, var(--accent) 0%, var(--accent-2) 100%);
    border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    font-size: 1.1rem; color: var(--on-accent); flex-shrink: 0;
    font-family: var(--font-head); font-weight: 700;
    box-shadow: 0 0 0 2px var(--surface-2);
    transition: transform var(--transition-fast), box-shadow var(--transition-fast);
}
/* Карточка целиком наводится (.stream-card:hover уже поднимает и
   освещает всю плашку) — аватар стримера чуть увеличивается следом,
   как маленький акцент внутри общего движения, а не отдельно живущий
   элемент. */
.stream-card:hover .streamer-avatar { transform: scale(1.08); box-shadow: 0 0 0 2px var(--accent-ring); }

.stream-details h4 { font-size: var(--text-md); font-weight: 700; color: var(--text); margin-bottom: var(--space-1); }
.stream-details p { color: var(--text-dim); font-size: var(--text-sm); margin-bottom: var(--space-2); }

.stream-game {
    background: var(--accent-soft);
    color: var(--accent-2);
    padding: 3px 10px;
    border-radius: 999px;
    font-size: var(--text-xs);
    font-weight: 600;
}

/* ---------- Состояния загрузки / пустоты ---------- */
.skeleton {
    background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%);
    background-size: 400% 100%;
    animation: shimmer 1.3s ease infinite;
    border-radius: var(--radius-lg);
    min-height: 190px;
    border: 1px solid var(--border);
}
@keyframes shimmer { 0% { background-position: 100% 0; } 100% { background-position: 0 0; } }

/* Один компонент на весь сайт (js/utils.js, emptyState()) — раньше
   просто пунктирная рамка, значок и строка текста, отсюда и была
   заметна «разница в стиле» между разделами: единственное место
   разнообразия — сам значок, а он терялся тем же серым цветом, что и
   рамка. Теперь у значка своя круглая подложка (тот же приём, что у
   .btn-icon/.theme-toggle — узнаваемый, а не новый), а текст и сама
   плашка — по шкале отступов. */
.empty-state {
    grid-column: 1 / -1;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: var(--space-8) var(--space-5);
    color: var(--text-dim);
    border: 1px dashed var(--border);
    border-radius: var(--radius-lg);
    animation: fade-in var(--transition-slow);
}
.empty-state i {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    margin-bottom: var(--space-4);
    border-radius: 50%;
    background: var(--accent-soft);
    color: var(--accent-2);
    font-size: 1.4rem;
}
.empty-state p {
    max-width: 42ch;
    font-size: var(--text-sm);
    line-height: var(--leading-relaxed);
}
/* Плашка почти всегда рядом с кнопкой действия («Создать турнир»,
   «Написать первым» и т. п.) — саму кнопку рисует вызывающий код, а не
   emptyState(), но раз она обычно идёт сразу следом, задаём ей отступ
   сверху здесь же, чтобы вызывающему коду не приходилось помнить об
   этом каждый раз самому. */
.empty-state + .btn, .empty-state ~ .btn { margin-top: var(--space-4); }

/* ---------- Футер ---------- */
.footer {
    background: var(--surface);
    padding: 56px 0 26px;
    border-top: 2px solid var(--accent);
    margin-top: 40px;
}

.footer-content {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: 36px;
    margin-bottom: 36px;
}

.footer-section p { color: var(--text-dim); margin: 14px 0; }
.footer-section h4 { font-size: 1.1rem; color: var(--text); margin-bottom: 16px; }
.footer-section ul { list-style: none; }
.footer-section ul li { margin-bottom: 9px; }
.footer-section ul li a {
    color: var(--text-dim); text-decoration: none;
    transition: color var(--transition), padding-left var(--transition);
}
.footer-section ul li a:hover { color: var(--accent-2); padding-left: 5px; }

.social-links { display: flex; gap: 12px; margin-top: 18px; }
.social-links a {
    width: 40px; height: 40px;
    background: var(--accent-soft);
    border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    color: var(--accent-2); font-size: 1.1rem;
    transition: background var(--transition), transform var(--transition), color var(--transition);
}
.social-links a:hover {
    background: linear-gradient(135deg, var(--accent) 0%, var(--accent-2) 100%);
    color: var(--on-accent); transform: translateY(-3px);
}

.footer-bottom {
    text-align: center; padding-top: 26px;
    border-top: 1px solid var(--border);
    color: var(--text-muted); font-size: 0.88rem;
}
/* Контакт поддержки — самая нижняя строка сайта. Заметнее копирайта:
   за ним приходят, когда что-то не работает, и искать его не должны. */
.footer-support { margin-top: 8px; color: var(--text-dim); }
.footer-support i { color: var(--accent-2); margin-right: 6px; }
.footer-support a { color: var(--accent-2); font-weight: 600; }
.footer-support a:hover { text-decoration: underline; }

/* ---------- Модальные окна ---------- */
.modal {
    display: none;
    position: fixed; inset: 0;
    background: rgba(0, 0, 0, 0.66);
    z-index: 2000;
    padding: 20px;
    /* Раньше сама .modal-content была ограничена по высоте (88vh) и
       прокручивалась внутри себя — длинную форму (например, «Новый
       пост» с открытым выбором файлов) приходилось скроллить в этом
       узком окошке отдельно от страницы. Теперь ограничения по высоте
       у неё нет вовсе — если контент выше экрана, скроллится сам
       оверлей .modal (overflow-y: auto здесь), а не блок внутри него:
       всё содержимое модалки просто целиком сдвигается при скролле,
       без своей внутренней полосы прокрутки. */
    overflow-y: auto;
    /* Подстраховка от горизонтального выезда: если что-то внутри формы
       (например, встроенный календарь у input type="date") всё равно
       окажется шире контейнера на редком мобильном браузере, оверлей
       обрежет лишнее, а не пустит страницу вбок с белой полосой сбоку. */
    overflow-x: hidden;
}
.modal.active { display: flex; animation: fade-in 0.2s ease; }

.modal-content {
    background: var(--surface);
    border-radius: var(--radius-xl);
    width: 100%;
    max-width: 520px;
    /* margin: auto (а не align-items/justify-content на .modal) —
       стандартный приём для центрирования по обеим осям во flex-
       контейнере, который при этом безопасно скроллится, если
       содержимое выше вьюпорта: контент не обрежется сверху, как
       бывает с align-items: center при overflow. */
    margin: auto;
    border: 1px solid var(--border);
    box-shadow: var(--shadow-lg);
    animation: slide-up 0.25s ease;
    overflow-x: hidden;
}

@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes slide-up {
    from { opacity: 0; transform: translateY(24px); }
    to   { opacity: 1; transform: translateY(0); }
}

.modal-header {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 20px 24px;
    border-bottom: 1px solid var(--border);
}
.modal-header h3 {
    font-size: 1.3rem; color: var(--text);
    display: flex; align-items: center; gap: 10px;
}

.modal-close {
    background: transparent; border: none;
    color: var(--text-muted); font-size: 1.6rem;
    cursor: pointer; line-height: 1;
    width: 38px; height: 38px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    transition: background var(--transition), color var(--transition);
    flex-shrink: 0;
}
.modal-close:hover { background: var(--accent-soft); color: var(--danger); }

.modal-body { padding: 24px; }

/* Текст в модалке подтверждения (DP.layout.confirmModal) */
.confirm-text { color: var(--text-muted); font-size: 0.95rem; line-height: 1.5; margin: 0 0 18px; }

/* Гостевая модалка (js/guest-gate.js): ссылка «Продолжить без аккаунта»
   стоит отдельной строкой под кнопками, по центру — это не действие
   наравне с ними, а тихий выход для тех, кто пока просто смотрит. */
.guest-gate-skip { display: block; margin: 14px auto 0; text-align: center; }

/* ---------- Формы ---------- */
.form-group { margin-bottom: 18px; }

/* Пара полей в одну строку. На узком экране разъезжается обратно в
   столбик: два селекта рядом на телефоне не читаются. */
.form-row { display: grid; gap: 0 16px; }
.form-row-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 640px) { .form-row-2 { grid-template-columns: 1fr; } }

/* ---------- Свой выпадающий список (js/select.js) ----------
   Настоящий <select> остаётся в форме и продолжает хранить значение —
   он лишь спрятан от глаз. Видимая часть: кнопка с текущим вариантом
   и панель списка, которую рисуем сами и потому можем оформить. */
.dp-select { position: relative; }
.dp-select > select {
    /* Не display:none — скрытое так поле выпадает из проверки
       required и не получает фокус от <label for>. */
    position: absolute; opacity: 0; pointer-events: none;
    width: 100%; height: 100%; left: 0; top: 0; margin: 0;
}
.dp-select-button {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    width: 100%;
    text-align: left;
    cursor: pointer;
    font: inherit;
}
.dp-select-button:disabled { cursor: not-allowed; opacity: 0.6; }
.dp-select-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dp-select-caret {
    flex-shrink: 0; font-size: 0.8rem; color: var(--text-muted);
    transition: transform var(--transition);
}
.dp-select.is-open .dp-select-button { border-color: var(--accent); }
.dp-select.is-open .dp-select-caret { transform: rotate(180deg); }

.dp-select-list {
    position: absolute;
    z-index: 2600;              /* выше модалок: список открывается и в них */
    max-height: 260px; overflow-y: auto;
    padding: 6px;
    border-radius: var(--radius-md, 12px);
    background: var(--surface-2);
    border: 1px solid var(--border);
    box-shadow: var(--shadow-lg);
    animation: fade-in 0.12s ease;
}
.dp-select-option {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    width: 100%;
    padding: 9px 12px;
    border: none; border-radius: var(--radius-sm, 8px);
    background: transparent;
    color: var(--text);
    font: inherit; text-align: left;
    cursor: pointer;
}
.dp-select-option i { opacity: 0; color: var(--accent-2); font-size: 0.85rem; }
.dp-select-option.is-selected i { opacity: 1; }
.dp-select-option.is-active,
.dp-select-option:hover { background: var(--accent-soft); color: var(--accent-2); }
.dp-select-option[disabled] { opacity: 0.5; cursor: not-allowed; }

.form-group > label {
    display: block; margin-bottom: 7px;
    color: var(--text); font-weight: 600; font-size: 0.93rem;
}
.form-group > label.checkbox-label { font-weight: 400; }

.form-input, .form-textarea, .form-select {
    width: 100%;
    padding: 11px 14px;
    background: var(--surface-soft);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text);
    font-family: var(--font-body);
    font-size: 1rem;
    transition: border-color var(--transition), box-shadow var(--transition);
}
.form-textarea { resize: vertical; min-height: 110px; }

/* Закрытый select уже был тёмным (наследует .form-input), но раскрытый
   список вариантов браузер рисует сам системными цветами — светлый,
   с синей подсветкой, ощутимо выбивался из тёмной темы. appearance:none
   убирает системную стрелку — рисуем свою, — а цвет/фон на option и
   optgroup Chromium/Firefox уважают и красят сам список вариантов тоже. */
.form-select {
    appearance: none;
    -webkit-appearance: none;
    padding-right: 40px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%238a84ff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='5 8 10 13 15 8'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 14px center;
    background-size: 16px;
    cursor: pointer;
}
.form-select option, .form-select optgroup {
    background: var(--surface-2);
    color: var(--text);
}

.form-input:focus, .form-textarea:focus, .form-select:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-ring);
}
.form-input[aria-invalid="true"], .form-textarea[aria-invalid="true"] { border-color: var(--danger); }
.form-input::placeholder, .form-textarea::placeholder { color: var(--text-dim); opacity: 0.75; }

/* Встроенный календарь браузера (input type="date"/"datetime-local"/"time").
   width: 100% у .form-input его не удерживает: у этого поля есть свой
   внутренний минимальный размер (день/месяц/год + часы/минуты + значок
   календаря), и на узких экранах браузер рисует его шире, чем позволяет
   контейнер, вместо того чтобы сжать, — форма/модалка вокруг него
   выезжает за пределы экрана вбок. min-width: 0 снимает с поля скрытое
   стремление к своей "естественной" ширине, max-width подстраховывает
   сверху, а сам виджет-календарь (который открывается по тапу) браузер
   всегда позиционирует внутри вьюпорта сам — переполнение шло именно
   от самого поля ввода, не от всплывающего календаря.  */
input[type="date"],
input[type="datetime-local"],
input[type="time"] {
    width: 100%;
    min-width: 0;
    max-width: 100%;
}
/* Плашки-сегменты (день/месяц/год/часы/минуты) внутри — то, что реально
   не помещается на узких экранах. Разрешаем им ужиматься вместо того,
   чтобы раздвигать поле шире родителя. */
input[type="date"]::-webkit-datetime-edit,
input[type="datetime-local"]::-webkit-datetime-edit,
input[type="time"]::-webkit-datetime-edit {
    min-width: 0;
}
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator {
    margin-left: 4px;
    flex-shrink: 0;
}

@media (max-width: 420px) {
    /* На самых узких экранах ("дата и время начала/окончания" в форме
       турнира — самое частое место с этим полем) сегментам ещё тесно —
       чуть меньше шрифт и внутренние отступы освобождают те пиксели,
       которых не хватало. */
    input[type="date"],
    input[type="datetime-local"],
    input[type="time"] {
        padding-left: 10px;
        padding-right: 10px;
        font-size: 0.92rem;
    }
}

.form-error {
    color: var(--danger);
    font-size: 0.87rem;
    margin-top: 5px;
    display: none;
}
.form-error:not(:empty) { display: block; }

.form-hint { color: var(--text-dim); font-size: 0.84rem; margin-top: 5px; }
.form-hint-inline { color: var(--text-dim); font-size: 0.82rem; font-weight: 400; }

.banner-image-preview {
    margin-top: 8px;
    border-radius: var(--radius-sm);
    overflow: hidden;
    border: 1px solid var(--border);
    background: var(--surface-2);
}
.banner-image-preview img { display: block; width: 100%; max-height: 180px; object-fit: cover; }

.checkbox-label {
    display: flex; align-items: center; gap: 10px;
    color: var(--text-muted); cursor: pointer; font-weight: 400;
}
.checkbox-label input[type="checkbox"] {
    width: 18px; height: 18px; accent-color: var(--accent); flex-shrink: 0;
}
.checkbox-group { display: flex; flex-direction: column; gap: 10px; }

.form-actions { display: flex; gap: 12px; margin-top: 26px; flex-wrap: wrap; }
/* Кнопки не должны рвать слово посередине: «Пригласить игроков» и
   «Расформировать» превращались в лесенку из трёх строк. Запрещаем перенос
   внутри кнопки и не даём ей сжаться уже собственного текста — если в ряд
   они больше не помещаются, переносится сама строка кнопок (flex-wrap
   выше), а не буквы внутри них. */
.form-actions .btn {
    flex: 1 1 140px;
    min-width: fit-content;
    white-space: nowrap;
}

.form-footer {
    text-align: center; margin-top: 18px; padding-top: 18px;
    border-top: 1px solid var(--border); color: var(--text-muted);
}
.form-footer a { color: var(--accent-2); text-decoration: none; font-weight: 600; }
.form-footer a:hover { text-decoration: underline; }

/* ---------- Ввод кода подтверждения ---------- */
.otp-lead {
    color: var(--text-muted);
    margin-bottom: 22px;
    line-height: 1.55;
}
.otp-lead strong { color: var(--text); overflow-wrap: anywhere; }

.code-input {
    display: flex;
    gap: 10px;
    justify-content: center;
    margin-bottom: 6px;
}

.code-input input {
    width: 52px;
    height: 62px;
    text-align: center;
    font-family: var(--font-head);
    font-size: 1.6rem;
    font-weight: 700;
    color: var(--text);
    background: var(--surface-soft);
    border: 2px solid var(--border);
    border-radius: var(--radius-md);
    transition: border-color var(--transition), box-shadow var(--transition);
    caret-color: var(--accent-2);
}
.code-input input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-ring);
}
.code-input input:not(:placeholder-shown),
.code-input input[value]:not([value=""]) { border-color: var(--accent); }

#otpError { text-align: center; }

.btn-link {
    background: none;
    border: none;
    padding: 0;
    color: var(--accent-2);
    font-family: var(--font-body);
    font-size: 1rem;
    font-weight: 600;
    cursor: pointer;
    text-decoration: none;
}
.btn-link:hover:not(:disabled) { text-decoration: underline; }
.btn-link:disabled { color: var(--text-dim); cursor: not-allowed; }

.otp-timer { color: var(--text-dim); font-size: 0.9rem; }

@media (max-width: 420px) {
    .code-input { gap: 6px; }
    .code-input input { width: 44px; height: 54px; font-size: 1.35rem; }
}

/* ---------- Уведомления ---------- */
.toast-stack {
    position: fixed; top: 18px; right: 18px;
    z-index: 10000;
    display: flex; flex-direction: column; gap: 10px;
    max-width: min(360px, calc(100vw - 36px));
}

.toast {
    background: var(--surface-2);
    color: var(--text);
    padding: 13px 16px;
    border-radius: var(--radius-md);
    border: 1px solid var(--border);
    border-left: 4px solid var(--info);
    box-shadow: var(--shadow-md);
    display: flex; align-items: center; gap: 12px;
    transform: translateX(120%);
    opacity: 0;
    transition: transform var(--transition), opacity var(--transition);
}
.toast.show { transform: translateX(0); opacity: 1; }
.toast-success { border-left-color: var(--success); }
.toast-error   { border-left-color: var(--danger); }
.toast-info    { border-left-color: var(--info); }
.toast i { flex-shrink: 0; }
.toast-close {
    background: none; border: none; color: var(--text-muted);
    cursor: pointer; padding: 4px; margin-left: auto;
}
.toast-close:hover { color: var(--text); }

/* ---------- Центр уведомлений ---------- */
.notif { position: relative; }

.notif-btn { position: relative; }
.notif-btn.has-unread { color: var(--gold); }

.notif-badge {
    position: absolute;
    top: -3px; right: -3px;
    min-width: 18px; height: 18px;
    padding: 0 5px;
    border-radius: 999px;
    background: var(--danger);
    color: #fff;
    font-family: var(--font-head);
    font-size: 0.7rem;
    font-weight: 700;
    display: flex; align-items: center; justify-content: center;
    border: 2px solid var(--surface);
}

.notif-panel {
    position: absolute;
    top: calc(100% + 12px);
    right: 0;
    width: min(370px, calc(100vw - 32px));
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    z-index: 1200;
    overflow: hidden;
    animation: slide-up 0.18s ease;
}

.notif-head {
    display: flex; align-items: center; gap: 10px;
    padding: 14px 16px;
    border-bottom: 1px solid var(--border);
}
.notif-head h3 { font-size: 1rem; color: var(--text); }
.notif-count {
    background: var(--danger); color: #fff;
    border-radius: 999px; padding: 1px 9px;
    font-size: 0.78rem; font-weight: 700;
    font-family: var(--font-head);
}

.notif-tabs {
    display: flex;
    flex-wrap: wrap;          /* вкладки в узкой панели переносим, а не прячем за скролл */
    gap: 6px;
    padding: 10px 12px;
    border-bottom: 1px solid var(--border);
    background: var(--surface-soft);
    overflow-x: auto;
}
.notif-tab {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 7px 11px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface-3);
    color: var(--text-dim);
    font-family: var(--font-body);
    font-size: 0.8rem;
    font-weight: 500;
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--transition), color var(--transition), border-color var(--transition);
}
.notif-tab:hover { color: var(--text); border-color: var(--accent); }
.notif-tab[aria-selected="true"] {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--on-accent);
}
.notif-tab i { font-size: 0.78rem; }
.notif-tab .subtab-count { background: rgba(0, 0, 0, 0.18); color: inherit; }
.notif-tab[aria-selected="true"] .subtab-count { background: rgba(255, 255, 255, 0.28); }

.notif-list {
    max-height: min(60vh, 420px);
    overflow-y: auto;
}
.notif-item {
    display: grid;
    grid-template-columns: 40px minmax(0, 1fr);
    gap: 12px;
    padding: 14px 16px;
    border-bottom: 1px solid var(--border);
}
.notif-item:last-child { border-bottom: none; }

.notif-body { min-width: 0; }
.notif-text { color: var(--text); font-size: 0.92rem; line-height: 1.45; }
.notif-text strong { color: var(--accent-2); }
.notif-time { color: var(--text-dim); font-size: 0.8rem; }

.notif-actions {
    grid-column: 2;
    display: flex; gap: 8px; flex-wrap: wrap;
    margin-top: 10px;
}

.notif-section {
    padding: 10px 16px 6px;
    color: var(--text-dim);
    font-size: 0.76rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    background: var(--surface-soft);
}
.notif-item-link { text-decoration: none; align-items: center; }
.notif-item-link:hover { background: var(--accent-soft); }
.notif-item-clickable { cursor: pointer; }
.notif-item-clickable:hover { background: var(--accent-soft); }
.notif-unread {
    align-self: center;
    background: var(--danger); color: #fff;
    border-radius: 999px; padding: 1px 8px;
    font-size: 0.74rem; font-weight: 700; font-family: var(--font-head);
}
.notif-item-link { grid-template-columns: 40px minmax(0, 1fr) auto; }

.notif-empty { padding: 34px 16px; text-align: center; color: var(--text-dim); }
.notif-empty i { font-size: 1.6rem; color: var(--accent-2); display: block; margin-bottom: 10px; }

/* Иконка разового события (подписчик, пополнение, отмена, премиум) —
   занимает то же место в сетке, что и аватар у сообщений/заявок. */
.notif-event-icon {
    width: 40px; height: 40px;
    border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    background: var(--accent-soft);
    color: var(--accent-2);
    font-size: 1.05rem;
    flex-shrink: 0;
}
.notif-item-new { background: var(--accent-soft); }

/* В подвале теперь две ссылки: переключатель «показать прочитанные» и
   переход к друзьям, — поэтому строка, а не центрированный текст. */
.notif-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    padding: 12px 16px;
    border-top: 1px solid var(--border);
    background: var(--surface-soft);
}
.notif-foot .btn-link { background: none; border: none; cursor: pointer; font: inherit; padding: 0; }

/* Телефоны и планшеты: положение задаёт placePanel() в js/friends.js. */
.notif-panel.notif-panel-fixed {
    position: fixed; right: 12px; left: 12px; width: auto;
    overflow-y: auto; overscroll-behavior: contain;
    z-index: 1200;
}

@media (max-width: 480px) {
    /* top: 66px было фиксированным числом — угадывало высоту шапки
       ровно для одного случая (мелкие кнопки, без скругления). Стоило
       шапке подрасти (крупнее аватар/кнопки на этой ширине, ниже в
       этом же файле) или скруглиться (см. .header выше) — панель
       начиналась ВЫШЕ настоящего низа шапки и наезжала на неё углом.
       --site-header-h — та же переменная, что меряет всегда точную
       высоту шапки (trackHeaderHeight() в js/layout.js) и уже
       используется в css/pages.css для высоты чата — здесь работает
       по тому же принципу: панель начинается ровно под шапкой, какой
       бы высоты та ни была. */
    .notif-panel { position: fixed; top: calc(var(--site-header-h, 84px) + 8px); right: 12px; left: 12px; width: auto; }
}

/* ---------- Мобильное меню ---------- */
.mobile-menu {
    position: fixed; inset: 0;
    background: var(--bg);
    z-index: 1500;
    padding: 84px 20px 20px;
    transform: translateX(-100%);
    transition: transform var(--transition);
    overflow-y: auto;
    visibility: hidden;
}
.mobile-menu.active { transform: translateX(0); visibility: visible; }
.mobile-menu .nav-list { flex-direction: column; gap: 8px; }
.mobile-menu .nav-link { padding: 14px; font-size: 1.05rem; justify-content: flex-start; }
.mobile-menu-close {
    position: absolute; top: 18px; right: 18px;
}

/* ---------- Анимация появления ---------- */
.reveal { opacity: 0; transform: translateY(24px) scale(0.985); }
.reveal.is-visible { animation: reveal-up 0.55s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
@keyframes reveal-up { to { opacity: 1; transform: translateY(0) scale(1); } }

/* ---------- Адаптивность ---------- */
@media (max-width: 1240px) {
    .mobile-menu-btn { display: inline-flex; }
    .main-nav { display: none; }
    .header-content { justify-content: space-between; }
    .mobile-menu .nav-list { flex-wrap: wrap; }
    .mobile-menu .nav-link { font-size: 1.05rem; }
}

/* ============================================================
   Планшетная версия (769–1240px — burger-меню и «...» уже активны
   с 1240px, а настоящая телефонная раскладка начинается только с
   768px, см. блок ниже): убираем часть пунктов бургер-меню и
   разворачиваем «...» в шапке в отдельные кнопки.
   ============================================================ */
@media (min-width: 769px) and (max-width: 1240px) {
    /* ---------- Бургер-меню: реже используемые разделы прячем,
       остальные кнопки делаем светлее — не так тяжело смотрятся,
       когда их меньше. data-key проставляет js/layout.js navMarkup(). */
    .mobile-menu .nav-item[data-key="season"],
    .mobile-menu .nav-item[data-key="pass"],
    .mobile-menu .nav-item[data-key="teams"],
    .mobile-menu .nav-item[data-key="feed"],
    .mobile-menu .nav-item[data-key="clips"],
    .mobile-menu .nav-item[data-key="deals"],
    .mobile-menu .nav-item[data-key="updates"] {
        display: none;
    }
    .mobile-menu .nav-link {
        background: var(--surface-2);
        color: var(--text);
    }

    /* ---------- Шапка: «...» разворачивается в ряд отдельных кнопок ----------
       Пункты (Dantes Pass/Поиск/Dantes AI) физически остаются внутри
       того же #headerMorePanel (его достраивают js/dantes-pass.js,
       js/search.js, js/dantes-ai.js — переписывать их не нужно), но на
       планшете сама панель перестаёт быть скрывающимся поповером и
       превращается в обычный ряд кнопок в шапке, а кнопка «⋮» пропадает
       — открывать уже нечего. Колокольчик (js/friends.js) — свой значок
       вне #headerMorePanel, класс header-more-btn делит с «⋮» только
       ради одинакового размера (см. mountBell) — на планшете скрывать
       его незачем, он и так не поповер, а :not() исключает его из
       общего display:none. */
    .header-more-btn:not(.notif-btn) { display: none; }
    .header-more-panel {
        position: static;
        display: flex;
        flex-direction: row;
        align-items: center;
        gap: 4px;
        background: none;
        border: none;
        box-shadow: none;
        padding: 0;
        min-width: 0;
        animation: none;
    }
    /* Панель раньше пряталась атрибутом hidden до первого открытия —
       на планшете она нужна открытой всегда. !important обязателен:
       глобальное [hidden] { display: none !important; } (в начале
       этого файла) иначе перебивает это правило, сколько его сюда ни
       переставляй — атрибут hidden у панели никуда не девается, кнопка
       «⋮», которая раньше его снимала, на планшете скрыта. */
    .header-more-panel[hidden] { display: flex !important; }
    /* Крупнее — почти как аватар профиля (54px на этой ширине, .btn-profile-avatar
       в этом же файле): были маленькими квадратиками, заметно мельче
       остальных кнопок шапки. */
    .header-more-item {
        width: 46px;
        height: 46px;
        padding: 0;
        justify-content: center;
        border-radius: 50%;
        font-size: 1.05rem;
    }
    .header-more-item i { width: auto; font-size: 1.15rem; }
    /* Подпись («Dantes Pass», «Поиск» и т.д.) убираем — остаётся только
       иконка, как у любой другой кнопки шапки; сам текст остаётся в
       DOM для скринридеров и всплывающей подсказки (title/aria-label
       уже расставлены в разметке кнопок). Бейдж (точка Dantes Pass) —
       не обычная подпись, у него есть свой класс, поэтому :not([class])
       его не трогает. */
    .header-more-item > span:not([class]) { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); }

    /* ---------- Подвал: «Разделы» — 2 колонки по 4, а не один
       столбик из восьми строк (footer-section-links, js/layout.js
       renderFooter — тот же список, что порядком не меняется). ---------- */
    .footer-section-links ul {
        display: grid;
        grid-template-columns: repeat(2, 1fr);
        column-gap: 18px;
        row-gap: 9px;
    }
    .footer-section-links ul li { margin-bottom: 0; }
}

@media (max-width: 768px) {
    /* Верх страницы — заметно ближе к шапке: было 40px, глазу тут делать
       нечего, а до первых плашек с информацией приходилось долистывать.
       Низ (перед подвалом) не трогаем — там воздух настоящий, свёрстан
       под конец страницы, а не под шапку. */
    .main-content { padding: 20px 0 40px; }
    .home-banner-slides { height: clamp(220px, 60vw, 320px); }
    .home-banner-arrow { width: 34px; height: 34px; }

    /* Те же реже используемые разделы, что уже прячутся из бургер-меню
       на планшете (769–1240px, блок выше) — по просьбе убираем их и
       на телефоне, тем же списком data-key. */
    .mobile-menu .nav-item[data-key="season"],
    .mobile-menu .nav-item[data-key="pass"],
    .mobile-menu .nav-item[data-key="teams"],
    .mobile-menu .nav-item[data-key="feed"],
    .mobile-menu .nav-item[data-key="clips"],
    .mobile-menu .nav-item[data-key="deals"],
    .mobile-menu .nav-item[data-key="updates"] {
        display: none;
    }

    /* Шапка на телефоне — во всю ширину экрана, без бокового отступа:
       раньше margin: 12px 16px 0 у .header (см. правило выше) делал её
       на 32px уже плашек с информацией под ней (.container/.pulse
       используют только padding, без своего margin) — рамки не
       совпадали. Заодно освобождает те же 32px под кнопки и аватарку,
       которые ниже сделаны крупнее.

       Скругление (border-radius) раньше тоже снимали здесь целиком —
       по просьбе владельца сайта возвращаем: сверху у шапки уже есть
       12px отступа (margin-top), поэтому углы видно и при полной
       ширине, ничего не «съедается» краем экрана. */
    .header { margin: 12px 0 0; border-radius: var(--radius-lg); }

    /* Без плашки и ника ограничивать ширину больше нечего — осталась
       одна аватарка, крупнее прежней ровно настолько, насколько
       позволило освободившееся место. */
    .btn-profile-avatar { width: 48px; height: 48px; font-size: 1.05rem; border-width: 2px; }
    .footer { padding: 40px 0 20px; }
}

/* Колокольчик занял место в шапке — на узких экранах прячем текст логотипа
   в футере (там ещё есть иконка-замена); в хедере иконки больше нет
   (см. renderHeader в layout.js), поэтому там текст остаётся видимым всегда. */
@media (max-width: 430px) {
    .footer .logo span { display: none; }
    .footer .logo i { font-size: 1.5rem; }
}

@media (max-width: 560px) {
    .logo { font-size: 1.1rem; gap: 7px; }
    .logo i { font-size: 1.2rem; }
    .header-content { gap: 8px; }
    .header-actions { gap: 6px; }
}

@media (max-width: 480px) {
    .container { padding: 0 14px; }
    /* Баннер лежит вне .container и повторяет его поля вручную — здесь
       они сузились до 14px, значит и у баннера тоже, иначе он окажется
       уже разделов под ним. */
    .home-banner { width: calc(100% - 28px); }
    .header-actions .btn { padding: 9px 11px; }
    .btn-profile { max-width: 44px; }
    .tournament-grid, .stream-grid { grid-template-columns: 1fr; }
    .footer-content { grid-template-columns: 1fr; gap: 26px; }
    .header-actions .btn span { display: none; }
    .modal-body { padding: 18px; }

    /* Кнопки в шапке ужимаем ИМЕННО этим селектором, в две ступени
       вложенности. Правило выше (.header-actions .btn-icon — 50px)
       специфичнее одиночного .btn-icon, поэтому прежняя строчка
       «.btn-icon, .theme-toggle { width: 36px }» здесь не срабатывала:
       кнопки оставались десктопными, ряд не влезал в 390px и аватарка
       уезжала за правый край экрана. */
    .header-actions .btn-icon,
    .header-actions .theme-toggle {
        width: 44px; height: 44px; font-size: 1.05rem;
    }
    .header-actions .btn-profile-avatar {
        width: 44px; height: 44px; font-size: 1rem; border-width: 2px;
    }
    .header-actions { gap: 5px; }
    .mobile-menu-btn { width: 44px; height: 44px; }

    /* Логотип уступает место кнопкам, а не выталкивает их за экран. */
    .header-content { gap: 6px; min-width: 0; }
    .logo { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}

/* Календарь переехал сюда из pages.css: кнопка стоит в шапке, а шапка
   есть на каждой странице — включая главную, где pages.css не подключён. */

/* ============================================================
   Календарь ежедневных призов
   ------------------------------------------------------------
   Летняя палитра: закат над морем. Она своя, не из переменных
   темы, — календарь должен выглядеть одинаково и в светлой,
   и в тёмной теме, как отдельная праздничная штука.
   ============================================================ */
/* ============================================================
   Dantes Pass — лента наград вместо календаря.

   Устроено как пропуска в играх: две дорожки идут сверху вниз, между
   ними по центру рельс с номерами уровней, закрашенный до сегодняшнего
   дня. Левая дорожка золотая (подписка), правая бесплатная, подписи
   колонок стоят над лентой и никуда не уезжают при прокрутке.

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

   Окно занимает 80% высоты экрана: лента в нём одна, ей нужна высота
   под десяток уровней сразу, но не весь экран — фон сайта должен
   оставаться виден, иначе это уже не окно, а страница.
   ============================================================ */
/* Точка «есть что забрать» — теперь внутри пункта меню «⋮», а не на
   отдельной круглой кнопке (см. js/dantes-pass.js), поэтому просто
   пристёгнута справа в строке, как .notif-badge у соседнего пункта
   «Уведомления», а не висит поверх значка абсолютным позиционированием. */
.dpass-dot {
    width: 8px; height: 8px; border-radius: 50%;
    background: #ff5a5f;
    box-shadow: 0 0 0 2px var(--surface);
    animation: dpass-pulse 1.6s ease-in-out infinite;
    flex-shrink: 0;
}
.header-more-item .dpass-dot { margin-left: auto; }
@keyframes dpass-pulse { 50% { transform: scale(0.7); opacity: 0.65; } }

/* ---------- Окно ---------- */
.dpass-back {
    position: fixed;
    inset: 0;
    z-index: 1200;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
    background: rgba(4, 4, 18, 0.72);
    backdrop-filter: blur(3px);
}
body.dpass-open { overflow: hidden; }

.dpass-window {
    position: relative;
    display: flex;
    flex-direction: column;
    width: min(1040px, 100%);
    height: 80vh;
    overflow: hidden;
    border-radius: 18px;
    border: 1px solid rgba(202, 138, 74, 0.45);

    /* Разметка ленты живёт в переменных: шапка колонок и сами уровни —
       два разных элемента (подписи не должны уезжать при прокрутке), а
       колонки у них обязаны совпадать до пикселя. Одно место правки. */
    --dpass-cols: 1fr 56px 1fr;
    --dpass-colgap: 12px;
    --dpass-pad: 16px;

    /* Осенние цвета окна — тоже переменными, чтобы плитки, узлы и
       подписи брали их из одного места. */
    --dpass-ink: #4a3421;            /* основной текст */
    --dpass-ink-soft: rgba(74, 52, 33, 0.66);
    --dpass-line: rgba(180, 120, 60, 0.26);
    --dpass-amber: #e07b20;          /* жжёный апельсин: «пройдено» */
    --dpass-amber-deep: #a8410f;     /* заголовки и акценты */

    /* Фон ставит администратор; светлая заливка ниже — запасная и она
       же вуаль поверх картинки. Вуаль держим лёгкой (20% непрозрачности),
       чтобы картинка админа была видна на 80% и календарь читался живее —
       контраст текста теперь берут на себя не эта заливка, а более жирный
       шрифт и собственный тёмный фон у каждой плашки награды ниже. */
    background:
        linear-gradient(180deg, rgba(255, 247, 236, 0.2), rgba(250, 232, 208, 0.2)),
        var(--dpass-bg, none) center / cover no-repeat,
        radial-gradient(circle at 12% 16%, rgba(244, 162, 97, 0.26), transparent 42%),
        radial-gradient(circle at 88% 6%, rgba(214, 120, 60, 0.2), transparent 40%),
        radial-gradient(circle at 78% 92%, rgba(191, 139, 62, 0.18), transparent 45%),
        linear-gradient(170deg, #fff7ec 0%, #ffeed8 45%, #f7dcb8 100%);
    box-shadow: 0 30px 80px rgba(70, 40, 12, 0.45);
    color: var(--dpass-ink);
    /* Весь текст ленты — жирнее по умолчанию; элементы со своим
       font-weight (заголовки, узлы, подписи колонок) его не теряют,
       это правило подхватывают только те, что раньше были обычной
       гарнитуры. */
    font-weight: 600;
}

/* ---------- Шапка ---------- */
.dpass-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px;
    padding: 14px var(--dpass-pad);
    border-bottom: 1px solid var(--dpass-line);
    flex-shrink: 0;
    /* Своя почти непрозрачная подложка: фон окна теперь показывает
       картинку админа на 80%, а заголовок и таймер должны читаться
       ЛЮБОЙ картинкой позади — не только этой. */
    background: #fff7ec;
}
.dpass-title { display: flex; align-items: center; gap: 12px; min-width: 0; }
.dpass-title b {
    display: block;
    font-family: var(--font-head);
    font-size: 1.15rem;
    letter-spacing: 0.02em;
    color: var(--dpass-amber-deep);
}
/* Обратный отсчёт до конца сезона — не «осталось 15 дней», а точное
   время до полуночи первого числа, с минутами (см. countdownText()
   в js/dantes-pass.js). Цифры меняются каждую секунду, поэтому строка
   не должна прыгать по ширине: моноширинные цифры. */
.dpass-title small {
    display: block;
    font-size: 0.76rem;
    color: var(--dpass-ink-soft);
    font-variant-numeric: tabular-nums;
}
.dpass-clock-tail[hidden] { display: none; }
.dpass-logo {
    display: flex; align-items: center; justify-content: center;
    width: 40px; height: 40px;
    border-radius: 12px;
    background: linear-gradient(160deg, #f6a43d, #d2691e);
    color: #fff8ee;
    font-size: 1.1rem;
    flex-shrink: 0;
    box-shadow: 0 6px 16px rgba(190, 95, 25, 0.28);
}
.dpass-head-actions { display: flex; align-items: center; gap: 4px; flex-shrink: 0; }
.dpass-head .btn-icon { color: rgba(74, 52, 33, 0.7); border-color: transparent; }
.dpass-head .btn-icon:hover { color: #3a2612; background: rgba(180, 120, 60, 0.16); }

/* ---------- Строка состояния ----------
   Строки «Уровень X из Y» и «Забрано: …» убраны целиком — тот же путь
   уже рисует сама лента (закрашенный рельс до сегодняшнего узла), и
   пользователю эта дублирующая сводка мешала. Обёртка осталась: в ней
   по-прежнему может стоять стрик (см. streakHtml() в js/dantes-pass.js). */
.dpass-progress {
    padding: 10px var(--dpass-pad);
    flex-shrink: 0;
    /* Та же подложка, что у шапки и подписей колонок: стрик — обычный
       текст без своей карточки, ему тоже нужен гарантированный фон под
       буквами, а не фон окна, который меняется от картинки админа. */
    background: #fff7ec;
    border-bottom: 1px solid var(--dpass-line);
}

.dpass-streak {
    display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
    margin-top: 6px; font-size: 0.76rem; color: var(--dpass-ink-soft);
}
.dpass-streak i.fa-fire { color: #ff7a3d; }
.dpass-streak b { color: var(--dpass-amber-deep); }
.dpass-streak small { color: var(--dpass-ink-soft); opacity: 0.85; }
.dpass-streak small i.fa-coins { color: var(--gold, #ffd93d); }

/* ---------- Тело: подписи колонок и вертикальная лента ----------
   Уровни идут сверху вниз: одна CSS-сетка на весь список, три колонки
   (золотая награда / номер уровня / бесплатная награда) — рельс с
   номерами проходит ровно посередине, между двумя дорожками наград.
   Обёртки строк (.dpass-row) сами ничего не рисуют (display:contents) —
   их дети встают прямо в сетку .dpass-track на нужные места.

   Подписи колонок — отдельная полоска НАД прокруткой, а не первая
   строка ленты: они не должны уезжать вверх вместе с уровнями. Чтобы
   колонки у полоски и у ленты совпадали, у обеих одна и та же сетка и
   одинаковые боковые отступы (переменные на .dpass-window). Полоса
   прокрутки — тонкая и полупрозрачная, как везде на сайте; колесо,
   тачпад, клавиши и перетаскивание работают как раньше. */
.dpass-body {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    padding: 0 0 8px;
}
.dpass-colhead {
    display: grid;
    grid-template-columns: var(--dpass-cols);
    column-gap: var(--dpass-colgap);
    padding: 10px var(--dpass-pad) 8px;
    flex-shrink: 0;
    border-bottom: 1px solid var(--dpass-line);
    /* Подписи «Premium»/«Бесплатно» тоже читаются текстом прямо на фоне
       окна — своя подложка нужна им так же, как шапке. */
    background: #fff7ec;
}
.dpass-colname {
    display: flex; align-items: center; justify-content: center; gap: 6px;
    font-family: var(--font-head);
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
.dpass-colname.is-gold { color: var(--dpass-amber-deep); }
.dpass-colname.is-free { color: rgba(74, 52, 33, 0.82); }

.dpass-rail {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 10px var(--dpass-pad) 14px;


    overscroll-behavior: contain;
}
.dpass-track {
    display: grid;
    grid-template-columns: var(--dpass-cols);
    column-gap: var(--dpass-colgap);
    row-gap: 10px;
    grid-auto-rows: minmax(76px, auto);
    align-items: center;
}

.dpass-row { display: contents; }

/* Сегодняшний уровень подсвечен по обеим наградам строки: в ленте из
   тридцати строк «где я сейчас» должно быть видно раньше, чем прочитан
   номер. display:contents не даёт красить строку целиком одним фоном —
   красим оба .dpass-tile этой строки по отдельности через потомковый
   селектор, он работает сквозь contents как обычно. Кольцо теперь на
   самой монете, а не рамкой вокруг всей строки — плашек с рамками
   на дорожках больше нет ни у одного состояния. */
.dpass-row.is-today .dpass-coin,
.dpass-row.is-today .dpass-box-icon,
.dpass-row.is-today .dpass-tile-icon > i.fas {
    box-shadow:
        0 0 0 2px rgba(210, 85, 27, 0.45),
        0 2px 4px rgba(90, 50, 5, 0.4),
        inset 0 2px 3px rgba(255, 255, 255, 0.55),
        inset 0 -2px 3px rgba(90, 50, 5, 0.3);
}

/* ---------- Узел рельса ----------
   Номер уровня — главная цифра ленты, по ней отсчитывают, где ты
   сейчас. Кружок крупный (38px), цифра тёмная на светлом и белая на
   залитых — контраст в обе стороны, а не «золотое по золотому», как
   было в тёмной версии, где цифру приходилось выискивать. */
.dpass-node {
    position: relative;
    display: flex; align-items: center; justify-content: center;
    width: 38px; height: 38px;
    border-radius: 50%;
    background: #fffaf2;
    border: 2px solid #c79a62;
    font-family: var(--font-head);
    font-size: 0.95rem;
    font-weight: 700;
    line-height: 1;
    color: #6b4522;
    z-index: 1;
    justify-self: center;
    align-self: center;
    box-shadow: 0 2px 6px rgba(120, 70, 20, 0.14);
}
/* Рельс между узлами — вертикальная линия через сам узел вверх и вниз,
   с запасом: строки бывают разной высоты (крупная награда последнего
   уровня), а линия всё равно должна доходить до соседних узлов без
   разрывов. */
.dpass-node::before {
    content: '';
    position: absolute;
    top: -60px; bottom: -60px;
    left: 50%;
    width: 5px;
    transform: translateX(-50%);
    background: #d9b98f;
    z-index: -1;
    border-radius: 999px;
}
/* У первого и последнего уровня рельс обрывается ровно на кружке:
   иначе линия торчала бы в пустоту над первой и под последней
   наградой — на светлом фоне это особенно заметно. */
.dpass-row:first-child .dpass-node::before { top: 50%; }
.dpass-row:last-child .dpass-node::before { bottom: 50%; }

.dpass-node.is-passed {
    border-color: #fff0d8;
    color: #fff;
    background: linear-gradient(160deg, #f6a43d, #e07b20);
    text-shadow: 0 1px 2px rgba(120, 55, 10, 0.45);
}
.dpass-node.is-passed::before { background: linear-gradient(180deg, #e07b20, #f6a43d); }
.dpass-node.is-now {
    background: linear-gradient(160deg, #e8641b, #c2410c);
    color: #fff;
    border-color: #fff;
    box-shadow: 0 0 0 5px rgba(210, 85, 27, 0.2), 0 4px 10px rgba(150, 60, 10, 0.3);
}

/* ---------- Плитка награды ----------
   Плашка — карточка с почти сплошным фоном: не полупрозрачная тонировка
   поверх картинки окна, а фон, который сам держит контраст с тёмным
   текстом независимо от того, что сейчас стоит фоном (дефолтная осень,
   своя картинка админа, светлая она или тёмная). Рамка вокруг — чётко
   видимая. Монета внутри та же, что и везде на сайте у баланса Dantes
   Coins, только теперь она несёт ЛЮБУЮ награду ленты (не только
   монеты). Уже собранная награда (.is-done ниже) — единственная со
   светлым фоном, остальные — тёплым тёмно-охристым: это и есть сигнал
   «готово», без дополнительного затемнения непрозрачностью. */
.dpass-tile {
    position: relative;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    gap: 10px;
    box-sizing: border-box;
    width: 100%;
    min-height: 62px;
    padding: 8px 10px;
    border-radius: 12px;
    border: 1.5px solid rgba(122, 74, 10, 0.6);
    /* Почти без прозрачности: тёплый охристый тон держит тёмный текст
       читаемым, даже если под окном — тёмная или пёстрая картинка. */
    background: #c9a877;
    box-shadow: none;
    color: var(--dpass-ink);
    font-family: inherit;
    text-align: left;
    line-height: 1.15;
}
.dpass-tile.is-empty { visibility: hidden; border-color: transparent; background: none; }
/* Монета — общий контейнер под ЛЮБУЮ иконку награды (шрифтовая иконка,
   буква «D», картинка сундука): им задаём только размер, а сам
   металлический вид и обводка — на самой .dpass-coin ниже, чтобы
   дорожка монет и монета баланса были одной и той же деталью
   интерфейса, а не двумя разными рисованными кружками. */
.dpass-tile-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.4rem;
    line-height: 1;
    flex-shrink: 0;
}
.dpass-tile-name {
    font-size: 0.82rem;
    font-weight: 700;
    color: rgba(74, 52, 33, 0.9);
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow-wrap: anywhere;
}

/* Золотую дорожку от бесплатной, помимо цвета рамки плашки, отличает
   ещё и обод монеты: у золотой он ярче и чуть толще. */
.dpass-tile.is-gold .dpass-coin,
.dpass-tile.is-gold .dpass-box-icon {
    box-shadow:
        0 0 0 2px rgba(246, 164, 61, 0.55),
        0 2px 4px rgba(90, 50, 5, 0.4),
        inset 0 2px 3px rgba(255, 255, 255, 0.55),
        inset 0 -2px 3px rgba(90, 50, 5, 0.3);
}

/* Готово к получению — единственное состояние, на которое можно нажать,
   и единственное, которое двигается; свечение — на самой монете, поверх
   обычной плашки состояния. */
.dpass-tile.is-ready {
    cursor: pointer;
}
.dpass-tile.is-ready .dpass-tile-name { color: var(--dpass-amber-deep); font-weight: 700; }
.dpass-tile.is-ready .dpass-coin,
.dpass-tile.is-ready .dpass-box-icon {
    animation: dpass-ready 1.8s ease-in-out infinite;
}
@keyframes dpass-ready {
    0%, 100% { box-shadow: 0 0 0 3px rgba(224, 123, 32, 0.3), 0 2px 4px rgba(90, 50, 5, 0.4); }
    50% { box-shadow: 0 0 0 8px rgba(224, 123, 32, 0.12), 0 2px 4px rgba(90, 50, 5, 0.4); }
}
.dpass-tile.is-ready:hover { transform: translateY(-2px); }
.dpass-tile.is-ready:active { transform: translateY(0); }
.dpass-tile.is-ready:focus-visible { outline: 2px solid var(--dpass-amber); outline-offset: 2px; border-radius: 8px; }
.dpass-tile.is-busy { cursor: progress; animation: none; filter: brightness(.92); }

/* Получено — плашка светлее остальных (и тоже почти без прозрачности),
   это и есть сигнал «готово», без дополнительного затемнения. Заперто/
   пропущено — наоборот, обычная плашка из базового правила притушена
   непрозрачностью, а сама монета ещё и обесцвечена, плюс значок-
   галочка/замок в углу. */
.dpass-tile.is-done {
    opacity: 1;
    background: #fff6e6;
    border-color: #e07b20;
}
.dpass-tile.is-locked, .dpass-tile.is-shut, .dpass-tile.is-missed {
    /* без прозрачности: «закрыто» показывает сплошной приглушённый тон */
    opacity: 1;
    background: #b9a58a;
    border-color: #8a6a3a;
}
.dpass-tile.is-locked .dpass-coin, .dpass-tile.is-shut .dpass-coin, .dpass-tile.is-missed .dpass-coin,
.dpass-tile.is-locked .dpass-box-icon, .dpass-tile.is-shut .dpass-box-icon, .dpass-tile.is-missed .dpass-box-icon {
    filter: grayscale(0.6);
}

.dpass-check, .dpass-lock {
    position: absolute;
    top: -4px; right: -4px;
    width: 22px; height: 22px;
    display: flex; align-items: center; justify-content: center;
    border-radius: 50%;
    font-size: 0.62rem;
    box-shadow: 0 1px 4px rgba(90, 55, 15, 0.2);
}
.dpass-check { background: #3f8f52; color: #fff; }
.dpass-lock { background: #e8d7be; color: #7a5333; }

/* Значок «сверху ещё и Осенний бокс» — дни 5/15/25 золотой дорожки
   (см. tileHtml() в js/dantes-pass.js, reward.bonus_box_key). Левый
   верхний угол: check/lock уже держат правый, и, в отличие от них,
   этот значок не зависит от статуса — виден всегда, пока награда
   дня не забрана насовсем. */
.dpass-bonus-box {
    position: absolute;
    top: -9px; left: 8px;
    height: 20px; padding: 0 8px; gap: 4px;
    border: 2px solid #fff;
    display: flex; align-items: center; justify-content: center;
    border-radius: 999px;
    font-size: 0.62rem; font-weight: 800; white-space: nowrap;
    background: linear-gradient(135deg, #e07b20, #ffb347);
    color: #fff;
    box-shadow: 0 1px 4px rgba(90, 55, 15, 0.25);
}
.dpass-tile.is-done .dpass-bonus-box { filter: grayscale(.4); }

/* Крупная награда последнего уровня — крупнее монета и подпись, сияние
   тоже на монете: ради неё и листают ленту до конца. */
.dpass-tile.is-big {
    min-height: 84px;
}
.dpass-tile.is-big .dpass-tile-icon { font-size: 1.9rem; }
.dpass-tile.is-big .dpass-tile-name { font-size: 0.88rem; }
.dpass-tile.is-big .dpass-coin,
.dpass-tile.is-big .dpass-box-icon {
    box-shadow: 0 0 16px rgba(224, 123, 32, 0.5), 0 2px 4px rgba(90, 50, 5, 0.4);
}

/* ---------- Монета ----------
   Один и тот же металлический кружок несёт ЛЮБУЮ награду ленты — не
   только монеты. Шрифтовая иконка (fa-gift, fa-shield-halved…), буква
   «D», картинка сундука и образец косметики садятся на неё одинаково:
   класс .dpass-coin даёт форму, обод и объём, происхождение картинки
   внутри роли не играет. Тот же приём, что и у .dc-coin (шапка,
   кошелёк) — здесь просто отдельная копия под размеры ленты. */
.dpass-coin {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.1em;
    height: 2.1em;
    flex-shrink: 0;
    border-radius: 50%;
    background:
        radial-gradient(circle at 30% 22%, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0) 24%),
        radial-gradient(circle at 34% 30%, #fff7d6 0%, #ffe17a 20%, #ffc94a 42%, #e2982a 66%, #b06d12 88%, #8a4f0a 100%);
    border: 2px solid #7a4a0a;
    box-shadow:
        0 2px 4px rgba(90, 50, 5, 0.4),
        inset 0 2px 3px rgba(255, 255, 255, 0.55),
        inset 0 -2px 3px rgba(90, 50, 5, 0.3);
    color: #4a3200;
    font-family: var(--font-head);
    font-weight: 700;
    font-size: 0.62em;
}
.dpass-tile-icon > i.fas {
    /* Одиночная шрифтовая иконка (не .dpass-coin/img) тоже садится на
       монету — тот же кружок, просто без обёртки в разметке. Именно
       этот кружок теперь несёт награды-косметику (рамка/фон профиля):
       вместо превью самого предмета — нейтральная иконка на монете. */
    display: flex; align-items: center; justify-content: center;
    width: 2.1em; height: 2.1em; flex-shrink: 0;
    border-radius: 50%;
    background:
        radial-gradient(circle at 30% 22%, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0) 24%),
        radial-gradient(circle at 34% 30%, #fff7d6 0%, #ffe17a 20%, #ffc94a 42%, #e2982a 66%, #b06d12 88%, #8a4f0a 100%);
    border: 2px solid #7a4a0a;
    box-shadow:
        0 2px 4px rgba(90, 50, 5, 0.4),
        inset 0 2px 3px rgba(255, 255, 255, 0.55),
        inset 0 -2px 3px rgba(90, 50, 5, 0.3);
    color: #4a3200;
    font-size: 0.62em;
}

/* Иконка «Осеннего бокса» — картинка сундука на той же монете: кружок
   тем же металлическим фоном, сундук по центру чуть меньше диаметра. */
.dpass-box-icon {
    /* Ящик — без кружка вокруг: просто крупная картинка */
    width: 3.6em; height: 3.6em;
    object-fit: contain;
    flex-shrink: 0;
    filter: drop-shadow(0 3px 4px rgba(74, 52, 33, 0.45));
}
.dpass-tile.is-box { min-height: 84px; }
.dpass-tile.is-box .dpass-tile-icon { font-size: 1.3rem; }
.dpass-tile.is-box.is-big .dpass-box-icon { width: 4.4em; height: 4.4em; }
.dpass-tile.is-box .dpass-box-icon,
.dpass-tile.is-box.is-ready .dpass-box-icon { box-shadow: none; animation: none; }
.dpass-tile.is-box.is-ready .dpass-box-icon { filter: drop-shadow(0 0 8px rgba(224,123,32,.85)); }
.dpass-tile.is-locked .dpass-box-icon, .dpass-tile.is-shut .dpass-box-icon, .dpass-tile.is-missed .dpass-box-icon { filter: grayscale(.6) drop-shadow(0 3px 4px rgba(74,52,33,.35)); }

/* Своя шапка пасса (картинку ставит админ) */
.dpass-window.has-head-img .dpass-head {
    background: linear-gradient(90deg, rgba(255,247,236,.82), rgba(255,247,236,.35)), var(--dpass-head-bg) center / cover no-repeat;
    background-color: #fff7ec;
}


/* ---------- Подвал ----------
   Поясняющей подписи под лентой больше нет: правила («бесплатная
   сгорает в тот же день, золотая ждёт до конца месяца») и так видны
   по состояниям плиток, а лишняя строка съедала высоту ленты. */
.dpass-unlock {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px;
    margin: 0 var(--dpass-pad) 12px;
    padding: 10px 14px;
    border-radius: 12px;
    border: 1px solid rgba(214, 120, 40, 0.45);
    /* Была полупрозрачной поверх фона окна — на светлой картинке
       сливалась, текст было не прочитать. Теперь почти сплошная. */
    background: linear-gradient(90deg, #f6c98a, #fffaf0);
    flex-shrink: 0;
}
.dpass-unlock b { display: block; color: var(--dpass-amber-deep); font-size: 0.92rem; }
.dpass-unlock small { display: block; font-size: 0.76rem; color: rgba(74, 52, 33, 0.72); }
.dpass-loading, .dpass-empty {
    padding: 40px 24px; text-align: center; color: rgba(74, 52, 33, 0.75);
}

@media (max-width: 720px) {
    .dpass-back { padding: 8px; }
    .dpass-window {
        height: 80vh; border-radius: 14px;
        --dpass-cols: 1fr 44px 1fr;
        --dpass-colgap: 7px;
        --dpass-pad: 10px;
    }
    /* На телефоне в шапке остаётся только само время: пояснение
       «потом уровни начнутся заново» занимало две строки из трёх. */
    .dpass-clock-tail { display: none; }
    /* Вертикальная лента и на телефоне остаётся вертикальной — колонки
       просто уже. Подписи «Premium»/«Бесплатно» стоят над лентой, места
       им хватает даже на узком экране. */
    .dpass-track { row-gap: 8px; grid-auto-rows: minmax(64px, auto); }
    .dpass-colname { font-size: 0.7rem; }
    .dpass-node { width: 32px; height: 32px; font-size: 0.82rem; }
    .dpass-tile { padding: 7px 9px; gap: 8px; min-height: 56px; }
    .dpass-tile-icon { font-size: 1.2rem; }
    .dpass-tile-name { font-size: 0.74rem; }
    .dpass-unlock { flex-direction: column; align-items: stretch; text-align: center; }
}

/* ------------------------------------------------------------
   Монета Dantes Coins — золотой значок с буквой «D». Заменяет
   generic-иконку fa-coins везде, где речь именно об этой валюте
   (не о рублях). Размеры — в em от родителя, поэтому один и тот
   же класс одинаково смотрится в карточке статистики, в кошельке
   и в клетке календаря без отдельных настроек под каждое место.
   ------------------------------------------------------------ */
.dc-coin {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* font-size (0.52em) resolves against the PARENT's font-size, but
       width/height (em, below) resolve against THIS element's own —
       already-shrunk — font-size. 1.92 ≈ 1 / 0.52, so the visible
       diameter still comes out to ~1 line of the surrounding text,
       with the "D" sitting at the same 52%-of-diameter proportion
       everywhere the badge is dropped in. */
    width: 1.92em;
    height: 1.92em;
    flex: none;
    border-radius: 50%;
    /* Две тени внутрь (светлая сверху, тёмная снизу) плюс блик —
       вместо плоского кружка выходит выпуклая монета с металлическим
       отливом, как и просили: «более настоящей». */
    background:
        radial-gradient(circle at 30% 22%, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0) 24%),
        radial-gradient(circle at 34% 30%, #fff7d6 0%, #ffe17a 20%, #ffc94a 42%, #e2982a 66%, #b06d12 88%, #8a4f0a 100%);
    border: 0.1em solid #7a4a0a;
    box-shadow:
        0 0.12em 0.22em rgba(90, 50, 5, 0.55),
        inset 0 0.14em 0.16em rgba(255, 255, 255, 0.55),
        inset 0 -0.16em 0.18em rgba(90, 50, 5, 0.55),
        inset 0 0 0 0.1em rgba(255, 222, 140, 0.5);
    color: #5c3406;
    font-family: var(--font-head, inherit);
    font-weight: 800;
    font-size: 0.52em;
    line-height: 1;
    text-shadow: 0 0.06em 0 rgba(255, 236, 180, 0.65), 0 -0.05em 0.03em rgba(90, 50, 5, 0.55);
    vertical-align: middle;
}

/* ============================================================
   Баннер «сайт не может связаться с сервером» (js/config.js).
   Показывается, когда запрос не дошёл до сервера вообще: провайдер,
   блокировщик, расширение. Стоит в style.css, а не в pages.css,
   потому что нужен на всех страницах без исключения.
   ============================================================ */
.offline-banner {
    position: relative;
    z-index: 60;
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 12px 44px 12px 16px;
    background: color-mix(in srgb, var(--danger, #ff6b6b) 16%, var(--surface-2, #1e1e2e));
    border-bottom: 1px solid var(--danger, #ff6b6b);
    color: var(--text, #fff);
    font-size: 0.9rem;
    line-height: 1.5;
}
.offline-banner b { font-weight: 700; }
.offline-banner span { color: var(--text-muted, #b9b9c9); font-size: 0.85rem; }
.offline-banner button {
    position: absolute;
    top: 8px;
    right: 10px;
    background: none;
    border: none;
    color: var(--text-muted, #b9b9c9);
    font-size: 1.3rem;
    line-height: 1;
    cursor: pointer;
    padding: 4px 8px;
}
.offline-banner button:hover { color: var(--text, #fff); }

/* Профиль в шапке — одна аватарка, без плашки и подписи (js/auth.js).
   Раньше здесь стоял значок «человечек с галочкой» — один и тот же у всех,
   потом кнопка-таблетка с ником. Своя картинка узнаётся с одного взгляда,
   а плашка вокруг неё только занимала место; инициалы остаются запасным
   вариантом, когда аватарки нет.

   Размер — во всю прежнюю высоту таблетки, обводка — как у аватара в
   профиле (accent-2), чтобы это читалось как один и тот же аватар. */
.btn-profile-link {
    display: inline-flex;
    padding: 0;
    border: none;
    background: none;
    line-height: 0;
    /* Это ссылка, а не кнопка: без сброса под инициалами появляется
       подчёркивание, которого у аватарки быть не должно. */
    text-decoration: none;
    color: var(--text);
    border-radius: 50%;
    transition: transform var(--transition);
}
.btn-profile-link:hover { transform: translateY(-1px); }
.btn-profile-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.btn-profile-avatar {
    width: 54px;
    height: 54px;
    border: 3px solid var(--accent-2);
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    overflow: hidden;
    position: relative; /* якорь для .frame-fire-canvas, см. ниже */
    /* Подложка под инициалы. Раньше это была полупрозрачная белизна —
       она работала поверх фиолетовой таблетки, а без неё инициалы
       оказались бы на цвете шапки и терялись. */
    background: var(--surface-3);
    font-size: 0.95rem;
    font-weight: 700;
    line-height: 1;
}
/* Косметическая рамка — та же, что надета в профиле.
   Одинаковая фиолетовая обводка у всех превращала аватарку в шапке в
   безликий кружок, хотя в профиле у человека стоит своя рамка.

   Приём такой: обводку делаем прозрачной и кладём два слоя фона —
   внутренний (подложка под инициалы) обрезан по padding-box, внешний
   (градиент рамки) по border-box. Один слой не годится: он залил бы и
   середину, и у человека без аватарки вместо инициалов на подложке
   получался бы сплошной цветной кружок. */
.btn-profile-avatar.has-frame {
    border-color: transparent;
    background-image: linear-gradient(var(--surface-3), var(--surface-3)),
                      var(--btn-frame-value);
    background-origin: padding-box, border-box;
    background-clip: padding-box, border-box;
}

/* Анимированная рамка — Dantes Pass, награда дня 31 (легендарная рамка
   с флагом is_animated, см. supabase/dantes_pass_reward_effects.sql).
   Сам градиент рамки (--btn-frame-value / фон .profile-avatar-frame)
   не трогаем — он может быть каким угодно, а мерцающая обводка кладётся
   поверх через box-shadow, который ни с чем из уже занятых слоёв фона
   не спорит. Работает только вместе с has-frame — на пустой аватарке
   без рамки мерцать нечему. */
@keyframes dantes-frame-glow {
    0%, 100% { box-shadow: 0 0 0 0 transparent; }
    50% { box-shadow: 0 0 16px 3px var(--accent); }
}
.btn-profile-avatar.has-frame.is-animated,
.profile-avatar-frame.has-frame.is-animated {
    animation: dantes-frame-glow 2.2s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
    .btn-profile-avatar.has-frame.is-animated,
    .profile-avatar-frame.has-frame.is-animated { animation: none; }
}

/* Искры/пламя по кольцу рамки — canvas, который вставляет js/frame-fire.js
   ровно при появлении .has-frame.is-animated (и убирает при исчезновении).
   pointer-events: none — под ним по-прежнему кликабельна сама аватарка;
   при prefers-reduced-motion: reduce файл вообще не запускает анимацию, и
   canvas просто не создаётся — глушить его здесь отдельно не нужно. */
.frame-fire-canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    pointer-events: none;
}

/* Квадратную аватарку профиля кнопка повторяет, а не спорит с ней */
.btn-profile-avatar.is-square { border-radius: 14px; }
.btn-profile-link:has(.is-square) { border-radius: 16px; }
.btn-profile-avatar img,
.btn-profile-avatar video { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Игровые иконки нарисованы с чёрной каймой прямо в файле — обрезаем её
   тем же приёмом, что и у большого аватара в профиле. */
.btn-profile-avatar img.is-game-icon { transform: scale(1.1); }

/* Разделы колокольчика — архив: в них лежит вся история, поэтому
   переключателя «показать прочитанные» там нет, а есть пояснение. */
.notif-foot-note { color: var(--text-muted); font-size: 0.85rem; }

/* Свой вектор вместо шрифтовой иконки в пункте меню (js/layout.js).
   Выравниваем по строке так же, как выравнивается <i> у остальных
   пунктов, иначе значок «висит» выше подписи. */
/* Колизей нарисован в три яруса арок, и в размер шрифта эта детализация
   схлопывается в пятно. Даём ему чуть больше места, чем букве, — рядом с
   иконками Font Awesome он всё равно читается как равный им по весу. */
.nav-svg-icon { width: 1.45em; height: 1.45em; vertical-align: -0.34em; flex-shrink: 0; }


/* ------------------------------------------------------------
   Длинные списки: не рисуем то, что за экраном.

   content-visibility: auto разрешает браузеру пропускать вёрстку и
   отрисовку карточек, которых сейчас не видно. Вместе с ними
   останавливаются и все анимации внутри. contain-intrinsic-size задаёт
   примерную высоту заготовки — без него полоса прокрутки прыгала бы по
   мере доскроллинга до реальных карточек.

   Размеры взяты с запасом по факту: лучше чуть больше, чем меньше —
   заниженная высота даёт рывок в момент подстановки настоящей.
   ------------------------------------------------------------ */
.post-card,
.friend-card,
.gift-card,
.user-card,
.cosmetic-card,
.stream-card,
.tournament-card,
.notif-item  {
    content-visibility: auto;
}
.post-card { contain-intrinsic-size: auto 320px; }
.stream-card { contain-intrinsic-size: auto 260px; }
.tournament-card { contain-intrinsic-size: auto 220px; }
.friend-card, .user-card { contain-intrinsic-size: auto 96px; }
.gift-card, .cosmetic-card { contain-intrinsic-size: auto 180px; }
.notif-item { contain-intrinsic-size: auto 84px; }


/* ============================================================
   Телефон: компактная раскладка

   Настольные размеры на экране в 390px читаются как «всё огромное»:
   заголовок в три строки, кнопки во всю ширину, между секциями по
   полсотни пикселей воздуха. До первой карточки приходится прокрутить
   целый экран, хотя на нём почти ничего нет.

   Ниже — одна шкала на весь сайт: поля, промежутки и кегль ужимаются
   примерно на треть. Ничего не прячем и не выкидываем — только плотнее
   укладываем. Ширина отсечки 560px, а не 480: планшет в портрете и
   крупный телефон страдают от того же.
   ============================================================ */
@media (max-width: 560px) {
    /* ---------- Ритм страницы ---------- */
    .main-content { padding: 14px 0 28px; }
    .section-title { font-size: clamp(1.15rem, 5vw, 1.45rem); margin-bottom: 16px; }

    /* Заголовок и ссылка «Все …» вставали в две строки: кнопка не
       помещалась рядом и переносилась, забирая полосу под собой.
       В одну строку они укладываются, если ссылке не давать расти. */
    .section-head {
        gap: 10px; margin-bottom: 14px;
        flex-wrap: nowrap; align-items: center;
    }
    .section-head .section-title { min-width: 0; }
    .section-head .btn {
        flex: 0 0 auto; padding: 7px 11px; font-size: 0.82rem;
    }
    /* Счётчик справа («8 турниров») переносился по слову и занимал две
       строки при пустом месте слева. Он короткий — пусть не рвётся. */
    .section-head > span { flex: 0 0 auto; white-space: nowrap; font-size: 0.85rem; }

    /* Секции шли встык с крупными отступами у каждой — на телефоне это
       складывалось в пустые экраны между блоками. */
    .main-content > .container > section,
    .main-content > .container > .home-section { margin-bottom: 26px; }

    /* ---------- Карточки и сетки ---------- */
    .tournament-grid, .stream-grid { gap: 12px; }
    .tournament-card { padding: 16px; }
    .card, .panel { padding: 16px; }

    /* ---------- Кнопки ---------- */
    /* Крупная кнопка на телефоне — это не «удобнее нажать», а «съело
       строку». 44px по высоте хватает пальцу с запасом (это и есть
       рекомендованный минимум), остальное было воздухом. */
    .btn { padding: 10px 14px; font-size: 0.92rem; }
    .btn-lg { padding: 12px 18px; font-size: 0.98rem; }

    /* Две кнопки подряд встают в ряд, а не столбиком: «Мои товары» и
       «Продать», «Редактировать профиль» и «Выйти» — это пары. */
    .btn-row, .form-actions, .profile-actions {
        display: flex; flex-wrap: wrap; gap: 8px;
    }
    .btn-row > .btn, .form-actions > .btn, .profile-actions > .btn {
        flex: 1 1 calc(50% - 4px);
        /* Раньше здесь стоял фиксированный min-width: 130px — на узких
           телефонах кнопка «Зарегистрироваться» (одно длинное слово,
           перенос запрещён общим правилом .form-actions .btn выше)
           сжималась в эти 130px и текст вылезал за её границы. fit-content
           не даёт сжаться уже собственного текста: не помещаются в ряд —
           переносится сама кнопка на новую строку (flex-wrap на
           контейнере), а не буквы внутри неё. Для пар с короткими
           подписями («Мои товары» / «Продать») ничего не меняется — им
           и так хватало места по 50%. */
        min-width: fit-content;
    }

    /* ---------- Баннер на главной ---------- */
    /* Заголовок в 2em занимал три строки и полэкрана под собой. */
    .home-banner { margin: 12px 14px 0; }
    .home-banner-empty { height: auto; min-height: 0; padding: 18px 14px; }
    .home-banner-empty h1 { font-size: 1.3rem; line-height: 1.2; }
    .home-banner-empty p { font-size: 0.9rem; }

    /* Кнопка «Баннер» висит абсолютом в правом верхнем углу и на узком
       экране наезжала прямо на заголовок — текст читался сквозь неё.
       Уводим её вниз, где под текстом есть свободное поле, и делаем
       компактной: это кнопка для владельца сайта, а не для гостя. */
    .home-banner-admin { top: auto; bottom: 10px; right: 10px; }
    .home-banner-admin .btn { padding: 6px 10px; font-size: 0.78rem; }
    .home-banner-empty { padding-bottom: 46px; }

    /* ---------- Формы ---------- */
    .form-group { margin-bottom: 12px; }
    .form-input, .form-textarea, select.form-input { padding: 10px 12px; font-size: 0.95rem; }
    .form-label, label { font-size: 0.9rem; }

    /* ---------- Модалки ---------- */
    /* Модалка во весь экран с полями по 18px терялa половину ширины. */
    .modal-content { width: calc(100vw - 20px); max-height: 92vh; }
    .modal-header { padding: 14px 16px; }
    .modal-header h3 { font-size: 1.05rem; }
    .modal-body { padding: 14px 16px 18px; }
}

/* ------------------------------------------------------------
   Ряды фильтров и вкладок: одна прокручиваемая лента

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

   scroll-snap притягивает чип к краю, чтобы лента не замирала на
   полусреза.
   ------------------------------------------------------------ */
@media (max-width: 820px) {
    .filter-chips,
    .categories-nav,
    .category-filters,
    .subtabs,
    .notif-tabs,
    .tabs-nav  {
        display: flex;
        flex-wrap: nowrap;
        overflow-x: auto;
        scroll-snap-type: x proximity;
        -webkit-overflow-scrolling: touch;

        gap: 6px;
        padding-bottom: 2px;
    }
.filter-chips > *,
    .categories-nav > *,
    .category-filters > *,
    .subtabs > *,
    .notif-tabs > *,
    .tabs-nav > *  {
        flex: 0 0 auto;
        scroll-snap-align: start;
    }
}


/* ------------------------------------------------------------
   Телефон: витрина и карточки на главной

   Эти классы объявлены здесь, а не в pages.css, потому что главная
   подключает только style.css. Смысл тот же, что у магазина: карточка
   во всю ширину экрана — это один товар за пролистывание.
   ------------------------------------------------------------ */
@media (max-width: 560px) {

    /* Турниры и трансляции остаются в одну колонку: в них важны даты,
       призовой фонд и число участников — в половине ширины эти строки
       начали бы переноситься, и карточка стала бы не ниже, а выше. */
    .tournament-grid, .stream-grid { grid-template-columns: 1fr; gap: 12px; }
}


/* ------------------------------------------------------------
   Вход через Telegram
   ------------------------------------------------------------ */
/* Разделитель «или» между обычной формой и кнопкой Telegram:
   без него кнопка читается как ещё одно поле формы. */
.auth-divider {
    display: flex; align-items: center; gap: 12px;
    margin: 14px 0 16px;
    color: var(--text-dim);
    font-size: 0.85rem;
    white-space: nowrap;
}
.auth-divider::before,
.auth-divider::after {
    content: ''; flex: 1; height: 1px; background: var(--border);
}

/* Фирменный синий Telegram, а не акцент сайта: кнопку узнают по цвету
   быстрее, чем читают надпись. */
.btn-telegram {
    width: 100%;
    justify-content: center;
    background: #2aabee;
    color: #fff;
    border-color: #2aabee;
}
.btn-telegram:hover { background: #1f95d4; border-color: #1f95d4; }
.btn-telegram i { font-size: 1.1em; }

/* Дозаполнение профиля закрывать нечем: без юзернейма и даты рождения
   аккаунт неполон, поэтому крестика в шапке этой модалки нет. */
#finishSignupModal .modal-header { border-bottom: 1px solid var(--border); }

/* ============================================================
   Нижняя панель вкладок — телефон (js/layout.js, renderTabBar)

   Показывается только там, где до бургер-меню в верхнем углу не
   дотянуться большим пальцем. На широком экране её нет вовсе: там
   верхняя навигация видна целиком.
   ============================================================ */
.tab-bar { display: none; }

/* Телефон и планшет (до 1240px — там же, где пропадает верхнее меню).
   Бокового бургер-меню больше нет: все разделы живут в этой панели.
   Панель «плавающая»: скруглена и отодвинута от краёв экрана. Внутри —
   полоса с пунктами (прокручивается жестом, собственную полосу браузера
   прячем) и под ней всегда видимая шкала прокрутки: на телефонах
   системные полосы исчезают сами, поэтому рисуем свою. */
@media (max-width: 1240px) {
    .mobile-menu-btn, .mobile-menu { display: none !important; }

    .tab-bar {
        position: fixed;
        left: 12px; right: 12px;
        bottom: calc(10px + env(safe-area-inset-bottom, 0px));
        z-index: 900;
        display: flex; flex-direction: column;
        max-width: 720px; margin: 0 auto;
        background: color-mix(in srgb, var(--surface) 92%, transparent);
        backdrop-filter: blur(14px);
        -webkit-backdrop-filter: blur(14px);
        border: 1px solid var(--border);
        border-radius: 22px;
        box-shadow: var(--shadow-lg);
        overflow: hidden;
    }
    .tab-bar-scroll {
        display: flex;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        scroll-snap-type: x proximity;
        scrollbar-width: none;
    }
    .tab-bar-scroll::-webkit-scrollbar { display: none; }
    .tab-bar-item {
        position: relative;
        flex: 0 0 20%;
        scroll-snap-align: start;
        display: flex; flex-direction: column; align-items: center; justify-content: center;
        gap: 3px;
        padding: 9px 4px 6px;
        color: var(--text-dim);
        text-decoration: none;
        font-size: 0.66rem;
        min-height: 54px;
        transition: color var(--transition);
    }
    .tab-bar-item i, .tab-bar-item svg { font-size: 1.05rem; }
    .tab-bar-item span { line-height: 1; }
    .tab-bar-item.is-active { color: var(--accent-2); }
    /* Текущий раздел — скруглённая «таблетка» вместо черты сверху */
    .tab-bar-item.is-active::before {
        content: '';
        position: absolute; inset: 4px 6px;
        border-radius: 16px;
        background: var(--accent-soft);
        z-index: -1;
    }
    .tab-bar-badge {
        position: absolute; top: 5px; left: 55%;
        min-width: 16px; height: 16px; padding: 0 4px;
        border-radius: 999px;
        background: var(--danger); color: #fff;
        font-size: 0.62rem; font-weight: 700;
        display: flex; align-items: center; justify-content: center;
    }
    .tab-bar-badge[hidden] { display: none; }

    /* Шкала прокрутки: дорожка + ползунок, видны всегда */
    .tab-bar-track {
        position: relative;
        height: 4px; margin: 0 16px 6px;
        border-radius: 999px;
        background: var(--border-soft);
    }
    .tab-bar-track[hidden] { display: none; }
    .tab-bar-thumb {
        position: absolute; top: 0; bottom: 0; left: 0;
        width: 40%;
        border-radius: 999px;
        background: var(--accent-2);
    }

    /* Панель парит над низом страницы — освобождаем под неё место */
    body.has-tab-bar #site-footer { padding-bottom: calc(92px + env(safe-area-inset-bottom, 0px)); }
    body.has-tab-bar .main-content { padding-bottom: calc(96px + env(safe-area-inset-bottom, 0px)); }
    /* В чате низ занят полем ввода — панель прячем */
    body:has(.chat-container)[data-chat-view="dialog"] .tab-bar { display: none; }
}
/* Планшет шире телефона: пунктов в видимой части больше */
@media (min-width: 769px) and (max-width: 1240px) {
    .tab-bar-item { flex-basis: 12.5%; font-size: 0.72rem; }
}

/* ============================================================
   Вручение награды (js/reward-fx.js)

   Живёт в style.css, а не в pages.css рядом со сценой открытия ящика,
   хотя по смыслу это соседи: награда может прилететь на ЛЮБОЙ странице,
   включая главную — код друга применяется сразу после перехода по
   ссылке-приглашению, — а pages.css к главной не подключён. Пока стили
   лежали там, сцена на главной открывалась вообще без позиционирования:
   обычным блоком в конце страницы, под нижней панелью вкладок.

   Одна сцена на любую награду: задание дня, ежедневный приз, код друга,
   титул, уровень. Собрана по образцу картинки для сторис
   (js/share-card.js) — лучи из центра, круг с наградой, надпись сверху,
   название, плашки со счётом. Цвет один на всю сцену и приходит классом
   .rfx-<цвет>: так одна и та же награда всюду выглядит одинаково.

   Весь полёт — на transform и opacity, без анимации размеров и теней:
   только эти два свойства браузер считает на видеокарте, поэтому сцена
   не заикается даже на слабом телефоне, где в этот же момент грузится
   обновлённый баланс.
   ============================================================ */
.rfx-scene {
    position: fixed;
    inset: 0;
    /* Лучи заведомо шире экрана: обрезаем их здесь, иначе они дают
       странице горизонтальную прокрутку на телефоне. */
    overflow: hidden;
    /* Тот же этаж, что и у сцены открытия ящика: выше модалок (2000),
       ниже тостов (10000). */
    z-index: 9500;
    display: flex;
    align-items: center;
    justify-content: center;
    --rfx-1: #fbbf24;
    --rfx-2: #d97706;
}
body.rfx-locked { overflow: hidden; }

.rfx-gold   { --rfx-1: #fbbf24; --rfx-2: #d97706; }
.rfx-green  { --rfx-1: #4ade80; --rfx-2: #16a34a; }
.rfx-blue   { --rfx-1: #38bdf8; --rfx-2: #0284c7; }
.rfx-purple { --rfx-1: #c084fc; --rfx-2: #7c3aed; }
.rfx-red    { --rfx-1: #fb7185; --rfx-2: #e11d48; }
.rfx-brand  { --rfx-1: #8b84ff; --rfx-2: #6c63ff; }

.rfx-backdrop {
    position: absolute;
    inset: 0;
    background: radial-gradient(circle at 50% 42%,
                rgba(24, 20, 70, 0.78) 0%, rgba(6, 6, 24, 0.95) 72%);
    backdrop-filter: blur(6px);
    animation: rfx-fade 0.26s ease both;
}
.rfx-scene.is-leaving { animation: rfx-fade 0.24s ease reverse both; }
@keyframes rfx-fade { from { opacity: 0; } to { opacity: 1; } }

.rfx-stage {
    position: relative;
    width: min(520px, 92vw);
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 20px 0;
}

/* ---------- Лучи ----------
   Ровно та же фигура, что рисует картинка для сторис: 24 клина из
   центра. Один градиент вместо двух десятков элементов, маска гасит их
   к краям, чтобы луч не упирался в угол экрана обрубком. */
.rfx-rays {
    position: absolute;
    top: 118px;
    left: 50%;
    width: 150vmax;
    height: 150vmax;
    transform: translate(-50%, -50%);
    /* Тонкие и редкие клинья: лучи должны читаться как свет позади
       награды, а не как полосатый забор поверх текста. */
    background: repeating-conic-gradient(from 0deg,
        color-mix(in srgb, var(--rfx-1) 22%, transparent) 0deg 3.5deg,
        transparent 3.5deg 16deg);
    -webkit-mask-image: radial-gradient(circle, #000 0%, #000 10%, transparent 46%);
            mask-image: radial-gradient(circle, #000 0%, #000 10%, transparent 46%);
    opacity: 0;
    pointer-events: none;
}
.rfx-scene.is-live .rfx-rays {
    animation: rfx-rays-in 0.7s ease both, rfx-spin 26s linear 0.7s infinite;
}
@keyframes rfx-rays-in {
    from { opacity: 0; transform: translate(-50%, -50%) scale(0.55) rotate(-14deg); }
    to   { opacity: 0.34; transform: translate(-50%, -50%) scale(1) rotate(0deg); }
}
@keyframes rfx-spin {
    from { transform: translate(-50%, -50%) rotate(0deg); }
    to   { transform: translate(-50%, -50%) rotate(360deg); }
}

.rfx-glow {
    position: absolute;
    top: 118px;
    left: 50%;
    width: min(760px, 130vw);
    height: min(760px, 130vw);
    transform: translate(-50%, -50%);
    background: radial-gradient(circle,
                color-mix(in srgb, var(--rfx-1) 32%, transparent) 0%,
                transparent 64%);
    opacity: 0;
    pointer-events: none;
}
.rfx-scene.is-live .rfx-glow { animation: rfx-glow-in 0.9s ease 0.05s both; }
@keyframes rfx-glow-in {
    from { opacity: 0; transform: translate(-50%, -50%) scale(0.4); }
    60%  { opacity: 1; }
    to   { opacity: 0.62; transform: translate(-50%, -50%) scale(1); }
}

/* ---------- Круг с наградой ---------- */
.rfx-orb-wrap {
    position: relative;
    width: min(196px, 48vw);
    aspect-ratio: 1;
    display: grid;
    place-items: center;
    margin-bottom: 26px;
}

.rfx-orb {
    position: relative;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    display: grid;
    place-items: center;
    overflow: hidden;
    background: radial-gradient(circle at 50% 34%, #2c2a63 0%, #16143a 100%);
    border: 5px solid var(--rfx-1);
    box-shadow: 0 0 0 8px color-mix(in srgb, var(--rfx-1) 14%, transparent),
                0 0 60px color-mix(in srgb, var(--rfx-1) 45%, transparent);
    transform: scale(0.2);
    opacity: 0;
}
.rfx-scene.is-live .rfx-orb {
    /* Перелёт в конце (кубическая с выбросом за 1) — круг «прилетает» и
       чуть отыгрывает назад, как предмет, а не как всплывшее окно. */
    animation: rfx-pop 0.62s cubic-bezier(0.2, 1.5, 0.4, 1) 0.06s both,
               rfx-breathe 3.4s ease-in-out 0.9s infinite;
}
@keyframes rfx-pop {
    from { transform: scale(0.2) rotate(-24deg); opacity: 0; }
    to   { transform: scale(1) rotate(0deg); opacity: 1; }
}
@keyframes rfx-breathe {
    0%, 100% { box-shadow: 0 0 0 8px color-mix(in srgb, var(--rfx-1) 14%, transparent),
                           0 0 60px color-mix(in srgb, var(--rfx-1) 45%, transparent); }
    50%      { box-shadow: 0 0 0 14px color-mix(in srgb, var(--rfx-1) 8%, transparent),
                           0 0 90px color-mix(in srgb, var(--rfx-1) 62%, transparent); }
}

.rfx-orb i {
    font-size: clamp(3.2rem, 15vw, 5rem);
    color: var(--rfx-1);
    filter: drop-shadow(0 0 18px color-mix(in srgb, var(--rfx-1) 60%, transparent));
}
.rfx-image { width: 100%; height: 100%; object-fit: cover; }
.rfx-swatch { width: 100%; height: 100%; display: block; }
/* Рамка в сцене вручения — та же связка «кольцо цвета рамки + аватар
   внутри», что и в инвентаре (.cosmetic-frame-preview/.cosmetic-avatar
   в css/pages.css), только на весь круг сцены. */
.rfx-frame-ring {
    width: 100%; height: 100%;
    border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    padding: 10%;
}
.rfx-frame-avatar {
    width: 100%; height: 100%;
    border-radius: 50%;
    overflow: hidden;
    background: var(--surface-2);
    display: flex; align-items: center; justify-content: center;
}
.rfx-frame-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.rfx-frame-initials {
    font-family: var(--font-head);
    font-weight: 800;
    font-size: clamp(1.6rem, 7vw, 2.4rem);
    color: #e0e0ff;
}
.rfx-initials {
    font-family: var(--font-head);
    font-weight: 800;
    font-size: clamp(2.6rem, 12vw, 4rem);
    color: #e0e0ff;
}

/* Расходящиеся кольца — короткий отзвук удара, второе идёт следом за
   первым, чтобы вспышка не выглядела одиночным «пшиком». */
.rfx-ring {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    border: 2px solid var(--rfx-1);
    opacity: 0;
    pointer-events: none;
}
.rfx-scene.is-live .rfx-ring   { animation: rfx-ring-out 1s ease-out 0.2s both; }
.rfx-scene.is-live .rfx-ring-2 { animation: rfx-ring-out 1s ease-out 0.36s both; }
@keyframes rfx-ring-out {
    from { transform: scale(0.85); opacity: 0.85; }
    to   { transform: scale(2.1); opacity: 0; }
}

.rfx-flash {
    position: absolute;
    width: 40%;
    aspect-ratio: 1;
    border-radius: 50%;
    background: radial-gradient(circle, #fff 0%, color-mix(in srgb, var(--rfx-1) 70%, transparent) 45%, transparent 70%);
    opacity: 0;
    pointer-events: none;
}
.rfx-scene.is-live .rfx-flash { animation: rfx-flash-out 0.55s ease-out 0.1s both; }
@keyframes rfx-flash-out {
    from { transform: scale(0.3); opacity: 0.95; }
    to   { transform: scale(4.6); opacity: 0; }
}

.rfx-sparks { position: absolute; inset: 0; pointer-events: none; }
.rfx-spark {
    position: absolute;
    top: 50%; left: 50%;
    width: var(--s); height: var(--s);
    margin: calc(var(--s) / -2) 0 0 calc(var(--s) / -2);
    border-radius: 50%;
    background: var(--rfx-1);
    box-shadow: 0 0 10px color-mix(in srgb, var(--rfx-1) 80%, transparent);
    opacity: 0;
}
.rfx-scene.is-live .rfx-spark { animation: rfx-spark-fly 0.85s ease-out var(--t) both; }
@keyframes rfx-spark-fly {
    from { transform: rotate(var(--a)) translateX(20px) scale(1); opacity: 1; }
    to   { transform: rotate(var(--a)) translateX(var(--d)) scale(0.2); opacity: 0; }
}

/* ---------- Текст ----------
   Все надписи поднимаются одним и тем же движением с разной задержкой:
   взгляд читает их сверху вниз в том же порядке, в каком они появляются. */
.rfx-eyebrow, .rfx-title, .rfx-subtitle, .rfx-stats li, .rfx-actions {
    position: relative;
    opacity: 0;
    transform: translateY(16px);
}
.rfx-scene.is-live .rfx-eyebrow  { animation: rfx-rise 0.5s ease 0.42s both; }
.rfx-scene.is-live .rfx-title    { animation: rfx-rise 0.5s ease 0.52s both; }
.rfx-scene.is-live .rfx-subtitle { animation: rfx-rise 0.5s ease 0.62s both; }
/* Плашки — по одной слева направо: так взгляд успевает прочитать
   каждую цифру, а не видит их появление одним пятном. */
.rfx-scene.is-live .rfx-stats li { animation: rfx-rise 0.45s ease calc(0.74s + var(--i) * 0.09s) both; }
.rfx-scene.is-live .rfx-actions  { animation: rfx-rise 0.5s ease 0.95s both; }
@keyframes rfx-rise {
    from { opacity: 0; transform: translateY(16px); }
    to   { opacity: 1; transform: translateY(0); }
}

.rfx-eyebrow {
    font-family: var(--font-head);
    font-weight: 700;
    font-size: 0.82rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--rfx-1);
    margin: 0 0 10px;
}
.rfx-title {
    font-family: var(--font-head);
    font-weight: 800;
    font-size: clamp(1.5rem, 6.5vw, 2.1rem);
    line-height: 1.15;
    color: #fff;
    margin: 0;
    max-width: 100%;
    overflow-wrap: anywhere;
}
.rfx-subtitle {
    color: #b8b8ff;
    font-size: 0.95rem;
    margin: 10px 0 0;
    max-width: 34ch;
    overflow-wrap: anywhere;
}

.rfx-stats {
    list-style: none;
    display: flex;
    gap: 12px;
    justify-content: center;
    flex-wrap: wrap;
    margin: 22px 0 0;
    padding: 0;
}
.rfx-stats li {
    min-width: 96px;
    padding: 12px 18px;
    border-radius: var(--radius-md);
    background: rgba(255, 255, 255, 0.07);
    border: 1px solid rgba(255, 255, 255, 0.14);
}
.rfx-stats b {
    display: block;
    font-family: var(--font-head);
    font-weight: 800;
    font-size: 1.35rem;
    color: #fff;
    font-variant-numeric: tabular-nums;
}
.rfx-stats span { font-size: 0.76rem; color: #a0a0ff; }

.rfx-actions {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    justify-content: center;
    margin-top: 26px;
}

/* «Меньше движения» — не повод прятать награду: показываем сразу
   собранную сцену, без полёта, вращения и дыхания. */
.rfx-scene.is-instant .rfx-rays,
.rfx-scene.is-instant .rfx-glow,
.rfx-scene.is-instant .rfx-orb,
.rfx-scene.is-instant .rfx-eyebrow,
.rfx-scene.is-instant .rfx-title,
.rfx-scene.is-instant .rfx-subtitle,
.rfx-scene.is-instant .rfx-stats li,
.rfx-scene.is-instant .rfx-actions {
    animation: none !important;
    opacity: 1;
    transform: translate(0);
}
.rfx-scene.is-instant .rfx-rays  { opacity: 0.3; transform: translate(-50%, -50%); }
.rfx-scene.is-instant .rfx-glow  { opacity: 0.7; transform: translate(-50%, -50%); }
.rfx-scene.is-instant .rfx-orb   { transform: scale(1); }
.rfx-scene.is-instant .rfx-flash,
.rfx-scene.is-instant .rfx-ring,
.rfx-scene.is-instant .rfx-spark { display: none; }

@media (prefers-reduced-motion: reduce) {
    .rfx-scene .rfx-rays,
    .rfx-scene .rfx-orb { animation-iteration-count: 1 !important; }
}

/* ============================================================
   Поиск по сайту  (js/search.js + js/nav-brain.js)

   Живёт здесь, а не в pages.css, по одной причине: окно поиска
   открывается на ЛЮБОЙ странице по Ctrl+K, а pages.css подключён
   не везде — главная обходится одним style.css. Пока эти правила
   лежали там, поиск на главной открывался голыми синими ссылками.

   Всё вложено в #searchModal намеренно. Имя .search-results занято
   дважды: им же размечен поиск по сообщениям в чате (pages.css,
   «Поиск по сообщениям»). Раньше файлы подключались в одном
   порядке и побеждало нужное; теперь правила разъехались по двум
   файлам, и полагаться на порядок нельзя — привязка к окну решает
   это раз и навсегда.
   ============================================================ */

#searchModal .search-box {
    display: flex; align-items: center; gap: 10px;
    padding: 0 0 12px;
}
#searchModal .search-box i { color: var(--text-dim); }
#searchModal .search-box .form-input { flex: 1; }
#searchModal .search-results { max-height: 60vh; overflow-y: auto; }
#searchModal .search-tip { padding: 18px 4px; }
#searchModal .search-group + .search-group { margin-top: 14px; }
#searchModal .search-group h4 {
    margin: 0 0 8px; font-size: 0.82rem; letter-spacing: 0.04em;
    text-transform: uppercase; color: var(--text-dim);
    display: flex; align-items: center; gap: 8px;
}
#searchModal .search-row {
    display: flex; align-items: center; gap: 12px;
    padding: 9px 10px; border-radius: var(--radius-md);
    color: inherit; text-decoration: none;
}
#searchModal .search-row:hover { background: var(--surface-soft); }
#searchModal .search-row-body { display: flex; flex-direction: column; min-width: 0; }
#searchModal .search-row-body b { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#searchModal .search-icon {
    width: 38px; height: 38px; flex: 0 0 38px;
    display: grid; place-items: center; font-size: 1.05rem;
    border-radius: var(--radius-md); background: var(--surface-2); color: var(--accent-2);
}

/* ---------- Ответ навигационной сети ----------
   Плашка стоит НАД находками по базе и намеренно выглядит иначе:
   это не «ещё один результат», а ответ на вопрос. Первый вариант
   выделен рамкой — в девяти случаях из десяти нажимают именно его,
   и глаз не должен выбирать между тремя одинаковыми строками. */

#searchModal .search-answer:empty { display: none; }
#searchModal .search-answer {
    display: flex; flex-direction: column; gap: 4px;
    padding: 9px; margin-bottom: 10px;
    border: 1px solid var(--border-soft); border-radius: var(--radius-lg);
    background: var(--accent-soft);
}
#searchModal .search-answer-head {
    display: flex; align-items: center; gap: 8px;
    font-size: 0.78rem; letter-spacing: 0.04em; text-transform: uppercase;
    color: var(--text-dim);
}
#searchModal .search-answer-row {
    display: flex; align-items: center; gap: 10px;
    padding: 6px 8px; border-radius: var(--radius-md);
    color: inherit; text-decoration: none;
    border: 1px solid transparent;
}
#searchModal .search-answer-row:hover,
#searchModal .search-answer-row:focus-visible { background: var(--surface-soft); }
#searchModal .search-answer-row.is-main {
    border-color: var(--accent-ring);
    background: var(--surface-2);
}
#searchModal .search-answer-icon {
    width: 32px; height: 32px; flex: 0 0 32px;
    display: grid; place-items: center; font-size: 0.95rem;
    border-radius: var(--radius-md);
    background: var(--accent); color: var(--on-accent);
}
#searchModal .search-answer-row:not(.is-main) .search-answer-icon {
    background: var(--surface-2); color: var(--accent-2);
}
#searchModal .search-answer-go { margin-left: auto; color: var(--text-dim); font-size: 0.85rem; }

/* Подсветка того места, куда привёл ответ. Живёт уже на целевой
   странице, вне окна поиска, — поэтому без привязки к нему. */
.nav-brain-found {
    animation: navBrainFound 2.4s ease-out;
    border-radius: var(--radius-lg);
}
@keyframes navBrainFound {
    0%, 60%  { box-shadow: 0 0 0 3px var(--accent-ring); }
    100%     { box-shadow: 0 0 0 3px transparent; }
}
@media (prefers-reduced-motion: reduce) {
    .nav-brain-found { animation: none; box-shadow: 0 0 0 3px var(--accent-ring); }
}

/* Аватарка в находках поиска. Один в один с .lb-face из лидерборда:
   тот же размер и та же логика, потому что это одна и та же строка —
   лицо, ник, подпись. avatarMarkup() отдаёт голый <img> без размеров,
   поэтому рамку задаём здесь. */
#searchModal .search-face {
    width: 38px; height: 38px; flex: 0 0 38px;
    border-radius: 50%;
    overflow: hidden;
    display: flex; align-items: center; justify-content: center;
    background: var(--surface-3);
    font-family: var(--font-head);
    font-weight: 700;
    font-size: 0.8rem;
    color: var(--text-dim);
}
#searchModal .search-face.is-square { border-radius: 11px; }
#searchModal .search-face img,
#searchModal .search-face video { width: 100%; height: 100%; object-fit: cover; }
/* Иконки Brawl Stars приходят с нарисованной каймой — подрезаем масштабом. */
#searchModal .search-face img.is-game-icon { transform: scale(1.18); }

/* Длинный ник не должен распирать окно: строка и так узкая. */
#searchModal .search-row-body,
#searchModal .search-answer-row .search-row-body { min-width: 0; flex: 1; }
#searchModal .search-row-body .form-hint {
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Переход из поиска в разговор. Кнопка мелкая и стоит под строкой:
   поиск остаётся главным, а окно Dantes AI — для тех случаев, когда
   вопрос не укладывается в одну строку и хочется переспросить.
   Набранное переносится туда же, чтобы не печатать дважды. */
#searchModal .search-ai-btn {
    display: inline-flex; align-items: center; gap: 7px;
    align-self: flex-start;
    margin-bottom: 10px;
    padding: 4px 11px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: transparent;
    color: var(--text-dim);
    font-family: var(--font-body);
    font-size: 0.78rem;
    cursor: pointer;
    transition: border-color var(--transition), color var(--transition);
}
#searchModal .search-ai-btn:hover,
#searchModal .search-ai-btn:focus-visible { border-color: var(--accent); color: var(--accent-2); }
#searchModal .search-ai-btn i { color: var(--accent-2); }

/* ---------- Реплика сайта и быстрые ответы ----------
   Разговор намеренно не выглядит чатом: ни аватарок, ни пузырей, ни
   истории. Одна строка того, что сайт сейчас спросил, и две-три
   кнопки с готовыми ответами. Всё остальное — обычный поиск, каким
   он и был; полноценная переписка здесь только мешала бы, потому что
   разговор живёт четыре реплики и заканчивается переходом. */
#searchModal .search-say:empty { display: none; }
#searchModal .search-say {
    display: flex; flex-direction: column; gap: 6px;
    padding: 8px 11px; margin-bottom: 10px;
    border-left: 3px solid var(--accent);
    background: var(--surface-soft);
    border-radius: 0 var(--radius-md) var(--radius-md) 0;
}
#searchModal .search-say-text { margin: 0; color: var(--text); font-size: 0.88rem; line-height: 1.45; }
/* Кнопки быстрых ответов — мельче и теснее: это не органы управления,
   а подсказки «можно ответить так». Прежние 5×12 и 0.84rem занимали
   почти строку на каждую, и три подсказки выталкивали находки за
   нижний край окна. */
#searchModal .search-chips { display: flex; flex-wrap: wrap; gap: 6px; }
#searchModal .search-chip {
    padding: 3px 10px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--text-dim);
    font-family: var(--font-body);
    font-size: 0.78rem;
    line-height: 1.5;
    cursor: pointer;
    transition: border-color var(--transition), color var(--transition);
}
#searchModal .search-chip:hover,
#searchModal .search-chip:focus-visible {
    border-color: var(--accent); color: var(--text);
}

/* ---------- Своя подсказка при наведении (js/tooltip.js) ----------
   Вместо системной, которую браузер рисует по атрибуту title: белый
   прямоугольник системным шрифтом, одинаково чужой в обеих темах.
   Разметку ради этого править не нужно — title как писали, так и
   пишут, перехват делает скрипт. */
.dp-tip {
    position: absolute;
    z-index: 4000;
    max-width: 280px;
    padding: 7px 11px;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--surface-2);
    color: var(--text);
    font-family: var(--font-body);
    font-size: 0.82rem;
    line-height: 1.35;
    box-shadow: 0 8px 22px rgba(0, 0, 0, 0.45);
    pointer-events: none;          /* подсказка не должна ловить мышь */
    opacity: 0;
    transform: translateY(4px);
    transition: opacity 0.12s ease-out, transform 0.12s ease-out;
}
.dp-tip.is-visible { opacity: 1; transform: translateY(0); }
.dp-tip.is-below { transform: translateY(-4px); }
.dp-tip.is-below.is-visible { transform: translateY(0); }

/* Хвостик, указывающий на элемент. */
.dp-tip::after {
    content: '';
    position: absolute;
    left: 50%;
    width: 8px; height: 8px;
    margin-left: -4px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    transform: rotate(45deg);
}
.dp-tip:not(.is-below)::after { bottom: -5px; border-top: 0; border-left: 0; }
.dp-tip.is-below::after      { top: -5px;    border-bottom: 0; border-right: 0; }

@media (prefers-reduced-motion: reduce) {
    .dp-tip { transition: none; }
}

/* ============================================================
   Dantes AI — окно разговора
   ============================================================
   Поиск намеренно не выглядит чатом: там одна строка и находки под
   ней. Здесь наоборот — переписка: видно, что спросили, что ответили
   и куда это ведёт. Поэтому пузыри, лицо собеседника и история,
   которой в поиске нет и не нужно.
   ============================================================ */
#dantesAiModal .modal-content { max-width: 620px; }

.ai-log {
    display: flex; flex-direction: column; gap: 12px;
    /* Высота от экрана, а не числом: на ноутбуке 420px — половина
       окна, на телефоне — весь экран вместе с полем ввода. */
    height: min(52vh, 440px);
    overflow-y: auto;
    padding: 4px 2px 8px;


}
.ai-msg { display: flex; align-items: flex-end; gap: 9px; max-width: 100%; }
.ai-msg.is-me { justify-content: flex-end; }

/* Лицо: не аватарка человека, а значок — не выдаём сеть за живого
   собеседника. */
.ai-face {
    flex: 0 0 30px;
    width: 30px; height: 30px;
    border-radius: 50%;
    display: grid; place-items: center;
    background: linear-gradient(135deg, var(--accent) 0%, var(--accent-2) 100%);
    color: var(--on-accent);
    font-size: 0.82rem;
}

.ai-bubble {
    max-width: 82%;
    padding: 9px 13px;
    border-radius: 16px 16px 16px 4px;
    background: var(--surface-soft);
    color: var(--text);
    font-size: 0.92rem;
    line-height: 1.5;
    animation: aiBubbleIn 0.18s ease-out;
}
.ai-msg.is-me .ai-bubble {
    border-radius: 16px 16px 4px 16px;
    background: var(--accent);
    color: var(--on-accent);
}
.ai-text { margin: 0; white-space: pre-wrap; }

@keyframes aiBubbleIn {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
}

/* Курсор при наборе: тонкая черта у последней буквы. Держится на
   ::after, чтобы не лезть в сам текст — его читает экранный диктор. */
.ai-text.is-typing::after {
    content: '';
    display: inline-block;
    width: 2px; height: 1em;
    margin-left: 2px;
    vertical-align: -0.15em;
    background: var(--accent-2);
    animation: aiCaret 0.8s steps(1) infinite;
}
@keyframes aiCaret { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }

/* Три точки, пока считается ответ */
.ai-dots { display: inline-flex; gap: 4px; padding: 3px 0; }
.ai-dots i {
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--text-dim);
    animation: aiDot 1.1s ease-in-out infinite;
}
.ai-dots i:nth-child(2) { animation-delay: 0.16s; }
.ai-dots i:nth-child(3) { animation-delay: 0.32s; }
@keyframes aiDot {
    0%, 60%, 100% { opacity: 0.3; transform: translateY(0); }
    30%           { opacity: 1;   transform: translateY(-3px); }
}

/* Карточки переходов внутри реплики */
.ai-cards { display: flex; flex-direction: column; gap: 5px; margin-top: 9px; }
.ai-card {
    display: flex; align-items: center; gap: 9px;
    padding: 6px 8px;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--surface-2);
    color: inherit;
    text-decoration: none;
    transition: border-color var(--transition), background var(--transition);
}
.ai-card:hover, .ai-card:focus-visible {
    border-color: var(--accent);
    background: var(--accent-soft);
}
.ai-card-icon {
    flex: 0 0 28px;
    width: 28px; height: 28px;
    border-radius: var(--radius-sm);
    display: grid; place-items: center;
    background: var(--accent); color: var(--on-accent);
    font-size: 0.82rem;
}
.ai-card-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.ai-card-body b { font-size: 0.87rem; line-height: 1.3; }
.ai-card-body span {
    color: var(--text-dim); font-size: 0.75rem;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ai-card > i:last-child { color: var(--text-dim); font-size: 0.78rem; }

/* Быстрые ответы — того же мелкого кроя, что и в поиске */
.ai-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0 0; }
.ai-chips:empty { display: none; }
.ai-chip {
    padding: 3px 10px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--text-dim);
    font-family: var(--font-body);
    font-size: 0.78rem;
    line-height: 1.5;
    cursor: pointer;
    transition: border-color var(--transition), color var(--transition);
}
.ai-chip:hover, .ai-chip:focus-visible { border-color: var(--accent); color: var(--text); }

.ai-form { display: flex; gap: 8px; margin-top: 12px; }
.ai-form .form-input { flex: 1; min-width: 0; }
.ai-send {
    flex: 0 0 42px;
    width: 42px; height: 42px;
    border: 0;
    border-radius: 50%;
    background: var(--accent);
    color: var(--on-accent);
    cursor: pointer;
    display: grid; place-items: center;
    transition: filter var(--transition);
}
.ai-send:hover { filter: brightness(1.1); }

@media (prefers-reduced-motion: reduce) {
    .ai-bubble { animation: none; }
    .ai-dots i { animation: none; opacity: 0.6; }
    .ai-text.is-typing::after { animation: none; }
}

@media (max-width: 520px) {
    .ai-log { height: min(58vh, 420px); }
    .ai-bubble { max-width: 88%; font-size: 0.88rem; }
}


/* ---------- Прелоадер: Хэллоуин ---------- */
.site-preloader {
    background:
        radial-gradient(520px 320px at 50% 46%, rgba(255, 140, 40, .22), transparent 70%),
        radial-gradient(900px 400px at 50% 110%, rgba(150, 110, 255, .22), transparent 70%),
        #0a0716;
    overflow: hidden;
}
.site-preloader::before, .site-preloader::after {
    content: ''; position: absolute; left: 0; right: 0; bottom: -40px; height: 220px; pointer-events: none;
    background: radial-gradient(closest-side, rgba(190, 160, 255, .25), transparent);
    filter: blur(14px); width: 70%; animation: pl-fog 14s linear infinite;
}
.site-preloader::after { animation-duration: 21s; animation-delay: -7s; background: radial-gradient(closest-side, rgba(255, 150, 60, .18), transparent); }
@keyframes pl-fog { from { transform: translateX(-80%); } to { transform: translateX(160%); } }
.site-preloader-logo {
    background: url("../assets/halloween/pumpkin-angry.png") center / contain no-repeat;
    font-size: 0; color: transparent; border-radius: 0; box-shadow: none;
    filter: drop-shadow(0 0 16px rgba(255, 140, 30, .9));
}
.site-preloader-mark { width: 84px; height: 84px; }
.site-preloader-ring { border-color: transparent; border-top-color: #ff9330; border-right-color: #7a4cff; inset: -12px; border-radius: 50%; }
.site-preloader-dot { background: #ffb347; }
.site-preloader-label { color: #ffd9a8; text-shadow: 0 0 10px rgba(255, 140, 30, .6); letter-spacing: .02em; }

.lang-picker{margin-bottom:12px}

/* Dantes Pass: плашки всех уровней одной высоты — как у уровней с боксом */
.dpass-tile,
.dpass-tile.is-big,
.dpass-tile.is-box { min-height: 84px; }
@media (max-width: 640px) {
    .dpass-tile,
    .dpass-tile.is-big,
    .dpass-tile.is-box { min-height: 84px; }
    .dpass-track { grid-auto-rows: minmax(84px, auto); }
}
