/* ======================= 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;
    --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-padding-top: 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; }
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; }

.burger { display: none; flex-direction: column; gap: 5px; background: none; border: 0; cursor: pointer; padding: 6px; }
.burger span { width: 26px; height: 2px; background: var(--white); border-radius: 2px; transition: .3s; }
.burger.is-open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.burger.is-open span:nth-child(2) { opacity: 0; }
.burger.is-open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* ======================= HERO ======================= */
.hero {
    position: relative;
    min-height: 100vh;
    scroll-snap-align: start;
    scroll-snap-stop: always;
    padding-bottom: 36px;
    background:
        /* Круговое затемнение у нижней границы секции — сглаживает переход к следующему блоку и низ фигуры врача */
        radial-gradient(85% 65% at 72% 96%, rgba(5, 6, 9, .85) 0%, rgba(5, 6, 9, 0) 62%),
        radial-gradient(120% 90% at 78% 40%, rgba(40, 55, 85, .55) 0%, rgba(8, 9, 12, 0) 55%),
        linear-gradient(180deg, #0a0c11 0%, #08090c 100%);
    overflow: hidden;
}
.hero::before {
    content: '';
    position: absolute;
    inset: 0;
    background-image: url('../photos/fon.png');
    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; 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: 6%;
    /* Широкий блок: обрезаются только прозрачные поля PNG, фигура целиком в кадре (без «рамок»).
       Блок доходит до самого низа hero (top+height=100%) — раньше он заканчивался чуть выше
       границы секции, и между растворённым краем фигуры и следующей секцией была пустая
       полоса фона, из-за которой врач выглядел "подвешенным" в воздухе. */
    width: 37%;
    /* min() вместо чистого 94% — на узких окнах текст переносится на
       больше строк, .hero (min-height:100vh) вырастает выше самого экрана,
       и 94% от этой раздутой высоты уводит картинку далеко за нижний край
       видимой области. Ограничение в vh не даёт секции "утащить" фигуру
       за собой при таком росте. */
    height: min(94%, 82vh);
    pointer-events: none;
}
/* Мягкое радиальное свечение по центру фотографии — без жёстких краёв блока */
.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;
    object-fit: cover;
    object-position: top center;
    /* drop-shadow тут раньше давал видимую прямоугольную "рамку" — object-fit:cover
       кроит картинку по границе блока, и у самого файла край не идеально
       прозрачный, так что тень обводила весь прямоугольник кадра, а не
       силуэт фигуры. Без тени рамки не остаётся. */
    /* Плавно растворяем только низ фигуры — переход к следующей секции без резкой обрезки */
    -webkit-mask-image: linear-gradient(to bottom, black 0%, black 58%, transparent 99%);
    mask-image: linear-gradient(to bottom, black 0%, black 58%, transparent 99%);
}

/* ======================= 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.png');
    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.png (вкладка-"папка"),
   наложенная как 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.png');
    mask-image: url('../photos/papka.png');
    -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.png');
    mask-image: url('../photos/papka.png');
    -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); }

.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.png');
    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; }

/* Курс обмена рубля на внутриигровую валюту + сама форма обмена —
   компактная карточка над формой пополнения (та же ширина колонки,
   .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); }
/* Курс внутри самой игры — по нему же ниже работает обмен RC (см.
   комментарий в разметке, shop.html), поэтому не прячем его тусклой мелкой
   сноской, а выделяем не менее заметно, чем курс пополнения выше */
.currency-rate__lore { font-size: 14px; font-weight: 800; color: var(--accent); }

/* Баланс 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; }
}

.currency-rate__field { margin-bottom: 14px; }
.currency-rate__btn { width: 100%; }
/* Заголовок вынесен из левой колонки и смещён к центру над фигурой девушки —
   не так далеко вправо, как раньше (иначе слово полностью наезжало на лицо) */
.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;
}
.balance__char {
    width: 100%;
    height: 100%;
    max-width: none;
    /* object-fit:cover обрезает картинку под размер блока (это буквально его
       назначение — залить контейнер, обрезав всё лишнее). Из-за этого низ
       фигуры "срезало" по границе блока вместо плавного схождения на нет.
       contain вписывает изображение целиком, без обрезки — видно всю
       фигуру, а не только верхнюю часть. */
    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%);
    mask-image: linear-gradient(to bottom, black 0%, black 55%, transparent 96%);
}

