/* ======================= FONTS ======================= */
@font-face {
    font-family: 'Graffiti';
    src: url('../fonts/GuanoApesRus.otf') format('opentype');
    font-weight: normal;
    font-style: normal;
    font-display: swap;
}

/* ======================= VARIABLES ======================= */
:root {
    --bg:            #08090c;
    --bg-2:          #0c0e13;
    --bg-card:       rgba(255, 255, 255, 0.03);
    --border:        rgba(255, 255, 255, 0.08);
    --border-strong: rgba(255, 255, 255, 0.14);
    --white:         #f4f6fb;
    --muted:         #9aa3b4;
    --muted-2:       #6d7686;
    --accent:        #9fc7ff;
    --accent-soft:   #cfe4ff;
    --accent-deep:   #7fb0f2;
    --accent-graf:   #9fc7ff;
    --discord:       #5865f2;
    --coin:          #4693ff;
    /* ======================= Палитра — ТЗ №2 (ревью), задача 9 =======================
       Два акцента на весь сайт, не три: голубой — обычный/рекомендованный,
       янтарный — только премиальные предложения (VIP Premium, старший
       стартовый пакет "Свой"). Фиолетовый убран отовсюду — раньше он
       отдельно кодировал VIP Premium, создавая ложное впечатление, что это
       нечто третье, не связанное ни с VIP (был золотой), ни со "Своим"
       (была оливково-золотая кнопка) — два разных оттенка золота вдобавок
       путали VIP с премиальным пакетом. Everything ниже — плоская заливка,
       не градиент: градиенты на кнопках дают заметный артефакт при
       перерисовке DOM и хуже читаются на тёмном фоне, чем сплошной цвет
       (сам сайт кроме VIP/пакетов ими почти не пользуется). Подчеркнуть
       премиальность — тонкой 1px окантовкой янтарного, не градиентом. */
    --accent-primary:       var(--accent);
    --accent-primary-hover: #4693ff;
    --accent-premium:       #ffb84d;
    --accent-premium-hover: #ffa726;
    --accent-success:       #6bd97e;
    --accent-danger:        #ff6b6b;
    --radius:        22px;
    --maxw:          1240px;
    --ease:          cubic-bezier(.22, .61, .36, 1);
}

/* ======================= RESET ======================= */
* { margin: 0; padding: 0; box-sizing: border-box; }

html {
    scroll-behavior: smooth;
    /* --scroll-offset считается динамически в JS (initScrollOffsets,
       main.js) по реальной высоте шапки — ТЗ №4, задача 6: якоря не должны
       уходить под липкую шапку ни на одном разрешении. 90px — запасное
       значение на случай, если JS ещё не отработал. */
    scroll-padding-top: var(--scroll-offset, 90px);
    /* Нативный скроллбар скрыт — вместо него кастомный (см. ниже), у него ширина реально анимируется */
    scrollbar-width: none;
    -ms-overflow-style: none;
}
/* Постраничный скролл только на главной (html.snap-page): каждая секция — отдельный
   «экран», прокрутка переключает между ними. mandatory — чтобы "Соц.сети" магнитились
   так же надёжно, как первые две страницы. Футер — отдельная снап-точка (align:end),
   иначе mandatory не пускал бы скролл дальше последней снап-секции. На остальных
   страницах (например, "Магазин") своих снап-точек нет, а без явного отключения
   .footer{scroll-snap-align:end} всё равно оставался бы единственной точкой и
   страница намертво примагничивалась бы к низу — поэтому скоуп обязателен. */
html.snap-page {
    scroll-snap-type: y mandatory;
}
/* Временно отключает снеп на время программного перехода по ссылке на
   #media (см. initMediaAnchorScroll в main.js) — .media не входит в снап-точки
   (см. комментарий у .media ниже), и без отключения mandatory-снеп после
   прыжка по ссылке "магнитил" остановку к ближайшей настоящей снап-точке
   (это футер), проскакивая мимо самой секции "Медиа" */
html.snap-page.snap-suspended {
    scroll-snap-type: none;
}
::-webkit-scrollbar { display: none; }

/* ======================= КАСТОМНЫЙ СКРОЛЛБАР ======================= */
.custom-scrollbar {
    position: fixed;
    top: 0;
    right: 0;
    width: 18px;
    height: 100vh;
    z-index: 1000;
    pointer-events: none;
}
.custom-scrollbar__thumb {
    position: absolute;
    right: 4px;
    width: 6px;
    border-radius: 10px;
    background: var(--accent);
    transition: width .22s var(--ease), background .2s var(--ease);
    pointer-events: auto;
    touch-action: none;
}
.custom-scrollbar__thumb:hover,
.custom-scrollbar__thumb.is-dragging {
    width: 12px;
    background: var(--accent-deep);
}

body {
    font-family: 'Montserrat', system-ui, 'Segoe UI', Arial, sans-serif;
    background: var(--bg);
    color: var(--white);
    line-height: 1.55;
    overflow-x: hidden;
    -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }
a { text-decoration: none; color: inherit; }
/* ТЗ №4, задача 8 — явный сброс на случай наследования подчёркивания
   обычным текстом (замечено в /refund п.1.3 и подписи чекбокса согласия
   на обработку персданных); подчёркивание остаётся только у ссылок и
   элементов, которым оно задано отдельно и осознанно (см. правила a:hover/
   .balance__account-change/.legal__toc a и т.п. ниже по файлу). */
p, li, span, label { text-decoration: none; }
ul { list-style: none; }
/* .btn и подобные классы сами задают display — без !important атрибут
   hidden проигрывал бы им по порядку объявления в каскаде (одинаковая
   специфичность) и элемент оставался бы видимым */
[hidden] { display: none !important; }

/* Лёгкий тоннельный эффект (виньетка) по всему сайту */
body::after {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 999;
    pointer-events: none;
    background: radial-gradient(140% 100% at 50% 45%, transparent 60%, rgba(0, 0, 0, .38) 100%);
}

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

.graffiti {
    font-family: 'Graffiti', 'Montserrat', cursive;
    color: var(--accent-graf);
    font-weight: 400;
    text-transform: none;
    letter-spacing: 0;
}

.accent { color: var(--accent); }

/* ======================= BUTTONS ======================= */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    font-weight: 600;
    font-size: 15px;
    padding: 13px 30px;
    border-radius: 12px;
    border: 1px solid transparent;
    cursor: pointer;
    transition: transform .25s var(--ease), box-shadow .25s var(--ease), background .25s, border-color .25s;
    white-space: nowrap;
}
.btn--primary {
    background: linear-gradient(135deg, var(--accent-soft), var(--accent));
    color: #0a1224;
    box-shadow: 0 10px 30px -12px rgba(159, 199, 255, .7);
}
.btn--primary:hover { transform: translateY(-2px); box-shadow: 0 16px 38px -12px rgba(159, 199, 255, .85); }
.btn--ghost {
    background: rgba(255, 255, 255, 0.02);
    border-color: var(--border-strong);
    color: var(--white);
}
.btn--ghost:hover { background: rgba(255, 255, 255, 0.07); transform: translateY(-2px); }
.btn--lg { padding: 17px 46px; font-size: 16px; border-radius: 14px; }

/* Короткая анимация подтверждения на кнопке входа (см. initAuthForm в
   main.js) — успешный логин на мгновение показывает "монетку" с логотипом
   региона прямо на месте текста кнопки, она переворачивается лицом к
   зрителю, прежде чем страница перейдёт в личный кабинет */
.btn.is-success { pointer-events: none; animation: btn-success-pulse .4s var(--ease); }
.btn.is-success .btn__check {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    background: radial-gradient(circle at 32% 28%, #223257, #0a1224 72%);
    box-shadow: 0 0 0 2px rgba(255, 255, 255, .3) inset, 0 0 0 1px rgba(10, 18, 36, .5), 0 3px 8px rgba(0, 0, 0, .4);
    overflow: hidden;
    animation: btn-check-flip .5s var(--ease) .05s both;
}
.btn.is-success .btn__check img { width: 15px; height: 15px; object-fit: contain; }
@keyframes btn-success-pulse {
    0% { transform: scale(1); }
    40% { transform: scale(1.045); }
    100% { transform: scale(1); }
}
/* Монетка "влетает" развёрнутой на 180° (видна с ребра — тонкая полоска) и
   доворачивается лицевой стороной к зрителю ровно к концу анимации */
@keyframes btn-check-flip {
    0% { transform: perspective(240px) rotateY(180deg) scale(.5); opacity: 0; }
    55% { opacity: 1; }
    100% { transform: perspective(240px) rotateY(0deg) scale(1); opacity: 1; }
}

/* ======================= HEADER ======================= */
/* Эффект «жидкого стекла» (glassmorphism) — активен всегда, не только при скролле */
.header {
    position: sticky;
    top: 0;
    z-index: 100;
    background: linear-gradient(180deg, rgba(255, 255, 255, .07), rgba(255, 255, 255, .02)), rgba(19, 21, 24, .55);
    backdrop-filter: blur(22px) saturate(160%);
    -webkit-backdrop-filter: blur(22px) saturate(160%);
    border-bottom: 1px solid rgba(255, 255, 255, .1);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .12), 0 8px 30px rgba(0, 0, 0, .25);
}
.header__inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 82px;
    gap: 24px;
}
/* Весь логотип (иконка + текст) увеличен целиком — иконка была 42px, теперь 54px, остальное отмасштабировано пропорционально */
.logo { display: flex; align-items: center; gap: 14px; }
.logo__img { height: 54px; width: auto; }
.logo__text { display: flex; flex-direction: column; line-height: 1; font-style: italic; transform: translateY(2px); }
.logo__text b { font-size: 26px; font-weight: 900; letter-spacing: 1.4px; }
.logo__text small { font-size: 17px; font-weight: 400; letter-spacing: 5px; color: var(--white); margin-top: 2px; display: inline-block; transform: scaleY(1.2); transform-origin: left top; }

.nav { display: flex; align-items: center; gap: 44px; }
.nav__link {
    position: relative;
    display: inline-block;
    font-size: 14px;
    font-weight: 600;
    letter-spacing: .5px;
    color: var(--muted);
    padding: 10px 6px;
    transition: color .2s, text-shadow .2s;
}
.nav__link::before {
    content: '';
    position: absolute;
    left: 50%;
    top: 50%;
    width: 180px;
    height: 76px;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: radial-gradient(closest-side, rgba(159, 199, 255, .35) 0%, rgba(159, 199, 255, 0) 72%);
    opacity: 0;
    transition: opacity .3s var(--ease);
    z-index: -1;
    pointer-events: none;
}
.nav__link:hover { color: var(--white); }
.nav__link:hover::before { opacity: .65; }
.nav__dot {
    position: absolute;
    left: -13px;
    top: 50%;
    width: 7px; height: 7px; border-radius: 50%;
    background: var(--accent); box-shadow: 0 0 10px var(--accent);
    opacity: 0; transform: translateY(-50%) scale(.4);
    transition: opacity .25s var(--ease), transform .25s var(--ease);
}
.nav__link.is-active { color: var(--white); text-shadow: 0 0 8px rgba(159, 199, 255, .5); }
.nav__link.is-active::before { opacity: .65; }
.nav__link.is-active .nav__dot {
    opacity: 1;
    transform: translateY(-50%) scale(1);
}

.header__auth { padding: 12px 34px; background: #9fc7ff; color: #fff; font-weight: 400; }
/* Дубль кнопки "Авторизация" внутри самого меню .nav — на десктопе скрыт
   (там достаточно отдельной .header__auth рядом с бургером), появляется
   только в раскрытом мобильном меню, см. .nav.is-open .nav__auth-mobile
   ниже — раньше на мобильном/планшетном кнопки авторизации не было вообще,
   т.к. .header__auth целиком прячется в этом брейкпоинте */
.nav__auth-mobile { display: none; }
/* Тот же принцип, что у .nav__auth-mobile выше: на десктопе не нужна вовсе
   (там своя кнопка в profile__head), появляется только в раскрытом мобильном
   меню — и то лишь когда JS снимет атрибут hidden после проверки /api/me */
.nav__admin-mobile { display: none; }

/* Бургер — скруглённый квадрат-иконка (спокойнее и сдержаннее, чем прошлый
   вариант с круглой кнопкой на весь акцентный градиент и разворотом на
   180° — тут ничего не крутится и не заливается сплошным цветом). В покое —
   тонкая обводка, три ровные линии. При открытии просто подсвечивается
   акцентным цветом (тонкая рамка + линии становятся var(--accent)) и линии
   аккуратно собираются в крестик на месте — без вращения всей кнопки. */
.burger {
    display: none;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 6px;
    width: 44px;
    height: 44px;
    background: rgba(255, 255, 255, .04);
    border: 1px solid var(--border);
    border-radius: 12px;
    cursor: pointer;
    transition: border-color .3s var(--ease), background-color .3s var(--ease);
}
.burger:hover { border-color: var(--border-strong); background: rgba(255, 255, 255, .08); }
.burger span {
    width: 22px;
    height: 2px;
    border-radius: 999px;
    background: var(--white);
    transition: transform .4s var(--ease), opacity .25s var(--ease), background-color .3s var(--ease);
}
.burger.is-open { border-color: var(--accent); background: rgba(159, 199, 255, .08); }
.burger.is-open span { background-color: var(--accent); }
.burger.is-open span:nth-child(1) { transform: translateY(8px) rotate(45deg); }
.burger.is-open span:nth-child(2) { opacity: 0; transform: scaleX(0); }
.burger.is-open span:nth-child(3) { transform: translateY(-8px) rotate(-45deg); }

/* ======================= HERO ======================= */
.hero {
    position: relative;
    min-height: 100vh;
    scroll-snap-align: start;
    scroll-snap-stop: always;
    padding-bottom: 36px;
    background:
        /* Круговое затемнение у нижней границы секции — сглаживает переход к следующему блоку и низ фигуры врача.
           Растянуто с 62% до 90% — на таком тёмном и почти однотонном участке резкий (короткий) переход между
           близкими оттенками показывал не плавную дымку, а отчётливую тонкую линию (бандинг: браузер округляет
           цвет по шагам, и на коротком расстоянии эти шаги становятся видны глазом как чёткая грань). Тот же
           переход, растянутый на большее расстояние — тот же результат по краям, но без видимой границы. */
        radial-gradient(85% 65% at 72% 96%, rgba(5, 6, 9, .85) 0%, rgba(5, 6, 9, 0) 90%),
        radial-gradient(120% 90% at 78% 40%, rgba(40, 55, 85, .55) 0%, rgba(8, 9, 12, 0) 80%),
        linear-gradient(180deg, #0a0c11 0%, #08090c 100%);
    overflow: hidden;
}
.hero::before {
    content: '';
    position: absolute;
    inset: 0;
    background-image: url('../photos/fon.webp');
    background-size: cover;
    background-position: center;
    opacity: .02;
    z-index: 0;
    pointer-events: none;
}
.hero__inner {
    position: relative;
    z-index: 3;
    display: flex;
    align-items: flex-start;
    /* Раньше 82vh — на высоких экранах это создавало огромный пустой промежуток
       перед следующей секцией, т.к. контент не дотягивался до этой высоты */
    min-height: min(82vh, 660px);
    padding-top: 160px;
    padding-bottom: 20px;
}
.hero__content { position: relative; z-index: 3; max-width: 620px; }
.hero__title {
    font-weight: 800;
    font-size: clamp(42px, 5.4vw, 78px);
    line-height: 0.98;
    letter-spacing: -1px;
    text-transform: uppercase;
}
.hero__line { display: block; position: relative; white-space: nowrap; }
.hero__line:first-child { margin-bottom: 0.62em; }
.hero__graffiti {
    position: absolute;
    left: 4px;
    top: 100%;
    transform: translateY(-46%) rotate(-8deg);
    font-size: 80px;
    text-transform: none;
    pointer-events: none;
    text-shadow: 0 6px 24px rgba(159, 199, 255, .45);
}
.hero__desc {
    margin-top: 30px;
    max-width: 500px;
    color: var(--muted);
    font-size: 17px;
}
.hero__desc b { color: var(--white); font-style: italic; }

.hero__join {
    display: inline-flex;
    align-items: center;
    gap: 18px;
    margin-top: 40px;
    transition: transform .25s var(--ease);
}
.hero__join:hover { transform: translateY(-3px); }
.avatars { display: inline-flex; align-items: center; }
.avatars__item {
    position: relative;
    width: 72px; height: 72px;
    border-radius: 50%;
    overflow: hidden;
    border: 2px solid #12151c;
    margin-left: -18px;
    background: #1a2130;
    box-shadow: 0 6px 18px rgba(0,0,0,.4);
}
.avatars__item:first-child { margin-left: 0; }
.avatars__item img {
    position: absolute;
    left: 50%;
    top: 0;
    width: auto;
    height: 180%;
    max-width: none;
    transform: translateX(-50%);
}
/* Ручная подстройка кропа под прозрачные поля каждого PNG: кадрируем строго по пояс */
.avatars__item:nth-child(1) img { top: -6%; }  /* medic  */
.avatars__item:nth-child(2) img { top: -24%; } /* spech  */
.avatars__item:nth-child(3) img { top: -6%; }  /* police */
.avatars__item:nth-child(4) img { top: -6%; }  /* man    */
.avatars__plus {
    width: 72px; height: 72px;
    border-radius: 50%;
    margin-left: -18px;
    display: grid; place-items: center;
    font-size: 38px; font-weight: 400;
    color: #fff;
    text-shadow: 0 0 10px rgba(255, 255, 255, .85), 0 0 22px rgba(255, 255, 255, .45);
    background: linear-gradient(135deg, var(--accent-soft), var(--accent));
    border: 2px solid #12151c;
}
.hero__join-text { display: flex; flex-direction: column; line-height: 1.25; }
.hero__join-text b { font-size: 17px; font-style: italic; font-weight: 700; }
.hero__join-text small { color: var(--muted); font-size: 15px; }

.hero__stats { display: flex; align-items: center; gap: 22px; margin-top: 46px; flex-wrap: wrap; }
.online {
    position: relative;
    display: flex; align-items: center; justify-content: center; gap: 14px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 16px;
    padding: 16px 26px;
}
.online__count { font-size: 30px; font-weight: 800; color: var(--accent); display: inline-flex; align-items: center; gap: 10px; }
.online__dot {
    width: 9px; height: 9px; border-radius: 50%;
    background: var(--accent); box-shadow: 0 0 12px var(--accent);
    animation: dot-blink 2s ease-in-out infinite;
}
@keyframes dot-blink {
    0%, 100% { opacity: 1; box-shadow: 0 0 12px var(--accent); }
    50% { opacity: .3; box-shadow: 0 0 5px var(--accent); }
}
.online__label { font-size: 13px; color: var(--muted); line-height: 1.15; }

.promo { position: relative; display: inline-flex; align-items: center; gap: 16px; transition: color .2s; border-radius: 16px; }
.promo__play {
    width: 52px; height: 52px; border-radius: 14px;
    display: grid; place-items: center;
    background: var(--bg-card);
    border: 1px solid var(--border-strong);
    color: var(--accent);
    transition: .25s var(--ease);
}
.promo__play svg { width: 22px; height: 22px; }
.promo:hover .promo__play { background: var(--accent); color: #0a1224; transform: scale(1.06); }
.promo__label { color: var(--muted); font-size: 15px; }
.promo:hover .promo__label { color: var(--white); }

/* ======================= ЦИТАТА РАЙОНА (index.html) ======================= */
/* min-height держит блок стабильным по высоте, пока фразы разной длины
   сменяют друг друга — без него карточка бы "прыгала" при каждой смене */
.hero__quotes {
    position: relative;
    z-index: 3;
    display: flex;
    align-items: flex-start;
    gap: 14px;
    max-width: 560px;
    margin-top: 40px;
    padding: 18px 22px;
    border-radius: 16px;
    background: linear-gradient(160deg, rgba(255, 255, 255, .045), rgba(255, 255, 255, .01));
    border: 1px solid var(--border);
    min-height: 80px;
}
.hero__quotes-icon { flex-shrink: 0; width: 24px; height: 24px; margin-top: 2px; color: var(--accent); opacity: .65; }
.hero__quotes-icon svg { width: 100%; height: 100%; }
.hero__quotes-text {
    font-size: 14.5px;
    font-style: italic;
    line-height: 1.55;
    color: var(--muted);
    transition: opacity .4s ease;
}
.hero__quotes-text.is-fading { opacity: 0; }

@media (max-width: 640px) {
    .hero__quotes { margin-top: 28px; padding: 16px 18px; }
}

.hero__media {
    position: absolute;
    z-index: 2;
    /* Ближе к правому краю и чуть меньше по ширине — раньше фигура была
       крупнее и сильнее сдвинута к центру, из-за чего на многих
       разрешениях наезжала на текстовый блок слева. */
    right: 6%;
    /* top-значение по умолчанию (используется, только если JS ниже не
       нашёл .hero__quotes — например, очень старый браузер без поддержки
       чего-то нужного скрипту). initHeroMediaAlign сразу после загрузки
       выставляет inline top так, чтобы низ фигуры совпадал с началом
       строки цитаты — расстояние 6% недостаточно надёжно само по себе,
       т.к. высота секции меняется от длины текста над цитатой. */
    top: 6%;
    width: 41%;
    /* aspect-ratio вместо независимых width+height — сама фигура на фото
       занимает примерно 40% ширины холста и почти всю высоту, то есть у
       неё СВОЯ пропорция (~0.72), не совпадающая с пропорцией широкого
       исходного PNG (4096×2286). Раньше высота задавалась отдельно
       (min(94%, 82vh)) — на многих сочетаниях ширины/высоты окна это
       давало блок УЖЕ, чем сама фигура, и cover обрезал плечи по бокам,
       выходя за пределы прозрачного запаса. С aspect-ratio блок всегда
       ровно по фигуре — обрезать в cover уже нечего. */
    aspect-ratio: 0.72;
    /* max-height раньше стоял тут (78vh) — убран: он клипал только высоту,
       оставляя ширину прежней, а не пропорционально уменьшая весь блок —
       из-за этого фигура визуально "сплющивалась" и cover обрезал её по
       бокам сильнее, чем должен был. Теперь высоту (и вместе с ней
       ширину, сохраняя те же 0.72) считает initHeroMediaAlign в main.js —
       единый источник правды вместо двух спорящих друг с другом. */
    pointer-events: none;
}
/* Именно 1024×768 — та же проблема, что и у более широких узких экранов
   (цитата уходит за нижний край), плюс здесь работает СВОЁ правило:
   @media(max-width:1080px) переопределяет .container padding на "0 28px" —
   а .hero__inner несёт оба класса ("container hero__inner"), так что при
   такой же специфичности он перебивал наш padding-top, и текст всегда
   упирался в самый верх секции, что бы мы тут ни задавали через
   ".hero__inner{padding-top}". Используем более специфичный селектор
   ".hero .hero__inner", чтобы наконец получить приоритет. Фигуру справа
   увеличиваем, опускаем к переходу в следующую секцию и сдвигаем правее. */
@media (min-width: 961px) and (max-width: 1080px) {
    .hero .hero__inner { padding-top: 60px; }
    .hero__title { font-size: clamp(38px, 5vw, 56px); }
    .hero__graffiti { font-size: 44px; }
    .hero__desc { font-size: 16px; max-width: 400px; margin-top: 12px; }
    .hero__join { margin-top: 12px; gap: 10px; }
    .avatars__item, .avatars__plus { width: 48px; height: 48px; }
    .avatars__plus { font-size: 26px; line-height: 1; }
    .hero__join-text b { font-size: 13px; }
    .hero__join-text small { font-size: 11.5px; }
    .hero__stats { margin-top: 14px; gap: 11px; }
    .online { padding: 9px 13px; border-radius: 11px; }
    .online__count { font-size: 20px; }
    .online__label { font-size: 10px; }
    .promo__play { width: 34px; height: 34px; }
    .promo__label { font-size: 11px; }
    .hero__quotes { max-width: 400px; min-height: 60px; margin-top: 12px; padding: 10px 12px; }
    .hero__quotes-text { font-size: 12px; }
    .hero__media { width: 46%; right: 4%; }
}
/* Именно 1176×664 — цитата с обычным текстом уходила далеко за нижний край.
   Уменьшаем текст сильно, чтобы поместилось; фигуру справа опускаем и
   сближаем с текстом (main.js — узкий диапазон 1101–1230px). */
@media (min-width: 1101px) and (max-width: 1230px) {
    .hero__inner { padding-top: 30px; }
    .hero__title { font-size: clamp(28px, 4vw, 44px); }
    .hero__graffiti { font-size: 34px; }
    .hero__desc { font-size: 12.5px; max-width: 380px; margin-top: 10px; }
    .hero__join { margin-top: 10px; gap: 9px; }
    .avatars__item, .avatars__plus { width: 46px; height: 46px; }
    .avatars__plus { font-size: 24px; line-height: 1; }
    .hero__join-text b { font-size: 11px; }
    .hero__join-text small { font-size: 9.5px; }
    .hero__stats { margin-top: 12px; gap: 11px; }
    .online { padding: 8px 12px; border-radius: 10px; }
    .online__count { font-size: 18px; }
    .online__label { font-size: 8.5px; }
    .promo__play { width: 32px; height: 32px; }
    .promo__label { font-size: 10px; }
    .hero__quotes { max-width: 380px; margin-top: 10px; padding: 10px 12px; }
    .hero__quotes-text { font-size: 10.5px; }
    .hero__media { width: 40%; right: 14%; }
}
/* Именно 1152×864 — та же ширина 1101–1230px, но выше (min-height:700px
   отделяет этот случай от уже одобренного 1176×664): текст крупнее и
   сдвинут правее, фигура крупнее и тоже чуть правее — но не настолько,
   чтобы между ними снова появился большой промежуток. */
@media (min-width: 1101px) and (max-width: 1230px) and (min-height: 700px) {
    .hero__inner { padding-top: 70px; }
    .hero__content { margin-left: 30px; }
    .hero__title { font-size: clamp(38px, 4.8vw, 58px); }
    .hero__graffiti { font-size: 42px; }
    .hero__desc { font-size: 15px; max-width: 440px; }
    .avatars__item, .avatars__plus { width: 58px; height: 58px; }
    .avatars__plus { font-size: 30px; }
    .hero__join-text b { font-size: 14px; }
    .hero__join-text small { font-size: 12px; }
    .hero__stats { margin-top: 16px; gap: 14px; }
    .online { padding: 10px 15px; border-radius: 12px; }
    .online__count { font-size: 22px; }
    .online__label { font-size: 10.5px; }
    .promo__play { width: 40px; height: 40px; }
    .promo__label { font-size: 13px; }
    .hero__quotes { max-width: 360px; min-height: 56px; padding: 10px 12px; }
    .hero__quotes-text { font-size: 12.5px; }
    .hero__media { width: 48%; right: 4%; }
}
/* Именно 1280×1024 — текст приподнимаем уменьшенным padding-top, фигуру
   немного увеличиваем (шире блок) и тоже приподнимаем (main.js — небольшой
   отрицательный сдвиг). Диапазон 1231–1300px — не задевает соседний
   1301–1400px и обычные более узкие экраны. */
@media (min-width: 1231px) and (max-width: 1300px) {
    .hero__inner { padding-top: 100px; }
    .hero__media { width: 44%; }
}
/* Именно 1280×800 — та же ширина 1231–1300px, но заметно ниже: цитата с
   текстом из блока выше всё равно уходила за нижний край (max-height:850px
   отделяет этот случай от уже одобренных 1280×960 и 1280×1024). Уменьшаем
   текст сильнее и поднимаем его, фигуру справа делаем компактнее. */
@media (min-width: 1231px) and (max-width: 1300px) and (max-height: 850px) {
    .hero__inner { padding-top: 40px; }
    .hero__title { font-size: clamp(36px, 4.4vw, 56px); }
    .hero__desc { font-size: 15px; max-width: 440px; margin-top: 14px; }
    .hero__join { margin-top: 14px; gap: 12px; }
    .avatars__item, .avatars__plus { width: 56px; height: 56px; }
    .hero__join-text b { font-size: 13.5px; }
    .hero__join-text small { font-size: 12px; }
    .hero__stats { margin-top: 16px; gap: 14px; }
    .online { padding: 10px 15px; border-radius: 12px; }
    .online__count { font-size: 22px; }
    .online__label { font-size: 10.5px; }
    .promo__play { width: 39px; height: 39px; }
    .promo__label { font-size: 12px; }
    .hero__quotes { max-width: 440px; margin-top: 14px; padding: 12px 14px; }
    .hero__quotes-text { font-size: 12.5px; }
    .hero__media { width: 40%; }
}
/* Именно 1366×768 — на этой высоте цитата с обычным текстом уходила далеко
   за нижний край (секция была выше окна почти на 220px). Уменьшаем и
   поднимаем весь текстовый блок, чтобы цитата поместилась без скролла;
   фигуру немного увеличиваем и опускаем (main.js даёт ей больше запаса по
   высоте в этом диапазоне — см. main.js). Диапазон 1301–1400px — не задевает
   соседний 1401+ и обычные более узкие ноутбучные экраны. */
@media (min-width: 1301px) and (max-width: 1400px) {
    .hero__inner { padding-top: 40px; }
    .hero__title { font-size: clamp(34px, 4vw, 54px); }
    .hero__desc { font-size: 14px; max-width: 440px; margin-top: 12px; }
    .hero__join { margin-top: 14px; gap: 12px; }
    .avatars__item, .avatars__plus { width: 56px; height: 56px; }
    .hero__join-text b { font-size: 13px; }
    .hero__join-text small { font-size: 11.5px; }
    .hero__stats { margin-top: 16px; gap: 14px; }
    .online { padding: 10px 16px; border-radius: 12px; }
    .online__count { font-size: 22px; }
    .online__label { font-size: 10px; }
    .promo__play { width: 38px; height: 38px; }
    .promo__label { font-size: 12px; }
    .hero__quotes { max-width: 440px; margin-top: 14px; padding: 12px 14px; }
    .hero__quotes-text { font-size: 12px; }
    .hero__media { width: 44%; }
}
/* Именно 1440×1080 — фигуру увеличиваем (шире блок) и опускаем ниже (запас
   вниз даёт main.js), текст приподнимаем уменьшенным padding-top. Диапазон
   1401–1549px — не задевает ни обычный десктоп (1366 и уже), ни соседнюю
   полосу 1550–1600px с её собственной настройкой. */
@media (min-width: 1401px) and (max-width: 1549px) {
    .hero__inner { padding-top: 120px; }
    .hero__media { width: 41%; }
}
/* Именно 1600×1024 — та же ширина 1550–1600px, что и у 1600×1200 (см. сдвиг
   в main.js), НО отдельно по высоте (951–1100px), чтобы не задеть уже
   одобренный 1600×1200 (высота 1200, выше порога) и не попасть в более
   низкий 1600×900 (см. блок ниже, у него своя, ещё более узкая настройка). */
@media (min-width: 1550px) and (max-width: 1600px) and (min-height: 951px) and (max-height: 1100px) {
    .hero__inner { padding-top: 90px; }
    .hero__media { width: 39%; }
}
/* Именно 1600×900 — цитата на этой высоте с обычным текстом уходила за
   нижний край экрана. Уменьшаем и поднимаем текст, чтобы цитата стала видна
   без скролла; фигуру справа увеличиваем и опускаем (initHeroMediaAlign
   в main.js даёт ей больше вертикального запаса именно в этом диапазоне —
   см. main.js). Только высота ≤950px и ширина 1550–1600px — соседние
   1600×1024 и 1600×1200 не трогаем. */
@media (min-width: 1550px) and (max-width: 1600px) and (max-height: 950px) {
    .hero__inner { padding-top: 40px; }
    .hero__title { font-size: clamp(40px, 4.4vw, 64px); }
    .hero__desc { font-size: 15.5px; max-width: 480px; margin-top: 16px; }
    .hero__join { margin-top: 18px; gap: 15px; }
    .avatars__item, .avatars__plus { width: 64px; height: 64px; }
    .hero__join-text b { font-size: 15px; }
    .hero__join-text small { font-size: 13px; }
    .hero__stats { margin-top: 20px; gap: 17px; }
    .online { padding: 13px 20px; border-radius: 15px; }
    .online__count { font-size: 26px; }
    .online__label { font-size: 12px; }
    .promo__play { width: 46px; height: 46px; }
    .promo__label { font-size: 14px; }
    .hero__quotes { max-width: 500px; margin-top: 18px; padding: 15px 18px; }
    .hero__quotes-text { font-size: 13.5px; }
}
/* Именно 1680×1050 (и соседние по диапазону 1601–1900px) — персонаж справа
   и так уже поднят до предела (initHeroMediaAlign в main.js прижимает его
   top к 0, выше некуда — упрётся в шапку), а текст слева со стандартным
   padding-top:160px начинался заметно ниже него. Поднимаем текст навстречу,
   чтобы верх текста и верх фигуры визуально совпадали. Только этот диапазон:
   обычные ноутбучные экраны (<1601px) и оба широких блока (>=1901px) уже
   одобрены и не трогаются. */
@media (min-width: 1601px) and (max-width: 1900px) {
    .hero__inner { padding-top: 40px; }
}
/* Широкие мониторы (2K/4K, >1900px) — крупнее, по просьбе (низ дополнительно
   опущен через initHeroMediaAlign в main.js, тут это не задать: top всё
   равно перебивается скриптом). */
@media (min-width: 1901px) {
    /* Чуть компактнее прежних 48% и дальше от левого текстового блока —
       на некоторых пропорциях экрана (напр. 1920×1440) рука с планшетом
       заезжала на цитату и плашку "Стань частью проекта" слева. */
    .hero__media { width: 36%; right: 7%; }
}
/* Широкие мониторы: текстовый блок слева был рассчитан на 620px и на
   1920–2560px оставлял вокруг себя много пустого места, пока справа стоит
   увеличенный персонаж. Пропорционально увеличиваем сам текст и его отступы,
   а не просто раздвигаем колонку — иначе он остался бы мелким на большом
   пустом фоне. */
@media (min-width: 1901px) {
    /* .hero__inner НЕ трогаем — у него тот же .container, что и в шапке
       (max-width из --maxw), это и держит текст на одной вертикали с
       логотипом. Раньше здесь стояло max-width:1700px, из-за чего .hero
       __inner становился шире шапки и текст съезжал левее логотипа. */
    .hero__content { max-width: 820px; }
    .hero__title { font-size: clamp(64px, 4.6vw, 104px); }
    .hero__desc { font-size: 19px; max-width: 640px; margin-top: 36px; }
    .hero__join { margin-top: 48px; gap: 22px; }
    .avatars__item, .avatars__plus { width: 84px; height: 84px; }
    .hero__join-text b { font-size: 19px; }
    .hero__join-text small { font-size: 16px; }
    .hero__stats { margin-top: 56px; gap: 28px; }
    .online { padding: 20px 30px; border-radius: 18px; }
    .online__count { font-size: 36px; }
    .online__label { font-size: 14px; }
    .promo__play { width: 60px; height: 60px; }
    .promo__label { font-size: 17px; }
    .hero__quotes { max-width: 700px; margin-top: 48px; padding: 22px 28px; }
    .hero__quotes-text { font-size: 16.5px; }
}
/* Именно 1920×1080 (и близкие к нему по высоте широкие окна, до 2000px) —
   отдельно от общего блока выше: тот текст был откалиброван и одобрен под
   2560×1440 (там высоты 1440px вполне хватало), а на 1080px по высоте то же
   самое уже не помещалось — цитата внизу уходила за нижний край экрана.
   Только эта, более узкая по высоте группа экранов уменьшается обратно;
   1901px+ общий блок и всё, что шире 2000px (см. следующий блок — там уже
   своя, увеличенная настройка для 2048×1080), не трогаем совсем. */
@media (min-width: 1901px) and (max-width: 2000px) {
    .hero__inner { padding-top: 96px; }
    .hero__title { font-size: clamp(44px, 3.4vw, 72px); }
    .hero__desc { font-size: 17px; max-width: 560px; margin-top: 22px; }
    .hero__join { margin-top: 26px; gap: 18px; }
    .avatars__item, .avatars__plus { width: 76px; height: 76px; }
    .hero__join-text b { font-size: 17px; }
    .hero__join-text small { font-size: 14px; }
    .hero__stats { margin-top: 32px; gap: 22px; }
    .hero__quotes { max-width: 600px; margin-top: 26px; }
}
/* Именно 2048×1080 — та же высота 1080px, что и у 1920×1080, но ширина уже
   за пределами блока выше (2001–2200px), так что текст можно взять крупнее:
   свободного места по горизонтали больше, чем на 1920. Небольшое увеличение
   line-height (растяжка по вертикали) — просили "чуть-чуть", не переходя на
   line-height 1900px+ блока, чтобы не ловить артефакты переноса строк.
   Фигуру справа делаем шире (меньше обрезки по бокам) и опускаем ниже
   (main.js). Только этот диапазон — 1920×1080 (≤2000px) и 2560×1440
   (>2200px, общий блок) не трогаем. */
@media (min-width: 2001px) and (max-width: 2200px) {
    .hero__inner { padding-top: 90px; }
    .hero__title { font-size: clamp(50px, 3.8vw, 84px); line-height: 1.04; }
    .hero__desc { font-size: 18px; max-width: 600px; margin-top: 26px; }
    .hero__join { margin-top: 30px; gap: 20px; }
    .avatars__item, .avatars__plus { width: 80px; height: 80px; }
    .hero__join-text b { font-size: 18px; }
    .hero__join-text small { font-size: 15px; }
    .hero__stats { margin-top: 36px; gap: 24px; }
    .hero__quotes { max-width: 650px; margin-top: 30px; }
    .hero__media { width: 41%; right: 6%; }
}
/* Именно 2048×1536 — та же ширина, что у 2048×1080 (блок выше), но заметно
   выше по высоте: под фигурой оставалось много пустого места до перехода в
   следующую секцию. Фигуру уменьшаем, сдвигаем правее и опускаем ниже
   (main.js); текст слегка сдвигаем влево. min-height:1451px, чтобы не
   задеть 2048×1080. */
@media (min-width: 2001px) and (max-width: 2200px) and (min-height: 1451px) {
    .hero__content { margin-left: -25px; }
    .hero__title { font-size: clamp(58px, 4vw, 92px); }
    .hero__desc { font-size: 20px; max-width: 640px; }
    .hero__quotes { max-width: 680px; }
    .hero__media { width: 38%; right: 4%; }
}
/* Именно 2560×1080 (ультраширокий, но не очень высокий монитор) — текст,
   откалиброванный под 2560×1440 (в общем блоке >=1901px), на высоте 1080px
   был слишком крупным: цитата уходила за нижний край. Уменьшаем и
   поднимаем текст, чуть сокращаем интервал между строками заголовка,
   увеличиваем графити CRMP. Диапазон — ширина за пределами блока 2048
   (>2200px) и высота до 1100px, чтобы не задеть уже одобренный 2560×1440
   (высота 1440, выше порога) и 2048×1080 (ширина ≤2200). */
@media (min-width: 2201px) and (max-height: 1100px) {
    .hero__inner { padding-top: 110px; }
    .hero__title { font-size: clamp(54px, 3.3vw, 84px); }
    .hero__line:first-child { margin-bottom: 0.3em; }
    .hero__graffiti { font-size: 100px; }
    .hero__desc { font-size: 21px; max-width: 640px; margin-top: 28px; }
    .hero__join { margin-top: 32px; gap: 22px; }
    .avatars__item, .avatars__plus { width: 86px; height: 86px; }
    .hero__join-text b { font-size: 20px; }
    .hero__join-text small { font-size: 16.5px; }
    .hero__stats { margin-top: 38px; gap: 24px; }
    .online { padding: 20px 29px; border-radius: 17px; }
    .online__count { font-size: 35px; }
    .online__label { font-size: 14px; }
    .promo__play { width: 58px; height: 58px; }
    .promo__label { font-size: 17px; }
    .hero__quotes { max-width: 660px; margin-top: 28px; padding: 20px 24px; }
    .hero__quotes-text { font-size: 17px; }
    .hero__media { width: 41%; right: 9%; }
}
/* Именно 2560×1536 — та же ширина, что у 2560×1440 (общий блок >=1901px),
   но заметно выше: снизу под фигурой оставалось много пустого места до
   перехода в следующую секцию. Фигуру уменьшаем, сдвигаем правее и
   опускаем ниже (main.js); текст слегка сдвигаем влево. Диапазон —
   min-height:1451px, чтобы не задеть уже одобренный 2560×1440. max-width:2600px
   — иначе это же правило (без верхней границы) случайно цепляло и 3200×1800,
   у которого теперь своя, отдельная настройка ниже. */
@media (min-width: 2201px) and (max-width: 2600px) and (min-height: 1451px) {
    .hero__content { margin-left: -30px; }
    .hero__media { width: 33%; right: 5%; }
}
/* Именно 3200×1800 (диапазон 2601–3699px тоже покрывает 2880×1620 и
   2880×1800 — они достаточно близки по ширине, чтобы использовать те же
   пропорции без отдельной калибровки). Текст, откалиброванный под
   2560×1440 (title упирался в потолок клампа 104px), выглядел мелким на
   таком просторе — увеличиваем весь левый блок и саму фигуру. Верхняя
   граница 3699px — дальше (3840/4096/5120) уже свои, ещё более крупные
   диапазоны ниже. */
@media (min-width: 2601px) and (max-width: 3699px) {
    .hero__inner { padding-top: 146px; }
    .hero__content { max-width: 700px; }
    .hero__title { font-size: clamp(66px, 3.4vw, 102px); }
    .hero__line:first-child { margin-bottom: 0.4em; }
    .hero__graffiti { font-size: 110px; }
    .hero__desc { font-size: 26px; max-width: 700px; margin-top: 46px; }
    .hero__join { margin-top: 58px; gap: 28px; }
    .avatars__item, .avatars__plus { width: 104px; height: 104px; }
    .avatars__plus { font-size: 46px; }
    .hero__join-text b { font-size: 24px; }
    .hero__join-text small { font-size: 19px; }
    .hero__stats { margin-top: 70px; gap: 34px; }
    .online { padding: 26px 36px; border-radius: 21px; }
    .online__count { font-size: 43px; }
    .online__dot { width: 12px; height: 12px; box-shadow: 0 0 16px var(--accent); }
    .online__label { font-size: 17px; }
    .promo__play { width: 72px; height: 72px; }
    .promo__label { font-size: 20px; }
    .hero__quotes { max-width: 820px; margin-top: 58px; padding: 28px 34px; }
    .hero__quotes-text { font-size: 20px; }
    .hero__media { width: 32%; right: 5%; }
}
/* Именно 3840×2160 (4K). Всё пропорционально крупнее диапазона выше —
   абсолютные размеры текста/фигуры/шапки на 3200 выглядели бы мелко на
   ещё большем 4K-холсте. */
@media (min-width: 3700px) and (max-width: 4079px) {
    .hero__inner { padding-top: 166px; }
    .hero__content { max-width: 800px; }
    .hero__title { font-size: clamp(74px, 3.2vw, 122px); }
    .hero__line:first-child { margin-bottom: 0.4em; }
    .hero__graffiti { font-size: 128px; }
    .hero__desc { font-size: 30px; max-width: 780px; margin-top: 54px; }
    .hero__join { margin-top: 66px; gap: 32px; }
    .avatars__item, .avatars__plus { width: 124px; height: 124px; }
    .avatars__plus { font-size: 54px; }
    .hero__join-text b { font-size: 26px; }
    .hero__join-text small { font-size: 20px; }
    .hero__stats { margin-top: 80px; gap: 38px; }
    .online { padding: 30px 42px; border-radius: 24px; }
    .online__count { font-size: 52px; }
    .online__dot { width: 13px; height: 13px; box-shadow: 0 0 19px var(--accent); }
    .online__label { font-size: 19px; }
    .promo__play { width: 80px; height: 80px; }
    .promo__label { font-size: 22px; }
    .hero__quotes { max-width: 900px; margin-top: 66px; padding: 30px 36px; }
    .hero__quotes-text { font-size: 23px; }
    .hero__media { width: 31%; right: 5%; }
}
/* Именно 4096×2160 — та же высота, что у 3840×2160 выше, но заметно шире
   (кино-формат 17:9), поэтому текстовому блоку и фигуре есть куда расти по
   горизонтали при той же вертикальной раскладке. */
@media (min-width: 4080px) and (max-width: 4599px) {
    .hero__inner { padding-top: 170px; }
    .hero__content { max-width: 840px; }
    .hero__title { font-size: clamp(78px, 3.1vw, 128px); }
    .hero__line:first-child { margin-bottom: 0.4em; }
    .hero__graffiti { font-size: 134px; }
    .hero__desc { font-size: 31px; max-width: 800px; margin-top: 56px; }
    .hero__join { margin-top: 68px; gap: 34px; }
    .avatars__item, .avatars__plus { width: 130px; height: 130px; }
    .avatars__plus { font-size: 56px; }
    .hero__join-text b { font-size: 27px; }
    .hero__join-text small { font-size: 21px; }
    .hero__stats { margin-top: 84px; gap: 40px; }
    .online { padding: 32px 44px; border-radius: 25px; }
    .online__count { font-size: 55px; }
    .online__dot { width: 14px; height: 14px; box-shadow: 0 0 20px var(--accent); }
    .online__label { font-size: 20px; }
    .promo__play { width: 84px; height: 84px; }
    .promo__label { font-size: 23px; }
    .hero__quotes { max-width: 940px; margin-top: 70px; padding: 32px 38px; }
    .hero__quotes-text { font-size: 24px; }
    .hero__media { width: 30%; right: 5%; }
}
/* Именно 5120×2880 (5K) — самый большой из одобренных диапазонов. */
@media (min-width: 4600px) {
    .hero__inner { padding-top: 182px; }
    .hero__content { max-width: 880px; }
    .hero__title { font-size: clamp(84px, 3vw, 140px); }
    .hero__line:first-child { margin-bottom: 0.4em; }
    .hero__graffiti { font-size: 145px; }
    .hero__desc { font-size: 34px; max-width: 860px; margin-top: 62px; }
    .hero__join { margin-top: 74px; gap: 38px; }
    .avatars__item, .avatars__plus { width: 140px; height: 140px; }
    .avatars__plus { font-size: 60px; }
    .hero__join-text b { font-size: 29px; }
    .hero__join-text small { font-size: 23px; }
    .hero__stats { margin-top: 92px; gap: 44px; }
    .online { padding: 36px 48px; border-radius: 28px; }
    .online__count { font-size: 60px; }
    .online__dot { width: 16px; height: 16px; box-shadow: 0 0 24px var(--accent); }
    .online__label { font-size: 22px; }
    .promo__play { width: 92px; height: 92px; }
    .promo__label { font-size: 25px; }
    .hero__quotes { max-width: 1000px; margin-top: 78px; padding: 36px 42px; }
    .hero__quotes-text { font-size: 27px; }
    .hero__media { width: 29%; right: 5%; }
}
/* Хедер (не только hero — сайт целиком) на очень широких экранах (>2600px)
   выглядел мелким рядом с увеличенным текстом/фигурой hero. Масштабируем
   логотип, пункты меню, кнопку "Авторизация", свечение за пунктами меню и
   точку активного пункта — пропорционально размеру экрана, но не в разы
   крупнее (чтобы шапка не превратилась в мобильный "крупный палец"-режим).
   Диапазоны те же, что у hero-блоков выше. */
@media (min-width: 2601px) and (max-width: 3699px) {
    .header__inner { height: 110px; }
    .catalog__filters { top: 110px; }
    .logo__img { height: 76px; }
    .logo__text b { font-size: 36px; }
    .logo__text small { font-size: 22px; }
    .nav { gap: 60px; }
    .nav__link { font-size: 19px; padding: 14px 8px; }
    .nav__link::before { width: 210px; height: 84px; }
    .nav__dot { width: 8px; height: 8px; }
    .header__auth { padding: 16px 42px; font-size: 19px; }
}
@media (min-width: 3700px) and (max-width: 4079px) {
    .header__inner { height: 122px; }
    .catalog__filters { top: 122px; }
    .logo__img { height: 86px; }
    .logo__text b { font-size: 41px; }
    .logo__text small { font-size: 25px; }
    .nav { gap: 68px; }
    .nav__link { font-size: 23px; padding: 15px 9px; }
    .nav__link::before { width: 240px; height: 96px; background: radial-gradient(closest-side, rgba(159, 199, 255, .45) 0%, rgba(159, 199, 255, 0) 72%); }
    .nav__dot { width: 9px; height: 9px; box-shadow: 0 0 13px var(--accent); }
    .header__auth { padding: 19px 48px; font-size: 23px; }
}
@media (min-width: 4080px) and (max-width: 4599px) {
    .header__inner { height: 132px; }
    .catalog__filters { top: 132px; }
    .logo__img { height: 94px; }
    .logo__text b { font-size: 45px; }
    .logo__text small { font-size: 27px; }
    .nav { gap: 74px; }
    .nav__link { font-size: 26px; padding: 16px 10px; }
    .nav__link::before { width: 260px; height: 104px; background: radial-gradient(closest-side, rgba(159, 199, 255, .48) 0%, rgba(159, 199, 255, 0) 72%); }
    .nav__dot { width: 10px; height: 10px; box-shadow: 0 0 14px var(--accent); }
    .header__auth { padding: 21px 52px; font-size: 26px; }
}
@media (min-width: 4600px) {
    .header__inner { height: 148px; }
    .catalog__filters { top: 148px; }
    .logo__img { height: 108px; }
    .logo__text b { font-size: 52px; }
    .logo__text small { font-size: 31px; }
    .nav { gap: 84px; }
    .nav__link { font-size: 30px; padding: 18px 11px; }
    .nav__link::before { width: 290px; height: 116px; background: radial-gradient(closest-side, rgba(159, 199, 255, .5) 0%, rgba(159, 199, 255, 0) 72%); }
    .nav__dot { width: 12px; height: 12px; box-shadow: 0 0 16px var(--accent); }
    .header__auth { padding: 25px 60px; font-size: 30px; }
}
/* Расширяем сам --maxw для очень широких экранов — контейнер шапки и hero
   расширятся вместе с ним (они не разъедутся, т.к. используют одну и ту же
   переменную). Остальные правила для этого диапазона (типографика и
   карточки секций "Как попасть", "Соц.сети", "Игровые кадры") — ниже,
   после базовых объявлений этих классов (иначе более специфичный по
   порядку в файле, но не по CSS-специфичности, базовый класс их бы
   перебивал). */
@media (min-width: 2601px) {
    :root { --maxw: 1700px; }
}
/* Мягкое радиальное свечение по центру фотографии — без жёстких краёв блока */
.hero__media::before {
    content: '';
    position: absolute;
    left: 50%;
    top: 50%;
    width: 120%;
    height: 100%;
    transform: translate(-50%, -50%);
    background: radial-gradient(circle, rgba(159, 199, 255, .22) 0%, rgba(159, 199, 255, .08) 42%, rgba(159, 199, 255, 0) 72%);
    filter: blur(45px);
    z-index: -1;
}
.hero__char {
    width: 100%;
    height: 100%;
    max-width: none;
    /* Теперь безопасно — блок (.hero__media) уже подогнан под пропорции
       самой фигуры через aspect-ratio, так что cover обрезает только
       прозрачный запас исходного PNG, а не саму фигуру. */
    object-fit: cover;
    object-position: top center;
    /* drop-shadow тут раньше давал видимую прямоугольную "рамку" — у файла
       край не идеально прозрачный, тень обводила весь прямоугольник кадра,
       а не силуэт фигуры. Без тени рамки не остаётся. */
    /* Затухание собственного низа фигуры (не границы блока) — переход к
       следующей секции плавный, без резкого среза.
       У низа халата в исходном файле есть нарисованная обводка контура —
       предыдущая версия (transparent уже на 78%) гасила её полностью, но
       вместе с ней срезала и заметную часть реально видимого халата выше
       неё. Компромисс: держим фигуру видимой дольше (черный до 60%) и
       догораем до прозрачности ближе к её собственному краю (92%, не
       97-99% как раньше) — обводка достаточно ослаблена, а халат при этом
       не обрублен посередине.
       Раньше здесь был ВТОРОЙ слой маски (лёгкая растушёвка по бокам) +
       mask-composite:intersect, чтобы объединить два градиента в один
       результат. Это и оказалось источником отдельной тонкой полосы прямо
       на границе, где вертикальное затухание доходит до нуля — стык двух
       наложенных друг на друга масок браузер считает не идеально точно.
       Один-единственный слой маски (без composite вообще) от этого не
       страдает — боковая растушёвка всё равно была почти незаметной
       (всего 1%), потерять её незачем. */
    -webkit-mask-image: linear-gradient(to bottom, black 0%, black 60%, transparent 92%);
    mask-image: linear-gradient(to bottom, black 0%, black 60%, transparent 92%);
}

/* ======================= SECTION TITLE ======================= */
.section-title {
    font-weight: 800;
    font-size: clamp(38px, 5.2vw, 74px);
    line-height: 0.98;
    text-transform: uppercase;
    letter-spacing: -1px;
    margin-bottom: 48px;
}
.section-title .graffiti { font-size: 0.9em; display: inline-block; }

section { padding: 72px 0; }

/* Постраничный скролл: "Как попасть", "Соц.сети" и "Игровые кадры" — отдельные
   полноэкранные страницы, каждая своя точка прилипания. Раньше "Игровые кадры"
   снап-точкой не была — из-за этого скролл вниз от "Соц.сетей" перепрыгивал
   сразу к следующей реальной снап-точке, футеру, полностью пропуская эту
   секцию по пути. Теперь она тоже полноценная остановка; к футеру после неё
   скролл по-прежнему свободный (см. .footer ниже) — жёсткого "залипания"
   между ними как не было, так и нет. */
.how, .social, .media {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    scroll-snap-align: start;
    scroll-snap-stop: always;
}

/* ======================= КАК ПОПАСТЬ / LAUNCHER ======================= */
.how {
    /* Плавный переход цвета фона от героя, чтобы граница секций не бросалась в глаза */
    background: linear-gradient(180deg, var(--bg) 0%, var(--bg-2) 140px, var(--bg-2) 100%);
    padding-top: 54px;
    padding-bottom: 56px;
}
.launcher {
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: center;
    gap: 30px;
    background: linear-gradient(160deg, rgba(255,255,255,.035), rgba(255,255,255,.01));
    border: 1px solid var(--border);
    border-radius: 28px;
    padding: 56px;
    overflow: hidden;
}
.launcher__title { font-size: clamp(28px, 3vw, 40px); font-weight: 800; line-height: 1.05; margin-bottom: 26px; }
.tags { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 34px; }
.tag {
    font-size: 14px;
    color: var(--muted);
    background: rgba(255,255,255,.04);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 11px 18px;
}
.launcher__subtitle { color: var(--muted); font-size: 16px; margin-bottom: 16px; }
.reqs { display: flex; flex-direction: column; gap: 12px; margin-bottom: 38px; }
.reqs li { font-size: 16px; font-weight: 600; }
.reqs li span { color: var(--white); }
.reqs li em { color: var(--accent); font-style: normal; margin-left: 4px; }
.launcher__visual { display: flex; justify-content: center; }
.launcher__visual img { filter: drop-shadow(0 30px 50px rgba(0,0,0,.55)); }

.how__note {
    margin-top: 34px;
    color: var(--muted);
    font-size: 18px;
    max-width: 1000px;
    line-height: 1.6;
}

/* ======================= СОЦ.СЕТИ ======================= */
/* Общий фон растянут через "Регион в соц.сетях" и "Игровые кадры" (единый background-attachment: fixed для бесшовной стыковки), заканчивается перед футером */
.social, .media { position: relative; overflow: hidden; }
.social::before, .media::before {
    content: '';
    position: absolute;
    inset: 0;
    background-image: url('../photos/fon.webp');
    background-size: cover;
    background-position: center;
    background-attachment: fixed;
    opacity: .02;
    z-index: 0;
    pointer-events: none;
}
.social .container, .media .container { position: relative; z-index: 1; }

.social__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 26px; }

/* Форма карточки — точь-в-точь силуэт photos/papka.webp (вкладка-"папка"),
   наложенная как CSS-маска на псевдоэлемент. Это даёт использовать один и
   тот же контур для всех карточек, но красить каждую в свой градиент —
   вместо трёх отдельных PNG под каждый цвет. */
.soc-card {
    position: relative;
    padding: 40px 34px 10px;
    display: flex;
    flex-direction: column;
    min-height: 300px;
    border: none;
    border-radius: var(--radius);
    transition: transform .35s var(--ease);
    isolation: isolate;
}
/* Серая обводка — отдельный слой того же силуэта (та же маска), сплошного
   серого цвета, чуть больше по размеру (inset отрицательный) и позади
   заливки. border на самом замаскированном элементе рендерился неровно —
   маска у papka.png по краю не резкая, а с лёгким сглаживанием/градиентом
   прозрачности, из-за чего тонкая линия border местами "терялась" или
   подсвечивалась цветом заливки сквозь полупрозрачные пиксели маски.
   Отдельный слой сплошного цвета таких артефактов не даёт — он просто
   виден по всему периметру там, где не закрыт слоем заливки поверх. */
.soc-card::after {
    content: '';
    position: absolute;
    inset: -1px;
    background: rgba(150, 158, 172, .45);
    -webkit-mask-image: url('../photos/papka.webp');
    mask-image: url('../photos/papka.webp');
    -webkit-mask-size: 100% 100%;
    mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    z-index: -2;
}
.soc-card::before {
    content: '';
    position: absolute;
    inset: 0;
    -webkit-mask-image: url('../photos/papka.webp');
    mask-image: url('../photos/papka.webp');
    -webkit-mask-size: 100% 100%;
    mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    z-index: -1;
    transition: filter .35s;
}
.soc-card:hover { transform: translateY(-6px); }
/* drop-shadow, а не box-shadow — box-shadow рисуется по прямоугольной
   рамке элемента и "протекает" ниже маски там, где реальный контур папки
   уже закруглён/срезан. drop-shadow повторяет именно видимый (масками
   обрезанный) силуэт, поэтому тень нигде не торчит за пределы формы. */
.soc-card--tg:hover::before { filter: brightness(1.18) saturate(1.1) drop-shadow(0 22px 32px rgba(56,189,248,.4)); }
.soc-card--vk:hover::before { filter: brightness(1.18) saturate(1.1) drop-shadow(0 22px 32px rgba(74,144,255,.4)); }
.soc-card--ds:hover::before { filter: brightness(1.18) saturate(1.1) drop-shadow(0 22px 32px rgba(137,120,255,.4)); }

/* Градиент снизу вверх — ярче/цветнее у нижнего края, к верху уходит в
   тёмный, как на референсе. Растянут почти на всю карточку (0-88%) для
   максимально размытого, не резкого перехода, а полностью тёмной
   остаётся только узкая полоса у самого верха (88-100%) — под заголовком.
   Второй слой (сплошной тёмный цвет) — непрозрачная подложка ПОД самим
   градиентом: у него самого цвета полупрозрачные (rgba c альфой < 1),
   рассчитанные на просвет пустого тёмного фона страницы. Без этой
   подложки серый слой обводки (::after) просвечивал бы сквозь всю
   заливку целиком, а не только по кромке, - именно так палитра и
   "выцвела" после добавления обводки. */
.soc-card--tg::before { background: linear-gradient(-10deg, rgba(56,189,248,.4) 0%, rgba(30,140,190,.22) 40%, rgba(7,10,16,.92) 70%), #0a0d13; }
.soc-card--vk::before { background: linear-gradient(-10deg, rgba(74,144,255,.4) 0%, rgba(45,105,230,.22) 40%, rgba(7,10,16,.92) 70%), #0a0d13; }
.soc-card--ds::before { background: linear-gradient(-10deg, rgba(137,120,255,.4) 0%, rgba(105,95,240,.22) 40%, rgba(7,10,16,.92) 70%), #0a0d13; }

.soc-card__head { display: flex; align-items: center; gap: 12px; margin-bottom: 22px; }
.soc-card__icon { width: 44px; height: 44px; border-radius: 12px; object-fit: contain; flex: none; }
.soc-card__icon--vk { width: 30px; height: 30px; }
.soc-card__icon--ds { width: 36px; height: 36px; }
.soc-card__icon--tg { width: 40px; height: 40px; margin-right: -6px; }
.soc-card__head h3 { font-size: 21px; font-weight: 800; letter-spacing: .5px; }
/* Первое предложение (обычный текст) — белое, второе (.accent, после <br>) — синее. */
.soc-card p { color: #fff; font-size: 15px; margin-bottom: auto; }
.soc-card p .accent { color: rgba(159, 199, 255, .95); }
.soc-card .btn { align-self: flex-end; margin-top: 26px; min-width: 150px; padding-right: 40px; border: none; color: #fff; }
.soc-card--tg .btn { background: linear-gradient(135deg, #38bdf8, #0ea5e9); box-shadow: 0 10px 30px -12px rgba(56,189,248,.8); }
.soc-card--vk .btn { background: linear-gradient(135deg, #5b9dff, #3b6bef); box-shadow: 0 10px 30px -12px rgba(74,144,255,.8); }
.soc-card--ds .btn { background: linear-gradient(135deg, #8f7bff, #5865f2); box-shadow: 0 10px 30px -12px rgba(137,120,255,.8); }
.soc-card--tg .btn:hover { box-shadow: 0 16px 38px -12px rgba(56,189,248,.95); }
.soc-card--vk .btn:hover { box-shadow: 0 16px 38px -12px rgba(74,144,255,.95); }
.soc-card--ds .btn:hover { box-shadow: 0 16px 38px -12px rgba(137,120,255,.95); }

/* ======================= ИГРОВЫЕ КАДРЫ ======================= */
.media__grid { display: grid; grid-template-columns: 1fr 1.15fr; gap: 24px; }
.feature {
    position: relative;
    border-radius: var(--radius);
    overflow: hidden;
    min-height: 420px;
    display: flex;
    align-items: flex-end;
    padding: 34px;
}
.feature__img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform .6s var(--ease); }
.feature:hover .feature__img { transform: scale(1.06); }
.feature__overlay { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(8,10,20,.15) 0%, rgba(8,10,20,.35) 45%, rgba(6,12,28,.9) 100%); }
.feature__play {
    position: absolute; top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    width: 64px; height: 64px; border-radius: 18px;
    display: grid; place-items: center;
    background: rgba(159, 199, 255, .92);
    color: #0a1224;
    z-index: 2;
    transition: transform .25s var(--ease);
}
.feature:hover .feature__play { transform: translate(-50%, -50%) scale(1.08); }
.feature__play svg { width: 26px; height: 26px; }
.feature__caption { position: relative; z-index: 2; font-size: clamp(22px, 2.4vw, 30px); font-weight: 800; text-transform: uppercase; line-height: 1.1; }
.feature__caption b { color: var(--accent); }

/* Именно здесь (после базовых правил секций "Как попасть", "Соц.сети",
   "Игровые кадры") — на экранах ≥2601px (контейнер уже расширен через
   --maxw в отдельном правиле выше) сама типографика и карточки этих
   секций были откалиброваны под обычный десктоп и выглядели мелкими
   на таком просторе. Увеличиваем пропорционально тому же масштабу,
   что и у hero-текста на этой ширине. */
@media (min-width: 2601px) {
    section { padding: 100px 0; }
    .section-title { font-size: clamp(54px, 5.2vw, 104px); margin-bottom: 64px; }

    .launcher { gap: 60px; }
    .launcher__title { font-size: clamp(38px, 3vw, 56px); margin-bottom: 34px; }
    .tags { gap: 16px; margin-bottom: 44px; }
    .tag { font-size: 18px; padding: 14px 22px; border-radius: 13px; }
    .launcher__subtitle { font-size: 21px; margin-bottom: 20px; }
    .reqs { gap: 16px; margin-bottom: 48px; }
    .reqs li { font-size: 21px; }
    .btn--lg { padding: 23px 60px; font-size: 21px; border-radius: 17px; }
    .how__note { font-size: 23px; max-width: 1300px; margin-top: 44px; }

    .social__grid { gap: 34px; }
    .soc-card { padding: 40px; }
    .soc-card__head { gap: 16px; margin-bottom: 28px; }
    .soc-card__icon { width: 58px; height: 58px; }
    .soc-card__icon--vk { width: 40px; height: 40px; }
    .soc-card__icon--ds { width: 48px; height: 48px; }
    .soc-card__icon--tg { width: 52px; height: 52px; }
    .soc-card__head h3 { font-size: 28px; }
    .soc-card p { font-size: 19px; }
    .soc-card .btn { font-size: 18px; padding: 16px 44px 16px 22px; min-width: 190px; margin-top: 32px; }

    .media__grid { gap: 32px; }
    .feature { min-height: 560px; padding: 44px; border-radius: 24px; }
    .feature__play { width: 84px; height: 84px; border-radius: 22px; }
    .feature__play svg { width: 34px; height: 34px; }
    .feature__caption { font-size: clamp(30px, 2.4vw, 42px); }
}

.gallery { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(2, 1fr); gap: 16px; }
.gallery__item { border-radius: 16px; overflow: hidden; aspect-ratio: 1 / 1; }
.gallery__item img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s var(--ease); }
.gallery__item:hover img { transform: scale(1.1); }

/* ======================= МАГАЗИН: ПОПОЛНЕНИЕ БАЛАНСА ======================= */
.balance {
    position: relative;
    /* Снова 100vh: "Пополнение баланса" теперь отдельная полноэкранная
       страница со снапом (см. html.snap-page ниже), точно как секции на
       главной. Раньше здесь стояла фиксированная высота (820px) —
       компромисс, чтобы каталог не наезжал на форму при обычном свободном
       скролле, но раз секция теперь сама снап-точка и заканчивается ровно
       на границе экрана, эта проблема решается самим снапом, а не хаком. */
    min-height: 100vh;
    scroll-snap-align: start;
    scroll-snap-stop: always;
    display: flex;
    flex-direction: column;
    /* flex-start + padding-top вместо justify-content:center — так отступ от
       хедера до заголовка остаётся одинаковым на любой высоте экрана. Только
       для десктопа: на мобильном ниже возвращаем center, как было. */
    justify-content: flex-start;
    padding-top: 60px;
    padding-bottom: 28px;
    overflow: hidden;
    background:
        /* Тоннельное затемнение — заметно усилено (шире и плотнее), базовый
           фон при этом остаётся светлым, темнеют только края/угол */
        radial-gradient(95% 80% at 72% 100%, rgba(3, 4, 6, .96) 0%, rgba(3, 4, 6, 0) 68%),
        radial-gradient(120% 95% at 50% 0%, rgba(2, 3, 5, .55) 0%, rgba(2, 3, 5, 0) 45%),
        /* Синеватый акцент приглушён — не должен читаться как "подсветка" */
        radial-gradient(90% 70% at 78% 35%, rgba(30, 40, 62, .22) 0%, rgba(8, 9, 12, 0) 58%),
        linear-gradient(180deg, #0a0c11 0%, #08090c 100%);
}
/* Текстура фона — затемнена самостоятельно (filter), а не просто сделана
   полупрозрачной: полупрозрачная светлая картинка на тёмном фоне визуально
   давала подсветку/"свечение", а не затемнение */
.balance::before {
    content: '';
    position: absolute;
    inset: 0;
    background-image: url('../photos/fon_shops.webp');
    background-size: cover;
    background-position: center;
    filter: brightness(.5) saturate(.75);
    opacity: .06;
    z-index: 0;
    pointer-events: none;
}
/* display:flex — иначе margin-bottom заголовка и margin-top формы схлопываются
   как соседние блочные отступы (стандартное поведение CSS для смежных block-боксов),
   и правка margin-top формы визуально ничего не меняла, пока не "перебивала" бы
   margin-bottom заголовка. У flex-элементов отступы не схлопываются. */
/* z-index выше, чем у .balance__fade (3) — иначе на страницах с длинным
   контентом (например, развёрнутое "Подробнее" на new-password.html) текст,
   доходящий до нижней зоны секции, оказывался НИЖЕ затемняющего "тоннельного"
   градиента и читался хуже — эффект должен затемнять фон позади текста, а
   не сам текст поверх него */
.balance__inner { position: relative; z-index: 4; display: flex; flex-direction: column; }
/* Форма опущена и сдвинута левее относительно стандартного отступа контейнера */
.balance__content { max-width: 560px; margin-top: 62px; }

/* ======================= Иллюстрации по бокам формы (shop.html) =======================
   ТЗ Задача 6 — две декоративные фигуры (ППС слева, "уличный" персонаж
   справа), полностью на CSS-брейкпоинтах, без JS. Вертикально
   отцентрованы (top:50%+translateY).

   object-fit:contain, а не cover — принципиально. Раньше стоял cover, и
   у офицера (широкое невысокое фото, 700×573) в узком высоком контейнере
   это заставляло браузер обрезать бОльшую часть ширины кадра, показывая
   случайный вертикальный "ломтик" из середины фото — то, что выглядело
   как "жёсткий порез" и "фигуру уводит влево", было именно это: край
   среза cover проходил через сам кадр, а не по естественной прозрачной
   рамке фотографии. У обоих PNG прозрачные поля по всем четырём краям
   (проверено — альфа=0 в углах), поэтому contain показывает фото
   целиком, и его СОБСТВЕННЫЙ край уже мягкий — никакой mask-image для
   этого больше не нужно.

   Раскрытая проблема contain из прошлой правки (офицер выглядел мельче
   соседа) была не в contain как таковом, а в том, что у контейнера не
   было aspect-ratio, соответствующего фото — при слишком высоком узком
   контейнере широкое фото офицера вписывалось по ширине и оставляло
   пустоту сверху/снизу.

   Раньше здесь был aspect-ratio, зашитый под ПИКСЕЛЬНЫЕ размеры
   конкретного файла на тот момент (700/573 и 700/932) — это работало,
   пока файлы не трогали, но с заменой картинок на другие по размеру
   зашитое здесь старое соотношение сторон начинало спорить с новым
   файлом: object-fit:contain послушно вписывал
   пересохранённую картинку обратно в СТАРУЮ форму рамки, как её ни
   редактируй — снаружи это выглядело как "невидимая рамка не даёт
   увеличить". Теперь aspect-ratio не задан вообще — высота у img просто
   auto, то есть следует НАСТОЯЩИМ пропорциям того файла, что сейчас
   лежит в photos/, каким бы он ни был. max-height — только защита от
   уж совсем случайной крайности (если вдруг сохранят что-то экстремально
   вытянутое), а не рабочий механизм кадрирования. */
/* Боковые иллюстрации магазина (офицер/уличный персонаж) убраны по
   просьбе — вместе с разметкой в shop.html, файлами в photos/ и
   /api/illustration-sizes в server.js. Форма пополнения теперь просто
   центрируется в .balance без привязки к боковым фигурам. */
@media (min-width: 1024px) {
    .shop .balance__inner { align-items: center; }
    .shop .balance__title { margin-left: 0; }
    .shop .balance__content { width: min(560px, 100%); margin-left: auto; margin-right: auto; }
}

/* Курс обмена рубля на внутриигровую валюту + сама форма обмена —
   компактная карточка над формой пополнения (та же ширина колонки,
   .balance__content), рядом с фигурой девушки, но не поверх неё — своей
   раскладкой не задевает точную абсолютную позицию .balance__media (см.
   ниже), которая и так тонко подобрана под десяток разных ширин экрана. */
.currency-rate {
    background: rgba(255, 255, 255, .04);
    border: 1px solid var(--border);
    border-radius: 16px;
    padding: 18px 20px 20px;
    margin-bottom: 22px;
}
.currency-rate__head { display: flex; align-items: center; justify-content: space-between; gap: 14px; margin-bottom: 18px; }
.currency-rate__info { display: flex; align-items: center; gap: 12px; min-width: 0; }
.currency-rate__icon {
    width: 34px;
    height: 34px;
    flex-shrink: 0;
    border-radius: 50%;
    background: var(--coin);
    display: grid;
    place-items: center;
    overflow: hidden;
}
.currency-rate__icon .coin-icon { width: 60%; height: 60%; }
.currency-rate__text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.currency-rate__label { font-size: 12px; color: var(--muted); }
.currency-rate__value { font-size: 15px; font-weight: 800; color: var(--white); white-space: nowrap; }
.currency-rate__unit { color: var(--accent); }
/* Ссылка на #starter-packs — заменила собой убранный блок "подъёмных"
   (лор-строку и кнопку "Получить"), см. .starter-packs в этом файле. */
.currency-rate__starter-link { display: inline-block; margin: 0; font-size: 14px; font-weight: 600; color: var(--accent); text-decoration: none; }
.currency-rate__starter-link:hover { text-decoration: underline; }

/* Баланс RC — плашка справа от курса, а не отдельной строкой текста ниже
   (см. applyShopBalance в main.js — то же значение видно и в капсуле у
   поля "Сумма пополнения") */
.currency-rate__balance {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
    background: linear-gradient(135deg, rgba(70, 147, 255, .18), rgba(70, 147, 255, .05));
    border: 1px solid rgba(70, 147, 255, .35);
    border-radius: 999px;
    padding: 7px 16px 7px 7px;
}
/* Тот же кружок-значок, что и у капсулы .field__coin в форме пополнения ниже —
   единый визуальный язык для RC на всей странице */
.currency-rate__balance-badge {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--coin);
    display: grid;
    place-items: center;
    overflow: hidden;
    flex-shrink: 0;
}
.currency-rate__balance-badge .coin-icon { width: 60%; height: 60%; }
.currency-rate__balance-label { font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; color: var(--accent); white-space: nowrap; }
.currency-rate__balance-value { font-size: 16px; font-weight: 800; color: var(--white); white-space: nowrap; margin-left: 2px; }

/* На узких экранах курсу и плашке баланса не хватает места в одну строку —
   текст начинает переноситься вразнобой. Ниже определённой ширины
   раскладываем их друг под другом на всю ширину карточки, а не сжимаем. */
@media (max-width: 480px) {
    .currency-rate__head { flex-direction: column; align-items: stretch; gap: 12px; }
    .currency-rate__balance { align-self: stretch; justify-content: space-between; }
}

/* Общий вид неактивной кнопки — нужен модалке покупки в каталоге (#catalogModalConfirm) */
.btn:disabled { opacity: .5; cursor: not-allowed; box-shadow: none; }
.btn:disabled:hover { transform: none; }
/* Заголовок вынесен из левой колонки и смещён к центру над фигурой девушки —
   не так далеко вправо, как раньше (иначе слово полностью наезжало на лицо) */
.balance__title {
    margin-left: 8%;
    text-align: center;
    margin-bottom: 46px;
}
/* Строка-контейнер сжимается по своему тексту (inline-block), а не растягивается
   на всю ширину h1 — иначе граффити ниже центрировалось бы относительно широкого
   h1, а не относительно самого слова "ПОПОЛНЕНИЕ" */
.balance__title-line { display: inline-block; position: relative; }
/* "БАЛАНСА" — position:absolute внутри строки-родителя, тот же приём, что и с
   CRMP в hero (.hero__graffiti): margin-based наложение не сработало здесь из-за
   display:inline-block от .section-title .graffiti (более высокая специфичность
   гасила margin-top у инлайн-блока), а absolute-позиционирование не зависит от
   этого и наезжает на строку выше предсказуемо в любом браузере */
.balance__title-graf {
    position: absolute;
    left: 50%;
    top: 100%;
    transform: translate(-50%, -64%) rotate(-2deg);
    white-space: nowrap;
    pointer-events: none;
}

.balance__media {
    position: absolute;
    z-index: 1;
    /* Меньше right — блок ближе к правому краю секции */
    right: 5%;
    /* На самых широких экранах (>1900px, где действует именно это базовое
       правило — ниже 1900px его перекрывает центрирующий брейкпоинт) фигура
       стояла чуть выше гармоничного положения — слегка опущена */
    top: 1%;
    width: 41%;
    /* aspect-ratio вместо независимого height в процентах — исходный
       woman.png квадратный (1:1), а object-fit:contain на прямоугольном
       блоке, где height% не совпадает с реальными пропорциями картинки,
       оставлял "мёртвую" прозрачную зону снизу блока (фигура упирается в
       ширину и рендерится квадратом, а остаток высоты блока просто пуст).
       Из-за этого mask-image ниже (затухание по низу) на 80–100% высоты
       блока в основном приходился на эту пустую зону, а не на реальные
       пиксели фигуры — затухание почти не успевало сработать, и низ
       фотографии обрывался резким жёстким краем вместо плавного перехода.
       aspect-ratio:1/1 делает блок точно по размеру видимого изображения
       на любом разрешении — mask-image ниже теперь всегда попадает точно
       на реальные пиксели, а не в пустоту. */
    aspect-ratio: 1 / 1;
    height: auto;
    pointer-events: none;
}
/* Круглый синеватый ореол прямо за фигурой, по центру — тот же приём, что и у hero,
   только форма честно круглая (равные ширина/высота) и чуть заметнее */
.balance__media::before {
    content: '';
    position: absolute;
    left: 50%;
    /* Ниже уровня головы — светит будто из-за спины/торса, а не из макушки */
    top: 62%;
    /* Ширина + aspect-ratio (а не % по обеим осям) — иначе на непрямоугольном
       блоке фигуры "круг" превращался бы в вытянутый овал */
    width: 95%;
    aspect-ratio: 1 / 1;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: radial-gradient(circle, rgba(159, 199, 255, .4) 0%, rgba(159, 199, 255, .18) 45%, rgba(159, 199, 255, 0) 74%);
    filter: blur(38px);
    z-index: -1;
}
/* ======================= МАГАЗИН: точная подгонка под пропорции woman.png =======================
   На самом фото фигура (от макушки до низа кадра, от локтя с бокалом слева
   до локтя на бедре справа) занимает не квадрат, а прямоугольник примерно
   0.8 (ширина/высота) — заметно уже базового 1:1. С квадратным блоком и
   object-fit:contain вокруг оставались пустые поля, и локоть не доходил до
   правого края карточки. Блок под точную пропорцию + cover — фигура
   заполняет карточку вплотную (макушка сверху, локоть у края, ступни внизу),
   без лишних полей. */
.shop .balance__media { aspect-ratio: 0.8; }
.shop .balance__char {
    object-fit: cover;
    /* Блок теперь растягивается JS (initBalanceMediaAlign) впритык от
       надписи "БАЛАНСА" до низа видимой части экрана — переход в фон должен
       начинаться у самого низа этого блока, а не на середине, как в общем
       правиле ниже (там расчёт под контур doctor/police, где фигура гораздо
       меньше своего блока). */
    -webkit-mask-image:
        linear-gradient(to bottom, black 0%, black 88%, transparent 99%),
        linear-gradient(to right, transparent 0%, black 4%, black 96%, transparent 100%);
    mask-image:
        linear-gradient(to bottom, black 0%, black 88%, transparent 99%),
        linear-gradient(to right, transparent 0%, black 4%, black 96%, transparent 100%);
}

.balance__char {
    width: 100%;
    height: 100%;
    max-width: none;
    /* object-fit:cover обрезает картинку под размер блока (это буквально его
       назначение — залить контейнер, обрезав всё лишнее). Из-за этого низ
       фигуры "срезало" по границе блока вместо плавного схождения на нет.
       contain вписывает изображение целиком, без обрезки — видно всю
       фигуру, а не только верхнюю часть. У "Магазина" (.shop) это
       переопределено на cover — там блок уже точно подогнан под пропорции
       самой фигуры, обрезать нечего. */
    object-fit: contain;
    object-position: top center;
    /* Тень отсюда убрана — с contain весь прямоугольник холста картинки
       попадает в кадр целиком, и drop-shadow обводил тенью не силуэт
       фигуры, а весь этот прямоугольник (у файла край не идеально
       прозрачный) — выглядело как рамка вокруг фото. */
    /* Затухание конца фигуры (её собственного низа, а не границы блока) —
       переход в следующую секцию мягкий, без резкой границы. Начинается
       раньше (55%, было 80%), потому что в исходном файле фигура доходит
       почти до самого края холста (ступни обрезаны практически впритык) —
       узкая зона затухания не успевала погасить яркость до реального края
       картинки, и снизу оставался заметный жёсткий срез */
    -webkit-mask-image:
        linear-gradient(to bottom, black 0%, black 55%, transparent 96%),
        linear-gradient(to right, transparent 0%, black 4%, black 96%, transparent 100%);
    mask-image:
        linear-gradient(to bottom, black 0%, black 55%, transparent 96%),
        linear-gradient(to right, transparent 0%, black 4%, black 96%, transparent 100%);
    -webkit-mask-composite: source-in;
    mask-composite: intersect;
}

/* ======================= ПЛАВАЮЩАЯ КНОПКА "РУЛЕТКА" ======================= */
/* Зафиксирована в правом нижнем углу ЭКРАНА (position:fixed, не absolute) —
   остаётся на месте при скролле, как и .to-top в левом нижнем углу (см. её
   ниже — тот же приём, просто с другой стороны, коллизий между ними нет).
   Раньше была абсолютно спозиционирована рядом с фигурой девушки и
   прокручивалась вместе со страницей. На узких экранах по-прежнему скрыта,
   вместо неё .roulette-btn-mobile, уже в потоке страницы (см. брейкпоинт внизу). */
.roulette-fab {
    position: fixed;
    z-index: 900;
    right: 28px;
    bottom: 28px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    text-decoration: none;
    padding: 16px 18px;
    border-radius: 20px;
    background: linear-gradient(160deg, rgba(70, 147, 255, .22), rgba(70, 147, 255, .06));
    border: 1px solid rgba(70, 147, 255, .4);
    color: var(--white);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    box-shadow: 0 16px 34px -16px rgba(0, 0, 0, .6);
    transition: transform .25s var(--ease), box-shadow .25s var(--ease), border-color .25s var(--ease);
}
.roulette-fab:hover {
    transform: translateY(-6px);
    box-shadow: 0 16px 38px rgba(70, 147, 255, .28);
    border-color: rgba(70, 147, 255, .7);
}
/* Кольцо-пульс за иконкой — тот же "живой" акцент, что и точка "в сети"/счётчик
   онлайна на сайте, только кольцом, а не точкой: расширяется и растворяется */
.roulette-fab__ring {
    position: absolute;
    top: 16px;
    left: 50%;
    width: 48px;
    height: 48px;
    margin-left: -24px;
    border-radius: 50%;
    border: 1px solid rgba(70, 147, 255, .6);
    animation: roulette-fab-pulse 2.2s ease-out infinite;
    pointer-events: none;
}
@keyframes roulette-fab-pulse {
    0% { transform: scale(.85); opacity: .8; }
    100% { transform: scale(1.55); opacity: 0; }
}
.roulette-fab__icon {
    position: relative;
    width: 48px;
    height: 48px;
    border-radius: 50%;
    background: var(--coin);
    display: grid;
    place-items: center;
    color: var(--white);
    animation: roulette-fab-spin 7s linear infinite;
}
.roulette-fab__icon svg { width: 60%; height: 60%; }
@keyframes roulette-fab-spin {
    to { transform: rotate(360deg); }
}
.roulette-fab__label {
    font-size: 12px;
    font-weight: 800;
    letter-spacing: .04em;
    text-transform: uppercase;
}
/* Мобильная кнопка — скрыта по умолчанию, показывается вместо .roulette-fab
   ниже определённой ширины (см. брейкпоинт в самом конце файла) */
.roulette-btn-mobile { display: none; align-items: center; justify-content: center; gap: 10px; margin: -8px 0 24px; }
.roulette-btn-mobile svg { width: 20px; height: 20px; }

/* Плавный переход к следующей секции: тёмный градиент поверх всего низа
   .balance, растворяющий фото/фон/тоннельный эффект в сплошной фон каталога —
   без него граница между секциями выглядела резкой "линией разреза" */
.balance__fade {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    /* Ещё выше — фигуру приподняли, и линия перехода должна оказаться там же,
       где заканчивается видимая часть фото, а не значительно ниже неё */
    height: 480px;
    z-index: 3;
    pointer-events: none;
    background: linear-gradient(to bottom, rgba(8, 9, 12, 0) 0%, var(--bg) 100%);
}

.balance__form { display: flex; flex-direction: column; gap: 14px; }

/* ======================= Гость/авторизован — ТЗ №2 (ревью), задача 3 =======================
   Ручной ввод почты/никнейма убран — гостю вместо формы CTA "Войдите...",
   у вошедшего аккаунт показан текстовой строкой вместо двух полей ввода. */
.balance__guest {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 20px;
    padding: 32px 28px;
    text-align: center;
}
.balance__guest-title { font-size: 19px; font-weight: 800; color: var(--white); margin-bottom: 10px; }
.balance__guest-text { font-size: 14px; color: var(--muted); line-height: 1.6; max-width: 420px; margin: 0 auto 22px; }
.balance__guest-actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }

.field--account {
    display: flex;
    align-items: center;
    gap: 14px;
    background: rgba(255, 255, 255, .05);
    border: 1px solid var(--border);
    border-radius: 16px;
    padding: 16px 20px;
    flex-wrap: wrap;
    row-gap: 10px;
}
/* Иконка аккаунта — чисто декоративная, задаёт визуальный якорь блоку,
   вместо того чтобы он был просто строкой текста. */
.balance__account-icon {
    width: 42px;
    height: 42px;
    border-radius: 12px;
    background: rgba(159, 199, 255, .12);
    color: var(--accent-primary);
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.balance__account-icon svg { width: 22px; height: 22px; }
.balance__account-info { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1 1 160px; }
.balance__account-label { font-size: 12px; color: var(--muted); }
/* Перенос по словам вместо ellipsis — длинный ник теперь виден целиком,
   строка просто становится в две, а не обрезается многоточием. */
.balance__account-nickname { font-size: 16px; font-weight: 600; color: var(--white); overflow-wrap: anywhere; line-height: 1.3; }
/* Сервер — отдельный бейдж, а не строка текста впритык к нику (единственный
   сервер сейчас, см. ТЗ №5 задача 3 — поле выбора убрано, это чисто
   информационная плашка). */
.balance__account-server {
    font-size: 12px;
    font-weight: 600;
    color: var(--muted);
    background: rgba(255, 255, 255, .06);
    border: 1px solid var(--border);
    padding: 6px 12px;
    border-radius: 999px;
    white-space: nowrap;
    flex-shrink: 0;
}

/* ======================= Блок "К зачислению" — ТЗ №2 (ревью), задача 4 ======================= */
.balance__summary {
    background: rgba(255, 255, 255, .05);
    border: 1px solid var(--border);
    border-radius: 16px;
    padding: 16px 20px;
    margin: 20px 0;
}
.balance__summary-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.balance__summary-row--bonus { margin-top: 6px; }
.balance__summary-label { font-size: 14px; color: var(--muted); }
.balance__summary-value { font-size: 22px; font-weight: 500; color: var(--accent); }
.balance__summary-row--bonus .balance__summary-label { font-size: 13px; }
.balance__summary-bonus { font-size: 13px; font-weight: 700; color: #6bd97e; }

.field { position: relative; }
/* Отдельный от .field контекст позиционирования для иконок внутри поля
   (крестик очистки, глаз пароля) — см. ниже */
.field__row { position: relative; }
.field__control {
    width: 100%;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 16px;
    padding: 21px 24px;
    color: var(--white);
    font-family: inherit;
    font-size: 16px;
    transition: border-color .2s var(--ease), background .2s var(--ease);
}
/* Автозаполнение браузера подставляет свой светлый фон под текст и обычным
   background его не перебить — сам браузер перерисовывает поле поверх. Трюк
   с огромной внутренней тенью того же цвета, что и фон поля, "затирает"
   этот светлый фон изнутри, а color/-webkit-text-fill-color возвращают
   обратно наш белый текст (иначе он тоже красится браузером в тёмный) */
.field__control:-webkit-autofill,
.field__control:-webkit-autofill:hover,
.field__control:-webkit-autofill:focus {
    -webkit-box-shadow: 0 0 0 1000px var(--bg-card) inset;
    box-shadow: 0 0 0 1000px var(--bg-card) inset;
    -webkit-text-fill-color: var(--white);
    caret-color: var(--white);
    transition: background-color 999999s ease-in-out 0s;
}
.field__control::placeholder { color: var(--muted); }
.field__control:focus { outline: none; border-color: var(--border-strong); background: rgba(255, 255, 255, .05); }

/* ======================= КРЕСТИК ОЧИСТКИ / ГЛАЗ ПАРОЛЯ ======================= */
/* Иконки — кнопки внутри поля (справа), а не рядом с ним. Обёртка та же
   .field__row, что и у капсулы баланса в сумме пополнения — тот же приём:
   отдельный от .field контекст позиционирования, чтобы текст ошибки под
   полем (растит высоту .field) не сдвигал иконки вниз вместе с собой. */
.field--clearable .field__control { padding-right: 52px; }
.field--password .field__control { padding-right: 88px; }
.field__icon-btn {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 34px;
    height: 34px;
    display: grid;
    place-items: center;
    background: none;
    border: none;
    padding: 0;
    color: var(--muted);
    cursor: pointer;
    transition: color .2s var(--ease);
}
.field__icon-btn:hover { color: var(--white); }
.field__icon-btn svg { width: 18px; height: 18px; }
.field__clear {
    right: 8px;
    /* Крестик виден, только когда в поле что-то введено (класс вешает JS) */
    opacity: 0;
    pointer-events: none;
    transition: opacity .15s var(--ease), color .2s var(--ease);
}
.field--has-value .field__clear { opacity: 1; pointer-events: auto; }
/* В поле пароля два значка сразу — крестик слева, глаз справа (ближе к
   краю), и расстояние между ними чуть меньше, чем было раньше */
.field--password .field__clear { right: 38px; }
.field__toggle-pass { right: 8px; }
/* По умолчанию показан открытый глаз (пароль скрыт) — второй SVG (перечёркнутый)
   скрыт до переключения атрибута aria-pressed на кнопке */
.field__toggle-pass .icon-eye-off { display: none; }
.field__toggle-pass[aria-pressed="true"] .icon-eye { display: none; }
.field__toggle-pass[aria-pressed="true"] .icon-eye-off { display: block; }

/* ======================= ОШИБКИ ВАЛИДАЦИИ ФОРМЫ ======================= */
.field.has-error .field__control {
    border-color: #ff5c6c;
    background: rgba(255, 92, 108, .06);
}
.field.has-error .field__control:focus { border-color: #ff5c6c; }
.field__error {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 8px;
    font-size: 13px;
    font-weight: 600;
    color: #ff8792;
    animation: field-error-in .2s var(--ease);
}
.field__error::before {
    content: '!';
    flex: none;
    width: 15px;
    height: 15px;
    border-radius: 50%;
    background: rgba(255, 92, 108, .18);
    color: #ff8792;
    font-size: 11px;
    font-weight: 800;
    display: grid;
    place-items: center;
}
@keyframes field-error-in {
    from { opacity: 0; transform: translateY(-4px); }
    to { opacity: 1; transform: none; }
}

/* Живой пересчёт суммы пополнения в игровую валюту по курсу (см.
   .currency-rate выше, initBalanceForm в main.js) — тот же отступ/анимация,
   что у .field__error, но акцентным цветом, а не тревожным красным */
.field__hint {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 8px;
    font-size: 13px;
    font-weight: 600;
    color: var(--accent);
    animation: field-error-in .2s var(--ease);
}

/* ======================= Пресеты суммы пополнения ======================= */
/* Шесть готовых сумм над полем ручного ввода (см. .amount-presets в
   shop.html) — клик подставляет значение в #amountInput и ведёт себя как
   обычный ввод (событие 'input' диспатчится вручную, см. initBalanceForm в
   main.js), поэтому крестик очистки/подсказка "К зачислению" работают
   одинаково для пресета и ручной суммы. */
/* ТЗ №2 (ревью), задача 6 — бейдж "Популярный выбор" раньше стоял НАД
   кнопкой 500₽ (отдельным элементом в потоке), из-за чего первый ряд кнопок
   визуально не выравнивался по высоте (третья кнопка ниже соседних). Теперь
   бейдж — position:absolute ВНУТРИ самой кнопки (см. .amount-preset__badge
   ниже), overflow:visible на кнопке и align-items:stretch на сетке — все
   кнопки ряда всегда одной высоты независимо от бейджа. */
.amount-presets { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; align-items: stretch; }
/* ТЗ №3, задача A1 — рамка означает РОВНО ОДНО: "эта кнопка выбрана".
   Раньше и "популярное" (.amount-preset--popular, всегда), и "выбрано"
   (.is-active) красили рамку похожим голубым — при выборе 5000₽ рамка
   500₽ (популярной) и рамка 5000₽ (выбранной) читались как одно и то же
   состояние, пользователь не мог понять, какая сумма спишется. Теперь
   "популярное" — только бейдж (см. .amount-preset__badge ниже, кнопка в
   невыбранном виде ничем не отличается от обычной), а "выбрано" — только
   заливка (сильный, однозначный сигнал, который физически не спутать с
   тонкой 1px рамкой). */
.amount-preset {
    position: relative;
    overflow: visible;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: 2px;
    min-height: 76px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 22px 14px 12px;
    text-align: left;
    cursor: pointer;
    transition: border-color .2s var(--ease), background .2s var(--ease), color .2s var(--ease), transform .15s var(--ease);
}
.amount-preset:hover { border-color: var(--border-strong); transform: translateY(-1px); }
/* Фокус — отдельное от "выбрано" кольцо (клавиатурная навигация, ТЗ
   требует их не путать). :focus-visible — не показывать кольцо на обычный
   мышиный клик, только при реальной навигации с клавиатуры. */
.amount-preset:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Выбранная — заливка акцентным, текст тёмный контрастный (не рамка) */
.amount-preset.is-active {
    background: var(--accent);
    border-color: var(--accent-soft);
}
.amount-preset.is-active .amount-preset__rub { color: #0a1a33; }
.amount-preset.is-active .amount-preset__rc { color: rgba(10, 26, 51, .7); }
.amount-preset.is-active .amount-preset__bonus { color: #0a1a33; }

.amount-preset__rub { font-size: 15px; font-weight: 800; color: var(--white); transition: color .2s var(--ease); }
.amount-preset__rc { font-size: 12.5px; color: var(--muted); transition: color .2s var(--ease); }
/* Бейдж бонуса — нижний правый угол (верхний правый занят "ПОПУЛЯРНОЕ" у
   пресета 500₽, см. .amount-preset__badge ниже — два бейджа не должны
   накладываться друг на друга) */
.amount-preset__bonus {
    position: absolute;
    bottom: 10px;
    right: 12px;
    font-size: 11px;
    font-weight: 800;
    color: #6bd97e;
    transition: color .2s var(--ease);
}
/* Бейдж "ПОПУЛЯРНОЕ" — единственный носитель этого смысла (никакой рамки
   у .amount-preset--popular в невыбранном виде, специально не задаём).
   top:6px, а не на границе кнопки — раньше бейдж лежал ровно на верхнем
   крае и визуально выпирал за пределы кнопки; padding-top:22px у самой
   кнопки (см. выше) освобождает ему место, не наезжая на сумму. */
.amount-preset__badge {
    position: absolute;
    top: 6px;
    right: 8px;
    z-index: 2;
    background: var(--accent);
    color: #0a1a33;
    font-size: 10px;
    line-height: 1;
    font-weight: 800;
    letter-spacing: .04em;
    text-transform: uppercase;
    padding: 3px 9px;
    border-radius: 999px;
    transition: background .2s var(--ease), color .2s var(--ease);
}
/* Выбранная популярная кнопка: фон кнопки сам становится акцентным
   голубым — бейдж в прежних (голубой на голубом) цветах слился бы,
   поэтому у него на этом фоне обратная схема: тёмный фон, светлый текст */
.amount-preset.is-active .amount-preset__badge { background: var(--bg); color: var(--white); }

@media (max-width: 640px) {
    .amount-presets { grid-template-columns: repeat(2, 1fr); }
}

.field--select .field__control {
    appearance: none;
    -webkit-appearance: none;
    cursor: pointer;
    color: var(--muted);
}
.field--select .field__control:valid { color: var(--white); }
.field--select::after {
    content: '';
    position: absolute;
    right: 24px;
    top: 50%;
    width: 10px;
    height: 10px;
    border-right: 2px solid var(--muted);
    border-bottom: 2px solid var(--muted);
    transform: translateY(-70%) rotate(45deg);
    pointer-events: none;
}
/* Как только JS собрал кастомный список — прячем оригинальный <select> (он
   остаётся в DOM ради значения/валидации формы) и старую стрелку-псевдоэлемент,
   у кастомного триггера уже есть своя */
.field--select.is-enhanced select.field__control {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    pointer-events: none;
}
.field--select.is-enhanced::after { display: none; }

/* ======================= КАСТОМНЫЙ ВЫПАДАЮЩИЙ СПИСОК ======================= */
.custom-select__trigger {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    text-align: left;
    color: var(--muted);
    font: inherit;
}
.custom-select__label.has-value { color: var(--white); }
.custom-select__chevron {
    flex: none;
    width: 18px;
    height: 18px;
    margin-left: 12px;
    color: var(--muted);
    transition: transform .25s var(--ease);
}
.field--select.is-open .custom-select__chevron { transform: rotate(180deg); color: var(--accent); }
.field--select.is-open .custom-select__trigger {
    border-color: var(--border-strong);
    background: rgba(255, 255, 255, .05);
}

.custom-select__list {
    position: absolute;
    left: 0;
    right: 0;
    top: calc(100% + 8px);
    z-index: 40;
    background: linear-gradient(165deg, rgba(28, 31, 40, .98), rgba(14, 16, 21, .98));
    border: 1px solid var(--border-strong);
    border-radius: 16px;
    padding: 8px;
    box-shadow: 0 24px 48px -16px rgba(0, 0, 0, .7), 0 0 0 1px rgba(255, 255, 255, .03) inset;
    backdrop-filter: blur(18px);
    -webkit-backdrop-filter: blur(18px);
    max-height: 260px;
    overflow-y: auto;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-8px) scale(.98);
    transform-origin: top center;
    transition: opacity .2s var(--ease), transform .2s var(--ease), visibility 0s linear .2s;
    pointer-events: none;
}
.field--select.is-open .custom-select__list {
    opacity: 1;
    visibility: visible;
    transform: translateY(0) scale(1);
    transition: opacity .2s var(--ease), transform .2s var(--ease), visibility 0s;
    pointer-events: auto;
}
.custom-select__option {
    padding: 13px 16px;
    border-radius: 10px;
    color: var(--muted);
    font-size: 15px;
    cursor: pointer;
    transition: background .15s var(--ease), color .15s var(--ease);
}
.custom-select__option:not(:last-child) { margin-bottom: 2px; }
.custom-select__option:hover,
.custom-select__option.is-active {
    background: rgba(159, 199, 255, .12);
    color: var(--white);
}
.custom-select__option.is-selected {
    color: var(--accent);
    background: rgba(159, 199, 255, .08);
    font-weight: 600;
}
.custom-select__list::-webkit-scrollbar { width: 6px; }
.custom-select__list::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 6px; }

/* Стрелочки-степпер у любого <input type="number"> на сайте — родом из
   стиля браузера, а не из общего дизайна, и с тёмной темой смотрятся чужеродно
   (см. скриншот в чате). Точное значение всё равно удобнее вводить с
   клавиатуры или бегунком (см. .punishment-modal__slider), чем этими
   стрелочками — поэтому не заменяем их своими, а просто убираем везде разом,
   одним общим правилом, а не отдельно на каждом числовом поле. */
input[type="number"].field__control {
    -moz-appearance: textfield;
}
input[type="number"].field__control::-webkit-outer-spin-button,
input[type="number"].field__control::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

/* Логотип перекрашен в белый через filter, а не mask-image: mask-image с
   внешним PNG ненадёжно поддерживается (в части браузеров/окружений просто
   ничего не рисует, отсюда и пропадал логотип на ПК) — filter работает
   стабильно везде. brightness(0) превращает картинку в чёрный силуэт по её
   же alpha-каналу, invert(1) переворачивает чёрный в белый. */
.coin-icon {
    display: inline-block;
    object-fit: contain;
    filter: brightness(0) invert(1);
}

.checkbox {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    cursor: pointer;
    font-size: 14px;
    /* Ярче обычного var(--muted) — рядом с затемнением у низа секции текст
       такой яркости остаётся читаемым, а не сливается в тусклое пятно */
    color: #b7bece;
    line-height: 1.5;
    user-select: none;
}
.checkbox input { position: absolute; opacity: 0; width: 0; height: 0; }
.checkbox__box {
    flex: none;
    width: 20px;
    height: 20px;
    margin-top: 1px;
    border-radius: 6px;
    border: 1px solid var(--border-strong);
    background: var(--bg-card);
    position: relative;
    transition: background .2s var(--ease), border-color .2s var(--ease);
}
.checkbox__box::after {
    content: '';
    position: absolute;
    left: 6px;
    top: 2px;
    width: 5px;
    height: 10px;
    border-right: 2px solid #0a1224;
    border-bottom: 2px solid #0a1224;
    transform: rotate(45deg);
    opacity: 0;
    transition: opacity .15s var(--ease);
}
.checkbox input:checked + .checkbox__box {
    background: linear-gradient(135deg, var(--accent-soft), var(--accent));
    border-color: var(--accent);
}
.checkbox input:checked + .checkbox__box::after { opacity: 1; }
.checkbox__label b { color: var(--white); font-weight: 700; }

/* ======================= ЛИЧНЫЙ КАБИНЕТ («Мой персонаж») ======================= */
.profile { padding: 56px 0 100px; }
/* Лёгкое появление личного кабинета при заходе — особенно заметно сразу
   после анимации кнопки на auth.html, вход ощущается одним плавным переходом,
   а не резким обрывом на новой странице */
.profile__enter { animation: profile-enter .5s var(--ease) both; }
@keyframes profile-enter {
    from { opacity: 0; transform: translateY(14px); }
    to { opacity: 1; transform: none; }
}

.profile__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    margin-bottom: 28px;
}
.profile__title { font-size: clamp(26px, 3vw, 34px); font-weight: 800; margin: 0; }
.profile__head-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
/* Точечно только для этой пары: одна кнопка <a>, другая <button> — без
   одинакового line-height <a> получает высоту больше (наследует line-height
   body), чем <button> (браузерный line-height:normal) при том же padding.
   Не трогаем .btn глобально, чтобы не задеть кнопки шапки и другие места сайта */
.profile__head-actions .btn { line-height: 1; }

/* Кнопка "Схрон" (см. initStash в main.js) — та же .btn--ghost, что и
   остальные кнопки шапки профиля, просто с иконкой сундука и бейджем
   непрочитанных/неактивированных призов справа от подписи */
.stash-btn svg { width: 18px; height: 18px; }
.stash-btn__badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    border-radius: 999px;
    background: var(--accent);
    color: #0a1224;
    font-size: 12px;
    font-weight: 800;
}

/* Активные наказания (войс-чат/чат) — по одной компактной плашке на каждое,
   тот же .punishment-pill, что и у блокировки аккаунта/предупреждения (см.
   .account-ban ниже, у самого никнейма) — единый стиль для всех типов
   наказаний. Заполняется динамически из /api/punishments (см. renderBanStrip
   в main.js) — пусто и скрыто, пока не пришли реальные данные. */
.profile__ban { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 30px; }

/* ======================= МЕНЮ "ДЕЙСТВИЯ С ПРОФИЛЕМ" (клик по никнейму) ======================= */
/* Только на player-profile.html — клик по никнейму найденного игрока
   открывает выпадающее меню действий (см. initProfileActionsMenu в main.js).
   Тот же визуальный язык, что и у кастомного select (тёмная карточка,
   пункты со скруглением на ховере), но привязано к самому никнейму, а не к
   полю формы — поэтому свой, отдельный компонент. */
.profile-actions { position: relative; display: inline-block; }
.profile-actions__trigger {
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    font: inherit;
    color: inherit;
    cursor: pointer;
    border-bottom: 1px dashed currentColor;
    opacity: .9;
    transition: opacity .2s var(--ease);
}
.profile-actions__trigger:hover { opacity: 1; }
.profile-actions__menu {
    position: absolute;
    left: 0;
    top: calc(100% + 10px);
    z-index: 40;
    min-width: 240px;
    background: linear-gradient(165deg, rgba(28, 31, 40, .98), rgba(14, 16, 21, .98));
    border: 1px solid var(--border-strong);
    border-radius: 16px;
    padding: 8px;
    box-shadow: 0 24px 48px -16px rgba(0, 0, 0, .7), 0 0 0 1px rgba(255, 255, 255, .03) inset;
    backdrop-filter: blur(18px);
    -webkit-backdrop-filter: blur(18px);
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.profile-actions__menu button {
    text-align: left;
    padding: 12px 14px;
    border-radius: 10px;
    background: transparent;
    border: none;
    color: var(--white);
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    transition: background .15s var(--ease);
}
.profile-actions__menu button:hover { background: rgba(159, 199, 255, .12); }
.profile-actions__menu button[data-danger] { color: #ff8792; }
/* Текущая должность в списке "Изменить должность" (см. renderRankPicker в
   main.js) — не кликабельна, отмечена галочкой прямо в тексте кнопки */
.profile-actions__menu button:disabled { opacity: .55; cursor: default; }
.profile-actions__menu button:disabled:hover { background: transparent; }

/* ======================= ПЛАШКА АКТИВНОГО НАКАЗАНИЯ (единый стиль для всех типов) ======================= */
/* Один и тот же компонент — и у войс/чат-наказаний (.profile__ban), и у
   блокировки аккаунта/предупреждения (.account-ban, у строки "Уровень - ...",
   сразу после плашки ранга; если ранга нет — естественно прижимается к
   никнейму за счёт обычного потока, без JS-переставления DOM). Сама плашка
   с фактом наказания видна всем, кнопка-карандаш (открывает
   .punishment-modal с подробностями и слайдером) — только администратору
   (canManage в ответе /api/punishments). */
.account-ban { display: inline-flex; align-items: center; gap: 8px; }
.punishment-pill {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 6px 6px 6px 14px;
    border-radius: 999px;
    background: rgba(255, 92, 108, .12);
    border: 1px solid rgba(255, 92, 108, .32);
}
.punishment-pill__label { color: #ff8792; font-size: 12.5px; font-weight: 700; white-space: nowrap; }
.punishment-pill__trigger {
    width: 26px;
    height: 26px;
    flex-shrink: 0;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(255, 92, 108, .18);
    border: 1px solid rgba(255, 92, 108, .4);
    color: #ff5c6c;
    cursor: pointer;
    transition: background .2s var(--ease);
}
.punishment-pill__trigger:hover { background: rgba(255, 92, 108, .3); }
.punishment-pill__trigger svg { width: 13px; height: 13px; display: block; }

/* Раскрывающийся блок "Подробнее" в модалке изменения наказания — кто/когда/
   за что/кому выдано. Свёрнут по умолчанию, чтобы не перегружать окно —
   для самого изменения (чекбокс + слайдер ниже) эти детали не нужны. */
.punishment-modal__details-toggle {
    display: flex;
    align-items: center;
    gap: 6px;
    background: none;
    border: none;
    padding: 0;
    margin-bottom: 18px;
    color: var(--accent);
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
}
.punishment-modal__details-toggle svg { width: 12px; height: 12px; transition: transform .2s var(--ease); }
.punishment-modal__details-toggle[aria-expanded="true"] svg { transform: rotate(180deg); }
.punishment-modal__details {
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--bg-card);
    padding: 12px 14px;
    margin: -6px 0 18px;
    display: flex;
    flex-direction: column;
    gap: 9px;
}
.punishment-modal__details-row { display: flex; justify-content: space-between; gap: 14px; font-size: 13px; }
.punishment-modal__details-row span:first-child { color: var(--muted); flex-shrink: 0; }
.punishment-modal__details-row span:last-child { color: var(--white); font-weight: 600; text-align: right; }
/* "Экономия" в модалке пакета документов — ТЗ №2, задача 7 */
.punishment-modal__details-row--savings span:last-child { color: #6bd97e; }

/* Кто/когда/на сколько последний раз поправил срок — отдельным блоком по
   левому краю снизу (не в сетке "подпись/значение" выше: текст тут длиннее
   одной строки и с ней смотрелся бы тесно). Видно, только если наказание
   уже хоть раз меняли после выдачи, см. openPunishmentModal в main.js. */
.punishment-modal__last-change {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin-top: 4px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
    text-align: left;
    font-size: 12.5px;
    line-height: 1.55;
    color: var(--muted);
}
.punishment-modal__last-change b { color: var(--white); font-weight: 700; }
.punishment-modal__last-change svg { width: 14px; height: 14px; flex-shrink: 0; margin-top: 2px; color: var(--muted-2); }

/* Подсказка про обжалование — читалка "Подробности наказания" для обычного
   игрока (см. ensurePunishmentDetailsModal в main.js), янтарный акцент,
   чтобы не путать с самим наказанием (красное) и с успехом (зелёное). */
.punishment-modal__appeal {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin: 4px 0 18px;
    padding: 12px 14px;
    border-radius: 12px;
    background: rgba(255, 184, 77, .1);
    border: 1px solid rgba(255, 184, 77, .3);
}
.punishment-modal__appeal svg { width: 18px; height: 18px; flex-shrink: 0; margin-top: 1px; color: #ffb84d; }
.punishment-modal__appeal p { margin: 0; font-size: 12.5px; line-height: 1.55; color: var(--muted); }

/* ======================= МОДАЛЬНОЕ ОКНО "ИЗМЕНИТЬ НАКАЗАНИЕ" ======================= */
/* Использует базовую разметку/стили .search-modal (тот же оверлей, та же
   карточка) — здесь только то, что добавляется поверх: чекбокс
   "снять/разблокировать" и слайдер минут/дней. */
.punishment-modal__checkbox {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 14px;
    border-radius: 12px;
    border: 1px solid var(--border);
    background: var(--bg-card);
    margin-bottom: 20px;
    cursor: pointer;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--white);
    transition: border-color .2s var(--ease);
}
.punishment-modal__checkbox:hover { border-color: var(--border-strong); }
.punishment-modal__checkbox input {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    accent-color: #ff5c6c;
    cursor: pointer;
}
.punishment-modal__slider-wrap { transition: opacity .2s var(--ease); }
.punishment-modal__slider-wrap.is-disabled { opacity: .35; pointer-events: none; }
.punishment-modal__slider-label {
    display: block;
    font-size: 13px;
    color: var(--muted);
    margin-bottom: 12px;
}
.punishment-modal__slider-row { display: flex; align-items: center; gap: 16px; }
.punishment-modal__slider {
    flex: 1 1 auto;
    -webkit-appearance: none;
    appearance: none;
    height: 6px;
    border-radius: 999px;
    background: var(--border-strong);
    outline: none;
    cursor: pointer;
}
.punishment-modal__slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--accent-soft), var(--accent));
    cursor: pointer;
    border: 3px solid var(--bg-2);
    box-shadow: 0 2px 8px rgba(0, 0, 0, .4);
}
.punishment-modal__slider::-moz-range-thumb {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    border: 3px solid var(--bg-2);
    background: linear-gradient(135deg, var(--accent-soft), var(--accent));
    cursor: pointer;
}
.punishment-modal__value {
    display: flex;
    align-items: baseline;
    gap: 6px;
    flex-shrink: 0;
}
/* Ползунок и это поле — два представления одного значения (см.
   openPunishmentModal в main.js): тянешь ползунок — число обновляется само,
   или можно просто вписать точное число руками */
.punishment-modal__value-input {
    width: 62px;
    background: var(--bg-card);
    border: 1px solid var(--border-strong);
    border-radius: 8px;
    padding: 6px 8px;
    color: var(--white);
    font-weight: 800;
    font-size: 15px;
    text-align: right;
    font-family: inherit;
}
.punishment-modal__value-input:focus { outline: none; border-color: var(--accent); }
/* Стрелочки инкремента нативного <input type="number"> визуально не
   вписываются в дизайн — управление числом и так есть через ползунок рядом */
.punishment-modal__value-input::-webkit-outer-spin-button,
.punishment-modal__value-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.punishment-modal__value-input { -moz-appearance: textfield; }
.punishment-modal__value span { font-size: 13px; color: var(--muted); flex-shrink: 0; }
.punishment-modal__range-labels {
    display: flex;
    justify-content: space-between;
    font-size: 11.5px;
    color: var(--muted-2);
    margin-top: 8px;
}

.profile__level {
    font-size: clamp(18px, 2vw, 22px);
    font-weight: 700;
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    margin-bottom: 18px;
}
/* Бейдж должности наследовал бы крупный размер шрифта родителя (.profile__level) —
   тут он гость, а не часть самого заголовка, поэтому шрифт зафиксирован
   отдельно, вне зависимости от font-size вокруг. Сам цвет фона/рамки задаётся
   ниже в блоке рангов (.profile.rank-*) — здесь только форма. */
.profile__level .profile__badge {
    font-size: 13px;
    font-weight: 700;
    padding: 5px 12px;
    border-radius: 8px;
}

/* Номер аккаунта — отдельной строкой НАД бейджами (см. .profile__badges
   ниже): раньше делили одну строку с бейджами, и с длинным "VIP PREMIUM"
   не хватало места вдвоём — либо номер обрезался в многоточие, либо бейдж
   вылезал за край карточки. */
.profile__account {
    color: var(--accent);
    font-weight: 700;
    font-size: 15px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    margin-bottom: 10px;
}
/* min-width:0 обязателен на самой grid-колонке — без него CSS Grid по
   умолчанию не даёт ячейке сжаться меньше содержимого, и text-overflow
   выше просто не сработает. */
.profile__grid > div { min-width: 0; }
/* flex-wrap — если бейджей когда-нибудь станет больше, чем помещается в
   одну строку колонки аватарки, они переносятся на следующую строку, а не
   вылезают за её пределы */
/* ======================= "В СЕТИ" / "НЕ В СЕТИ" ======================= */
/* Та же пилюля, что и рядом бейдж должности (.profile__level .profile__badge)
   — только точка вместо иконки. По умолчанию — серая и неподвижная ("не в
   сети"), см. isOnline в server.js. Точка мигает так же плавно, как синяя
   точка счётчика онлайна на главной (.online__dot, keyframes dot-blink) —
   единый визуальный язык "живого" индикатора на сайте, просто зелёная. */
.profile-status {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    font-weight: 700;
    padding: 5px 14px 5px 12px;
    border-radius: 999px;
    background: rgba(255, 255, 255, .05);
    border: 1px solid var(--border);
    color: var(--muted);
}
.profile-status__dot {
    width: 8px; height: 8px; border-radius: 50%;
    background: var(--muted-2);
    flex-shrink: 0;
}
.profile-status--online {
    color: #4ade80;
    background: rgba(74, 222, 128, .12);
    border-color: rgba(74, 222, 128, .35);
}
.profile-status--online .profile-status__dot {
    background: #4ade80;
    box-shadow: 0 0 12px #4ade80;
    animation: dot-blink-green 2s ease-in-out infinite;
}
@keyframes dot-blink-green {
    0%, 100% { opacity: 1; box-shadow: 0 0 12px #4ade80; }
    50% { opacity: .3; box-shadow: 0 0 5px #4ade80; }
}

.profile__badges { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 16px; }
.profile__badge { display: inline-flex; align-items: center; gap: 7px; }
.profile__badge svg { width: 19px; height: 19px; flex-shrink: 0; display: block; }
.profile__badge--player { color: var(--muted); }
.profile__badge--player svg { color: var(--muted); }
.profile__badge--admin { font-weight: 800; }

/* ======================= ПРИВИЛЕГИИ ИГРОКА (VIP / VIP Premium) ======================= */
/* Независимо от админского статуса — обычный игрок и администратор оба
   могут быть VIP (см. vip_tier в db.js, /api/players/:nickname/vip в
   server.js). Значок рядом с "ИГРОК"/"АДМ", содержимое (иконка+текст+цвет)
   подменяется в JS (см. renderVipBadge в main.js) — золото для VIP,
   фиолетово-розовый градиент для старшей ступени VIP Premium. */
.profile__badge--vip {
    border-radius: 999px;
    padding: 4px 11px 4px 8px;
    font-weight: 800;
    letter-spacing: .2px;
}
.profile__badge--vip svg { width: 16px; height: 16px; }
/* ТЗ №2 (ревью), задача 9 — "фиолетовый удаляется из палитры полностью", не
   только в разделе магазина: тот же VIP Premium показан и тут, на профиле
   игрока, раньше фиолетовым — оставлять его тут значило бы вернуть ту же
   путаницу, которую задача устраняет в магазине. VIP теперь голубой (был
   золотой — золото теперь зарезервировано только под премиум-предложения,
   см. --accent-premium), VIP Premium — янтарный, обе заливки плоские. */
.profile__badge--vip.is-vip {
    color: #0a1a33;
    background: var(--accent-primary);
}
.profile__badge--vip.is-premium {
    color: #3a2a06;
    background: var(--accent-premium);
}

/* Строка "VIP-статус" в общей статистике — тот же цвет, что и у значка,
   чтобы привилегия бросалась в глаза, а не терялась среди обычных "Нет" */
.profile__stat-value--vip { color: var(--accent-primary); }
.profile__stat-value--premium { color: var(--accent-premium); }

/* ======================= ЦВЕТА РАНГОВ АДМИНИСТРАЦИИ ======================= */
/* Класс ранга ("rank-founder" и т.д.) вешается на .profile целиком (см.
   initWelcomePage в main.js, поле adminRank из /api/me) — красит и никнейм, и
   корону "АДМ" рядом с "ИГРОК", и саму плашку должности одним цветом сразу,
   без дублирования разметки под каждый ранг. Основатель проекта — строгий
   красный (тот же акцент, что и у полосы наказания/ошибок валидации), у
   остальных рангов — свои узнаваемые цвета по нарастающей "теплоте" от
   младшего к старшему. Порядок лестницы (см. ADMIN_RANK_LADDER в server.js):
   gamemaster (бирюзовый) → junior (зелёный) → admin (синий, акцент сайта) →
   senior (янтарный) → deputy (оранжевый) → head (фиолетовый) → special
   (розовый) → founder (красный, вне лестницы, статус зашит за аккаунтом). */
.profile.rank-gamemaster .js-nickname,
.profile.rank-gamemaster .profile__badge--admin,
.profile.rank-gamemaster .profile__badge--rank { color: #5eead4; }
.profile.rank-gamemaster .profile__badge--admin svg,
.profile.rank-gamemaster .profile__badge--rank svg { color: #5eead4; stroke: currentColor; }
.profile.rank-gamemaster .profile__level .profile__badge--rank { background: rgba(94, 234, 212, .12); border: 1px solid rgba(94, 234, 212, .32); }

.profile.rank-junior .js-nickname,
.profile.rank-junior .profile__badge--admin,
.profile.rank-junior .profile__badge--rank { color: #4ade80; }
.profile.rank-junior .profile__badge--admin svg,
.profile.rank-junior .profile__badge--rank svg { color: #4ade80; stroke: currentColor; }
.profile.rank-junior .profile__level .profile__badge--rank { background: rgba(74, 222, 128, .12); border: 1px solid rgba(74, 222, 128, .32); }

.profile.rank-admin .js-nickname,
.profile.rank-admin .profile__badge--admin,
.profile.rank-admin .profile__badge--rank { color: #9fc7ff; }
.profile.rank-admin .profile__badge--admin svg,
.profile.rank-admin .profile__badge--rank svg { color: #9fc7ff; stroke: currentColor; }
.profile.rank-admin .profile__level .profile__badge--rank { background: rgba(159, 199, 255, .12); border: 1px solid rgba(159, 199, 255, .32); }

.profile.rank-senior .js-nickname,
.profile.rank-senior .profile__badge--admin,
.profile.rank-senior .profile__badge--rank { color: #ffb84d; }
.profile.rank-senior .profile__badge--admin svg,
.profile.rank-senior .profile__badge--rank svg { color: #ffb84d; stroke: currentColor; }
.profile.rank-senior .profile__level .profile__badge--rank { background: rgba(255, 184, 77, .12); border: 1px solid rgba(255, 184, 77, .32); }

.profile.rank-deputy .js-nickname,
.profile.rank-deputy .profile__badge--admin,
.profile.rank-deputy .profile__badge--rank { color: #fb923c; }
.profile.rank-deputy .profile__badge--admin svg,
.profile.rank-deputy .profile__badge--rank svg { color: #fb923c; stroke: currentColor; }
.profile.rank-deputy .profile__level .profile__badge--rank { background: rgba(251, 146, 60, .12); border: 1px solid rgba(251, 146, 60, .32); }

.profile.rank-head .js-nickname,
.profile.rank-head .profile__badge--admin,
.profile.rank-head .profile__badge--rank { color: #c084fc; }
.profile.rank-head .profile__badge--admin svg,
.profile.rank-head .profile__badge--rank svg { color: #c084fc; stroke: currentColor; }
.profile.rank-head .profile__level .profile__badge--rank { background: rgba(192, 132, 252, .12); border: 1px solid rgba(192, 132, 252, .32); }

.profile.rank-special .js-nickname,
.profile.rank-special .profile__badge--admin,
.profile.rank-special .profile__badge--rank { color: #f472b6; }
.profile.rank-special .profile__badge--admin svg,
.profile.rank-special .profile__badge--rank svg { color: #f472b6; stroke: currentColor; }
.profile.rank-special .profile__level .profile__badge--rank { background: rgba(244, 114, 182, .12); border: 1px solid rgba(244, 114, 182, .32); }

.profile.rank-founder .js-nickname,
.profile.rank-founder .profile__badge--admin,
.profile.rank-founder .profile__badge--rank { color: #ff5c6c; }
.profile.rank-founder .profile__badge--admin svg,
.profile.rank-founder .profile__badge--rank svg { color: #ff5c6c; stroke: currentColor; }
.profile.rank-founder .profile__level .profile__badge--rank { background: rgba(255, 92, 108, .12); border: 1px solid rgba(255, 92, 108, .32); }

/* Фото и полоса опыта — одной ширины (ширина колонки), чтобы всё было
   выровнено ровно друг под другом, без перекоса в размерах */
.profile__grid { display: grid; grid-template-columns: 340px 1fr; gap: 48px; align-items: start; }

.profile__avatar {
    width: 100%;
    aspect-ratio: 3 / 4;
    border-radius: 18px;
    overflow: hidden;
    background: var(--bg-card);
    border: 1px solid var(--border);
}
.profile__avatar img { width: 100%; height: 100%; object-fit: cover; object-position: center 20%; }

/* ======================= СМЕНА АВАТАРА (наведение на фото, только свой профиль) ======================= */
/* .profile__avatar-overlay — полупрозрачная подсказка поверх фото, видна
   только при наведении/фокусе; клик открывает системный выбор файла (см.
   initAvatarUpload в main.js, POST /api/avatar — 10 МБ и проверка
   содержимого на сервере, а не только по расширению). */
.profile__avatar--editable { position: relative; }
.profile__avatar-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    background: linear-gradient(180deg, rgba(10, 11, 15, .35), rgba(10, 11, 15, .78));
    border: none;
    color: var(--white);
    font-size: 14px;
    font-weight: 700;
    cursor: pointer;
    opacity: 0;
    transition: opacity .2s var(--ease);
}
.profile__avatar-overlay svg { width: 30px; height: 30px; }
.profile__avatar--editable:hover .profile__avatar-overlay,
.profile__avatar--editable:focus-within .profile__avatar-overlay { opacity: 1; }
/* На тач-устройствах ":hover" не отпускает — оверлей всегда виден слабой
   тенью снизу, чтобы было очевидно, что фото вообще можно менять */
@media (hover: none) {
    .profile__avatar-overlay {
        opacity: 1;
        inset: auto 0 0 0;
        height: 30%;
        background: linear-gradient(180deg, transparent, rgba(10, 11, 15, .85));
        flex-direction: row;
        font-size: 12.5px;
    }
    .profile__avatar-overlay svg { width: 20px; height: 20px; }
}

.profile__avatar-remove {
    position: absolute;
    top: 10px;
    right: 10px;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(10, 11, 15, .55);
    border: 1px solid rgba(255, 255, 255, .18);
    color: var(--white);
    cursor: pointer;
    opacity: 0;
    transition: opacity .2s var(--ease), background .2s var(--ease);
}
.profile__avatar-remove svg { width: 14px; height: 14px; }
.profile__avatar-remove:hover { background: rgba(255, 92, 108, .5); }
.profile__avatar--editable:hover .profile__avatar-remove,
.profile__avatar--editable:focus-within .profile__avatar-remove { opacity: 1; }
@media (hover: none) { .profile__avatar-remove { opacity: 1; } }

.profile__avatar-empty {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    color: var(--muted);
    background: radial-gradient(120% 100% at 50% 15%, rgba(159, 199, 255, .08), transparent 60%);
}
.profile__avatar-empty svg { width: 56px; height: 56px; color: var(--muted-2); }
.profile__avatar-empty span { font-size: 13px; font-weight: 700; }

/* ======================= МОДАЛЬНОЕ ОКНО "ОБРЕЗКА ФОТО" ======================= */
/* Показывается сразу после выбора файла (см. openAvatarCropModal в main.js)
   — перед загрузкой на сервер, не после. Окно — то же .search-modal, что и
   везде на сайте; своя часть — окошко-видоискатель фиксированного размера
   (то же соотношение сторон 3/4, что у самого .profile__avatar) с фото
   внутри, которое можно таскать и увеличивать. */
.avatar-crop__card { max-width: 420px; }
.avatar-crop__viewport {
    position: relative;
    width: 100%;
    max-width: 260px;
    aspect-ratio: 3 / 4;
    margin: 4px auto 20px;
    border-radius: 18px;
    overflow: hidden;
    background: var(--bg-card);
    border: 1px solid var(--border);
    cursor: grab;
    touch-action: none;
}
.avatar-crop__viewport.is-dragging { cursor: grabbing; }
.avatar-crop__img {
    position: absolute;
    top: 0;
    left: 0;
    max-width: none;
    transform-origin: 0 0;
    pointer-events: none;
    user-select: none;
}
.avatar-crop__zoom { display: flex; align-items: center; gap: 14px; margin-bottom: 4px; }
.avatar-crop__zoom svg { width: 18px; height: 18px; color: var(--muted); flex-shrink: 0; }

/* ======================= КАРТОЧКА ПРОГРЕССА УРОВНЯ ======================= */
/* Тот же язык карточек, что у .online в шапке главной: фон .bg-card,
   тонкая рамка, крупный акцентный акцент на главном числе */
/* Карточка ровно под ширину фотографии (колонка .profile__grid), поэтому
   вся внутренняя раскладка рассчитана на узкую ширину: заголовок — отдельной
   строкой сверху (не отжимает бар в середине), а не втиснут в общий ряд */
.level-card {
    margin-top: 24px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 18px;
    padding: 22px 20px 16px;
}
.level-card__title { display: flex; align-items: center; justify-content: center; gap: 7px; font-size: 13px; color: var(--muted); margin: 4px 0 16px; text-align: center; }
.level-card__title svg { width: 14px; height: 14px; color: var(--accent); flex-shrink: 0; }

/* Отступ побольше между кружками уровня и баром — иначе курсор на нулевом
   прогрессе визуально сливается с кружком "1" вплотную */
.level-card__row { display: flex; align-items: center; gap: 22px; }

.level-card__level { display: flex; flex-direction: column; align-items: center; gap: 6px; flex-shrink: 0; }
.level-card__level-num {
    width: 38px;
    height: 38px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    font-size: 15px;
    font-weight: 800;
}
.level-card__level--current .level-card__level-num {
    background: linear-gradient(135deg, var(--accent-soft), var(--accent));
    color: #0a1224;
    box-shadow: 0 0 0 4px rgba(159, 199, 255, .18), 0 10px 24px -10px rgba(159, 199, 255, .65);
}
.level-card__level--next .level-card__level-num {
    background: transparent;
    border: 2px dashed var(--border-strong);
    color: var(--muted);
}
.level-card__level-label {
    font-size: 10.5px;
    color: var(--muted);
    text-align: center;
    white-space: nowrap;
    text-transform: uppercase;
    letter-spacing: .3px;
}

.level-card__mid { flex: 1; min-width: 0; }

/* Обёртка нужна, чтобы курсор с текущим значением мог свободно висеть над
   полосой (сама полоса — overflow:hidden ради плоских краёв заливки) */
.level-card__bar { position: relative; }
.level-card__track { height: 18px; border-radius: 9px; background: rgba(255, 255, 255, .06); border: 1px solid var(--border); overflow: hidden; }
.level-card__fill { height: 100%; border-radius: 9px; background: linear-gradient(90deg, var(--accent-soft), var(--accent)); transition: width .4s var(--ease); }

/* Курсор-указатель прогресса: кружок ровно на границе заливки + всплывающее
   значение "сколько часов уже отыграно" над ним — движется вместе с width заливки */
.level-card__cursor { position: absolute; top: 50%; transform: translate(-50%, -50%); z-index: 2; transition: left .4s var(--ease); }
.level-card__cursor-dot {
    display: block;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: var(--white);
    border: 3px solid var(--accent);
    box-shadow: 0 0 0 5px rgba(159, 199, 255, .22), 0 2px 8px rgba(0, 0, 0, .4);
}
.level-card__cursor-tip {
    position: absolute;
    bottom: calc(100% + 9px);
    left: 50%;
    transform: translateX(-50%);
    background: var(--accent);
    color: #0a1224;
    font-weight: 800;
    font-size: 11.5px;
    white-space: nowrap;
    padding: 3px 9px;
    border-radius: 6px;
}
.level-card__cursor-tip::after {
    content: '';
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    border: 5px solid transparent;
    border-top-color: var(--accent);
}

/* Каждая строка подвала — на своей полосе, а не в общий ряд: на узкой
   карточке (по ширине фото) вариант "в строку" начинал переноситься и
   выглядел тесно */
.level-card__foot { display: flex; flex-direction: column; gap: 5px; margin-top: 12px; font-size: 12.5px; color: var(--muted); text-align: center; }
.level-card__foot b { color: var(--white); font-weight: 700; }

.profile__stats-title { font-size: 18px; font-weight: 800; margin: 0 0 18px; }
/* Раньше это была CSS Grid с "grid-auto-flow: column" и жёстко прибитым
   числом строк (repeat(13, auto)) — ровно под 26 строк статистики. Стоило
   добавить один новый пункт ("VIP-статус"), как счёт съехал, и последняя
   строка вываливалась в невидимую третью колонку, сжимаясь до нечитаемого
   размера. Многоколоночная вёрстка (column-count) сама балансирует высоту
   между колонками при ЛЮБОМ количестве строк — никаких магических чисел
   для пересчёта при следующем добавлении пункта в статистику. */
.profile__stats { column-count: 2; column-gap: 22px; }
.profile__stat {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 16px 20px;
    margin-bottom: 14px;
    font-size: 15.5px;
    break-inside: avoid;
}
.profile__stat-label { color: var(--muted); }
.profile__stat-value { color: var(--white); font-weight: 700; text-align: right; }

.profile__vehicles-title { font-size: 20px; font-weight: 800; margin: 56px 0 22px; }
/* 5 слотов теперь (см. renderVehiclesGrid в main.js): 2 свободных + 1 c
   VIP + 1 c VIP Premium + 1 всегда ведущий в магазин */
.vehicle-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px; }
.vehicle-card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 16px;
    padding: 18px;
    min-height: 190px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: 12px;
}
.vehicle-card img { max-width: 100%; max-height: 96px; object-fit: contain; }
.vehicle-card__name { font-weight: 700; font-size: 14px; }
.vehicle-card--empty .vehicle-card__name,
.vehicle-card--locked .vehicle-card__name { color: var(--muted); font-weight: 600; }
.vehicle-card--locked svg { color: var(--muted-2); }
/* Что именно откроет слот ("Нужен VIP" / "Нужен VIP Premium") — под
   основной подписью, помельче и приглушённей */
.vehicle-card__hint { font-size: 12px; color: var(--muted-2); font-weight: 600; }

/* Пятый слот — не машина, а прямая ссылка в магазин (см.
   renderVehiclesGrid в main.js) — акцентная рамка вместо серой, курсор
   показывает, что карточка кликабельна */
.vehicle-card--shop {
    border-color: rgba(159, 199, 255, .35);
    background: radial-gradient(120% 100% at 50% 0%, rgba(159, 199, 255, .1), var(--bg-card) 70%);
    cursor: pointer;
    transition: border-color .2s var(--ease), transform .2s var(--ease);
}
.vehicle-card--shop:hover { border-color: var(--accent); transform: translateY(-2px); }
.vehicle-card--shop svg { width: 30px; height: 30px; color: var(--accent); }
.vehicle-card--shop .vehicle-card__name { color: var(--accent); }

@media (max-width: 1100px) {
    .vehicle-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 900px) {
    .profile__grid { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {
    .vehicle-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 560px) {
    .profile__stats { column-count: 1; }
}
@media (max-width: 380px) {
    .vehicle-grid { grid-template-columns: 1fr; }
}

/* ======================= АДМИН-РЕЖИМ ПРОФИЛЯ ======================= */
/* Виден только тем, кто прошёл /api/admin-login (см. initWelcomePage,
   main.js) — обычный игрок весь этот блок не видит вообще (элементы
   скрыты атрибутом hidden в разметке, JS снимает его только при
   подтверждённом isAdmin). Цвет никнейма/короны/должности определяется
   рангом (класс .rank-*, см. блок "ЦВЕТА РАНГОВ АДМИНИСТРАЦИИ" выше) —
   .is-admin сам по себе цвет не красит, только открывает видимость блока. */

/* Полоса вкладок админ-панели — выше "Номер аккаунта". На мобильных и
   планшетах не переносится и не сжимается, а листается по горизонтали
   (тот же приём, что у .profile__ban) — вкладок много, и упаковывать их
   в несколько строк выглядело бы тесно и нечитаемо. */
.admin-tabs { margin-bottom: 24px; overflow-x: auto; touch-action: pan-x; -webkit-overflow-scrolling: touch; }
.admin-tabs__row { display: flex; gap: 10px; width: max-content; min-width: 100%; }
.admin-tabs__tab {
    flex-shrink: 0;
    white-space: nowrap;
    padding: 12px 20px;
    border-radius: 10px;
    border: 1px solid var(--border);
    background: var(--bg-card);
    color: var(--muted);
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    transition: color .2s var(--ease), background .2s var(--ease), border-color .2s var(--ease);
}
.admin-tabs__tab:hover { color: var(--white); border-color: var(--border-strong); }
.admin-tabs__tab.is-active {
    background: linear-gradient(135deg, var(--accent-soft), var(--accent));
    border-color: transparent;
    color: #0a1224;
}

/* "Поиск игрока" — не часть группы переключаемых вкладок справа (у неё нет
   своей панели, клик открывает модальное окно, см. initPlayerSearch в
   main.js), поэтому визуально выделена отдельно: пунктирная рамка и
   приглушённый цвет вместо активной синей заливки — читается как "действие",
   а не как "выбранный раздел", хотя кнопка полностью кликабельна. */
.admin-tabs__tab--search {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    border-style: dashed;
    background: transparent;
}
.admin-tabs__tab--search svg { width: 16px; height: 16px; flex-shrink: 0; }
.admin-tabs__divider { flex-shrink: 0; width: 1px; align-self: stretch; background: var(--border); margin: 2px 2px; }

/* ======================= ЛОГИ (админ-режим) ======================= */
.logs { margin-top: 56px; }
.logs__title { font-size: 20px; font-weight: 800; text-align: center; margin: 0 0 6px; }
.logs__subtitle { text-align: center; color: var(--muted); font-size: 13px; margin: 0 0 18px; }
/* Фильтр периода (24 часа / неделя / 14 дней / месяц) — переключает окно
   выборки для всех трёх таблиц логов (и блога на player-profile.html) сразу,
   см. wireLogsRangeButtons в main.js. Пилюли поменьше и приглушённее вкладок
   типа лога ниже — это вспомогательный, а не основной контрол. */
.logs__range { display: flex; justify-content: center; gap: 8px; flex-wrap: wrap; margin: 0 0 18px; }
.logs__range-btn {
    padding: 7px 14px;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: transparent;
    color: var(--muted);
    font-size: 12.5px;
    font-weight: 600;
    cursor: pointer;
    transition: color .2s var(--ease), border-color .2s var(--ease), background .2s var(--ease);
}
.logs__range-btn:hover { color: var(--white); border-color: var(--border-strong); }
.logs__range-btn.is-active {
    color: #0a1224;
    background: linear-gradient(135deg, var(--accent-soft), var(--accent));
    border-color: transparent;
}
.logs__tabs { display: flex; justify-content: center; gap: 10px; flex-wrap: wrap; margin-bottom: 22px; }
.logs__tab {
    padding: 10px 18px;
    border-radius: 10px;
    border: 1px solid var(--border);
    background: var(--bg-card);
    color: var(--muted);
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    transition: color .2s var(--ease), border-color .2s var(--ease);
}
.logs__tab:hover { color: var(--white); }
.logs__tab.is-active { color: var(--accent); border-color: var(--accent); }

/* overflow-x:auto — на узких экранах таблица листается по горизонтали, а
   не сжимается и не переносит ячейки (та же логика, что у .admin-tabs и
   .profile__ban выше); страница при этом продолжает скроллиться
   вертикально как обычно, скроллы независимы */
/* max-height + overflow-y — таблица логов "за последний месяц" может быть
   длиннее одного экрана, вместо растягивания всей страницы она листается
   собственным скроллом. Нативный скроллбар браузера спрятан глобально
   (см. ::-webkit-scrollbar { display:none } в начале файла) — здесь он
   явно включается заново, только для этого конкретного контейнера, и
   красится под акцентный цвет сайта вместо системного вида. */
.logs__table-wrap {
    overflow-x: auto;
    overflow-y: auto;
    max-height: 380px;
    touch-action: pan-x pan-y;
    -webkit-overflow-scrolling: touch;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--bg-card);
    scrollbar-width: thin;
    scrollbar-color: var(--accent) transparent;
}
.logs__table-wrap::-webkit-scrollbar { display: block; width: 8px; height: 8px; }
.logs__table-wrap::-webkit-scrollbar-track { background: transparent; }
.logs__table-wrap::-webkit-scrollbar-thumb { background: var(--accent); border-radius: 8px; }
.logs__table-wrap::-webkit-scrollbar-thumb:hover { background: var(--accent-deep); }
.logs__table { width: 100%; min-width: 720px; border-collapse: collapse; font-size: 14px; }
.logs__table th {
    position: sticky;
    top: 0;
    text-align: left;
    color: var(--muted);
    font-weight: 600;
    padding: 16px 20px;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
    background: #101319;
    z-index: 1;
}
.logs__table td { padding: 14px 20px; border-bottom: 1px solid var(--border); white-space: nowrap; }
.logs__table tr:last-child td { border-bottom: none; }
.logs__empty { padding: 40px 20px; text-align: center; color: var(--muted); font-size: 14px; }

/* ======================= ТАБЛИЦА СХРОНА (welcome.html) ======================= */
/* Та же .logs__table, что и у логов денег/наказаний — просто своя первая
   колонка с иконкой приза и мягким свечением в цвет его редкости (--rarity
   инлайн на самой картинке, см. renderStashTable в main.js) */
.stash-table-wrap { max-height: 360px; }
/* Колонки "Кейс" и "Статус" — по центру (и заголовок, и содержимое) */
.stash-table th:nth-child(2), .stash-table td:nth-child(2),
.stash-table th:nth-child(4), .stash-table td:nth-child(4) { text-align: center; }
.stash-table td:nth-child(2) .stash-table__case { justify-content: center; }
.stash-table__prize { display: flex; align-items: center; gap: 12px; font-weight: 700; }
.stash-table__prize img {
    width: 36px;
    height: 36px;
    object-fit: contain;
    flex-shrink: 0;
    filter: drop-shadow(0 0 8px var(--rarity, transparent));
    -webkit-mask-image: radial-gradient(ellipse 64% 64% at 50% 50%, #000 55%, transparent 92%);
    mask-image: radial-gradient(ellipse 64% 64% at 50% 50%, #000 55%, transparent 92%);
}
.stash-table__done { color: #4ade80; font-weight: 700; font-size: 13.5px; white-space: nowrap; }
.stash-table__activate { padding: 9px 18px; font-size: 13.5px; }

/* Документы из магазина — отдельный блок под таблицей призов (см.
   #stashDocuments/initDocumentsStash в main.js), скрыт, пока нечего
   показать. Своя таблица переиспользует .stash-table целиком (те же
   колонки "по центру", тот же .stash-table__done/__activate). */
.stash-documents { margin-top: 28px; }
.stash-documents__title { font-size: 16px; margin-bottom: 14px; }
.stash-documents__pending {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    color: var(--muted);
    font-size: 13.5px;
    white-space: nowrap;
}
.stash-documents__flag {
    display: grid;
    place-items: center;
    width: 17px;
    height: 17px;
    border-radius: 50%;
    background: rgba(255, 196, 92, .16);
    border: 1px solid rgba(255, 196, 92, .45);
    color: #ffc45c;
    font-size: 11px;
    font-weight: 800;
    cursor: help;
    flex-shrink: 0;
}
/* Из какого кейса выпал приз — маленькая иконка-ссылка, ведёт прямо на
   страницу этого кейса (см. renderStashTable в main.js) */
.stash-table__case {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    color: var(--muted);
    font-size: 13px;
    font-weight: 600;
    text-decoration: none;
    transition: color .2s var(--ease);
}
.stash-table__case:hover { color: var(--accent); }
.stash-table__case svg { width: 16px; height: 16px; flex-shrink: 0; }


@media (max-width: 900px) {
    .admin-tabs__tab { padding: 11px 16px; font-size: 13px; }
    .logs__table-wrap { max-height: 320px; }
}

/* ======================= БЛОГ ИГРОКА (просмотр чужого профиля) ======================= */
.blog { margin-top: 56px; }
.blog__title { font-size: 20px; font-weight: 800; text-align: center; margin: 0 0 6px; }
.blog__subtitle { text-align: center; color: var(--muted); font-size: 13px; margin: 0 0 22px; }
.blog__list { display: flex; flex-direction: column; gap: 14px; }
.blog__item { border: 1px solid var(--border); border-radius: 14px; background: var(--bg-card); padding: 18px 22px; }
.blog__item-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 8px; }
.blog__item-title { font-weight: 700; font-size: 15px; color: var(--white); }
.blog__item-date { font-size: 12.5px; color: var(--muted-2); white-space: nowrap; }
.blog__item-text { font-size: 14px; line-height: 1.6; color: var(--muted); margin: 0; }
.blog__empty { text-align: center; color: var(--muted); padding: 30px 0; }

/* ======================= МОДАЛЬНОЕ ОКНО "ПОИСК ИГРОКА" ======================= */
/* align-items: flex-start (а не center) — окно должно быть видно сразу,
   прямо под шапкой, там же, где сейчас скролл пользователя, а не где-то по
   центру всей (часто очень длинной) страницы профиля, куда пришлось бы
   долистывать вручную */
.search-modal {
    position: fixed;
    inset: 0;
    z-index: 1200;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 96px 20px 20px;
    opacity: 0;
    pointer-events: none;
    transition: opacity .25s var(--ease);
}
.search-modal.is-visible { opacity: 1; pointer-events: auto; }
.search-modal__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(4, 6, 12, .72);
    backdrop-filter: blur(6px);
}
.search-modal__card {
    position: relative;
    width: 100%;
    max-width: 420px;
    background: var(--bg-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    padding: 32px 28px 28px;
    box-shadow: 0 40px 80px -30px rgba(0, 0, 0, .7);
    transform: translateY(14px) scale(.98);
    transition: transform .25s var(--ease);
}
.search-modal.is-visible .search-modal__card { transform: translateY(0) scale(1); }
/* Модификатор для карточек с таблицей внутри (см. схрон, initStash в
   main.js) — обычная .search-modal__card рассчитана на узкую форму поиска,
   таблице из четырёх колонок там тесно. На широких экранах (компьютер/
   ноутбук) карточка ещё шире — вся таблица видна без горизонтальной
   прокрутки. */
.search-modal__card--wide { max-width: 640px; }
@media (min-width: 760px) {
    .search-modal__card--wide { max-width: 760px; }
}
@media (min-width: 1024px) {
    .search-modal__card--wide { max-width: 920px; }
}
.search-modal__close {
    position: absolute;
    top: 14px;
    right: 14px;
    width: 34px;
    height: 34px;
    border-radius: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: none;
    color: var(--muted);
    font-size: 22px;
    line-height: 1;
    cursor: pointer;
    transition: color .2s var(--ease), background .2s var(--ease);
}
.search-modal__close:hover { color: var(--white); background: rgba(255, 255, 255, .08); }
.search-modal__icon {
    width: 52px;
    height: 52px;
    border-radius: 14px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(159, 199, 255, .12);
    border: 1px solid rgba(159, 199, 255, .3);
    color: var(--accent);
    margin-bottom: 18px;
}
.search-modal__icon svg { width: 24px; height: 24px; }
/* Настоящая картинка купона вместо обычной svg-иконки — своя рамка в цвет
   купона (см. модалку "Купоны на скидку", initCaseCoupon в main.js) */
.search-modal__icon--coupon { background: rgba(255, 215, 0, .12); border-color: rgba(255, 215, 0, .35); }
.search-modal__icon--coupon img { width: 32px; height: 32px; object-fit: contain; }
.search-modal__title { font-size: 19px; font-weight: 800; margin: 0 0 8px; }
.search-modal__text { font-size: 14px; color: var(--muted); line-height: 1.5; margin: 0 0 20px; }
.search-modal__actions { display: flex; gap: 12px; margin-top: 22px; }
.search-modal__actions .btn { flex: 1; }

/* Подсказки по никнейму — выпадают прямо под полем ввода (см.
   initPlayerSearch/renderSuggestions в main.js). max-height ровно на 5
   строк — если совпадений больше, появляется вертикальный скролл (тот же
   акцентный скроллбар, что и у .logs__table-wrap), не растягивая саму карточку. */
.search-modal__suggestions {
    margin-top: 10px;
    margin-bottom: 20px;
    max-height: 220px;
    overflow-y: auto;
    border: 1px solid var(--border-strong);
    border-radius: 12px;
    background: var(--bg-card);
    scrollbar-width: thin;
    scrollbar-color: var(--accent) transparent;
}
.search-modal__suggestions::-webkit-scrollbar { display: block; width: 8px; }
.search-modal__suggestions::-webkit-scrollbar-track { background: transparent; }
.search-modal__suggestions::-webkit-scrollbar-thumb { background: var(--accent); border-radius: 8px; }
.search-modal__suggestion {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 10px 14px;
    background: transparent;
    border: none;
    border-bottom: 1px solid var(--border);
    color: var(--white);
    font-size: 14px;
    font-weight: 600;
    text-align: left;
    cursor: pointer;
    transition: background .15s var(--ease);
}
.search-modal__suggestion:last-child { border-bottom: none; }
.search-modal__suggestion:hover,
.search-modal__suggestion.is-active { background: rgba(255, 255, 255, .06); }
.search-modal__suggestion svg { width: 15px; height: 15px; flex-shrink: 0; color: var(--muted); }
.search-modal__suggestion--admin svg { color: #ff5c6c; }

@media (max-width: 480px) {
    .search-modal__card { padding: 26px 20px 22px; }
    .search-modal__actions { flex-direction: column; }
}

/* ======================= МОДАЛЬНОЕ ОКНО "ОРГАНИЗАЦИИ" ======================= */
/* Использует базовую разметку/стили .search-modal (тот же оверлей, та же
   карточка) — здесь только список организаций и список сотрудников внутри
   (см. initOrgSearch в main.js). */
.org-modal__list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-height: 320px;
    overflow-y: auto;
    margin-bottom: 4px;
    scrollbar-width: thin;
    scrollbar-color: var(--accent) transparent;
}
.org-modal__list::-webkit-scrollbar { display: block; width: 8px; }
.org-modal__list::-webkit-scrollbar-track { background: transparent; }
.org-modal__list::-webkit-scrollbar-thumb { background: var(--accent); border-radius: 8px; }
.org-modal__org-btn {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 16px;
    border-radius: 12px;
    border: 1px solid var(--border);
    background: var(--bg-card);
    color: var(--white);
    cursor: pointer;
    text-align: left;
    font-size: 14px;
    font-weight: 700;
    transition: border-color .2s var(--ease), background .2s var(--ease);
}
.org-modal__org-btn:hover { border-color: var(--border-strong); background: rgba(255, 255, 255, .05); }
.org-modal__org-btn svg { width: 16px; height: 16px; flex-shrink: 0; color: var(--muted); }
.org-modal__org-count {
    flex-shrink: 0;
    font-size: 12px;
    font-weight: 700;
    color: var(--muted);
    background: rgba(255, 255, 255, .06);
    padding: 3px 10px;
    border-radius: 999px;
}
.org-modal__member-btn {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    width: 100%;
    padding: 12px 14px;
    border-radius: 10px;
    border: 1px solid var(--border);
    background: var(--bg-card);
    color: var(--white);
    cursor: pointer;
    text-align: left;
    font-size: 13.5px;
    font-weight: 700;
    transition: border-color .2s var(--ease), background .2s var(--ease);
}
.org-modal__member-btn:hover { border-color: var(--border-strong); background: rgba(255, 255, 255, .05); }
.org-modal__member-rank { flex-shrink: 0; color: var(--muted); font-size: 12px; font-weight: 600; }
.org-modal__back {
    display: flex;
    align-items: center;
    gap: 6px;
    background: none;
    border: none;
    padding: 0;
    margin-bottom: 16px;
    color: var(--accent);
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
}
.org-modal__back svg { width: 14px; height: 14px; }
.org-modal__empty { text-align: center; color: var(--muted); padding: 26px 0; font-size: 13.5px; }

/* Кнопка "Разбанить по IP" на player-profile.html — видна только тем, кто
   пришёл именно из списка "Разбан IP" (см. initIpUnbanButton в main.js).
   Красный акцент — тот же язык, что и у остальных модерационных элементов
   (наказания, блокировка аккаунта). */
.ip-unban-btn {
    color: #ff5c6c;
    border-color: rgba(255, 92, 108, .4);
    background: rgba(255, 92, 108, .1);
}
.ip-unban-btn:hover { background: rgba(255, 92, 108, .2); border-color: rgba(255, 92, 108, .6); }
.ip-unban-btn:disabled { opacity: .6; cursor: default; }

/* ======================= МОДАЛЬНОЕ ОКНО "ПРОМОКОДЫ" ======================= */
/* Список/детали переиспользуют .org-modal__list и .org-modal__member-btn
   (код промокода — вместо никнейма, аудитория — вместо ранга); здесь только
   то, что специфично для промокодов: форма создания/редактирования и
   карточка деталей одного промокода. */
.promo-modal__create-btn { width: 100%; margin-top: 6px; }
.promo-modal__field-label {
    display: block;
    font-size: 12.5px;
    font-weight: 700;
    color: var(--muted);
    margin: 16px 0 8px;
}
.promo-modal__field-label:first-of-type { margin-top: 2px; }
.promo-modal__custom-input { margin-top: 10px; }
.promo-modal__code {
    font-size: 21px;
    font-weight: 800;
    letter-spacing: .5px;
    margin-bottom: 18px;
    word-break: break-all;
}
.promo-modal__delete-btn { color: #ff5c6c; border-color: rgba(255, 92, 108, .4); }
.promo-modal__delete-btn:hover { background: rgba(255, 92, 108, .14); border-color: rgba(255, 92, 108, .6); }

/* ======================= МОДАЛЬНОЕ ОКНО "ЧЁРНЫЙ СПИСОК EMAIL" ======================= */
/* Список переиспользует .org-modal__list (тот же скролл/отступы), но не
   .org-modal__member-btn — строки здесь не ведут никуда по клику, у каждой
   свой собственный элемент удаления справа, поэтому это не <button>, а
   обычная строка с флекс-раскладкой. */
/* .field__control в базовом виде — большое поле форм авторизации/магазина
   (padding 21px 24px, font-size 16px), рядом с обычной .btn (padding 13px
   30px) высоты не совпадают процентов на 30%, и сама .btn ощутимо выше и
   "тяжелее" узкого однострочного поля для домена. Тут — компактная пара
   поле+кнопка одной высоты (40px, только для этой формы, не общий стиль). */
.email-blacklist__add { display: flex; gap: 8px; margin-bottom: 16px; }
.email-blacklist__add .field { flex: 1; }
.email-blacklist__add .field__control {
    height: 40px;
    padding: 0 14px;
    font-size: 13.5px;
    border-radius: 10px;
}
.email-blacklist__add-btn {
    flex-shrink: 0;
    height: 40px;
    padding: 0 18px;
    font-size: 13.5px;
    border-radius: 10px;
}
.email-blacklist__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    width: 100%;
    padding: 12px 14px;
    border-radius: 10px;
    border: 1px solid var(--border);
    background: var(--bg-card);
}
/* Домен + автор/дата — своя колонка слева (min-width:0 — иначе на узких
   экранах flex-элемент отказывался сжиматься меньше своего "естественного"
   содержимого, и место для .email-blacklist__pattern схлопывалось почти до
   нуля, а word-break:break-all разбивал текст по одной букве в строку).
   Автор/дата — отдельной строкой под доменом, а не в той же строке через
   justify-content — так и домен, и подпись всегда читаются целиком. */
.email-blacklist__info { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.email-blacklist__pattern { font-size: 13.5px; font-weight: 700; color: var(--white); word-break: break-all; }
.email-blacklist__meta { color: var(--muted); font-size: 11.5px; font-weight: 600; }
.email-blacklist__remove {
    flex-shrink: 0;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: 1px solid rgba(255, 92, 108, .4);
    background: rgba(255, 92, 108, .1);
    color: #ff5c6c;
    display: grid;
    place-items: center;
    cursor: pointer;
    transition: background .2s var(--ease), border-color .2s var(--ease);
}
.email-blacklist__remove:hover { background: rgba(255, 92, 108, .2); border-color: rgba(255, 92, 108, .6); }
.email-blacklist__remove:disabled { opacity: .5; cursor: default; }
.email-blacklist__remove svg { width: 14px; height: 14px; }

/* Заметка о неизменности ранга основателя — используется в меню "Действия
   с этим профилем" (см. .profile-actions в блоке ниже, initProfileActionsMenu
   в main.js), раньше была частью карточки администратора в отдельном
   модальном окне "Управление администрацией", которое теперь просто список. */
.admin-modal__note { font-size: 12.5px; color: var(--muted); line-height: 1.5; margin: 4px 4px 6px; }

/* ======================= ПРОСМОТР ПРОФИЛЯ ИГРОКА (не найден / нет доступа) ======================= */
.player-notfound { text-align: center; padding: 90px 20px; }
.player-notfound__icon {
    width: 64px;
    height: 64px;
    margin: 0 auto 20px;
    border-radius: 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(255, 92, 108, .12);
    border: 1px solid rgba(255, 92, 108, .3);
    color: #ff5c6c;
}
.player-notfound__icon svg { width: 28px; height: 28px; }
.player-notfound__title { font-size: 20px; font-weight: 800; margin: 0 0 10px; }
.player-notfound__text { color: var(--muted); font-size: 14px; margin: 0 0 26px; }

/* ======================= СТРАНИЦА АВТОРИЗАЦИИ ======================= */
.auth__forgot {
    /* Раньше был отрицательный margin-top, прижимавший ссылку к полю пароля —
       из-за этого отступ до поля и отступ до кнопки ниже был не одинаковым.
       Без него оба зазора равны gap (.balance__form), и ссылка стоит ровно
       посередине между полем пароля и кнопкой "Авторизоваться" */
    /* Флеш с левым краем полей и кнопки "Авторизоваться" — без отступа,
       раньше был margin-left:24px "под текст внутри полей", но так ссылка
       визуально уезжала правее самих полей/кнопки, а не вровень с ними */
    align-self: flex-start;
    color: var(--muted);
    font-size: 14px;
    transition: color .2s var(--ease);
}
.auth__forgot:hover { color: var(--white); }

/* Пояснительный текст на странице восстановления пароля — между полем
   e-mail и полем кода, и строка таймера повторной отправки */
.recover__hint {
    color: var(--muted);
    font-size: 14px;
    line-height: 1.5;
    margin: -4px 0 0;
}
/* Обёртка шага "код подтверждения" — скрыта атрибутом [hidden], пока код
   ещё не отправлен на почту (см. initRecoverForm, js/main.js). Когда видна,
   её собственные дети (подсказка/поле/таймер) должны идти с тем же
   вертикальным зазором, что и у остальных элементов .balance__form — а
   без display:flex он бы потерялся (обычный <div> не наследует flex-gap
   родителя, сам становится лишь одним flex-item). :not([hidden]) — чтобы
   это правило не спорило по specificity с УА-стилем "[hidden]{display:none}" */
.recover__code-step:not([hidden]) {
    display: flex;
    flex-direction: column;
    gap: 14px;
}
.recover__timer { color: var(--accent); font-weight: 700; }
.recover__resend-link {
    color: var(--accent);
    font-weight: 700;
    transition: color .2s var(--ease);
}
.recover__resend-link:hover { color: var(--white); }

/* Правила пароля на странице установки нового пароля + сворачиваемое
   "Подробнее" (нативные <details>/<summary> — раскрытие/закрытие уже
   встроено в браузер, лишний JS не нужен) */
.password-rules {
    margin-top: 24px;
    padding-top: 20px;
    border-top: 1px solid var(--border);
}
.password-rules__text {
    color: var(--muted);
    font-size: 14px;
    line-height: 1.6;
    margin: 0 0 14px;
}
.password-rules__details summary {
    list-style: none;
    cursor: pointer;
    display: inline-block;
    color: var(--accent);
    font-size: 14px;
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 3px;
    transition: color .2s var(--ease);
}
.password-rules__details summary::-webkit-details-marker { display: none; }
.password-rules__details summary:hover { color: var(--white); }
.password-rules__more {
    margin-top: 14px;
    color: var(--muted);
    font-size: 14px;
    line-height: 1.6;
}
.password-rules__more p { margin: 0 0 14px; }
.password-rules__more p:last-child { margin-bottom: 0; }
.password-rules__more b { color: var(--white); }

/* Мелко и приглушённо — справочная ссылка, а не ещё один пункт согласия
   (те уже покрыты чекбоксами выше) */
.balance__refund-note { margin: -6px 0 0; font-size: 12px; color: var(--muted); }
.balance__refund-note a { color: var(--muted); text-decoration: underline; }
.balance__refund-note a:hover { color: var(--accent); }
/* stretch — во всю ширину полей формы, а не по размеру текста кнопки */
.balance__submit { margin-top: 6px; align-self: stretch; }
/* Явно усиленное свечение именно в активном состоянии (когда обе галочки
   отмечены) — раньше разница между "выключена" и "включена" была только в
   opacity, и на ярком фоне формы кнопка едва отличалась глазами. Работает
   одинаково на всех разрешениях, т.к. .balance__submit не переопределяется
   по брейкпоинтам */
.balance__submit:not(:disabled) {
    box-shadow: 0 14px 36px -8px rgba(159, 199, 255, .95);
}
.balance__submit:disabled {
    opacity: .35;
    filter: grayscale(.5) brightness(.85);
    cursor: not-allowed;
    transform: none;
    box-shadow: none;
}

/* ======================= АВТОРИЗАЦИЯ: свои поправки поверх общего .balance ======================= */
/* Заголовок "Пополнение баланса" в шаблоне сделан двухстрочным намеренно
   (графити наезжает на строку снизу), но "АВТОРИЗАЦИЯ АККАУНТА" в такой же
   раскладке съезжает вбок и наслаивается на "АВТОРИЗАЦИЯ" — слово значительно
   длиннее "ПОПОЛНЕНИЕ". Здесь графити возвращается в обычный инлайн-поток
   рядом с основным словом — заголовок становится одной строкой — а сам
   заголовок выравнивается по левому краю формы (как и было у форм на
   остальных страницах), а не центрируется над фигурой. */
.auth .balance__title { margin-left: 0; text-align: left; }
/* Форма короткая (2 поля) — весь блок с дефолтным отступом стоял заметно
   ниже визуального центра секции. Поднимаем повыше. */
.auth .balance__content { margin-top: 20px; max-width: 480px; }
/* Возвращено к тому же двухстрочному наплыву, что и на "Магазине"
   ("АККАУНТА" наезжает на "АВТОРИЗАЦИЯ" снизу) — только слабее (-38%
   вместо -64%), чтобы длинное слово "АВТОРИЗАЦИЯ" не перекрывалось
   слишком сильно. */
.auth .balance__title-graf {
    position: absolute;
    left: 50%;
    top: 100%;
    transform: translate(-50%, -38%) rotate(-2deg);
    white-space: nowrap;
}
/* Свечение (.balance__media::before) по умолчанию центровано по всей
   непрозрачной области фигуры — для woman.png (магазин) это совпадает с
   торсом, но у police.png в руке планшет, вытянутый далеко вправо, и он же
   раздвигает эту область, утягивая геометрический центр вправо от
   реального корпуса/спины. Поэтому здесь свечение возвращаем ближе к его
   плечам и спине, а не к общему "bounding box" вместе с планшетом */
.auth .balance__media::before { left: 39%; }
/* На разрешениях примерно 901–1900px (обычное "не на весь экран" окно
   десктопа) раньше фигура висела отдельным блоком справа абсолютным
   позиционированием, а заголовок с формой — колонкой у левого края: две
   несвязанные половины экрана вместо одной композиции. Просили сделать
   гармоничнее — фигура сверху по центру с плавным затуханием в заголовок
   под ней, и сам заголовок с формой тоже по центру, а не у края.
   Меняем .balance__media из абсолютного в обычный блок в потоке (он и так
   идёт в разметке ПЕРЕД .balance__inner, поэтому просто окажется сверху) —
   JS-центрирование (initBalanceMediaAlign) для .auth в этом диапазоне
   больше не трогает её вообще (см. main.js). */
/* Та же центрированная композиция и для recover.html/new-password.html —
   у них своя, более длинная надпись ("ВОССТАНОВЛЕНИЕ ПАРОЛЯ"), но раскладка
   (фигура сверху по центру, заголовок и форма под ней) нужна та же самая. */
@media (min-width: 901px) and (max-width: 1900px) {
    /* Раньше секция была занижена до 82vh (чтобы футер стоял ближе) — но
       из-за этого фигуре элементарно не хватало высоты. Возвращаем полную
       высоту ради нормального размера персонажа, футер чуть дальше —
       приемлемый компромисс. */
    /* :not(.admin-auth) — admin-login.html тоже несёт класс .auth (только
       ради общих поправок заголовка), но у неё совсем своя, отдельная
       раскладка на абсолютном позиционировании (см. .admin-auth ниже,
       initAdminAuthMediaAlign в main.js), заточенная под широкий кадр
       man.png. Без этого исключения .balance__media здесь получала
       position:relative вместо ожидаемого absolute — JS считал отступ top
       как для абсолютного блока, а он вместо смещения ПОВЕРХ страницы
       сдвигал фигуру вниз ПО ПОТОКУ, оставляя пустое место наверху и уводя
       картинку куда-то в область формы. */
    .auth:not(.admin-auth) { min-height: 100vh; }
    .auth:not(.admin-auth) .balance__inner { align-items: center; }
    .auth:not(.admin-auth) .balance__title { text-align: center; margin-left: 0; }
    /* Явная width, а не только max-width — иначе как flex-элемент с
       align-items:center блок сжимается по своему самому узкому "естественному"
       содержимому (тексту кнопки/ссылки), а не по полям, которые растянуты
       на 100% ЕГО ширины — поля получались уже, чем сама кнопка под ними. */
    .auth:not(.admin-auth) .balance__content { width: min(480px, 100%); margin-left: auto; margin-right: auto; }
    .auth:not(.admin-auth) .balance__media {
        position: relative;
        display: block;
        top: auto;
        right: auto;
        left: auto;
        transform: none;
        margin: 0 auto 4px;
        width: min(560px, 62%);
        aspect-ratio: 1 / 1;
        height: auto;
    }
    /* Свечение по умолчанию (50%) — центровано под саму фигуру, а не
       смещено к плечу компенсируя правое прижатие блока (которого здесь
       больше нет) */
    .auth:not(.admin-auth) .balance__media::before { left: 50%; }
}
/* Шире 1900px позицией фигуры на .balance уже не управляет JS
   (initBalanceMediaAlign сознательно не трогает эту зону — там у страницы
   магазина "как есть"-режим), и раскладка здесь остаётся прежней —
   абсолютным блоком справа, как и была: просьба сделать композицию
   центрированной относилась именно к обычному "не на весь экран" окну
   (901–1900px, см. блок выше), а не к полностью развёрнутому широкому
   десктопу. */
@media (min-width: 1901px) {
    /* :not(.admin-auth) — та же history, что и в блоке 901–1900px выше:
       admin-login.html тоже несёт класс .auth, и здесь у него своя
       раскладка (см. .admin-auth ниже) — без исключения это правило (три
       класса в селекторе, специфичность выше, чем у .admin-auth
       .balance__media) перебивало значения ниже независимо от порядка
       в файле. */
    .auth:not(.recover):not(.admin-auth) .balance__media { width: 36%; top: 8%; right: 5%; }
    /* recover/new-password на этой ширине жили по исходным (до сегодняшних
       правок) значениям — восстановлено отдельно, чтобы правки "Авторизации"
       их не задевали. */
    .auth.recover .balance__media { width: 32%; top: 12.3%; }
}

/* ======================= "ВЫ УЖЕ АДМИНИСТРАТОР" (admin-login.html) ======================= */
.admin-already { display: flex; flex-direction: column; align-items: flex-start; gap: 18px; }
.admin-already__icon {
    width: 52px;
    height: 52px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: rgba(159, 199, 255, .12);
    border: 1px solid rgba(159, 199, 255, .35);
    color: var(--accent);
}
.admin-already__icon svg { width: 24px; height: 24px; }
.admin-already__text { color: var(--muted); font-size: 15px; line-height: 1.6; max-width: 420px; margin: 0; }
.admin-already__actions { display: flex; flex-wrap: wrap; gap: 14px; }

/* ======================= АВТОРИЗАЦИЯ АДМИНИСТРАТОРА (man.png) ======================= */
/* Полностью своя раскладка — JS-центрирование (initBalanceMediaAlign)
   намеренно исключает эту страницу (см. main.js), т.к. здесь нужна
   фиксированная позиция "голова упирается в надпись", а не центрирование
   между заголовком и низом секции.
   aspect-ratio под реальные пропорции man.png (1280×714 — кадр шире, чем
   высокий, в отличие от остальных портретных фигур сайта). Квадратный
   aspect-ratio:1/1 из общего .balance__media в этом случае оставлял пустые
   прозрачные поля сверху/снизу картинки (object-fit:contain никогда не
   обрезает, а вписывает по меньшей стороне) — маска затухания ниже
   приходилась на эту пустоту, а не на реальные пиксели ног, поэтому низ
   фигуры обрывался жёстко, без всякого плавного перехода. */
.admin-auth .balance__media {
    aspect-ratio: 1280 / 714;
    height: auto;
}
@media (min-width: 901px) {
    .admin-auth .balance__media {
        /* Чуть крупнее прежних 66% — по просьбе. */
        width: 70%;
        right: 0%;
        transform: none;
        /* top больше не calc() от vh/vw — та формула была откалибрована
           только под две конкретные высоты окна (950×900 и 1050×768) и на
           любой другой высоте (например, вдвое уже развёрнутого окна) съезжала
           так, что фигура заезжала прямо под заголовок, а не под него. Реальное
           положение теперь считает JS (initAdminAuthMediaAlign в main.js) — по
           фактической нижней границе заголовка, а не по угаданной формуле. */
    }
}
@media (min-width: 1360px) {
    .admin-auth .balance__media { width: 72%; right: 0; }
}
@media (min-width: 1901px) {
    /* Крупнее и левее (больше right — дальше от правого края, ближе к
       тексту), плюс сама колонка с заголовком/формой чуть опущена — по
       просьбе, раньше весь блок жался в самый верх секции, а под ним
       на широких мониторах просто пусто. */
    .admin-auth .balance__media { width: 66%; right: 8%; top: 135px; }
    .admin-auth .balance__inner { padding-top: 70px; }
}
/* Свечение по центру фигуры (за спиной/торсом), а не смещено к плечу, как
   у police.png с планшетом в отставленной руке — у man.png руки сведены
   ближе к центру корпуса. top ниже прежних 62% — при новых пропорциях
   кадра (широкий, а не квадратный) торс расположен ощутимо выше по высоте
   блока, чем у портретных фигур */
/* width:95% из базового правила рассчитан на квадратный блок (aspect-ratio:1/1).
   Здесь блок под реальные пропорции картинки — широкий и невысокий
   (1280×714), и тот же расчёт "95% ширины" при aspect-ratio:1/1 у самого
   свечения давал круг куда больше высоты блока — он вылезал вверх/вниз и
   упирался в соседние блоки (шапку сайта, форму) плоским обрезанным краем
   вместо мягкого растворения. Считаем размер от высоты (меньшей стороны)
   — круг гарантированно помещается внутри блока по обеим осям. */
.admin-auth .balance__media::before { left: 50%; top: 54%; width: auto; height: 92%; }
/* Плавное затухание внизу — теперь, когда aspect-ratio блока совпадает с
   реальными пропорциями картинки (без пустых полей), маска действительно
   ложится на пиксели ног/обуви, а не в пустоту. Кадр обрезан почти впритык
   по подошве (~99.7% высоты кадра) — фигуру приходится "дотирать" затуханием
   реальных пикселей, естественного прозрачного запаса внизу в файле нет */
.admin-auth .balance__char {
    -webkit-mask-image: linear-gradient(to bottom, black 0%, black 55%, transparent 99%);
    mask-image: linear-gradient(to bottom, black 0%, black 55%, transparent 99%);
}

/* .balance__submit несёт margin-top:6px, рассчитанный на форму магазина
   (визуальный отступ от чекбоксов) — здесь перед кнопкой стоит "Забыли
   пароль?", и этот лишний отступ делал зазор до кнопки заметно больше
   зазора до поля пароля над ссылкой, хотя должны быть одинаковыми */
.auth .balance__submit { margin-top: 0; }

/* На ноутбучных экранах (901–1360px) заголовок в одну строку почти упирается
   в персонажа (там он уже крупный и стоит близко). Возвращаем сюда тот же
   двухстрочный "нахлёст" заголовка, что и на "Пополнении баланса"
   (.balance__title-graf без переопределения от .auth) — второе слово
   съезжает под первое внахлёст, освобождая по горизонтали ту же ширину,
   что и одна строка "АВТОРИЗАЦИЯ". Форму опускаем ниже — заголовок стал
   заметно выше (две строки вместо одной). */
@media (min-width: 901px) {
    .admin-auth .balance__title-graf {
        position: absolute;
        left: 50%;
        top: 100%;
        transform: translate(-50%, -64%) rotate(-2deg);
        white-space: nowrap;
        margin-left: 0;
        display: block;
    }
}
/* Форма опущена чуть ниже под увеличившийся (двухстрочный) заголовок — на
   ноутбучной ширине заметнее (там же корректировали позицию персонажа под
   тот же заголовок), на широком десктопе сдвиг небольшой, т.к. там и без
   того много вертикального пространства (.auth{min-height:82vh}) */
@media (min-width: 901px) and (max-width: 1360px) {
    .admin-auth .balance__content { margin-top: 130px; }
}
@media (min-width: 1360px) {
    .admin-auth .balance__content { margin-top: 110px; }
}

/* Ноутбуки и небольшой десктоп (901–1900px): раньше здесь (кроме 901–1360)
   работала "широкая" раскладка по умолчанию — заголовок+форма прижаты к
   верху (flex-start), а фото стояло с фиксированным top в процентах. На
   этом диапазоне высот/ширин это давало ровно две проблемы разом: фигура
   зависала где-то в верхней трети секции с большим пустым полем под ней
   (не по центру), и блок "Пополнение баланса" с полями формы — тоже не
   по центру секции. Начиная с этой точки и до 1900px обе вещи явно
   центрируем по вертикали, а фигуру дополнительно увеличиваем — на таких
   ширинах места достаточно, и она смотрится гармоничнее крупнее. */
@media (max-width: 1900px) {
    .balance { justify-content: center; padding-top: 0; padding-bottom: 0; }
    .balance__media {
        width: 58%;
        right: 2%;
        /* top:50%+translateY вместо top в процентах секции — фигура всегда
           точно по центру высоты .balance, независимо от того, сколько
           места сверху/снизу оставляет justify-content:center у формы */
        top: 50%;
        transform: translateY(-50%);
    }
}

/* Ноутбуки/небольшой десктоп (901–1360px): % отступ заголовка был подобран
   под широкий экран (1440px+) и на более узких вьюпортах утягивал заголовок
   слишком далеко вправо. Форма (.balance__content) больше не сдвигается
   отрицательным margin-left — она выровнена по общему левому краю сайта
   (как логотип и весь остальной контент), поэтому здесь её трогать не нужно. */
@media (max-width: 1360px) {
    .balance__title { margin-left: 4%; }
    /* На узких десктопах/ноутбуках (901–1360px) слишком крупный блок фото
       физически перекрывается с заголовком по горизонтали — заголовок
       рисуется поверх лица/головы девушки. Возвращаем фото к более
       компактному размеру именно в этом диапазоне. */
    .balance__media { width: 54%; right: 3%; }
}

/* Отдельный, ещё более узкий диапазон (901–1080px, вплотную к границе с
   мобильной раскладкой): раньше здесь заголовок выравнивался по левому
   краю, потому что фото стояло высоко и текст при центрировании дотягивался
   до её лица. Теперь фото в этом диапазоне позиционируется скриптом
   (initBalanceMediaAlign, main.js) по центру всей секции, ниже заголовка с
   гарантированным отступом — горизонтального наложения с центрированным
   текстом здесь уже не будет, т.к. они не делят одну и ту же полосу по
   вертикали. Возвращаем обычное центрирование заголовка. */
@media (max-width: 1080px) {
    .balance__title { text-align: center; margin-left: 0; }
    /* Стартовый (до подгонки скриптом initBalanceMediaAlign) размер — он
       всё равно сам ужмёт фигуру, если места по вертикали не хватит, так
       что тут можно смело просить размер побольше, а не занижать заранее */
    .balance__media { width: 58%; right: 2%; }
}

/* ======================= КАТАЛОГ МАГАЗИНА ======================= */
/* Тоже снап-точка (как .balance) — пользователь должен долистать ровно до
   каталога тем же "постраничным" скроллом, а не проскакивать сразу к футеру.
   scroll-snap-type:none на мобильном (см. html.snap-page ниже) делает эти
   свойства там не действующими, так что мобильную раскладку они не трогают. */
.catalog { position: relative; scroll-snap-align: start; scroll-snap-stop: always; }

/* ======================= Чипы категорий каталога =======================
   ТЗ Задача 7 — липкая лента над секциями, прилипает сразу под хедером
   (top совпадает с высотой .header__inner — см. переопределения top в тех
   же брейкпоинтах, где меняется высота хедера, ниже по файлу). Активный
   чип подсвечивает initCatalogFilters (main.js) через IntersectionObserver
   по data-catalog-section, а не CSS :target — так подсветка успевает
   обновляться и при обычном скролле колесом мыши, не только по клику. */
.catalog__filters {
    position: sticky;
    top: 82px;
    z-index: 20;
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin: -6px 0 30px;
    padding: 14px 0;
    background: rgba(8, 9, 12, .88);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
}
.catalog__filter {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 10px 18px;
    color: var(--muted);
    font-size: 13.5px;
    font-weight: 700;
    cursor: pointer;
    transition: border-color .2s var(--ease), color .2s var(--ease), background .2s var(--ease);
}
.catalog__filter:hover { color: var(--white); border-color: var(--border-strong); }
.catalog__filter.is-active { color: #0a1a33; background: linear-gradient(135deg, var(--accent-soft), var(--accent) 65%, #4693ff); border-color: transparent; }

/* ТЗ №4, задача 6 — --scroll-offset(-filters-extra) считаются в JS
   (initScrollOffsets в main.js) по реальной высоте шапки/панели фильтров,
   не хардкод. --scroll-offset уже учтён в html { scroll-padding-top },
   здесь добавляется только высота самой панели фильтров сверху него. */
.catalog__section { scroll-margin-top: var(--scroll-offset-filters-extra, 40px); }
.catalog__section + .catalog__section { margin-top: 48px; }
/* Заголовок + счётчик товаров + тонкая разделительная линия — ТЗ Задача 7.
   flex вместо просто text — счётчик прижат вправо (justify-content:
   space-between), а не идёт сразу после названия сплошным текстом. */
.catalog__section-title {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    font-size: 20px;
    font-weight: 800;
    color: var(--white);
    margin-bottom: 14px;
    padding-bottom: 14px;
    border-bottom: 1px solid var(--border);
}
.catalog__section-count { font-size: 14px; font-weight: 600; color: var(--muted); }

.catalog__grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 22px;
}
.shop-card {
    /* flex-column, а не голый блок — иначе цена шла сразу за заголовком, и
       у карточек с однострочным названием ("Военный билет") она оказывалась
       заметно выше, чем у соседей с двухстрочным ("Лицензия пилота"): грид
       растягивает все карточки в ряду на одинаковую высоту, но без flex это
       просто оставляло пустоту НИЖЕ цены на короткой карточке, а не
       выравнивало сами цены по одной линии. margin-top:auto на самой цене
       (см. .shop-card__price ниже) решает это — прижимает её к низу. */
    display: flex;
    flex-direction: column;
    background: linear-gradient(225deg, rgba(255, 255, 255, .07) 0%, rgba(255, 255, 255, .015) 32%, rgba(6, 7, 10, .55) 100%);
    border: 1px solid var(--border);
    border-radius: 18px;
    overflow: hidden;
    transition: transform .3s var(--ease), border-color .3s, box-shadow .3s;
}
.shop-card:hover { transform: translateY(-6px); border-color: var(--border-strong); box-shadow: 0 30px 60px -30px rgba(0, 0, 0, .7); }
.shop-card__thumb {
    flex-shrink: 0;
    aspect-ratio: 16 / 11;
    background:
        radial-gradient(120% 100% at 20% 0%, rgba(159, 199, 255, .1) 0%, rgba(159, 199, 255, 0) 55%),
        linear-gradient(160deg, rgba(255, 255, 255, .05), rgba(255, 255, 255, .01));
}
.shop-card__body { display: flex; flex-direction: column; flex: 1; padding: 18px 20px 22px; }
.shop-card__body h3 { font-size: 16px; font-weight: 700; margin-bottom: 14px; }
.shop-card__price {
    display: inline-flex;
    align-items: center;
    /* Прижата к низу тела карточки (см. flex-column на .shop-card__body
       выше) — align-self, иначе flex растянул бы пилюлю на всю ширину
       карточки, а не оставил её "по размеру содержимого" */
    align-self: flex-start;
    margin-top: auto;
    gap: 8px;
    font-size: 14px;
    font-weight: 700;
    color: var(--white);
    background: rgba(255, 255, 255, .04);
    border: 1px solid var(--border);
    /* Овальная (пилюля), а не прямоугольная — и почти без отступа справа от
       кружка, чтобы тёмный фон визуально "начинался" от самого кружка, а не
       выступал за его пределы */
    border-radius: 999px;
    padding: 6px 3px 6px 14px;
}
/* Без монетки — цена ещё не назначена, поэтому и пилюля без "тяжёлого"
   левого паддинга под кружок иконки (см. .shop-card__price выше), просто
   симметричный текст "Скоро" */
.shop-card__price--soon { padding: 6px 16px; color: var(--muted); }
.shop-card__coin {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--coin);
    display: grid;
    place-items: center;
    overflow: hidden;
}
.shop-card__coin .coin-icon { width: 60%; height: 60%; }

/* ======================= МАГАЗИН: КАТАЛОГ — РЕАЛЬНЫЕ ТОВАРЫ ======================= */
/* Первые три карточки каталога (медкнижка, права B, права C+E) — с фото и
   рабочей покупкой, см. data-catalog-item в shop.html и initShopCatalog в
   main.js. Остальные карточки ("Военный билет") остаются пустыми заглушками
   — .shop-card__thumb без <img> внутри просто не запускает это правило. */
/* Задник — тонкая гильоширная сетка (как защитный узор на настоящих
   документах/деньгах), а не голая заливка: тема "государственный документ"
   должна читаться уже в самой плашке, до того как разглядишь фото. Два
   слоя линий под разными углами дают эффект плетёной решётки, а не
   плоской штриховки в одну сторону. */
.shop-card__thumb { position: relative; overflow: hidden; }
.shop-card__thumb::before {
    content: '';
    position: absolute;
    inset: 0;
    background-image:
        repeating-linear-gradient(58deg, rgba(159, 199, 255, .07) 0 1px, transparent 1px 9px),
        repeating-linear-gradient(-58deg, rgba(159, 199, 255, .05) 0 1px, transparent 1px 9px);
    pointer-events: none;
}
/* Само фото — не "во всю плашку", а отдельная приподнятая карточка на этом
   фоне: слегка развёрнута, с собственной тенью, будто настоящий скан/фото
   документа положили и сфотографировали под небольшим углом. При наведении
   карточка выпрямляется и чуть приподнимается — то же "магнитное" ощущение,
   что и у .shop-card целиком, только на уровне самого фото. */
.shop-card__photo {
    position: absolute;
    inset: 22px 22px 16px;
    border-radius: 10px;
    overflow: hidden;
    background: #0b0d12;
    box-shadow: 0 18px 34px -14px rgba(0, 0, 0, .7), 0 0 0 1px rgba(255, 255, 255, .1);
    transition: transform .35s var(--ease), box-shadow .35s;
}
.shop-card__photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Чуть больший отступ от края — специально для фото с мелким/плотным
   текстом на самой картинке (лицензия на оружие, военный билет, пакет
   документов сразу с четырьмя обложками в кадре): чем компактнее фото в
   рамке, тем менее заметны огрехи ИИ-генерации текста на нём. */
.shop-card__photo--compact { inset: 30px 30px 22px; }
/* Разный наклон у каждой карточки — читается как "несколько документов
   разложены рядом", а не штампованное повторение одной и той же плашки */
.catalog__grid .shop-card:nth-child(1) .shop-card__photo { transform: rotate(-3deg); }
.catalog__grid .shop-card:nth-child(2) .shop-card__photo { transform: rotate(2.5deg); }
.catalog__grid .shop-card:nth-child(3) .shop-card__photo { transform: rotate(-2deg); }
.catalog__grid .shop-card:nth-child(4) .shop-card__photo { transform: rotate(2.5deg); }
.catalog__grid .shop-card:nth-child(5) .shop-card__photo { transform: rotate(-2.5deg); }
.catalog__grid .shop-card:nth-child(6) .shop-card__photo { transform: rotate(3deg); }
.catalog__grid .shop-card:nth-child(7) .shop-card__photo { transform: rotate(-1.5deg); }
.catalog__grid .shop-card:nth-child(8) .shop-card__photo { transform: rotate(-2deg); }
.catalog__grid .shop-card:nth-child(9) .shop-card__photo { transform: rotate(2deg); }
.catalog__grid .shop-card:nth-child(10) .shop-card__photo { transform: rotate(-3deg); }
.catalog__grid .shop-card:nth-child(11) .shop-card__photo { transform: rotate(2.5deg); }
.catalog__grid .shop-card:nth-child(12) .shop-card__photo { transform: rotate(-2deg); }
.catalog__grid .shop-card:nth-child(13) .shop-card__photo { transform: rotate(3deg); }
.catalog__grid .shop-card:nth-child(14) .shop-card__photo { transform: rotate(-1.5deg); }
/* .catalog__grid-префикс тут обязателен — иначе это правило проигрывает по
   специфичности разным наклонам nth-child выше (у них на один класс-уровня
   селектор больше) и наведение визуально ничего не выпрямляет */
.catalog__grid .shop-card:hover .shop-card__photo {
    transform: rotate(0deg) scale(1.03);
    box-shadow: 0 26px 50px -16px rgba(0, 0, 0, .8), 0 0 0 1px rgba(159, 199, 255, .35);
}
.shop-card__level {
    position: absolute;
    top: 10px;
    right: 10px;
    display: grid;
    place-items: center;
    min-width: 26px;
    height: 26px;
    padding: 0 6px;
    border-radius: 999px;
    background: rgba(6, 7, 10, .72);
    border: 1px solid rgba(255, 255, 255, .18);
    backdrop-filter: blur(4px);
    color: var(--white);
    font-size: 12px;
    font-weight: 800;
    cursor: help;
}
/* Бейдж скидки у товаров-наборов, продающихся дешевле суммы входящих
   позиций по отдельности (ТЗ №2, задача 7 — правило на будущее: любой
   такой набор получает такой бейдж, символ "!" в бейджах больше не
   используется вообще) */
.shop-card__level--discount { background: #6bd97e; border-color: #6bd97e; color: #0a2e12; }
/* Тултип с пояснением уровня — чистый CSS, без JS: текст берётся из
   data-tooltip, показывается по :hover/:focus. Тот же принцип, что у
   .search-modal__text (тёмная плашка, лёгкая тень), только компактнее. */
.shop-card__level::after {
    content: attr(data-tooltip);
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    width: max-content;
    max-width: 200px;
    padding: 8px 12px;
    border-radius: 10px;
    background: #14161c;
    border: 1px solid var(--border-strong);
    box-shadow: 0 20px 40px -20px rgba(0, 0, 0, .8);
    color: var(--muted);
    font-size: 12px;
    font-weight: 600;
    line-height: 1.4;
    opacity: 0;
    transform: translateY(-4px);
    pointer-events: none;
    transition: opacity .2s, transform .2s;
    z-index: 5;
}
.shop-card__level:hover::after,
.shop-card__level:focus-visible::after { opacity: 1; transform: translateY(0); }
/* На мобильном (и на узких карточках 2 в ряд — тач-устройства без
   настоящего hover) эта плашка (max-width 200px) шире самой карточки —
   родитель .shop-card__thumb обрезает её через overflow:hidden (он же
   держит в рамках наклонное фото карточки, см. .shop-card__photo выше),
   и текст обрывался посередине, а не переносился по словам. Кроме того,
   тап по бейджу на телефоне — это не настоящий hover, только его
   имитация браузером, ненадёжная и неочевидная для пользователя.
   Проще и честнее — не показывать этот тултип на мобильном вовсе: то же
   пояснение (требуемый уровень, срок и т.д.) и так есть в модалке
   покупки, которая открывается по тапу на цену. */
@media (max-width: 900px) {
    .shop-card__level::after { display: none; }
    .shop-card__level { cursor: default; }
}
/* Цена теперь кликабельная кнопка (не просто <span>) — открывает модалку
   подтверждения покупки (см. initShopCatalog в main.js), поэтому нужно
   явно снять стандартные стили <button> и оставить только внешний вид
   .shop-card__price, плюс лёгкую подсветку по наведению/фокусу как сигнал
   кликабельности. */
button.shop-card__price {
    border: 1px solid var(--border);
    font-family: inherit;
    cursor: pointer;
    transition: border-color .2s, background .2s;
}
button.shop-card__price:hover,
button.shop-card__price:focus-visible { border-color: var(--accent); background: rgba(159, 199, 255, .1); }
/* Уже купленный документ — та же серая недоступная кнопка, что и везде на
   сайте (.btn:disabled), только тут это .shop-card__price, а не .btn (см.
   markCatalogCardOwned в main.js) */
button.shop-card__price:disabled {
    opacity: .5;
    cursor: not-allowed;
    border-color: var(--border);
    background: rgba(255, 255, 255, .04);
}
button.shop-card__price:disabled:hover { border-color: var(--border); }
/* Обычная цена — асимметричный padding (мало справа), рассчитанный под
   монетку вплотную к правому краю пилюли (см. .shop-card__price выше). У
   "Есть в наличии" никакой монетки нет (весь innerHTML заменён текстом,
   см. markCatalogCardOwned) — тот же padding без неё сдвигал бы текст
   вправо, поэтому тут отступы одинаковые с двух сторон и текст по центру. */
button.shop-card__price--owned { padding: 6px 14px; justify-content: center; }

/* ------------------- Модалка подтверждения покупки ------------------- */
/* Использует базовую разметку .search-modal (тот же оверлей/карточка, что у
   остальных модалок сайта), плюс своя крупная картинка товара сверху. */
.catalog-modal__media {
    width: 100%;
    aspect-ratio: 16 / 10;
    border-radius: 14px;
    overflow: hidden;
    margin-bottom: 18px;
    background: rgba(255, 255, 255, .03);
    border: 1px solid var(--border);
    /* Отступ от края, а не во весь размер плашки — та же логика, что и у
       миниатюр каталога (.shop-card__photo): фото смотрится аккуратным
       превью, а не растянутым на всю модалку */
    padding: 22px;
    box-sizing: border-box;
}
.catalog-modal__media img { width: 100%; height: 100%; object-fit: contain; border-radius: 8px; }
/* Предупреждение под описанием товара — не у всех товаров есть (сейчас
   только у налоговой льготы, см. SHOP_PERK_DISPLAY.tax_14.warning в
   main.js), тот же жёлтый, что и у значка "в ожидании" в Схроне
   (.stash-documents__flag) — цвет уже занят под "важно, но не ошибка" */
.catalog-modal__warning {
    margin: -4px 0 18px;
    padding: 12px 14px;
    border-radius: 10px;
    background: rgba(255, 196, 92, .1);
    border: 1px solid rgba(255, 196, 92, .35);
    color: #ffc45c;
    font-size: 13px;
    font-weight: 600;
    line-height: 1.5;
}

/* ======================= МАГАЗИН: VIP-ПРИВИЛЕГИИ ======================= */
/* Между "Пополнением баланса" и каталогом — тоже отдельная снап-страница
   (см. html.snap-page выше), тот же язык карточек, что и .shop-card
   ("эффект магнита" — лёгкий подъём и тень при наведении). ТЗ №2, задача 9 —
   VIP теперь на голубом (основной цвет проекта), VIP Premium на янтарном
   (премиальный), фиолетовый убран полностью, все заливки плоские (см.
   переменные --accent-primary/-premium в :root в начале файла). */
.vip-plans {
    position: relative;
    scroll-snap-align: start;
    scroll-snap-stop: always;
    min-height: 100vh;
    display: flex;
    align-items: center;
    /* ТЗ №4, задача 7 — сноска раньше прижималась к нижнему краю секции и
       на части разрешений перекрывалась индикатором прокрутки. */
    padding-bottom: 80px;
    background: linear-gradient(180deg, #08090c 0%, #0a0c11 100%);
}
.vip-plans .container { width: 100%; }
.vip-plans__desc { max-width: 560px; margin: -6px 0 8px; color: var(--muted); font-size: 15px; line-height: 1.6; }
/* Баннер скидки первой покупки — ТЗ B2, виден только пока
   !user.first_vip_purchase_used (см. renderVipStatus в main.js) */
.vip-plans__first-discount {
    display: inline-block;
    background: rgba(107, 217, 126, .12);
    border: 1px solid rgba(107, 217, 126, .35);
    color: #6bd97e;
    font-size: 13.5px;
    font-weight: 600;
    padding: 10px 16px;
    border-radius: 999px;
    margin-bottom: 26px;
}
.vip-plans__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 28px; align-items: stretch; }
.vip-plans__footnote { max-width: 720px; margin: 32px auto 0; text-align: center; color: var(--muted); font-size: 13px; line-height: 1.6; }

.vip-plan {
    position: relative;
    display: flex;
    flex-direction: column;
    background: linear-gradient(225deg, rgba(255, 255, 255, .07) 0%, rgba(255, 255, 255, .015) 32%, rgba(6, 7, 10, .55) 100%);
    border: 1px solid var(--border);
    border-radius: 22px;
    padding: 34px 32px 32px;
    transition: transform .3s var(--ease), border-color .3s, box-shadow .3s;
}
.vip-plan:hover { transform: translateY(-8px); box-shadow: 0 34px 70px -30px rgba(0, 0, 0, .75); }
.vip-plan--vip { border-color: rgba(159, 199, 255, .3); }
.vip-plan--vip:hover { border-color: rgba(159, 199, 255, .55); }
/* border 2px (не 1px, как у VIP) — ТЗ №3, задача B5, таблица "Цвета" */
.vip-plan--premium {
    border-width: 2px;
    border-color: rgba(255, 184, 77, .4);
    background: rgba(255, 184, 77, .06);
    padding: 33px 31px 31px;
}
.vip-plan--premium:hover { border-color: rgba(255, 184, 77, .7); box-shadow: 0 34px 70px -26px rgba(255, 184, 77, .35); }
/* Бейдж "АКТИВНА ДО ..." — ТЗ B6, поверх карточки, когда этот тариф уже
   активен у пользователя (см. renderVipCard в main.js) */
.vip-plan__active-badge {
    text-align: center;
    font-size: 12px;
    font-weight: 800;
    letter-spacing: .04em;
    text-transform: uppercase;
    padding: 8px 14px;
    border-radius: 999px;
    margin-bottom: 16px;
}
.vip-plan--vip .vip-plan__active-badge { background: rgba(159, 199, 255, .14); color: var(--accent-primary); }
.vip-plan--premium .vip-plan__active-badge { background: rgba(255, 184, 77, .16); color: var(--accent-premium); }

.vip-plan__ribbon {
    position: absolute;
    top: -14px;
    right: 28px;
    background: var(--accent-premium);
    color: #3a2a06;
    font-size: 11.5px;
    font-weight: 800;
    letter-spacing: .3px;
    text-transform: uppercase;
    padding: 7px 16px;
    border-radius: 999px;
    box-shadow: 0 10px 24px -8px rgba(255, 184, 77, .55);
}

.vip-plan__head { display: flex; align-items: center; gap: 16px; margin-bottom: 18px; }
.vip-plan__icon {
    width: 52px;
    height: 52px;
    border-radius: 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.vip-plan__icon svg { width: 27px; height: 27px; }
.vip-plan--vip .vip-plan__icon { background: rgba(159, 199, 255, .16); color: var(--accent-primary); }
.vip-plan--premium .vip-plan__icon { background: rgba(255, 184, 77, .18); color: var(--accent-premium); }
.vip-plan__title { font-size: 23px; font-weight: 800; }
.vip-plan__tagline { font-size: 13px; color: var(--muted); margin-top: 2px; }
.vip-plan__desc { color: var(--muted); font-size: 14.5px; line-height: 1.6; margin-bottom: 22px; }

.vip-plan__perks { list-style: none; display: flex; flex-direction: column; gap: 12px; margin-bottom: 0; flex-shrink: 0; }
.vip-plan__perks li { display: flex; align-items: flex-start; gap: 10px; font-size: 14.5px; color: var(--white); line-height: 1.4; }
.vip-plan__perks li::before {
    content: '✓';
    flex-shrink: 0;
    width: 20px;
    height: 20px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    font-size: 11px;
    font-weight: 900;
    margin-top: 1px;
}
.vip-plan--vip .vip-plan__perks li::before { background: rgba(159, 199, 255, .18); color: var(--accent-primary); }
.vip-plan--premium .vip-plan__perks li::before { background: rgba(255, 184, 77, .2); color: var(--accent-premium); }

/* Заголовки групп привилегий ("Доход"/"Кейсы"/"Комфорт"/"Статус") — ТЗ B5.
   Свой li, без чекмарка (::before выключен ниже), с тонкими линиями
   слева-справа от текста через flex + ::before/::after самого заголовка
   (не путать с чекмарком остальных li — это ДРУГОЙ псевдоэлемент того же
   li, но у li.vip-plan__group-title чекмарк .vip-plan__perks li::before
   переопределён на content:none). */
.vip-plan__perks li.vip-plan__group-title {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--muted);
    margin: 8px 0 -2px;
}
.vip-plan__perks li.vip-plan__group-title:first-child { margin-top: 0; }
.vip-plan__perks li.vip-plan__group-title::before { content: none; width: 0; height: 0; }
.vip-plan__perks li.vip-plan__group-title::after {
    content: '';
    flex: 1 1 auto;
    align-self: center;
    height: 1px;
    background: var(--border);
    margin-left: 10px;
}
/* "Все привилегии VIP" — первая строка карточки Premium, не чекмарк-пункт
   и не заголовок группы, а отдельное акцентное утверждение (ТЗ B1) */
.vip-plan__perks li.vip-plan__perks-all {
    font-weight: 700;
    color: var(--accent-premium);
}
.vip-plan__perks li.vip-plan__perks-all::before { background: rgba(255, 184, 77, .25); color: var(--accent-premium); }

/* Свёртывание на мобильном — ТЗ B5: первые 6 строк списка видны всегда,
   остальные скрыты за кнопкой "Показать все", БЕЗ анимации высоты (сама
   ТЗ просит: "height:auto работает нестабильно") — просто toggle класса
   is-expanded на самом списке. nth-child(n+7) — считает ЛЮБЫЕ li подряд
   (включая заголовки групп) как "строки", не только пункты с чекмарком —
   так проще и предсказуемее, чем отдельно отслеживать группы. */
@media (max-width: 767px) {
    .vip-plan__perks:not(.is-expanded) li:nth-child(n+7) { display: none; }
    .vip-plan__toggle-perks { display: block !important; }
}
.vip-plan__toggle-perks {
    width: 100%;
    background: none;
    border: none;
    color: var(--accent);
    font-size: 13px;
    font-weight: 700;
    text-align: center;
    padding: 10px 0 0;
    cursor: pointer;
    font-family: inherit;
}
.vip-plan__toggle-perks:hover { text-decoration: underline; }

/* Распорка — ТЗ B5: список flex-shrink:0 (не растягивается сам, см. выше),
   эта пустая распорка flex:1 поглощает разницу высоты между короткой VIP
   и длинной Premium, блок покупки ниже всегда прижат к низу карточки на
   одной линии в обеих. */
.vip-plan__spacer { flex: 1 1 auto; min-height: 20px; }

.vip-plan__periods { display: flex; gap: 8px; margin-bottom: 14px; }
.vip-plan__period {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    background: rgba(255, 255, 255, .03);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 10px 8px;
    color: var(--muted);
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
    transition: background .2s var(--ease), border-color .2s, color .2s;
}
.vip-plan__period-save { font-size: 11px; font-weight: 800; color: #6bd97e; }
.vip-plan--vip .vip-plan__period.is-active { background: rgba(159, 199, 255, .16); border-color: rgba(159, 199, 255, .5); color: var(--accent-primary); }
.vip-plan--premium .vip-plan__period.is-active { background: rgba(255, 184, 77, .16); border-color: rgba(255, 184, 77, .5); color: var(--accent-premium); }

/* Автопродление — ТЗ B3, показан только когда выбран тариф "Месяц" (см.
   renderVipCard в main.js), выключен по умолчанию (сам чекбокс без
   checked в разметке) */
.vip-plan__autorenew {
    background: rgba(255, 255, 255, .04);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 12px 14px;
    margin-bottom: 14px;
}
.vip-plan__autorenew-label { display: flex; align-items: center; gap: 8px; cursor: pointer; font-size: 13.5px; font-weight: 700; color: var(--white); }
.vip-plan__autorenew-label input { accent-color: var(--accent); width: 16px; height: 16px; flex-shrink: 0; }
.vip-plan__autorenew-badge { margin-left: auto; font-size: 11px; font-weight: 800; color: #6bd97e; }
.vip-plan__autorenew-note { font-size: 12px; color: var(--muted); margin: 6px 0 0 24px; }

.vip-plan__note { font-size: 12.5px; color: var(--muted); line-height: 1.4; margin: 0 0 10px; text-align: center; }
.vip-plan__upgrade-calc { margin-top: 10px; font-size: 12.5px; color: var(--muted); }
.vip-plan__upgrade-calc summary { cursor: pointer; color: var(--accent); font-weight: 600; }
.vip-plan__upgrade-calc summary:hover { text-decoration: underline; }
.vip-plan__upgrade-calc p { margin-top: 8px; line-height: 1.6; white-space: pre-line; }

.vip-plan__buy { width: 100%; border: none; }
.vip-plan__buy:disabled { opacity: .5; cursor: not-allowed; }
.vip-plan__buy-strike { text-decoration: line-through; opacity: .6; margin-left: 6px; font-weight: 500; }
.vip-plan--vip .vip-plan__buy { background: var(--accent-primary); color: #0a1a33; }
.vip-plan--vip .vip-plan__buy:hover { background: var(--accent-primary-hover); }
.vip-plan--premium .vip-plan__buy { background: var(--accent-premium); color: #3a2a06; }
.vip-plan--premium .vip-plan__buy:hover { background: var(--accent-premium-hover); }
.vip-plan__coin { width: 20px; height: 20px; border-radius: 50%; background: var(--coin); display: inline-grid; place-items: center; overflow: hidden; }
.vip-plan__coin .coin-icon { width: 60%; height: 60%; }

/* ======================= МАГАЗИН: стартовые пакеты =======================
   Замена убранных "подъёмных" — витрина трёх наборов (см. #starter-packs в
   shop.html). Карточка переиспользует визуальный язык .vip-plan (та же
   геометрия, тот же приём с рамкой-подсветкой и .starter-pack__ribbon
   аналогичным .vip-plan__ribbon), но кнопка покупки — [data-soon]-заглушка,
   а не реальная транзакция (см. initSoonToast). */
.starter-packs { position: relative; padding: 90px 0 10px; }
/* ТЗ №2 (ревью), задача 12 — точные отступы: 16px от заголовка, 40px до карточек */
.starter-packs__desc { max-width: 620px; margin: 16px 0 40px; color: var(--muted); font-size: 16px; line-height: 1.6; }
/* Принудительный <br> внутри длинных подписей — виден только там, где сам
   текст его требует (см. использования в shop.html), скрыт на мобильном:
   на узком экране естественный перенос по ширине уже делает разрыв не
   нужным, а местами и лишним (ТЗ №2, задачи 12-13) */
@media (max-width: 767px) {
    .hard-break { display: none; }
}
.starter-packs__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; align-items: stretch; }

.starter-pack {
    position: relative;
    display: flex;
    flex-direction: column;
    background: linear-gradient(225deg, rgba(255, 255, 255, .06) 0%, rgba(255, 255, 255, .015) 32%, rgba(6, 7, 10, .55) 100%);
    border: 1px solid var(--border);
    border-radius: 20px;
    padding: 28px 26px 26px;
    transition: transform .3s var(--ease), border-color .3s, box-shadow .3s;
}
.starter-pack:hover { transform: translateY(-6px); box-shadow: 0 30px 60px -28px rgba(0, 0, 0, .7); }

.starter-pack--grey { border-color: rgba(154, 163, 180, .3); }
.starter-pack--grey:hover { border-color: rgba(154, 163, 180, .55); }
/* ТЗ №5, задача 2 — Новосёл/Свой получают более явную, толщиной 2px рамку
   акцентного цвета уровня (Приезжий остаётся 1px приглушённой серой —
   он младший уровень, не должен визуально спорить со старшими). */
.starter-pack--blue { border-width: 2px; border-color: rgba(70, 147, 255, .55); }
.starter-pack--blue:hover { border-color: var(--accent-primary); box-shadow: 0 30px 60px -26px rgba(70, 147, 255, .4); }
.starter-pack--amber { border-width: 2px; border-color: rgba(255, 184, 77, .55); }
.starter-pack--amber:hover { border-color: var(--accent-premium); }

.starter-pack--featured {
    background: linear-gradient(225deg, rgba(159, 199, 255, .14) 0%, rgba(255, 255, 255, .015) 32%, rgba(6, 7, 10, .6) 100%);
    transform: scale(1.03);
}
.starter-pack--featured:hover { transform: scale(1.03) translateY(-6px); }

.starter-pack__ribbon {
    position: absolute;
    top: -14px;
    right: 24px;
    background: linear-gradient(135deg, var(--accent-soft), var(--accent) 65%, #4693ff);
    color: #0a1a33;
    font-size: 11px;
    font-weight: 800;
    letter-spacing: .3px;
    text-transform: uppercase;
    padding: 7px 15px;
    border-radius: 999px;
    box-shadow: 0 10px 24px -8px rgba(70, 147, 255, .6);
}

/* Раньше тут была просто цветная плашка с SVG-иконкой (домик/звезда/ромб) —
   теперь тематическое фото набора (чемодан на автобусной остановке —
   "Приезжий", коробки переезда — "Новосёл", ключи+документы на машину —
   "Свой", см. photos/packages/). position:relative + overflow:hidden — под
   ::after снизу (мягкое затухание в цвет карточки, чтобы край фото не
   обрывался жёсткой линией на границе с текстом снизу) и под сам object-fit
   картинки. inset box-shadow вместо border — цветная рамка поверх фото, не
   раздвигая его (border добавил бы себе ширину и сдвинул картинку). */
.starter-pack__illustration {
    position: relative;
    height: 172px;
    border-radius: 14px;
    overflow: hidden;
    margin-bottom: 18px;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .08);
}
.starter-pack__illustration img { display: block; width: 100%; height: 100%; object-fit: cover; }
.starter-pack__illustration::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgba(10, 11, 15, 0) 55%, rgba(10, 11, 15, .55) 100%);
    pointer-events: none;
}
.starter-pack--grey .starter-pack__illustration { box-shadow: inset 0 0 0 1px rgba(154, 163, 180, .3); }
.starter-pack--blue .starter-pack__illustration { box-shadow: inset 0 0 0 1px rgba(159, 199, 255, .35); }
.starter-pack--amber .starter-pack__illustration { box-shadow: inset 0 0 0 1px rgba(255, 184, 77, .3); }

.starter-pack__title { font-size: 21px; font-weight: 800; margin-bottom: 4px; }
.starter-pack__tagline { color: var(--muted); font-size: 13.5px; line-height: 1.5; margin-bottom: 18px; }

/* flex:0 (не 1) — список не растягивается сам; растягивается отдельная
   распорка ниже (.starter-pack__spacer), поглощая разницу высоты между
   короткой "Приезжий" (5 пунктов) и длинной "Свой" (11) картами, так что
   кнопка покупки всегда прижата к низу карточки на одной высоте у всех
   трёх (ТЗ №2, задача 8) */
.starter-pack__list { list-style: none; display: flex; flex-direction: column; gap: 10px; margin-bottom: 24px; flex-shrink: 0; }
.starter-pack__spacer { flex: 1 1 auto; }
/* ТЗ №5, задача 2 — 18×18px круг, отступ 10px до текста, выравнивание по
   верхней строке (top:2px абсолютным позиционированием — работает и для
   многострочных пунктов, т.к. кружок не зависит от высоты всего блока). */
.starter-pack__list li { position: relative; padding-left: 28px; font-size: 14px; color: var(--white); line-height: 1.4; }
.starter-pack__list li::before {
    content: '';
    position: absolute;
    left: 0;
    top: 2px;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: rgba(154, 163, 180, .15);
}
.starter-pack__list li::after {
    content: '';
    position: absolute;
    left: 5.5px;
    top: 7px;
    width: 7px;
    height: 4px;
    border-left: 1.6px solid #9aa3b4;
    border-bottom: 1.6px solid #9aa3b4;
    transform: rotate(-45deg);
}
/* Приезжий — ПРИГЛУШЁННЫЙ голубой: тот же приглушённо-голубой оттенок, что
   и рамка его кнопки (starter-pack__buy--grey), но заметно менее
   насыщенный, чем яркий --accent-primary у Новосёла — иначе два младших
   уровня перестанут различаться между собой. */
.starter-pack--grey .starter-pack__list li::before { background: rgba(154, 163, 180, .18); }
.starter-pack--grey .starter-pack__list li::after { border-color: #9aa3b4; }
.starter-pack--blue .starter-pack__list li::before { background: rgba(159, 199, 255, .15); }
.starter-pack--blue .starter-pack__list li::after { border-color: var(--accent-primary); }
.starter-pack--amber .starter-pack__list li::before { background: rgba(255, 184, 77, .15); }
.starter-pack--amber .starter-pack__list li::after { border-color: var(--accent-premium); }

.starter-pack__buy { width: 100%; margin-top: auto; border: 1px solid transparent; background: rgba(255, 255, 255, .08); color: var(--white); transition: background .2s, border-color .2s, color .2s; }
.starter-pack__buy:hover { background: rgba(255, 255, 255, .14); }
/* ТЗ №5, задача 1 — регрессия ТЗ №4: оформление было завязано на класс
   КАРТОЧКИ (.starter-pack--grey/blue/amber .starter-pack__buy), из-за чего
   при доработке серой кнопки легко было задеть остальные две через общий
   потомковый селектор. Модификатор уровня теперь — явный класс НА САМОЙ
   кнопке (starter-pack__buy--grey/blue/amber, задан в разметке из данных
   пакета), три уровня друг от друга больше не зависят. */
.starter-pack__buy--grey { background: rgba(154, 163, 180, .16); border-color: rgba(159, 199, 255, .4); color: var(--white); font-weight: 600; }
.starter-pack__buy--grey:hover { background: rgba(154, 163, 180, .24); border-color: rgba(159, 199, 255, .65); }
.starter-pack__buy--grey:active { background: rgba(154, 163, 180, .1); }
.starter-pack__buy--blue { background: var(--accent-primary); color: #0a1a33; }
.starter-pack__buy--blue:hover { background: var(--accent-primary-hover); }
.starter-pack__buy--blue:active { background: #3d7fd6; }
.starter-pack__buy--amber { background: var(--accent-premium); color: #3a2a06; }
.starter-pack__buy--amber:hover { background: var(--accent-premium-hover); }
.starter-pack__buy--amber:active { background: #d9a03f; }
.starter-pack__buy--grey:focus-visible,
.starter-pack__buy--blue:focus-visible,
.starter-pack__buy--amber:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.starter-pack__coin { width: 20px; height: 20px; border-radius: 50%; background: var(--coin); display: inline-grid; place-items: center; overflow: hidden; }
.starter-pack__coin .coin-icon { width: 60%; height: 60%; }

.starter-packs__disclaimer { max-width: 720px; margin: 32px auto 0; text-align: center; color: var(--muted); font-size: 14px; line-height: 1.6; }

/* ТЗ — кнопка "во всю ширину карточки, высота 48px" */
.starter-pack__buy { min-height: 48px; }
/* "Недоступно" — одинаково для всех трёх уровней и ПЕРЕКРЫВАЕТ модификатор
   уровня целиком (!important — намеренно, ТЗ №5 явно требует, чтобы
   заблокированное состояние не могло быть спутано с активным ни у одного
   из трёх пакетов, поэтому оно не должно зависеть от порядка правил
   каскада, только от самого атрибута disabled). Фон карточки не меняется
   вовсе, только приглушённая серая рамка и вторичный текст полупрозрачностью. */
.starter-pack__buy:disabled { background: transparent !important; border-color: rgba(154, 163, 180, .3) !important; color: var(--muted) !important; opacity: .5; cursor: not-allowed; box-shadow: none; }
.starter-pack__buy:disabled:hover { background: transparent !important; border-color: rgba(154, 163, 180, .3) !important; }
/* "Улучшить" — тот же акцентный цвет, что у остальных кнопок покупки этой
   карточки (не отдельный третий цвет — ТЗ явно просит не плодить лишние
   градиентные системы), просто другой текст/назначение */
.starter-pack__buy--upgrade { background: rgba(255, 255, 255, .08); }
.starter-pack--blue .starter-pack__buy--upgrade,
.starter-pack--amber .starter-pack__buy--upgrade { background: rgba(255, 255, 255, .08); color: var(--white); box-shadow: none; }
.starter-pack--blue .starter-pack__buy--upgrade:hover,
.starter-pack--amber .starter-pack__buy--upgrade:hover { background: rgba(255, 255, 255, .14); }

.starter-pack__owned {
    text-align: center;
    font-size: 13.5px;
    font-weight: 700;
    color: #6bd97e;
    margin: 0 0 4px;
}
.starter-pack__note { font-size: 12.5px; color: var(--muted); line-height: 1.4; text-align: center; margin: 8px 0 0; }

/* Уровень > 10 — блок сворачивается в одну строку вместо трёх карточек
   (см. renderStatus в main.js) */
.starter-packs__collapsed {
    text-align: center;
    padding: 22px 24px;
    border: 1px solid var(--border);
    border-radius: 16px;
    background: rgba(255, 255, 255, .03);
    color: var(--muted);
    font-size: 14.5px;
}

@media (max-width: 900px) {
    .starter-packs__grid { grid-template-columns: 1fr; }
    .starter-pack--featured { transform: none; }
    .starter-pack--featured:hover { transform: translateY(-6px); }
}

/* ======================= МАГАЗИН на 2048/2560 =======================
   Базовое правило (right:5%) держало фигуру у самого физического края
   экрана — на этих ширинах между ней и кнопкой "Авторизация" в шапке
   получался большой перекос. right ниже подобран измерением так, чтобы
   правый край фигуры совпадал с правым краем этой кнопки (тот же
   контейнер, что и у .header__inner). Сам размер/положение по вертикали
   считает initBalanceMediaAlign (main.js, теперь с 2001px) — точно между
   карточкой курса обмена и кнопкой "Пополнить". */
@media (min-width: 2001px) and (max-width: 2600px) {
    /* Прошлая попытка (400px + увеличенный шрифт) исказила поля — текст
       ("1 RC = 10 ₽ в игре", текст чекбоксов) переставал помещаться в
       одну строку и переносился, карточка курса обмена раздувалась по
       высоте. Возвращаем колонку к обычной ширине (560px, как везде на
       сайте) — без переносов; лёгкое увеличение шрифта/отступов ниже
       остаётся, но уже без искажений. */
    .balance__content { max-width: 560px; margin-top: 64px; }
    .balance__title { margin-bottom: 30px; }
    /* Отступы/зазоры (не шрифт — чтобы не задеть перенос текста выше)
       увеличены по просьбе: фигура девушки на этом ярусе считается через
       initBalanceMediaAlign (main.js) от верха карточки курса обмена до
       низа кнопки "Пополнить" — чем больше этот промежуток, тем крупнее
       сама фигура, без отдельной настройки её размера напрямую. */
    .currency-rate { padding: 20px 22px 22px; margin-bottom: 32px; }
    .balance__form { gap: 25px; }
    .balance__submit { margin-top: 18px; }
    .field__control { padding: 22px 26px; font-size: 17px; }
    .checkbox { font-size: 15px; gap: 12px; }
    .checkbox__box { width: 21px; height: 21px; }
    /* Заголовок и вся колонка формы опущены заметно ниже хедера, чем на
       других разрешениях, — по просьбе, вместе с этим тянется вниз и сама
       фигура (тот же initBalanceMediaAlign, см. комментарий выше). */
    .balance { padding-top: 64px; }
}
@media (min-width: 2001px) and (max-width: 2200px) {
    /* Было ровно по кнопке "Авторизация" (21.7%) — попросили сдвинуть чуть
       правее этого. */
    .shop .balance__media { right: 19%; }
}
@media (min-width: 2201px) and (max-width: 2600px) {
    .shop .balance__media { right: 25%; }
}

/* ======================= МАГАЗИН: короткие экраны =======================
   .balance — min-height:100vh + overflow:hidden (снап-страница на всю
   высоту экрана). На обычных широких, но НЕ очень высоких мониторах/ноутбуках
   (1920×1080, 1600×900, 1366×768 и т.п. — где-то от 1920×1080 и ниже по
   высоте) полная форма (заголовок + курс обмена + 5 полей + чекбоксы +
   кнопка) не помещалась в 1080px и даже меньше, и кнопку "Пополнить" внизу
   срезало overflow:hidden — она оставалась видна только полоской в 10-15px
   и была едва кликабельна. Сначала просто ужимаем внутренние отступы —
   должно хватать для 1024-1200px высоты. */
@media (min-width: 901px) and (max-height: 1150px) {
    .balance { padding-top: 24px; padding-bottom: 16px; }
    .balance__content { margin-top: 24px; }
    .balance__title { margin-bottom: 20px; }
    .currency-rate { padding: 16px 18px 18px; margin-bottom: 14px; }
    .balance__form { gap: 10px; }
    /* Эти четыре идут ПОСЛЕ блока "МАГАЗИН на 2048/2560" в файле специально —
       тот блок увеличивает поля/чекбокс независимо от высоты экрана (чтобы
       вырастить фигуру через высоту формы), а здесь, где высота реально
       мала, этого увеличения перебор — без явного отката именно этих
       четырёх свойств сюда самих полей/чекбокса общая экономия места выше
       не спасала от той же самой обрезанной кнопки. */
    .field__control { padding: 16px 20px; font-size: 16px; }
    .checkbox { font-size: 14px; gap: 10px; }
    .checkbox__box { width: 20px; height: 20px; }
    .balance__submit { margin-top: 6px; }
}
/* Экраны совсем низкие (ноутбуки/окна высотой до ~820px — 800×600, 1024×768,
   1152×864, 1280×720/768/800, 1360/1366×768, 1600×900 и т.п.) — даже
   сжатых отступов выше недостаточно. Дальше сжимать поля до нечитаемого
   размера — не вариант ("текст должен быть читаемым"), поэтому вместо
   этого снимаем overflow:hidden: секция остаётся видимой целиком (просто
   не умещается в ровно один экран без небольшого скролла), но НИЧЕГО
   никогда не обрезается и не становится некликабельным — это переживает
   любую комбинацию ширины/высоты, а не только перечисленные. */
@media (min-width: 901px) and (max-height: 820px) {
    .balance { overflow: visible; padding-bottom: 32px; }
}

/* 2K-ширина (2001–2600px), но невысокий экран (2048×1080/2560×1080 и
   т.п., высота ≤1150px) — блок "МАГАЗИН на 2048/2560" выше специально
   опускает и увеличивает колонку/фигуру, но блок "короткие экраны" сразу
   над этим комментарием идёт позже в файле и на такой высоте уже откатил
   те же самые свойства обратно к сжатым значениям (иначе кнопку
   "Пополнить" срезает overflow:hidden — см. его комментарий). Здесь — тот
   же компромисс ещё раз, но уже не по нулям сжатого варианта: опускаем и
   увеличиваем настолько, насколько можно, не обрезая кнопку внизу. */
@media (min-width: 2001px) and (max-width: 2600px) and (max-height: 1150px) {
    .balance { padding-top: 40px; }
    .balance__content { margin-top: 40px; }
    .currency-rate { margin-bottom: 20px; }
    .balance__form { gap: 14px; }
    .balance__submit { margin-top: 10px; }
}

/* ======================= МАГАЗИН на 2880/3200 (2601–3699px) =======================
   Раньше в этом диапазоне у магазина не было вообще никакой отдельной
   настройки (только у hero на главной) — форма/VIP/каталог оставались
   базового десктопного размера и читались мелко на такой ширине. Фигура
   девушки — тот же алгоритм "точно между курсом обмена и кнопкой
   Пополнить", что и у 4096/5120 (см. initBalanceMediaAlign, теперь
   включён с 2601px). */
@media (min-width: 2601px) and (max-width: 3699px) {
    .shop .balance__media { right: 20%; }
    /* Заголовок был заметно ниже хедера, чем нужно — плотный, а не большой
       отступ (просили ~5-10px). Фигуру это не уменьшает: её размер теперь
       считается напрямую от карточки курса обмена до кнопки "Пополнить",
       а не от этого отступа. */
    .balance { justify-content: flex-start; padding-top: 32px; padding-bottom: 28px; }
    .balance__content { max-width: 600px; margin-top: 48px; }
    .balance__title { margin-bottom: 44px; }

    .currency-rate { padding: 28px 31px 31px; border-radius: 20px; margin-bottom: 32px; }
    .currency-rate__icon { width: 52px; height: 52px; }
    .currency-rate__label { font-size: 17px; }
    .currency-rate__value { font-size: 22px; }
    .currency-rate__balance-badge { width: 42px; height: 42px; }
    .currency-rate__balance-label { font-size: 15px; }
    .currency-rate__balance-value { font-size: 22px; }

    .balance__form { gap: 28px; }
    .balance__submit { margin-top: 16px; }
    .field__control { padding: 28px 33px; font-size: 24px; border-radius: 19px; }
    .field--clearable .field__control { padding-right: 64px; }
    .field--password .field__control { padding-right: 106px; }
    .field__icon-btn { width: 44px; height: 44px; }
    .field__icon-btn svg { width: 24px; height: 24px; }
    .field__hint, .field__error { font-size: 18px; }
    .checkbox { font-size: 19px; gap: 16px; }
    .checkbox__box { width: 27px; height: 27px; }

    .vip-plans__grid { gap: 40px; }
    .vip-plan { padding: 56px 50px 52px; border-radius: 26px; }
    .vip-plan__head { gap: 22px; margin-bottom: 28px; }
    .vip-plan__icon { width: 72px; height: 72px; }
    .vip-plan__icon svg { width: 40px; height: 40px; }
    .vip-plan__title { font-size: 38px; }
    .vip-plan__tagline { font-size: 19px; }
    .vip-plan__desc { font-size: 22px; margin-bottom: 33px; }
    .vip-plan__perks { gap: 18px; margin-bottom: 37px; }
    .vip-plan__perks li { font-size: 22px; gap: 14px; }
    .vip-plan__periods { gap: 14px; margin-bottom: 28px; }
    .vip-plan__period { padding: 16px 10px; font-size: 18px; border-radius: 11px; }
    .vip-plan__coin { width: 30px; height: 30px; }

    .catalog__grid { gap: 34px; }
    .shop-card { border-radius: 22px; }
    .shop-card__body { padding: 30px 32px 36px; }
    .shop-card__body h3 { font-size: 24px; margin-bottom: 21px; }
    .shop-card__price { font-size: 19px; padding: 10px 5px 10px 22px; }
    .shop-card__coin { width: 32px; height: 32px; }
}
/* То же самое, но для плавающих кнопок — свои, чуть меньшие размеры. */
@media (min-width: 2601px) and (max-width: 3699px) {
    .roulette-fab { bottom: 36px; padding: 17px 20px; border-radius: 20px; gap: 9px; }
    .roulette-fab__ring { top: 17px; width: 52px; height: 52px; margin-left: -26px; }
    .roulette-fab__icon { width: 52px; height: 52px; }
    .roulette-fab__label { font-size: 14px; }
    .to-top { bottom: 36px; width: 58px; height: 58px; }
    .to-top svg { width: 25px; height: 25px; }
}

/* ======================= МАГАЗИН на 4096×2160 (4K, шире 16:9) =======================
   Тот же принцип, что и в блоке под 5120×2880 ниже — своя ширина, поэтому
   свои значения (примерно ×0.8 от 5120, где не завязано на конкретных
   измерениях экрана). .balance__media выровнена ПРАВЫМ краем с кнопкой
   "Авторизация" в шапке (обе — правый край одного и того же контейнера,
   см. .shop .balance__media ниже), как отдельно просили для этого разрешения. */
@media (min-width: 4080px) and (max-width: 4599px) {
    .section-title { font-size: clamp(64px, 5vw, 122px); margin-bottom: 68px; }
    .btn--lg { padding: 30px 78px; font-size: 26px; border-radius: 18px; }

    /* Было выровнено ровно по правому краю кнопки "Авторизация" (right:30.2%)
       — попросили сдвинуть чуть правее этого, ближе к своему месту у
       правого края секции. */
    .shop .balance__media { right: 26%; }
    .balance { justify-content: flex-start; padding-top: 90px; padding-bottom: 32px; }
    /* Колонка шире (просили "подлиннее" поля), но отступ от заголовка до формы
       уменьшен обратно — раньше он специально раздувался, чтобы вырастить
       через него фигуру (см. initBalanceMediaAlign), но с новым способом
       (центр по всей доступной полосе, не завязан на форму) в этом больше
       нет смысла — саму форму, наоборот, попросили поднять повыше. */
    .balance__content { max-width: 720px; margin-top: 120px; }
    .balance__title { margin-bottom: 58px; }

    .currency-rate { padding: 34px 37px 37px; border-radius: 22px; margin-bottom: 37px; }
    .currency-rate__icon { width: 61px; height: 61px; }
    .currency-rate__label { font-size: 18px; }
    .currency-rate__value { font-size: 24px; }
    .currency-rate__balance-label { font-size: 16px; }
    .currency-rate__balance-value { font-size: 24px; }

    .balance__form { gap: 32px; }
    .balance__submit { margin-top: 18px; }
    .field__control { padding: 34px 39px; font-size: 28px; border-radius: 21px; }
    .field--clearable .field__control { padding-right: 74px; }
    .field--password .field__control { padding-right: 122px; }
    .field__icon-btn { width: 48px; height: 48px; }
    .field__icon-btn svg { width: 26px; height: 26px; }
    .field__hint, .field__error { font-size: 20px; }
    .checkbox { font-size: 22px; gap: 18px; }
    .checkbox__box { width: 31px; height: 31px; }

    .vip-plans__grid { gap: 48px; }
    .vip-plan { padding: 68px 61px 63px; border-radius: 30px; }
    .vip-plan__head { gap: 26px; margin-bottom: 32px; }
    .vip-plan__icon { width: 84px; height: 84px; }
    .vip-plan__icon svg { width: 46px; height: 46px; }
    .vip-plan__title { font-size: 45px; }
    .vip-plan__tagline { font-size: 22px; }
    .vip-plan__desc { font-size: 25px; margin-bottom: 38px; }
    .vip-plan__perks { gap: 21px; margin-bottom: 43px; }
    .vip-plan__perks li { font-size: 25px; gap: 16px; }
    .vip-plan__periods { gap: 16px; margin-bottom: 32px; }
    .vip-plan__period { padding: 18px 11px; font-size: 20px; border-radius: 12px; }
    .vip-plan__coin { width: 34px; height: 34px; }

    .catalog__grid { gap: 40px; }
    .shop-card { border-radius: 26px; }
    .shop-card__body { padding: 35px 37px 42px; }
    .shop-card__body h3 { font-size: 27px; margin-bottom: 24px; }
    .shop-card__price { font-size: 22px; padding: 12px 6px 12px 24px; }
    .shop-card__coin { width: 35px; height: 35px; }
}

/* ======================= МАГАЗИН на 5120×2880 (5K) =======================
   Все три снап-страницы магазина ("Пополнение", VIP-привилегии, каталог)
   были откалиброваны под обычный десктоп — на 5K смотрелись мелко и с
   непропорционально большими пустыми полями. initBalanceMediaAlign в
   main.js меряет реальную высоту формы (от карточки курса обмена до низа
   кнопки "Пополнить") и подгоняет фигуру девушки под неё — сама эта
   высота была маленькой (поля/шрифты не увеличены), поэтому фигура тоже
   оставалась маленькой независимо от того, сколько места есть по ширине.
   Увеличивая поля формы ниже, автоматически увеличиваем и фигуру, без
   правок в JS. */
@media (min-width: 4600px) {
    .section-title { font-size: clamp(64px, 5vw, 150px); margin-bottom: 84px; }
    .btn--lg { padding: 36px 96px; font-size: 32px; border-radius: 22px; }

    /* Правый край (right:18%) и низ фигуры не трогаем — её увеличивает
       отдельный множитель в initBalanceMediaAlign (main.js, ветка
       window.innerWidth >= 4600), который растит её от того же
       зафиксированного нижне-правого угла, не сдвигая его. */
    /* 18% почти не оставляло места для дальнейшего роста фигуры (упиралась в
       16px зазор до формы независимо от множителя в JS) — 16% отодвигает
       правый край чуть дальше от формы, освобождая место именно под рост. */
    .shop .balance__media { right: 16%; }
    /* Отступ секции от хедера — сначала подняли слишком высоко (120px),
       заголовку не хватало воздуха сверху; отпускаем чуть ниже обратно.
       Размер фигуры от этого не страдает: он завязан на margin-top формы
       ниже (расстояние от заголовка до неё самой), а не на этом отступе. */
    .balance { justify-content: flex-start; padding-top: 170px; padding-bottom: 40px; }
    /* Меню слева — крупнее ещё раз, читаемо на 5K. Отступ от заголовка до
       формы тоже увеличен ещё сильнее — это расстояние initBalanceMediaAlign
       использует, чтобы посчитать высоту (и от неё — размер) фигуры девушки.
       Колонка чуть уже (было 860px) — специально: не по шрифту (он не
       трогается), а чтобы отдать освободившееся место под рост фигуры. */
    .balance__content { max-width: 800px; margin-top: 460px; }
    .balance__title { margin-bottom: 70px; }

    .currency-rate { padding: 42px 46px 46px; border-radius: 28px; margin-bottom: 46px; }
    .currency-rate__icon { width: 76px; height: 76px; }
    .currency-rate__label { font-size: 22px; }
    .currency-rate__value { font-size: 29px; }
    .currency-rate__balance-badge { width: 58px; height: 58px; }
    .currency-rate__balance-value { font-size: 30px; }

    .balance__form { gap: 40px; }
    .balance__submit { margin-top: 22px; }
    .field__control { padding: 42px 48px; font-size: 34px; border-radius: 26px; }
    .field--clearable .field__control { padding-right: 92px; }
    .field--password .field__control { padding-right: 152px; }
    .field__icon-btn { width: 60px; height: 60px; }
    .field__icon-btn svg { width: 32px; height: 32px; }
    .field__hint, .field__error { font-size: 24px; }
    .checkbox { font-size: 27px; gap: 22px; }
    .checkbox__box { width: 38px; height: 38px; }

    .vip-plans__grid { gap: 60px; }
    .vip-plan { padding: 84px 76px 78px; border-radius: 38px; }
    .vip-plan__head { gap: 32px; margin-bottom: 40px; }
    .vip-plan__icon { width: 104px; height: 104px; }
    .vip-plan__icon svg { width: 58px; height: 58px; }
    .vip-plan__title { font-size: 56px; }
    .vip-plan__tagline { font-size: 27px; }
    .vip-plan__desc { font-size: 31px; margin-bottom: 48px; }
    .vip-plan__perks { gap: 26px; margin-bottom: 54px; }
    .vip-plan__perks li { font-size: 31px; gap: 20px; }
    .vip-plan__periods { gap: 20px; margin-bottom: 40px; }
    .vip-plan__period { padding: 22px 14px; font-size: 25px; border-radius: 15px; }
    .vip-plan__coin { width: 42px; height: 42px; }

    .catalog__grid { gap: 50px; }
    .shop-card { border-radius: 32px; }
    .shop-card__body { padding: 44px 46px 52px; }
    .shop-card__body h3 { font-size: 34px; margin-bottom: 30px; }
    .shop-card__price { font-size: 28px; padding: 15px 8px 15px 30px; }
    .shop-card__coin { width: 44px; height: 44px; }
}

@media (max-width: 1080px) {
    .vip-plan { padding: 28px 24px 26px; }
}
@media (max-width: 900px) {
    .vip-plans { min-height: auto; }
    .vip-plans__grid { grid-template-columns: 1fr; }
}

/* ======================= FOOTER ======================= */
/* Финальная снап-точка (только на главной) — иначе mandatory-скролл не пускал бы дальше "Соц.сетей" */
.footer { position: relative; background: var(--bg); border-top: 1px solid var(--border); padding: 80px 0 0; }
html.snap-page .footer { scroll-snap-align: end; }
/* Ширина как у .container — совпадает с шапкой: начало слева под логотипом, конец справа под кнопкой «Авторизация» */
.footer__inner { position: relative; z-index: 2; display: grid; grid-template-columns: 1.1fr 1fr 1.4fr 1.1fr; gap: 30px; padding-bottom: 56px; }
.footer__brand p { color: #8b8d92; font-size: 15px; line-height: 1.5; }
.footer__col h4 { font-size: 18px; font-weight: 800; margin-bottom: 10px; }
.footer__col a { display: block; color: #8b8d92; font-size: 15px; margin-bottom: 6px; transition: color .2s; }
.footer__col a:hover { color: var(--white); }

/* ТЗ №2 (ревью), задача 11 — раньше сплошная надпись при font-size до 300px
   не помещалась в контейнер высотой до 210px и обрезалась криво (видна
   только средняя полоса букв — читалось как баг вёрстки). Вариант A из ТЗ:
   не сплошная заливка, а едва заметный контур (text-stroke, сама заливка
   прозрачна) — тогда высоту контейнера достаточно просто синхронизировать
   с font-size 1:1, ничего не обрезается, а сама надпись читается как
   текстура фона, а не как недорисованный текст. overflow-x:hidden — чтобы
   letter-spacing на очень широких экранах не вызвал горизонтальный скролл
   страницы (сама надпись не должна быть шире вьюпорта). */
.footer__watermark-wrap {
    position: relative;
    height: clamp(120px, 18vw, 320px);
    overflow: hidden;
    pointer-events: none;
}
.footer__watermark {
    position: absolute;
    left: 0; right: 0; top: 0;
    z-index: 2;
    text-align: center;
    font-size: clamp(120px, 18vw, 320px);
    font-weight: 900;
    letter-spacing: .02em;
    line-height: 1;
    color: transparent;
    -webkit-text-stroke: 1px rgba(255, 255, 255, .04);
    text-stroke: 1px rgba(255, 255, 255, .04);
    user-select: none;
    pointer-events: none;
}
.footer__watermark-wrap::after {
    content: '';
    position: absolute;
    z-index: 1;
    left: 50%;
    bottom: -38%;
    transform: translateX(-50%);
    width: 85%;
    height: 90%;
    background: radial-gradient(ellipse closest-side, rgba(159, 199, 255, .5), rgba(159, 199, 255, 0) 72%);
    filter: blur(24px);
}

/* Футер на очень широких экранах — та же типографика, что и у остального
   контента: заголовки колонок, ссылки и юр. блок читались заметно мельче
   всего остального. min-width:2601px, а не только 5120 — контейнер футера
   (.footer__inner — тот же .container, max-width:var(--maxw)=1700px) не
   меняется по ширине НИ на одной из этих ширин (2880/3200/3840/4096/5120),
   так что одни и те же абсолютные размеры (в px, не vw/%) одинаково хорошо
   ложатся на любую из них — отдельные правила под каждую не нужны, в
   отличие от hero/меню/VIP, которые действительно должны расти вместе с
   шириной экрана. Должно идти ПОСЛЕ базовых правил .footer__watermark выше —
   иначе более позднее по файлу безусловное правило с той же специфичностью
   перебивало бы это, несмотря на подходящий @media. */
@media (min-width: 2601px) {
    .footer { padding-top: 120px; }
    /* Прошлая версия расширяла .footer__inner аж до 2900px — колонки ссылок
       уехали заметно правее конца кнопки "Авторизация" в шапке, вся строка
       перестала быть "по линии" с шапкой. .footer__inner — тот же .container,
       что и .header__inner (тот же max-width:var(--maxw), то же
       margin:0 auto) — они и так уже выровнены по этим же самым краям без
       единой правки здесь; никакого расширения контейнера не нужно вообще.
       "ООО РЕГИОН ХАБ" фиксируем в px (352px, её нынешняя ширина) вместо
       доли (1.1fr) и добавляем гэп покрупнее — три колонки ссылок просто
       шире раздвигаются между собой в ПРЕЖНИХ границах контейнера, вплоть
       до его правого края (= правый край кнопки "Авторизация"). */
    .footer__inner { grid-template-columns: 352px 1fr 1.4fr 1.1fr; gap: 60px; padding-bottom: 80px; }
    .footer__brand p { font-size: 22px; }
    .footer__col h4 { font-size: 27px; margin-bottom: 18px; }
    .footer__col a { font-size: 22px; margin-bottom: 12px; }
    /* "REGION" — фоновая надпись во всю ширину футера. top:90px из прошлой
       правки прятал почти всю надпись целиком (сама "линия обреза" верно
       задумана — низ букв должен прятаться под overflow:hidden обёртки —
       но сдвиг был слишком сильным, читалась только узкая полоска сверху).
       Меньший сдвиг вниз — заметная часть букв сверху видна, а обрезается
       по-прежнему только нижняя часть. */
    .footer__watermark-wrap { height: 260px; }
    .footer__watermark { font-size: 390px; letter-spacing: 12px; top: 20px; }
}

/* ======================= REVEAL ANIMATION ======================= */
.reveal { opacity: 0; transform: translateY(34px); transition: opacity .7s var(--ease), transform .7s var(--ease); }
.reveal.is-visible { opacity: 1; transform: none; }

/* ======================= RESPONSIVE ======================= */
@media (max-width: 1080px) {
    .container { padding: 0 28px; }
    .launcher { padding: 40px; }
    .footer__inner { grid-template-columns: 1fr 1fr; gap: 40px 30px; }
    .catalog__grid { grid-template-columns: repeat(3, 1fr); }
}

@media (max-width: 900px) {
    /* На мобильных секции складываются в одну колонку и часто становятся выше экрана
       (особенно "Соц.сети" и "Игровые кадры" с их сеткой скриншотов). Обязательный
       постраничный снап в этом случае перепрыгивает через секции, у которых нет своей
       снап-точки — свайп с "Соц.сетей" улетает сразу к футеру, минуя "Игровые кадры".
       На мобильном снап поэтому отключаем — обычный свободный скролл. */
    html.snap-page { scroll-snap-type: none; }

    section { padding: 56px 0; }
    .social::before, .media::before { background-attachment: scroll; }
    .nav, .header__auth { display: none; }
    .burger { display: flex; }

    /* mobile dropdown nav */
    .nav.is-open {
        display: flex;
        flex-direction: column;
        gap: 4px;
        position: absolute;
        top: 82px; left: 0; right: 0;
        background: rgba(10, 12, 16, .98);
        backdrop-filter: blur(14px);
        border-bottom: 1px solid var(--border);
        padding: 16px 28px 24px;
    }
    .nav.is-open .nav__link { padding: 12px 0; font-size: 16px; }
    .nav.is-open .nav__admin-mobile,
    .nav.is-open .nav__auth-mobile {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        align-self: stretch;
        margin-top: 10px;
        text-align: center;
    }
    /* Обе кнопки идут подряд (админ-панель над "Мой персонаж") — не нужно
       двойного отступа между ними, второй хватает gap контейнера. Селектор
       обязательно с :not([hidden]) — иначе сработал бы и для скрытой
       (неавторизованный посетитель) админ-ссылки, просто по факту её
       соседства в DOM, а не видимости */
    .nav.is-open .nav__admin-mobile:not([hidden]) + .nav__auth-mobile { margin-top: 0; }

    .hero__inner { flex-direction: column; text-align: center; min-height: auto; padding-top: 30px; }
    .hero__content { max-width: 100%; margin: 0 auto; }
    .hero__line:first-child { margin-bottom: .55em; }
    .hero__graffiti { position: static; display: inline-block; margin-left: 12px; font-size: 40px; transform: rotate(-6deg); vertical-align: middle; }
    .hero__desc, .hero__join { margin-left: auto; margin-right: auto; }
    .hero__stats { justify-content: center; }
    .hero__media {
        position: relative;
        /* Сбрасываем и left, и right — иначе десктопное right:11% всё равно сдвигает
           блок (position:relative считает смещение от right), и врач съезжает влево */
        left: auto; right: auto; top: auto; width: 100%; height: 44vh; min-height: 320px;
        margin-bottom: 6px;
    }
    .hero__char { object-fit: cover; object-position: center 12%; }
    /* Именно короткие мобильные окна (напр. 800×600 — окно браузера, а не
       телефон "стоя") — фигура (44vh с min-height:320px) вместе с текстом
       базового мобильного размера в сумме сильно превышали высоту экрана,
       и низ (цитата) уходил далеко за экран. Уменьшаем и фигуру, и весь
       текст, чтобы всё уместилось без скролла. Вложенный @media — тот же
       max-width:900px этого блока плюс max-height:700px. */
    @media (max-height: 700px) {
        .hero__media { height: 28vh; min-height: 200px; margin-top: -30px; margin-bottom: 2px; }
        .hero__char { object-fit: contain; object-position: center top; }
        .hero__inner { padding-top: 0px; }
        .hero__title { font-size: clamp(24px, 7vw, 32px); }
        .hero__line:first-child { margin-bottom: -4px; }
        .hero__graffiti { display: block; margin: -6px 0 -8px; font-size: 30px; position: relative; z-index: 1; }
        .hero__desc { font-size: 13px; margin-top: 2px; }
        .hero__join { margin-top: 4px; gap: 8px; }
        .avatars__item, .avatars__plus { width: 40px; height: 40px; }
        .avatars__plus { font-size: 20px; line-height: 1; }
        .hero__join-text b { font-size: 11px; }
        .hero__join-text small { font-size: 10px; }
        .hero__stats { margin-top: 4px; gap: 8px; }
        .online { padding: 7px 11px; border-radius: 10px; }
        .online__count { font-size: 17px; }
        .online__label { font-size: 8.5px; }
        .promo__play { width: 30px; height: 30px; }
        .promo__label { font-size: 9.5px; }
        .hero__quotes { min-height: 44px; margin-top: 2px; padding: 6px 9px; }
        .hero__quotes-text { font-size: 10.5px; }
    }

    .launcher { grid-template-columns: 1fr; text-align: left; }
    .launcher__visual { order: -1; }
    .social__grid { grid-template-columns: 1fr; }
    .soc-card { min-height: auto; }
    /* Раньше кнопка растягивалась на всю ширину карточки на мобильном —
       теперь остаётся такой же маленькой "таблеткой" в нижней ямке, как и
       на десктопе, просто прижатой к правому краю. Правый внутренний
       отступ карточки на мобильном уже чуть меньше (24px вместо 34px) —
       кнопка сама сдвигается ближе к правому краю. */
    .soc-card { padding-right: 24px; }
    .soc-card .btn { align-self: flex-end; padding-right: 30px; }

    .media__grid { grid-template-columns: 1fr; }
    .feature { min-height: 300px; }

    /* Мобильная раскладка не трогается: возвращаем прежнее поведение
       (картинка+форма по центру экрана), чтобы изменения в .balance
       для десктопа сюда не протекали */
    .balance { justify-content: center; padding-top: 0; padding-bottom: 56px; min-height: 100vh; }
    /* На мобильном фото стоит сверху стека, а не внизу секции — градиент-переход
       (рассчитан на десктопную раскладку) на такой высокой секции перекрывал
       чекбоксы и кнопку "Пополнить", делая их тусклыми, будто под затемнением */
    .balance__fade { height: 140px; }
    /* text-align:center возвращаем явно — иначе на мобильном наследуется
       text-align:left из брейкпоинта ≤1080px (он рассчитан на узкий десктоп,
       где заголовок не центрируют над фото) и заголовок съезжает влево.
       .auth .balance__title (auth.html/recover.html/new-password.html —
       свой левый заголовок для десктопа, где рядом стоит фигура) — та же
       проблема, но по специфичности (два класса) перебивает это правило
       даже здесь, на мобильном: заголовок съезжал влево, а на
       "ВОССТАНОВЛЕНИЕ ПАРОЛЯ" даже обрезался по правому краю экрана. */
    .balance__title,
    .auth .balance__title { margin-left: 0; text-align: center; }
    /* "ВОССТАНОВЛЕНИЕ" (recover.html/new-password.html) на 14 букв заметно
       длиннее "АВТОРИЗАЦИЯ" (11) — на узких телефонах даже сам нижний предел
       общего .section-title (clamp(38px, 5.2vw, 74px) — на такой ширине
       экрана всегда упирается в 38px минимум) уже не помещается по ширине и
       вылезает за правый край экрана, это не про центрирование, слово в
       принципе шире контейнера. Слово одно, переносить некуда — уменьшаем
       именно этот заголовок отдельно, остальные страницы (клумп 38px) не трогаем. */
    .recover .balance__title { font-size: clamp(24px, 8vw, 32px); }
    .balance__content { max-width: 100%; text-align: center; margin-top: 0; margin-left: 0; }
    .balance__submit { align-self: center; }
    .checkbox { text-align: left; }
    /* Плавающая .roulette-fab привязана к абсолютной позиции фигуры на
       десктопе — на мобильном фигура уже не absolute (см. ниже), поэтому
       вместо неё показываем отдельную кнопку в потоке страницы */
    .roulette-fab { display: none; }
    .roulette-btn-mobile { display: flex; }
    .balance__media {
        position: relative;
        left: auto; right: auto; top: auto; width: 100%; height: 44vh; min-height: 300px;
        aspect-ratio: auto;
        /* Сброс transform: translateY(-50%) из брейкпоинта ≤1900px (там им
           центрируют фото по вертикали в секции) — на мобильном фото не
           абсолютное и не должно центрироваться, а без сброса этот transform
           продолжал действовать и здесь, поднимая картинку почти на половину
           её собственной высоты вверх, "срезая" верх фигуры за пределы экрана */
        transform: none;
        margin-bottom: 24px;
    }
    /* object-fit:cover на разных мобильных ширинах при неизменной высоте
       блока (44vh) даёт разный масштаб увеличения картинки — в зависимости
       от ширины экрана либо срезало макушку/подбородок, либо, наоборот,
       обрезало кадр так, что фигура частично уходила за левый/правый край
       (например, бокал в руке). contain никогда не обрезает — фигура
       всегда видна целиком, просто чуть меньше на непропорциональных
       по ширине экранах. object-position не нужен — contain сам центрирует. */
    .balance__char {
        object-fit: contain;
        object-position: center center;
        /* Полноразмерное затухание из базового правила (55–96% высоты) сюда
           не подходит — на мобильном фото не занимает низ секции, а стоит
           отдельным блоком, и такой длинный градиент "съедал" половину
           фигуры. Но и совсем без затухания низ картинки обрывается резко —
           в самом файле фигура обрезана почти впритык по ступням/подолу.
           Короткое затухание только по последним ~10% высоты кадра смягчает
           именно этот жёсткий край, не трогая руки/бокал/платье выше. При
           object-fit:contain высота блока почти всегда и есть ограничивающая
           сторона (высота 44vh обычно меньше ширины экрана), поэтому кадр
           картинки совпадает с высотой блока и градиент ложится точно по
           реальным пикселям, а не в пустоту. */
        -webkit-mask-image: linear-gradient(to bottom, black 0%, black 90%, transparent 100%);
        mask-image: linear-gradient(to bottom, black 0%, black 90%, transparent 100%);
    }
    .catalog__grid { grid-template-columns: repeat(2, 1fr); }

    /* Персонаж администратора крупнее и на планшете/телефоне — тот же
       принцип, что и на десктопе (см. .admin-auth выше). Раньше здесь была
       фиксированная height:62vh при aspect-ratio:auto (унаследовано от
       базового мобильного правила) — на невысоких экранах (мобильный
       landscape, ~375–450px высоты) это давало короткий, но ШИРОКИЙ блок,
       чьи пропорции не совпадали с реальной картинкой (1280×714). Circle
       glow (::before) считает себя от ширины родителя (width:95% +
       aspect-ratio:1/1) — в широком-и-низком блоке это давало гигантский
       круг, который вылезал далеко за верх/низ блока разлитым пятном
       (никакой не круг, а как будто грубый прямоугольный "вырез"). Тот же
       фикс, что и на десктопе — aspect-ratio по реальной картинке, размер
       по ширине — гарантирует, что блок ВСЕГДА пропорционален контенту,
       на любой высоте экрана. */
    .admin-auth .balance__media {
        width: 84%;
        max-width: 360px;
        height: auto;
        /* min-height:300px из базового мобильного правила выше иначе
           продолжает "подпирать" блок снизу и здесь, при узком width
           заставляя его снова быть шире, чем высоким — та же поломка
           aspect-ratio, которую этот блок должен был устранить */
        min-height: 0;
        aspect-ratio: 1280 / 714;
        margin: 0 auto 24px;
    }
    .admin-auth .balance__char {
        -webkit-mask-image: linear-gradient(to bottom, black 0%, black 65%, transparent 99%);
        mask-image: linear-gradient(to bottom, black 0%, black 65%, transparent 99%);
    }
}

@media (max-width: 560px) {
    .container { padding: 0 18px; }
    .header__inner { height: 68px; }
    .catalog__filters { top: 68px; }
    .logo { gap: 10px; }
    .logo__img { height: 49px; }
    .logo__text b { font-size: 22px; }
    .logo__text small { font-size: 11px; letter-spacing: 4px; }
    .hero__title { font-size: 40px; }
    .hero__line { white-space: normal; }
    .hero__graffiti { font-size: 30px; }
    .avatars__item, .avatars__plus { width: 50px; height: 50px; margin-left: -14px; }
    .online__count { font-size: 24px; }
    .hero__stats { flex-direction: column; align-items: stretch; }
    .online { justify-content: center; }
    .promo { justify-content: center; }
    .launcher { padding: 26px 22px; }
    .tag { font-size: 13px; padding: 9px 14px; }
    .reqs li { font-size: 15px; }
    .gallery { grid-template-columns: 1fr 1fr; grid-template-rows: auto; }
    .footer__inner { grid-template-columns: 1fr; gap: 34px; }
}
/* Надпись REGION скрыта целиком на мобильном (ТЗ №2, задача 11) — на узком
   экране декоративная текстура фона только сокращает и без того тесное
   место под реальным контентом подвала, отдельного правила добавлять
   некуда в существующий @media(max-width:560px) выше — этот порог 768px
   шире. */
@media (max-width: 768px) {
    .footer__watermark-wrap { display: none; }
}

/* ======================= ТОСТ «В РАЗРАБОТКЕ» ======================= */
.soon-toast {
    position: fixed;
    right: 28px;
    bottom: 28px;
    z-index: 2000;
    display: flex;
    align-items: flex-start;
    gap: 16px;
    width: 360px;
    max-width: calc(100vw - 40px);
    padding: 20px 20px 20px 18px;
    background: linear-gradient(160deg, rgba(30, 33, 44, .92), rgba(12, 13, 18, .92));
    border: 1px solid rgba(88, 101, 242, .4);
    border-radius: 18px;
    box-shadow: 0 24px 60px -20px rgba(0, 0, 0, .65), 0 0 0 1px rgba(255, 255, 255, .04) inset;
    backdrop-filter: blur(18px) saturate(160%);
    -webkit-backdrop-filter: blur(18px) saturate(160%);
    overflow: hidden;
    opacity: 0;
    transform: translateY(24px) scale(.96);
    pointer-events: none;
    transition: opacity .35s var(--ease), transform .35s var(--ease);
}
.soon-toast.is-visible {
    opacity: 1;
    transform: translateY(0) scale(1);
    pointer-events: auto;
}
.soon-toast__icon {
    flex: none;
    width: 42px;
    height: 42px;
    display: grid;
    place-items: center;
    border-radius: 12px;
    background: linear-gradient(160deg, rgba(88, 101, 242, .35), rgba(88, 101, 242, .12));
    color: #aab2f8;
    box-shadow: 0 0 0 1px rgba(88, 101, 242, .3) inset;
}
/* "Успешный" вариант тоста (например, код подтверждения отправлен) —
   зелёный акцент вместо фиолетового предупреждающего, чтобы явно читался
   как хорошая новость, а не как "заглушка в разработке" */
.soon-toast--success { border-color: rgba(74, 222, 128, .4); }
.soon-toast--success .soon-toast__icon {
    background: linear-gradient(160deg, rgba(74, 222, 128, .35), rgba(74, 222, 128, .12));
    color: #6ee7a0;
    box-shadow: 0 0 0 1px rgba(74, 222, 128, .35) inset;
}
.soon-toast--success .soon-toast__progress { background: #4ade80; }
.soon-toast__body { flex: 1; min-width: 0; }
.soon-toast__title { font-size: 15px; font-weight: 800; color: var(--white); margin-bottom: 4px; }
.soon-toast__text { font-size: 13.5px; line-height: 1.45; color: var(--muted); }
.soon-toast__close {
    flex: none;
    width: 26px;
    height: 26px;
    display: grid;
    place-items: center;
    margin: -4px -4px 0 0;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: var(--muted);
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
    transition: background .2s, color .2s;
}
.soon-toast__close:hover { background: rgba(255, 255, 255, .08); color: var(--white); }
.soon-toast__progress {
    position: absolute;
    left: 0; bottom: 0;
    height: 3px;
    width: 100%;
    background: var(--discord);
    transform-origin: left;
    animation: soon-toast-progress 4.2s linear forwards;
}
@keyframes soon-toast-progress {
    from { transform: scaleX(1); }
    to { transform: scaleX(0); }
}

@media (max-width: 560px) {
    .soon-toast { right: 16px; left: 16px; bottom: 16px; width: auto; }
}

/* ======================= Тост "вы уже администратор" ======================= */
/* Та же карточка, что и .soon-toast (тот же fixed-угол, blur, тени), но
   красный акцент (тема "режим администратора" — см. .profile.is-admin) и
   кнопка действия вместо авто-исчезающего прогресс-бара */
.admin-toast {
    position: fixed;
    right: 28px;
    bottom: 28px;
    z-index: 2000;
    display: flex;
    align-items: flex-start;
    gap: 16px;
    width: 360px;
    max-width: calc(100vw - 40px);
    padding: 20px 20px 20px 18px;
    background: linear-gradient(160deg, rgba(30, 33, 44, .92), rgba(12, 13, 18, .92));
    border: 1px solid rgba(255, 92, 108, .4);
    border-radius: 18px;
    box-shadow: 0 24px 60px -20px rgba(0, 0, 0, .65), 0 0 0 1px rgba(255, 255, 255, .04) inset;
    backdrop-filter: blur(18px) saturate(160%);
    -webkit-backdrop-filter: blur(18px) saturate(160%);
    opacity: 0;
    transform: translateY(24px) scale(.96);
    pointer-events: none;
    transition: opacity .35s var(--ease), transform .35s var(--ease);
}
.admin-toast.is-visible { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
.admin-toast__icon {
    flex: none;
    width: 42px;
    height: 42px;
    display: grid;
    place-items: center;
    border-radius: 12px;
    background: linear-gradient(160deg, rgba(255, 92, 108, .35), rgba(255, 92, 108, .12));
    color: #ff8792;
    box-shadow: 0 0 0 1px rgba(255, 92, 108, .3) inset;
}
.admin-toast__body { flex: 1; min-width: 0; }
.admin-toast__title { font-size: 15px; font-weight: 800; color: var(--white); margin-bottom: 4px; }
.admin-toast__text { font-size: 13.5px; line-height: 1.45; color: var(--muted); margin-bottom: 14px; }
.admin-toast__action {
    padding: 9px 16px;
    border-radius: 9px;
    border: 1px solid rgba(255, 92, 108, .4);
    background: rgba(255, 92, 108, .12);
    color: #ff8792;
    font-size: 13.5px;
    font-weight: 700;
    cursor: pointer;
    transition: background .2s, color .2s, border-color .2s;
}
.admin-toast__action:hover { background: rgba(255, 92, 108, .22); color: var(--white); border-color: rgba(255, 92, 108, .6); }
.admin-toast__close {
    flex: none;
    width: 26px;
    height: 26px;
    display: grid;
    place-items: center;
    margin: -4px -4px 0 0;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: var(--muted);
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
    transition: background .2s, color .2s;
}
.admin-toast__close:hover { background: rgba(255, 255, 255, .08); color: var(--white); }

@media (max-width: 560px) {
    .admin-toast { right: 16px; left: 16px; bottom: 16px; width: auto; }
}

/* ======================= КНОПКА "НАВЕРХ" ======================= */
.to-top {
    position: fixed;
    left: 28px;
    bottom: 28px;
    z-index: 900;
    width: 52px;
    height: 52px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    border: 1px solid var(--border-strong);
    background: linear-gradient(160deg, rgba(159, 199, 255, .16), rgba(12, 14, 19, .85));
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    color: var(--accent);
    cursor: pointer;
    box-shadow: 0 16px 34px -16px rgba(0, 0, 0, .6);
    opacity: 0;
    visibility: hidden;
    transform: translateY(16px) scale(.85);
    transition: opacity .3s var(--ease), transform .3s var(--ease), visibility 0s linear .3s,
                background .25s var(--ease), color .25s var(--ease), box-shadow .25s var(--ease), border-color .25s var(--ease);
}
.to-top.is-visible {
    opacity: 1;
    visibility: visible;
    transform: translateY(0) scale(1);
    transition: opacity .3s var(--ease), transform .3s var(--ease), visibility 0s,
                background .25s var(--ease), color .25s var(--ease), box-shadow .25s var(--ease), border-color .25s var(--ease);
}
.to-top svg { width: 22px; height: 22px; }
.to-top:hover {
    background: linear-gradient(160deg, var(--accent-soft), var(--accent));
    color: #0a1224;
    border-color: transparent;
    box-shadow: 0 20px 40px -14px rgba(159, 199, 255, .55);
    transform: translateY(-4px) scale(1);
}
.to-top:active { transform: translateY(-1px) scale(.96); }

@media (max-width: 560px) {
    .to-top { left: 16px; bottom: 16px; width: 46px; height: 46px; }
    .to-top svg { width: 19px; height: 19px; }
}

/* То же самое, но под 4096×2160 — свои, чуть меньшие размеры. */
@media (min-width: 4080px) and (max-width: 4599px) {
    .roulette-fab { bottom: 40px; padding: 19px 22px; border-radius: 22px; gap: 10px; }
    .roulette-fab__ring { top: 19px; width: 58px; height: 58px; margin-left: -29px; }
    .roulette-fab__icon { width: 58px; height: 58px; }
    .roulette-fab__label { font-size: 15px; }
    .to-top { bottom: 40px; width: 63px; height: 63px; }
    .to-top svg { width: 27px; height: 27px; }
}

/* На 5120×2880 обе плавающие кнопки были мелкими — просто увеличиваем их,
   оставляя в тех же углах ФИЗИЧЕСКОГО экрана, где они и были (right/left:
   28px, как в базовом правиле). Прошлая попытка подтянуть их к краю
   колонки контента (calc((100vw-1700px)/2)) вместо этого стянула обе
   кнопки в середину экрана, почти друг к другу — визуально даже хуже,
   чем просто мелкие по углам. */
@media (min-width: 4600px) {
    .roulette-fab {
        bottom: 48px;
        padding: 24px 28px;
        border-radius: 28px;
        gap: 12px;
    }
    .roulette-fab__ring { top: 24px; width: 72px; height: 72px; margin-left: -36px; }
    .roulette-fab__icon { width: 72px; height: 72px; }
    .roulette-fab__label { font-size: 18px; }
    .to-top {
        bottom: 48px;
        width: 78px;
        height: 78px;
    }
    .to-top svg { width: 34px; height: 34px; }
}

/* ======================= ЛАЙТБОКС (ПРОСМОТР ФОТО) ======================= */
body.lightbox-lock { overflow: hidden; }
html.modal-scroll-lock { overflow: hidden; }

.lightbox {
    position: fixed;
    inset: 0;
    z-index: 3000;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(5, 6, 9, .9);
    backdrop-filter: blur(16px) saturate(140%);
    -webkit-backdrop-filter: blur(16px) saturate(140%);
    opacity: 0;
    visibility: hidden;
    transition: opacity .3s var(--ease), visibility 0s linear .3s;
}
.lightbox.is-open {
    opacity: 1;
    visibility: visible;
    transition: opacity .3s var(--ease), visibility 0s;
}
.lightbox__stage {
    position: relative;
    max-width: min(1100px, 86vw);
    max-height: 84vh;
    display: flex;
}
.lightbox__img {
    max-width: 100%;
    max-height: 84vh;
    width: auto;
    height: auto;
    border-radius: 18px;
    box-shadow: 0 40px 100px -28px rgba(0, 0, 0, .75), 0 0 0 1px rgba(255, 255, 255, .06);
    transform: scale(.92);
    opacity: 0;
    transition: transform .35s var(--ease), opacity .35s var(--ease);
}
.lightbox.is-open .lightbox__img { transform: scale(1); opacity: 1; }

/* Переход между фото: текущее "уезжает" в сторону навигации, следующее "въезжает" с обратной */
.lightbox.is-open .lightbox__img.is-switching {
    transition: transform .26s var(--ease), opacity .26s var(--ease);
}
.lightbox.is-open .lightbox__img.is-switching.dir-next { transform: scale(.97) translateX(-34px); opacity: 0; }
.lightbox.is-open .lightbox__img.is-switching.dir-prev { transform: scale(.97) translateX(34px); opacity: 0; }

.lightbox__close,
.lightbox__arrow {
    position: absolute;
    display: grid;
    place-items: center;
    border-radius: 50%;
    border: 1px solid var(--border-strong);
    background: rgba(255, 255, 255, .06);
    color: var(--white);
    cursor: pointer;
    transition: background .2s var(--ease), color .2s var(--ease), transform .2s var(--ease), opacity .2s var(--ease);
}

.lightbox__close {
    top: 26px;
    right: 30px;
    width: 46px;
    height: 46px;
    font-size: 26px;
    line-height: 1;
}
.lightbox__close:hover { background: rgba(255, 255, 255, .16); transform: rotate(90deg); }

.lightbox__arrow {
    top: 50%;
    transform: translateY(-50%);
    width: 56px;
    height: 56px;
    z-index: 2;
}
.lightbox__arrow svg { width: 24px; height: 24px; }
.lightbox__arrow:hover { background: var(--accent); border-color: var(--accent); color: #0a1224; }
.lightbox__arrow:hover svg { transform: none; }
.lightbox__arrow--prev { left: 24px; }
.lightbox__arrow--next { right: 24px; }
.lightbox__arrow.is-hidden {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translateY(-50%) scale(.8);
}

.gallery__item { cursor: pointer; }

@media (max-width: 900px) {
    .lightbox__close { top: 16px; right: 16px; width: 40px; height: 40px; font-size: 22px; }
    .lightbox__arrow { width: 46px; height: 46px; }
    .lightbox__arrow--prev { left: 10px; }
    .lightbox__arrow--next { right: 10px; }
    .lightbox__stage { max-width: 92vw; max-height: 78vh; }
    .lightbox__img { max-height: 78vh; }
}

/* ======================= ПРАВОВЫЕ СТРАНИЦЫ (соглашение, возврат и т.д.) =======================
   ТЗ №2 (ревью), задача 2 — заголовок и корпус документа должны читаться
   как официальный текст, а не как маркетинговый блок сайта: без курсива,
   без акцентного голубого, без выключки по центру, колонка не шире 760px.
   Раньше .legal__notice тут же стилизовал жёлтую плашку-предупреждение —
   сама плашка убрана из разметки (задача 1, см. комментарий в refund.html),
   поэтому и её CSS удалён как мёртвый код, а не просто скрыт. */
.legal { padding: 64px 0 100px; }
.legal__title {
    text-align: left;
    font-style: normal;
    font-weight: 500;
    color: var(--white);
    font-size: 32px;
    letter-spacing: normal;
    margin-bottom: 8px;
}
/* Дата под заголовком (задача 1) — отдельно от .legal__date внизу
   документа (та — подпись под текстом, эта — быстрая справка сразу под
   заголовком; обе меняются синхронно при правке редакции, см. комментарий
   в refund.html) */
.legal__date-top { font-size: 14px; color: var(--muted); margin-bottom: 32px; }

/* Оглавление — задача 2. Список без маркеров, ссылки акцентные,
   scroll-behavior:smooth — общее правило на html в начале файла */
.legal__toc {
    max-width: 760px;
    margin: 0 auto 40px;
    padding: 20px 24px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: rgba(255, 255, 255, .02);
}
.legal__toc-title { font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); margin-bottom: 12px; }
.legal__toc a { display: block; padding: 5px 0; color: var(--accent); font-size: 14.5px; text-decoration: none; }
.legal__toc a:hover { text-decoration: underline; }

.legal__body { max-width: 760px; margin: 0 auto; color: var(--muted); font-size: 16px; line-height: 1.7; text-align: left; }
.legal__body p { margin-bottom: 16px; }
.legal__h { display: block; color: var(--white); font-size: 20px; font-weight: 500; margin-top: 40px; margin-bottom: 4px; }
.legal__items { margin: -6px 0 16px 22px; }
.legal__items p { margin-bottom: 6px; }
.legal__meta { color: var(--white); }
.legal__meta:last-child { margin-bottom: 0; }
.legal__date { margin-top: 24px; }

/* ======================= ЛЕНТА ВЫПАДЕНИЙ (cases.html) ======================= */
/* Заменяет обычный .header только на этой странице (см. комментарий в
   cases.html) — витрина последних выигрышей вместо навигации, в духе
   ленты открытия кейсов CS:GO/CS2. Новые карточки добавляются в JS слева
   (см. pushDropFeedItem в main.js) через transition на width/margin — сама
   лента при этом просто одна длинная flex-строка без ограничения по ширине,
   а .drop-feed вокруг неё — overflow:hidden, поэтому старые карточки не
   анимированно "уезжают", а естественным образом уходят за правый край и
   там же обрезаются, как только их вытесняют новые слева. DOM не растёт
   бесконечно — pushDropFeedItem удаляет самые старые узлы справа пачками,
   когда их накапливается больше разумного (они там всё равно уже не видны).
*/
.drop-feed {
    position: relative;
    width: 100%;
    height: 128px;
    overflow: hidden;
    background: linear-gradient(180deg, rgba(255, 255, 255, .07), rgba(255, 255, 255, .02)), rgba(19, 21, 24, .55);
    backdrop-filter: blur(22px) saturate(160%);
    -webkit-backdrop-filter: blur(22px) saturate(160%);
    border-bottom: 1px solid rgba(255, 255, 255, .1);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .12), 0 8px 30px rgba(0, 0, 0, .25);
}
.drop-feed__track {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    display: flex;
    align-items: center;
    padding-left: 24px;
}
/* Плавное затемнение по краям — без него у левой карточки (которая ещё
   растёт при появлении) и у обрезанных справа был бы резкий обрубленный край */
.drop-feed__fade {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 80px;
    z-index: 2;
    pointer-events: none;
}
.drop-feed__fade--left { left: 0; background: linear-gradient(90deg, #0a0c11, rgba(10, 12, 17, 0)); }
.drop-feed__fade--right { right: 0; background: linear-gradient(270deg, #0a0c11, rgba(10, 12, 17, 0)); }

/* Карточка предмета — цвет редкости передаётся через --rarity инлайном (см.
   ROULETTE_FEED_META в main.js), единая переменная и на рамку, и на подсветку,
   чтобы не заводить по классу на каждый из 7 уровней редкости */
.drop-feed__item {
    --rarity: #7f8b9c;
    position: relative;
    flex: 0 0 auto;
    width: 0;
    height: 92px;
    margin-right: 0;
    opacity: 0;
    border-radius: 12px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    background: linear-gradient(180deg, rgba(255, 255, 255, .06), rgba(255, 255, 255, .015));
    border: 1px solid rgba(255, 255, 255, .08);
    border-top: 3px solid var(--rarity);
    box-shadow: inset 0 0 22px -6px var(--rarity);
    /* width/margin-right анимируются, а не transform — так соседние карточки
       реально отталкиваются вправо самим потоком (reflow), а не одна эта
       карточка визуально наезжает поверх них */
    transition: width .5s cubic-bezier(.22, .8, .22, 1), margin-right .5s cubic-bezier(.22, .8, .22, 1), opacity .4s ease;
}
.drop-feed__item.is-in { width: 88px; margin-right: 12px; opacity: 1; }
/* mask-image смягчает края у настоящих фото-иконок (выигрыши «Крути-гуляй»,
   см. resolveFeedEntryMeta в main.js — у них реальная картинка приза, а не
   общий логотип) — у самого логотипа эффект тоже есть, но там он попросту
   не заметен, лого и так на прозрачном фоне без острых краёв */
.drop-feed__item__icon {
    width: 32px;
    height: 32px;
    object-fit: contain;
    filter: drop-shadow(0 2px 6px rgba(0, 0, 0, .5));
    -webkit-mask-image: radial-gradient(ellipse 62% 62% at 50% 50%, #000 55%, transparent 92%);
    mask-image: radial-gradient(ellipse 62% 62% at 50% 50%, #000 55%, transparent 92%);
}
.drop-feed__item__label {
    font-size: 10.5px;
    font-weight: 800;
    letter-spacing: .2px;
    color: var(--white);
    white-space: normal;
    text-align: center;
    line-height: 1.2;
    max-width: 78px;
    padding: 0 4px;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
/* Джекпот — то же золото, что и на самом колесе, плюс более заметное сияние */
.drop-feed__item--jackpot { box-shadow: inset 0 0 28px -4px var(--rarity), 0 0 18px rgba(255, 215, 0, .35); }
.drop-feed__item--jackpot .drop-feed__item__label { color: #ffe9a8; }

@media (max-width: 560px) {
    .drop-feed { height: 96px; }
    .drop-feed__item { height: 74px; }
    .drop-feed__item.is-in { width: 70px; margin-right: 9px; }
    .drop-feed__item__icon { width: 24px; height: 24px; }
    .drop-feed__item__label { font-size: 9px; max-width: 62px; }
}

/* ======================= РУЛЕТКА (cases.html) ======================= */
.roulette-page {
    min-height: 100vh;
    display: flex;
    /* flex-start вместо center — сверху уже своя лента выпадений (.drop-feed,
       в потоке страницы, не наложением), так что колесо незачем ещё и
       центровать по всей высоте экрана под ней: смотрится так, будто оно
       "висит" далеко от неё. */
    align-items: flex-start;
    padding: 40px 0 80px;
    position: relative;
    overflow: hidden;
    background:
        radial-gradient(80% 60% at 50% 15%, rgba(70, 147, 255, .12) 0%, rgba(8, 9, 12, 0) 60%),
        linear-gradient(180deg, #0a0c11 0%, #08090c 100%);
}
/* Фоновая текстура (photos/roulette/fon.webp, граффити-стена) — на этой
   странице и на case.html (см. .case-page::before ниже), тот же самый общий
   фон "рулетки" в целом. Едва заметная (2% непрозрачности, как и просили) —
   отдельным слоем через ::before, а не прямо в background секции, потому что
   opacity затронул бы и сам градиент/цвет фона, а не только картинку. */
.roulette-page::before, .case-page::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 0;
    background-image: url('../photos/roulette/fon.webp');
    background-size: cover;
    background-position: center;
    opacity: .02;
    pointer-events: none;
}
.roulette-page__inner { position: relative; z-index: 1; display: flex; flex-direction: column; align-items: center; text-align: center; width: 100%; }
.roulette-page__title { margin-bottom: 10px; }
.roulette-page__sub { color: var(--muted); font-size: 15px; margin-bottom: 26px; }
/* Переиспользует .currency-rate__balance/-badge/-label/-value из shop.html —
   та же плашка RC, тот же визуальный язык (см. комментарий в разметке) */
.roulette-page__balance { margin-bottom: 44px; }

/* ======================= МИНИ-ПРОФИЛЬ (cases.html) ======================= */
/* Узкая полоска под лентой выпадений, замещающая ту часть обычной .header,
   где на других страницах виден баланс/аватар/ссылка назад — видна всегда,
   в том числе гостю (в "гостевом" виде: баланс 0, аватар-заглушка), см.
   initPlayerHud в main.js — он только донастраивает её под реального игрока,
   если сессия есть, а не показывает/прячет целиком. */
.player-hud-bar {
    width: 100%;
    padding: 14px 0;
    background: linear-gradient(180deg, rgba(255, 255, 255, .03), rgba(255, 255, 255, 0));
    border-bottom: 1px solid rgba(255, 255, 255, .06);
}
.player-hud-bar__inner { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
/* Слева — просто ссылка назад, не тяжёлая кнопка: второстепенное действие
   рядом с явной CTA "Пополнить" справа, не должна с ней конкурировать */
.player-hud-bar__back {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 14px;
    font-weight: 600;
    color: var(--muted);
    transition: color .2s var(--ease);
}
.player-hud-bar__back svg { width: 16px; height: 16px; }
.player-hud-bar__back:hover { color: var(--white); }
.player-hud { display: flex; align-items: center; gap: 12px; }
.player-hud__topup { padding: 9px 20px; font-size: 13px; }
/* Сердечко-переключатель "Мои избранные" — тот же принцип, что и у
   .case-page__favorite: контур красным по умолчанию, заливка красным в
   активном состоянии. Правило на самом <path>, а не только на <svg> —
   у path свой presentation-атрибут stroke="currentColor", который иначе
   перебивает унаследованный цвет (CSS на том же элементе всегда сильнее
   его собственного presentation-атрибута). */
.player-hud__favorite {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
    flex-shrink: 0;
}
.player-hud__favorite svg { width: 22px; height: 22px; fill: none; transition: transform .2s var(--ease); }
.player-hud__favorite svg path { stroke: #ff5c6c; transition: fill .2s var(--ease), stroke .2s var(--ease); }
.player-hud__favorite:hover svg { transform: scale(1.08); }
.player-hud__favorite.is-active svg path { fill: #ff5c6c; }
/* Тот же кружок-значок и структура, что у .currency-rate__balance на
   shop.html, только в зелёном (а не синем) тоне — по референсу от
   пользователя, "не точь-в-точь", просто узнаваемая плашка баланса */
.player-hud__balance {
    display: flex;
    align-items: center;
    gap: 8px;
    background: linear-gradient(135deg, rgba(74, 222, 128, .22), rgba(74, 222, 128, .06));
    border: 1px solid rgba(74, 222, 128, .4);
    border-radius: 999px;
    padding: 6px 16px 6px 6px;
}
.player-hud__balance-badge {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--coin);
    display: grid;
    place-items: center;
    overflow: hidden;
    flex-shrink: 0;
}
.player-hud__balance-badge .coin-icon { width: 60%; height: 60%; }
.player-hud__balance-value { font-size: 15px; font-weight: 800; color: #eafff0; white-space: nowrap; }
.player-hud__avatar {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    overflow: hidden;
    flex-shrink: 0;
    display: block;
    border: 2px solid rgba(255, 255, 255, .18);
    transition: border-color .2s var(--ease), transform .2s var(--ease);
}
.player-hud__avatar:hover { border-color: rgba(159, 199, 255, .6); transform: translateY(-1px); }
.player-hud__avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Заглушка "нет фото" — для гостя и для аккаунтов без своей фотографии,
   тот же силуэт, что и на welcome.html/player-profile.html, только без
   подписи (кружок слишком маленький, чтобы под неё было место) */
.player-hud__avatar-empty {
    width: 100%;
    height: 100%;
    display: grid;
    place-items: center;
    background: rgba(255, 255, 255, .06);
    color: var(--muted);
}
.player-hud__avatar-empty svg { width: 60%; height: 60%; }

@media (max-width: 480px) {
    /* Текста "Вернуться в магазин" и всей плашки профиля вместе на такой
       ширине не хватает места — оставляем от ссылки назад только стрелку,
       aria-label на самой ссылке всё ещё называет её для скринридеров */
    .player-hud-bar__back-text { display: none; }
    .player-hud { gap: 8px; }
    .player-hud__topup { padding: 8px 14px; font-size: 12px; }
    .player-hud__balance { padding: 5px 12px 5px 5px; }
    .player-hud__balance-value { font-size: 13.5px; }
    .player-hud__avatar { width: 34px; height: 34px; }
}

/* ======================= БОНУС-БАННЕР (cases.html) ======================= */
/* Карусель из двух (и, возможно, в будущем больше) слайдов — листается
   стрелками/точками или сама раз в 3с, см. initBonusBanner в main.js. Каждый
   .bonus-banner__slide — свой слой на всю карточку (картинка + затемнение +
   текст), крестом переходящий через opacity/transform; .bonus-banner__dots
   и __nav — общий "хром" поверх обоих слоёв, а не часть конкретного слайда,
   поэтому лежат отдельно от .bonus-banner__slides и всегда видны. */
.bonus-banner {
    position: relative;
    width: 100%;
    min-height: 300px;
    overflow: hidden;
    border-radius: 20px;
    background: #1b1030; /* запасной цвет, пока не загрузилась картинка */
    box-shadow: 0 24px 60px -20px rgba(0, 0, 0, .55), 0 0 0 rgba(0, 0, 0, 0);
    transition: box-shadow .6s ease;
}
/* Внешнее свечение под цвет текущего слайда — янтарное для banner.jpg
   (дворовые фонари), синее для banner2.jpg (см. setGlow в initBonusBanner,
   main.js). Второй box-shadow в паре с базовой тенью выше — transition на
   box-shadow плавно перетекает между двумя цветами при смене класса. */
.bonus-banner.is-glow-amber { box-shadow: 0 24px 60px -20px rgba(0, 0, 0, .55), 0 0 80px 8px rgba(255, 184, 77, .32); }
.bonus-banner.is-glow-blue { box-shadow: 0 24px 60px -20px rgba(0, 0, 0, .55), 0 0 80px 8px rgba(70, 147, 255, .32); }
.bonus-banner__slides { position: absolute; inset: 0; }
.bonus-banner__slide {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    text-align: left;
    opacity: 0;
    z-index: 1;
    pointer-events: none;
    transition: opacity .6s ease, transform .6s cubic-bezier(.22, .8, .22, 1);
}
.bonus-banner__slide.is-active { opacity: 1; transform: translateX(0); z-index: 2; pointer-events: auto; }
/* Уходящий слайд — в сторону, откуда пришёл новый (см. goToSlide в main.js) */
.bonus-banner__slide.is-exit-left { transform: translateX(-36px); opacity: 0; }
.bonus-banner__slide.is-exit-right { transform: translateX(36px); opacity: 0; }
/* Входящий слайд стартует смещённым — этот же класс снимается почти сразу
   (в следующем кадре), после чего transform/opacity едут к состоянию
   .is-active по transition выше, и получается плавный въезд с нужной стороны */
.bonus-banner__slide.is-enter-left { transform: translateX(-36px); opacity: 0; }
.bonus-banner__slide.is-enter-right { transform: translateX(36px); opacity: 0; }

.bonus-banner__dots {
    position: absolute;
    top: 18px;
    right: 22px;
    z-index: 5;
    display: flex;
    gap: 6px;
}
.bonus-banner__dot { width: 7px; height: 7px; border-radius: 50%; background: rgba(255, 255, 255, .3); border: 0; padding: 0; cursor: pointer; transition: background .2s var(--ease), transform .2s var(--ease); }
.bonus-banner__dot:hover { background: rgba(255, 255, 255, .55); }
.bonus-banner__dot.is-active { background: #fff; transform: scale(1.15); }
/* Готовая широкая композиция (персонаж/сцена + фон) — переход между ними
   уже нарисован внутри самой картинки, поэтому она просто растягивается на
   весь слайд; .bonus-banner__fade ниже только слегка затемняет текстовую
   сторону для читаемости, а не имитирует сам переход. */
.bonus-banner__image {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 50% 35%;
    z-index: 1;
}
.bonus-banner__fade {
    position: absolute;
    inset: 0;
    z-index: 2;
    background: linear-gradient(90deg,
        rgba(10, 6, 20, .78) 0%,
        rgba(10, 6, 20, .6) 32%,
        rgba(10, 6, 20, .3) 52%,
        rgba(10, 6, 20, .08) 70%,
        rgba(10, 6, 20, 0) 85%);
    pointer-events: none;
}
.bonus-banner__content { position: relative; z-index: 3; padding: 32px 0 32px 44px; max-width: 420px; }
.bonus-banner__title {
    font-size: 26px;
    font-weight: 900;
    text-transform: uppercase;
    line-height: 1.22;
    letter-spacing: .3px;
    margin-bottom: 14px;
}
.bonus-banner__text { color: rgba(244, 246, 251, .7); font-size: 14.5px; line-height: 1.55; }
.bonus-banner__nav { position: absolute; right: 22px; bottom: 18px; z-index: 5; display: flex; gap: 8px; }
/* Квадратные (не круглые) кнопки с эффектом "жидкого стекла" — размытие
   того, что под ними (backdrop-filter), полупрозрачная заливка с лёгким
   диагональным бликом сверху и тонкая светлая обводка, как у мокрого стекла */
.bonus-banner__arrow {
    width: 36px;
    height: 36px;
    border-radius: 11px;
    background: linear-gradient(145deg, rgba(255, 255, 255, .24), rgba(255, 255, 255, .06));
    backdrop-filter: blur(14px) saturate(180%);
    -webkit-backdrop-filter: blur(14px) saturate(180%);
    border: 1px solid rgba(255, 255, 255, .32);
    box-shadow: 0 8px 20px -10px rgba(0, 0, 0, .6), inset 0 1px 0 rgba(255, 255, 255, .35);
    color: #fff;
    display: grid;
    place-items: center;
    cursor: pointer;
    transition: background .2s var(--ease), transform .2s var(--ease), border-color .2s var(--ease);
}
.bonus-banner__arrow:hover { background: linear-gradient(145deg, rgba(255, 255, 255, .34), rgba(255, 255, 255, .1)); border-color: rgba(255, 255, 255, .5); transform: translateY(-1px); }
.bonus-banner__arrow:active { transform: translateY(0) scale(.94); }
.bonus-banner__arrow svg { width: 18px; height: 18px; }

@media (max-width: 720px) {
    /* Карточка выше, а текст — в узкой колонке (не на всю ширину), иначе на
       телефоне описание растягивается под лицо персонажа и упирается в
       стрелки снизу — при overlay-раскладке (текст поверх фото, без
       отдельной "полосы" под картинку, как раньше) на таком узком экране
       иначе не разойтись */
    .bonus-banner { min-height: 340px; }
    .bonus-banner__content { padding: 22px 20px; max-width: 62%; }
    .bonus-banner__title { font-size: 18px; }
    .bonus-banner__text { font-size: 12.5px; }
    .bonus-banner__dots { top: 12px; right: 14px; }
    .bonus-banner__nav { right: 14px; bottom: 14px; }
    .bonus-banner__arrow { width: 32px; height: 32px; border-radius: 10px; }
    .bonus-banner__arrow svg { width: 16px; height: 16px; }
}

/* ======================= БЕСПЛАТНЫЕ КЕЙСЫ (cases.html) ======================= */
.free-cases { width: 100%; margin-top: 40px; }
/* Не абстрактно "красивый заголовок", а концепт в духе самого сайта —
   REGION ONLINE, криминальный ролеплей: печать вроде гарантийной/нотариальной
   (галочка в круге — "сделка чистая"), фраза из уличного жаргона в кавычках-
   ёлочках вместо нейтрального "бесплатные кейсы", подпись про район ниже
   мельче и спокойнее — второй, тихий голос рядом с "выкриком" заголовка. */
.free-cases__heading { display: flex; align-items: center; justify-content: center; gap: 16px; margin-bottom: 30px; text-align: left; }
.free-cases__seal {
    width: 46px;
    height: 46px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    flex-shrink: 0;
    color: #4ade80;
    background: radial-gradient(circle at 35% 30%, rgba(74, 222, 128, .25), rgba(74, 222, 128, .04));
    border: 1.5px solid rgba(74, 222, 128, .5);
    box-shadow: 0 0 0 4px rgba(74, 222, 128, .08);
}
.free-cases__seal svg { width: 22px; height: 22px; }
.free-cases__title {
    font-size: clamp(23px, 3.2vw, 29px);
    font-weight: 900;
    font-style: italic;
    text-transform: uppercase;
    letter-spacing: .3px;
    line-height: 1.05;
    color: var(--white);
    margin: 0 0 4px;
}
.free-cases__title::before { content: '«'; color: #4ade80; font-style: normal; margin-right: 1px; }
.free-cases__title::after { content: '»'; color: #4ade80; font-style: normal; margin-left: 1px; }
.free-cases__subtitle { font-size: 13.5px; color: var(--muted); margin: 0; }
.free-cases__empty { font-size: 14px; color: var(--muted); padding: 18px 2px 4px; margin: 0; }

@media (max-width: 480px) {
    .free-cases__heading { gap: 12px; }
    .free-cases__seal { width: 38px; height: 38px; }
    .free-cases__seal svg { width: 18px; height: 18px; }
}
.free-cases__grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
    gap: 24px;
    justify-content: center;
}
/* Карточка кейса — ссылка на его страницу (case.html). Никакой панели вокруг
   (фона/рамки/паддинга) — только сама картинка и подпись под ней, как в
   присланном референсе, где кейс стоит прямо на фоне страницы, без своей
   отдельной карточки-контейнера. */
.free-case-card {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    cursor: pointer;
    transition: transform .35s var(--ease);
}
.free-case-card:hover { transform: scale(1.06); z-index: 2; }
/* Тёплое янтарно-жёлтое свечение позади картинки — в цвет уличных фонарей
   на самой картинке (photos/roulette/case.webp), а не нейтрально-голубое.
   Крупнее самой картинки (140%/115%), поэтому даже лёжа НИЖЕ неё по
   z-index, выглядывает размытым кольцом вокруг краёв, а не прячется целиком
   за ней. Два слоя с разным размером/цветом/задержкой анимации — не одно
   ровное пятно, а неравномерное "живое" переливание, как настоящий фонарь. */
.free-case-card__glow {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 140%;
    aspect-ratio: 1 / 1;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: radial-gradient(closest-side, rgba(255, 184, 77, .6), rgba(255, 138, 61, .18) 55%, rgba(255, 138, 61, 0) 78%);
    opacity: 0;
    pointer-events: none;
    z-index: 0;
}
.free-case-card__glow-accent {
    position: absolute;
    top: 46%;
    left: 54%;
    width: 115%;
    aspect-ratio: 1 / 1;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: radial-gradient(closest-side, rgba(255, 224, 130, .5), rgba(255, 224, 130, 0) 70%);
    opacity: 0;
    pointer-events: none;
    z-index: 0;
}
.free-case-card:hover .free-case-card__glow { animation: free-case-glow 2.2s ease-in-out infinite; }
.free-case-card:hover .free-case-card__glow-accent { animation: free-case-glow 2.2s ease-in-out infinite; animation-delay: .7s; }
/* "Искры" — 6 точек вокруг кейса, каждая летит в свою сторону (--sx/--sy,
   заданы инлайном в разметке) и гаснет; z-index ВЫШЕ картинки (не как у
   .free-case-card__glow) — иначе, начинаясь от центра, они первые доли
   секунды летели бы под картинкой и были не видны вовсе. Анимация стоит на
   паузе, пока курсор не наведён, чтобы не крутиться вхолостую по всей
   странице сразу. Цвет — тот же тёплый янтарный, что и у свечения, а не
   голубой, как раньше — единая гамма "фонарь во дворе". */
.free-case-card__sparks { position: absolute; inset: 0; pointer-events: none; z-index: 2; }
.free-case-card__sparks i {
    position: absolute;
    top: 46%;
    left: 50%;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: #ffd27a;
    box-shadow: 0 0 8px 2px rgba(255, 184, 77, .8);
    opacity: 0;
    animation: free-case-spark 1.3s ease-out infinite;
    animation-delay: var(--d, 0s);
    animation-play-state: paused;
}
.free-case-card:hover .free-case-card__sparks i { animation-play-state: running; }
.free-case-card__image-wrap {
    position: relative;
    z-index: 1;
    width: 100%;
    aspect-ratio: 1 / 1;
    border-radius: 14px;
    overflow: hidden;
    margin-bottom: 12px;
}
.free-case-card__image { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Нижняя панель — бейдж и цена внутри общей полоски с собственным фоном,
   а не голым текстом на пустом месте под картинкой (было именно так и
   выглядело "подвешенным в пустоте") */
.free-case-card__footer {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: 9px 14px;
    border-radius: 12px;
    background: linear-gradient(180deg, rgba(255, 255, 255, .05), rgba(255, 255, 255, .015));
    border: 1px solid rgba(255, 255, 255, .08);
}
/* Короткая бейдж-подпись вместо простого текста "Бесплатный №N" — свой
   маленький акцентный чип, другой стиль, не копия референса */
.free-case-card__label {
    display: inline-block;
    font-size: 11px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .6px;
    color: #ffb84d;
    background: rgba(255, 184, 77, .12);
    border: 1px solid rgba(255, 184, 77, .35);
    border-radius: 999px;
    padding: 3px 12px;
}
.free-case-card__price { font-size: 15px; font-weight: 900; color: #4ade80; }

@keyframes free-case-glow {
    0%, 100% { opacity: .3; transform: translate(-50%, -50%) scale(.92); }
    50% { opacity: .6; transform: translate(-50%, -50%) scale(1.04); }
}
@keyframes free-case-spark {
    0% { opacity: 0; transform: translate(0, 0) scale(.5); }
    18% { opacity: 1; }
    100% { opacity: 0; transform: translate(var(--sx), var(--sy)) scale(1); }
}

@media (prefers-reduced-motion: reduce) {
    .free-case-card, .free-case-card__glow, .free-case-card__glow-accent, .free-case-card__sparks i { animation: none !important; transition: none !important; }
    .case-stage__reveal-glow,
    .case-stage__reveal-platform,
    .case-stage__reveal-icon::before,
    .case-contents__item--glow::after { animation: none !important; opacity: .55 !important; }
}

@media (max-width: 560px) {
    .free-cases__grid { grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 16px; }
}

@media (max-width: 560px) {
    .roulette-page { padding: 28px 0 60px; }
}

/* ======================= СТРАНИЦА КЕЙСА (case.html) ======================= */
/* Верхняя полоска — это .player-hud-bar, общий со страницей рулетки
   компонент (см. разметку выше и style.css у самого .player-hud-bar) */
.case-page {
    position: relative;
    overflow: hidden;
    min-height: 100vh;
    padding: 48px 0 90px;
    background:
        radial-gradient(80% 55% at 50% 8%, rgba(255, 184, 77, .1) 0%, rgba(8, 9, 12, 0) 60%),
        linear-gradient(180deg, #0a0c11 0%, #08090c 100%);
}
.case-page__inner { position: relative; z-index: 1; display: flex; flex-direction: column; align-items: center; text-align: center; }
.case-page__title {
    font-size: clamp(30px, 4.4vw, 42px);
    font-weight: 900;
    text-transform: uppercase;
    letter-spacing: .3px;
    margin-bottom: 12px;
}
/* Жёлтая плашка-предупреждение "кейс бесплатный, нужно пополнение" (см.
   refreshFreeCaseGate в initCaseStage, main.js) — тёплый янтарный тон,
   отличается от красных ошибок и синих обычных подсказок сайта. */
.case-free-notice {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    max-width: 420px;
    margin: 0 auto 24px;
    padding: 16px 18px;
    border-radius: 14px;
    background: linear-gradient(165deg, rgba(255, 200, 60, .16), rgba(255, 200, 60, .05));
    border: 1px solid rgba(255, 200, 60, .35);
    color: #ffd876;
    text-align: left;
}
.case-free-notice svg { width: 22px; height: 22px; flex: none; margin-top: 1px; }
.case-free-notice p { margin: 0; font-size: 14px; line-height: 1.45; color: #ffe6ad; }
/* Кнопка "Открыть" на время кулдауна сама превращается в таймер — просто
   визуально другое (приглушённое) состояние disabled-кнопки, без лишней
   разметки под отдельный виджет */
.case-stage__open-btn--locked {
    background: rgba(255, 255, 255, .06) !important;
    color: rgba(255, 255, 255, .55) !important;
    box-shadow: none !important;
    cursor: not-allowed;
    font-variant-numeric: tabular-nums;
}

/* Ряд "Открыть" + сердечко избранного рядом справа */
.case-stage__action-row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 16px;
    margin: 0 0 40px;
}
.case-stage__action-row .case-stage__open-btn { margin: 0; }
/* Избранное — сердечко всегда красное: просто контур, пока кейс не в
   избранном, и сплошная заливка, когда добавлен. Состояние живёт в
   localStorage конкретного браузера (см. initCasePage в main.js) */
.case-page__favorite {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
    flex-shrink: 0;
}
.case-page__favorite svg { width: 24px; height: 24px; fill: none; transition: transform .2s var(--ease); }
/* Селектор на сам <path>, а не только на <svg> — у него в разметке есть
   собственный stroke="currentColor", и как presentation-атрибут элемента он
   перебивал бы унаследованный от родителя цвет; CSS-правило на этом же
   элементе всегда сильнее его собственного presentation-атрибута */
.case-page__favorite svg path { stroke: #ff5c6c; transition: fill .2s var(--ease), stroke .2s var(--ease); }
.case-page__favorite:hover svg { transform: scale(1.08); }
.case-page__favorite.is-active svg { transform: scale(1.12); }
.case-page__favorite.is-active svg path { fill: #ff5c6c; }

/* Купоны на скидку из «Крути-гуляй» — значок слева от "Открыть" (см.
   initCaseCoupon в main.js), скрыт, пока купонов нет. Настоящая картинка
   приза-купона (kugreen.png), а не самодельная иконка — та же самая, что и
   в самом кейсе/схроне, только в круглой рамке помельче. Золотистый акцент
   вокруг — в цвет самого купона, не спутать с красным сердечком избранного
   рядом. */
.case-coupon-btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    height: 48px;
    padding: 0 16px 0 8px;
    border-radius: 999px;
    background: rgba(255, 215, 0, .1);
    border: 1px solid rgba(255, 215, 0, .4);
    color: #ffd76a;
    font-weight: 800;
    font-size: 14px;
    cursor: pointer;
    flex-shrink: 0;
    transition: background .2s var(--ease), transform .2s var(--ease), border-color .2s var(--ease);
}
.case-coupon-btn__img {
    width: 34px;
    height: 34px;
    object-fit: contain;
    flex-shrink: 0;
    border-radius: 50%;
    background: radial-gradient(closest-side, rgba(255, 215, 0, .35), rgba(255, 215, 0, 0) 75%);
    filter: drop-shadow(0 0 6px rgba(255, 215, 0, .55));
}
.case-coupon-btn:hover { background: rgba(255, 215, 0, .18); border-color: rgba(255, 215, 0, .65); transform: translateY(-1px); }

/* ======================= МОДАЛКА "КУПОНЫ НА СКИДКУ" ======================= */
.coupon-modal__field {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    font-size: 14px;
    font-weight: 600;
    color: var(--muted);
    margin-bottom: 14px;
}
.coupon-modal__field input {
    width: 70px;
    padding: 10px 12px;
    border-radius: 10px;
    border: 1px solid var(--border-strong);
    background: var(--bg-card);
    color: var(--white);
    font-size: 15px;
    font-weight: 700;
    text-align: center;
}
.coupon-modal__total { font-size: 15px; font-weight: 800; color: #ffd76a; margin: 0; }

/* Никакой панели-рамки вокруг картинки — сама картинка (прозрачный PNG)
   прямо на фоне страницы. Боковых пятен-свечений (были синее/жёлтое) больше
   нет — только маска на самой картинке ниже (.case-stage__image), которая
   гасит её края в прозрачность, чтобы прямоугольный силуэт файла не был
   виден резкой рамкой на чёрном фоне. */
.case-stage {
    position: relative;
    width: 100%;
    max-width: 640px;
    height: 380px;
    margin: 0 auto 32px;
}
/* Сама картинка — крупнее, чем раньше ("не стесняйся"); клик по ней гасит её
   и запускает демо-прокрутку (см. .case-stage__reel ниже, initCaseStage в
   main.js) */
/* Просто картинка, не интерактивный элемент — запуск демо-прокрутки только
   через кнопку "Открыть" (.case-stage__open-btn ниже) */
.case-stage__trigger {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: opacity .4s var(--ease), transform .4s var(--ease);
}
.case-stage__trigger.is-hidden { opacity: 0; transform: scale(.92); pointer-events: none; }
/* Никакого свечения позади статичного логотипа в состоянии покоя — оно
   уместно только когда реально показан результат (см. .case-stage__reveal-glow
   ниже, привязано к самой иконке выигранного предмета, а не висит тут само по
   себе просто "для красоты"). */
/* max-height ограничивает картинку высотой самого .case-stage (она
   квадратная 1:1 — при növых фото-кейсах, снятых уже с собственным фоном
   комнаты/т.п., а не прозрачным вырезом персонажа, это не даёт картинке
   вылезать за пределы сцены и наезжать на заголовок сверху — раньше картинка
   тянулась только по ширине, а по высоте свободно перетекала за верхнюю
   границу .case-stage, и было незаметно только пока там была прозрачность.
   Маска теперь гасит края со ВСЕХ сторон симметрично (эллипс от центра), а
   не только книзу/по бокам — рассчитана на то, что теперь это цельное фото
   с собственным прямоугольным фоном, а не силуэт на прозрачности, поэтому
   резкая рамка иначе видна была бы и сверху тоже. */
.case-stage__image {
    position: relative;
    z-index: 1;
    max-width: min(440px, 84vw);
    max-height: 100%;
    width: auto;
    height: auto;
    display: block;
    /* Без drop-shadow — на нечёткой маскированной кромке он рисовал
       собственный размытый повтор силуэта, и это само по себе читалось как
       ещё одно кольцо-граница поверх плавного затухания */
    -webkit-mask-image: radial-gradient(ellipse 66% 66% at 50% 46%, #000 0%, #000 30%, rgba(0, 0, 0, .75) 50%, rgba(0, 0, 0, .32) 72%, transparent 100%);
    mask-image: radial-gradient(ellipse 66% 66% at 50% 46%, #000 0%, #000 30%, rgba(0, 0, 0, .75) 50%, rgba(0, 0, 0, .32) 72%, transparent 100%);
}
/* Приглушает жёлтое сияние из самого фото (под словом FREE, там, где
   светится открытый кейс) — тёмное пятно поверх этого места, умножающееся
   с исходными пикселями (mix-blend-mode: multiply), а не просто закрашивающее
   их сплошным цветом сверху */
.case-stage__image-mute {
    position: absolute;
    left: 50%;
    top: 66%;
    width: 46%;
    aspect-ratio: 1 / 1;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    z-index: 2;
    background: radial-gradient(closest-side, rgba(8, 6, 16, .6), rgba(8, 6, 16, 0) 78%);
    filter: blur(14px);
    mix-blend-mode: multiply;
    pointer-events: none;
}
/* Лента наград — едет строго под неподвижной чёрточкой-указателем по центру,
   как в настоящих кейс-открывалках; появляется вместо картинки на клик и
   гаснет обратно спустя пару секунд после того, как остановилась */
.case-stage__reel {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 2;
    width: 100%;
    height: 132px;
    border-radius: 16px;
    overflow: hidden;
    background: linear-gradient(180deg, #171029 0%, #0e0a1c 100%);
    box-shadow: 0 30px 70px -30px rgba(0, 0, 0, .7), inset 0 1px 0 rgba(255, 255, 255, .06);
    opacity: 0;
    transition: opacity .5s ease;
}
.case-stage__reel.is-visible { opacity: 1; }
.case-stage__reel-track { position: absolute; top: 0; left: 0; height: 100%; display: flex; align-items: center; will-change: transform; }
.case-stage__reel-item {
    flex: 0 0 auto;
    width: 96px;
    height: 96px;
    margin: 0 7px;
    border-radius: 12px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    background: linear-gradient(180deg, var(--rarity) 0%, rgba(10, 12, 17, 0) 72%), #12141c;
    border: 1px solid rgba(255, 255, 255, .08);
    transition: border-color .3s var(--ease), box-shadow .3s var(--ease);
}
.case-stage__reel-item img { width: 34px; height: 34px; object-fit: contain; filter: drop-shadow(0 2px 6px rgba(0, 0, 0, .5)); }
/* Как и у .case-stage__reveal-label — длинные названия ("Ferrari 488
   Spider" и т.п.) без переноса/ограничения ширины вылезали за пределы
   96px карточки. Перенос в 2 строки + обрезка по высоте держат подпись
   внутри карточки. */
.case-stage__reel-item span {
    font-size: 11px;
    font-weight: 800;
    color: #fff;
    text-align: center;
    line-height: 1.2;
    max-width: 84px;
    padding: 0 4px;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.case-stage__reel-item.is-winner { border-color: rgba(255, 255, 255, .55); box-shadow: 0 0 0 2px rgba(255, 255, 255, .25), 0 0 26px rgba(255, 255, 255, .3); }
/* Неподвижная чёрточка-указатель по центру ленты — не сама крутится,
   крутится лента ЗА ней */
.case-stage__pointer {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 50%;
    width: 3px;
    transform: translateX(-50%);
    background: linear-gradient(180deg, rgba(255, 184, 77, 0), #ffb84d 15%, #ffb84d 85%, rgba(255, 184, 77, 0));
    box-shadow: 0 0 12px rgba(255, 184, 77, .8);
    z-index: 3;
    pointer-events: none;
}
.case-stage__pointer::before, .case-stage__pointer::after {
    content: '';
    position: absolute;
    left: 50%;
    width: 0;
    height: 0;
    border-left: 6px solid transparent;
    border-right: 6px solid transparent;
    transform: translateX(-50%);
}
.case-stage__pointer::before { top: -1px; border-top: 7px solid #ffb84d; }
.case-stage__pointer::after { bottom: -1px; border-bottom: 7px solid #ffb84d; }

/* Крупная карточка выигрыша — выезжает поверх сцены после того, как лента
   остановилась (или мгновенно "телепортировалась" при пропуске анимации),
   держится, пока не нажата "Продолжить" (см. revealWin в initCaseStage,
   main.js). --rarity подставляется инлайном под цвет выпавшей награды —
   то же пятно-свечение позади, что и на карточках содержимого ниже по
   странице. Это вид ПО УМОЛЧАНИЮ — у кейса «Свой» (case.html), без
   изменений. У «Крути-гуляй» (case3.html) поверх этого же разметочного
   каркаса включается модификатор .case-stage__reveal--showcase ниже —
   там совсем другой, более просторный вид с крупной картинкой без рамки. */
.case-stage__reveal {
    position: absolute;
    top: 50%;
    left: 50%;
    z-index: 5;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    padding: 30px 36px;
    border-radius: 22px;
    background: linear-gradient(180deg, rgba(23, 16, 41, .96) 0%, rgba(14, 10, 28, .98) 100%);
    border: 1px solid rgba(255, 255, 255, .12);
    box-shadow: 0 30px 80px -20px rgba(0, 0, 0, .8);
    opacity: 0;
    pointer-events: none;
    transform: translate(-50%, -50%) scale(.5);
    transition: opacity .4s ease, transform .5s cubic-bezier(.34, 1.56, .64, 1);
}
.case-stage__reveal.is-visible { opacity: 1; transform: translate(-50%, -50%) scale(1); }
/* Свечение висит на самой иконке (см. вложенность в разметке), а не на всей
   карточке целиком — inset:-30% отсчитывался бы от карточки со всем её
   паддингом+подписью, и пятно оказалось бы между иконкой и текстом, а не
   ровно на иконке предмета */
.case-stage__reveal-icon {
    position: relative;
    width: 76px;
    height: 76px;
    border-radius: 50%;
    background: rgba(255, 255, 255, .08);
    display: grid;
    place-items: center;
    box-shadow: 0 0 30px var(--rarity, rgba(159, 199, 255, .5));
}
.case-stage__reveal-glow {
    position: absolute;
    inset: -55%;
    z-index: -1;
    border-radius: 50%;
    background: radial-gradient(closest-side, var(--rarity, #9fc7ff), rgba(159, 199, 255, 0) 70%);
    opacity: .5;
    filter: blur(20px);
    animation: case-reveal-pulse 1.8s ease-in-out infinite;
}
@keyframes case-reveal-pulse {
    0%, 100% { opacity: .4; transform: scale(.9); }
    50% { opacity: .75; transform: scale(1.1); }
}
.case-stage__reveal-icon img { position: relative; width: 60%; height: 60%; object-fit: contain; }
/* white-space:nowrap раньше не давал длинным названиям призов (например,
   "Ferrari 488 Spider") переноситься — карточка не тянется под текст, и он
   просто вылезал за её границы. max-width + перенос держат подпись внутри
   карточки на любой длине названия. */
.case-stage__reveal-label {
    position: relative;
    font-size: 20px;
    font-weight: 900;
    color: #fff;
    text-transform: uppercase;
    letter-spacing: .4px;
    white-space: normal;
    text-align: center;
    max-width: 260px;
}

/* ==== «Крути-гуляй» (case3.html) — витринный вид карточки выигрыша ==== */
/* Без рамки и фона карточки вообще — просто крупная картинка приза (её
   собственные жёсткие края фотографии смягчены mask-image ниже), подпись
   под ней, и единственный источник света — пятно в цвет редкости У ОСНОВАНИЯ
   иконки, вытянутое вверх (не кружок вокруг предмета, а будто он подсвечен
   снизу и свет тянется вверх сквозь него). Порядок в разметке: сначала
   иконка (с этим свечением внутри неё, за картинкой), затем подпись под
   ним — см. case3.html. */
.case-stage__reveal--showcase {
    gap: 10px;
    padding: 0;
    border-radius: 0;
    background: none;
    border: 0;
    box-shadow: none;
}
.case-stage__reveal--showcase .case-stage__reveal-icon {
    width: 168px;
    height: 168px;
    border-radius: 0;
    background: none;
    box-shadow: none;
}
/* Свечение — два слоя вместо одного плоского пятна, для объёма и "живости":
   яркое плотное ядро прямо у предмета (::before, слабое размытие) и мягкое
   крупное сияние вокруг, выходящее за пределы самой иконки (.platform,
   сильное размытие). У них разный период и небольшой сдвиг по фазе — ядро и
   ореол дышат не совсем синхронно, будто это одно живое свечение, а не
   заводной пульс. Центрируем через inset (left/right/top/bottom), а не
   left:50%+transform — see комментарий ниже про анимацию. */
.case-stage__reveal--showcase .case-stage__reveal-icon::before {
    content: '';
    position: absolute;
    inset: 20%;
    border-radius: 50%;
    background: radial-gradient(closest-side, var(--rarity, #9fc7ff) 0%, transparent 76%);
    filter: blur(5px);
    opacity: 1;
    z-index: -1;
    pointer-events: none;
    animation: case-reveal-pulse 1.7s ease-in-out infinite;
}
/* Раньше анимация пульса (case-reveal-pulse) трогает transform (scale) и
   ПОЛНОСТЬЮ ЗАМЕЩАЕТ, а не складывает его со своим transform — если бы
   центрирование здесь шло через left:50%+transform:translateX(-50%), оно бы
   слетало на каждом кадре анимации (так и был баг раньше). Инсетом эта
   проблема не грозит вообще. Сейчас крупнее и заметнее, чем было (inset был
   6%, стал -16% — сияние выходит за пределы самой картинки). */
.case-stage__reveal--showcase .case-stage__reveal-platform {
    position: absolute;
    inset: -16%;
    border-radius: 50%;
    background: radial-gradient(closest-side, var(--rarity, #9fc7ff) 0%, transparent 68%);
    filter: blur(22px);
    opacity: .8;
    z-index: -2;
    pointer-events: none;
    animation: case-reveal-pulse 2.3s ease-in-out infinite;
    animation-delay: -.7s;
}
/* Плавное затухание краёв самой фотографии — часть исходников (мешки денег,
   банковская карта и т.п.) имеют собственную чёткую прямоугольную границу
   вырезки, без этой маски она читалась бы как рамка-срез. Чуть меньше, чем
   раньше (94% → 82%), чтобы вокруг предмета было видно само свечение. */
.case-stage__reveal--showcase .case-stage__reveal-icon img {
    width: 82%;
    height: 82%;
    object-fit: contain;
    filter: drop-shadow(0 12px 22px rgba(0, 0, 0, .55));
    -webkit-mask-image: radial-gradient(ellipse 62% 62% at 50% 50%, #000 55%, transparent 92%);
    mask-image: radial-gradient(ellipse 62% 62% at 50% 50%, #000 55%, transparent 92%);
}
.case-stage__reveal--showcase .case-stage__reveal-label { text-shadow: 0 2px 14px rgba(0, 0, 0, .7); }

/* Реплика "барыги" (только у кейса «Крути-гуляй», case3.html) — карточка в
   духе облака-реплики, с маленьким хвостиком сверху, будто персонаж на
   картинке только что это сказал. Та же появляющаяся анимация, что и у
   .case-stage__reveal (fade + пружинистый scale), только без позиционирования
   absolute — просто идёт в потоке под сценой. */
.case-stage__dealer-line {
    position: relative;
    max-width: 420px;
    margin: 0 auto 28px;
    padding: 16px 22px;
    border-radius: 16px;
    background: linear-gradient(180deg, rgba(23, 16, 41, .96) 0%, rgba(14, 10, 28, .98) 100%);
    border: 1px solid rgba(94, 226, 255, .25);
    box-shadow: 0 20px 50px -18px rgba(0, 0, 0, .7);
    color: #eaf6ff;
    font-size: 14.5px;
    font-weight: 600;
    line-height: 1.5;
    text-align: center;
    opacity: 0;
    transform: translateY(-8px) scale(.94);
    transition: opacity .35s ease, transform .4s cubic-bezier(.34, 1.56, .64, 1);
}
.case-stage__dealer-line::before {
    content: '';
    position: absolute;
    top: -8px;
    left: 50%;
    transform: translateX(-50%) rotate(45deg);
    width: 16px;
    height: 16px;
    background: rgba(23, 16, 41, .98);
    border-left: 1px solid rgba(94, 226, 255, .25);
    border-top: 1px solid rgba(94, 226, 255, .25);
    border-radius: 3px 0 0 0;
}
.case-stage__dealer-line.is-visible { opacity: 1; transform: translateY(0) scale(1); }

.case-stage__open-btn { min-width: 220px; margin: 0 0 40px; }

/* Переключатель — уже полностью рабочий (сохраняет выбор в localStorage),
   просто пока нечего пропускать: реального открытия ещё нет (см. __notice
   ниже). Когда оно появится, эта же настройка сразу включится в дело. */
.case-page__skip {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    font-size: 13px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .4px;
    color: var(--muted);
    cursor: pointer;
    user-select: none;
    margin-bottom: 40px;
}
.case-page__skip-switch { position: relative; display: inline-flex; }
.case-page__skip-switch input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.case-page__skip-track {
    width: 40px;
    height: 22px;
    border-radius: 999px;
    background: rgba(255, 255, 255, .12);
    border: 1px solid rgba(255, 255, 255, .18);
    position: relative;
    transition: background .2s var(--ease), border-color .2s var(--ease);
}
.case-page__skip-thumb {
    position: absolute;
    top: 2px;
    left: 2px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: #fff;
    transition: transform .2s var(--ease);
}
.case-page__skip-switch input:checked ~ .case-page__skip-track { background: rgba(159, 199, 255, .35); border-color: rgba(159, 199, 255, .6); }
.case-page__skip-switch input:checked ~ .case-page__skip-track .case-page__skip-thumb { transform: translateX(18px); }

/* Разделитель между превью кейса и содержимым — тонкая линия, гаснущая по
   краям, с маленьким ромбом-огоньком по центру (тот же дуэт цветов, что и
   у остального оформления этого кейса — янтарный + синий акцент бренда) */
.case-divider {
    width: 100%;
    max-width: 420px;
    margin: 4px auto 34px;
    display: flex;
    align-items: center;
    justify-content: center;
}
.case-divider::before, .case-divider::after {
    content: '';
    flex: 1;
    height: 1px;
    background: linear-gradient(90deg, rgba(255, 255, 255, 0), rgba(255, 255, 255, .2));
}
.case-divider::after { background: linear-gradient(90deg, rgba(255, 255, 255, .2), rgba(255, 255, 255, 0)); }
.case-divider span {
    flex-shrink: 0;
    width: 9px;
    height: 9px;
    margin: 0 14px;
    border-radius: 2px;
    background: linear-gradient(135deg, #ffb84d, #4693ff);
    transform: rotate(45deg);
    box-shadow: 0 0 14px rgba(159, 199, 255, .55);
}

.case-contents__title { font-size: 20px; font-weight: 900; text-transform: uppercase; letter-spacing: .3px; margin-bottom: 24px; }
.case-contents__grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: 16px;
    width: 100%;
    max-width: 900px;
}
/* Цвет редкости — та же CS:GO-подобная градация, что и на ленте выпадений
   рулетки (.drop-feed__item, ROULETTE_FEED_META в main.js): один и тот же
   визуальный язык для "какой ценности выигрыш" по всему сайту */
.case-contents__item {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 22px 10px;
    border-radius: 14px;
    background: linear-gradient(180deg, var(--rarity) 0%, rgba(10, 12, 17, 0) 72%), #12141c;
    border: 1px solid rgba(255, 255, 255, .08);
    overflow: hidden;
}
.case-contents__item-icon {
    position: relative;
    z-index: 1;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: rgba(255, 255, 255, .12);
    display: grid;
    place-items: center;
}
.case-contents__item-icon img { width: 60%; height: 60%; object-fit: contain; }
.case-contents__item-name { position: relative; z-index: 1; font-size: 13px; font-weight: 800; color: #fff; }
.case-contents__item--jackpot .case-contents__item-icon { box-shadow: 0 0 18px rgba(255, 215, 0, .6); }
.case-contents__item--jackpot .case-contents__item-name { color: #ffe9a8; }

/* ==== «Крути-гуляй» (case3.html) — крупные фото наград без кружка-подложки ==== */
/* Настоящие фотографии призов, а не единая иконка-логотип — в маленьком
   кружке с подложкой (как у «Свой») они читались мелко и невыразительно.
   Тут иконка растянута почти на всю ширину карточки, без своего фона/рамки,
   а mask-image на самой картинке гасит её собственные жёсткие края (у
   некоторых исходников — мешков денег, банковской карты — свой чёткий
   прямоугольный/фигурный контур вырезки, без маски он читался бы как рамка). */
.case-contents__grid--showcase .case-contents__item-icon {
    width: 84%;
    aspect-ratio: 1 / 1;
    height: auto;
    border-radius: 0;
    background: none;
}
.case-contents__grid--showcase .case-contents__item-icon img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    -webkit-mask-image: radial-gradient(ellipse 64% 64% at 50% 50%, #000 55%, transparent 92%);
    mask-image: radial-gradient(ellipse 64% 64% at 50% 50%, #000 55%, transparent 92%);
}
/* У золотых иконка теперь прямоугольная (не круг, как в базовом виде выше),
   и наследуемый box-shadow-"ободок" вокруг box-жестей читался бы жёсткой
   квадратной рамкой — тут его нет, сияние золотых карточек теперь идёт от
   контура самой карточки (см. --glow ниже), не от иконки. */
.case-contents__grid--showcase .case-contents__item--jackpot .case-contents__item-icon { box-shadow: none; }

/* Более мягкий, "дорогой" градиент подложки карточки — вместо плоской
   заливки цветом редкости сверху (резкий блок) поверх нее полупрозрачная
   тёмная дымка приглушает цвет и растягивает переход на три ступени, а не
   на одну границу — читается как объёмное свечение изнутри карточки, а не
   как залитый цветом прямоугольник. */
.case-contents__grid--showcase .case-contents__item {
    background:
        linear-gradient(165deg, rgba(8, 9, 14, .15) 0%, rgba(8, 9, 14, .8) 58%, #101219 100%),
        linear-gradient(165deg, var(--rarity) 0%, rgba(10, 12, 17, 0) 78%);
    border-color: rgba(255, 255, 255, .1);
}
/* Начиная с фиолетовых и у золотых — ещё и мощное синхронное сияние от
   самого контура карточки наружу (не от иконки внутри), в цвет её же
   редкости (--rarity уже задан инлайн в разметке каждой карточки). Все пять
   карточек пульсируют ОДНИМ и тем же тактом — специально без сдвига фазы
   (раньше был --glow-delay вразнобой, но по правке это читалось как
   рассинхронизированный шум, а не единый эффект; теперь один и тот же такт
   на всех, как будто витрина редких призов дышит заодно). Двойной радиальный
   градиент (тесное яркое ядро + широкий мягкий ореол) даёт объём и яркость
   вместо плоского пятна; overflow у карточки снят, иначе она обрезала бы
   собственное сияние по краю. */
.case-contents__grid--showcase .case-contents__item--glow {
    overflow: visible;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .18);
}
.case-contents__grid--showcase .case-contents__item--glow::after {
    content: '';
    position: absolute;
    inset: -30%;
    z-index: -1;
    border-radius: 26px;
    background:
        radial-gradient(closest-side, var(--rarity, #a855f7) 0%, transparent 55%),
        radial-gradient(closest-side, var(--rarity, #a855f7) 0%, transparent 82%);
    filter: blur(18px);
    opacity: .55;
    pointer-events: none;
    animation: case-content-glow-pulse 2.2s ease-in-out infinite;
}
@keyframes case-content-glow-pulse {
    0%, 100% { opacity: .42; transform: scale(.92); }
    50% { opacity: 1; transform: scale(1.12); }
}

@media (max-width: 560px) {
    .case-stage { height: 300px; }
    .case-stage__reel { height: 108px; }
    .case-stage__reel-item { width: 78px; height: 78px; margin: 0 5px; }
    .case-contents__grid { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 12px; }
}

/* =====================================================================
   ШИРОКИЕ ЭКРАНЫ (3K / 4K / 5K) — ОБЩАЯ АДАПТАЦИЯ ОСТАЛЬНЫХ СТРАНИЦ
   =====================================================================
   Блок намеренно в самом конце файла: правила ниже дополняют (а местами
   перебивают по порядку каскада) более ранние — так не нужно трогать уже
   одобренные поштучные настройки hero на главной и магазина, у них свои
   диапазоны выше по файлу.

   Что чинится:
   1) Фигуры на auth/recover/admin-login были привязаны к краю ФИЗИЧЕСКОГО
      экрана (right:5-8%, width:32-70% от вьюпорта), а текст — внутри
      центрированного контейнера (--maxw). На 5120px это разносило их на
      800-1500px ("картинка улетела на другой конец экрана"), а на
      admin-login фигура шириной 3379px вообще наезжала на форму.
      Теперь и позиция, и размер считаются ОТ КОНТЕЙНЕРА (var(--maxw)),
      поэтому работают на любой ширине сразу, без правил под каждое
      разрешение.
   2) Рулетка, кейсы, профиль и юр. страницы вообще не имели настроек под
      широкие экраны — текст оставался 10.5-15.5px на 4K/5K.
   3) Плавающим кнопкам ("Рулетка" / "Наверх") не хватало диапазона
      3700-4079px (3840x2160), и их просили поднять повыше от нижнего края.
*/

/* ---------- 1. Фигуры: привязка к контейнеру вместо края экрана ---------- */
@media (min-width: 1901px) {
    /* Правый край фигуры — на 30px правее правого края контейнера (там же,
       где заканчивается кнопка "Авторизация" в шапке), ширина — доля от
       самого контейнера, а не от вьюпорта. */
    .auth:not(.recover):not(.admin-auth) .balance__media {
        width: min(34vw, calc(var(--maxw) * 0.52));
        right: calc((100vw - var(--maxw)) / 2 - 30px);
    }
    .auth.recover .balance__media {
        width: min(30vw, calc(var(--maxw) * 0.46));
        right: calc((100vw - var(--maxw)) / 2 - 30px);
    }
    /* У admin-login кадр man.png широкий (1280x714) и слева в нём много
       прозрачного — рамка блока по задумке заходит за колонку формы, важна
       не она, а пропорция. Коэффициенты подобраны так, чтобы на 1920px
       (уже одобренная раскладка) получалось то же, что и раньше:
       66% * 1920 = 1267px ≈ 1240 * 1.02, а right:8% * 1920 = 154px ≈
       (1920-1240)/2 - 186. Дальше композиция масштабируется уже вместе с
       контейнером, а не вместе с диагональю монитора. */
    .admin-auth .balance__media {
        width: min(66vw, calc(var(--maxw) * 1.02));
        right: calc((100vw - var(--maxw)) / 2 - 186px);
    }
}

/* ---------- 2. Колонка формы на auth/recover/admin ---------- */
/* Сами поля (.field__control и т.д.) уже увеличены общими правилами
   магазина выше по файлу, а колонка под ними оставалась 480px — из-за
   этого на 5120px получались огромные поля в узкой колонке. */
@media (min-width: 2601px) {
    .auth .balance__content { max-width: 620px; }
    .auth .balance__title-graf { font-size: 0.62em; }
}
@media (min-width: 3700px) {
    .auth .balance__content { max-width: 680px; }
}
@media (min-width: 4600px) {
    .auth .balance__content { max-width: 720px; }
}

/* ---------- 3. Рулетка / кейсы / профиль / юр. страницы ---------- */
@media (min-width: 2601px) {
    /* Высота полосы задаётся явно (не auto): карточки внутри появляются
       анимацией с нулевой ширины, и на auto пустая полоса схлопывалась в
       1px, пока не приедет первая карточка. */
    .drop-feed { height: 176px; }
    .drop-feed__item { height: 128px; border-radius: 16px; gap: 10px; }
    .drop-feed__item.is-in { width: 124px; margin-right: 16px; }
    .drop-feed__item__icon { width: 44px; height: 44px; }
    .drop-feed__item__label { font-size: 15px; max-width: 110px; }

    .free-cases__subtitle { font-size: 20px; }
    .free-case-card__label { font-size: 16px; }
    .free-case-card__price { font-size: 22px; }
    .free-case-card__title { font-size: 30px; }

    .case-contents__item-name { font-size: 19px; }
    .player-hud-bar__back-text { font-size: 20px; }
    .bonus-banner__text { font-size: 21px; }
    .bonus-banner__title { font-size: 30px; }

    .legal__h { font-size: 26px; }
    .legal__meta, .legal p, .legal li { font-size: 22px; }
    .player-notfound__text { font-size: 20px; }
}
@media (min-width: 3700px) {
    .drop-feed { height: 202px; }
    .drop-feed__item { height: 150px; }
    .drop-feed__item.is-in { width: 146px; margin-right: 19px; }
    .drop-feed__item__icon { width: 52px; height: 52px; }
    .drop-feed__item__label { font-size: 18px; max-width: 130px; }

    .free-cases__subtitle { font-size: 23px; }
    .free-case-card__label { font-size: 19px; }
    .free-case-card__price { font-size: 26px; }
    .free-case-card__title { font-size: 35px; }

    .case-contents__item-name { font-size: 22px; }
    .player-hud-bar__back-text { font-size: 24px; }
    .bonus-banner__text { font-size: 25px; }
    .bonus-banner__title { font-size: 35px; }

    .legal__h { font-size: 30px; }
    .legal__meta, .legal p, .legal li { font-size: 26px; }
    .player-notfound__text { font-size: 24px; }
}
@media (min-width: 4600px) {
    .drop-feed { height: 236px; }
    .drop-feed__item { height: 178px; }
    .drop-feed__item.is-in { width: 172px; margin-right: 22px; }
    .drop-feed__item__icon { width: 62px; height: 62px; }
    .drop-feed__item__label { font-size: 21px; max-width: 152px; }

    .free-cases__subtitle { font-size: 27px; }
    .free-case-card__label { font-size: 22px; }
    .free-case-card__price { font-size: 30px; }
    .free-case-card__title { font-size: 41px; }

    .case-contents__item-name { font-size: 26px; }
    .player-hud-bar__back-text { font-size: 28px; }
    .bonus-banner__text { font-size: 29px; }
    .bonus-banner__title { font-size: 41px; }

    .legal__h { font-size: 35px; }
    .legal__meta, .legal p, .legal li { font-size: 31px; }
    .player-notfound__text { font-size: 28px; }
}

/* ---------- 4. Плавающие кнопки: недостающий диапазон + подъём выше ---------- */
/* 3700-4079px (3840x2160) раньше не попадал ни в один диапазон — кнопки
   оставались базового размера. Плюс просили поднять их повыше от нижнего
   края экрана на всех широких разрешениях. */
@media (min-width: 2601px) and (max-width: 3699px) {
    .roulette-fab { bottom: 56px; }
    .to-top { bottom: 56px; }
}
@media (min-width: 3700px) and (max-width: 4079px) {
    .roulette-fab { bottom: 64px; padding: 21px 25px; border-radius: 25px; gap: 11px; }
    .roulette-fab__ring { top: 21px; width: 65px; height: 65px; margin-left: -32px; }
    .roulette-fab__icon { width: 65px; height: 65px; }
    .roulette-fab__label { font-size: 16px; }
    .to-top { bottom: 64px; width: 70px; height: 70px; }
    .to-top svg { width: 30px; height: 30px; }
}
@media (min-width: 4080px) and (max-width: 4599px) {
    .roulette-fab { bottom: 70px; }
    .to-top { bottom: 70px; }
}
@media (min-width: 4600px) {
    .roulette-fab { bottom: 82px; }
    .to-top { bottom: 82px; }
}

/* ---------- 5. Фигура hero на главной: рядом с текстом, а не у края экрана ---------- */
/* На широких экранах right:5% (от вьюпорта) уносил фигуру к самому краю
   монитора: на 5120px между текстом и ней оставалось 749px пустоты, хотя
   на магазине уже одобрена противоположная композиция — фигура вплотную
   к колонке текста, а свободное поле остаётся справа от неё. Приводим
   hero к тому же виду. Проценты подобраны измерением под каждый диапазон
   (ширина фигуры и колонки текста в них разная), зазор до текста ~50-100px.
   Диапазоны до 2600px не трогаем — там экран узкий, фигура и так рядом. */
@media (min-width: 2601px) and (max-width: 3699px) {
    .hero__media { right: 19.3%; }
}
@media (min-width: 3700px) and (max-width: 4079px) {
    .hero__media { right: 17.4%; }
}
@media (min-width: 4080px) and (max-width: 4599px) {
    .hero__media { right: 17.6%; }
}
@media (min-width: 4600px) {
    .hero__media { right: 18.3%; }
}

/* ---------- 6. HUD-панель игрока и кнопки кейсов/рулетки ---------- */
/* Панель сверху на страницах кейсов/рулетки (кнопка "Назад", баланс,
   аватар, "Пополнить") и кнопка "Открыть" оставались базового размера —
   на 4K/5K это самый мелкий текст на странице (13-16px). */
@media (min-width: 2601px) {
    .player-hud-bar__back { font-size: 20px; }
    .player-hud-bar__back svg { width: 22px; height: 22px; }
    .player-hud__topup { font-size: 19px; padding: 14px 26px; border-radius: 12px; }
    .player-hud__balance { font-size: 21px; }
    .player-hud__balance-badge { width: 36px; height: 36px; }
    .player-hud__balance-value { font-size: 21px; }
    .player-hud__avatar { width: 54px; height: 54px; }
    .player-hud__avatar-empty { width: 48px; height: 48px; }
    .case-stage__open-btn { font-size: 21px; padding: 18px 40px; }
    .case-page__favorite { width: 62px; height: 62px; }
    .case-page__favorite svg { width: 30px; height: 30px; }
}
@media (min-width: 3700px) {
    .player-hud-bar__back { font-size: 24px; }
    .player-hud-bar__back svg { width: 26px; height: 26px; }
    .player-hud__topup { font-size: 22px; padding: 17px 31px; border-radius: 14px; }
    .player-hud__balance { font-size: 25px; }
    .player-hud__balance-badge { width: 42px; height: 42px; }
    .player-hud__balance-value { font-size: 25px; }
    .player-hud__avatar { width: 64px; height: 64px; }
    .player-hud__avatar-empty { width: 57px; height: 57px; }
    .case-stage__open-btn { font-size: 25px; padding: 21px 47px; }
    .case-page__favorite { width: 73px; height: 73px; }
    .case-page__favorite svg { width: 35px; height: 35px; }
}
@media (min-width: 4600px) {
    .player-hud-bar__back { font-size: 28px; }
    .player-hud-bar__back svg { width: 31px; height: 31px; }
    .player-hud__topup { font-size: 26px; padding: 20px 37px; border-radius: 16px; }
    .player-hud__balance { font-size: 29px; }
    .player-hud__balance-badge { width: 50px; height: 50px; }
    .player-hud__balance-value { font-size: 29px; }
    .player-hud__avatar { width: 76px; height: 76px; }
    .player-hud__avatar-empty { width: 68px; height: 68px; }
    .case-stage__open-btn { font-size: 29px; padding: 24px 55px; }
    .case-page__favorite { width: 86px; height: 86px; }
    .case-page__favorite svg { width: 41px; height: 41px; }
}

/* ---------- 7. Ярус 2K (2048x1080/1536, 2560x1080/1440/1600) ---------- */
/* Здесь экран уже заметно больше обычного десктопа, но ещё не 4K —
   увеличение умеренное (~1.25x), чтобы текст читался, но не выглядел
   раздутым. Диапазон 2001-2600px: выше начинается ярус 2601+. */
@media (min-width: 2001px) and (max-width: 2600px) {
    .drop-feed { height: 148px; }
    .drop-feed__item { height: 108px; border-radius: 14px; }
    .drop-feed__item.is-in { width: 104px; margin-right: 14px; }
    .drop-feed__item__icon { width: 38px; height: 38px; }
    .drop-feed__item__label { font-size: 13px; max-width: 94px; }

    .free-cases__subtitle { font-size: 17px; }
    .free-case-card__label { font-size: 14px; }
    .free-case-card__price { font-size: 19px; }
    .free-case-card__title { font-size: 26px; }

    .case-contents__item-name { font-size: 16px; }
    .bonus-banner__text { font-size: 18px; }
    .bonus-banner__title { font-size: 26px; }

    .legal__h { font-size: 22px; }
    .legal__meta, .legal p, .legal li { font-size: 19px; }
    .player-notfound__text { font-size: 17px; }

    .player-hud-bar__back, .player-hud-bar__back-text { font-size: 17px; }
    .player-hud-bar__back svg { width: 19px; height: 19px; }
    .player-hud__topup { font-size: 16px; padding: 12px 22px; }
    .player-hud__balance, .player-hud__balance-value { font-size: 18px; }
    .player-hud__balance-badge { width: 31px; height: 31px; }
    .player-hud__avatar { width: 46px; height: 46px; }
    .player-hud__avatar-empty { width: 41px; height: 41px; }
    .case-stage__open-btn { font-size: 18px; padding: 16px 34px; }
    .case-page__favorite { width: 54px; height: 54px; }
    .case-page__favorite svg { width: 26px; height: 26px; }

    .roulette-fab { bottom: 44px; padding: 15px 18px; border-radius: 18px; }
    .roulette-fab__ring { top: 15px; width: 46px; height: 46px; margin-left: -23px; }
    .roulette-fab__icon { width: 46px; height: 46px; }
    .roulette-fab__label { font-size: 13px; }
    .to-top { bottom: 44px; width: 52px; height: 52px; }
    .to-top svg { width: 23px; height: 23px; }

    /* Футер и водяной знак: на 2K тоже читались мелко, а надпись REGION
       была вдвое уже контейнера. Значения — от контейнера (--maxw:1240),
       поэтому пропорция та же, что и на 4K. */
    .footer__col h4 { font-size: 21px; }
    .footer__col a, .footer__brand p { font-size: 17px; }
    .footer__watermark-wrap { height: 190px; }
    .footer__watermark { font-size: 285px; letter-spacing: 9px; top: 14px; }
}

/* ---------- 8. Авторизация на 2K (2001-2600px): опустить форму и фигуру ---------- */
/* На высоких 2K-окнах (2048x1080/1536, 2560x1080/1440/1600) форма
   (короткая — 2 поля) с базовым отступом жалась под самый хедер, а под
   персонажем оставалось много пустого места снизу секции. Опускаем оба
   блока и слегка увеличиваем фигуру — распределяет пустое место сверху и
   снизу вместо того, чтобы оно всё оставалось внизу. */
@media (min-width: 2001px) and (max-width: 2600px) {
    .auth:not(.recover):not(.admin-auth) { padding-top: 190px; }
    .auth .balance__content { margin-top: 50px; }
    .auth:not(.recover):not(.admin-auth) .balance__media {
        width: min(38vw, calc(var(--maxw) * 0.58));
        top: 18%;
    }
    /* Восстановление пароля (.auth.recover) — заметно отличалось от
       "Авторизации" на этом же диапазоне: тот же короткий отступ сверху и
       та же немного увеличенная фигура ниже одобренной 1901+ раскладки
       (30vw/0.46*maxw, top:12.3%) не применялись. Тот же множитель, что и
       у обычной "Авторизации" (~1.115x), приложенный к собственным
       значениям recover, а не одинаковые с ней цифры — там и так была
       чуть более узкая фигура (одобрено ранее), это соотношение сохранено. */
    /* top фигуры — процент от высоты СЕКЦИИ (min-height:100vh), а не от
       положения заголовка, поэтому одно только уменьшение padding-top тут
       не поднимает фигуру вообще (что и произошло в прошлый раз — "как
       была на месте, так и осталась"). У самой картинки (police.png)
       прозрачного поля сверху всего ~5% высоты кадра (проверено по
       пикселям) — figuru нельзя просто поднять на глаз, не разобравшись,
       иначе кепка персонажа уедет прямо в буквы заголовка. Поэтому здесь
       двигаем оба анкера разом: чуть меньший отступ сверху (190→130px)
       И меньший top фигуры, посчитанный так, чтобы зазор между низом
       заголовка и верхом фигуры остался тем же (не отрицательным). */
    .auth.recover { padding-top: 130px; }
    /* Заголовок "ВОССТАНОВЛЕНИЕ ПАРОЛЯ" — самое длинное слово в семье этих
       страниц, на базовом клампе (до 74px) читалось непропорционально
       крупно рядом с короткой формой в 1 поле — просили уменьшить. */
    .auth.recover .balance__title { font-size: 56px; }
    .auth.recover .balance__media {
        width: min(38vw, calc(var(--maxw) * 0.58));
        top: 18%;
    }
}
/* 2048×1080 (2001-2200px) — самый невысокий экран в этом диапазоне: даже
   на текущем размере фигура упирается почти впритык в низ секции (запас
   всего ~19px), поэтому здесь её больше не растим — только диапазон
   2201-2600px (2560 и выше) ниже, где по высоте есть заметный запас. */
@media (min-width: 2201px) and (max-width: 2600px) {
    .auth.recover .balance__media {
        width: min(38.7vw, calc(var(--maxw) * 0.592));
        top: 8%;
    }
}

/* Document access and keyboard navigation share the existing blue accent. */
:is(a, button, input, select, textarea, [tabindex]):focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 4px;
}
.checkbox input:focus-visible + .checkbox__box {
    outline: 2px solid var(--accent);
    outline-offset: 4px;
}
.checkbox__label a,
.form-policy-note a,
.service-notice a {
    color: var(--accent);
    text-decoration: underline;
    text-underline-offset: .18em;
}
.checkbox__label a { font-weight: 700; }
.document-links {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 16px;
    margin: 18px 0;
    text-align: left;
}
.document-links a {
    color: var(--accent);
    font-size: 14px;
    line-height: 1.6;
    text-decoration: underline;
    text-underline-offset: .18em;
    padding: 4px 0;
}
.form-policy-note {
    color: var(--muted);
    font-size: 13px;
    line-height: 1.7;
    margin-top: 20px;
    max-width: 560px;
}
.service-notice {
    padding: 22px 26px;
    margin-bottom: 28px;
    border: 1px solid rgba(159, 199, 255, .24);
    border-radius: 16px;
    background: rgba(159, 199, 255, .05);
    color: var(--muted);
    font-size: 15px;
    line-height: 1.65;
}
.service-notice__title {
    font-size: clamp(20px, 3vw, 26px);
    color: var(--white);
    margin-bottom: 8px;
}
.service-notice a { display: inline-block; margin-top: 10px; }
@media (max-width: 560px) {
    .service-notice { padding: 18px; font-size: 14px; }
    .document-links { gap: 6px 14px; }
}
@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    html.snap-page { scroll-snap-type: none; }
    *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
        scroll-behavior: auto !important;
    }
}