/* ======================= ПЛАВАЮЩАЯ КНОПКА "РУЛЕТКА" ======================= */
/* Зафиксирована в правом нижнем углу ЭКРАНА (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; }

.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);
}

.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; }

/* Кто/когда/на сколько последний раз поправил срок — отдельным блоком по
   левому краю снизу (не в сетке "подпись/значение" выше: текст тут длиннее
   одной строки и с ней смотрелся бы тесно). Видно, только если наказание
   уже хоть раз меняли после выдачи, см. 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; }
.profile__badge--vip.is-vip {
    color: #3a2a06;
    background: linear-gradient(135deg, #ffe29a, #ffb84d);
}
.profile__badge--vip.is-premium {
    color: #2a0f3a;
    background: linear-gradient(135deg, #f0abfc, #c084fc 55%, #818cf8);
}

/* Строка "VIP-статус" в общей статистике — тот же цвет, что и у значка,
   чтобы привилегия бросалась в глаза, а не терялась среди обычных "Нет" */
.profile__stat-value--vip { color: #ffb84d; }
.profile__stat-value--premium {
    background: linear-gradient(135deg, #f0abfc, #c084fc 55%, #818cf8);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

/* ======================= ЦВЕТА РАНГОВ АДМИНИСТРАЦИИ ======================= */
/* Класс ранга ("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; }
/* Из какого кейса выпал приз — маленькая иконка-ссылка, ведёт прямо на
   страницу этого кейса (см. 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); }

/* Заметка о неизменности ранга основателя — используется в меню "Действия
   с этим профилем" (см. .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); }

/* 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; }
.auth .balance__title-graf {
    position: static;
    display: inline-block;
    margin-left: .25em;
    transform: rotate(-2deg);
}
/* Свечение (.balance__media::before) по умолчанию центровано по всей
   непрозрачной области фигуры — для woman.png (магазин) это совпадает с
   торсом, но у police.png в руке планшет, вытянутый далеко вправо, и он же
   раздвигает эту область, утягивая геометрический центр вправо от
   реального корпуса/спины. Поэтому здесь свечение возвращаем ближе к его
   плечам и спине, а не к общему "bounding box" вместе с планшетом */
.auth .balance__media::before { left: 39%; }
/* Из-за длинной однострочной надписи фигуре нужно быть компактнее и левее,
   чем в шаблоне — иначе строка при обычном размере просто не помещается,
   упираясь в фуражку. Правило вне медиа-запросов, но с min-width — не
   должно перекрывать мобильную раскладку (там своя, position:relative) */
@media (min-width: 901px) {
    .auth .balance__media { width: 34%; right: 12%; }
    /* На авторизации формы всего два коротких поля — при полных 100vh под
       фигурой/формой остаётся много пустого места до футера. Немного
       уменьшаем высоту секции, чтобы футер стоял повыше, ближе к фото */
    .auth { min-height: 82vh; }
}
/* Шире 1900px позицией фигуры на .balance уже не управляет JS
   (initBalanceMediaAlign сознательно не трогает эту зону — там у страницы
   магазина "как есть"-режим). Фигура там просто сидела по базовому
   top:1% (у самого верха секции), и с длинным заголовком
   "ВОССТАНОВЛЕНИЕ ПАРОЛЯ" текст залезал на неё. Отдельный узкий диапазон —
   специально НЕ внутри min-width:901px блока выше, чтобы никак не
   пересекаться с диапазоном 901–1900px, которым управляет скрипт (там
   любое значение width/top всё равно перебивается инлайн-стилем, но так
   правки гарантированно изолированы и предсказуемы) */
@media (min-width: 1901px) {
    .auth .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 {
        width: 66%;
        right: 0%;
        /* Голова почти вплотную под словом "АДМИНИСТРАТОРА" (у неё
           собственный отступ внутри кадра до макушки — см. firstOpaqueRow
           в анализе прозрачности, ~5.6% высоты кадра, учтено в формуле).
           Фиксированный top в px не годится: заголовок сдвигается и от
           ширины (font-size: clamp(...,5.2vw,...)), и от высоты экрана
           (.auth{min-height:82vh} двигает вертикальный центр секции) —
           калибровано по двум контрольным точкам (950×900 и 1050×768). */
        top: calc(38.9vh - 1.9vw - 97px);
        transform: none;
    }
}
@media (min-width: 1360px) {
    /* Тот же приём, что и в диапазоне 901–1360 (см. выше) — top как функция
       от vh, откалибровано отдельно под width:68% (влияние ширины здесь
       практически не заметно, в отличие от высоты, поэтому без vw-члена) */
    .admin-auth .balance__media { width: 68%; right: 0; top: calc(40.5vh - 114px); }
}
@media (min-width: 1901px) {
    .admin-auth .balance__media { width: 58%; right: 2%; top: 135px; }
}
/* Свечение по центру фигуры (за спиной/торсом), а не смещено к плечу, как
   у 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: 52%;
        right: 3%;
        /* 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: 48%; right: 4%; }
}

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

/* ======================= КАТАЛОГ МАГАЗИНА ======================= */
/* Тоже снап-точка (как .balance) — пользователь должен долистать ровно до
   каталога тем же "постраничным" скроллом, а не проскакивать сразу к футеру.
   scroll-snap-type:none на мобильном (см. html.snap-page ниже) делает эти
   свойства там не действующими, так что мобильную раскладку они не трогают. */
.catalog { position: relative; scroll-snap-align: start; scroll-snap-stop: always; }
.catalog__grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 22px;
}
.shop-card {
    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 {
    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 { 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;
    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__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%; }

/* ======================= МАГАЗИН: VIP-ПРИВИЛЕГИИ ======================= */
/* Между "Пополнением баланса" и каталогом — тоже отдельная снап-страница
   (см. html.snap-page выше), тот же язык карточек, что и .shop-card
   ("эффект магнита" — лёгкий подъём и тень при наведении), только крупнее
   и с двумя явно различающимися уровнями: золото для VIP, фиолетово-
   розовый градиент для VIP Premium (та же пара цветов, что и у бейджей
   привилегий в профиле — .profile__badge--vip в этом же файле). */
.vip-plans {
    position: relative;
    scroll-snap-align: start;
    scroll-snap-stop: always;
    min-height: 100vh;
    display: flex;
    align-items: center;
    background: linear-gradient(180deg, #08090c 0%, #0a0c11 100%);
}
.vip-plans .container { width: 100%; }
.vip-plans__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 28px; align-items: stretch; }

.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(255, 184, 77, .3); }
.vip-plan--vip:hover { border-color: rgba(255, 184, 77, .55); }
.vip-plan--premium {
    border-color: rgba(192, 132, 252, .35);
    background: linear-gradient(225deg, rgba(192, 132, 252, .14) 0%, rgba(255, 255, 255, .015) 32%, rgba(6, 7, 10, .6) 100%);
}
.vip-plan--premium:hover { border-color: rgba(192, 132, 252, .65); box-shadow: 0 34px 70px -26px rgba(129, 66, 189, .5); }

.vip-plan__ribbon {
    position: absolute;
    top: -14px;
    right: 28px;
    background: linear-gradient(135deg, #f0abfc, #c084fc 55%, #818cf8);
    color: #1a0b2e;
    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(129, 66, 189, .7);
}

.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: linear-gradient(135deg, #ffe29a, #ffb84d); color: #3a2a06; }
.vip-plan--premium .vip-plan__icon { background: linear-gradient(135deg, #f0abfc, #c084fc 55%, #818cf8); color: #2a0f3a; }
.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: 26px; flex: 1 1 auto; }
.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(255, 184, 77, .18); color: #ffb84d; }
.vip-plan--premium .vip-plan__perks li::before { background: rgba(192, 132, 252, .2); color: #c084fc; }

.vip-plan__periods { display: flex; gap: 8px; margin-bottom: 20px; }
.vip-plan__period {
    flex: 1 1 0;
    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--vip .vip-plan__period.is-active { background: rgba(255, 184, 77, .16); border-color: rgba(255, 184, 77, .5); color: #ffb84d; }
.vip-plan--premium .vip-plan__period.is-active { background: rgba(192, 132, 252, .18); border-color: rgba(192, 132, 252, .55); color: #c084fc; }

.vip-plan__buy { width: 100%; margin-top: auto; border: none; }
.vip-plan--vip .vip-plan__buy { background: linear-gradient(135deg, #ffe29a, #ffb84d); color: #3a2a06; box-shadow: 0 14px 34px -14px rgba(255, 184, 77, .55); }
.vip-plan--vip .vip-plan__buy:hover { box-shadow: 0 18px 42px -14px rgba(255, 184, 77, .75); }
.vip-plan--premium .vip-plan__buy { background: linear-gradient(135deg, #f0abfc, #c084fc 55%, #818cf8); color: #1a0b2e; box-shadow: 0 14px 34px -14px rgba(192, 132, 252, .55); }
.vip-plan--premium .vip-plan__buy:hover { box-shadow: 0 18px 42px -14px rgba(192, 132, 252, .75); }
.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%; }

@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); }

.footer__watermark-wrap {
    position: relative;
    height: clamp(100px, 15vw, 210px);
    overflow: hidden;
    pointer-events: none;
}
.footer__watermark {
    position: absolute;
    left: 0; right: 0; top: 0;
    z-index: 2;
    text-align: center;
    font-size: clamp(90px, 20vw, 300px);
    font-weight: 900;
    letter-spacing: 8px;
    line-height: 1;
    color: rgba(255, 255, 255, 0.07);
    user-select: 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);
}

/* ======================= 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%; }

    .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 (он рассчитан на узкий десктоп,
       где заголовок не центрируют над фото) и заголовок съезжает влево */
    .balance__title { margin-left: 0; text-align: center; }
    .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; }
    .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; }
    .footer__watermark-wrap { height: 70px; }
}

/* ======================= ТОСТ «В РАЗРАБОТКЕ» ======================= */
.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; }
}

/* ======================= ЛАЙТБОКС (ПРОСМОТР ФОТО) ======================= */
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; }
}

/* ======================= ПРАВОВЫЕ СТРАНИЦЫ (соглашение, политика и т.д.) ======================= */
.legal { padding: 64px 0 100px; }
.legal__title {
    text-align: center;
    font-style: italic;
    font-weight: 800;
    color: var(--accent);
    font-size: clamp(24px, 3vw, 36px);
    letter-spacing: -.2px;
    margin-bottom: 44px;
}
.legal__body { max-width: 980px; margin: 0 auto; color: #c7cad3; font-size: 15.5px; line-height: 1.65; }
.legal__body p { margin-bottom: 20px; }
.legal__h { color: var(--white); margin-top: 8px; }
.legal__items { margin: -10px 0 20px 22px; }
.legal__items p { margin-bottom: 6px; }
.legal__meta { color: var(--white); }
.legal__meta:last-child { margin-bottom: 0; }
.legal__date { margin-top: 24px; }

/* ======================= ЛЕНТА ВЫПАДЕНИЙ (roulette.html) ======================= */
/* Заменяет обычный .header только на этой странице (см. комментарий в
   roulette.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; }
}

/* ======================= РУЛЕТКА (roulette.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.jpg, граффити-стена) — на этой
   странице и на 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.jpg');
    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; }

/* ======================= МИНИ-ПРОФИЛЬ (roulette.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; }
}

/* ======================= БОНУС-БАННЕР (roulette.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; }
}

/* ======================= БЕСПЛАТНЫЕ КЕЙСЫ (roulette.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.jpg), а не нейтрально-голубое.
   Крупнее самой картинки (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;
    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__reel-item span { font-size: 11px; font-weight: 800; color: #fff; }
.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; }
.case-stage__reveal-label { position: relative; font-size: 20px; font-weight: 900; color: #fff; text-transform: uppercase; letter-spacing: .4px; white-space: nowrap; }

/* ==== «Крути-гуляй» (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; }
}
