/* ─── FONTS (self-hosted, CSP blocks external CDN) ─── */
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url('/fonts/inter-cyrillic.woff2') format('woff2');
    unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url('/fonts/inter-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Orbitron';
    font-style: normal;
    font-weight: 400 900;
    font-display: swap;
    src: url('/fonts/orbitron.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/*
    ПАЛИТРА.

    Antares — красный сверхгигант, и оранжевый акцент это опознавательный знак проекта.
    Он сохранён, но выправлен: #FF5F1F на тёмном давал резкую, чуть «дорожно-знаковую» ноту,
    #FF6B2C светлее и теплее, читается спокойнее и лучше держит контраст на мелком тексте.

    Фон сдвинут из нейтрально-серого в сторону ночного неба: у всех тёмных тонов появился
    едва заметный сине-фиолетовый подтон. Разница в отдельном цвете почти незаметна, но
    вместе они перестают выглядеть «выключенным монитором» и дают ту самую атмосферу,
    которой не хватало. Тема остаётся тёмной и узнаваемой.

    Главная содержательная правка — лестница поверхностей. Раньше фонов было три и они
    почти не отличались, поэтому карточка, меню и страница читались как одна плоскость:
    отсюда ощущение пустоты. Теперь пять уровней с ощутимым шагом плюс тени и подсветка
    верхней кромки, так что глубина видна без единой лишней рамки.
*/
:root {
    /* ── Поверхности, от самой глубокой к самой верхней ── */
    --bg-void: #07080E;        /* за пределами контента: подложка страницы */
    --bg-dark: #0B0D14;        /* основной фон */
    --bg-primary: #10131C;     /* поля ввода, «утопленные» участки */
    --bg-secondary: #161A26;   /* карточки, панели */
    --bg-tertiary: #1E2332;    /* поднятое: меню, поповеры, активные строки */
    --bg-overlay: #262C3D;     /* поверх поднятого: тултипы, подсказки */

    /* ── Акцент ── */
    --accent: #FF6B2C;
    --accent-strong: #FF8C55;  /* наведение и подсветка */
    --accent-deep: #DB4A12;    /* нажатие, нижняя точка градиента */
    --accent-dim: rgba(255, 107, 44, 0.14);
    --accent-glow: rgba(255, 107, 44, 0.32);

    /* Второй и третий акценты нужны градиентам и состояниям, где оранжевый уже занят. */
    --accent-sec: #4DA3FF;
    --accent-violet: #8B6DFF;

    /* ── Смысловые цвета ── */
    --red: #FF5C5C;
    --green: #3DDC84;
    --gold: #FFC542;

    /* ── Текст ── */
    --text-primary: #EEF1F7;
    /* Было #8B919A — на карточке это 5.47:1, впритык к норме AA для мелкого текста.
       #9BA3B6 даёт 6.87:1 и при этом остаётся заметно тише основного (у того 15.34:1).
       Значения измерены в браузере на фактическом фоне, а не прикинуты. */
    --text-secondary: #9BA3B6;
    --text-muted: #6C7488;     /* для того, что должно уходить на задний план целиком */

    /* ── Границы ── */
    --border: #232939;
    --border-strong: #2F3648;
    --border-accent: rgba(255, 107, 44, 0.28);

    /* ── Градиенты ── */
    --grad-accent: linear-gradient(135deg, #FF8C55 0%, #FF6B2C 48%, #E8501C 100%);
    --grad-cosmic: linear-gradient(135deg, #8B6DFF 0%, #FF6B2C 60%, #FFC542 100%);
    /* Подсветка верхней кромки: имитирует свет сверху и отделяет карточку от фона
       мягче, чем ещё одна рамка. */
    --grad-surface: linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0) 42%);
    --grad-glow: radial-gradient(circle at 50% 0%, var(--accent-dim), transparent 70%);

    /* ── Тени: одна лестница на всё приложение ── */
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
    --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.42);
    --shadow-lg: 0 14px 38px rgba(0, 0, 0, 0.55);
    --shadow-accent: 0 6px 22px rgba(255, 107, 44, 0.22);

    /* ── Скругления ── */
    --radius-sm: 8px;
    --radius-md: 12px;
    --radius-lg: 16px;
    --radius-xl: 18px;
    --radius-pill: 999px;

    /* ── Движение. Одна кривая и три длительности на всё, иначе интерфейс
           начинает двигаться вразнобой. ── */
    --ease: cubic-bezier(0.4, 0, 0.2, 1);
    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
    --dur-fast: 0.14s;
    --dur: 0.22s;
    --dur-slow: 0.38s;

    --font-main: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    --font-display: 'Orbitron', 'Inter', -apple-system, 'Segoe UI', sans-serif;
    --font-mono: 'Cascadia Code', 'Fira Code', monospace;
}

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

html, body {
    font-family: var(--font-main);
    font-size: 16px;
    /* Два едва различимых пятна света сверху — именно они убирают ощущение «чёрного листа».
       Свечение вписано в фон, а не в псевдоэлемент: тот пришлось бы позиционировать, и он
       рисовался бы поверх контента. background-attachment держит пятна на месте при прокрутке. */
    background:
        radial-gradient(760px 420px at 18% -8%, rgba(255, 107, 44, 0.06), transparent 60%),
        radial-gradient(680px 400px at 88% -12%, rgba(139, 109, 255, 0.05), transparent 62%),
        var(--bg-dark);
    background-attachment: fixed;
    color: var(--text-primary);
    min-height: 100vh;
    line-height: 1.5;
    /* Сглаживание шрифта на тёмном фоне: без него Inter выглядит жирнее, чем задуман. */
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

a {
    color: var(--accent);
    text-decoration: none;
    transition: color var(--dur) var(--ease), opacity var(--dur) var(--ease);
}

a:hover {
    opacity: 0.85;
}

/* Видимый фокус с клавиатуры — по всему приложению, одним правилом. */
:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

::selection {
    background: var(--accent-dim);
    color: var(--text-primary);
}

.app-layout {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

.navbar {
    position: sticky;
    top: 0;
    z-index: 100;
    display: flex;
    justify-content: center;
    height: 56px;
    background: rgba(11, 12, 16, 0.85);
    backdrop-filter: blur(12px);
    border-bottom: 1px solid var(--border);
}

/* Inner column matches .main-content width so the logo and actions line up with page
   content (VK-style "everything inside the body") instead of hugging the screen edges. */
.navbar-inner {
    width: 100%;
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 24px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

.navbar-brand .brand-link {
    font-family: var(--font-display);
    font-size: 22px;
    font-weight: 800;
    letter-spacing: 3px;
    text-transform: uppercase;
    background: linear-gradient(120deg, var(--accent) 0%, #ff8a4c 60%, var(--gold) 120%);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    transition: filter 0.2s;
}

.navbar-brand .brand-link:hover {
    opacity: 1;
    filter: drop-shadow(0 0 8px var(--accent-glow));
}

.navbar-links {
    display: flex;
    gap: 8px;
    /* min-width: 0 обязателен: у flex-элемента по умолчанию min-width: auto, поэтому ряд ссылок
       не сжимается ниже своей длины и выталкивает кнопки выхода за край окна. На 1024px семи
       пунктов уже не хватает места (нужно 1029px при 1014 доступных), и появлялась
       горизонтальная прокрутка всей страницы. */
    min-width: 0;
    /* Ссылки прокручиваются внутри своей полосы, а не ломают страницу. */
    overflow-x: auto;
    scrollbar-width: none;
}

.navbar-links::-webkit-scrollbar {
    display: none;
}

.navbar-links .nav-link {
    /* Иначе названия переносятся по словам и ряд становится двухэтажным. */
    white-space: nowrap;
}

.navbar-actions {
    display: flex;
    gap: 8px;
    align-items: center;
}

.nav-link {
    padding: 8px 14px;
    border-radius: var(--radius-sm);
    color: var(--text-primary);
    font-size: 14px;
    font-weight: 500;
    transition: background 0.2s, color 0.2s, transform 0.15s;
}

.nav-link:hover {
    background: var(--bg-tertiary);
    opacity: 1;
    transform: translateY(-1px);
}

.nav-link-accent {
    background: var(--accent);
    color: white;
}

.nav-link-accent:hover {
    background: #e5541a;
    color: white;
}

/* Profile chip: round avatar initial + name */
.nav-profile-link {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 5px 12px 5px 5px;
}

.nav-profile-avatar {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--accent), #ff8a4c);
    color: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    font-weight: 700;
    flex-shrink: 0;
}

/* Icon-only action button (logout) */
.nav-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border-radius: 50%;
    color: var(--text-secondary);
    transition: background 0.2s, color 0.2s;
}

.nav-icon-btn:hover {
    background: rgba(224, 86, 79, 0.14);
    color: var(--accent);
    opacity: 1;
}

@media (max-width: 640px) {
    .nav-profile-name { display: none; }
}


.main-content {
    flex: 1;
    padding: 24px;
    max-width: 1200px;
    margin: 0 auto;
    width: 100%;
}

.auth-page {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-dark);
}

.auth-container {
    width: 100%;
    max-width: 400px;
    padding: 32px;
    background: var(--bg-secondary);
    border-radius: var(--radius-lg);
    border: 1px solid var(--border);
}

.auth-title {
    font-size: 24px;
    font-weight: 700;
    margin-bottom: 24px;
    text-align: center;
    color: var(--accent);
}

.auth-error {
    padding: 12px;
    margin-bottom: 16px;
    background: rgba(224, 64, 64, 0.15);
    border: 1px solid var(--red);
    border-radius: var(--radius-sm);
    color: var(--red);
    font-size: 14px;
}

.auth-links {
    margin-top: 16px;
    display: flex;
    justify-content: space-between;
    font-size: 14px;
}

.form-group {
    margin-bottom: 16px;
}

.form-group label {
    display: block;
    margin-bottom: 6px;
    font-size: 14px;
    color: var(--text-secondary);
}

.form-input {
    width: 100%;
    padding: 10px 14px;
    background: var(--bg-dark);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text-primary);
    font-size: 14px;
    font-family: var(--font-main);
    outline: none;
    transition: border-color 0.2s;
}

.form-input:focus {
    border-color: var(--accent);
}

.password-field {
    position: relative;
}

.password-field .form-input {
    padding-right: 44px;
}

.password-toggle {
    position: absolute;
    top: 0;
    bottom: 0;
    right: 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    padding: 0;
    border: none;
    background: none;
    color: var(--text-secondary);
    cursor: pointer;
    transition: color 0.2s;
}

.password-toggle:hover,
.password-toggle:focus-visible {
    color: var(--accent);
}

.password-toggle .ic-eye-off,
.password-toggle.is-shown .ic-eye {
    display: none;
}

.password-toggle.is-shown .ic-eye-off {
    display: block;
}

.checkbox-label {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    cursor: pointer;
}

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 10px 20px;
    border: none;
    border-radius: var(--radius-sm);
    font-size: 14px;
    font-weight: 600;
    font-family: var(--font-main);
    cursor: pointer;
    transition: background 0.2s, transform 0.1s;
}

.btn:active {
    transform: scale(0.98);
}

.btn-accent {
    background: var(--accent);
    color: white;
}

/* Ширина во всю строку — теперь ЯВНАЯ, отдельным классом.

   Раньше width: 100% стояло прямо на .btn-accent — наследие форм входа, где кнопка и правда
   занимает строку целиком. Но акцентных кнопок на сайте полсотни, и большинство из них
   стоят в ряду с другими: там это правило раздувало их на всю доступную ширину. Оно ломало
   разметку трижды — в строке ввода канала, в приглашении «позвать всех» и в онбординге,
   где кнопка «Подписаться» занимала всю карточку. Каждый раз это чинили заплаткой
   width: auto на месте, то есть боролись со следствием.

   Теперь по умолчанию кнопка занимает столько, сколько ей нужно, а .btn-block ставится там,
   где ширина нужна намеренно. */
.btn-block {
    width: 100%;
}

.btn-accent:hover {
    background: #e5541a;
}

.btn-secondary {
    background: var(--bg-tertiary);
    color: var(--text-primary);
}

.btn-secondary:hover {
    background: var(--border);
}

.not-found-page {
    text-align: center;
    padding: 80px 20px;
}

.not-found-page h1 {
    font-size: 80px;
    font-weight: 800;
    color: var(--accent);
    margin-bottom: 16px;
}

.not-found-page p {
    font-size: 18px;
    color: var(--text-secondary);
    margin-bottom: 24px;
}

.home-hero {
    position: relative;
    text-align: center;
    /* Top padding leaves room for the supergiant that sits above the wordmark. */
    padding: clamp(150px, 24vw, 250px) 20px clamp(48px, 8vw, 90px);
    overflow: hidden;
    /* The aurora deliberately bleeds past the edges; clip it here so it can never widen the page. */
    max-width: 100%;
}

/* Nothing on the landing page may introduce a horizontal scrollbar. */
.home { overflow-x: clip; }

.home-hero::before {
    content: "";
    position: absolute;
    top: 30%;
    left: 50%;
    width: 520px;
    height: 520px;
    max-width: 90vw;
    transform: translate(-50%, -50%);
    background: radial-gradient(circle, var(--accent-glow) 0%, transparent 70%);
    opacity: 0.5;
    pointer-events: none;
    z-index: 0;
}

.hero-title {
    position: relative;
    z-index: 1;
    font-family: var(--font-display);
    /* Scales with the viewport all the way down: a fixed 40px floor plus letter-spacing made the
       wordmark wider than a ~320px screen and pushed the whole page into horizontal scroll. */
    font-size: clamp(26px, 10vw, 76px);
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: clamp(1px, 1vw, 8px);
    max-width: 100%;
    /* Never break the wordmark across lines — it split into "ANTARE / S". It scales down instead. */
    white-space: nowrap;
    background: linear-gradient(120deg, var(--accent) 0%, #ff8a4c 55%, var(--gold) 115%);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    filter: drop-shadow(0 0 24px var(--accent-glow));
    margin-bottom: 18px;
}

.hero-subtitle {
    position: relative;
    z-index: 1;
    font-size: clamp(16px, 2.5vw, 20px);
    color: var(--text-secondary);
    letter-spacing: 0.5px;
}

.home-features {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 20px;
    max-width: 960px;
    margin: 0 auto 80px;
    padding: 0 20px;
}

.feature-card {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 28px 24px;
    text-align: center;
    transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}

.feature-card:hover {
    transform: translateY(-4px);
    border-color: var(--border-accent);
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35);
}

/* ─── Landing: starfield + Antares supergiant (CSS only, no JS → CSP-safe) ─── */
.home-hero-content { position: relative; z-index: 2; }

/* ─── Scroll reveal ─────────────────────────────────────────────────────────
   The hidden state is scoped to .reveal-ready, which JS adds only once the
   observer is wired up. Without that guard a blocked/stale script would leave
   the whole page permanently invisible. --d is an optional per-item delay. */
.reveal-ready .reveal {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity 0.7s cubic-bezier(0.22, 1, 0.36, 1) var(--d, 0s),
                transform 0.7s cubic-bezier(0.22, 1, 0.36, 1) var(--d, 0s);
    will-change: opacity, transform;
}
.reveal-ready .reveal.in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
    .reveal-ready .reveal { opacity: 1; transform: none; transition: none; }
}

/* Slow drifting colour wash behind the hero. */
.aurora {
    position: absolute;
    inset: -20% -10% auto -10%;
    height: 120%;
    z-index: 0;
    pointer-events: none;
    background:
        radial-gradient(40% 55% at 22% 30%, rgba(255, 95, 31, 0.16), transparent 70%),
        radial-gradient(35% 50% at 78% 20%, rgba(31, 156, 255, 0.12), transparent 70%),
        radial-gradient(45% 60% at 50% 80%, rgba(255, 215, 0, 0.08), transparent 70%);
    filter: blur(10px);
    animation: auroraDrift 26s ease-in-out infinite alternate;
}
@keyframes auroraDrift {
    from { transform: translate3d(-2%, 0, 0) scale(1); }
    to   { transform: translate3d(2%, -3%, 0) scale(1.08); }
}

.hero-tagline {
    position: relative;
    z-index: 2;
    margin-top: 10px;
    font-size: clamp(14px, 2vw, 16px);
    color: var(--text-secondary);
    opacity: 0.75;
}

/* Quiet "there's more below" hint — a soft vertical fade line. */
.hero-scroll {
    display: block;
    width: 1px;
    height: 54px;
    margin: 40px auto 0;
    background: linear-gradient(to bottom, transparent, var(--accent), transparent);
    opacity: 0.5;
    animation: scrollHint 2.8s ease-in-out infinite;
}
@keyframes scrollHint {
    0%, 100% { transform: translateY(0); opacity: 0.25; }
    50%      { transform: translateY(8px); opacity: 0.6; }
}

@media (prefers-reduced-motion: reduce) {
    .aurora, .hero-scroll { animation: none; }
}

.starfield { position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }

/* Each layer is one element painting many stars via repeating radial-gradients, drifting at its
   own speed for a cheap parallax. */
.star-layer {
    position: absolute;
    inset: -100% 0 0 0;
    height: 200%;
    background-repeat: repeat;
    opacity: 0.5;
    animation: starDrift linear infinite;
}
.star-layer-1 {
    background-image: radial-gradient(1px 1px at 20% 30%, #fff, transparent),
                      radial-gradient(1px 1px at 70% 60%, #fff, transparent),
                      radial-gradient(1px 1px at 40% 80%, #cfe3ff, transparent);
    background-size: 220px 220px;
    animation-duration: 90s;
}
.star-layer-2 {
    background-image: radial-gradient(1.5px 1.5px at 55% 25%, #ffe9d6, transparent),
                      radial-gradient(1.5px 1.5px at 15% 70%, #fff, transparent);
    background-size: 340px 340px;
    opacity: 0.35;
    animation-duration: 140s;
}
.star-layer-3 {
    background-image: radial-gradient(2px 2px at 80% 15%, var(--gold), transparent),
                      radial-gradient(2px 2px at 30% 50%, #ffb38a, transparent);
    background-size: 480px 480px;
    opacity: 0.25;
    animation-duration: 200s;
}
@keyframes starDrift {
    from { transform: translateY(0); }
    to   { transform: translateY(50%); }
}

/* Sits ABOVE the wordmark, not behind it — centred on the title the orange disc swallowed the
   gradient text and made "ANTARES" unreadable. */
.supergiant {
    position: absolute;
    top: clamp(28px, 6vw, 56px);
    left: 50%;
    transform: translateX(-50%);
    width: clamp(96px, 16vw, 168px);
    height: clamp(96px, 16vw, 168px);
    z-index: 1;
    pointer-events: none;
}
.supergiant span { position: absolute; inset: 0; border-radius: 50%; }
.supergiant-core {
    background: radial-gradient(circle at 38% 35%, #fff3e2 0%, #ff8a4c 40%, var(--accent) 70%, #a52d00 100%);
    box-shadow: 0 0 60px 12px var(--accent-glow);
    animation: supergiantPulse 6s ease-in-out infinite;
}
.supergiant-glow {
    background: radial-gradient(circle, var(--accent-glow) 0%, transparent 65%);
    transform: scale(1.9);
    opacity: 0.55;
    animation: supergiantPulse 6s ease-in-out infinite reverse;
}
.supergiant-ring {
    border: 1px solid rgba(255, 175, 120, 0.25);
    transform: scale(1.5) rotate(0deg);
    animation: ringSpin 40s linear infinite;
}
@keyframes supergiantPulse {
    0%, 100% { transform: scale(1); opacity: 1; }
    50%      { transform: scale(1.06); opacity: 0.9; }
}
@keyframes ringSpin {
    from { transform: scale(1.5) rotate(0deg); }
    to   { transform: scale(1.5) rotate(360deg); }
}

/* Respect users who ask the OS to cut motion. */
@media (prefers-reduced-motion: reduce) {
    .star-layer, .supergiant-core, .supergiant-glow, .supergiant-ring { animation: none; }
}

.feature-icon {
    width: 48px;
    height: 48px;
    margin: 0 auto 14px;
    border-radius: 14px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--accent);
    background: rgba(255, 95, 31, 0.12);
    border: 1px solid rgba(255, 95, 31, 0.25);
}

/* (.feature-link and .hero-actions were dropped along with the landing page's buttons.) */

.home-ranks { max-width: 960px; margin: 0 auto 90px; padding: 0 20px; text-align: center; }
.home-section-title { font-size: 22px; font-weight: 700; margin-bottom: 6px; }
.home-section-sub { color: var(--text-secondary); font-size: 14px; margin-bottom: 20px; }
.rank-track { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
.rank-chip {
    font-size: 13px;
    font-weight: 600;
    padding: 6px 14px;
    border-radius: 999px;
    border: 1px solid currentColor;
    background: rgba(255, 255, 255, 0.04);
}

.feature-card-title {
    font-size: 18px;
    font-weight: 700;
    margin-bottom: 8px;
}

.feature-card-content {
    font-size: 14px;
    color: var(--text-secondary);
    line-height: 1.6;
}

/* ─── UNIFIED PAGE HEADINGS ─── */
.feed-container > h1,
.friends-container > h1,
.admin-container > h1,
.profile-edit-container > h1 {
    display: flex;
    align-items: center;
    gap: 12px;
    font-weight: 700;
    letter-spacing: 0.4px;
    margin-bottom: 20px;
}

.feed-container > h1::before,
.friends-container > h1::before,
.admin-container > h1::before,
.profile-edit-container > h1::before {
    content: "";
    width: 4px;
    height: 1em;
    border-radius: 2px;
    background: linear-gradient(var(--accent), var(--gold));
    flex: 0 0 auto;
}

.admin-container {
    max-width: 760px;
    margin: 0 auto;
    padding: 40px 20px 24px;
}

.admin-container h1 {
    font-size: 26px;
    font-weight: 700;
    margin-bottom: 4px;
}

.admin-menu {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-top: 24px;
}

.admin-menu-item {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 20px 24px;
    text-decoration: none;
    color: inherit;
    transition: border-color 0.2s, transform 0.15s, background 0.2s;
}

.admin-menu-item:hover {
    opacity: 1;
    border-color: var(--border-accent);
    background: var(--bg-tertiary);
    transform: translateY(-2px);
}

.admin-menu-item h3 {
    color: var(--accent);
    margin-bottom: 4px;
}

.admin-menu-item p {
    color: var(--text-secondary);
    font-size: 14px;
}

.admin-back-link {
    display: inline-block;
    color: var(--text-secondary);
    margin: 12px 0 28px;
    text-decoration: none;
    font-size: 14px;
    transition: color 0.2s;
}

.admin-back-link:hover {
    color: var(--accent);
    opacity: 1;
}

/* ─── ADMIN DASHBOARD ─── */
.dash-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 14px;
    margin: 20px 0;
}

.dash-tile {
    display: flex;
    flex-direction: column;
    gap: 4px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 18px 20px;
    transition: border-color 0.2s, transform 0.15s;
}

.dash-tile:hover {
    border-color: var(--border-accent);
    transform: translateY(-2px);
}

.dash-tile-value {
    font-family: var(--font-display);
    font-size: 30px;
    font-weight: 700;
    color: var(--text-primary);
    line-height: 1.1;
}

.dash-tile-label {
    font-size: 13px;
    color: var(--text-secondary);
}

.dash-tile-warn .dash-tile-value {
    color: var(--red);
}

.dash-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 16px;
    margin-bottom: 28px;
}

.dash-card {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 18px 20px;
    /* Keep the chart's SVG (axis labels, series line) inside the card — it used to spill over
       and print the title/labels on top of the neighbouring card. */
    overflow: hidden;
    min-width: 0;
}

/* Chart body: a fixed-height box the SVG scales into, so labels never overflow the card. */
.dash-chart-wrap {
    position: relative;
    overflow: hidden;
}

.dash-range {
    display: flex;
    align-items: center;
    gap: 4px;
}

.dash-range-btn {
    background: var(--bg-tertiary);
    border: 1px solid var(--border);
    color: var(--text-secondary);
    border-radius: var(--radius-sm);
    padding: 3px 9px;
    font-size: 12px;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.dash-range-btn:hover { color: var(--text-primary); }

.dash-range-btn.active {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}

.dash-card-wide {
    grid-column: 1 / -1;
}

.dash-card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 12px;
}

.dash-card-head h3 {
    font-size: 15px;
    font-weight: 600;
    color: var(--text-primary);
}

.dash-demo-badge {
    font-size: 11px;
    color: var(--gold);
    background: rgba(255, 215, 0, 0.12);
    border: 1px solid rgba(255, 215, 0, 0.3);
    border-radius: 999px;
    padding: 2px 10px;
}

.dash-donut-wrap {
    display: flex;
    justify-content: center;
}

.dash-media {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 8px;
}

.dash-media-item {
    position: relative;
    aspect-ratio: 1;
    border-radius: var(--radius-md);
    overflow: hidden;
    border: 1px solid var(--border);
}

.dash-media-item img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.dash-media-play {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 20px;
    color: #fff;
    background: rgba(0, 0, 0, 0.35);
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}

@media (max-width: 768px) {
    .dash-grid {
        grid-template-columns: 1fr;
    }
    .dash-card-wide {
        grid-column: auto;
    }
}

.admin-saved-message {
    background: var(--accent-dim);
    border: 1px solid var(--border-accent);
    border-radius: var(--radius-sm);
    padding: 12px 16px;
    margin-bottom: 20px;
    color: var(--accent);
    font-size: 14px;
}

.admin-section-card {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 24px;
    margin-bottom: 20px;
    transition: border-color 0.2s;
}

.admin-section-card:hover {
    border-color: var(--border-accent);
}

.admin-section-header {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 20px;
    flex-wrap: wrap;
}

.admin-section-key {
    font-weight: 700;
    color: var(--accent);
}

.admin-section-type {
    font-size: 11px;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    background: var(--bg-tertiary);
    padding: 3px 8px;
    border-radius: var(--radius-sm);
}

.admin-active-toggle {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 14px;
    color: var(--text-secondary);
    cursor: pointer;
}

.admin-banned-badge {
    margin-left: auto;
    font-size: 12px;
    color: var(--red);
    background: rgba(224, 64, 64, 0.12);
    padding: 4px 10px;
    border-radius: var(--radius-sm);
}

.admin-users-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 4px;
}

.admin-rank-select {
    max-width: 220px;
}

/* ─── Admin users (redesigned cards) ─── */
.admin-users-count {
    color: var(--text-secondary);
    font-size: 13px;
    margin: 4px 0 14px;
}

.admin-users-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
    gap: 14px;
}

/* NOTE: these are namespaced .admin-user-card* on purpose — a bare .user-card already exists for
   the Friends list further down, and sharing the name leaked flex-direction:column into it. */
.admin-user-card {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 16px 18px;
    display: flex;
    flex-direction: column;
    gap: 12px;
    transition: border-color 0.2s ease;
}
.admin-user-card:hover { border-color: var(--border-accent); }
.admin-user-card-banned { border-color: rgba(224, 64, 64, 0.4); }

.admin-user-card-head {
    display: flex;
    align-items: center;
    gap: 12px;
}

.admin-user-card-avatar {
    width: 46px;
    height: 46px;
    border-radius: 50%;
    object-fit: cover;
    background: var(--bg-tertiary);
    flex-shrink: 0;
}

.admin-user-card-identity {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    flex: 1;
}

.admin-user-card-name {
    font-weight: 700;
    color: var(--text-primary);
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.admin-user-card-name:hover { color: var(--accent); }

.admin-user-card-email {
    font-size: 12px;
    color: var(--text-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.admin-user-card-rank {
    font-size: 12px;
    font-weight: 600;
}

.admin-user-card-flags {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 4px;
    flex-shrink: 0;
}

.user-flag {
    font-size: 11px;
    font-weight: 600;
    padding: 3px 8px;
    border-radius: 999px;
    white-space: nowrap;
}
.user-flag-admin { color: var(--accent); background: rgba(255, 95, 31, 0.12); }
.user-flag-banned { color: var(--red); background: rgba(224, 64, 64, 0.12); }

.admin-user-card-uid {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    padding: 6px 10px;
    background: var(--bg-tertiary);
    border-radius: var(--radius-sm);
}
.admin-user-card-uid-label {
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    font-size: 10px;
}
.admin-user-card-uid code {
    color: var(--text-secondary);
    word-break: break-all;
    font-size: 11px;
}

.admin-user-card-actions {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.admin-user-card-rank-edit {
    display: flex;
    gap: 8px;
}
.admin-user-card-rank-edit select { flex: 1; min-width: 0; }
.admin-user-card-buttons {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

.admin-container-wide {
    max-width: 1100px;
}

.admin-audit-filters {
    display: flex;
    gap: 20px;
    flex-wrap: wrap;
    margin-bottom: 24px;
}

.admin-audit-filters .form-group {
    flex: 1;
    min-width: 220px;
}

.admin-audit-grid-wrap {
    overflow-x: auto;
    border-radius: var(--radius-lg);
}

@media (max-width: 640px) {
    .admin-container {
        padding: 24px 16px 24px;
    }

    .admin-section-card {
        padding: 18px;
    }

    .admin-users-actions {
        flex-direction: column;
        align-items: stretch;
    }

    .admin-rank-select {
        max-width: none;
    }

    .admin-audit-filters {
        flex-direction: column;
        gap: 12px;
    }
}

.rank-stardust { color: #8b9bb4; }
.rank-voyager { color: #4a9eff; }
.rank-sentinel { color: #00d4aa; }
.rank-pathfinder { color: #2ecc71; }
.rank-navigator { color: #f1c40f; }
.rank-commander { color: #ff5f1f; }
.rank-oracle { color: #e84393; }
.rank-warlord { color: #e74c3c; }

.profile-container {
    max-width: 800px;
    margin: 0 auto;
    padding: 20px;
}

.profile-header {
    display: flex;
    gap: 24px;
    align-items: flex-start;
    padding: 24px;
    background: var(--bg-secondary);
    border-radius: var(--radius-lg);
    border: 1px solid var(--border);
    margin-bottom: 24px;
}

.profile-avatar {
    width: 120px;
    height: 120px;
    border-radius: 50%;
    overflow: hidden;
    flex-shrink: 0;
    background: var(--bg-tertiary);
    display: flex;
    align-items: center;
    justify-content: center;
}

.profile-avatar img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.profile-avatar-initial {
    font-size: 48px;
    font-weight: 700;
    color: var(--accent);
    text-transform: uppercase;
}

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

.profile-info h1 {
    font-size: 28px;
    font-weight: 700;
    margin-bottom: 4px;
}

.rank-badge {
    display: inline-block;
    font-size: 13px;
    font-weight: 600;
    padding: 2px 10px;
    border-radius: 20px;
    background: rgba(255, 255, 255, 0.05);
    margin-bottom: 8px;
}

/* ─── Admin rank manager ─── */
.rank-manager {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 12px 16px;
    margin-bottom: 16px;
}
.rank-manager > summary {
    cursor: pointer;
    font-weight: 600;
    color: var(--text-primary);
    user-select: none;
}
.rank-manager-body { margin-top: 14px; }
.rank-editor-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }
.rank-editor-row {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}
.rank-editor-row .rank-badge { margin-bottom: 0; }
.rank-key {
    font-size: 12px;
    color: var(--text-secondary);
    background: rgba(255,255,255,0.05);
    padding: 2px 6px;
    border-radius: 6px;
}
.rank-color-input {
    width: 40px;
    height: 34px;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: none;
    cursor: pointer;
    flex-shrink: 0;
}
.rank-name-input { max-width: 180px; }
.rank-sort-input { max-width: 70px; }
.rank-key-input { max-width: 160px; }
.rank-add-form {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    border-top: 1px solid var(--border);
    padding-top: 14px;
}
.rank-delete-confirm {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    color: var(--text-secondary);
}
/* .btn-sm объявлен ниже (строка ~3500) — то объявление и работает, это перекрывалось. */

/* Non-image attachments in the admin support mini-chat */
.report-chat-file {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--accent-sec);
    font-size: 13px;
    text-decoration: none;
}
.report-chat-file:hover { text-decoration: underline; }

/* ─── Message forward / delete ─── */
.msg-forwarded {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 12px;
    color: var(--accent-sec, #1F9CFF);
    border-left: 2px solid var(--accent-sec, #1F9CFF);
    padding-left: 6px;
    margin-bottom: 4px;
    opacity: 0.9;
}

.msg-menu-btn {
    position: absolute;
    top: 4px;
    right: 4px;
    /* Above attachments: an image/video sits later in the bubble's DOM, so without a stacking
       context of its own the button ended up painted underneath and was unclickable. */
    z-index: 3;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    /* Always visible rather than hover-only — touch devices have no hover at all, which made the
       menu unreachable on phones. Kept subtle until interacted with. */
    color: #fff;
    background: rgba(0, 0, 0, 0.35);
    opacity: 0.55;
    transition: opacity 0.15s ease, background 0.15s ease;
}
.message-bubble:hover .msg-menu-btn { opacity: 0.95; }
.msg-menu-btn:hover,
.msg-menu-btn:focus-visible { opacity: 1; background: rgba(0, 0, 0, 0.55); }

/* The bubble whose menu is open must out-stack its neighbours, otherwise later bubbles
   (also positioned) paint over the dropdown. */
.message-bubble.menu-open { z-index: 40; }

.msg-menu {
    position: absolute;
    top: 28px;
    right: 4px;
    z-index: 50;
    background: #1a1d24;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: 0 8px 24px rgba(0,0,0,0.5);
    padding: 4px;
    min-width: 150px;
    display: flex;
    flex-direction: column;
}
.msg-menu button {
    display: flex;
    align-items: center;
    gap: 8px;
    background: none;
    border: none;
    color: var(--text-primary);
    text-align: left;
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-size: 14px;
}
.msg-menu button:hover { background: rgba(255,255,255,0.06); }

/* Flipped open above the bubble for the last messages in a thread. */
.msg-menu.up { top: auto; bottom: 28px; }

/* У входящих меню раскрывается ВПРАВО, а не влево.
   Меню шире 150px и прижато к правому краю пузыря, поэтому у короткого входящего сообщения
   («Да», «Ок») его левый край уходил левее области переписки. Та скроллится, то есть обрезает
   по своей границе, и меню просто исчезало — на его месте видна левая панель со списком чатов.
   Ни один z-index здесь не помогает: элемент отсечён, а не перекрыт. */
.message-bubble.other .msg-menu {
    right: auto;
    left: 4px;
}
.msg-menu-danger { color: var(--red) !important; }

/* Badge with the reader count on the "Просмотрено" menu item. */
.msg-readers-count {
    margin-left: auto;
    font-size: 11px;
    font-weight: 600;
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 16%, transparent);
    border-radius: 8px;
    padding: 1px 6px;
}

/* Per-message read receipts inside the group message menu. */
.msg-readers {
    border-top: 1px solid var(--border);
    margin-top: 4px;
    padding: 6px 4px 2px;
    max-height: 190px;
    overflow-y: auto;
}
.msg-readers-title {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    color: var(--text-secondary);
    padding: 0 6px 6px;
}
.msg-readers-empty {
    font-size: 12px;
    color: var(--text-secondary);
    padding: 2px 6px 6px;
}
.msg-reader {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 6px;
    border-radius: 6px;
}
.msg-reader img {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    object-fit: cover;
    background: var(--bg-tertiary);
    flex-shrink: 0;
}
.msg-reader-name {
    font-size: 13px;
    flex: 1;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.msg-reader-time { font-size: 11px; color: var(--text-secondary); flex-shrink: 0; }

.msg-delete-actions { flex-wrap: wrap; justify-content: flex-end; }

.forward-dialog-body { padding-top: 8px; }
.forward-target-list {
    display: flex;
    flex-direction: column;
    gap: 2px;
    max-height: 320px;
    overflow-y: auto;
}
.forward-target {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: background 0.15s ease;
}
.forward-target:hover { background: rgba(255,255,255,0.06); }
.forward-target .chat-avatar { width: 38px; height: 38px; }

/* ─── Reply / quote ─── */
/* Quote block inside a sent message — click scrolls to the original. */
.msg-quote {
    display: flex;
    flex-direction: column;
    gap: 1px;
    margin-bottom: 6px;
    padding: 4px 8px;
    border-left: 3px solid var(--accent-sec, #1F9CFF);
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.07);
    cursor: pointer;
    max-width: 100%;
    overflow: hidden;
}
.msg-quote:hover { background: rgba(255, 255, 255, 0.13); }
/* The quoted text is the thing being referred to — give it its own tinted, outlined block so it
   reads as a citation rather than as part of the reply. */
.message-bubble.other .msg-quote {
    background: rgba(31, 156, 255, 0.12);
    box-shadow: inset 0 0 0 1px rgba(31, 156, 255, 0.28);
}
.message-bubble.own .msg-quote {
    background: rgba(255, 255, 255, 0.16);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.28);
}
.message-bubble.own .msg-quote { border-left-color: rgba(255, 255, 255, 0.85); }
.msg-quote-author {
    font-size: 12px;
    font-weight: 700;
    color: var(--accent-sec, #1F9CFF);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.message-bubble.own .msg-quote-author { color: #fff; }
.msg-quote-text {
    font-size: 12px;
    opacity: 0.85;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Brief flash on the message a quote points at. */
@keyframes msgHighlightPulse {
    0%   { box-shadow: 0 0 0 0 rgba(31, 156, 255, 0.55); }
    100% { box-shadow: 0 0 0 14px rgba(31, 156, 255, 0); }
}
/* Compound selector on purpose: .message-bubble sets its own `animation: messageSlideIn` further
   down the file, and at equal specificity the later rule would win and swallow the pulse. */
.message-bubble.msg-highlight {
    animation: msgHighlightPulse 1.4s ease-out;
}

/* Reply banner above the composer. */
.reply-bar {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    margin-bottom: 8px;
    background: var(--bg-secondary, #1a1d24);
    border-left: 3px solid var(--accent);
    border-radius: 8px;
}
.reply-bar-icon { color: var(--accent); display: flex; flex-shrink: 0; }
.reply-bar-body { display: flex; flex-direction: column; min-width: 0; flex: 1; gap: 1px; }
.reply-bar-author { font-size: 12px; font-weight: 700; color: var(--accent); }
.reply-bar-text {
    font-size: 12px;
    color: var(--text-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.reply-bar-close {
    background: none;
    border: none;
    color: var(--text-secondary);
    cursor: pointer;
    padding: 4px;
    border-radius: 6px;
    flex-shrink: 0;
}
.reply-bar-close:hover { color: var(--accent); background: rgba(255, 255, 255, 0.08); }

/* ─── Pinned message bar ─── */
.pinned-bar {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 14px;
    background: var(--bg-secondary, #1a1d24);
    border-bottom: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    cursor: pointer;
    flex-shrink: 0;
}
.pinned-bar:hover { background: rgba(255, 255, 255, 0.04); }
.pinned-bar-icon { color: var(--accent); display: flex; flex-shrink: 0; }
.pinned-bar-body { display: flex; flex-direction: column; min-width: 0; flex: 1; gap: 1px; }
.pinned-bar-title { font-size: 11px; font-weight: 700; color: var(--accent); }
.pinned-bar-text {
    font-size: 12px;
    color: var(--text-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.pinned-bar-close {
    background: none;
    border: none;
    color: var(--text-secondary);
    cursor: pointer;
    padding: 4px;
    border-radius: 6px;
    flex-shrink: 0;
}
.pinned-bar-close:hover { color: var(--accent); background: rgba(255, 255, 255, 0.08); }

/* ─── Group settings (name + picture) ─── */
.group-settings {
    display: flex;
    gap: 14px;
    align-items: flex-start;
    padding-bottom: 14px;
    margin-bottom: 12px;
    border-bottom: 1px solid var(--border);
}
.group-settings-avatar {
    position: relative;
    flex-shrink: 0;
    /* Belt and braces: even if a child ignores the sizing below, the round frame clips it. */
    width: 64px;
    height: 64px;
}

/* The uploaded picture is cropped to a circle exactly like the avatar in the chat list:
   fixed square box + object-fit:cover, so a wide or very large photo is centre-cropped
   instead of stretching the frame. */
.group-settings-avatar img {
    display: block;
    width: 64px;
    height: 64px;
    aspect-ratio: 1 / 1;
    border-radius: 50%;
    object-fit: cover;
    object-position: center;
    background: var(--bg-tertiary);
}

.group-settings-avatar .group-avatar {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-tertiary);
}
.group-settings-avatar-btn {
    position: absolute;
    right: -2px;
    bottom: -2px;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    border: 2px solid var(--bg-secondary);
}
.group-settings-fields { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.group-settings-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* ─── Delivery / read ticks (Telegram-style) ─── */
.msg-ticks {
    display: inline-flex;
    align-items: center;
    margin-left: 4px;
    vertical-align: middle;
    /* Sent + delivered are muted; read lights up. On own (orange) bubbles the muted state is a
       translucent white so it stays legible against the gradient. */
    color: rgba(255, 255, 255, 0.65);
}
.msg-ticks.read { color: #7ad7ff; }
.message-bubble.other .msg-ticks { color: var(--text-secondary); }
.message-bubble.other .msg-ticks.read { color: var(--accent-sec, #1F9CFF); }

/* ─── Call log entries in the conversation ─── */
.call-log {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    align-self: flex-start;
    max-width: 85%;
    padding: 9px 14px;
    margin: 2px 0;
    border-radius: 18px;
    background: var(--border);
    color: var(--text-primary);
    font-size: 13px;
}
.call-log.own { align-self: flex-end; }
.call-log-icon {
    display: flex;
    align-items: center;
    color: var(--accent-sec, #1F9CFF);
}
.call-log.missed .call-log-icon { color: var(--red, #e04040); }
.call-log-text { display: flex; align-items: center; gap: 6px; }
.call-log-duration { color: var(--text-secondary); }
.call-log-time { color: var(--text-secondary); font-size: 11px; }

/* ─── Video player: playback speed ─── */
.vp-speed { position: relative; flex: 0 0 auto; }
.vp-speed-btn {
    background: none;
    border: none;
    color: #fff;
    cursor: pointer;
    font-size: 12px;
    font-weight: 600;
    padding: 4px 6px;
    border-radius: 4px;
    min-width: 34px;
}
.vp-speed-btn:hover { background: rgba(255, 255, 255, 0.15); }
.vp-speed-panel {
    display: none;
    position: absolute;
    bottom: calc(100% + 8px);
    right: 0;
    z-index: 5;
    background: rgba(20, 22, 28, 0.97);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 10px 12px;
    width: 168px;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.5);
}
.vp-speed.open .vp-speed-panel { display: block; }

.vp-speed-panel-title {
    display: block;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-secondary);
    margin-bottom: 8px;
}

.vp-speed-range {
    width: 100%;
    accent-color: var(--accent);
    cursor: pointer;
}

.vp-speed-ticks {
    display: flex;
    justify-content: space-between;
    font-size: 10px;
    color: var(--text-secondary);
    margin-top: 2px;
}

/* Privacy toggle on the profile edit form */
.privacy-toggle {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    cursor: pointer;
    font-size: 14px;
    line-height: 1.4;
}
.privacy-toggle input { width: 16px; height: 16px; accent-color: var(--accent); flex-shrink: 0; margin-top: 2px; }
.form-hint { color: var(--text-secondary); font-size: 12px; margin-top: 6px; }

.profile-bio {
    color: var(--text-secondary);
    font-size: 14px;
    margin-bottom: 8px;
    line-height: 1.6;
    overflow-wrap: anywhere;
    word-break: break-word;
    white-space: pre-wrap;
}

.profile-registered {
    color: var(--text-secondary);
    font-size: 13px;
    margin-bottom: 12px;
}

.profile-stats {
    display: flex;
    gap: 20px;
}

.profile-stats span {
    font-size: 14px;
    color: var(--text-secondary);
}

.profile-action {
    flex-shrink: 0;
    align-self: center;
    display: flex;
    flex-direction: column;
    gap: 8px;
    width: 190px;
}

/* Uniform, compact action buttons for both own ("Редактировать профиль") and other users'
   (friend actions + "Пожаловаться") profiles — otherwise multiple buttons wrapped inline and
   broke the header layout. */
.profile-action .btn {
    display: block;
    box-sizing: border-box;
    width: 100%;
    padding: 8px 12px;
    font-size: 13px;
    line-height: 1.3;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: center;
}

.profile-wall h2 {
    font-size: 20px;
    font-weight: 600;
    margin-bottom: 16px;
}

.wall-hidden {
    text-align: center;
    color: var(--text-secondary);
    padding: 40px;
    font-size: 15px;
    background: var(--bg-secondary);
    border-radius: var(--radius-md);
    border: 1px solid var(--border);
}

.post-form {
    background: var(--bg-secondary);
    border-radius: var(--radius-md);
    border: 1px solid var(--border);
    padding: 16px;
    margin-bottom: 20px;
}

.post-form textarea {
    width: 100%;
    min-height: 80px;
    padding: 10px 14px;
    background: var(--bg-dark);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text-primary);
    font-size: 14px;
    font-family: var(--font-main);
    outline: none;
    resize: vertical;
    transition: border-color 0.2s;
}

.post-form textarea:focus {
    border-color: var(--accent);
}

.post-form-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 10px;
}

.char-counter {
    font-size: 12px;
    color: var(--text-secondary);
    margin-left: auto;
}

.attach-btn-label {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: var(--radius-md);
    background: var(--bg-tertiary);
    cursor: pointer;
    font-size: 16px;
    transition: background 0.15s;
}

.attach-btn-label:hover {
    background: var(--border);
}

.compress-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--text-secondary);
    cursor: pointer;
    white-space: nowrap;
}

.vp-root {
    position: relative;
    width: 100%;
    max-width: 100%;
    max-height: 480px;
    background: #000;
    border-radius: var(--radius-md);
    overflow: hidden;
    margin-top: 10px;
}

.vp-root video {
    width: 100%;
    max-height: 480px;
    display: block;
    cursor: pointer;
}

/* Fullscreen: the max-height caps above would otherwise keep the video small on a black
   backdrop. Let it fill the screen and letterbox with object-fit. */
.vp-root:fullscreen {
    max-width: none;
    max-height: none;
    width: 100vw;
    height: 100vh;
    border-radius: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}

.vp-root:fullscreen video {
    max-height: none;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.vp-big-play {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 22px;
    color: #fff;
    background: rgba(0, 0, 0, 0.25);
    cursor: pointer;
    width: 64px;
    height: 64px;
    margin: auto;
    border-radius: 50%;
    pointer-events: none;
}

.vp-controls {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 10px;
    background: linear-gradient(to top, rgba(0, 0, 0, 0.75), transparent);
}

.vp-play-btn, .vp-mute-btn, .vp-fullscreen-btn {
    background: none;
    border: none;
    color: #fff;
    cursor: pointer;
    font-size: 15px;
    padding: 2px 4px;
}

.vp-progress {
    flex: 1;
    height: 4px;
    background: rgba(255, 255, 255, 0.3);
    border-radius: 2px;
    cursor: pointer;
    position: relative;
}

.vp-progress-fill {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 0;
    background: var(--accent);
    border-radius: 2px;
}

.vp-time {
    color: #fff;
    font-size: 11px;
    white-space: nowrap;
}

.vp-volume {
    width: 60px;
    accent-color: var(--accent);
}

/* Centred media frame — photos and videos sit centred on a neutral backdrop so
   portrait and landscape attachments both read consistently inside the post. */
.post-media {
    display: flex;
    align-items: center;
    justify-content: center;
    background: #0d0d0d;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    overflow: hidden;
    margin: 14px 0;
    max-height: 560px;
}

.post-media .post-attachment-img {
    margin: 0;
    max-height: 560px;
    width: auto;
}

.post-media .vp-root {
    margin: 0;
    width: 100%;
    max-height: 560px;
    border-radius: 0;
    background: transparent;
}

.post-media .vp-root video {
    max-height: 560px;
    object-fit: contain;
}

.post-attachment-img {
    max-width: 100%;
    max-height: 560px;
    border-radius: var(--radius-md);
    object-fit: contain;
    display: block;
    margin: 14px auto 0;
}

.comment-sticker-btn {
    background: var(--bg-tertiary);
    border: none;
    border-radius: var(--radius-md);
    width: 34px;
    height: 34px;
    cursor: pointer;
    font-size: 15px;
}

.comment-sticker-btn:hover {
    background: var(--border);
}

.comment-sticker-panel {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    padding: 8px;
    margin-top: 6px;
    background: var(--bg-tertiary);
    border-radius: var(--radius-md);
    max-height: 160px;
    overflow-y: auto;
}

.comment-sticker-preview {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 6px;
}

.comment-sticker-thumb {
    width: 40px;
    height: 40px;
    object-fit: contain;
}

.comment-sticker-img {
    width: 64px;
    height: 64px;
    object-fit: contain;
    display: block;
    margin-top: 4px;
}

.comment-photo {
    display: block;
    max-width: 220px;
    max-height: 220px;
    border-radius: 10px;
    object-fit: cover;
    margin-top: 6px;
}

.comment-photo-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    font-size: 16px;
    padding: 0 4px;
    background: none;
    border: none;
    line-height: 1;
}

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

.post,
.post-card {
    background: var(--bg-secondary);
    border-radius: 16px;
    padding: 20px 22px;
    margin-bottom: 18px;
    border: 1px solid var(--border);
    animation: messageSlideIn 0.3s ease-out;
    transition: border-color 0.2s ease;
}

.post:hover,
.post-card:hover {
    border-color: rgba(255, 255, 255, 0.14);
}

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

.post-author {
    display: flex;
    align-items: center;
    gap: 12px;
}

.post-avatar-img {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    object-fit: cover;
    background: var(--bg-tertiary);
}

.post-avatar-initial {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: var(--bg-tertiary);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 18px;
    font-weight: 600;
    color: var(--accent);
}

.post-author-name {
    font-weight: 600;
    font-size: 15px;
    color: var(--text-primary);
}

.post-author-name:hover {
    color: var(--accent);
    opacity: 1;
}

.post-date {
    font-size: 12px;
    color: var(--text-secondary);
}

.post-content {
    font-size: 16px;
    line-height: 1.65;
    color: var(--text-primary);
    margin-bottom: 14px;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    word-break: break-word;
}

.post-actions {
    display: flex;
    gap: 8px;
    padding-top: 14px;
    margin-top: 4px;
    border-top: 1px solid var(--border);
}

.post-action-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border: none;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text-secondary);
    font-size: 14px;
    font-family: var(--font-main);
    cursor: pointer;
    transition: color 0.2s, background 0.2s;
}

.post-action-btn:hover {
    background: rgba(255, 255, 255, 0.05);
}

.like-btn.liked {
    color: var(--accent);
}

.like-btn.liked:hover {
    background: var(--accent-dim);
}

.comments-section {
    margin-top: 12px;
    border-top: 1px solid var(--border);
    padding-top: 12px;
}

.comment {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 6px 0;
    font-size: 14px;
}

.comment-author {
    font-weight: 600;
    color: var(--accent);
    white-space: nowrap;
    font-size: 13px;
}

.comment-author:hover {
    opacity: 1;
    text-decoration: underline;
}

.comment-text {
    color: var(--text-primary);
    flex: 1;
    word-break: break-word;
}

.comment-date {
    font-size: 11px;
    color: var(--text-secondary);
    white-space: nowrap;
    margin-left: auto;
}

.no-comments {
    color: var(--text-secondary);
    font-size: 13px;
    padding: 8px 0;
    text-align: center;
}

.comment-form {
    display: flex;
    gap: 8px;
    margin-top: 8px;
}

.comment-form input {
    flex: 1;
    padding: 8px 12px;
    background: var(--bg-dark);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text-primary);
    font-size: 13px;
    font-family: var(--font-main);
    outline: none;
    transition: border-color 0.2s;
}

.comment-form input:focus {
    border-color: var(--accent);
}

.comment-form button {
    padding: 8px 16px;
    border: none;
    border-radius: var(--radius-sm);
    background: var(--accent);
    color: white;
    font-size: 13px;
    font-weight: 600;
    font-family: var(--font-main);
    cursor: pointer;
    transition: background 0.2s;
    white-space: nowrap;
}

.comment-form button:hover {
    background: #e5541a;
}

.comment-form button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.pagination {
    display: flex;
    justify-content: center;
    gap: 12px;
    margin-top: 24px;
    padding-bottom: 40px;
}

.no-posts {
    text-align: center;
    color: var(--text-secondary);
    padding: 40px;
    font-size: 15px;
}

.notification-error {
    position: fixed;
    bottom: 24px;
    right: 24px;
    padding: 12px 20px;
    background: rgba(224, 64, 64, 0.9);
    color: white;
    border-radius: var(--radius-sm);
    font-size: 14px;
    z-index: 1000;
    animation: notifSlideIn 0.3s ease-out;
}

.profile-edit-container {
    max-width: 600px;
    margin: 0 auto;
    padding: 20px;
}

.profile-edit-container h1 {
    font-size: 24px;
    font-weight: 700;
    margin-bottom: 24px;
}

.edit-error {
    padding: 12px;
    margin-bottom: 16px;
    background: rgba(224, 64, 64, 0.15);
    border: 1px solid var(--red);
    border-radius: var(--radius-sm);
    color: var(--red);
    font-size: 14px;
}

.field-error {
    color: var(--red);
    font-size: 13px;
    margin-top: 6px;
}

.validation-message {
    color: var(--red);
    font-size: 13px;
    margin-top: 4px;
    display: block;
}

.form-textarea {
    min-height: 100px;
    resize: vertical;
    font-family: var(--font-main);
}

.avatar-section {
    display: flex;
    flex-direction: column;
    gap: 12px;
    align-items: flex-start;
}

.avatar-preview {
    width: 120px;
    height: 120px;
    border-radius: 50%;
    object-fit: cover;
    border: 3px solid var(--border);
}

.avatar-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-tertiary);
    font-size: 48px;
    font-weight: 700;
    color: var(--accent);
    text-transform: uppercase;
}

.avatar-crop {
    display: flex;
    flex-direction: column;
    gap: 10px;
    align-items: center;
    width: 260px;
}

.avatar-crop-stage {
    position: relative;
    width: 260px;
    height: 260px;
    border-radius: var(--radius-md);
    overflow: hidden;
    background: #000;
    touch-action: none;
}

.avatar-crop-stage canvas {
    display: block;
    cursor: grab;
}

.avatar-crop-stage canvas:active {
    cursor: grabbing;
}

.avatar-crop-mask {
    position: absolute;
    inset: 0;
    pointer-events: none;
    box-shadow: 0 0 0 999px rgba(0, 0, 0, 0.55);
    border-radius: 50%;
    border: 2px solid rgba(255, 255, 255, 0.7);
}

.avatar-zoom {
    width: 100%;
    accent-color: var(--accent);
}

.avatar-crop-actions {
    display: flex;
    gap: 8px;
}

.avatar-crop-hint {
    font-size: 12px;
    color: var(--text-secondary);
    text-align: center;
}

/* .btn-sm объявлен ниже (строка ~3500) — то объявление и работает, это перекрывалось. */

.form-actions {
    display: flex;
    gap: 12px;
    align-items: center;
    margin-top: 24px;
}

.form-actions .btn {
    width: auto;
}

.form-actions .btn-secondary {
    color: var(--text-secondary);
}

@media (max-width: 640px) {
    .profile-header {
        flex-direction: column;
        align-items: center;
        text-align: center;
    }

    .profile-avatar {
        width: 100px;
        height: 100px;
    }

    .profile-stats {
        justify-content: center;
    }

    .profile-action {
        width: 100%;
    }

    .profile-action .btn {
        width: 100%;
    }

    .post-header {
        flex-direction: column;
        align-items: flex-start;
        gap: 4px;
    }
}

.friends-container {
    max-width: 800px;
    margin: 0 auto;
    padding: 20px;
}

.friends-container h1 {
    font-size: 24px;
    font-weight: 700;
    margin-bottom: 24px;
}

.search-section {
    margin-bottom: 32px;
}

.search-input {
    width: 100%;
    padding: 12px;
    border-radius: var(--radius-sm);
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    color: var(--text-primary);
    font-size: 16px;
    font-family: var(--font-main);
    outline: none;
    transition: border-color 0.2s;
}

.search-input:focus {
    border-color: var(--accent);
}

.search-results {
    margin-top: 16px;
}

.search-results h3 {
    font-size: 16px;
    font-weight: 600;
    margin-bottom: 12px;
    color: var(--text-secondary);
}

.section {
    margin-bottom: 32px;
}

.section-title {
    font-size: 20px;
    font-weight: 600;
    margin-bottom: 16px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--border);
}

.user-card {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 12px;
    background: var(--bg-secondary);
    border-radius: var(--radius-md);
    margin-bottom: 12px;
    border: 1px solid var(--border);
}

.user-card-link {
    display: flex;
    align-items: center;
    gap: 16px;
    flex: 1;
    text-decoration: none;
    color: inherit;
    min-width: 0;
}

.user-card-link:hover {
    opacity: 1;
}

.user-avatar {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
    background: var(--bg-tertiary);
}

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

.username {
    font-weight: 600;
    font-size: 16px;
    margin-bottom: 2px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.actions {
    display: flex;
    gap: 8px;
    flex-shrink: 0;
}

.btn-danger {
    background: var(--red);
    color: white;
}

.btn-danger:hover {
    background: #c0392b;
}

.empty-state {
    text-align: center;
    padding: 60px 20px;
    color: var(--text-secondary);
    font-size: 15px;
    background: var(--bg-secondary);
    border-radius: var(--radius-md);
    border: 1px solid var(--border);
}

.error-message {
    background: var(--red);
    color: white;
    padding: 12px;
    border-radius: var(--radius-sm);
    margin-bottom: 16px;
    font-size: 14px;
}

.empty-text {
    color: var(--text-secondary);
    font-size: 14px;
    text-align: center;
    padding: 20px;
}

@media (max-width: 640px) {
    .user-card {
        flex-direction: column;
        align-items: flex-start;
    }

    .user-card-link {
        width: 100%;
    }

    .actions {
        width: 100%;
        justify-content: flex-end;
    }

    .actions .btn {
        flex: 1;
    }
}

.feed-container {
    max-width: 700px;
    margin: 0 auto;
    padding: 20px;
}

.feed-container h1 {
    font-size: 24px;
    font-weight: 700;
    margin-bottom: 24px;
}

.feed-post {
    animation: messageSlideIn 0.3s ease-out;
}

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

.post-avatar-initial {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    background: var(--bg-tertiary);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 20px;
    font-weight: 700;
    color: var(--accent);
    flex-shrink: 0;
}

/* Left group: avatar + author info, with a comfortable gap (feed felt cramped before) */
.post-author-group {
    display: flex;
    align-items: center;
    gap: 14px;
    min-width: 0;
}

.post-info {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}

.post-info-line {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.post-info .rank-badge {
    align-self: flex-start;
}

.post-info .post-author-name {
    font-weight: 600;
    font-size: 16px;
    color: var(--text-primary);
    text-decoration: none;
}

/* Timestamp pinned to the right of the header */
.post-header .post-date {
    flex-shrink: 0;
    margin-left: 12px;
    align-self: flex-start;
}

.post-info .post-author-name:hover {
    color: var(--accent);
    opacity: 1;
}

.post-on-wall {
    font-size: 13px;
    color: var(--text-secondary);
    margin-left: 4px;
}

.post-on-wall a {
    color: var(--accent-sec);
    text-decoration: none;
}

.post-on-wall a:hover {
    text-decoration: underline;
}

.load-more {
    width: 100%;
    padding: 12px;
    border-radius: var(--radius-sm);
    background: var(--bg-tertiary);
    color: var(--text-primary);
    border: 1px solid var(--border);
    cursor: pointer;
    font-size: 15px;
    font-family: var(--font-main);
    margin-top: 16px;
    transition: background 0.2s;
}

.load-more:hover {
    background: var(--border);
}

.load-more:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

/* ═══════════════════════════════════════════════════════════
   MESSENGER — Telegram/VK-style redesign
   ═══════════════════════════════════════════════════════════ */

.messenger-container {
    display: flex;
    /* Высоту даёт оболочка — .app-layout-fixed в 26-app-shell.css. Здесь ничего не
       вычитается: прежний calc(100vh - 104px) учитывал навбар и отступы, но забыл футер,
       и страница прокручивалась ровно на его высоту, давая вторую полосу прокрутки. */
    height: 100%;
    min-height: 0;
    max-width: 100%;
    overflow: hidden;
}

/* ─── SIDEBAR ─── */

.sidebar {
    width: 380px;
    min-width: 380px;
    background: var(--bg-secondary);
    display: flex;
    flex-direction: column;
    border-right: 1px solid var(--border);
}

.sidebar-header {
    padding: 20px 20px 16px;
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.sidebar-header h2 {
    margin: 0;
    font-size: 24px;
    font-weight: 700;
    color: #ffffff;
}

.search-box {
    position: relative;
    padding: 0 16px 12px;
}

.search-box .search-icon {
    position: absolute;
    left: 28px;
    top: 50%;
    transform: translateY(-50%);
    width: 18px;
    height: 18px;
    color: var(--text-secondary);
    pointer-events: none;
}

.search-box input {
    width: 100%;
    padding: 10px 16px 10px 44px;
    border-radius: 20px;
    background: var(--border);
    border: 2px solid transparent;
    color: var(--text-primary);
    font-family: var(--font-main);
    font-size: 14px;
    outline: none;
    transition: border-color 0.2s ease, background 0.2s ease;
}

.search-box input:focus {
    border-color: var(--accent);
    background: #333333;
}

.search-box input::placeholder {
    color: var(--text-secondary);
}

.chat-list {
    flex: 1;
    overflow-y: auto;
}

.chat-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 16px;
    cursor: pointer;
    transition: background 0.15s ease;
    position: relative;
    height: 72px;
    border-left: 3px solid transparent;
}

.chat-item:hover {
    background: #252525;
}

.chat-item.active {
    background: #2c2c2c;
    border-left-color: var(--accent-sec);
}

.avatar-wrapper {
    position: relative;
    flex-shrink: 0;
    width: 50px;
    height: 50px;
}

.chat-avatar {
    width: 50px;
    height: 50px;
    border-radius: 50%;
    object-fit: cover;
    background: var(--border);
}

.online-indicator {
    position: absolute;
    bottom: 1px;
    right: 1px;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: #00d4aa;
    border: 2px solid var(--bg-secondary);
}

.chat-info {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 4px;
}

.chat-name {
    font-weight: 600;
    font-size: 15px;
    color: #ffffff;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.chat-last-message {
    font-size: 13px;
    color: var(--text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.chat-meta {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 6px;
    flex-shrink: 0;
    min-width: 40px;
}

.chat-time {
    font-size: 12px;
    color: var(--text-secondary);
    white-space: nowrap;
}

.unread-badge {
    background: var(--accent);
    color: white;
    border-radius: 12px;
    padding: 2px 8px;
    font-size: 11px;
    font-weight: 700;
    min-width: 20px;
    text-align: center;
    line-height: 1.4;
}

.search-results-label {
    padding: 10px 16px 4px;
    font-size: 12px;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* ─── CHAT WINDOW ─── */

.chat-window {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-width: 0;
    background: #0f0f0f;
}

.chat-header {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 0 20px;
    height: 64px;
    background: var(--bg-secondary);
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
    position: relative;
}

.back-btn {
    display: none;
    background: none;
    border: none;
    color: var(--text-primary);
    font-size: 22px;
    cursor: pointer;
    padding: 4px 8px;
    border-radius: 8px;
    transition: background 0.15s;
}

.back-btn:hover {
    background: var(--border);
}

.chat-header-avatar {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
    background: var(--border);
}

.chat-header-info {
    flex: 1;
    min-width: 0;
}

.chat-header-name {
    font-weight: 600;
    font-size: 16px;
    color: #ffffff;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.chat-header-status {
    font-size: 13px;
    color: var(--text-secondary);
}

.chat-header-status.online {
    color: #00d4aa;
}

.chat-menu-btn {
    background: none;
    border: none;
    color: var(--text-secondary);
    font-size: 20px;
    cursor: pointer;
    padding: 8px;
    border-radius: 8px;
    transition: color 0.15s, background 0.15s;
}

.chat-menu-btn:hover {
    color: var(--text-primary);
    background: var(--border);
}

/* ─── MESSAGES AREA ─── */

.messages-container {
    flex: 1;
    overflow-y: auto;
    padding: 20px;
    display: flex;
    flex-direction: column;
    gap: 4px;
    background: radial-gradient(circle at 50% 50%, #0f0f0f 0%, var(--bg-secondary) 100%);
}

.messages-container > :not(.date-divider) + .date-divider {
    margin-top: 16px;
}

.messages-container > .date-divider + * {
    margin-top: 8px;
}

.date-divider {
    align-self: center;
    background: rgba(26, 26, 26, 0.85);
    border-radius: 12px;
    padding: 4px 14px;
    font-size: 13px;
    color: var(--text-secondary);
    margin: 12px 0 8px;
    pointer-events: none;
    user-select: none;
}

/* ─── MESSAGE BUBBLE ─── */

.message-bubble {
    max-width: 65%;
    width: fit-content;
    /* Extra right padding reserves room for the ⋮ button so it never overlaps text/timestamp. */
    padding: 10px 34px 10px 14px;
    border-radius: 18px;
    word-wrap: break-word;
    overflow-wrap: break-word;
    line-height: 1.5;
    font-size: 14px;
    animation: messageSlideIn 0.3s ease-out;
    position: relative;
}

.message-bubble.own {
    align-self: flex-end;
    background: linear-gradient(135deg, #FF5F1F 0%, #FF7F3F 100%);
    color: #ffffff;
    border-radius: 18px 18px 4px 18px;
    box-shadow: 0 2px 8px rgba(255, 95, 31, 0.25);
}

.message-bubble.other {
    align-self: flex-start;
    background: var(--border);
    color: var(--text-primary);
    border-radius: 18px 18px 18px 4px;
}

/* Grouped messages (same sender, <5 min apart) */
.message-bubble.grouped {
    margin-top: 2px;
}

.message-bubble.grouped.own {
    border-radius: 18px 4px 4px 18px;
}

.message-bubble.grouped.other {
    border-radius: 4px 18px 18px 4px;
}

.message-content {
    white-space: pre-wrap;
}

.message-attachment {
    max-width: 100%;
    border-radius: 12px;
    display: block;
    margin-top: 6px;
}

.message-time {
    font-size: 11px;
    opacity: 0.7;
    margin-top: 5px;
    text-align: right;
    white-space: nowrap;
}

/* ─── TYPING INDICATOR ─── */

/* Полоса «печатает» живёт МЕЖДУ лентой и строкой ввода, а не внутри ленты.

   Прежде она была последней строкой в прокручиваемой области: появляясь под последним
   сообщением, уезжала за край, и увидеть её можно было только прокрутив вниз.

   flex-shrink: 0 обязателен — иначе полоса сожмётся в ноль, когда лента займёт всю высоту. */
.typing-indicator {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 20px;
    background: var(--bg-secondary);
    border-top: 1px solid var(--border);
}

.typing-indicator span {
    font-size: 13px;
    color: var(--text-secondary);
}

.typing-dots {
    display: flex;
    align-items: center;
    gap: 3px;
}

.typing-dots span {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--text-secondary);
    animation: typingDot 1.4s ease-in-out infinite;
}

.typing-dots span:nth-child(2) { animation-delay: 0.2s; }
.typing-dots span:nth-child(3) { animation-delay: 0.4s; }

/* ─── INPUT AREA ─── */

.input-area {
    position: relative;
    padding: 16px 20px;
    border-top: 1px solid var(--border);
    background: var(--bg-secondary);
    flex-shrink: 0;
}

.input-row {
    display: flex;
    align-items: flex-end;
    gap: 10px;
}

.attach-btn {
    background: none;
    border: none;
    color: var(--text-secondary);
    font-size: 24px;
    cursor: pointer;
    padding: 8px;
    border-radius: 8px;
    line-height: 1;
    transition: color 0.15s, background 0.15s;
    margin-bottom: 2px;
}

.attach-btn:hover {
    color: var(--accent);
    background: rgba(255, 95, 31, 0.1);
}

.input-row textarea {
    flex: 1;
    padding: 12px 16px;
    border-radius: 20px;
    background: var(--border);
    border: 2px solid transparent;
    color: var(--text-primary);
    resize: none;
    font-family: var(--font-main);
    font-size: 14px;
    min-height: 44px;
    max-height: 120px;
    /* Auto-resize (messenger.js) grows the field to fit, so no scrollbar is needed until the text
       exceeds max-height — hidden by default; the script flips it to auto past the cap. Without this
       a permanent scrollbar showed on the small single-row field (taller than the field itself). */
    overflow-y: hidden;
    outline: none;
    transition: border-color 0.2s ease, background 0.2s ease;
    line-height: 1.4;
}

.input-row textarea:focus {
    border-color: var(--accent);
    background: #333333;
}

.input-row textarea::placeholder {
    color: var(--text-secondary);
}

.send-btn {
    width: 44px;
    height: 44px;
    min-width: 44px;
    background: var(--accent);
    color: white;
    border: none;
    border-radius: 50%;
    cursor: pointer;
    font-size: 20px;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s, transform 0.1s, opacity 0.15s;
    padding: 0;
    margin-bottom: 2px;
}

.send-btn:hover:not(:disabled) {
    background: #FF7F3F;
    transform: scale(1.05);
}

.send-btn:active:not(:disabled) {
    transform: scale(0.95);
}

.send-btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

/* ─── ATTACHMENT PREVIEW ─── */

.attachment-preview {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    margin-bottom: 10px;
    background: var(--border);
    border-radius: 12px;
    position: relative;
}

/* ─── Attach confirm dialog ─── */
.attach-dialog-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.65);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
}

.attach-dialog {
    width: min(420px, 92vw);
    background: #1e1e1e;
    border: 1px solid #333;
    border-radius: 16px;
    overflow: hidden;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}

.attach-dialog-header {
    padding: 16px 20px;
    font-weight: 600;
    font-size: 16px;
    border-bottom: 1px solid var(--border);
}

.attach-dialog-body {
    padding: 20px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
}

.attach-dialog-preview {
    max-width: 100%;
    max-height: 300px;
    border-radius: 12px;
    object-fit: contain;
}

.attach-dialog-fileicon {
    width: 96px;
    height: 96px;
    border-radius: 16px;
    background: var(--border);
    display: flex;
    align-items: center;
    justify-content: center;
}

.attach-dialog-emoji {
    font-size: 44px;
}

.attach-dialog-name {
    font-weight: 500;
    word-break: break-all;
    text-align: center;
}

.attach-dialog-size {
    color: var(--text-secondary);
    font-size: 13px;
}

.attach-dialog-compress {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 8px;
    cursor: pointer;
    font-size: 14px;
}

.attach-dialog-compress input {
    width: 16px;
    height: 16px;
    accent-color: var(--accent, #e0564f);
    cursor: pointer;
}

.attach-dialog-hint {
    margin-top: 8px;
    color: var(--text-secondary);
    font-size: 13px;
}

.attach-dialog-actions {
    display: flex;
    gap: 10px;
    padding: 16px 20px;
    border-top: 1px solid var(--border);
}

.attach-dialog-actions button {
    flex: 1;
    padding: 10px;
    border-radius: 10px;
    border: none;
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
}

.attach-dialog-cancel {
    background: var(--border);
    color: var(--text-primary, #eee);
}

.attach-dialog-confirm {
    background: var(--accent, #e0564f);
    color: #fff;
}

.attach-dialog-actions button:disabled {
    opacity: 0.6;
    cursor: default;
}

/* Report reason dialog + toast */
.report-reason-input {
    width: 100%;
    resize: vertical;
    padding: 10px 12px;
    border-radius: 10px;
    border: 1px solid #333;
    background: #141414;
    color: var(--text-primary, #eee);
    font-family: inherit;
    font-size: 14px;
}

.report-toast {
    position: fixed;
    left: 50%;
    bottom: 40px;
    transform: translateX(-50%);
    background: #1f9c55;
    color: #fff;
    padding: 12px 22px;
    border-radius: 12px;
    z-index: 2100;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
}

/* Admin reports page */
.reports-filters {
    display: flex;
    gap: 8px;
    margin: 16px 0;
    flex-wrap: wrap;
}

.reports-filter {
    padding: 7px 14px;
    border-radius: 20px;
    border: 1px solid #333;
    background: var(--bg-secondary);
    color: var(--text-secondary);
    cursor: pointer;
    font-size: 14px;
}

.reports-filter.active {
    background: var(--accent, #e0564f);
    color: #fff;
    border-color: transparent;
}

.reports-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.report-card {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-left: 4px solid #666;
    border-radius: 12px;
    padding: 14px 16px;
}

.report-card.report-pending { border-left-color: #f0a020; }
.report-card.report-reviewed { border-left-color: #1f9c55; }
.report-card.report-dismissed { border-left-color: #777; }

.report-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
}

.report-user {
    font-weight: 600;
    color: var(--text-primary, #eee);
    text-decoration: none;
}

.report-user.report-target { color: var(--accent, #e0564f); }

.report-arrow {
    color: var(--text-secondary);
    font-size: 13px;
    margin: 0 4px;
}

.report-status-badge {
    font-size: 12px;
    padding: 3px 10px;
    border-radius: 20px;
}

.report-status-pending { background: rgba(240, 160, 32, 0.18); color: #f0a020; }
.report-status-reviewed { background: rgba(31, 156, 85, 0.18); color: #33c46f; }
.report-status-dismissed { background: rgba(120, 120, 120, 0.18); color: #999; }

.report-uids {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    margin: 8px 0;
    font-size: 12px;
    color: var(--text-secondary);
}

.report-uids code {
    font-family: monospace;
    background: #141414;
    border: 1px solid var(--border);
    padding: 1px 6px;
    border-radius: 5px;
    user-select: all;
}

.report-reason {
    margin: 8px 0;
    font-style: italic;
    color: var(--text-primary, #ddd);
}

.report-meta {
    color: var(--text-secondary);
    font-size: 13px;
}

.report-actions {
    display: flex;
    gap: 8px;
    margin-top: 12px;
}

.btn-sm {
    padding: 6px 12px;
    font-size: 13px;
}

/* Profile presence + pinned posts */
.profile-badges-row {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 4px;
}

.presence-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--text-secondary);
    background: var(--bg-tertiary);
    padding: 3px 10px;
    border-radius: 20px;
}

.presence-badge.online {
    color: #33c46f;
    background: rgba(31, 156, 85, 0.15);
}

.presence-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: #777;
}

.presence-badge.online .presence-dot {
    background: #33c46f;
    box-shadow: 0 0 0 3px rgba(51, 196, 111, 0.2);
}

.post-pinned {
    border-color: rgba(224, 86, 79, 0.45) !important;
}

.pinned-label {
    font-size: 12px;
    color: var(--accent);
    font-weight: 600;
    margin-bottom: 10px;
}

.post-header-right {
    display: flex;
    align-items: center;
    gap: 10px;
}

.post-pin-btn {
    background: transparent;
    border: none;
    cursor: pointer;
    font-size: 14px;
    opacity: 0.35;
    padding: 2px 4px;
    border-radius: 6px;
    transition: opacity 0.2s;
}

.post-pin-btn:hover { opacity: 0.8; }
.post-pin-btn.active { opacity: 1; }

/* Admin users: UID + role badge */
.admin-user-uid {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 6px 0 10px;
    font-size: 12px;
}

.admin-uid-label {
    color: var(--text-secondary);
}

.admin-uid-value {
    font-family: monospace;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    padding: 2px 8px;
    border-radius: 6px;
    color: var(--text-secondary);
    user-select: all;
}

.admin-role-badge {
    font-size: 11px;
    padding: 2px 10px;
    border-radius: 12px;
    background: rgba(31, 156, 255, 0.18);
    color: #1f9cff;
}

/* Blocked-conversation notice (replaces the composer) */
.chat-blocked-notice {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 18px;
    margin: 8px;
    border-radius: 12px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    color: var(--text-secondary);
    font-size: 14px;
    text-align: center;
}

.chat-blocked-unblock {
    background: var(--accent, #e0564f);
    color: #fff;
    border: none;
    border-radius: 8px;
    padding: 6px 14px;
    cursor: pointer;
    font-size: 13px;
}

/* Group chats */
.create-chat-btn {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    border: none;
    background: var(--accent, #e0564f);
    color: #fff;
    font-size: 18px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
}

.group-avatar {
    display: flex !important;
    align-items: center;
    justify-content: center;
    background: linear-gradient(135deg, #1f9cff, #6c5ce7);
    color: #fff;
    font-size: 20px;
}

.chat-header-members {
    cursor: pointer;
    text-decoration: underline dotted;
}

.message-sender {
    font-size: 12px;
    font-weight: 600;
    /* Muted: full-accent on every grouped message shouted louder than the messages themselves. */
    color: rgba(255, 255, 255, 0.55);
    margin-bottom: 2px;
}

.message-sticker {
    max-width: 128px;
    max-height: 128px;
    display: block;
}

.group-create-dialog,
.group-info-dialog {
    width: min(460px, 94vw);
}

.group-create-body,
.group-info-body {
    align-items: stretch;
    gap: 12px;
    max-height: 60vh;
    overflow-y: auto;
    /* Прокрутка не уходит на страницу позади. Без этого колесо, докрутив список до конца,
       начинало прокручивать страницу под затемнением — а её ещё и размывает backdrop-filter,
       и каждый такой кадр браузер пересчитывает заново. Отсюда были рывки. */
    overscroll-behavior: contain;
}

.group-member-picker {
    display: flex;
    flex-direction: column;
    gap: 4px;
    /* Своей прокрутки у списка НЕТ намеренно. Раньше здесь стояли max-height: 220px
       и overflow-y: auto, то есть внутри прокручиваемого тела диалога была вторая
       прокручиваемая область. Колесо крутило внутреннюю, упиралось, цеплялось за внешнюю —
       и на глаз это выглядело как заедание. Прокрутка в диалоге теперь одна. */
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 6px;
}

.group-pick-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px;
    border-radius: 8px;
    cursor: pointer;
}

.group-pick-row:hover {
    background: #242424;
}

.group-pick-row > span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.group-pick-avatar {
    flex: 0 0 auto;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    object-fit: cover;
}

.group-add-plus {
    margin-left: auto;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border: 1px solid var(--border);
    border-radius: 50%;
    background: rgba(255, 95, 31, 0.12);
    color: var(--accent, #e0564f);
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
}
.group-add-plus:hover {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
    transform: scale(1.08);
}

.group-member-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.group-member-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px;
    border-radius: 8px;
}

/* Имя ужимается и обрывается многоточием. Без этого один длинный ник распирал строку:
   на телефоне значок роли и кнопки «Сделать админом» и «Удалить» уезжали за правый край
   окна, и человека из беседы было нечем удалить — до кнопки оставалось только догадаться
   доскроллить вбок внутри модального окна. */
.group-member-name {
    font-weight: 500;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.group-member-role {
    flex: 0 0 auto;
    font-size: 11px;
    padding: 2px 8px;
    border-radius: 12px;
    background: var(--border);
    color: var(--text-secondary);
    margin-left: 4px;
}

.group-member-role.role-owner { background: rgba(224, 86, 79, 0.2); color: #e0564f; }
.group-member-role.role-admin { background: rgba(31, 156, 255, 0.2); color: #1f9cff; }

/* 34, а не 28: это «Удалить» вплотную к «Сделать админом», и промах мышью или пальцем
   стоит человеку места в беседе. Тот же порог, по которому мелкие кнопки ловят в админке. */
.group-mini-btn {
    margin-left: auto;
    flex: 0 0 auto;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    border: 1px solid #333;
    background: #1e1e1e;
    color: var(--text-primary, #eee);
    cursor: pointer;
}

.group-member-row .group-mini-btn + .group-mini-btn {
    margin-left: 6px;
}

.group-remove-btn {
    color: #e0564f;
}

.group-leave-btn {
    background: #7a2a2a !important;
}

/* Message reactions (.message-bubble already has position:relative above) */
.react-add-btn {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 26px;
    height: 26px;
    border-radius: 50%;
    border: 1px solid #333;
    background: #1e1e1e;
    color: var(--text-secondary);
    cursor: pointer;
    font-size: 14px;
    line-height: 1;
    opacity: 0;
    transition: opacity 0.15s;
    z-index: 2;
}

.message-bubble.other .react-add-btn { right: -32px; }
.message-bubble.own .react-add-btn { left: -32px; }
.message-bubble:hover .react-add-btn { opacity: 0.8; }
.react-add-btn:hover { opacity: 1 !important; color: var(--accent); }

.reaction-picker {
    position: absolute;
    top: -40px;
    display: flex;
    gap: 2px;
    padding: 4px 6px;
    background: #1e1e1e;
    border: 1px solid #333;
    border-radius: 20px;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
    z-index: 5;
}

.message-bubble.other .reaction-picker { left: 0; }
.message-bubble.own .reaction-picker { right: 0; }

.reaction-emoji {
    background: none;
    border: none;
    cursor: pointer;
    font-size: 18px;
    padding: 2px 4px;
    border-radius: 8px;
    transition: transform 0.1s, background 0.1s;
}

.reaction-emoji:hover { transform: scale(1.25); background: var(--border); }

.reaction-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin-top: 4px;
}

.reaction-chip {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 1px 7px;
    border-radius: 12px;
    border: 1px solid #333;
    background: rgba(0, 0, 0, 0.25);
    color: var(--text-primary, #eee);
    font-size: 12px;
    cursor: pointer;
}

.reaction-chip.mine {
    border-color: var(--accent, #e0564f);
    background: rgba(224, 86, 79, 0.18);
}

.reaction-count { font-size: 11px; color: var(--text-secondary); }
.reaction-chip.mine .reaction-count { color: var(--accent, #e0564f); }

/* Support chat avatar */
.support-avatar {
    display: flex !important;
    align-items: center;
    justify-content: center;
    background: linear-gradient(135deg, #1f9c55, #2ecc71);
    color: #fff;
    font-size: 20px;
}

.chat-header-avatar.support-avatar {
    font-size: 22px;
}

/* "Избранное" self-chat avatar */
.self-avatar {
    display: flex !important;
    align-items: center;
    justify-content: center;
    background: linear-gradient(135deg, #e0564f, #c23b34);
    color: #fff;
    font-size: 20px;
}

.chat-header-avatar.self-avatar {
    font-size: 22px;
}

/* ─── Blacklist settings ─── */
.blacklist-hint {
    color: var(--text-secondary);
    margin-bottom: 16px;
}

.settings-extra {
    margin-top: 20px;
    padding-top: 20px;
    border-top: 1px solid var(--border);
}

.blacklist-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.blacklist-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 12px 16px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: 12px;
}

.blacklist-user {
    display: flex;
    align-items: center;
    gap: 12px;
    text-decoration: none;
    color: inherit;
    min-width: 0;
}

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

.blacklist-name {
    font-weight: 500;
}

.blacklist-date {
    color: var(--text-secondary);
    font-size: 13px;
}


/* Подтверждение передачи главных прав — прямо под строкой того, кому передают.
   Не общим диалогом подтверждения: тот нарисован раньше в разметке и оказался бы под
   окном состава. И не под всем списком: на телефоне оно уезжало за нижний край окна,
   и нажатие на ключ выглядело как нажатие, после которого ничего не произошло. */
.group-transfer-confirm {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin: 0 8px 6px;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: rgba(255, 255, 255, 0.03);
    font-size: 13px;
    line-height: 1.4;
}

.group-transfer-confirm strong {
    overflow-wrap: anywhere;
}

.group-transfer-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 8px;
}

.group-transfer-actions .btn-sm {
    min-height: 34px;
}

/* На телефоне у владельца в строке участника три кнопки и значок роли, и имени
   оставалось 30 пикселей — «Д…» вместо ника. Имя получает первую строку целиком,
   роль и кнопки переносятся под него. */
@media (max-width: 480px) {
    .group-member-row {
        flex-wrap: wrap;
        row-gap: 6px;
    }

    .group-member-name {
        flex: 1 1 calc(100% - 42px);
    }
}
/* ─── Image lightbox ─── */
img.lightbox-img {
    cursor: zoom-in;
}

.lightbox-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.85);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 2000;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.18s ease;
}

.lightbox-overlay.open {
    opacity: 1;
    visibility: visible;
}

.lightbox-full {
    max-width: 92vw;
    max-height: 92vh;
    border-radius: 8px;
    box-shadow: 0 20px 80px rgba(0, 0, 0, 0.6);
    cursor: default;
}

.lightbox-close {
    position: absolute;
    top: 20px;
    right: 24px;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    border: none;
    background: rgba(255, 255, 255, 0.12);
    color: #fff;
    font-size: 20px;
    cursor: pointer;
    z-index: 1;
}

.lightbox-close:hover {
    background: rgba(255, 255, 255, 0.24);
}

body.lightbox-lock {
    overflow: hidden;
}

/* File attachment chip inside a message bubble */
.message-file {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-top: 6px;
    padding: 8px 12px;
    background: rgba(0, 0, 0, 0.2);
    border-radius: 10px;
    text-decoration: none;
    color: inherit;
    max-width: 260px;
}

.message-file:hover {
    background: rgba(0, 0, 0, 0.3);
}

.message-file-icon {
    font-size: 18px;
}

.message-file-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.attachment-preview .preview-thumb {
    width: 60px;
    height: 60px;
    object-fit: cover;
    border-radius: 8px;
    border: 1px solid #3a3a3a;
}

.attachment-preview .preview-name {
    flex: 1;
    font-size: 13px;
    color: #b0b0b0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.remove-attachment {
    width: 28px;
    height: 28px;
    border: none;
    background: rgba(255, 255, 255, 0.1);
    color: var(--text-primary);
    border-radius: 50%;
    cursor: pointer;
    font-size: 14px;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s;
    flex-shrink: 0;
    padding: 0;
    line-height: 1;
}

.remove-attachment:hover {
    background: var(--red);
}

/* ─── UPLOAD STATUS ─── */

.upload-status {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    margin-bottom: 10px;
    background: var(--border);
    border-radius: 12px;
    font-size: 13px;
    color: #b0b0b0;
}

.upload-status .spinner {
    width: 18px;
    height: 18px;
    border: 2px solid #3a3a3a;
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: uploadSpin 0.6s linear infinite;
    flex-shrink: 0;
}

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

/* ─── STICKER PANEL ─── */

.sticker-panel {
    position: absolute;
    bottom: 100%;
    left: 0;
    right: 0;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: 12px 12px 0 0;
    margin-bottom: 8px;
    max-height: 260px;
    z-index: 10;
    display: flex;
    animation: stickerPanelSlideIn 0.2s ease-out;
}

@keyframes stickerPanelSlideIn {
    from { opacity: 0; transform: translateY(20px); }
    to { opacity: 1; transform: translateY(0); }
}

.sticker-packs {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 8px;
    border-right: 1px solid var(--border);
    min-width: 48px;
    overflow-y: auto;
    background: #151515;
    border-radius: 12px 0 0 0;
}

.sticker-pack-tab {
    width: 40px;
    height: 40px;
    border-radius: 8px;
    cursor: pointer;
    padding: 4px;
    transition: background 0.15s;
    display: flex;
    align-items: center;
    justify-content: center;
}

.sticker-pack-tab:hover {
    background: var(--border);
}

.sticker-pack-tab.active {
    background: rgba(255, 95, 31, 0.2);
    box-shadow: inset 0 0 0 2px #FF5F1F;
}

.sticker-pack-tab img {
    width: 32px;
    height: 32px;
    object-fit: contain;
    border-radius: 4px;
}

.sticker-content {
    flex: 1;
    padding: 8px;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.sticker-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 8px;
    padding-bottom: 6px;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}

.sticker-header span {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-primary);
}

.sticker-close {
    background: none;
    border: none;
    color: var(--text-secondary);
    cursor: pointer;
    font-size: 18px;
    padding: 4px;
    border-radius: 4px;
}

.sticker-close:hover {
    background: var(--border);
    color: var(--text-primary);
}

.sticker-list {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    overflow-y: auto;
    align-content: flex-start;
}

.sticker-item {
    width: 56px;
    height: 56px;
    border-radius: 8px;
    cursor: pointer;
    transition: transform 0.15s, box-shadow 0.15s;
    background: var(--border);
    object-fit: contain;
    padding: 4px;
}

.sticker-item:hover {
    transform: scale(1.15);
    box-shadow: 0 4px 16px rgba(255, 95, 31, 0.3);
    z-index: 5;
}

.sticker-btn {
    background: none;
    border: none;
    font-size: 22px;
    cursor: pointer;
    padding: 8px;
    border-radius: 8px;
    transition: background 0.15s;
    line-height: 1;
    margin-bottom: 2px;
}

.sticker-btn:hover {
    background: rgba(255, 95, 31, 0.15);
}

/* ─── MESSAGE ATTACHMENT ─── */

.message-attachment {
    max-width: 300px;
    max-height: 300px;
    border-radius: 12px;
    cursor: pointer;
    display: block;
    margin-top: 6px;
    border: 1px solid rgba(255,255,255,0.05);
    transition: transform 0.15s;
}

.message-attachment:hover {
    transform: scale(1.02);
}

.message-bubble .vp-root {
    max-width: 300px;
    max-height: 300px;
}

.message-bubble .vp-root video {
    max-height: 300px;
}

/* ─── EMPTY STATE ─── */

.empty-chat {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 16px;
    color: var(--text-secondary);
    font-size: 16px;
    background: radial-gradient(circle at 50% 50%, #0f0f0f 0%, var(--bg-secondary) 100%);
}

.empty-chat .empty-icon {
    width: 64px;
    height: 64px;
    color: #3a3a3a;
}

/* ─── SCROLLBARS ─── */

.messages-container::-webkit-scrollbar,
.chat-list::-webkit-scrollbar {
    width: 6px;
}

.messages-container::-webkit-scrollbar-track,
.chat-list::-webkit-scrollbar-track {
    background: transparent;
}

.messages-container::-webkit-scrollbar-thumb,
.chat-list::-webkit-scrollbar-thumb {
    background: #3a3a3a;
    border-radius: 3px;
}

.messages-container::-webkit-scrollbar-thumb:hover,
.chat-list::-webkit-scrollbar-thumb:hover {
    background: #4a4a4a;
}

/* ─── RESPONSIVE ─── */

.sidebar-backdrop {
    display: none;
}

@media (max-width: 768px) {
    .navbar {
        padding: 0 12px;
        /* backdrop-filter on this ancestor would create a new containing block,
           breaking position:fixed on .navbar-links below (it fixes to the navbar's
           box instead of the viewport) - disable it only at this breakpoint. */
        backdrop-filter: none;
    }

    .navbar-brand .brand-link {
        font-size: 17px;
    }

    .navbar-actions {
        gap: 2px;
    }

    .navbar-actions .nav-link {
        padding: 6px 10px;
        font-size: 13px;
    }

    /* Turn the top nav links into a fixed bottom tab bar on mobile. */
    .navbar-links {
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        /* Above the messenger sidebar/backdrop (z 100/99): they're fixed, full-height and later in
           the DOM, so at an equal z-index the chat list painted straight over the tab bar and the
           bottom navigation "disappeared" on the messages page. */
        z-index: 120;
        display: flex;
        justify-content: space-around;
        align-items: center;
        /* Одно слагаемое на весь проект — см. 26-app-shell.css. Раньше высота стояла
           числом здесь, безопасная зона добавлялась в 14-admin-polish, а место под панель
           резервировалось третьим числом, и они разошлись. */
        height: var(--tabbar-h);
        padding: 0 4px env(safe-area-inset-bottom, 0px);
        background: rgba(26, 29, 36, 0.95);
        backdrop-filter: blur(12px);
        border-top: 1px solid var(--border);
        gap: 0;
    }

    /* При открытой клавиатуре панель убирается совсем: дотянуться до неё всё равно
       нельзя, а место над клавиатурой нужно строке ввода. Класс ставит viewport.js. */
    .kb-open .navbar-links {
        display: none;
    }

    .navbar-links .nav-link {
        flex: 1;
        text-align: center;
        padding: 8px 4px;
        border-radius: var(--radius-sm);
        font-size: 11px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .main-content {
        padding-bottom: calc(var(--tabbar-h) + 16px);
    }

    .sidebar {
        /* min-width из базового правила (380px) по спецификации побеждает max-width, поэтому без
           обнуления сайдбар остаётся 380px на экранах уже этого, и правые ~60px — время чата и
           счётчик непрочитанных — уезжают под overflow: hidden контейнера. */
        min-width: 0;
        position: fixed;
        left: 0;
        top: 0;
        /* Не доходит до нижней панели, чтобы список чатов прокручивался над ней.
           Та же переменная и та же высота видимой области, что у оболочки: иначе список
           уезжает под панель ровно так же, как уезжала строка ввода. */
        height: calc(var(--app-vh, 100dvh) - var(--tabbar-h));
        z-index: 100;
        transform: translateX(-100%);
        transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
        width: 100%;
        max-width: 380px;
    }

    .sidebar.open {
        transform: translateX(0);
    }

    .sidebar-backdrop {
        display: block;
        position: fixed;
        inset: 0 0 var(--tabbar-h) 0;
        background: rgba(0, 0, 0, 0.5);
        z-index: 99;
        animation: backdropFadeIn 0.3s ease;
    }

    .back-btn {
        display: flex;
    }

    .chat-window {
        min-height: 400px;
    }
}

@media (max-width: 640px) {
    .feed-container {
        padding: 12px;
    }

    .post-info .post-author-name {
        display: block;
        margin-bottom: 2px;
    }

    .post-info .post-date {
        margin-left: 0;
    }

    .message-bubble {
        max-width: 85%;
        font-size: 13px;
        /* Keep the right gutter for the ⋮ button on phones too. */
        padding: 8px 32px 8px 12px;
    }

    .message-attachment {
        max-width: 100%;
        max-height: 200px;
    }

    .messages-container {
        padding: 12px;
    }

    .input-area {
        padding: 12px;
    }

    .input-row textarea {
        font-size: 15px;
    }

    .attachment-preview .preview-thumb {
        width: 48px;
        height: 48px;
    }
}

@media (max-width: 480px) {
    .sidebar {
        max-width: 100%;
    }

    .message-bubble {
        max-width: 90%;
    }

    .chat-header {
        padding: 0 12px;
        height: 56px;
    }

    .messages-container {
        padding: 10px;
    }

    .input-area {
        padding: 10px;
    }
}

/* ─── NOTIFICATION BELL ─── */

.notification-bell {
    position: relative;
    display: flex;
    align-items: center;
}

.bell-button {
    background: none;
    border: none;
    cursor: pointer;
    padding: 8px;
    border-radius: 8px;
    position: relative;
    transition: background 0.2s;
    display: flex;
    align-items: center;
}

.bell-button:hover {
    background: #242831;
}

.bell-button .bell-icon {
    width: 22px;
    height: 22px;
    fill: #E8EAED;
}

.bell-button.ringing .bell-icon {
    animation: bellRing 0.6s ease-in-out;
}

.bell-button .badge {
    position: absolute;
    top: 2px;
    right: 2px;
    background: #FF5F1F;
    color: white;
    border-radius: 10px;
    padding: 2px 6px;
    font-size: 10px;
    font-weight: 700;
    min-width: 18px;
    text-align: center;
    animation: badgePulse 0.3s ease-out;
    line-height: 1.4;
}

.notif-dropdown {
    position: absolute;
    top: 100%;
    right: 0;
    width: 380px;
    max-height: 500px;
    background: #1A1D24;
    border: 1px solid #2A2F3A;
    border-radius: 12px;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);
    overflow: hidden;
    z-index: 1000;
    animation: dropdownSlideIn 0.2s ease-out;
}

@keyframes dropdownSlideIn {
    from { opacity: 0; transform: translateY(-10px); }
    to { opacity: 1; transform: translateY(0); }
}

.notif-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 16px;
    border-bottom: 1px solid #2A2F3A;
}

.notif-header h3 {
    margin: 0;
    font-size: 16px;
    font-weight: 600;
    color: #E8EAED;
}

.mark-all-read {
    background: none;
    border: none;
    color: #1F9CFF;
    cursor: pointer;
    font-size: 13px;
    padding: 0;
}

.mark-all-read:hover {
    text-decoration: underline;
}

.notif-header-actions {
    display: flex;
    gap: 12px;
    align-items: center;
}

.notif-clear {
    background: none;
    border: none;
    color: var(--text-secondary);
    cursor: pointer;
    font-size: 13px;
    padding: 0;
}

.notif-clear:hover {
    color: var(--accent);
    text-decoration: underline;
}

.notif-list {
    max-height: 420px;
    overflow-y: auto;
}

.notif-list::-webkit-scrollbar {
    width: 6px;
}

.notif-list::-webkit-scrollbar-track {
    background: transparent;
}

.notif-list::-webkit-scrollbar-thumb {
    background: #3a3a3a;
    border-radius: 3px;
}

.notif-item {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 14px 16px;
    cursor: pointer;
    transition: background 0.2s;
    border-bottom: 1px solid #242831;
    animation: notifSlideIn 0.25s ease-out;
}

.notif-item:hover {
    background: #242831;
}

.notif-item.unread {
    background: rgba(31, 156, 255, 0.04);
    border-left: 3px solid #1F9CFF;
}

.notif-icon {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: #242831;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 18px;
    flex-shrink: 0;
}

.notif-body {
    flex: 1;
    min-width: 0;
}

.notif-text {
    font-size: 14px;
    color: #E8EAED;
    margin-bottom: 4px;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}

.notif-time {
    font-size: 12px;
    color: #8B919A;
}

.notif-count {
    background: #FF5F1F;
    color: white;
    border-radius: 12px;
    padding: 2px 10px;
    font-size: 13px;
    font-weight: 700;
    align-self: center;
    flex-shrink: 0;
}

.notif-empty {
    padding: 40px 20px;
    text-align: center;
    color: #8B919A;
    font-size: 14px;
}

/* ─── SKELETON LOADING ─── */

.skeleton {
    background: linear-gradient(90deg, var(--bg-tertiary) 25%, var(--border) 37%, var(--bg-tertiary) 63%);
    background-size: 400% 100%;
    animation: skeletonShimmer 1.4s ease infinite;
    border-radius: var(--radius-sm);
}

@keyframes skeletonShimmer {
    0% { background-position: 100% 50%; }
    100% { background-position: 0 50%; }
}

.skeleton-post {
    display: flex;
    flex-direction: column;
    gap: 12px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 16px;
    margin-bottom: 16px;
}

.skeleton-post-header {
    display: flex;
    align-items: center;
    gap: 12px;
}

.skeleton-avatar {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    flex-shrink: 0;
}

.skeleton-line {
    height: 12px;
}

.skeleton-line-title {
    width: 40%;
    height: 14px;
}

.skeleton-line-sub {
    width: 25%;
}

.skeleton-line-text {
    width: 100%;
}

.skeleton-line-text.short {
    width: 70%;
}

/* Кнопки на странице /Error. На 320px переносятся, а не выталкивают друг друга за экран. */
.error-actions {
    display: flex;
    gap: 10px;
    justify-content: center;
    flex-wrap: wrap;
    margin-top: 20px;
}

/* ─── PAGE ERROR (ErrorBoundary) ─── */

.page-error {
    max-width: 460px;
    margin: 48px auto;
    padding: 32px 24px;
    text-align: center;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: 12px;
}

.page-error-icon {
    color: var(--red);
    margin-bottom: 12px;
}

.page-error-title {
    font-size: 20px;
    margin: 0 0 8px;
    color: var(--text-primary);
}

.page-error-text {
    font-size: 14px;
    line-height: 1.6;
    color: var(--text-secondary);
    margin: 0 0 20px;
}

.page-error-details {
    text-align: left;
    font-size: 12px;
    /* Текст исключения — единственный длинный неразрывный кусок на этом экране. */
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    max-height: 180px;
    overflow-y: auto;
    background: var(--bg-primary);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 12px;
    margin: 0 0 20px;
    color: var(--text-secondary);
}

.page-error-actions {
    display: flex;
    gap: 10px;
    justify-content: center;
    flex-wrap: wrap;
}

/* ─── RECONNECT MODAL ─── */

#components-reconnect-modal {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 9999;
    align-items: center;
    justify-content: center;
    background: rgba(11, 12, 16, 0.6);
    backdrop-filter: blur(4px);
}

#components-reconnect-modal.components-reconnect-show,
#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-rejected {
    display: flex;
}

#components-reconnect-modal .reconnect-box {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    padding: 28px 36px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    text-align: center;
    color: var(--text-primary);
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}

#components-reconnect-modal .reconnect-spinner {
    width: 32px;
    height: 32px;
    border: 3px solid var(--border);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: reconnectSpin 0.8s linear infinite;
}

#components-reconnect-modal .reconnect-text-show,
#components-reconnect-modal .reconnect-text-failed,
#components-reconnect-modal .reconnect-text-rejected {
    display: none;
    margin: 0;
}

#components-reconnect-modal.components-reconnect-show .reconnect-text-show,
#components-reconnect-modal.components-reconnect-failed .reconnect-text-failed,
#components-reconnect-modal.components-reconnect-rejected .reconnect-text-rejected {
    display: block;
}

#components-reconnect-modal.components-reconnect-failed .reconnect-spinner,
#components-reconnect-modal.components-reconnect-rejected .reconnect-spinner {
    display: none;
}

#components-reconnect-modal .reconnect-text-failed button,
#components-reconnect-modal .reconnect-text-rejected button {
    margin-top: 8px;
    padding: 8px 18px;
    /* Единственная кнопка на экране, когда связь оборвалась, — по ней должно быть легко попасть
       пальцем: 30px по факту было меньше любого разумного минимума для тач-целей. */
    min-height: 40px;
    background: var(--accent);
    color: #fff;
    border: none;
    border-radius: var(--radius-md);
    cursor: pointer;
}

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

/* ─── ANIMATIONS ─── */

@keyframes messageSlideIn {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: translateY(0); }
}

@keyframes typingDot {
    0%, 60%, 100% { transform: scale(0.8); opacity: 0.4; }
    30% { transform: scale(1.2); opacity: 1; }
}

@keyframes bellRing {
    0%, 100% { transform: rotate(0); }
    20% { transform: rotate(-10deg); }
    40% { transform: rotate(10deg); }
    60% { transform: rotate(-5deg); }
    80% { transform: rotate(5deg); }
}

@keyframes badgePulse {
    0% { transform: scale(1); }
    50% { transform: scale(1.3); }
    100% { transform: scale(1); }
}

@keyframes notifSlideIn {
    from { opacity: 0; transform: translateX(10px); }
    to { opacity: 1; transform: translateX(0); }
}

@keyframes backdropFadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

@keyframes rotate {
    100% { transform: rotate(360deg); }
}

.chat-menu-dropdown {
    position: absolute;
    right: 8px;
    top: 100%;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 4px;
    z-index: 20;
    min-width: 200px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.4);
    animation: notifSlideIn 0.15s ease-out;
}

.chat-menu-item {
    display: block;
    width: 100%;
    padding: 10px 14px;
    background: none;
    border: none;
    color: var(--text-primary);
    font-size: 14px;
    text-align: left;
    cursor: pointer;
    border-radius: 6px;
    transition: background 0.15s;
}

.chat-menu-item:hover {
    background: var(--border);
}

@keyframes dash {
    0% { stroke-dasharray: 1, 200; stroke-dashoffset: 0; }
    50% { stroke-dasharray: 89, 200; stroke-dashoffset: -35; }
    100% { stroke-dasharray: 89, 200; stroke-dashoffset: -124; }
}

/* ===== Polls (голосовалки) ===== */
.poll {
    margin: 10px 0;
    padding: 14px 16px;
    background: var(--bg-tertiary);
    border: 1px solid var(--border);
    border-radius: 12px;
}
.poll-head {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin-bottom: 12px;
}
.poll-icon { font-size: 18px; line-height: 1.3; }
.poll-question {
    font-weight: 600;
    color: var(--text-primary);
    font-size: 15px;
    line-height: 1.35;
}
.poll-options { display: flex; flex-direction: column; gap: 8px; }
.poll-option {
    position: relative;
    display: block;
    width: 100%;
    text-align: left;
    padding: 10px 12px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: 9px;
    color: var(--text-primary);
    font-size: 14px;
    cursor: pointer;
    overflow: hidden;
    transition: border-color 0.15s, background 0.15s;
}
.poll-option:not(.disabled):hover { border-color: var(--accent); }
.poll-option.voted { border-color: var(--accent); }
.poll-option.disabled { cursor: default; }
.poll-option-fill {
    position: absolute;
    top: 0; left: 0; bottom: 0;
    background: var(--accent-dim);
    border-radius: 9px 0 0 9px;
    transition: width 0.4s ease;
    z-index: 0;
}
.poll-option.voted .poll-option-fill { background: var(--accent-glow); }
.poll-option-body {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: 8px;
}
.poll-option-check {
    flex: 0 0 16px;
    color: var(--accent);
    font-weight: 700;
}
.poll-option-text { flex: 1; }
.poll-option-count {
    flex: 0 0 auto;
    color: var(--text-secondary);
    font-size: 12px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.poll-foot {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 10px;
    font-size: 12px;
    color: var(--text-secondary);
    flex-wrap: wrap;
}
.poll-hint-closed { color: var(--red); }
.poll-close-btn {
    margin-left: auto;
    background: none;
    border: 1px solid var(--border);
    border-radius: 7px;
    color: var(--text-secondary);
    padding: 3px 10px;
    font-size: 12px;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s;
}
.poll-close-btn:hover { border-color: var(--red); color: var(--red); }

/* Poll composer */
.poll-composer {
    margin: 10px 0;
    padding: 14px;
    background: var(--bg-tertiary);
    border: 1px solid var(--border);
    border-radius: 12px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.poll-composer-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-weight: 600;
    color: var(--text-primary);
}
.poll-composer-x {
    background: none;
    border: none;
    color: var(--text-secondary);
    font-size: 15px;
    cursor: pointer;
}
.poll-composer-q,
.poll-composer-opt input {
    width: 100%;
    padding: 9px 11px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: 8px;
    color: var(--text-primary);
    font-size: 14px;
}
.poll-composer-q:focus,
.poll-composer-opt input:focus { outline: none; border-color: var(--accent); }
.poll-composer-options { display: flex; flex-direction: column; gap: 8px; }
.poll-composer-opt { display: flex; align-items: center; gap: 6px; }
.poll-composer-opt input { flex: 1; }
.poll-composer-opt-x {
    background: none;
    border: none;
    color: var(--text-secondary);
    cursor: pointer;
    font-size: 14px;
    padding: 4px;
}
.poll-composer-opt-x:hover { color: var(--red); }
.poll-composer-add {
    align-self: flex-start;
    background: none;
    border: 1px dashed var(--border);
    border-radius: 8px;
    color: var(--accent);
    padding: 7px 12px;
    font-size: 13px;
    cursor: pointer;
}
.poll-composer-add:hover { border-color: var(--accent); }
.poll-composer-multi {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--text-secondary);
    font-size: 13px;
    cursor: pointer;
}
.poll-composer-error { color: var(--red); font-size: 13px; }
.poll-composer-actions { display: flex; gap: 8px; }
.poll-composer-create { flex: 0 0 auto; }

/* Poll attach chip in a composer */
.poll-attached-chip {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 8px 0;
    padding: 8px 12px;
    background: var(--bg-tertiary);
    border: 1px solid var(--border);
    border-radius: 9px;
    font-size: 13px;
    color: var(--text-primary);
}
.poll-attached-text { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.poll-attach-btn {
    background: none;
    border: none;
    cursor: pointer;
    font-size: 18px;
    line-height: 1;
    padding: 0 4px;
}
.poll-attach-btn:disabled { opacity: 0.4; cursor: default; }

/* ===== Voice messages ===== */
.voice-msg {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 200px;
    max-width: 320px;
    padding: 6px 4px;
}
.voice-play {
    flex: 0 0 auto;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    border: none;
    background: var(--accent);
    color: #fff;
    font-size: 13px;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: transform 0.1s;
}
.voice-play:hover { transform: scale(1.06); }
.voice-wave {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    gap: 2px;
    height: 32px;
    cursor: pointer;
}
/* Столбик визуализации голосового сообщения. Имя занято этим с самого начала — если понадобится
   полоса чего-нибудь ещё, называйте её иначе (см. комментарий в 22-voice-rooms.css). */
.voice-bar {
    flex: 1 1 auto;
    min-width: 2px;
    max-width: 4px;
    height: 20%;
    background: var(--text-secondary);
    opacity: 0.5;
    border-radius: 2px;
    transition: background 0.1s, opacity 0.1s;
}
.voice-bar.played {
    background: var(--accent);
    opacity: 1;
}
.voice-time {
    flex: 0 0 auto;
    font-size: 12px;
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
    min-width: 34px;
    text-align: right;
}
.message-bubble.own .voice-bar { background: rgba(255,255,255,0.55); }
.message-bubble.own .voice-bar.played { background: #fff; opacity: 1; }
.message-bubble.own .voice-play { background: #fff; color: var(--accent); }
.message-bubble.own .voice-time { color: rgba(255,255,255,0.85); }

/* Recording bar in the composer */
.voice-recording-bar {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    background: var(--bg-secondary);
    border-top: 1px solid var(--border);
}
.voice-rec-dot {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: var(--red);
    animation: voiceRecPulse 1s ease-in-out infinite;
}
@keyframes voiceRecPulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50% { opacity: 0.4; transform: scale(0.8); }
}
.voice-rec-label { flex: 1; color: var(--text-primary); font-size: 14px; }
.voice-rec-cancel {
    background: none;
    border: 1px solid var(--border);
    border-radius: 8px;
    color: var(--text-secondary);
    padding: 6px 12px;
    font-size: 13px;
    cursor: pointer;
}
.voice-rec-cancel:hover { border-color: var(--red); color: var(--red); }
.voice-rec-send {
    background: var(--accent);
    border: none;
    border-radius: 8px;
    color: #fff;
    padding: 6px 14px;
    font-size: 13px;
    cursor: pointer;
}
.voice-mic-btn { font-size: 18px; }

/* ===== Calls (WebRTC) ===== */
.chat-call-btn {
    background: none;
    border: none;
    font-size: 18px;
    cursor: pointer;
    padding: 4px 6px;
    border-radius: 8px;
    line-height: 1;
    transition: background 0.15s;
}
.chat-call-btn:hover { background: var(--bg-tertiary); }

.call-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.75);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 4000;
}
.call-window {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: 16px;
    padding: 24px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 18px;
    min-width: 320px;
    max-width: 92vw;
    box-shadow: 0 20px 60px rgba(0,0,0,0.5);
}
.call-window.video { width: 720px; }

.call-video-stage {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    background: #000;
    border-radius: 12px;
    overflow: hidden;
}
.call-video-stage.hidden { display: none; }
.call-remote {
    width: 100%;
    height: 100%;
    object-fit: cover;
    background: #000;
}
.call-local {
    position: absolute;
    right: 12px;
    bottom: 12px;
    width: 140px;
    max-width: 32%;
    border-radius: 8px;
    border: 2px solid rgba(255,255,255,0.4);
    object-fit: cover;
    background: #111;
}

.call-audio-face,
.call-incoming {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 12px;
}
.call-peer-avatar {
    width: 72px;
    height: 72px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--accent), #ff8a4c);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 30px;
    font-weight: 700;
}
.call-peer-avatar.big { width: 96px; height: 96px; font-size: 40px; }
.call-peer-name { font-size: 20px; font-weight: 600; color: var(--text-primary); }
.call-status-text { color: var(--text-secondary); font-size: 14px; }

.call-actions { display: flex; gap: 18px; margin-top: 4px; }
.call-btn {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    border: none;
    font-size: 22px;
    cursor: pointer;
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: transform 0.1s, filter 0.15s;
    background: var(--bg-tertiary);
}
.call-btn:hover { transform: scale(1.06); }
.call-accept { background: var(--green); }
.call-decline, .call-hangup { background: var(--red); }
.call-mute.on, .call-camera.on { background: var(--accent); }

/* ===== Admin report mini support chat ===== */
.report-chat {
    margin-top: 12px;
    border-top: 1px solid var(--border);
    padding-top: 12px;
}
.report-chat-hint { font-size: 12px; color: var(--text-secondary); margin-bottom: 8px; }
.report-chat-thread {
    max-height: 260px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 4px;
    background: var(--bg-dark);
    border-radius: 8px;
}
.report-chat-empty { color: var(--text-secondary); font-size: 13px; padding: 8px; text-align: center; }
.report-chat-msg {
    max-width: 75%;
    padding: 8px 12px;
    border-radius: 12px;
    font-size: 14px;
}
.report-chat-msg.from-admin {
    align-self: flex-end;
    background: var(--accent);
    color: #fff;
    border-bottom-right-radius: 4px;
}
.report-chat-msg.from-user {
    align-self: flex-start;
    background: var(--bg-tertiary);
    color: var(--text-primary);
    border-bottom-left-radius: 4px;
}
/* Текст жалобы пишет человек: переносы строк у него значимые, а ссылка бывает длиннее
   пузыря. У пользователя в «Поддержке» то же сообщение показывает .message-content — там
   pre-wrap есть, и без него администратор читал бы жалобу одной строкой. */
.report-chat-body { white-space: pre-wrap; overflow-wrap: anywhere; }
.report-chat-img { max-width: 180px; border-radius: 8px; margin-top: 4px; display: block; cursor: pointer; }
.report-chat-time { font-size: 11px; opacity: 0.7; margin-top: 3px; }
.report-chat-preview { position: relative; display: inline-block; margin: 8px 0; }
.report-chat-preview img { max-width: 120px; border-radius: 8px; }
.report-chat-preview button {
    position: absolute; top: -6px; right: -6px;
    background: var(--red); color: #fff; border: none; border-radius: 50%;
    width: 20px; height: 20px; cursor: pointer; font-size: 12px;
}
/* (the old single-line .report-chat-form rules lived here — superseded by the chat-style
   composer further down; the leftover input[type=text] styling matched nothing.) */

/* Clickable DM header name */
.chat-header-name-link { color: inherit; text-decoration: none; }
.chat-header-name-link:hover { text-decoration: underline; }

/* Mic/send toggle in the composer — CSS-driven so typing never round-trips to the server. */
.input-row .send-arrow-btn { display: none; }
.input-row.has-text .send-arrow-btn { display: flex; }
.input-row.has-text .voice-mic-btn { display: none; }

/* ===== Admin panel polish ===== */
.admin-page-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 16px;
}
.admin-menu-top {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 12px;
    margin: 16px 0 28px;
}
.admin-menu-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 16px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: 12px;
    text-decoration: none;
    color: var(--text-primary);
    transition: border-color 0.15s, transform 0.1s, background 0.15s;
}
.admin-menu-item:hover { border-color: var(--accent); transform: translateY(-2px); }
.admin-menu-item-alert { border-color: var(--accent); }
.admin-menu-icon {
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    border-radius: 10px;
    background: var(--accent-dim);
    color: var(--accent);
    display: flex;
    align-items: center;
    justify-content: center;
}
.admin-menu-text h3 { font-size: 15px; margin: 0 0 2px; }
.admin-menu-text p { font-size: 12px; color: var(--text-secondary); margin: 0; }

/* Admin tabs */
.admin-tabs { display: flex; gap: 8px; margin-bottom: 16px; border-bottom: 1px solid var(--border); }
.admin-tab {
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    color: var(--text-secondary);
    padding: 10px 14px;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 6px;
}
.admin-tab.active { color: var(--accent); border-bottom-color: var(--accent); }
.admin-tab-count {
    background: var(--accent);
    color: #fff;
    border-radius: 10px;
    font-size: 11px;
    padding: 1px 7px;
}

/* Ticket cards */
.report-head-main { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.report-subject { color: var(--text-secondary); font-size: 14px; }
.ticket-status-badge { font-size: 12px; font-weight: 600; padding: 2px 10px; border-radius: 20px; white-space: nowrap; }
.ticket-status-new { background: rgba(31,156,255,0.18); color: #1F9CFF; }
.ticket-status-in_progress { background: rgba(255,95,31,0.18); color: var(--accent); }
.ticket-status-resolved { background: rgba(46,204,113,0.18); color: var(--green); }
.ticket-status-closed { background: rgba(139,145,154,0.18); color: var(--text-secondary); }
.ticket-status-btn { background: var(--bg-tertiary); border: 1px solid var(--border); color: var(--text-primary); }
.ticket-status-btn:hover { border-color: var(--accent); }

/* Support system + author labels in the admin mini-chat */
.report-chat-system {
    align-self: center;
    text-align: center;
    max-width: 90%;
    font-size: 12px;
    color: var(--text-secondary);
    background: var(--bg-secondary);
    border: 1px dashed var(--border);
    border-radius: 10px;
    padding: 6px 12px;
}
.report-chat-syslabel {
    font-weight: 700;
    color: var(--gold);
    margin-right: 4px;
}
.report-chat-author { font-size: 10px; opacity: 0.65; margin-bottom: 2px; }

/* Support system message in the user's chat */
.support-system-msg {
    align-self: center;
    max-width: 85%;
    margin: 6px auto;
    text-align: center;
    font-size: 13px;
    color: var(--text-secondary);
    background: var(--bg-tertiary);
    border: 1px dashed var(--border);
    border-radius: 12px;
    padding: 8px 14px;
}
.support-system-badge {
    display: inline-block;
    font-size: 10px;
    font-weight: 700;
    color: var(--gold);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-right: 6px;
}

/* Inline SVG icons (<Icon>) — align nicely inside buttons/labels that used to hold an emoji. */
.ic { display: inline-block; vertical-align: middle; flex-shrink: 0; }
.like-icon .ic { vertical-align: -3px; }
.liked-icon .ic { color: var(--red); }
.notif-icon .ic { vertical-align: middle; }

/* Unified обращения: complaint badge + chat-like admin composer */
.ticket-kind-badge {
    font-size: 12px;
    color: var(--red);
    font-weight: 600;
}
/* Chat-style composer: a roomy multi-line field with the actions tucked underneath, instead of a
   cramped single-line input squeezed next to the buttons. */
.report-chat-form {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-top: 12px;
    padding: 10px;
    background: var(--bg-tertiary);
    border: 1px solid var(--border);
    border-radius: 14px;
}
.report-chat-form textarea {
    width: 100%;
    resize: vertical;
    min-height: 84px;
    max-height: 260px;
    padding: 12px 14px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: 12px;
    color: var(--text-primary);
    font-size: 14px;
    line-height: 1.5;
    font-family: inherit;
}
.report-chat-form textarea:focus { outline: none; border-color: var(--accent); }
.report-chat-form-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
}
.report-chat-send {
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: none;
    background: var(--accent);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background 0.15s, transform 0.1s;
}
.report-chat-send:hover:not(:disabled) { background: #ff7f3f; transform: scale(1.05); }
.report-chat-send:disabled { opacity: 0.5; cursor: default; }
.report-chat-attach { cursor: pointer; display: flex; align-items: center; padding: 8px; }

/* Call error screen (e.g. no secure context / permission denied) */
.call-error .call-error-icon {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    background: rgba(224,64,64,0.15);
    color: var(--red);
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 4px;
}
.call-error .call-status-text {
    max-width: 340px;
    text-align: center;
    line-height: 1.45;
}

/* ============ Mobile polish for newer components ============ */
@media (max-width: 768px) {
    /* Call overlay: use the whole screen, no fixed 720px width. */
    .call-overlay { padding: 0; align-items: stretch; }
    .call-window,
    .call-window.video {
        width: 100%;
        min-width: 0;
        max-width: 100%;
        height: 100dvh;
        border-radius: 0;
        justify-content: center;
        padding: 16px;
    }
    .call-local { width: 96px; right: 8px; bottom: 8px; }
    .call-btn { width: 52px; height: 52px; }

    /* Notification popup: anchored right:0 with a fixed 380px width, it ran off the left edge on
       narrow screens. Pin it to both edges as a viewport-fixed panel instead. */
    .notif-dropdown {
        position: fixed;
        top: 56px;
        left: 8px;
        right: 8px;
        width: auto;
        max-width: none;
        max-height: calc(100dvh - 72px);
    }
    .notif-list { max-height: calc(100dvh - 140px); }
}

@media (max-width: 640px) {
    /* Voice messages fit narrow bubbles. */
    .voice-msg { min-width: 0; max-width: 100%; }
    .voice-wave { min-width: 0; }

    /* Poll composer/options wrap instead of overflowing. */
    .poll-option-text { white-space: normal; }
    .poll-composer-actions { flex-wrap: wrap; }

    /* Admin: obращения list + mini-chat on phones. */
    .admin-page-head { flex-direction: column; align-items: flex-start; gap: 6px; }
    .reports-filters { flex-wrap: wrap; gap: 6px; }
    .report-card { padding: 14px 12px; }
    .report-head { flex-direction: column; align-items: flex-start; gap: 6px; }
    .report-actions { flex-wrap: wrap; }
    .report-uids { flex-direction: column; gap: 2px; }
    .report-uids code { word-break: break-all; }
    .report-chat-thread { max-height: 300px; }
    .report-chat-msg { max-width: 88%; }
    .admin-menu-top { grid-template-columns: 1fr; }

    /* Admin tables (users/audit) scroll horizontally inside their own container. */
    .admin-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

    /* Безопасная зона отсюда убрана целиком, и панель, и строка ввода.
     *
     * Высота панели теперь живёт в --tabbar-h (26-app-shell.css) вместе с этой зоной,
     * а место под панель резервируется той же переменной. Здесь к 58 вырез добавлялся,
     * а к зарезервированным 74 — нет; расхождение и уводило нижний край строки ввода
     * под панель на телефонах с вырезом.
     *
     * Строке ввода зона больше не нужна: снизу её прикрывает либо панель (она эту зону
     * и занимает), либо клавиатура. */
}

/* ─── Very narrow phones (≤400px) ───────────────────────────────────────────
   Placed at the END of the file on purpose: the max-width:768px block further
   up sets the same properties, and at equal specificity the later rule wins —
   declaring this earlier had no effect at all.
   Without it the guest "Вход / Регистрация" pair is wider than the viewport
   and drags the whole page into horizontal scroll. */
@media (max-width: 400px) {
    .navbar { padding: 0 8px; }
    .navbar-inner { padding: 0 4px; }
    .navbar-actions { gap: 2px; min-width: 0; }
    .navbar-actions .nav-link {
        padding: 5px 7px;
        font-size: 11px;
        white-space: nowrap;
    }
    .navbar-brand .brand-link { font-size: 14px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Telegram-style message layout.
   Structure and spacing only — the palette is unchanged (accent bubbles for
   own messages, dark grey for incoming). Appended at the end of the file so
   these rules win over the earlier .message-bubble definitions.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Row = [avatar column] + bubble, bottom-aligned like TG. */
.msg-row {
    display: flex;
    align-items: flex-end;
    gap: 8px;
    width: 100%;
    max-width: 100%;
}
.msg-row.own { justify-content: flex-end; }
.msg-row.other { justify-content: flex-start; }

/* Tight inside a run, looser between runs — TG's key rhythm. */
.messages-container { gap: 0; }
.msg-row + .msg-row { margin-top: 2px; }
.msg-row.run-start { margin-top: 10px; }
.msg-row:first-child { margin-top: 0; }

/* Avatar column: real avatar on the first message of a run, spacer on the rest
   so grouped bubbles stay flush with it. */
.msg-avatar { width: 32px; flex: 0 0 32px; align-self: flex-end; }
.msg-avatar img {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    object-fit: cover;
    display: block;
    background: var(--bg-tertiary);
}

.msg-row .message-bubble {
    max-width: min(78%, 560px);
    align-self: auto;
    margin: 0;
    border-radius: 14px;
    padding: 7px 34px 7px 12px;
    line-height: 1.38;
}

/* Corner "tail" only on the run's outer message, like TG. */
.msg-row.other.run-start .message-bubble { border-bottom-left-radius: 5px; }
.msg-row.own.run-start .message-bubble { border-bottom-right-radius: 5px; }

/* Meta line (time + ticks) sits bottom-right inside the bubble. */
.message-bubble .message-time {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 4px;
    font-size: 11px;
    line-height: 1;
    opacity: 0.65;
    margin-top: 3px;
    text-align: right;
}
.message-bubble.own .message-time { opacity: 0.85; }

/* Sender name: compact, above the first message of a run. */
.msg-row .message-sender { font-size: 12.5px; margin-bottom: 3px; }

/* Date separator as a centred pill floating over the backdrop. */
.date-divider {
    align-self: center;
    background: rgba(0, 0, 0, 0.4);
    backdrop-filter: blur(6px);
    border-radius: 999px;
    padding: 4px 14px;
    font-size: 12px;
    font-weight: 600;
    color: rgba(255, 255, 255, 0.7);
    margin: 16px 0 10px;
}

/* Media fills the bubble edge-to-edge, as in TG. */
.msg-row .message-bubble .message-attachment,
.msg-row .message-bubble .message-sticker {
    border-radius: 10px;
    margin-top: 4px;
}

@media (max-width: 640px) {
    .msg-row .message-bubble { max-width: 84%; padding: 6px 30px 6px 10px; }
    .msg-avatar { width: 28px; flex: 0 0 28px; }
    .msg-avatar img { width: 28px; height: 28px; }
}

/* Edit mode: same bar as the reply banner, tinted differently so the two aren't confused. */
.reply-bar.edit-bar { border-left-color: var(--gold, #FFD700); }
.reply-bar.edit-bar .reply-bar-icon,
.reply-bar.edit-bar .reply-bar-author { color: var(--gold, #FFD700); }

/* "изм." marker in the message meta line. */
.msg-edited { font-style: italic; opacity: 0.75; }

/* ── Feed tabs + follow suggestions (stage 1: cold start) ─────────────────────
   Appended at the end deliberately: at equal specificity the later rule wins,
   and earlier blocks in this file already style .btn and .post-avatar. */
.feed-tabs {
    display: flex;
    gap: 4px;
    margin: 0 0 20px;
    border-bottom: 1px solid var(--border);
}
.feed-tab {
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    color: var(--text-secondary);
    font: inherit;
    font-weight: 600;
    padding: 10px 16px;
    cursor: pointer;
    transition: color .15s ease, border-color .15s ease;
}
.feed-tab:hover { color: var(--text-primary, #fff); }
.feed-tab.active {
    color: var(--accent);
    border-bottom-color: var(--accent);
}

.empty-state-actions {
    display: flex;
    gap: 10px;
    justify-content: center;
    flex-wrap: wrap;
}

.feed-suggestions {
    margin-top: 24px;
    padding: 16px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}
.feed-suggestions-title {
    font-size: 1rem;
    margin: 0 0 12px;
    color: var(--text-secondary);
}
.suggestion-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 8px 0;
    border-bottom: 1px solid var(--border);
}
.suggestion-row:last-child { border-bottom: none; }
.suggestion-user {
    display: flex;
    align-items: center;
    gap: 10px;
    text-decoration: none;
    color: inherit;
    min-width: 0;
}
.suggestion-info { display: flex; flex-direction: column; min-width: 0; }
.suggestion-name { font-weight: 600; }
.suggestion-meta { font-size: .82rem; color: var(--text-secondary); }
.suggestion-follow { flex-shrink: 0; }

.profile-follow-btn { display: inline-flex; align-items: center; gap: 6px; }

@media (max-width: 640px) {
    .feed-tab { flex: 1; padding: 10px 8px; }
    /* The name can be long; the action must stay reachable rather than be pushed off-screen. */
    .suggestion-row { flex-wrap: wrap; }
    .suggestion-follow { width: 100%; }
}

/* ── Onboarding (/app/welcome) ─────────────────────────────────────────────── */
.onboarding {
    max-width: 560px;
    margin: 40px auto;
    padding: 0 16px;
}
.onboarding-progress {
    display: flex;
    gap: 8px;
    justify-content: center;
    margin-bottom: 28px;
}
.onboarding-dot {
    width: 40px;
    height: 4px;
    border-radius: 2px;
    background: var(--border);
    transition: background .2s ease;
}
.onboarding-dot.done { background: var(--accent); }

.onboarding-title { margin: 0 0 8px; }
.onboarding-sub {
    color: var(--text-secondary);
    margin: 0 0 24px;
    line-height: 1.5;
}
.onboarding-card {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 20px;
}
.onboarding-avatar-row {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-bottom: 20px;
}
.onboarding-avatar {
    width: 72px;
    height: 72px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}
.onboarding-avatar-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-dark);
    border: 1px solid var(--border);
    font-size: 1.8rem;
    color: var(--text-secondary);
    text-transform: uppercase;
}
.onboarding-avatar-actions { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
/* The file input is driven by its label, so it must not also be clickable on its own. */
.onboarding-upload { position: relative; overflow: hidden; cursor: pointer; }
.onboarding-upload input[type=file] {
    position: absolute;
    inset: 0;
    opacity: 0;
    cursor: pointer;
}
.onboarding-hint { font-size: .8rem; color: var(--text-secondary); }
.onboarding-empty { color: var(--text-secondary); margin: 0; }

.onboarding-actions {
    display: flex;
    gap: 12px;
    justify-content: space-between;
    margin-top: 20px;
}

@media (max-width: 640px) {
    .onboarding { margin: 20px auto; }
    .onboarding-actions { flex-direction: column-reverse; }
    .onboarding-actions .btn { width: 100%; }
}

/* ── Карточки результатов поиска ────────────────────────────────────────────────────────────── */
.section-heading {
    font-size: 1.05rem;
    color: var(--text-secondary);
    margin: 24px 0 12px;
    font-weight: 600;
}
.result-list { display: flex; flex-direction: column; gap: 10px; }

.result-card {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 14px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    text-decoration: none;
    color: inherit;
    transition: border-color .15s ease, transform .15s ease;
}
.result-card:hover {
    border-color: var(--accent);
    transform: translateY(-2px);
}
.result-card-avatar {
    width: 52px;
    height: 52px;
    border-radius: var(--radius-md);
    object-fit: cover;
    flex-shrink: 0;
}
.result-card-avatar-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-dark);
    border: 1px solid var(--border);
    color: var(--accent);
    font-size: 1.5rem;
    font-weight: 600;
}
.result-card-body { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.result-card-name { font-weight: 600; }
.result-card-meta { color: var(--text-secondary); font-size: .8rem; }

.post-composer {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 14px;
    margin-bottom: 18px;
}
.post-composer-actions { display: flex; justify-content: flex-end; margin-top: 10px; }

.post-header-right { display: flex; align-items: center; gap: 8px; }
.post-delete-btn {
    background: none;
    border: none;
    color: var(--text-secondary);
    cursor: pointer;
    padding: 4px;
    border-radius: var(--radius-sm);
    display: inline-flex;
    transition: color .15s ease, background .15s ease;
}
.post-delete-btn:hover { color: #ff6b6b; background: rgba(255, 107, 107, .1); }
.post-action-static { cursor: default; }
.form-optional { color: var(--text-secondary); font-weight: 400; }

/* ── Hashtags and search ───────────────────────────────────────────────────── */
.hashtag {
    color: var(--accent);
    text-decoration: none;
}
.hashtag:hover { text-decoration: underline; }

.tag-title { color: var(--accent); }
.tag-count { color: var(--text-secondary); margin: -8px 0 16px; }

.search-bar {
    display: flex;
    gap: 10px;
    margin-bottom: 20px;
}
.search-input { flex: 1; min-width: 0; }

.tag-cloud {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}
.tag-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--accent);
    text-decoration: none;
    font-size: .9rem;
    transition: border-color .15s ease, transform .15s ease;
}
.tag-chip:hover { border-color: var(--accent); transform: translateY(-1px); }
.tag-chip-count { color: var(--text-secondary); font-size: .8rem; }

.post-footer {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--border);
}
.post-action {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--text-secondary);
    text-decoration: none;
    font-size: .9rem;
}
.post-action:hover { color: var(--accent); }
.post-open-link { margin-left: auto; }

@media (max-width: 640px) {
    .search-bar { flex-direction: column; }
    .search-bar .btn { width: 100%; }
}

/* ── Signed-out link preview page (served by LinkPreviewMiddleware) ────────── */
.preview-page {
    max-width: 520px;
    margin: 12vh auto;
    padding: 0 20px;
    text-align: center;
}
.preview-logo { border-radius: var(--radius-md); margin-bottom: 18px; }
.preview-title { margin: 0 0 10px; font-size: 1.6rem; }
.preview-description {
    color: var(--text-secondary);
    line-height: 1.5;
    margin: 0 0 20px;
}
.preview-hint { color: var(--text-secondary); margin: 0 0 20px; font-size: .9rem; }
.preview-page .btn { margin: 0 4px; }

@media (max-width: 640px) {
    .preview-page { margin: 8vh auto; }
    .preview-page .btn { display: block; width: 100%; margin: 8px 0; }
}

/* ── Invites ───────────────────────────────────────────────────────────────── */
.invites-intro { color: var(--text-secondary); margin: -8px 0 20px; line-height: 1.5; }

.invite-create {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 16px;
}
.invite-create-row { display: flex; gap: 12px; }
.invite-create-row .form-group { flex: 1; min-width: 0; }

.invite-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}
/* A spent or revoked link stays visible but reads as inactive. */
.invite-row-dead { opacity: .55; }
.invite-main { flex: 1; min-width: 0; }
.invite-code {
    display: block;
    font-size: .85rem;
    color: var(--accent);
    /* Long URLs must wrap rather than stretch the row off-screen. */
    word-break: break-all;
    margin-bottom: 4px;
}
.invite-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    color: var(--text-secondary);
    font-size: .8rem;
}
.invite-note { color: var(--text-primary); }
.invite-actions { display: flex; gap: 8px; flex-shrink: 0; }
.invite-dead-label { color: var(--text-secondary); font-size: .85rem; }

.auth-invite-banner {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 14px;
    margin-bottom: 16px;
    background: var(--bg-secondary);
    border: 1px solid var(--accent);
    border-radius: var(--radius-md);
    color: var(--text-secondary);
}

@media (max-width: 640px) {
    .invite-create-row { flex-direction: column; gap: 0; }
    .invite-row { flex-direction: column; align-items: stretch; }
    .invite-actions .btn { flex: 1; }
}

/* ── Share menu ────────────────────────────────────────────────────────────── */
.share-wrap { position: relative; display: inline-flex; }

/* Sits above the post card but below dialogs; the backdrop only needs to catch clicks. */
.share-backdrop {
    position: fixed;
    inset: 0;
    z-index: 40;
}
.share-menu {
    position: absolute;
    bottom: calc(100% + 6px);
    right: 0;
    z-index: 41;
    min-width: 200px;
    padding: 6px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: 0 8px 24px rgba(0, 0, 0, .4);
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.share-menu-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 9px 10px;
    background: none;
    border: none;
    border-radius: var(--radius-sm);
    color: var(--text-primary);
    text-decoration: none;
    font-size: .9rem;
    text-align: left;
    cursor: pointer;
    transition: background .12s ease;
}
.share-menu-item:hover { background: var(--bg-dark); color: var(--accent); }

/* Near the left edge the menu would otherwise hang off-screen on a phone. */
@media (max-width: 640px) {
    .share-menu { right: auto; left: 0; }
}

/* ── Автоматические флаги в очереди модерации ──────────────────────────────── */
.auto-flags {
    background: var(--bg-secondary);
    border: 1px solid var(--accent);
    border-radius: var(--radius-lg);
    padding: 16px;
    margin-bottom: 20px;
}
.auto-flags-title {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 6px;
    font-size: 1.05rem;
    color: var(--accent);
}
.auto-flags-hint { margin: 0 0 14px; color: var(--text-secondary); font-size: .85rem; }
.auto-flag-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 0;
    border-top: 1px solid var(--border);
}
.auto-flag-main { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; gap: 10px; align-items: baseline; }
.auto-flag-user { font-weight: 600; color: var(--text-primary); text-decoration: none; }
.auto-flag-user:hover { color: var(--accent); }
.auto-flag-reason { color: var(--text-secondary); font-size: .9rem; }
.auto-flag-date { color: var(--text-secondary); font-size: .8rem; }

@media (max-width: 640px) {
    .auto-flag-row { flex-direction: column; align-items: stretch; }
}

/* ── Юридические страницы и согласие ───────────────────────────────────────── */
.legal-page {
    max-width: 760px;
    margin: 32px auto 60px;
    padding: 0 20px;
    line-height: 1.6;
}
.legal-page h1 { margin-bottom: 6px; }
.legal-page h2 {
    margin: 28px 0 10px;
    font-size: 1.15rem;
    color: var(--accent);
}
.legal-page ul { padding-left: 22px; }
.legal-page li { margin-bottom: 6px; }
.legal-page code {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 1px 6px;
    font-size: .85em;
}
.legal-meta { color: var(--text-secondary); font-size: .85rem; margin-bottom: 20px; }
.legal-draft-notice {
    background: var(--bg-secondary);
    border: 1px solid var(--accent);
    border-radius: var(--radius-md);
    padding: 12px 14px;
    margin-bottom: 24px;
}
.legal-back { margin-top: 32px; }

.consent-toggle { align-items: flex-start; }

.consent-banner {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    background: var(--bg-secondary);
    border: 1px solid var(--accent);
    border-radius: var(--radius-lg);
    padding: 14px 16px;
    margin-bottom: 20px;
}
.consent-banner-text { flex: 1; min-width: 220px; color: var(--text-secondary); }

.site-footer {
    text-align: center;
    padding: 24px 16px 90px;
    color: var(--text-secondary);
    font-size: .85rem;
}
.site-footer a { color: var(--text-secondary); }
.site-footer a:hover { color: var(--accent); }

@media (max-width: 640px) {
    .consent-banner .btn { width: 100%; }
}

/* Пояснение про возраст в баннере согласия — отдельной строкой, чтобы «Принимаю»
   означало ровно то, что написано рядом. */
.consent-banner-age {
    display: block;
    margin-top: 4px;
    font-size: .8rem;
    color: var(--text-secondary);
}

/* ── Фрейм подтверждения почты ─────────────────────────────────────────────── */
.verify-frame {
    background: var(--bg-secondary);
    border: 1px solid var(--accent);
    border-left-width: 4px;
    border-radius: var(--radius-lg);
    padding: 16px 18px;
    margin-bottom: 20px;
}
.verify-frame-head {
    display: flex;
    align-items: baseline;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 8px;
}
.verify-frame-email { color: var(--accent); font-size: .9rem; }
.verify-frame-text { color: var(--text-secondary); margin: 0 0 14px; line-height: 1.5; }
.verify-frame-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.verify-frame-hint { color: var(--text-secondary); font-size: .8rem; margin: 10px 0 0; }
.verify-frame-ok { color: #6bcf7f; margin-top: 10px; }
.verify-frame-link {
    background: none;
    border: none;
    color: var(--text-secondary);
    text-decoration: underline;
    cursor: pointer;
    padding: 8px 0 0;
    font-size: .85rem;
}
.verify-frame-link:hover { color: var(--accent); }

/* Поле кода: моноширинный шрифт и разрядка, чтобы шесть цифр читались как шесть цифр. */
.code-input {
    font-family: 'Courier New', Courier, monospace;
    font-size: 1.3rem;
    letter-spacing: 8px;
    text-align: center;
    max-width: 200px;
}

@media (max-width: 640px) {
    .verify-frame-row { flex-direction: column; align-items: stretch; }
    .code-input { max-width: none; }
    .verify-frame-row .btn { width: 100%; }
}

/* ── Настройки аккаунта ────────────────────────────────────────────────────── */
.settings-card {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 18px;
    margin-bottom: 18px;
}
.settings-card-title { margin: 0 0 12px; font-size: 1.05rem; }
.settings-current { color: var(--text-secondary); margin: 0 0 14px; line-height: 1.5; }
.settings-badge-ok, .settings-badge-warn {
    display: inline-block;
    margin-left: 8px;
    padding: 1px 8px;
    border-radius: var(--radius-sm);
    font-size: .78rem;
}
.settings-badge-ok { color: #6bcf7f; border: 1px solid #6bcf7f; }
.settings-badge-warn { color: var(--accent); border: 1px solid var(--accent); }
.settings-link-row { margin: 18px 0; }

/* ── Удаление аккаунта ─────────────────────────────────────────────────────── */
/* .btn-danger объявлен выше (строка ~2554) и берёт цвет из палитры. Здесь остались только
   состояния, которых там не было: повторное объявление увело бы кнопку с var(--red) на
   собственный оттенок — ровно та ошибка, из-за которой в файле накопились дубли. */
.settings-card-danger { border-color: var(--red); }
.btn-danger:disabled { opacity: .5; cursor: not-allowed; }

.deleted-account-box {
    background: var(--bg-secondary);
    border: 1px solid var(--accent);
    border-radius: var(--radius-lg);
    padding: 18px;
    margin-bottom: 20px;
    line-height: 1.55;
}
.deleted-account-title { margin: 0 0 10px; font-size: 1.1rem; color: var(--accent); }
.admin-deleted-label { color: var(--text-secondary); font-size: .85rem; align-self: center; }
.admin-delete-confirm { max-width: 180px; }
/* ── Поиск в шапке ───────────────────────────────────────────────────────────
   Модуль добавлен в конец нумерации намеренно: правила ниже перекрывают навбар
   из 01-layout-navbar.css, и порядок здесь несущий. */

.navbar-search {
    position: relative;
    display: flex;
    align-items: center;
    /* Не растягивается на всю свободную ширину: навигация рядом важнее. */
    flex: 0 1 260px;
    min-width: 0;
    margin-left: 16px;
}

.navbar-search-icon {
    position: absolute;
    left: 10px;
    color: var(--text-secondary);
    pointer-events: none;
    transition: color .15s ease;
}

.navbar-search-input {
    width: 100%;
    min-width: 0;
    height: 34px;
    padding: 0 12px 0 33px;
    background: var(--bg-primary);
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--text-primary);
    font: inherit;
    font-size: 14px;
    outline: none;
    transition: border-color .15s ease, background .15s ease;
}

.navbar-search-input::placeholder { color: var(--text-secondary); }

.navbar-search-input:focus {
    border-color: var(--accent);
    background: var(--bg-secondary);
}

.navbar-search:focus-within .navbar-search-icon { color: var(--accent); }

/* Крестик очистки в WebKit рисуется белым квадратом на тёмном фоне — прячем свой,
   раз поле и так очищается с клавиатуры. */
.navbar-search-input::-webkit-search-cancel-button { display: none; }

/* Пункт меню «Поиск» нужен только там, где поля нет. */
.nav-link-search { display: none; }

@media (max-width: 900px) {
    .navbar-search { display: none; }
    .nav-link-search { display: inline-flex; }
}

/* ── Поле поиска на странице результатов ─────────────────────────────────── */

.search-field {
    position: relative;
    display: flex;
    align-items: center;
    flex: 1;
    min-width: 0;
}

.search-field-icon {
    position: absolute;
    left: 12px;
    color: var(--text-secondary);
    pointer-events: none;
}

.search-field .search-input {
    padding-left: 38px;
}

/* ── Насыщенные пустые состояния ─────────────────────────────────────────────
   Раньше пустой результат был одной строкой текста посреди пустоты. */

.empty-state-rich {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    padding: 40px 20px;
}

.empty-state-art {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 96px;
    height: 96px;
    border-radius: 50%;
    color: var(--accent);
    /* Мягкое свечение вместо плоского круга: даёт глубину, но не спорит с текстом. */
    background: radial-gradient(circle at 50% 40%,
                rgba(255, 95, 31, 0.16), rgba(255, 95, 31, 0.03) 65%, transparent 72%);
    margin-bottom: 4px;
    animation: emptyStateIn .35s ease-out both;
}

.empty-state-title {
    margin: 0;
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--text-primary);
}

.empty-state-tags {
    justify-content: center;
    margin-top: 8px;
}

@keyframes emptyStateIn {
    from { opacity: 0; transform: scale(.92); }
    to   { opacity: 1; transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
    .empty-state-art { animation: none; }
}
/* ── Редизайн: глубина, движение, насыщенность ────────────────────────────────
   Модуль последний по нумерации намеренно: он перекрывает более ранние правила
   поверхностей и кнопок. Порядок склейки здесь несущий.

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

/* ── Карточки и панели ──────────────────────────────────────────────────────
   Один общий приём: тень для отрыва от фона + подсветка верхней кромки, которая
   читается как свет сверху. Плоские заливки одного тона выглядели как разметка
   макета, а не как поверхности. */
.post,
.settings-card,
.feed-suggestions,
.result-card,
.invite-row,
.admin-user-card,
.onboarding-card,
.ticket-card {
    position: relative;
    background-image: var(--grad-surface);
    box-shadow: var(--shadow-md);
    transition: border-color var(--dur) var(--ease),
                box-shadow var(--dur) var(--ease),
                transform var(--dur) var(--ease);
}

.post:hover,
.result-card:hover,
.invite-row:hover {
    border-color: var(--border-strong);
    box-shadow: var(--shadow-lg);
}

/* ── Кнопки ─────────────────────────────────────────────────────────────────
   Главная кнопка получает градиент и подсветку: раньше основное и второстепенное
   действие различались только цветом заливки и на тёмном фоне сливались. */
.btn {
    transition: background var(--dur) var(--ease),
                border-color var(--dur) var(--ease),
                box-shadow var(--dur) var(--ease),
                transform var(--dur-fast) var(--ease);
}

.btn-accent {
    background: var(--grad-accent);
    border: none;
    box-shadow: var(--shadow-accent);
}

.btn-accent:hover:not(:disabled) {
    box-shadow: 0 8px 26px rgba(255, 107, 44, 0.32);
    transform: translateY(-1px);
}

/* Нажатие возвращает кнопку на место — короткий отклик вместо «мёртвого» клика. */
.btn:active:not(:disabled) { transform: translateY(0) scale(0.985); }

.btn:disabled { opacity: 0.55; box-shadow: none; }

/* ── Навбар ─────────────────────────────────────────────────────────────────
   Тонкая линия света под шапкой вместо жёсткой границы. */
.navbar {
    box-shadow: var(--shadow-sm);
    border-bottom: 1px solid var(--border);
}

.navbar::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 1px;
    background: linear-gradient(90deg,
                transparent, var(--border-accent) 22%, var(--border-accent) 78%, transparent);
    pointer-events: none;
}

.brand-link {
    background: var(--grad-accent);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    letter-spacing: 0.5px;
}

/* Подчёркивание пункта меню растёт из центра, а не появляется целиком. */
.nav-link {
    position: relative;
    transition: color var(--dur) var(--ease);
}

.nav-link::after {
    content: '';
    position: absolute;
    left: 50%;
    right: 50%;
    bottom: 2px;
    height: 2px;
    border-radius: 2px;
    background: var(--accent);
    transition: left var(--dur) var(--ease), right var(--dur) var(--ease);
}

.nav-link:hover::after { left: 12%; right: 12%; }

/* ── Аватары ────────────────────────────────────────────────────────────────
   Буквенные заглушки были плоскими серыми кругами — теперь у них тот же
   «звёздный» градиент, что у логотипа. */
.post-avatar-initial,
.result-card-avatar-empty,
.nav-profile-avatar {
    background: var(--grad-accent);
    color: #fff;
    font-weight: 600;
    border: none;
}

/* ── Появление списков ──────────────────────────────────────────────────────
   Карточки въезжают короткой волной. Задержка задана только первым шести:
   дальше эффект не читается, а ждать появления контента никто не должен. */
@keyframes cardIn {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}

.feed-container .post,
.result-list .result-card {
    animation: cardIn var(--dur-slow) var(--ease-out) both;
}

.feed-container .post:nth-child(1) { animation-delay: 0ms; }
.feed-container .post:nth-child(2) { animation-delay: 40ms; }
.feed-container .post:nth-child(3) { animation-delay: 80ms; }
.feed-container .post:nth-child(4) { animation-delay: 120ms; }
.feed-container .post:nth-child(5) { animation-delay: 160ms; }
.feed-container .post:nth-child(n+6) { animation-delay: 200ms; }

/* ── Вкладки ────────────────────────────────────────────────────────────────
   Активная вкладка получает свечение под подчёркиванием. */
.feed-tab.active {
    text-shadow: 0 0 18px var(--accent-glow);
}

.feed-tab::after { content: none; }

/* ── Модальные окна и меню ──────────────────────────────────────────────────
   Появление со смещением и размытый фон вместо мгновенной подмены. */
.attach-dialog-overlay {
    backdrop-filter: blur(3px);
    animation: overlayIn var(--dur) var(--ease) both;
}

.attach-dialog {
    background-image: var(--grad-surface);
    box-shadow: var(--shadow-lg);
    animation: dialogIn var(--dur-slow) var(--ease-out) both;
}

.msg-menu,
.share-menu {
    background-image: var(--grad-surface);
    box-shadow: var(--shadow-lg);
    animation: menuIn var(--dur-fast) var(--ease-out) both;
}

@keyframes overlayIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@keyframes dialogIn {
    from { opacity: 0; transform: translateY(12px) scale(0.98); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes menuIn {
    from { opacity: 0; transform: translateY(-4px) scale(0.98); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}

/* ── Мессенджер ─────────────────────────────────────────────────────────────
   Свои сообщения были залиты чистым оранжевым (#FF5F1F → #FF7F3F) с белым текстом.
   Измерено: 2.84:1 при норме AA 4.5:1 — на ярком оранжевом белый текст читается
   плохо в принципе, поэтому мессенджеры и не делают такие пузыри.

   Решение сохраняет опознание с одного взгляда, но меняет способ: свои — тёплые
   тёмные с акцентной кромкой и мягким свечением, чужие — холодные тёмные.
   Тёплое против холодного различается боковым зрением не хуже заливки, а текст
   становится читаемым. */
.message-bubble.own {
    background: linear-gradient(135deg, #3B2318 0%, #2E1B12 100%);
    border: 1px solid var(--border-accent);
    box-shadow: var(--shadow-sm), inset 0 1px 0 rgba(255, 140, 85, 0.09);
    color: var(--text-primary);
}

.message-bubble.other {
    background: var(--bg-tertiary);
    border: 1px solid var(--border);
    box-shadow: var(--shadow-sm);
}

/* Время и галочки внутри своего пузыря больше не белые по оранжевому. */
.message-bubble.own .message-time,
.message-bubble.own .msg-meta { color: var(--text-secondary); }

.chat-list-item,
.conversation-item {
    transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

/* ── Пустые состояния ───────────────────────────────────────────────────────
   Общий вид для всех: не строка текста, а собранный блок. */
.empty-state {
    border-radius: var(--radius-lg);
    background: var(--grad-glow);
}

/* ── Полосы прокрутки ───────────────────────────────────────────────────────
   Заметны при наведении, почти невидимы в покое. */
::-webkit-scrollbar-thumb {
    background: var(--border-strong);
    border-radius: var(--radius-pill);
    transition: background var(--dur) var(--ease);
}

::-webkit-scrollbar-thumb:hover { background: var(--accent-dim); }

/* ── Уважение к системной настройке ─────────────────────────────────────────
   Всё движение выше — украшение. Кто просил его отключить, получает статику. */
@media (prefers-reduced-motion: reduce) {
    .feed-container .post,
    .result-list .result-card,
    .attach-dialog,
    .attach-dialog-overlay,
    .msg-menu,
    .share-menu {
        animation: none;
    }

    .btn-accent:hover:not(:disabled),
    .btn:active:not(:disabled) { transform: none; }
}

/* ── Выбор звука в настройках ────────────────────────────────────────────── */
.sound-row {
    display: flex;
    gap: 10px;
    align-items: center;
}

.sound-row .form-input { flex: 1; min-width: 0; }

@media (max-width: 640px) {
    /* На узком экране кнопка «Прослушать» выталкивала select за край. */
    .sound-row { flex-wrap: wrap; }
    .sound-row .btn { width: 100%; }
}
/* ── Редизайн: экраны ────────────────────────────────────────────────────────
   Продолжение 19-redesign.css. Тот задал общий язык — поверхности, тени, движение;
   здесь он раскладывается по конкретным экранам.

   Все селекторы сверены с существующей разметкой: в прошлый заход половина правил
   целилась в придуманные классы вроде .post-card и не работала вовсе. */

/* ── Профиль ────────────────────────────────────────────────────────────────
   Шапка профиля была плоской полосой. Теперь у неё своя атмосфера: тёплое
   свечение сверху, как будто на человека падает свет звезды. */
.profile-header,
.profile-info {
    position: relative;
    background-image: var(--grad-surface);
    box-shadow: var(--shadow-md);
}

.profile-stats {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

/* Числа профиля были просто текстом в строку — теперь читаемые плашки. */
.profile-stats > * {
    padding: 8px 14px;
    background: var(--bg-tertiary);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.profile-stats > *:hover {
    border-color: var(--border-accent);
    transform: translateY(-1px);
}

.profile-bio {
    padding: 14px 16px;
    background: var(--bg-primary);
    border-left: 2px solid var(--border-accent);
    border-radius: 0 var(--radius-md) var(--radius-md) 0;
}

.profile-action { transition: transform var(--dur-fast) var(--ease); }
.profile-action:active { transform: scale(0.97); }

/* Форма записи: обводка загорается, когда в неё пишут. */
.post-form {
    background-image: var(--grad-surface);
    box-shadow: var(--shadow-sm);
    transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

.post-form:focus-within {
    border-color: var(--border-accent);
    box-shadow: var(--shadow-md), 0 0 0 3px var(--accent-dim);
}

/* ── Мессенджер ─────────────────────────────────────────────────────────────
   Боковая панель отделена от переписки не рамкой, а сменой глубины. */
.sidebar {
    background: var(--bg-secondary);
    box-shadow: inset -1px 0 0 var(--border);
}

.sidebar-header {
    background-image: var(--grad-surface);
    border-bottom: 1px solid var(--border);
}

.chat-list > * {
    border-radius: var(--radius-md);
    transition: background var(--dur) var(--ease), transform var(--dur-fast) var(--ease);
}

.chat-list > *:hover { background: var(--bg-tertiary); }
.chat-list > *:active { transform: scale(0.995); }

/* Непрочитанные — акцентная плашка со свечением: заметно, но не кричит. */
.unread-badge {
    background: var(--grad-accent);
    box-shadow: 0 2px 10px var(--accent-glow);
    border: none;
    color: #fff;
    font-weight: 600;
}

.online-indicator {
    background: var(--green);
    box-shadow: 0 0 0 2px var(--bg-secondary), 0 0 10px rgba(61, 220, 132, 0.55);
}

/* Поле ввода поднимается над перепиской собственной тенью вместо линии. */
.input-area {
    background: var(--bg-secondary);
    background-image: var(--grad-surface);
    box-shadow: 0 -6px 20px rgba(0, 0, 0, 0.28);
    border-top: 1px solid var(--border);
}

.send-btn {
    background: var(--grad-accent);
    border: none;
    box-shadow: var(--shadow-accent);
    transition: transform var(--dur-fast) var(--ease), box-shadow var(--dur) var(--ease);
}

.send-btn:hover:not(:disabled) { transform: scale(1.06); }
.send-btn:active:not(:disabled) { transform: scale(0.94); }

/* Разделитель дат — плашка поверх переписки, как в мессенджерах. */
.date-divider {
    backdrop-filter: blur(6px);
}

.typing-dots span { background: var(--accent); }

/* Пустой чат: не голая надпись, а приглашение. */
.empty-chat {
    background: var(--grad-glow);
    border-radius: var(--radius-lg);
}

/* ── Медиаплеер ─────────────────────────────────────────────────────────────
   Панель управления всплывает по наведению, а не висит поверх кадра постоянно. */
.vp-root {
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: var(--shadow-md);
}

.vp-controls {
    background: linear-gradient(0deg, rgba(7, 8, 14, 0.92), rgba(7, 8, 14, 0.35) 70%, transparent);
    transition: opacity var(--dur) var(--ease);
}

.vp-progress {
    background: rgba(255, 255, 255, 0.18);
    border-radius: var(--radius-pill);
    transition: height var(--dur-fast) var(--ease);
}

/* Полоса толстеет под курсором — попасть в неё становится проще. */
.vp-root:hover .vp-progress { height: 6px; }

.vp-progress-fill {
    background: var(--grad-accent);
    border-radius: var(--radius-pill);
    box-shadow: 0 0 12px var(--accent-glow);
}

.vp-play-btn,
.vp-speed-btn {
    transition: transform var(--dur-fast) var(--ease), color var(--dur) var(--ease);
}

.vp-play-btn:hover,
.vp-speed-btn:hover { transform: scale(1.12); color: var(--accent); }

.vp-big-play {
    background: rgba(11, 13, 20, 0.55);
    backdrop-filter: blur(4px);
    border: 1px solid var(--border-accent);
    box-shadow: var(--shadow-lg);
    transition: transform var(--dur) var(--ease-out), background var(--dur) var(--ease);
}

.vp-big-play:hover { transform: scale(1.08); background: rgba(255, 107, 44, 0.22); }

.vp-speed-panel {
    background-image: var(--grad-surface);
    box-shadow: var(--shadow-lg);
}

/* ── Звонки ─────────────────────────────────────────────────────────────────
   Экран звонка был плоской заливкой. Теперь глубокий фон со свечением
   и пульсирующая рамка у аватара, пока идёт вызов. */
.call-overlay {
    background:
        radial-gradient(900px 600px at 50% 0%, rgba(255, 107, 44, 0.10), transparent 62%),
        radial-gradient(700px 500px at 80% 100%, rgba(139, 109, 255, 0.09), transparent 60%),
        rgba(7, 8, 14, 0.96);
    backdrop-filter: blur(8px);
}

.call-peer-avatar {
    box-shadow: 0 0 0 2px var(--border-accent), var(--shadow-lg);
}

/* Пульс идёт только во время дозвона: на разговоре он был бы навязчивым. */
.call-incoming .call-peer-avatar,
.call-audio-face .call-peer-avatar {
    animation: callPulse 2.2s var(--ease) infinite;
}

@keyframes callPulse {
    0%, 100% { box-shadow: 0 0 0 2px var(--border-accent), 0 0 0 0 var(--accent-glow); }
    50%      { box-shadow: 0 0 0 2px var(--accent), 0 0 0 18px rgba(255, 107, 44, 0); }
}

.call-btn {
    transition: transform var(--dur-fast) var(--ease), box-shadow var(--dur) var(--ease);
}

.call-btn:hover { transform: scale(1.08); }
.call-btn:active { transform: scale(0.94); }

.call-accept {
    background: linear-gradient(135deg, #4BE38F, #22B463);
    box-shadow: 0 6px 22px rgba(61, 220, 132, 0.3);
}

.call-decline {
    background: linear-gradient(135deg, #FF7A7A, #D93A3A);
    box-shadow: 0 6px 22px rgba(255, 92, 92, 0.3);
}

.call-status-text { color: var(--text-secondary); letter-spacing: 0.3px; }

/* ── Опросы ─────────────────────────────────────────────────────────────────
   Заливка результата ползёт вместо мгновенной подстановки. */
.poll-option-fill {
    background: linear-gradient(90deg, var(--accent-dim), rgba(255, 107, 44, 0.22));
    border-radius: var(--radius-sm);
    transition: width var(--dur-slow) var(--ease-out);
}

.poll-options > * {
    transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}

.poll-options > *:hover { border-color: var(--border-accent); }

/* ── Голосовые ──────────────────────────────────────────────────────────────
   Точка записи пульсирует — видно, что идёт запись, а не зависло. */
.voice-rec-dot {
    background: var(--red);
    animation: recPulse 1.1s var(--ease) infinite;
}

@keyframes recPulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50%      { opacity: 0.45; transform: scale(0.86); }
}

.voice-play {
    background: var(--grad-accent);
    border: none;
    box-shadow: var(--shadow-sm);
}

.voice-wave { opacity: 0.9; }

/* ── Админка ────────────────────────────────────────────────────────────────
   Разделы получают ту же глубину, что и остальные карточки. */
.admin-section-card {
    background-image: var(--grad-surface);
    box-shadow: var(--shadow-md);
    transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

.admin-section-card:hover {
    border-color: var(--border-strong);
    box-shadow: var(--shadow-lg);
}

.admin-banned-badge {
    background: rgba(255, 92, 92, 0.14);
    border: 1px solid rgba(255, 92, 92, 0.35);
    color: var(--red);
}

/* ── Главная ────────────────────────────────────────────────────────────────
   Карточки возможностей приподнимаются и подсвечиваются. */
.feature-card {
    background-image: var(--grad-surface);
    box-shadow: var(--shadow-md);
    transition: transform var(--dur) var(--ease-out),
                border-color var(--dur) var(--ease),
                box-shadow var(--dur) var(--ease);
}

.feature-card:hover {
    transform: translateY(-4px);
    border-color: var(--border-accent);
    box-shadow: var(--shadow-lg), 0 0 40px rgba(255, 107, 44, 0.08);
}

.feature-icon {
    color: var(--accent);
    filter: drop-shadow(0 0 12px var(--accent-glow));
}

.hero-title {
    background: var(--grad-cosmic);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
}

/* ── Формы ──────────────────────────────────────────────────────────────────
   Единый отклик на фокус во всём приложении. */
.form-input {
    transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
                background var(--dur) var(--ease);
}

.form-input:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-dim);
    outline: none;
}

/* ── Уведомления ────────────────────────────────────────────────────────────*/
.bell-button { transition: transform var(--dur-fast) var(--ease); }
.bell-button:hover { transform: scale(1.1); }

/* ── Скелетоны ──────────────────────────────────────────────────────────────
   Блик вместо мигания прозрачностью: читается как загрузка, а не как сбой. */
.skeleton {
    background: linear-gradient(90deg,
                var(--bg-tertiary) 0%, var(--bg-overlay) 50%, var(--bg-tertiary) 100%);
    background-size: 200% 100%;
    animation: skeletonSweep 1.4s ease-in-out infinite;
}

@keyframes skeletonSweep {
    from { background-position: 200% 0; }
    to   { background-position: -200% 0; }
}

@media (prefers-reduced-motion: reduce) {
    .call-incoming .call-peer-avatar,
    .call-audio-face .call-peer-avatar,
    .voice-rec-dot,
    .skeleton { animation: none; }

    .feature-card:hover,
    .send-btn:hover:not(:disabled),
    .call-btn:hover,
    .vp-big-play:hover { transform: none; }
}

/* ── Ширина колонки контента ─────────────────────────────────────────────────
   Лента жила в 700px внутри навбара шириной 1200 — посты выглядели узкой полоской
   с пустыми полями по бокам. 880px заметно крупнее, но всё ещё в пределах рамки
   и в пределах комфортной длины строки: дальше текст начинает читаться тяжелее. */
.feed-container {
    max-width: 880px;
}

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

   Вместо неё — сам заголовок крупнее и с более широким межбуквенным просветом,
   а под ним тонкая линия, которая тает к правому краю. Она отделяет шапку
   страницы от содержимого, но не рубит его пополам. */
.feed-container > h1::before,
.friends-container > h1::before,
.admin-container > h1::before,
.profile-edit-container > h1::before {
    content: none;
}

.feed-container > h1,
.friends-container > h1,
.admin-container > h1,
.profile-edit-container > h1 {
    display: block;
    position: relative;
    font-size: 1.75rem;
    font-weight: 700;
    letter-spacing: -0.2px;
    padding-bottom: 14px;
    margin-bottom: 22px;
}

.feed-container > h1::after,
.friends-container > h1::after,
.admin-container > h1::after,
.profile-edit-container > h1::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 1px;
    background: linear-gradient(90deg, var(--border-accent), var(--border) 35%, transparent);
}

/* ── Крупное поле поиска ─────────────────────────────────────────────────────
   Одно поле вместо связки «мелкий инпут + кнопка Найти». Кнопка дублировала Enter
   и на узком экране разворачивалась во всю ширину, наезжая на поле. */
.search-field-big {
    position: relative;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 8px 0 16px;
    height: 54px;
    background: var(--bg-secondary);
    background-image: var(--grad-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    box-shadow: var(--shadow-md);
    margin-bottom: 24px;
    transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

.search-field-big:focus-within {
    border-color: var(--accent);
    box-shadow: var(--shadow-md), 0 0 0 4px var(--accent-dim);
}

.search-field-big .search-field-icon {
    position: static;
    flex: 0 0 auto;
    color: var(--text-secondary);
    transition: color var(--dur) var(--ease);
}

.search-field-big:focus-within .search-field-icon { color: var(--accent); }

.search-input-big {
    flex: 1;
    min-width: 0;
    height: 100%;
    background: none;
    border: none;
    outline: none;
    color: var(--text-primary);
    font: inherit;
    font-size: 1.05rem;
}

.search-input-big::placeholder { color: var(--text-muted); }

.search-go {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Ровно 38px и никакой ширины в процентах — именно из-за width:100% кнопка
       наезжала на поле на узком экране. */
    width: 38px;
    height: 38px;
    border: none;
    border-radius: 50%;
    background: var(--grad-accent);
    color: #fff;
    cursor: pointer;
    box-shadow: var(--shadow-accent);
    transition: transform var(--dur-fast) var(--ease), box-shadow var(--dur) var(--ease);
}

.search-go:hover:not(:disabled) { transform: scale(1.07); }
.search-go:active:not(:disabled) { transform: scale(0.93); }
.search-go:disabled { opacity: 0.6; cursor: default; }

.search-go-spinner {
    width: 16px;
    height: 16px;
    border: 2px solid rgba(255, 255, 255, 0.35);
    border-top-color: #fff;
    border-radius: 50%;
    animation: searchSpin 0.7s linear infinite;
}

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

/* Прежнее правило разворачивало кнопку во всю ширину на узком экране.
   Новая раскладка этого не требует: поле и так во всю ширину, а кнопка внутри него. */
@media (max-width: 640px) {
    .search-bar { flex-direction: row; }
    .search-bar .btn { width: auto; }

    .feed-container { padding: 16px 12px; }
    .feed-container > h1,
    .friends-container > h1,
    .admin-container > h1 { font-size: 1.45rem; }

    .search-field-big { height: 48px; padding-left: 14px; }
    .search-input-big { font-size: 1rem; }
}

@media (prefers-reduced-motion: reduce) {
    .search-go-spinner { animation-duration: 2s; }
    .search-go:hover:not(:disabled) { transform: none; }
}
/* ── Редизайн: второстепенные экраны ─────────────────────────────────────────
   Друзья, онбординг, юридические страницы и подстраницы админки получили от
   19/20 только общий слой — палитру и тени. Здесь у них появляется собственная
   проработка.

   Классы собраны из разметки этих страниц, а не придуманы. */

/* ── Друзья ─────────────────────────────────────────────────────────────────
   Карточка человека была плоской строкой. Теперь это карточка с глубиной,
   которая отзывается на наведение и подсвечивает аватар. */
.user-card {
    position: relative;
    background-image: var(--grad-surface);
    box-shadow: var(--shadow-md);
    border-radius: var(--radius-lg);
    transition: border-color var(--dur) var(--ease),
                box-shadow var(--dur) var(--ease),
                transform var(--dur) var(--ease);
}

.user-card:hover {
    border-color: var(--border-accent);
    box-shadow: var(--shadow-lg);
    transform: translateY(-2px);
}

.user-avatar {
    box-shadow: 0 0 0 2px var(--border), var(--shadow-sm);
    transition: box-shadow var(--dur) var(--ease);
}

.user-card:hover .user-avatar {
    box-shadow: 0 0 0 2px var(--border-accent), 0 0 18px var(--accent-dim);
}

.user-card-link { color: inherit; text-decoration: none; }
.username { font-weight: 600; }

/* Заголовок раздела со счётчиком: линия тянется от текста до края,
   заполняя пустоту вместо голого заголовка над списком. */
.section-title {
    display: flex;
    align-items: center;
    gap: 12px;
    font-size: 1.05rem;
    color: var(--text-secondary);
    margin: 26px 0 14px;
}

.section-title::after {
    content: '';
    flex: 1;
    height: 1px;
    background: linear-gradient(90deg, var(--border), transparent);
}

.section + .section { margin-top: 8px; }

.friends-container .search-input {
    height: 44px;
    border-radius: var(--radius-pill);
    padding-left: 16px;
}

/* Действия в карточке не прыгают при наведении — только откликаются нажатием. */
.user-card .actions .btn { transition: transform var(--dur-fast) var(--ease); }
.user-card .actions .btn:active { transform: scale(0.96); }

/* ── Онбординг ──────────────────────────────────────────────────────────────
   Первый экран новичка. Ему нужно ощущение, что всё идёт по плану, поэтому
   акцент на прогрессе. */
.onboarding-card {
    background-image: var(--grad-surface);
    box-shadow: var(--shadow-lg);
    /* Мягкое свечение сверху — карточка выглядит освещённой, а не вырезанной. */
    position: relative;
}

.onboarding-card::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: var(--grad-glow);
    pointer-events: none;
    opacity: 0.7;
}

.onboarding-card > * { position: relative; }

.onboarding-dot {
    transition: background var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

.onboarding-dot.done {
    background: var(--grad-accent);
    box-shadow: 0 0 12px var(--accent-glow);
}

.onboarding-title {
    background: var(--grad-cosmic);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
}

.onboarding-avatar-empty {
    background: var(--grad-accent);
    color: #fff;
    border: none;
}

.onboarding-upload {
    transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}

.onboarding-upload:hover { border-color: var(--accent); }

/* Строка подсказки-подписки на человека — та же карточка, что в ленте. */
.suggestion-row {
    border-radius: var(--radius-md);
    padding-left: 10px;
    padding-right: 10px;
    transition: background var(--dur) var(--ease);
}

.suggestion-row:hover { background: var(--bg-tertiary); }

/* ── Юридические страницы ───────────────────────────────────────────────────
   Сплошная простыня текста читалась тяжело: узкая колонка, крупнее строка,
   заметные заголовки разделов. */
.legal-page {
    max-width: 760px;
    margin: 0 auto;
    padding: 32px 20px 56px;
    line-height: 1.75;
}

.legal-page h1 {
    font-size: 1.9rem;
    margin-bottom: 8px;
    letter-spacing: -0.3px;
}

.legal-page h2 {
    font-size: 1.15rem;
    margin: 32px 0 10px;
    padding-top: 20px;
    border-top: 1px solid var(--border);
    color: var(--text-primary);
}

.legal-page h2:first-of-type { border-top: none; padding-top: 0; }

.legal-page p { margin-bottom: 14px; color: var(--text-secondary); }
.legal-page li { margin-bottom: 8px; color: var(--text-secondary); }
.legal-page strong { color: var(--text-primary); }

/* Дата редакции — плашка, а не строчка мелким шрифтом. */
.legal-meta {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 6px 14px;
    margin-bottom: 28px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    font-size: 0.85rem;
    color: var(--text-secondary);
}

.legal-back {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 20px;
    color: var(--text-secondary);
    transition: color var(--dur) var(--ease), gap var(--dur) var(--ease);
}

.legal-back:hover { color: var(--accent); gap: 10px; }

/* ── Админка: сводка ────────────────────────────────────────────────────────
   Плитки со счётчиками — главное на странице, поэтому им и глубина, и цифра
   градиентом. */
.dash-tile {
    position: relative;
    background-image: var(--grad-surface);
    box-shadow: var(--shadow-md);
    border-radius: var(--radius-lg);
    overflow: hidden;
    transition: border-color var(--dur) var(--ease),
                box-shadow var(--dur) var(--ease),
                transform var(--dur) var(--ease);
}

.dash-tile:hover {
    border-color: var(--border-accent);
    box-shadow: var(--shadow-lg);
    transform: translateY(-2px);
}

/* Тонкая акцентная полоса сверху — отличает плитку от обычной карточки. */
.dash-tile::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 2px;
    background: var(--grad-accent);
    opacity: 0.75;
}

.dash-tile-value {
    background: var(--grad-accent);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    font-weight: 700;
}

.dash-tile-label { color: var(--text-secondary); }

.dash-card,
.dash-card-wide {
    background-image: var(--grad-surface);
    box-shadow: var(--shadow-md);
    transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

.dash-card:hover,
.dash-card-wide:hover {
    border-color: var(--border-strong);
    box-shadow: var(--shadow-lg);
}

.dash-range-btn {
    transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
                border-color var(--dur) var(--ease);
}

.dash-range-btn:hover { border-color: var(--border-accent); color: var(--text-primary); }

.dash-media-item {
    border-radius: var(--radius-md);
    overflow: hidden;
    transition: transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease);
}

.dash-media-item:hover { transform: scale(1.03); box-shadow: var(--shadow-lg); }

.dash-demo-badge {
    background: rgba(255, 197, 66, 0.14);
    border: 1px solid rgba(255, 197, 66, 0.35);
    color: var(--gold);
    border-radius: var(--radius-pill);
}

/* ── Админка: меню ──────────────────────────────────────────────────────────
   Пункты разъезжаются вправо при наведении — понятно, что это переход. */
.admin-menu-item {
    background-image: var(--grad-surface);
    box-shadow: var(--shadow-sm);
    transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
                transform var(--dur) var(--ease);
}

.admin-menu-item:hover {
    border-color: var(--border-accent);
    box-shadow: var(--shadow-md);
    transform: translateX(3px);
}

.admin-menu-icon { color: var(--accent); }

.admin-back-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--text-secondary);
    transition: color var(--dur) var(--ease), gap var(--dur) var(--ease);
}

.admin-back-link:hover { color: var(--accent); gap: 10px; }

/* ── Админка: обращения ─────────────────────────────────────────────────────*/
.report-card {
    background-image: var(--grad-surface);
    box-shadow: var(--shadow-md);
    transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

.report-card:hover { border-color: var(--border-strong); box-shadow: var(--shadow-lg); }

.ticket-status-badge,
.ticket-kind-badge {
    border-radius: var(--radius-pill);
    font-weight: 600;
    letter-spacing: 0.2px;
}

.reports-filter {
    border-radius: var(--radius-pill);
    transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}

.reports-filter:hover { border-color: var(--border-accent); }

/* Свои сообщения в мини-чате поддержки — тем же приёмом, что и в мессенджере:
   тёплое тёмное вместо заливки акцентом, иначе белый текст на оранжевом. */
.report-chat-msg { border-radius: var(--radius-md); }

.auto-flag-row {
    border-radius: var(--radius-md);
    transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.auto-flag-row:hover { background: var(--bg-tertiary); border-color: var(--border-accent); }

/* ── Админка: люди ──────────────────────────────────────────────────────────*/
.admin-user-card-avatar {
    box-shadow: 0 0 0 2px var(--border), var(--shadow-sm);
}

.user-flag {
    border-radius: var(--radius-pill);
    font-weight: 600;
    font-size: 0.75rem;
    letter-spacing: 0.2px;
}

.user-flag-admin {
    background: var(--accent-dim);
    border: 1px solid var(--border-accent);
    color: var(--accent);
}

.user-flag-banned {
    background: rgba(255, 92, 92, 0.14);
    border: 1px solid rgba(255, 92, 92, 0.35);
    color: var(--red);
}

.admin-user-card-uid code {
    background: var(--bg-primary);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 2px 6px;
}

/* ── Пустые состояния на этих экранах ───────────────────────────────────────*/
.friends-container .empty-state,
.admin-container .empty-state,
.admin-container-wide .empty-state {
    padding: 36px 20px;
    text-align: center;
}

.empty-text { color: var(--text-secondary); }

@media (max-width: 640px) {
    .legal-page { padding: 20px 14px 40px; }
    .legal-page h1 { font-size: 1.5rem; }
    .user-card:hover,
    .dash-tile:hover { transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .user-card:hover,
    .dash-tile:hover,
    .dash-media-item:hover,
    .admin-menu-item:hover { transform: none; }

    .legal-back:hover,
    .admin-back-link:hover { gap: 6px; }
}

/* ── Состояние TURN в админке ────────────────────────────────────────────────
   Отдельный блок, а не строка в таблице: это первое, куда смотрят, когда жалуются
   на зависший звонок. */
.turn-status { margin-bottom: 18px; }

.turn-verdict {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 6px 14px;
    border-radius: var(--radius-pill);
    font-weight: 600;
    font-size: 0.9rem;
    margin: 4px 0 10px;
}

.turn-verdict::before {
    content: '';
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: currentColor;
    box-shadow: 0 0 10px currentColor;
}

.turn-ok {
    background: rgba(61, 220, 132, 0.12);
    border: 1px solid rgba(61, 220, 132, 0.35);
    color: var(--green);
}

.turn-bad {
    background: rgba(255, 92, 92, 0.12);
    border: 1px solid rgba(255, 92, 92, 0.35);
    color: var(--red);
}

.turn-endpoints {
    list-style: none;
    margin: 0 0 10px;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.turn-endpoints li {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    padding: 8px 12px;
    background: var(--bg-primary);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    font-size: 0.88rem;
}

.turn-ep-ok { border-left: 2px solid var(--green); }
.turn-ep-bad { border-left: 2px solid var(--red); }

.turn-ep-transport {
    padding: 1px 8px;
    border-radius: var(--radius-pill);
    background: var(--bg-tertiary);
    color: var(--text-secondary);
    font-size: 0.78rem;
    text-transform: uppercase;
}

.turn-ep-state { margin-left: auto; color: var(--text-secondary); }

.turn-hint {
    margin: 0;
    color: var(--text-secondary);
    font-size: 0.85rem;
    line-height: 1.55;
}

/* ── Профиль: убрать двойную тень ────────────────────────────────────────────
   В 20-redesign-screens.css одинаковая тень и градиент достались и .profile-header,
   и вложенному .profile-info. Вложенный блок получал их вторым слоем и выпирал
   на фоне родителя как отдельная карточка. Внутренний блок делаем плоским. */
.profile-header .profile-info {
    background-image: none;
    box-shadow: none;
    background-color: transparent;
    border: none;
}

/* ─── Список устройств в настройках аккаунта ─── */
/* Строка на приложение, вошедшее по токену. Браузеров здесь нет: их сессия живёт в куке,
   и перечислить её нечем. */
.device-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 16px;
}
.device-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
}
.device-icon {
    display: flex;
    color: var(--text-secondary);
    flex-shrink: 0;
}
.device-info {
    flex: 1;
    min-width: 0;
}
.device-name {
    font-weight: 600;
    /* Имя приходит от клиента, поэтому длинное не должно ломать строку. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.device-meta {
    font-size: 12px;
    color: var(--text-secondary);
}
.device-revoke {
    flex-shrink: 0;
}

@media (max-width: 560px) {
    .device-row { flex-wrap: wrap; }
    .device-revoke { width: 100%; }
}

/* ─── Токен бота ─── */
/* Показывается ровно один раз, поэтому выделен заметно: пропустить его — значит потерять
   доступ к собственному боту. */
.bot-token-box {
    margin: 12px 0 16px;
    padding: 12px;
    background: color-mix(in srgb, var(--accent) 10%, var(--bg-secondary));
    border: 1px solid var(--accent);
    border-radius: var(--radius-md);
}
.bot-token-title {
    display: flex;
    align-items: center;
    gap: 6px;
    font-weight: 600;
    margin-bottom: 8px;
    color: var(--accent-strong);
}
.bot-token-value {
    display: block;
    padding: 8px 10px;
    background: var(--bg-void);
    border-radius: var(--radius-sm);
    font-family: ui-monospace, "Cascadia Code", Consolas, monospace;
    font-size: 13px;
    /* Токен длинный и без пробелов — без переноса он растянул бы карточку. */
    word-break: break-all;
    margin-bottom: 8px;
}
/* ─── Голосовая комната беседы ─── */
/* Префикс .voice-room, а НЕ .voice-bar. Так уже вышло: полоса комнаты звалась .voice-bar, и
   этим же именем в 13-polls-voice-calls.css названы столбики визуализации голосового сообщения.
   Этот файл склеивается позже, поэтому его правила побеждали — каждый столбик получал
   padding: 6px 12px, визуализация вылезала далеко за пузырь и чат начинал прокручиваться вбок. */
/* Полоса живёт между шапкой чата и закреплённым сообщением. Она узкая нарочно: комната —
   постоянный фон беседы, а не событие, и не должна отъедать высоту у переписки. */

.voice-room {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 12px;
    background: var(--bg-secondary);
    border-bottom: 1px solid var(--border);
    font-size: 13px;
    /* Список участников длиннее полосы не растягивает её, а прокручивается: иначе беседа на
       десять говорящих сдвигала бы кнопки за край экрана. */
    overflow: hidden;
}

/* Когда сидишь внутри — полоса подсвечена: перепутать «слышу их» и «просто вижу, что там
   кто-то есть» слишком легко, а цена ошибки — говорить в выключенный микрофон. */
.voice-room-active {
    background: color-mix(in srgb, var(--accent) 12%, var(--bg-secondary));
    border-bottom-color: var(--accent);
}

.voice-room-icon {
    display: flex;
    color: var(--text-muted);
    flex: 0 0 auto;
}

.voice-room-active .voice-room-icon { color: var(--accent-strong); }

.voice-room-people {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 1 1 auto;
    min-width: 0;
    overflow-x: auto;
    scrollbar-width: none;
}

.voice-room-people::-webkit-scrollbar { display: none; }

.voice-room-empty {
    color: var(--text-muted);
    white-space: nowrap;
}

.voice-peer {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--bg-void);
    /* Прозрачная рамка держит место заранее: без неё начало речи дёргало бы всю полосу. */
    border: 1px solid transparent;
    white-space: nowrap;
    flex: 0 0 auto;
}

.voice-peer-speaking {
    border-color: var(--accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent);
}

.voice-peer-name {
    max-width: 140px;
    overflow: hidden;
    text-overflow: ellipsis;
}

.voice-peer-muted { display: flex; color: var(--text-muted); }

.voice-peer-btn {
    display: flex;
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    color: var(--text-muted);
}

.voice-peer-btn:hover { color: var(--text-primary); }

.voice-room-error {
    color: var(--red);
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Итог «позвать всех». Не красным: это не ошибка, а отчёт о том, скольких удалось позвать. */
.voice-room-notice {
    color: var(--text-muted);
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.voice-room-btn {
    display: flex;
    align-items: center;
    gap: 5px;
    padding: 4px 10px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--bg-void);
    color: var(--text-primary);
    cursor: pointer;
    flex: 0 0 auto;
}

.voice-room-btn:hover { border-color: var(--accent); }
.voice-room-btn:disabled { opacity: .6; cursor: default; }

.voice-room-join {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}

.voice-room-btn-off { color: var(--text-muted); }

.voice-room-leave {
    color: var(--red);
    border-color: color-mix(in srgb, var(--red) 40%, transparent);
}

/* ─── Приглашение в разговор ─── */
/* Всплывает поверх страницы, потому что позвать могут в любой момент и на любом экране.
   Внизу справа, а не по центру: перекрывать собой то, что человек читает, приглашение
   не должно — в отличие от настоящего звонка, оно не требует немедленного ответа. */

.voice-ring {
    position: fixed;
    right: 16px;
    bottom: 16px;
    z-index: 900;
    display: flex;
    align-items: center;
    gap: 10px;
    max-width: calc(100vw - 32px);
    padding: 12px 14px;
    background: var(--bg-secondary);
    border: 1px solid var(--accent);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
}

.voice-ring-icon { display: flex; color: var(--accent-strong); flex: 0 0 auto; }

.voice-ring-body { min-width: 0; flex: 1 1 auto; }

.voice-ring-who {
    font-weight: 600;
    overflow-wrap: anywhere;
}

.voice-ring-where {
    color: var(--text-muted);
    font-size: 13px;
    overflow-wrap: anywhere;
}

/* Ширины здесь больше не задаём: width: 100% убран из .btn-accent и переехал в .btn-block. */
.voice-ring-join { flex: 0 0 auto; }

.voice-ring-close {
    flex: 0 0 auto;
    display: flex;
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    color: var(--text-muted);
}

.voice-ring-close:hover { color: var(--text-primary); }

/* На узком экране имена съедают всю полосу — сокращаем их, они важнее всего теряют от длины. */
@media (max-width: 600px) {
    .voice-peer-name { max-width: 70px; }

    /* Приглашение растягивается на всю ширину и переносит кнопку под текст: рядом они
       оставляли бы от названия беседы два слога. */
    .voice-ring {
        left: 16px;
        right: 16px;
        flex-wrap: wrap;
    }

    .voice-ring-join { flex: 1 1 100%; }

    /* Надпись у «позвать всех» на телефоне не помещается — остаётся колокольчик.
       Подпись при этом никуда не девается: она в title. */
    .voice-room-ring-text { display: none; }

    /* Причина неудачи НЕ прячется. Раньше здесь стояло display: none — «чтобы не распирало
       полосу», — и на телефоне человек жал «Войти», ничего не происходило, и объяснения не
       было вовсе. Вместо этого полоса переносится, а текст занимает свою строку целиком. */
    .voice-room { flex-wrap: wrap; }
    .voice-room-error,
    .voice-room-notice {
        order: 10;
        flex: 1 1 100%;
        white-space: normal;
    }
}
/* ─── Пространства ─── */
/* Префиксы .space-* и .spaces-*. Ни одно из этих имён больше нигде не встречается — проверено
   перед тем, как писать: столкновение .voice-bar с полосами голосового сообщения обошлось в
   отдельный разбор, повторять не хочется. */

.spaces-page,
.space-page {
    max-width: 900px;
    margin: 0 auto;
    padding: 24px 16px 48px;
}

.spaces-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}

.spaces-lead {
    color: var(--text-muted);
    margin: 4px 0 20px;
}

.space-create {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 16px;
    margin-bottom: 20px;
}

/* ─── Список ─── */

.space-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.space-card {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 12px 14px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    text-decoration: none;
    color: inherit;
    transition: border-color .12s;
}

.space-card:hover { border-color: var(--accent); }

.space-card-avatar,
.space-header-avatar {
    width: 48px;
    height: 48px;
    border-radius: var(--radius-md);
    object-fit: cover;
    flex: 0 0 auto;
}

.space-header-avatar { width: 64px; height: 64px; }

.space-card-avatar-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    background: color-mix(in srgb, var(--accent) 18%, var(--bg-void));
    color: var(--accent-strong);
    font-size: 20px;
    font-weight: 600;
}

/* Тело карточки обязано уметь сжиматься: без min-width длинное описание распирает строку и
   уводит карточку за край. */
.space-card-body,
.space-header-body {
    flex: 1 1 auto;
    min-width: 0;
}

.space-card-title {
    display: flex;
    align-items: center;
    gap: 6px;
    font-weight: 600;
}

.space-card-lock { display: inline-flex; color: var(--text-muted); }

.space-card-desc,
.space-card-meta {
    color: var(--text-muted);
    font-size: 13px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.space-card-badge {
    flex: 0 0 auto;
    font-size: 12px;
    color: var(--accent-strong);
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    border-radius: 999px;
    padding: 3px 10px;
}

/* ─── Само пространство ─── */

.space-header {
    display: flex;
    align-items: center;
    gap: 16px;
    margin: 12px 0 20px;
    flex-wrap: wrap;
}

.space-header h1 {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0;
}

.space-header-desc {
    color: var(--text-muted);
    margin: 4px 0 0;
}

.space-header-actions {
    display: flex;
    gap: 8px;
    flex: 0 0 auto;
}

.space-member-avatar {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    object-fit: cover;
    flex: 0 0 auto;
}

.space-invite {
    margin-top: 20px;
    padding-top: 16px;
    border-top: 1px solid var(--border);
}

/* ─── Каналы ─── */

.channel-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-bottom: 14px;
}

.channel-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 10px;
    border-radius: var(--radius-sm);
    background: var(--bg-void);
}

/* Текстовый канал открывается щелчком, поэтому курсор обязан на это намекать: строка,
   которая реагирует на нажатие, но выглядит как подпись, просто не будет нажата. */
.channel-row-text { cursor: pointer; }
.channel-row-text:hover { background: var(--bg-secondary); }

/* Открытый канал остаётся выделенным, пока под ним лента: иначе, прокрутив переписку, человек
   теряет из виду, в каком именно канале он читает. */
.channel-row-open {
    background: color-mix(in srgb, var(--accent) 12%, var(--bg-void));
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
}

.channel-icon { display: flex; color: var(--text-muted); flex: 0 0 auto; }

/* Имя канала обязано сжиматься: без min-width длинное название распирает строку и уводит
   кнопку удаления за край. */
.channel-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.channel-del {
    flex: 0 0 auto;
    display: flex;
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    color: var(--text-muted);
}

.channel-del:hover { color: var(--red); }
.channel-del:disabled { opacity: .5; cursor: default; }

.channel-create {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
}

.channel-create .form-input { flex: 1 1 160px; min-width: 0; }
.channel-create select.form-input { flex: 0 0 auto; }

/* ─── Переписка текстового канала ─── */
/* Лента раскрывается прямо под строкой канала и продолжает её визуально — отсюда отсутствие
   верхних скруглений и общий фон с .channel-row-open. */

.channel-chat {
    display: flex;
    flex-direction: column;
    background: var(--bg-void);
    border: 1px solid var(--border);
    border-top: none;
    border-radius: 0 0 var(--radius-sm) var(--radius-sm);
    margin-bottom: 4px;
    /* Длинное слово или ссылка в сообщении не должны распирать ленту вширь: страница
       пространства начала бы прокручиваться вбок целиком. */
    min-width: 0;
    overflow: hidden;
}

/* Осталось только для «загрузка» и «здесь пока ничего не написали»: настоящую ленту рисует
   MessageList, и её высоту задаёт правило .channel-chat .messages-container ниже. */
.channel-chat-log {
    padding: 12px;
}

.channel-chat-empty {
    color: var(--text-muted);
    margin: 0;
}

/* Своей разметки сообщений у канала больше нет: их рисует общая лента MessageList, и все
   .message-* / .msg-* правила приходят из модулей мессенджера. Здесь остаётся только рамка
   вокруг неё и строка ввода. */

/* Лента внутри канала прокручивается сама и не распирает страницу вширь. */
.channel-chat .messages-container {
    max-height: 420px;
    overflow-y: auto;
    min-width: 0;
}

.channel-chat-error {
    color: var(--red);
    padding: 0 12px 8px;
    overflow-wrap: anywhere;
}

/* На телефоне заголовок с кнопками не помещается в строку — пусть переносится, а кнопки
   занимают всю ширину, иначе в них не попасть пальцем. */
@media (max-width: 600px) {
    .channel-create .form-input,
    .channel-create .btn { flex: 1 1 100%; }
    .space-header-actions { width: 100%; }
    .space-header-actions .btn { flex: 1 1 auto; }
    .space-card-badge { display: none; }

    /* Лента ниже: на экране телефона она и так занимает почти всё, а 420px оставляли бы
       под переписку две строки. */
    .channel-chat .messages-container { max-height: 55vh; }
}
/* ─── Аватары там, где раньше была только буква ─── */
/* Модуль идёт последним намеренно: размеры и форму кружка задают более ранние правила
   (.nav-profile-avatar в 01 и 19, .call-peer-avatar в 13 и 20), и здесь нужно только заменить
   фон с инициалом на саму картинку, ничего из этого не переопределяя. */

/* Картинка ложится в тот же кружок: размеры наследуются от базового правила, а фон и типографика
   инициала убираются — иначе за прозрачными краями просвечивал бы цветной кружок. */
.nav-profile-avatar-img,
.call-peer-avatar-img {
    object-fit: cover;
    background: none;
    /* Картинка — не текст: отступы и выравнивание инициала ей только мешают. */
    padding: 0;
    line-height: 0;
}
/* ─── Игроки Minecraft в панели управления ─── */
/* Префикс .mc-* — ни одно из этих имён больше нигде не встречается, проверено перед тем,
   как писать: столкновение .voice-bar с полосами голосового сообщения обошлось в отдельный
   разбор, повторять не хочется. */

/* Строка поиска над списком. Отдельный класс, а не .form-group: там сверху отступ под
   подпись, а подписи здесь нет — поле и так объясняет себя плейсхолдером. */
.admin-search-row {
    margin: 14px 0;
}

.admin-search-row .form-input {
    width: 100%;
    max-width: 420px;
}

/* Запрет входа отмечается словами, а не только цветом: цвет не читается теми, кто его
   не различает, и не переживает чёрно-белую печать жалобы. */
.mc-banned {
    margin-left: 8px;
    font-size: 12px;
    font-weight: 500;
    color: var(--red);
    border: 1px solid color-mix(in srgb, var(--red) 40%, transparent);
    border-radius: 999px;
    padding: 1px 8px;
    white-space: nowrap;
}

/* UUID нужен, когда приходят из логов игрового сервера: там игрок именно им и записан.
   Моноширинный — его сверяют глазами посимвольно. */
.mc-uuid {
    font-family: var(--font-mono, monospace);
    font-size: 11px;
    opacity: .7;
    overflow-wrap: anywhere;
}
/* ─── Полноэкранные страницы ─── */
/* Мессенджер и пространства занимают экран целиком: прокручивается их содержимое,
   а не страница.

   Раньше каждая такая страница считала свою высоту сама — calc(100vh - 104px) в
   08-messenger.css, и ещё дважды в 11-responsive и 14-admin-polish. Слагаемых было три:
   навбар, отступы .main-content и нижняя панель на телефоне. Футер в них не попал,
   а он около 138 пикселей, — ровно на них страница и прокручивалась, давая вторую
   полосу прокрутки поверх первой.

   Поэтому арифметики здесь нет вовсе. Высоту задаёт оболочка, страница берёт height: 100%
   и ничего не вычитает. Следующий, кто изменит навбар, ничего не забудет — вычитать
   нечего. */

/* Высота нижней панели вкладок — ОДНО слагаемое на весь проект.
 *
 * Их было три, и они разошлись: панель объявлена в 58 пикселей (11-responsive), на телефонах
 * с вырезом ей добавляется безопасная зона (14-admin-polish), а место под неё резервировалось
 * числом 74 — без этой зоны. На «чёлочном» телефоне панель вырастала до 92, а зарезервировано
 * оставалось 74, и нижние восемнадцать пикселей строки ввода уходили ПОД панель. Ровно это
 * и увидели вживую.
 *
 * Теперь слагаемое одно, и разойтись ему не с чем. */
:root {
    --tabbar-h: 0px;
}

@media (max-width: 768px) {
    :root {
        --tabbar-h: calc(58px + env(safe-area-inset-bottom, 0px));
    }
}

/* Клавиатура открыта — панели нет (её прячет 11-responsive), и резервировать под неё
   нечего: место над клавиатурой нужно строке ввода. */
.kb-open {
    --tabbar-h: 0px;
}

.app-layout-fixed {
    /* Сначала vh — его понимают все; следом dvh, который учитывает выезжающую панель
       браузера; последним --app-vh из viewport.js, который знает ещё и про клавиатуру.
       Без него страница при открытой клавиатуре остаётся «во весь экран», и строка ввода
       уезжает под неё. */
    height: 100vh;
    height: 100dvh;
    height: var(--app-vh, 100dvh);
    /* Именно height, а не min-height: min-height позволил бы содержимому вытолкнуть
       страницу за экран, ради чего всё и затевалось. */
    overflow: hidden;
}

.app-layout-fixed .main-content {
    /* Ширина как у остальных страниц: те же 1200 и те же 24 по бокам, что у .navbar-inner,
       поэтому края переписки приходятся ровно под края шапки. Читать строку во весь
       двадцатисемидюймовый экран невозможно — глазу негде остановиться.

       Прежде здесь стоял max-width: none, и мессенджер расползался на всю ширину. */
    max-width: 1200px;
    margin: 0 auto;
    width: 100%;

    /* Отступы теперь безопасны, и это главное отличие от прежнего устройства. Высоту задаёт
       оболочка, а не сама страница: main-content — это flex-элемент с min-height: 0, поэтому
       padding уменьшает его содержимое, а не выталкивает страницу за экран. Раньше высоту
       считала страница, и любой отступ приходилось вычитать вручную — про футер как раз
       и забыли. */
    padding: 16px 24px;

    /* Без min-height: 0 flex-элемент не даёт содержимому сжаться, и внутренняя прокрутка
       не появляется — вместо неё растёт сам элемент. */
    min-height: 0;
    overflow: hidden;
}

/* Футер прячется только здесь. Ссылка на обработку персональных данных остаётся на всех
   обычных страницах — убрать её со всего сайта было бы неправильно. */
.app-layout-fixed .site-footer {
    display: none;
}

/* На телефоне — во весь экран. Сужать там нечего: ширина и так меньше 1200, а боковые
   отступы отняли бы у переписки те пиксели, которых там и без того мало. */
@media (max-width: 768px) {
    .app-layout-fixed .main-content {
        max-width: none;
        /* Снизу закреплена панель вкладок плюс зазор.

           Зазор обязателен, и это не украшение. Панель полупрозрачная и с размытием
           (backdrop-filter), поэтому кнопка, стоящая вплотную к её краю, читается как
           уехавшая под неё, а попасть по ней пальцем можно только сверху.

           Высота берётся переменной, а не числом. Числом здесь стояло 74 при панели,
           которая на телефонах с вырезом вырастает до 92, — и строка ввода уходила
           под неё нижним краем. */
        padding: 0 0 calc(var(--tabbar-h) + 16px);
    }
}

/* ─── Подгрузка истории вверх ─── */
/* Полоса над лентой, пока догружается предыдущая страница. Видна только во время запроса:
   постоянная надпись у начала переписки означала бы, что она никогда не кончается. */
.messages-older {
    flex-shrink: 0;
    padding: 8px;
    text-align: center;
    font-size: 12px;
    color: var(--text-muted);
}
/* ─── Markdown внутри сообщения ─── */
/* Класс .md ставится там, где текст прошёл через MarkdownHelper. Всё внутри ограничено
   пузырём: сообщение не должно уметь раздвинуть чат.

   Этот урок уже оплачен: дорожка голосового сообщения когда-то распирала пузырь, и весь
   чат начинал прокручиваться вбок. Таблица от нейронки сделала бы то же самое, только
   чаще. */

.message-content.md {
    /* Markdown сам расставляет абзацы, поэтому pre-wrap из обычного сообщения здесь
       не нужен — иначе к отступам блоков добавятся ещё и пустые строки. */
    white-space: normal;
    overflow-wrap: anywhere;
}

.message-content.md > :first-child { margin-top: 0; }
.message-content.md > :last-child { margin-bottom: 0; }

.message-content.md p { margin: 0 0 8px; }

/* Заголовки приводятся к размеру текста с небольшим шагом. Без этого «# Итог» от нейронки
   растягивает пузырь на пол-экрана, а в переписке заголовок — это просто выделенная
   строка, а не титул страницы. */
.message-content.md h1,
.message-content.md h2,
.message-content.md h3,
.message-content.md h4,
.message-content.md h5,
.message-content.md h6 {
    margin: 12px 0 6px;
    font-weight: 700;
    line-height: 1.3;
}

.message-content.md h1 { font-size: 1.25em; }
.message-content.md h2 { font-size: 1.15em; }
.message-content.md h3 { font-size: 1.05em; }
.message-content.md h4,
.message-content.md h5,
.message-content.md h6 { font-size: 1em; }

.message-content.md ul,
.message-content.md ol {
    margin: 0 0 8px;
    /* Отступ внутрь пузыря, а не наружу: маркеры не должны вылезать за его край. */
    padding-left: 22px;
}

.message-content.md li { margin: 2px 0; }

.message-content.md blockquote {
    margin: 8px 0;
    padding-left: 10px;
    border-left: 3px solid var(--border);
    color: var(--text-secondary);
}

.message-content.md code {
    font-family: var(--font-mono, monospace);
    font-size: .9em;
    background: var(--bg-void);
    border-radius: 4px;
    padding: 1px 5px;
}

/* Блок кода прокручивается ВНУТРИ пузыря. Длинная строка кода — обычное дело, и без
   собственной прокрутки она растянула бы сообщение, а с ним и всю ленту. */
.message-content.md pre {
    margin: 8px 0;
    padding: 10px 12px;
    background: var(--bg-void);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    overflow-x: auto;
    max-width: 100%;
}

.message-content.md pre code {
    background: none;
    padding: 0;
    /* Перенос внутри блока кода отключён намеренно: код читают по строкам, и перенесённая
       строка меняет смысл отступов. Вместо переноса — прокрутка выше. */
    white-space: pre;
    overflow-wrap: normal;
}

/* Таблица тоже прокручивается внутри пузыря, а не расширяет его. Обёртку Markdig
   не создаёт, поэтому прокрутку берёт на себя сама таблица через display: block. */
.message-content.md table {
    display: block;
    max-width: 100%;
    overflow-x: auto;
    border-collapse: collapse;
    margin: 8px 0;
    font-size: .95em;
}

.message-content.md th,
.message-content.md td {
    border: 1px solid var(--border);
    padding: 4px 8px;
    text-align: left;
    white-space: nowrap;
}

.message-content.md th { background: var(--bg-void); font-weight: 600; }

.message-content.md a { color: var(--accent-strong); text-decoration: underline; }

.message-content.md hr {
    border: none;
    border-top: 1px solid var(--border);
    margin: 10px 0;
}

/* Картинку из markdown ограничиваем шириной пузыря — иначе она задаёт ему размер. */
.message-content.md img {
    max-width: 100%;
    height: auto;
    border-radius: var(--radius-sm);
}
/* ─── Пространство: три колонки ─── */
/* Каналы — переписка — участники. Устройство привычное по приложениям для игровых
   сообществ, оформление своё.

   Высоту даёт оболочка: MainLayout ставит этой странице .app-layout-fixed (см.
   26-app-shell.css), поэтому здесь height: 100% и никакой арифметики. Колонки
   прокручиваются каждая сама по себе — страница не прокручивается вовсе. */

.space-shell {
    display: flex;
    height: 100%;
    min-height: 0;
    overflow: hidden;
    background: var(--bg-void);
}

/* ─── Экран вместо колонок: загрузка, «не найдено», приглашение войти ─── */

.space-loading,
.space-gate {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 24px;
    text-align: center;
}

.space-gate h1 { margin: 0; }

.space-gate-avatar {
    width: 72px;
    height: 72px;
    border-radius: var(--radius-md);
    object-fit: cover;
    font-size: 28px;
}

/* ─── Колонка каналов ─── */

.space-channels {
    width: 260px;
    min-width: 260px;
    display: flex;
    flex-direction: column;
    background: var(--bg-secondary);
    border-right: 1px solid var(--border);
    min-height: 0;
}

.space-channels-head {
    display: flex;
    align-items: center;
    gap: 8px;
    height: 56px;
    padding: 0 14px;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}

/* Название сжимается, а не выталкивает кнопку настроек за край. */
.space-channels-name {
    flex: 1 1 auto;
    min-width: 0;
    font-weight: 700;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.space-channels-list {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 8px;
    /* Прокрутка списка каналов не уходит на соседние колонки. */
    overscroll-behavior: contain;
}

.space-channels-group {
    padding: 12px 8px 4px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.space-channels-empty {
    padding: 4px 8px 8px;
    font-size: 13px;
    color: var(--text-muted);
}

.space-channel {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: var(--text-secondary);
}

.space-channel:hover { background: var(--bg-void); color: var(--text-primary); }

.space-channel.active {
    background: color-mix(in srgb, var(--accent) 16%, var(--bg-void));
    color: var(--text-primary);
}

.space-channel-icon { display: flex; flex: 0 0 auto; color: var(--text-muted); }
.space-channel.active .space-channel-icon { color: var(--accent-strong); }

.space-channel-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.space-channel-del {
    flex: 0 0 auto;
    display: flex;
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    color: var(--text-muted);
    /* Появляется по наведению: постоянная корзина у каждого канала — это приглашение
       промахнуться. */
    opacity: 0;
}

.space-channel:hover .space-channel-del { opacity: 1; }
.space-channel-del:hover { color: var(--red); }
.space-channel-del:disabled { opacity: .4; cursor: default; }

/* Полоса голосового канала живёт под его названием, с отступом — так видно, что она
   относится именно к нему. */
.space-channel-voice {
    margin: 0 0 4px 22px;
    border-radius: var(--radius-sm);
    overflow: hidden;
}

.space-channels-create {
    flex-shrink: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    padding: 8px;
    border-top: 1px solid var(--border);
}

.space-channels-create .form-input { flex: 1 1 100%; min-width: 0; }
.space-channels-create .btn { flex: 1 1 auto; }

.space-me {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    border-top: 1px solid var(--border);
    background: var(--bg-void);
}

.space-me-name {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 13px;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ─── Середина: переписка ─── */

.space-main {
    flex: 1 1 auto;
    min-width: 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    background: var(--bg-void);
}

.space-main-head {
    display: flex;
    align-items: center;
    gap: 10px;
    height: 56px;
    padding: 0 14px;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}

.space-main-title {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 1 1 auto;
    min-width: 0;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.space-main-message { margin: 10px 14px 0; }

.space-main-empty {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    color: var(--text-muted);
    text-align: center;
    padding: 24px;
}

.space-main-empty p { margin: 0; }

/* Переписка канала занимает всю середину: рамка и скругления от карточного вида здесь
   не нужны — она и есть содержимое колонки. */
.space-main .channel-chat {
    flex: 1 1 auto;
    min-height: 0;
    border: none;
    border-radius: 0;
    margin: 0;
}

.space-main .channel-chat .messages-container {
    /* Высоту даёт колонка, а не max-height: в карточке лента была ограничена 420px,
       здесь ограничивать нечем — она должна занять всё, что осталось. */
    max-height: none;
    flex: 1 1 auto;
    min-height: 0;
}

/* ─── Справа: участники ─── */

.space-members {
    width: 240px;
    min-width: 240px;
    display: flex;
    flex-direction: column;
    background: var(--bg-secondary);
    border-left: 1px solid var(--border);
    min-height: 0;
}

.space-members-head {
    height: 56px;
    display: flex;
    align-items: center;
    padding: 0 14px;
    border-bottom: 1px solid var(--border);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text-muted);
    flex-shrink: 0;
}

.space-members-list {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 8px;
    overscroll-behavior: contain;
}

.space-member {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 8px;
    border-radius: var(--radius-sm);
}

.space-member:hover { background: var(--bg-void); }

.space-member-avatar {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    object-fit: cover;
    flex: 0 0 auto;
    font-size: 14px;
}

.space-member-body { flex: 1 1 auto; min-width: 0; }

.space-member-name {
    font-size: 14px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.space-member-role {
    font-size: 11px;
    color: var(--text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Действия появляются по наведению — как и корзина у канала. */
.space-member-actions { display: flex; gap: 4px; flex: 0 0 auto; opacity: 0; }
.space-member:hover .space-member-actions { opacity: 1; }
.space-member-kick:hover { color: var(--red); }

/* ─── Мелкие кнопки колонок ─── */

.space-head-btn,
.space-panel-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    padding: 4px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: var(--text-muted);
    flex: 0 0 auto;
}

.space-head-btn:hover,
.space-panel-toggle:hover { color: var(--text-primary); background: var(--bg-void); }
.space-head-btn:disabled { opacity: .4; cursor: default; }

/* На широком экране обе колонки видны всегда — переключатели не нужны. */
.space-panel-toggle { display: none; }

.space-settings-dialog { width: min(520px, 94vw); }
.space-settings-body { max-height: 70vh; overflow-y: auto; overscroll-behavior: contain; }

/* ─── Телефон ─── */
/* Трёх колонок на 375 пикселях быть не может: переписка получила бы полосу в сто
   пикселей. Боковые колонки уезжают за край и выдвигаются поверх по кнопке. */
@media (max-width: 900px) {
    .space-panel-toggle { display: flex; }

    .space-channels,
    .space-members {
        position: absolute;
        top: 0;
        bottom: 0;
        z-index: 20;
        width: min(280px, 82vw);
        min-width: 0;
        transition: transform .18s ease;
    }

    .space-shell { position: relative; }

    .space-channels { left: 0; transform: translateX(-100%); }
    .space-members { right: 0; transform: translateX(100%); }

    .space-channels.open,
    .space-members.open {
        transform: translateX(0);
        box-shadow: var(--shadow-lg);
    }
}

/* ─── Оформление пространства: аватарка и шапка ─── */

/* Шапка — над списком каналов, как обложка сообщества. Не над всей оболочкой: высоту
   оболочке даёт макет, и полоса поверх колонок отняла бы её у переписки. Пропорция
   одна и в колонке, и в предпросмотре настроек — что владелец видит, то и получит. */
.space-banner {
    flex-shrink: 0;
    aspect-ratio: 12 / 5;
    overflow: hidden;
    background: var(--bg-void);
    border-bottom: 1px solid var(--border);
}

.space-banner img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.space-channels-avatar {
    width: 28px;
    height: 28px;
    flex: 0 0 auto;
    border-radius: var(--radius-sm, 6px);
    object-fit: cover;
}

.space-gate-banner {
    width: min(480px, 100%);
    aspect-ratio: 12 / 5;
    border-radius: var(--radius-md);
    object-fit: cover;
}

.space-look {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.space-look-title {
    font-weight: 600;
}

.space-look-row {
    display: flex;
    align-items: center;
    gap: 12px;
}

.space-look-avatar {
    width: 56px;
    height: 56px;
    flex: 0 0 auto;
    border-radius: var(--radius-md);
    object-fit: cover;
}

.space-look-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.space-look-banner {
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
}

.space-look-upload {
    cursor: pointer;
}

.space-look-file {
    display: none;
}

/* Кнопка «Загрузить» — это label вокруг скрытого поля файла, и правило .form-group label
   делало её блоком другой высоты, чем «Убрать» рядом. */
.space-look-actions .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 42px;
    margin: 0;
}
/* ─── Вложения переписки ─── */
/* Панель со всем, что присылали: медиа, аудио, файлы. Открывается из меню чата. */

.chat-files { width: min(560px, 94vw); }

.chat-files-tabs {
    display: flex;
    gap: 4px;
    padding: 0 16px;
    border-bottom: 1px solid var(--border);
}

.chat-files-tab {
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    padding: 10px 12px;
    cursor: pointer;
    color: var(--text-muted);
    font-size: 14px;
}

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

.chat-files-tab.active {
    color: var(--text-primary);
    border-bottom-color: var(--accent);
}

.chat-files-body {
    /* Своя прокрутка одна на всю панель — вложенной второй здесь нет намеренно: именно она
       давала заедание в диалоге создания беседы. */
    max-height: 60vh;
    overflow-y: auto;
    overscroll-behavior: contain;
    align-items: stretch;
}

/* ─── Медиа плиткой ─── */

.chat-files-grid {
    display: grid;
    /* auto-fill, а не фиксированное число колонок: на узком экране плиток в ряду меньше,
       но каждая остаётся квадратной и узнаваемой. */
    grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
    gap: 6px;
}

.chat-files-cell {
    position: relative;
    aspect-ratio: 1;
    padding: 0;
    border: none;
    border-radius: var(--radius-sm);
    overflow: hidden;
    background: var(--bg-void);
    cursor: pointer;
}

.chat-files-cell img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.chat-files-cell:hover { outline: 2px solid var(--accent); }

/* Видео плиткой не показывается: браузер тянул бы файл целиком ради первого кадра,
   а их тут могут быть сотни. */
.chat-files-video {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    color: var(--text-muted);
}

/* ─── Аудио и файлы списком ─── */

.chat-files-list { display: flex; flex-direction: column; gap: 2px; }

.chat-files-row {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 8px;
    background: none;
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: inherit;
    text-align: left;
}

.chat-files-row:hover { background: var(--bg-void); }

.chat-files-icon { display: flex; flex: 0 0 auto; color: var(--text-muted); }

/* Имя обязано сжиматься: без min-width длинное имя файла распирает панель. */
.chat-files-body-text { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }

.chat-files-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.chat-files-meta { font-size: 12px; color: var(--text-muted); }

/* ─── Полоса пояснения над лентой ─── */

.chat-notice {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 20px;
    background: color-mix(in srgb, var(--accent) 10%, var(--bg-secondary));
    border-bottom: 1px solid var(--border);
    font-size: 13px;
}

.chat-notice span { flex: 1 1 auto; min-width: 0; }

.chat-notice-close {
    flex: 0 0 auto;
    display: flex;
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    color: var(--text-muted);
}

.chat-notice-close:hover { color: var(--text-primary); }
/* Граница прочитанного и кнопка «вниз».
 *
 * Две вещи про одно и то же ощущение: человек вернулся в переписку, где накопилось новое,
 * и должен понимать, откуда читать и как вернуться к концу. */

/* ─── «Новые сообщения» ─── */

/* Линия во всю ширину с подписью посередине — так же, как разделитель даты, чтобы взгляд
   читал их одинаково. Цвет акцентный: это единственное место в ленте, к которому человека
   нужно подвести. */
.unread-divider {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 14px 0 6px;
    color: var(--accent);
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .02em;
}

.unread-divider::before,
.unread-divider::after {
    content: '';
    flex: 1;
    height: 1px;
    background: color-mix(in srgb, var(--accent) 45%, transparent);
}

.unread-divider span {
    flex-shrink: 0;
    white-space: nowrap;
}

/* ─── Кнопка «вниз» ─── */

/* Нулевая по высоте обёртка: кнопка всплывает над последними сообщениями и ничего не
   сдвигает. Без неё пришлось бы делать position: relative у .chat-window, а это правило
   общее для всей переписки и задевало бы выпадающие меню. */
.chat-scroll-down-holder {
    position: relative;
    height: 0;
    z-index: 4;
    pointer-events: none;
}

.chat-scroll-down {
    position: absolute;
    right: 18px;
    bottom: 12px;
    pointer-events: auto;

    width: 38px;
    height: 38px;
    display: flex;
    align-items: center;
    justify-content: center;

    border: 1px solid var(--border);
    border-radius: 50%;
    background: var(--bg-secondary);
    color: var(--text-secondary);
    cursor: pointer;
    box-shadow: 0 4px 14px rgba(0, 0, 0, .35);
    transition: background .15s, color .15s, transform .15s;
}

.chat-scroll-down:hover {
    background: var(--bg-tertiary, var(--bg-secondary));
    color: var(--text-primary);
    transform: translateY(-1px);
}

/* На телефоне ниже — там под лентой ещё нижняя панель навигации. */
@media (max-width: 768px) {
    .chat-scroll-down {
        right: 12px;
        bottom: 8px;
        width: 34px;
        height: 34px;
    }
}

/* Полоса «сообщение не ушло». Тот же блок, что и обычное уведомление над лентой,
   но красный: это отказ, а не подсказка. */
.chat-notice-error {
    border-color: #7a3b36;
    background: rgba(160, 60, 50, .14);
    color: #f0b8b2;
}
/* Фотоны и игры.
 *
 * Тон намеренно не «казино»: ни золота, ни неона, ни мигающих выигрышей. Затея была
 * в том, чтобы увести людей от настоящих ставок, а не воспроизвести их ощущение
 * поточнее. Поэтому оформление — то же самое, что у остального сайта. */

/* ─── Баланс в шапке ─── */

.photon-chip {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 5px 12px 5px 9px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--bg-secondary);
    color: var(--text-primary);
    font-size: 13px;
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    transition: border-color .15s, background .15s;
}

.photon-chip:hover {
    border-color: var(--accent);
    background: var(--bg-tertiary, var(--bg-secondary));
}

/* Фотон — точка света с ореолом. Рисуется тенями, чтобы не тащить картинку. */
.photon-chip-icon {
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: #ffd97a;
    box-shadow: 0 0 6px 1px rgba(255, 217, 122, .75);
    flex-shrink: 0;
}

.photon-chip-value {
    font-variant-numeric: tabular-nums;
}

/* ─── Страница игр ─── */

.games-page {
    display: flex;
    flex-direction: column;
    gap: 22px;
}

.games-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}

.games-header h1 {
    margin: 0 0 4px;
    font-size: 26px;
}

.games-sub {
    margin: 0;
    max-width: 52ch;
    color: var(--text-secondary);
    font-size: 14px;
    line-height: 1.5;
}

.games-balance {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 2px;
    padding: 10px 16px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--bg-secondary);
}

.games-balance-label {
    color: var(--text-secondary);
    font-size: 12px;
}

.games-balance-value {
    font-size: 18px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* ─── Недельная лестница ─── */

.reward-card {
    padding: 18px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--bg-secondary);
}

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

.reward-head h2 {
    margin: 0;
    font-size: 17px;
}

.reward-badge.done {
    padding: 3px 10px;
    border-radius: 999px;
    background: color-mix(in srgb, var(--accent) 18%, transparent);
    color: var(--accent);
    font-size: 12px;
    font-weight: 600;
}

/* Семь дней в ряд. На узком экране — сетка в две строки, а не прокрутка вбок:
   неделя должна быть видна целиком, в этом весь смысл её показывать. */
.reward-week {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 8px;
}

.reward-day {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 12px 4px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--bg-primary, #0f0f0f);
    text-align: center;
}

.reward-day-num {
    color: var(--text-secondary);
    font-size: 11px;
}

.reward-day-amount {
    font-size: 15px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.reward-day.done {
    border-color: color-mix(in srgb, var(--accent) 40%, var(--border));
    background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.reward-day.today {
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent) inset;
}

.reward-day-mark {
    position: absolute;
    top: 5px;
    right: 6px;
    color: #ffd97a;
    line-height: 0;
}

.reward-hint {
    margin: 14px 0 0;
    color: var(--text-secondary);
    font-size: 13px;
    line-height: 1.5;
}

/* ─── Карточки игр ─── */

.games-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 14px;
}

.game-card {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--bg-secondary);
    overflow: hidden;
    text-decoration: none;
    color: inherit;
    transition: border-color .15s, transform .15s;
}

a.game-card:hover {
    border-color: var(--accent);
    transform: translateY(-2px);
}

.game-card-soon {
    opacity: .55;
}

.game-card-art {
    height: 108px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: radial-gradient(circle at 50% 40%, rgba(255, 255, 255, .06), transparent 70%);
    border-bottom: 1px solid var(--border);
}

/* Колесо: чередующиеся сектора, светлый обод и шарик. Статичное — на карточке крутиться
   нечему, а вот узнаваться должно с одного взгляда. */
.game-art-wheel {
    position: relative;
    width: 72px;
    height: 72px;
    border-radius: 50%;
    background: repeating-conic-gradient(#b5423a 0deg 20deg, #191919 20deg 40deg);
    box-shadow:
        inset 0 0 0 7px #2e2e2e,       /* обод */
        inset 0 0 0 9px #0f0f0f,
        inset 0 0 0 22px #232323,      /* середина */
        0 0 0 2px #3a3a3a;
}

/* Шарик в жёлобе — та деталь, по которой колесо и опознаётся. */
.game-art-wheel::after {
    content: '';
    position: absolute;
    top: 6px;
    left: 50%;
    width: 7px;
    height: 7px;
    margin-left: -3.5px;
    border-radius: 50%;
    background: #f2f2f2;
    box-shadow: 0 0 4px rgba(255, 255, 255, .6);
}

/* Ракета на карточке — та же иконка из общего набора, что и в самой игре. */
.game-art-icon {
    color: #d8dce4;
}

.game-art-rocket .game-art-icon {
    filter: drop-shadow(0 0 10px rgba(255, 160, 70, .35));
}

/* У мин значок рисуется прямо в разметке. Здесь стояли два правила на одном узле —
   размер карточки и размер сетки, — и не выходило ни того, ни другого: на месте
   картинки оставалась пустота. */

.game-card-body {
    padding: 14px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.game-card-body h3 {
    margin: 0;
    font-size: 16px;
}

.game-card-body p {
    margin: 0;
    color: var(--text-secondary);
    font-size: 13px;
    line-height: 1.45;
}

/* Преимущество заведения написано на самой карточке. Это не мелкий шрифт внизу
   страницы: если игра забирает деньги, об этом стоит знать до, а не после. */
.game-card-edge {
    margin-top: 2px;
    color: var(--text-secondary);
    font-size: 11px;
    letter-spacing: .02em;
}

/* Minecraft — не игра на фотоны и не ссылка на игру: карточка ведёт к установщику
   настольного приложения, поэтому она не поднимается при наведении, как соседние. */
.game-art-picture {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.game-card-desktop .game-card-art {
    padding: 0;
    overflow: hidden;
}

.game-card-download {
    align-self: flex-start;
    margin-top: 8px;
    min-height: 36px;
    display: inline-flex;
    align-items: center;
}

.game-card-later,
.game-card-hint {
    margin-top: 6px;
    color: var(--text-secondary);
    font-size: 12px;
    line-height: 1.4;
}

/* ─── Итог и история ─── */

.games-stats,
.games-history {
    padding: 18px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--bg-secondary);
}

.games-stats h2,
.games-history h2 {
    margin: 0 0 14px;
    font-size: 17px;
}

.games-stats-empty {
    margin: 0;
    color: var(--text-secondary);
    font-size: 14px;
}

.stats-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
    gap: 10px;
}

.stat-tile {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 12px 14px;
    border: 1px solid var(--border);
    border-radius: 10px;
}

.stat-label {
    color: var(--text-secondary);
    font-size: 12px;
}

.stat-value {
    font-size: 19px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.stat-negative .stat-value { color: #e06c62; }
.stat-positive .stat-value { color: #5fbf7a; }

.games-stats-note {
    margin: 12px 0 0;
    color: var(--text-secondary);
    font-size: 13px;
    line-height: 1.55;
}

.wallet-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
}

.wallet-item {
    display: grid;
    grid-template-columns: 92px 1fr auto auto;
    align-items: center;
    gap: 12px;
    padding: 9px 0;
    border-bottom: 1px solid var(--border);
    font-size: 13px;
}

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

.wallet-kind {
    color: var(--text-secondary);
    font-size: 12px;
}

.wallet-desc {
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.wallet-amount {
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.wallet-amount.plus { color: #5fbf7a; }
.wallet-amount.minus { color: #e06c62; }

.wallet-time {
    color: var(--text-secondary);
    font-size: 12px;
    white-space: nowrap;
}

/* ─── Телефон ─── */

@media (max-width: 640px) {
    .reward-week {
        /* Семь ячеек в ряд на 375px — это 40 пикселей на ячейку вместе с отступами,
           и сумма в них уже не читается. Четыре и три — читается. */
        grid-template-columns: repeat(4, 1fr);
    }

    .games-header {
        flex-direction: column;
        align-items: stretch;
    }

    .games-balance {
        align-items: flex-start;
    }

    .wallet-item {
        grid-template-columns: 1fr auto;
        row-gap: 2px;
    }

    .wallet-kind { grid-row: 1; }
    .wallet-amount { grid-row: 1; text-align: right; }
    .wallet-desc { grid-column: 1 / -1; grid-row: 2; white-space: normal; }
    .wallet-time { grid-column: 1 / -1; grid-row: 3; }
}

/* Экран выключенных игр. Намеренно скучный: гасим затею, а не устраиваем из этого событие. */
.games-off {
    max-width: 520px;
    margin: 40px auto;
    padding: 24px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--bg-secondary);
    text-align: center;
}

.games-off h1 {
    margin: 0 0 8px;
    font-size: 20px;
}

.games-off p {
    margin: 0;
    color: var(--text-secondary);
    font-size: 14px;
    line-height: 1.55;
}

/* ─── Кошелёк ─── */

.wallet-page {
    display: flex;
    flex-direction: column;
    gap: 20px;
    max-width: 820px;
    margin: 0 auto;
}

.transfer-card {
    padding: 18px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--bg-secondary);
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.transfer-card h2 {
    margin: 0;
    font-size: 17px;
}

.transfer-form {
    display: flex;
    gap: 10px;
    align-items: flex-end;
    flex-wrap: wrap;
}

/* Имя получателя длиннее суммы, поэтому забирает свободное место. Ширину поля
   приходится переопределять: у полей ракеты она фиксированные 140px, и имя в них
   не помещалось — было видно «имя пользовате». */
.transfer-who {
    flex: 1;
    min-width: 180px;
}

/* Селектор точнее нужного намеренно: 31-games.css склеивается ДО 33-rocket.css,
   а у .rocket-field input та же точность — при равной побеждает последний, то есть
   ширина ракеты. Поэтому здесь на класс больше. */
.transfer-form .transfer-who input {
    width: 100%;
}

.transfer-note {
    margin: 0;
    color: var(--text-secondary);
    font-size: 12px;
    line-height: 1.5;
}

.wallet-more {
    margin-top: 12px;
    align-self: flex-start;
}

/* Ссылка со страницы игр туда, куда переехали баланс и награда. */
.games-wallet-link {
    margin: 0;
    padding: 12px 14px;
    border: 1px dashed var(--border);
    border-radius: 10px;
    color: var(--text-secondary);
    font-size: 13px;
}

@media (max-width: 640px) {
    .transfer-form {
        flex-direction: column;
        align-items: stretch;
    }

    .transfer-form .btn-rocket {
        width: 100%;
    }
}

/* ─── Рейтинг выигрышей ─── */

.games-top {
    padding: 18px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--bg-secondary);
}

.games-top h2 {
    margin: 0 0 14px;
    font-size: 17px;
}

.top-list {
    list-style: none;
    margin: 0;
    padding: 0;
    counter-reset: top;
    display: flex;
    flex-direction: column;
}

.top-item {
    display: grid;
    grid-template-columns: 26px 28px 1fr auto auto;
    align-items: center;
    gap: 10px;
    padding: 8px 0;
    border-bottom: 1px solid var(--border);
    counter-increment: top;
    font-size: 14px;
}

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

/* Номер строки рисуется счётчиком, а не разметкой: список и так упорядоченный. */
.top-place::before {
    content: counter(top);
    color: var(--text-secondary);
    font-size: 13px;
    font-variant-numeric: tabular-nums;
}

/* Первые три — заметнее остальных, но без золота и кубков. */
.top-item:nth-child(1) .top-place::before { color: #ffd97a; font-weight: 700; }
.top-item:nth-child(2) .top-place::before { color: #d8d8d8; font-weight: 700; }
.top-item:nth-child(3) .top-place::before { color: #c8956a; font-weight: 700; }

.top-avatar {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    object-fit: cover;
}

.top-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.top-game {
    color: var(--text-secondary);
    font-size: 12px;
}

.top-amount {
    color: #5fbf7a;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

@media (max-width: 640px) {
    .top-item {
        grid-template-columns: 22px 24px 1fr auto;
        gap: 8px;
        font-size: 13px;
    }

    /* Название игры уходит: имя и сумма важнее, а места на 375px нет. */
    .top-game { display: none; }
}

/* Тихая кнопка рядом с основной: «показать ещё», «раскрыть зерно», «снять предел».
 *
 * Класс стоял в разметке десяти кнопок и не был описан нигде — браузер рисовал их
 * системными светлыми прямоугольниками посреди тёмной страницы. Заметно это только
 * глазами: разметка правильная, тесты зелёные. */
.btn-ghost {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 10px 16px;
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    background: transparent;
    color: var(--text-secondary);
    font-family: inherit;
    font-size: 14px;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    transition: border-color var(--dur) var(--ease),
                color var(--dur) var(--ease),
                background var(--dur) var(--ease);
}

.btn-ghost:hover:not(:disabled) {
    border-color: var(--border-accent);
    background: var(--accent-dim);
    color: var(--text-primary);
}

.btn-ghost:disabled {
    opacity: .55;
    cursor: not-allowed;
}
/* Рулетка.
 *
 * Колесо и шарик крутит браузер: угол задаётся переменной, а движение делает переход CSS.
 * Считать кадры на сервере через circuit значило бы дёргать колесо на каждой задержке сети. */

.roulette-page {
    display: flex;
    flex-direction: column;
    gap: 16px;
    max-width: 900px;
    margin: 0 auto;
}

.roulette-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

.roulette-back {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--text-secondary);
    text-decoration: none;
    font-size: 14px;
}

.roulette-back:hover { color: var(--text-primary); }

.roulette-balance {
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-size: 13px;
    color: var(--text-secondary);
}

.roulette-balance strong {
    font-size: 18px;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}

/* ─── Колесо ─── */

.wheel-area {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 28px;
    padding: 20px;
    border: 1px solid var(--border);
    border-radius: 16px;
    background:
        radial-gradient(circle at 50% 30%, rgba(255, 255, 255, .04), transparent 60%),
        var(--bg-secondary);
}

.wheel-frame {
    position: relative;
    width: 260px;
    height: 260px;
    flex-shrink: 0;
}

.wheel {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    transform: rotate(var(--wheel-turn, 0deg));
    background: #14100e;
    box-shadow:
        0 0 0 6px #2b2320,
        0 0 0 8px #17120f,
        0 14px 34px rgba(0, 0, 0, .55);
}

/* Замедление к концу — как у настоящего колеса, которое тормозит трением, а не тормозом.
   Линейная остановка читается механической и мёртвой. */
.wheel.spinning,
.wheel-ball-track.spinning {
    transition: transform 4.2s cubic-bezier(.17, .67, .16, 1);
}

.wheel-face {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    overflow: hidden;
}

/* Каждая ячейка — узкий сектор, повёрнутый на свой угол. Число внутри доворачивается
   обратно, иначе цифры стояли бы боком у трети колеса. */
.wheel-pocket {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 50%;
    height: 26px;
    margin-top: -13px;
    transform-origin: 0 50%;
    transform: rotate(var(--pocket-angle));
    display: flex;
    align-items: center;
    justify-content: flex-end;
    padding-right: 6px;
    clip-path: polygon(0 42%, 100% 0, 100% 100%, 0 58%);
    font-size: 10px;
    font-weight: 700;
    color: #f0eae6;
}

.wheel-pocket.red { background: #a8352d; }
.wheel-pocket.black { background: #1c1a19; }
.wheel-pocket.green { background: #1f6b46; }

.wheel-pocket span {
    /* Цифра всегда «головой вверх» относительно центра колеса. */
    transform: rotate(90deg);
    transform-origin: center;
}

.wheel-hub {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 84px;
    height: 84px;
    margin: -42px 0 0 -42px;
    border-radius: 50%;
    background: linear-gradient(150deg, #4a3f3a, #211c1a);
    box-shadow: inset 0 2px 6px rgba(255, 255, 255, .12), 0 3px 10px rgba(0, 0, 0, .6);
}

/* Дорожка шарика вращается целиком, шарик просто сидит на её краю. */
.wheel-ball-track {
    position: absolute;
    inset: 10px;
    border-radius: 50%;
    transform: rotate(var(--ball-turn, 0deg));
    pointer-events: none;
}

.wheel-ball {
    position: absolute;
    top: 2px;
    left: 50%;
    width: 12px;
    height: 12px;
    margin-left: -6px;
    border-radius: 50%;
    background: radial-gradient(circle at 35% 30%, #ffffff, #b9b9b9);
    box-shadow: 0 2px 5px rgba(0, 0, 0, .6);
}

/* Метка, под которой останавливается выпавшая ячейка. */
.wheel-marker {
    position: absolute;
    top: -10px;
    left: 50%;
    margin-left: -8px;
    border-left: 8px solid transparent;
    border-right: 8px solid transparent;
    border-top: 16px solid var(--accent);
    filter: drop-shadow(0 2px 4px rgba(0, 0, 0, .6));
}

.wheel-result {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    min-width: 170px;
}

.result-number {
    width: 92px;
    height: 92px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    font-size: 38px;
    font-weight: 800;
    color: #fff;
}

.result-number.red { background: #a8352d; }
.result-number.black { background: #1c1a19; border: 1px solid #3a3a3a; }
.result-number.green { background: #1f6b46; }

.result-win { color: #5fbf7a; font-size: 17px; font-weight: 700; }
.result-lose { color: #e06c62; font-size: 17px; font-weight: 700; }
.result-even { color: var(--text-secondary); font-size: 16px; }
.result-idle,
.result-spinning { color: var(--text-secondary); font-size: 15px; }

.roulette-error {
    padding: 10px 14px;
    border: 1px solid #7a3b36;
    border-radius: 10px;
    background: rgba(160, 60, 50, .14);
    color: #f0b8b2;
    font-size: 14px;
}

/* ─── Фишки ─── */

.chip-row {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.chip-row-label {
    color: var(--text-secondary);
    font-size: 13px;
    margin-right: 2px;
}

.chip {
    min-width: 54px;
    padding: 8px 12px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--bg-secondary);
    color: var(--text-primary);
    font-size: 13px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    cursor: pointer;
    transition: border-color .12s, background .12s;
}

.chip:hover:not(:disabled) { border-color: var(--accent); }

.chip.active {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 20%, transparent);
}

.chip:disabled { opacity: .5; cursor: default; }

/* ─── Поле ─── */

.board {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.board-row {
    display: grid;
    grid-template-columns: repeat(12, 1fr) 1.3fr;
    gap: 4px;
}

/* Зеро — левая колонка во всю высоту трёх рядов. */
.board-main {
    display: flex;
    gap: 4px;
}

.board-main .cell.zero {
    width: 46px;
    flex-shrink: 0;
    align-self: stretch;
}

.board-numbers {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.board-outside {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 4px;
}

.cell {
    position: relative;
    min-height: 42px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg-secondary);
    color: var(--text-primary);
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
    transition: transform .08s, border-color .12s, filter .12s;
}

.cell:hover:not(:disabled) {
    border-color: var(--accent);
    filter: brightness(1.25);
}

.cell:active:not(:disabled) { transform: scale(.96); }

.cell:disabled { cursor: default; }

.cell.red { background: #8e2f28; border-color: #a8352d; }
.cell.black { background: #191817; border-color: #333; }
.cell.zero,
.cell.green { background: #1a5c3c; border-color: #1f6b46; min-height: 40px; }
.cell.side { background: #23201f; font-size: 12px; }
.cell.outside { min-height: 46px; font-size: 13px; }
.cell.outside.dozen { background: #23201f; }

/* Поставленная фишка сидит поверх ячейки — как настоящая на сукне. */
.cell-chip {
    position: absolute;
    right: -4px;
    bottom: -4px;
    min-width: 22px;
    padding: 1px 5px;
    border-radius: 999px;
    background: #ffd97a;
    color: #241a05;
    font-size: 10px;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    box-shadow: 0 1px 4px rgba(0, 0, 0, .6);
}

.cell.has-bet {
    border-color: #ffd97a;
    box-shadow: 0 0 0 1px rgba(255, 217, 122, .5) inset;
}

/* ─── Управление ─── */

.roulette-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.stake-summary {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin-right: auto;
    color: var(--text-secondary);
    font-size: 13px;
}

.stake-summary strong {
    font-size: 18px;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}

.btn-spin {
    min-width: 140px;
    padding: 11px 22px;
    font-size: 15px;
}

/* ─── Что выпадало ─── */

.recent-row {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.recent-label {
    color: var(--text-secondary);
    font-size: 12px;
    margin-right: 4px;
}

.recent-chip {
    min-width: 26px;
    height: 26px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0 5px;
    border-radius: 6px;
    font-size: 12px;
    font-weight: 700;
    color: #fff;
}

.recent-chip.red { background: #8e2f28; }
.recent-chip.black { background: #191817; border: 1px solid #333; }
.recent-chip.green { background: #1a5c3c; }

/* ─── Честность ─── */

.fairness {
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--bg-secondary);
    overflow: hidden;
}

.fairness-toggle {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 13px 16px;
    border: none;
    background: none;
    color: var(--text-primary);
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    text-align: left;
}

.fairness-caret {
    margin-left: auto;
    line-height: 0;
    transition: transform .15s;
}

.fairness-caret.open { transform: rotate(180deg); }

.fairness-body {
    padding: 0 16px 16px;
    display: flex;
    flex-direction: column;
    gap: 12px;
    font-size: 13px;
    line-height: 1.55;
    color: var(--text-secondary);
}

.fairness-body p { margin: 0; }

.fairness-facts {
    display: grid;
    grid-template-columns: minmax(120px, auto) 1fr;
    gap: 6px 14px;
    margin: 0;
}

.fairness-facts dt {
    color: var(--text-secondary);
    font-size: 12px;
}

.fairness-facts dd {
    margin: 0;
    color: var(--text-primary);
    /* Хеш и зерно — длинные строки без пробелов: без переноса они растягивают страницу. */
    overflow-wrap: anywhere;
}

.mono {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 12px;
}

.fairness-seed-edit {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

.fairness-input {
    flex: 1;
    min-width: 160px;
    padding: 7px 10px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--bg-primary, #0f0f0f);
    color: var(--text-primary);
    font-size: 13px;
}

.fairness-formula,
.fairness-last {
    padding: 10px 12px;
    border-radius: 8px;
    background: rgba(255, 255, 255, .04);
    font-size: 12px;
}

.fairness-reveal {
    padding: 12px;
    border: 1px solid color-mix(in srgb, var(--accent) 40%, var(--border));
    border-radius: 10px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.games-footnote {
    margin: 0;
    padding: 12px 14px;
    border-left: 2px solid var(--border);
    color: var(--text-secondary);
    font-size: 13px;
    line-height: 1.6;
}

/* ─── Телефон ─── */

@media (max-width: 760px) {
    .wheel-area {
        flex-direction: column;
        gap: 16px;
        padding: 16px 12px;
    }

    .wheel-frame {
        width: 220px;
        height: 220px;
    }

    /* Зеро переезжает наверх: узкой колонкой слева оно съело бы восьмую часть ширины,
       которой на телефоне и так нет. */
    .board-main {
        flex-direction: column;
    }

    .board-main .cell.zero {
        width: auto;
        min-height: 36px;
    }

    /* Двенадцать колонок на 375px — это 26 пикселей на ячейку, и в неё не помещается
       ни число, ни палец. Поле поворачивается: числа идут сверху вниз, как на телефоне
       и удобнее. */
    .board-numbers {
        /* row-reverse, а не row: ряды идут сверху вниз от третьего к первому, и без
           разворота колонки читались бы справа налево — 3, 2, 1 вместо 1, 2, 3. */
        flex-direction: row-reverse;
        gap: 4px;
    }

    .board-row {
        grid-template-columns: 1fr;
        grid-auto-rows: minmax(38px, auto);
        flex: 1;
        gap: 4px;
    }

    .cell {
        min-height: 38px;
        font-size: 12px;
    }

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

    .roulette-actions {
        gap: 8px;
    }

    .btn-spin {
        flex: 1;
        min-width: 0;
    }

    .fairness-facts {
        grid-template-columns: 1fr;
        gap: 2px 0;
    }

    .fairness-facts dt { margin-top: 8px; }
}
/* «Ракета».
 *
 * Множитель и полёт рисует браузер по времени взлёта: сервер сообщает только смену фазы.
 * Двадцать сообщений в секунду каждому зрителю ради одной цифры — это то, чего здесь нет. */

.rocket-page {
    display: flex;
    flex-direction: column;
    gap: 16px;
    max-width: 900px;
    margin: 0 auto;
}

/* ─── Небо ─── */

.sky {
    position: relative;
    height: 340px;
    border: 1px solid var(--border);
    border-radius: 16px;
    overflow: hidden;
    background:
        radial-gradient(ellipse at 20% 100%, rgba(80, 60, 160, .28), transparent 60%),
        linear-gradient(180deg, #070a18 0%, #0d1020 55%, #141324 100%);
    transition: box-shadow .3s;
}

/* Туманность — самый дальний план. Почти не двигается: на таком расстоянии движение
   не читается, и попытка его показать выглядела бы как ползущее пятно. */
.sky-nebula {
    position: absolute;
    inset: -20%;
    background:
        radial-gradient(ellipse 40% 30% at 25% 30%, rgba(120, 70, 200, .22), transparent 70%),
        radial-gradient(ellipse 35% 25% at 78% 62%, rgba(40, 120, 190, .18), transparent 70%),
        radial-gradient(ellipse 30% 40% at 55% 85%, rgba(190, 70, 130, .12), transparent 70%);
    filter: blur(14px);
    animation: nebula-drift 46s ease-in-out infinite alternate;
}

@keyframes nebula-drift {
    from { transform: translate3d(0, 0, 0) scale(1); }
    to { transform: translate3d(-3%, 2%, 0) scale(1.08); }
}

/* Звёзды — точки, нарисованные градиентами: картинка ради этого не нужна.
 *
 * Три слоя, а не один. Глубина берётся именно из разницы скоростей: одиночный слой
 * при любой скорости читается как одна плоская картинка, которую тянут вниз.
 *
 * Каждый слой вдвое выше рамки и повторяется, поэтому сдвиг ровно на половину высоты
 * возвращает его в исходное положение — стык не виден, и петля бесшовна. */
.sky-stars {
    position: absolute;
    left: 0;
    right: 0;
    top: -100%;
    height: 200%;
    background-repeat: repeat-y;
    background-size: 100% 50%;
    will-change: transform;
}

.sky-stars-far {
    background-image:
        radial-gradient(1px 1px at 12% 22%, rgba(255, 255, 255, .6), transparent),
        radial-gradient(1px 1px at 55% 18%, rgba(255, 255, 255, .55), transparent),
        radial-gradient(1px 1px at 86% 76%, rgba(255, 255, 255, .5), transparent),
        radial-gradient(1px 1px at 30% 62%, rgba(200, 215, 255, .55), transparent),
        radial-gradient(1px 1px at 68% 88%, rgba(255, 255, 255, .45), transparent),
        radial-gradient(1px 1px at 41% 40%, rgba(255, 255, 255, .4), transparent),
        radial-gradient(1px 1px at 77% 8%, rgba(220, 230, 255, .5), transparent),
        radial-gradient(1px 1px at 5% 82%, rgba(255, 255, 255, .4), transparent),
        radial-gradient(1px 1px at 95% 48%, rgba(255, 255, 255, .45), transparent);
    animation: star-fall 34s linear infinite;
}

.sky-stars-mid {
    background-image:
        radial-gradient(1.5px 1.5px at 32% 68%, rgba(255, 255, 255, .75), transparent),
        radial-gradient(1.5px 1.5px at 72% 44%, rgba(200, 220, 255, .75), transparent),
        radial-gradient(1.5px 1.5px at 18% 34%, rgba(255, 255, 255, .65), transparent),
        radial-gradient(1.5px 1.5px at 91% 20%, rgba(255, 240, 220, .7), transparent),
        radial-gradient(1.5px 1.5px at 58% 82%, rgba(255, 255, 255, .6), transparent),
        radial-gradient(1.5px 1.5px at 8% 6%, rgba(210, 225, 255, .6), transparent);
    animation: star-fall 19s linear infinite;
}

.sky-stars-near {
    background-image:
        radial-gradient(2px 2px at 44% 88%, rgba(255, 255, 255, .95), transparent),
        radial-gradient(2px 2px at 64% 12%, rgba(210, 230, 255, .85), transparent),
        radial-gradient(2px 2px at 8% 54%, rgba(255, 255, 255, .8), transparent),
        radial-gradient(2px 2px at 84% 66%, rgba(255, 245, 225, .8), transparent);
    animation: star-fall 11s linear infinite;
}

@keyframes star-fall {
    from { transform: translate3d(0, 0, 0); }
    to { transform: translate3d(0, 50%, 0); }
}

/* В полёте слои ускоряются втрое — это и есть ощущение набора скорости. Множители
   подобраны так, чтобы разница между планами сохранялась: одинаковое ускорение
   схлопнуло бы глубину ровно в тот момент, когда она нужнее всего. */
.sky.flying .sky-stars-far { animation-duration: 12s; }
.sky.flying .sky-stars-mid { animation-duration: 6s; }
.sky.flying .sky-stars-near { animation-duration: 3s; }

/* Планета. Стоит на месте — единственная неподвижная вещь в кадре, и по ней видно,
   что двигаются именно звёзды, а не всё небо целиком.
 *
 * Слева вверху, а не справа: справа заканчивает полёт ракета, и там она садилась
 * прямо на планету. Слева же дуга проходит заметно ниже. */
.sky-planet {
    position: absolute;
    left: 4%;
    top: 8%;
    opacity: .45;
    animation: planet-bob 24s ease-in-out infinite alternate;
    pointer-events: none;
}

@keyframes planet-bob {
    from { transform: translateY(0) rotate(-2deg); }
    to { transform: translateY(-10px) rotate(2deg); }
}

/* Штрихи скорости. Полностью прозрачны в покое: --warp растёт от нуля к единице
   по ходу раунда, и небо «разгоняется» вместе с множителем. */
.sky-streaks {
    position: absolute;
    left: 0;
    right: 0;
    top: -100%;
    height: 200%;
    opacity: calc(var(--warp, 0) * .55);
    /* Тонкие линии, а не полосы. Первая версия закрашивала 28 пикселей из 130,
       и в движении это читалось не как скорость, а как серые полосы поперёк неба —
       будто картинка не догрузилась. */
    background-image: repeating-linear-gradient(
        180deg,
        rgba(255, 255, 255, 0) 0,
        rgba(255, 255, 255, 0) 86px,
        rgba(190, 210, 255, .55) 86px,
        rgba(190, 210, 255, .55) 88px,
        rgba(255, 255, 255, 0) 88px,
        rgba(255, 255, 255, 0) 120px);
    animation: streak-fall .9s linear infinite;
    transition: opacity .3s;
    pointer-events: none;
}

@keyframes streak-fall {
    from { transform: translate3d(0, 0, 0); }
    to { transform: translate3d(0, 120px, 0); }
}

/* Штрихи существуют только в полёте: перед стартом и после падения небо стоит. */
.sky:not(.flying) .sky-streaks { opacity: 0; }

/* Земля. Полоса свечения внизу — без неё ракета не стоит на чём-то, а висит,
   и точка старта выглядит случайной. */
.sky-ground {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 22%;
    background:
        radial-gradient(ellipse 55% 100% at 14% 100%, rgba(120, 90, 220, .35), transparent 70%),
        linear-gradient(180deg, transparent, rgba(10, 8, 24, .8));
    pointer-events: none;
}

/* Пока раундов нет, небо приглушено: игра стоит, и делать вид, что она идёт, незачем. */
.sky.idle {
    filter: saturate(.5) brightness(.8);
}

/* Падение видно всем сразу — красная вспышка по всему небу. */
.sky.crashed {
    box-shadow: inset 0 0 90px rgba(190, 60, 50, .35);
}

.sky.crashed .sky-streaks { animation: none; }

/* Цифра сидит в верхней трети, а ракета летит нижней полосой: по центру они
   пересекались, и в самый важный момент раунда множитель читался сквозь ракету. */
.crash-center {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    padding-top: 40px;
    gap: 8px;
    pointer-events: none;
}

.crash-multiplier {
    font-size: 62px;
    font-weight: 800;
    letter-spacing: -.02em;
    font-variant-numeric: tabular-nums;
    color: #f2f4ff;
    text-shadow: 0 4px 24px rgba(0, 0, 0, .6);
}

.sky.crashed .crash-multiplier { color: #ef6a5e; }
.sky.flying .crash-multiplier { color: #7fe0a0; }

.crash-caption {
    font-size: 14px;
    color: rgba(230, 235, 255, .75);
}

.crash-caption.crashed { color: #ef6a5e; font-weight: 600; }
.crash-caption.taken { color: #7fe0a0; font-weight: 600; }

/* ─── Ракета ─── */

/* Положение задаётся переменными, которые каждые 50 мс обновляет скрипт. Переход —
   короткий: он сглаживает шаг таймера, но не отстаёт от него. */
.crash-rocket {
    position: absolute;
    left: 10%;
    bottom: 12%;
    width: 34px;
    height: 73px;
    /* Поворот тоже переменная: ракета разворачивается по ходу дуги, а не летит одним
       и тем же боком от старта до конца раунда. */
    transform: translate(var(--fly-x, 0px), var(--fly-y, 0px)) rotate(var(--fly-rot, 14deg));
    transition: transform .06s linear;
    z-index: 2;
}

.crash-rocket svg {
    display: block;
    filter: drop-shadow(0 0 12px rgba(180, 200, 255, .3));
}

.crash-flame-outer { fill: rgba(255, 146, 44, .95); }
.crash-flame-inner { fill: rgba(255, 226, 140, .95); }

.crash-flame {
    opacity: 0;
    transform-origin: 20px 62px;
}

/* Огонь горит только в полёте: у стоящей на земле ракеты дюзы молчат. */
.sky.flying .crash-flame {
    opacity: 1;
    animation: flame-flicker .13s infinite alternate;
}

.sky.flying .crash-rocket svg {
    filter: drop-shadow(0 0 14px rgba(255, 160, 60, .45));
}

/* Длина пламени пляшет: постоянная выглядит нарисованной. */
@keyframes flame-flicker {
    from { transform: scaleY(.82); }
    to { transform: scaleY(1.12); }
}

/* Упавшая ракета сваливается носом вниз и уходит из кадра. */
.sky.crashed .crash-rocket {
    transform: translate(calc(var(--fly-x, 0px) + 30px), calc(var(--fly-y, 0px) + 60px)) rotate(158deg);
    opacity: 0;
    transition: transform .9s cubic-bezier(.4, 0, .9, .6), opacity .9s ease-in;
}

/* След — тот же путь, по которому летит ракета.
 *
 * Рисуется SVG-путём, а не повёрнутым прямоугольником: пока траектория была прямой,
 * прямоугольник совпадал с ней точно, но дуга ему не по силам — линия шла бы напрямик
 * и не касалась ракеты нигде, кроме концов. */
.crash-path {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    overflow: visible;
    z-index: 1;
}

.crash-trail {
    fill: none;
    stroke: url(#crash-trail-fade);
    stroke-width: 2.5;
    stroke-linecap: round;
}

/* Второй проход тем же путём, толще и размыто, — свечение выхлопа. Отдельным
   элементом, потому что фильтр на тонкой линии съедает её саму. */
.crash-trail-glow {
    fill: none;
    stroke: rgba(255, 150, 60, .28);
    stroke-width: 9;
    stroke-linecap: round;
    filter: blur(5px);
}

.sky.crashed .crash-trail { stroke: rgba(230, 90, 80, .5); }
.sky.crashed .crash-trail-glow { stroke: rgba(200, 60, 50, .25); }

/* ─── Полоса истории ─── */

.crash-chip {
    min-width: 46px;
    height: 26px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0 8px;
    border-radius: 6px;
    font-size: 12px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* Цвет по величине: сразу видно, что низких раундов большинство. Это и есть та самая
   картина, которую в настоящих играх стараются не показывать подряд. */
.crash-chip.low { background: rgba(190, 70, 60, .22); color: #ef8a80; }
.crash-chip.mid { background: rgba(90, 150, 90, .2); color: #8fd39c; }
.crash-chip.high { background: rgba(120, 110, 200, .25); color: #b3adf0; }

/* ─── Управление ─── */

.rocket-controls {
    display: flex;
    align-items: flex-end;
    gap: 12px;
    flex-wrap: wrap;
}

.rocket-field {
    display: flex;
    flex-direction: column;
    gap: 5px;
}

.rocket-field label {
    color: var(--text-secondary);
    font-size: 12px;
}

.rocket-field input {
    width: 140px;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--bg-secondary);
    color: var(--text-primary);
    font-size: 15px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.rocket-field input:disabled { opacity: .55; }

.btn-rocket {
    min-width: 170px;
    padding: 12px 24px;
    font-size: 15px;
}

/* Кнопка забора мигает — это единственное решение в игре, и принимать его надо быстро. */
.btn-cashout {
    animation: cashout-pulse 1s infinite;
}

@keyframes cashout-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(255, 190, 90, .55); }
    50% { box-shadow: 0 0 0 8px rgba(255, 190, 90, 0); }
}

/* Автозабор сработал: кнопка гаснет, но остаётся на месте и называет число.
   Убрать её совсем значило бы дёрнуть строку управления в момент, когда человек
   смотрит именно туда. */
.btn-auto-done {
    background: rgba(120, 200, 150, .16);
    color: #8fd39c;
    border: 1px solid rgba(120, 200, 150, .4);
    opacity: 1;
}

.rocket-hint {
    margin: 0;
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--text-secondary);
    max-width: 620px;
}

@media (prefers-reduced-motion: reduce) {
    .btn-cashout { animation: none; }
    .sky.flying .crash-flame { animation: none; }

    /* Движение фона выключается целиком: слои сделаны ради ощущения скорости, а оно
       и есть то, от чего эта настройка защищает. Ракета продолжает лететь — иначе
       игру нельзя будет вести. */
    .sky-stars,
    .sky-streaks,
    .sky-nebula,
    .sky-planet { animation: none; }

    .sky-streaks { opacity: 0; }
}

/* ─── Игроки раунда ─── */

.crash-players {
    padding: 16px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--bg-secondary);
}

.crash-players h2 {
    margin: 0 0 12px;
    font-size: 16px;
}

.crash-player-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    max-height: 260px;
    overflow-y: auto;
    /* Список растёт по ходу раунда: без этого страница прыгала бы под рукой у того,
       кто целится в кнопку «забрать». */
    overscroll-behavior: contain;
}

.crash-player {
    display: grid;
    grid-template-columns: 1fr auto 84px;
    align-items: center;
    gap: 10px;
    padding: 8px 0;
    border-bottom: 1px solid var(--border);
    font-size: 13px;
}

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

.crash-player.took { color: #8fd39c; }

.crash-player-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.crash-player-stake {
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
}

.crash-player-result { text-align: right; }
.crash-player-mult { font-weight: 700; color: #8fd39c; }
.crash-player-lost { color: #e06c62; }
.crash-player-flying { color: var(--text-secondary); }

/* ─── Телефон ─── */

@media (max-width: 640px) {
    .sky { height: 260px; }

    .crash-multiplier { font-size: 46px; }

    /* Планета уменьшается и уходит выше: в полный размер она занимала бы четверть
       неба и оказывалась ровно под цифрой множителя. */
    .sky-planet {
        transform-origin: top right;
        scale: .6;
        top: 6%;
        right: 2%;
        opacity: .4;
    }

    .rocket-controls {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 10px;
    }

    .rocket-field input { width: 100%; }

    .btn-rocket {
        grid-column: 1 / -1;
        min-width: 0;
        width: 100%;
    }
}
/* «Мины».
 *
 * Поле 5×5. Оформление сдержанное намеренно: мина здесь не «взрыв с тряской экрана»,
 * а перечёркнутый круг — проигрыш не должен выглядеть зрелищнее выигрыша. */

.mines-page {
    display: flex;
    flex-direction: column;
    gap: 16px;
    max-width: 620px;
    margin: 0 auto;
}

/* ─── Табло ─── */

.mines-board-head {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
}

.mines-stat {
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: 10px 14px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--bg-secondary);
}

.mines-stat-label {
    color: var(--text-secondary);
    font-size: 11px;
}

.mines-stat-value {
    font-size: 18px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.mines-stat-value.live { color: #7fe0a0; }

/* ─── Поле ─── */

.mines-field {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 8px;
}

.mines-cell {
    position: relative;
    aspect-ratio: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: linear-gradient(160deg, #23242c, #191a20);
    cursor: pointer;
    transition: transform .08s, border-color .12s, background .2s;
}

.mines-cell:hover:not(:disabled) {
    border-color: var(--accent);
    transform: translateY(-2px);
}

.mines-cell:active:not(:disabled) { transform: scale(.96); }

.mines-cell:disabled { cursor: default; }

.mines-cell.safe {
    background: linear-gradient(160deg, #1d3a2a, #16281f);
    border-color: #2f6a48;
}

.mines-cell.mine {
    background: linear-gradient(160deg, #3a1e1c, #281514);
    border-color: #7a3b36;
}

/* Клетки, до которых не дошли: после конца партии они гаснут, чтобы не спорить
   за внимание с тем, что действительно случилось. */
.mines-cell.dimmed { opacity: .45; }

.mines-mark.safe { color: #6fce8f; line-height: 0; }
.mines-mark.mine { color: #e8756a; line-height: 0; }

.mines-outcome {
    padding: 11px 14px;
    border-radius: 10px;
    font-size: 14px;
    font-weight: 600;
}

.mines-outcome.won {
    background: rgba(90, 170, 110, .16);
    color: #8fd39c;
}

.mines-outcome.lost {
    background: rgba(190, 80, 70, .16);
    color: #ef8a80;
}

/* ─── Телефон ─── */

@media (max-width: 640px) {
    .mines-board-head {
        grid-template-columns: repeat(3, 1fr);
        gap: 6px;
    }

    .mines-stat {
        padding: 8px 10px;
    }

    .mines-stat-value { font-size: 15px; }

    .mines-field { gap: 6px; }

    .mines-cell { border-radius: 10px; }
}
/* Чтобы ссылки в шапке не пропадали.
 *
 * Ряд ссылок прокручивается вбок со скрытой полосой — так сделано намеренно, чтобы при
 * нехватке места не появлялась горизонтальная прокрутка всей страницы. Но у этого решения
 * есть цена: ссылка не «уезжает», а именно исчезает, и понять, что она есть, нельзя.
 *
 * На этом дважды обожглись вживую. Сначала добавили «Игры» — и у администраторов пропала
 * кнопка «Админ». Перенесли «Админ» в полосу действий — и у тех же администраторов пропали
 * «Игры»: полоса действий стала шире на значок, а сжимается всегда ряд ссылок.
 *
 * Настоящая причина — длина подписей. «Пространства» и «Приглашения» вдвоём стоят двух
 * с лишним сотен пикселей, а пунктов семь. Поэтому теперь у каждого пункта есть значок,
 * и на нехватке места прячется ПОДПИСЬ, а не пункт: семь значков занимают меньше двух
 * прежних слов, и прятать больше нечего.
 *
 * Модуль в конце нумерации намеренно: его правила обязаны побеждать и 01-layout-navbar,
 * и 18-navbar-search, где заданы исходные ширины. */

.nav-link {
    display: inline-flex;
    align-items: center;
    gap: 7px;
}

/* Значок не сжимается никогда: он и есть тот минимум, ради которого всё затевалось. */
.nav-link-icon {
    flex: none;
}

.nav-link-text {
    white-space: nowrap;
}

/* ─── На большом экране остаются одни значки ───
 *
 * Без верхней границы, и это проверено счётом, а не на глаз. .navbar-inner ограничен
 * 1200 пикселями, из которых ряду ссылок достаётся 689 — остальное забирают название,
 * поиск и полоса действий. Восьми пунктам с подписями нужно 964. Значит подписи
 * не помещаются НИ НА КАКОЙ ширине окна, и «на просторном экране их можно вернуть» —
 * это про экран, а не про контейнер.
 *
 * Здесь уже дважды стояла верхняя граница — сначала 1500, потом 1280, — и оба раза
 * на широком мониторе крайний пункт пропадал за краем прокручиваемого ряда. */
@media (min-width: 901px) {
    .navbar-links .nav-link-text {
        /* Не display:none — так подпись остаётся доступной экранному диктору,
           а значок получает подсказку через title. */
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }

    .navbar-links .nav-link {
        padding-left: 9px;
        padding-right: 9px;
    }

    /* Имя в углу — самое дорогое и самое необязательное: аватар опознаётся быстрее,
       а своё имя человек и так знает. */
    .nav-profile-name {
        display: none;
    }

    .navbar-links {
        gap: 4px;
    }
}

/* Поле поиска убирается только там, где без этого не обойтись. Со значками ряд ссылок
   занимает чуть больше трёхсот пикселей вместо девятисот с лишним, поэтому порог
   опустился с 1100 до 1000, а отдельное сужение поля выше него стало не нужно. */
@media (min-width: 901px) and (max-width: 1000px) {
    .navbar-search { display: none; }
    .nav-link-search { display: inline-flex; }
}

/* Если ссылки всё-таки не помещаются, обрез должен быть ВИДЕН.
 *
 * Это и есть настоящая причина обеих поломок: полоса прокрутки спрятана, поэтому ссылка
 * не «уезжает вправо», а просто исчезает, и отличить её отсутствие от того, что её нет
 * в приложении вовсе, нельзя. Затухание у правого края говорит «здесь есть продолжение»
 * и стоит один градиент. */
@media (min-width: 769px) and (max-width: 1000px) {
    .navbar-links {
        -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent 100%);
        mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent 100%);
    }
}

/* ─── Нижняя панель на телефоне ───
 *
 * Значок над подписью. Раньше подпись стояла одна и обрезалась многоточием:
 * «Пространс…» и «Приглаше…» — то есть панель из семи пунктов, по которым нельзя
 * понять, куда ведёт каждый. Значок узнаётся раньше, чем прочитан обрезанный текст. */
@media (max-width: 768px) {
    .navbar-links .nav-link {
        flex-direction: column;
        gap: 2px;
        padding: 6px 2px;
        font-size: 9.5px;
        line-height: 1.15;
    }

    .navbar-links .nav-link-icon {
        width: 21px;
        height: 21px;
    }

    /* На узких экранах подписи всё же не помещаются даже мелкими: семь пунктов на 360
       пикселях дают по полсотни на каждый. Значки остаются. */
    @media (max-width: 400px) {
        .navbar-links .nav-link-text { display: none; }
        .navbar-links .nav-link { padding: 8px 2px; }
    }
}

/* Стикер — кнопка, а не картинка с обработчиком: так он доступен с клавиатуры и читается
   экранным диктором. Обёртка не должна ничего добавлять к внешнему виду. */
.sticker-item-btn {
    padding: 0;
    border: none;
    background: none;
    cursor: pointer;
    line-height: 0;
    border-radius: 8px;
}

.sticker-item-btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
/* Правка балансов в админке.
 *
 * Отдельная страница, отдельный модуль. Раскладка тут своя: строка на человека, где
 * рядом стоят баланс, сумма и причина, — и подмешивать её в общие карточки пользователей
 * значило бы тянуть за собой правила, рассчитанные на другую сетку. */

.admin-page-note {
    margin: 0 0 16px;
    font-size: 13px;
    line-height: 1.55;
    color: var(--text-secondary);
    max-width: 720px;
}

.admin-saved-message-error {
    border-color: rgba(224, 108, 98, .45);
    color: #e06c62;
}

.photon-search {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 14px;
}

.photon-search .form-input { max-width: 340px; }

.photon-search-count {
    font-size: 13px;
    color: var(--text-secondary);
    white-space: nowrap;
}

.photon-user-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.photon-user {
    display: grid;
    grid-template-columns: 40px minmax(140px, 1fr) auto minmax(320px, 1.4fr);
    align-items: center;
    gap: 14px;
    padding: 12px 14px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--bg-secondary);
}

.photon-user-avatar {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    object-fit: cover;
}

.photon-user-who {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.photon-user-name {
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.photon-user-uid {
    font-size: 11px;
    color: var(--text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.photon-user-balance {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 2px;
}

.photon-user-balance-label {
    font-size: 11px;
    color: var(--text-secondary);
}

.photon-user-balance-value {
    font-size: 17px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.photon-user-form {
    display: flex;
    align-items: center;
    gap: 8px;
}

.photon-amount { width: 110px; flex: none; }
.photon-reason { flex: 1 1 160px; min-width: 0; }

/* ─── Что уже сделано ─── */

.photon-log {
    margin-top: 26px;
    padding: 16px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--bg-secondary);
}

.photon-log h2 {
    margin: 0 0 12px;
    font-size: 16px;
}

/* Таблица прокручивается внутри своей рамки: причина бывает длинной, и без этого
   страница ехала бы вбок целиком. */
.photon-log-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
    display: block;
    overflow-x: auto;
    white-space: nowrap;
}

.photon-log-table th {
    text-align: left;
    font-weight: 600;
    color: var(--text-secondary);
    padding: 6px 12px 6px 0;
    border-bottom: 1px solid var(--border);
}

.photon-log-table td {
    padding: 8px 12px 8px 0;
    border-bottom: 1px solid var(--border);
}

.photon-log-table tr:last-child td { border-bottom: none; }

.photon-log-when { color: var(--text-secondary); }
.photon-log-after { font-variant-numeric: tabular-nums; color: var(--text-secondary); }
.photon-log-why { white-space: normal; min-width: 220px; }

.photon-plus { color: #7fe0a0; font-weight: 700; font-variant-numeric: tabular-nums; }
.photon-minus { color: #e06c62; font-weight: 700; font-variant-numeric: tabular-nums; }

/* ─── Телефон ─── */

@media (max-width: 900px) {
    .photon-user {
        grid-template-columns: 40px 1fr auto;
        row-gap: 10px;
    }

    /* Поля переезжают на свою строку во всю ширину: втиснуть сумму, причину и две
       кнопки в остаток строки на 375 пикселях невозможно. */
    .photon-user-form {
        grid-column: 1 / -1;
        flex-wrap: wrap;
    }

    .photon-reason { flex: 1 1 100%; }
}
/* «Кубик» и «Плинко».
 *
 * Обе игры мгновенные: ставка, исход, следующая. Поэтому главное на экране — не кнопка,
 * а то, ЧТО покупается за ставку: в кубике это закрашенный участок полосы и три числа
 * под ним, в плинко — ряд ячеек с множителями. В настоящих играх этого обычно не видно
 * вовсе, и понять цену решения нечем. */

.dice-page,
.plinko-page {
    display: flex;
    flex-direction: column;
    gap: 16px;
    max-width: 900px;
    margin: 0 auto;
}

/* ─────────────────────────── Кубик ─────────────────────────── */

.dice-track-wrap {
    padding: 26px 18px 10px;
    border: 1px solid var(--border);
    border-radius: 16px;
    background:
        radial-gradient(ellipse at 50% 0%, rgba(90, 80, 190, .18), transparent 65%),
        var(--bg-secondary);
}

/* Полоса — это вся игра. Слева ноль, справа сто, граница между зонами и есть
   загаданное число. */
.dice-track {
    position: relative;
    height: 22px;
    /* Ничего не обрезает: отметка и подпись выпавшего числа торчат за её края
       по замыслу. Скругление живёт на вложенной обёртке зон. */
    overflow: visible;
}

.dice-zones {
    position: absolute;
    inset: 0;
    border-radius: 11px;
    background: rgba(0, 0, 0, .25);
    /* Скругление именно обрезкой, а не border-radius на самих зонах: иначе граница
       между ними — то самое загаданное число — тоже стала бы скруглённой. */
    clip-path: inset(0 round 11px);
}

.dice-zone {
    position: absolute;
    top: 0;
    bottom: 0;
    transition: left .12s, width .12s, right .12s;
}

.dice-zone-lose {
    background: rgba(224, 108, 98, .28);
}

.dice-zone-win {
    background: linear-gradient(90deg, rgba(127, 224, 160, .45), rgba(127, 224, 160, .7));
}

.dice-track::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 11px;
    box-shadow: inset 0 0 0 1px var(--border);
    pointer-events: none;
}

.dice-marker {
    position: absolute;
    top: -7px;
    bottom: -7px;
    width: 2px;
    margin-left: -1px;
    background: #f2f4ff;
    box-shadow: 0 0 8px rgba(0, 0, 0, .6);
    transition: left .12s;
}

/* Шарик выпавшего числа. Появляется рывком — бросок мгновенный, и «плавно подъезжающий»
   результат выглядел бы так, будто его подбирают. */
.dice-ball {
    position: absolute;
    top: 50%;
    width: 18px;
    height: 18px;
    margin: -9px 0 0 -9px;
    border-radius: 50%;
    background: #f2f4ff;
    box-shadow: 0 2px 10px rgba(0, 0, 0, .5);
    animation: dice-pop .25s cubic-bezier(.2, 1.6, .5, 1);
}

.dice-ball.won { background: #7fe0a0; }
.dice-ball.lost { background: #e06c62; }

@keyframes dice-pop {
    from { transform: scale(.2); opacity: 0; }
    to { transform: scale(1); opacity: 1; }
}

.dice-ball-value {
    position: absolute;
    top: -26px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 12px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    color: var(--text-primary);
}

.dice-scale {
    display: flex;
    justify-content: space-between;
    margin-top: 10px;
    font-size: 11px;
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
}

.dice-result {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    min-height: 62px;
    justify-content: center;
    border-radius: 14px;
    border: 1px solid var(--border);
    background: var(--bg-secondary);
}

.dice-result-main {
    font-size: 28px;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
}

.dice-result.won .dice-result-main { color: #7fe0a0; }
.dice-result.lost .dice-result-main { color: #e06c62; }

.dice-result-sub,
.dice-result-hint {
    font-size: 12.5px;
    color: var(--text-secondary);
}

.dice-controls {
    display: flex;
    align-items: flex-end;
    gap: 14px;
    flex-wrap: wrap;
}

/* Не растягивается на весь остаток строки: с flex-grow поле уезжало к левому краю,
   а кнопки стороны улетали к правому, и связь между ними переставала читаться. */
.dice-field-target { flex: 0 1 300px; min-width: 200px; }

.dice-slider {
    width: 100%;
    accent-color: #7fe0a0;
}

.dice-target-number {
    margin-top: 6px;
    width: 110px;
    padding: 8px 10px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--bg-secondary);
    color: var(--text-primary);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* Две кнопки вместо переключателя: «меньше» и «больше» — равноправные решения,
   и прятать одно из них за галочкой значило бы делать вид, что одно основное. */
.dice-side {
    display: flex;
    gap: 6px;
}

.dice-side-btn {
    padding: 10px 16px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--bg-secondary);
    color: var(--text-secondary);
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    transition: background .15s, color .15s, border-color .15s;
}

.dice-side-btn:hover:not(:disabled) { color: var(--text-primary); }

.dice-side-btn.active {
    background: rgba(127, 224, 160, .16);
    border-color: rgba(127, 224, 160, .5);
    color: #7fe0a0;
}

.dice-side-btn:disabled { opacity: .5; cursor: default; }

/* Три числа, между которыми нет ничего, кроме одного процента заведения. */
.dice-odds {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
}

.dice-odd {
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: 12px 14px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--bg-secondary);
}

.dice-odd-label {
    font-size: 11px;
    color: var(--text-secondary);
}

.dice-odd-value {
    font-size: 19px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.dice-roll { align-self: flex-start; }

.dice-chip {
    min-width: 52px;
    height: 26px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0 8px;
    border-radius: 6px;
    font-size: 12px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.dice-chip.won { background: rgba(90, 150, 90, .2); color: #8fd39c; }
.dice-chip.lost { background: rgba(190, 70, 60, .22); color: #ef8a80; }

/* ─────────────────────────── Плинко ─────────────────────────── */

/* Поле с ячейками прокручивается как одно целое. Порознь они разъехались бы, и ячейка
   перестала бы стоять под тем местом, куда падает шарик. */
.plinko-field {
    overflow-x: auto;
    overscroll-behavior-x: contain;
}

.plinko-board {
    position: relative;
    height: 380px;
    border: 1px solid var(--border);
    border-radius: 16px 16px 0 0;
    border-bottom: none;
    overflow: hidden;
    background:
        radial-gradient(ellipse at 50% -10%, rgba(110, 90, 210, .22), transparent 65%),
        linear-gradient(180deg, #0b0d1c, #12142a);
}

.plinko-pegs,
.plinko-balls {
    position: absolute;
    /* Поле уже рамки: крайние колышки стоят по её краям, и без отступа половина
       каждого из них оказалась бы срезанной. */
    inset: 16px 14px 22px;
}

.plinko-peg {
    position: absolute;
    width: 6px;
    height: 6px;
    margin: -3px 0 0 -3px;
    border-radius: 50%;
    background: rgba(200, 210, 255, .55);
    box-shadow: 0 0 6px rgba(150, 170, 255, .3);
}

.plinko-ball {
    position: absolute;
    width: 14px;
    height: 14px;
    margin: -7px 0 0 -7px;
    border-radius: 50%;
    background: radial-gradient(circle at 35% 30%, #ffe6a8, #f0a63c 70%, #c97a1e);
    box-shadow: 0 0 12px rgba(255, 180, 80, .6);
}

.plinko-ball.landed {
    animation: plinko-land .25s ease-out forwards;
}

@keyframes plinko-land {
    to { transform: scale(1.6); opacity: 0; }
}

/* Ячейки. Ряд из семнадцати штук — на телефоне они становятся очень узкими, поэтому
   цифра ужимается вместе с ними, а не переносится. */
.plinko-buckets {
    display: grid;
    grid-template-columns: repeat(17, 1fr);
    gap: 2px;
    padding: 8px 14px 10px;
    border: 1px solid var(--border);
    border-top: none;
    border-radius: 0 0 16px 16px;
    background: var(--bg-secondary);
}

.plinko-bucket {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 30px;
    border-radius: 6px;
    font-size: 10.5px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    overflow: hidden;
    transition: transform .12s;
}

/* Цвет по редкости: чем дальше от центра, тем реже туда попадают и тем больше цифра.
   Это ровно то распределение, о котором игра и есть. */
.plinko-bucket.inner { background: rgba(120, 110, 200, .18); color: #b3adf0; }
.plinko-bucket.outer { background: rgba(230, 150, 60, .2); color: #f0b673; }
.plinko-bucket.edge { background: rgba(224, 108, 98, .24); color: #f09a92; }

.plinko-bucket.hit {
    animation: plinko-hit .7s ease-out;
}

@keyframes plinko-hit {
    0% { transform: translateY(0); filter: brightness(1); }
    25% { transform: translateY(6px); filter: brightness(2.1); }
    100% { transform: translateY(0); filter: brightness(1); }
}

.plinko-risk { flex-wrap: wrap; }

.plinko-chip {
    min-width: 46px;
    height: 26px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0 8px;
    border-radius: 6px;
    font-size: 12px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.plinko-chip.won { background: rgba(90, 150, 90, .2); color: #8fd39c; }
.plinko-chip.lost { background: rgba(190, 70, 60, .22); color: #ef8a80; }

@media (prefers-reduced-motion: reduce) {
    .dice-ball,
    .plinko-ball.landed,
    .plinko-bucket.hit { animation: none; }
}

/* ─── Телефон ─── */

@media (max-width: 640px) {
    /* Поле не сжимается ниже этой ширины, а уезжает под палец. При 375 пикселях
       на ячейку приходилось семнадцать — «1002×» там обрезалось до «.002×», и человек
       видел не тот множитель, за который ему заплатят. */
    .plinko-board,
    .plinko-buckets { min-width: 520px; }

    .plinko-board { height: 300px; }

    .plinko-buckets {
        gap: 2px;
        padding: 6px 10px 8px;
    }

    .plinko-bucket {
        height: 26px;
        font-size: 8px;
        border-radius: 4px;
    }

    .plinko-pegs,
    .plinko-balls { inset: 12px 10px 18px; }

    .plinko-peg { width: 5px; height: 5px; margin: -2.5px 0 0 -2.5px; }
    .plinko-ball { width: 12px; height: 12px; margin: -6px 0 0 -6px; }

    .dice-odds { grid-template-columns: 1fr 1fr; }
    .dice-roll { width: 100%; }

    .dice-controls { gap: 10px; }
    .dice-field-target { flex: 1 1 100%; }
}

/* ─────────────────── Ежедневные задания ─────────────────── */

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

.daily-tasks {
    padding: 16px 18px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--bg-secondary);
}

.daily-tasks-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 14px;
}

.daily-tasks-head h2 {
    margin: 0;
    font-size: 16px;
}

.daily-tasks-total {
    font-size: 12.5px;
    color: var(--text-secondary);
}

.daily-task-message {
    margin-bottom: 12px;
    padding: 8px 12px;
    border-radius: 10px;
    background: rgba(127, 224, 160, .12);
    color: #7fe0a0;
    font-size: 13px;
}

.daily-task-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.daily-task {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
}

.daily-task.ready { border-color: rgba(127, 224, 160, .45); }
.daily-task.claimed { opacity: .55; }

.daily-task-info {
    flex: 1;
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-areas: "title progress" "bar bar";
    align-items: center;
    gap: 4px 10px;
    min-width: 0;
}

.daily-task-title {
    grid-area: title;
    font-size: 13.5px;
    font-weight: 600;
}

.daily-task-progress {
    grid-area: progress;
    font-size: 11.5px;
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
}

.daily-task-bar {
    grid-area: bar;
    height: 4px;
    border-radius: 2px;
    background: rgba(255, 255, 255, .08);
    overflow: hidden;
}

.daily-task-bar-fill {
    height: 100%;
    border-radius: 2px;
    background: linear-gradient(90deg, rgba(127, 224, 160, .6), #7fe0a0);
    transition: width .3s;
}

.daily-task-reward {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: none;
}

.daily-task-amount {
    font-size: 14px;
    font-weight: 700;
    color: #f0b673;
    font-variant-numeric: tabular-nums;
}

.daily-task-done {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 12px;
    color: #7fe0a0;
}

.daily-task-pending {
    font-size: 12px;
    color: var(--text-secondary);
}

.daily-tasks-note {
    margin: 14px 0 0;
    font-size: 12px;
    line-height: 1.55;
    color: var(--text-secondary);
}

@media (max-width: 640px) {
    .daily-task {
        flex-wrap: wrap;
        gap: 8px;
    }

    .daily-task-reward {
        width: 100%;
        justify-content: space-between;
    }
}
/* Магазин.
 *
 * Витрина заказов, а не автомат: покупка создаёт заказ, владелец выполняет его руками.
 * Отсюда две вещи, которых не бывает в обычных магазинах, и обе намеренные — поле
 * уточнения прямо в карточке и список своих заказов на той же странице. Человек должен
 * видеть, что с его заказом, не уходя никуда. */

.shop-page {
    display: flex;
    flex-direction: column;
    gap: 20px;
    max-width: 1100px;
    margin: 0 auto;
}

.shop-message-error {
    background: rgba(224, 108, 98, .12);
    color: #e06c62;
}

/* ─── Витрина ─── */

.shop-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 16px;
}

.shop-card {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--bg-secondary);
    overflow: hidden;
    transition: border-color .15s, transform .15s;
}

.shop-card:hover {
    border-color: rgba(150, 140, 220, .45);
    transform: translateY(-2px);
}

.shop-card.free {
    border-color: rgba(127, 224, 160, .4);
}

/* Место под фотографию есть всегда, даже когда фотографии нет.
 *
 * Карточки без картинок иначе оказывались втрое ниже соседних, и витрина выглядела
 * поломанной. Оттенок заглушки считается из названия — так они хотя бы отличаются
 * друг от друга и читаются как «так задумано», а не как «не загрузилось». */
.shop-card-photo {
    position: relative;
    aspect-ratio: 16 / 10;
    display: flex;
    align-items: center;
    justify-content: center;
    background:
        radial-gradient(ellipse at 30% 20%, hsl(var(--shop-hue, 260) 45% 32% / .75), transparent 70%),
        linear-gradient(140deg, hsl(var(--shop-hue, 260) 40% 22%), hsl(var(--shop-hue, 260) 35% 14%));
    overflow: hidden;
}

.shop-card-photo img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.shop-card-nophoto {
    color: rgba(255, 255, 255, .3);
}

.shop-card-free {
    position: absolute;
    top: 10px;
    left: 10px;
    padding: 3px 9px;
    border-radius: 999px;
    background: rgba(127, 224, 160, .9);
    color: #10241a;
    font-size: 11px;
    font-weight: 700;
}

/* Точки переключения фотографий. Кнопки, а не картинки с обработчиком: так они
   доступны с клавиатуры. */
.shop-card-dots {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 8px;
    display: flex;
    justify-content: center;
    gap: 6px;
}

.shop-dot {
    width: 7px;
    height: 7px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: rgba(255, 255, 255, .45);
    cursor: pointer;
    transition: background .15s, transform .15s;
}

.shop-dot.on {
    background: #fff;
    transform: scale(1.3);
}

.shop-card-body {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 14px 15px 15px;
    flex: 1;
}

.shop-card-body h3 {
    margin: 0;
    font-size: 15.5px;
}

.shop-card-text {
    margin: 0;
    font-size: 13px;
    line-height: 1.5;
    color: var(--text-secondary);
    flex: 1;
}

.shop-card-note {
    font-size: 13px;
    padding: 8px 10px;
}

.shop-card-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-top: 2px;
}

.shop-card-price {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 17px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: #f0b673;
}

.shop-price-free { color: #7fe0a0; }

/* ─── Мои заказы ─── */

.shop-orders {
    padding: 16px 18px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--bg-secondary);
}

.shop-orders h2 {
    margin: 0 0 12px;
    font-size: 16px;
}

.shop-order-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.shop-order {
    display: grid;
    grid-template-columns: 92px 1fr auto auto;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    font-size: 13px;
}

.shop-order-status {
    justify-self: start;
    padding: 3px 9px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
}

.shop-order.new .shop-order-status { background: rgba(230, 170, 60, .18); color: #f0b673; }
.shop-order.done .shop-order-status { background: rgba(127, 224, 160, .18); color: #7fe0a0; }
.shop-order.refunded .shop-order-status { background: rgba(224, 108, 98, .18); color: #e06c62; }

.shop-order-what {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.shop-order-name { font-weight: 600; }

.shop-order-note,
.shop-order-answer {
    font-size: 12px;
    color: var(--text-secondary);
}

.shop-order-answer { color: #8fd39c; }

.shop-order-price {
    font-variant-numeric: tabular-nums;
    color: var(--text-secondary);
}

.shop-order-when {
    font-size: 12px;
    color: var(--text-secondary);
    white-space: nowrap;
}

.shop-note {
    margin: 14px 0 0;
    font-size: 12px;
    line-height: 1.55;
    color: var(--text-secondary);
}

/* ─── Магазин в админке ─── */

.shop-admin-orders {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.shop-admin-order {
    display: grid;
    grid-template-columns: 40px 1fr;
    gap: 12px;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    font-size: 13px;
}

.shop-admin-order.new { border-color: rgba(230, 170, 60, .4); }
.shop-admin-order.done,
.shop-admin-order.refunded { opacity: .6; }

.shop-admin-order-what {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
}

.shop-admin-order-price {
    margin-left: 8px;
    color: #f0b673;
    font-variant-numeric: tabular-nums;
}

.shop-admin-order-note {
    color: var(--text-primary);
    font-style: italic;
}

.shop-admin-order-when {
    font-size: 11.5px;
    color: var(--text-secondary);
}

/* Действия занимают всю вторую колонку: комментарий и две кнопки в остаток строки
   рядом с именем не помещаются даже на широком экране. */
.shop-admin-order-actions {
    grid-column: 2;
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-top: 4px;
}

.shop-admin-order-actions .form-input {
    flex: 1 1 200px;
    min-width: 0;
    padding: 6px 10px;
    font-size: 13px;
}

.shop-admin-items { margin-top: 26px; }

.shop-admin-form {
    padding: 16px;
    margin-bottom: 16px;
    border: 1px solid rgba(150, 140, 220, .4);
    border-radius: 12px;
    background: var(--bg-secondary);
}

.shop-admin-form h3 {
    margin: 0 0 14px;
    font-size: 15px;
}

.shop-admin-form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 12px;
}

.shop-admin-wide { grid-column: 1 / -1; }

.shop-admin-form-grid .rocket-field span {
    font-size: 12px;
    color: var(--text-secondary);
}

.shop-admin-check {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    align-self: end;
    padding-bottom: 10px;
}

.shop-admin-photos { margin-top: 16px; }

.shop-admin-photo-row {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 8px;
}

.shop-admin-photo {
    position: relative;
    width: 90px;
    height: 66px;
    border-radius: 8px;
    overflow: hidden;
    border: 1px solid var(--border);
}

.shop-admin-photo img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.shop-admin-photo-x {
    position: absolute;
    top: 3px;
    right: 3px;
    display: flex;
    padding: 3px;
    border: none;
    border-radius: 50%;
    background: rgba(0, 0, 0, .65);
    color: #fff;
    cursor: pointer;
}

.shop-admin-photo-add {
    position: relative;
    width: 90px;
    height: 66px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    border: 1px dashed var(--border);
    border-radius: 8px;
    color: var(--text-secondary);
    font-size: 11px;
    cursor: pointer;
}

/* Поле выбора файла невидимо, но остаётся на месте и кликабельно: подмена его
   кнопкой с JS-обработчиком отняла бы доступ с клавиатуры. */
.shop-admin-photo-add input[type="file"] {
    position: absolute;
    inset: 0;
    opacity: 0;
    cursor: pointer;
}

.shop-admin-form-actions {
    display: flex;
    gap: 8px;
    margin-top: 16px;
}

.shop-admin-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.shop-admin-row {
    display: grid;
    grid-template-columns: 56px 1fr auto auto;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--bg-secondary);
}

.shop-admin-row.free { border-color: rgba(127, 224, 160, .35); }

.shop-admin-row-photo {
    width: 56px;
    height: 42px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 7px;
    overflow: hidden;
    color: rgba(255, 255, 255, .3);
    background: linear-gradient(140deg, hsl(var(--shop-hue, 260) 40% 22%), hsl(var(--shop-hue, 260) 35% 14%));
}

.shop-admin-row-photo img { width: 100%; height: 100%; object-fit: cover; }

.shop-admin-row-what {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.shop-admin-row-name { font-weight: 600; font-size: 14px; }

.shop-admin-row-desc {
    font-size: 12px;
    color: var(--text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.shop-admin-row-price {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    color: #f0b673;
    white-space: nowrap;
}

.shop-admin-row-actions {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
}

/* Значок вида задания в списке — игровое, про общение, про кошелёк. */
.daily-task-mark {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    flex: none;
    border-radius: 8px;
}

.daily-task-mark.game { background: rgba(120, 110, 200, .2); color: #b3adf0; }
.daily-task-mark.social { background: rgba(90, 160, 200, .2); color: #8fc4e0; }
.daily-task-mark.wallet { background: rgba(230, 170, 60, .18); color: #f0b673; }

/* ─── Телефон ─── */

@media (max-width: 700px) {
    .shop-grid { grid-template-columns: 1fr; }

    .shop-order {
        grid-template-columns: 1fr auto;
        row-gap: 6px;
    }

    .shop-order-status { grid-column: 1; }
    .shop-order-price { grid-column: 2; grid-row: 1; justify-self: end; }
    .shop-order-what { grid-column: 1 / -1; }
    .shop-order-when { grid-column: 1 / -1; }

    .shop-admin-row {
        grid-template-columns: 56px 1fr;
        row-gap: 8px;
    }

    .shop-admin-row-price { grid-column: 2; justify-self: end; }
    .shop-admin-row-actions { grid-column: 1 / -1; }
}

/* Приписка под заголовком рейтинга: он недельный, и это должно быть видно сразу,
   а не выясняться из того, что вчерашний рекорд куда-то делся. */
.games-top-note {
    margin: -6px 0 12px;
    font-size: 12px;
    color: var(--text-secondary);
}

/* Кнопка заказа — главное действие витрины, и пальцем по ней надо попадать. Проект
   считает мелкой всё ниже 34 пикселей (так нашлись .btn-sm по 28 в админке); здесь
   было ровно 28. Правило узкое: .btn-sm в остальных местах не трогаем. */
.shop-card-foot .btn-sm {
    min-height: 34px;
}
/* Самоограничения: дневной предел ставок и перерыв в играх.
 *
 * Раздел живёт на странице кошелька рядом с переводом, поэтому форма переиспользует
 * .transfer-form и .rocket-field — своя разметка тут разъехалась бы с соседней при
 * первой же правке. Здесь только то, чего в них нет. */

.self-limit-card {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 18px;
    margin-bottom: 16px;
}

.self-limit-card h2 {
    margin: 0 0 14px;
    font-size: 17px;
}

/* Стоящий перерыв — не ошибка и не успех, поэтому ни красный, ни зелёный:
   спокойная рамка, которую видно, но которая не кричит. */
.self-limit-paused {
    padding: 12px 14px;
    border: 1px solid rgba(77, 163, 255, .35);
    border-radius: var(--radius-md);
    background: rgba(77, 163, 255, .08);
    color: var(--text-primary);
    font-size: 14px;
    line-height: 1.5;
    margin-bottom: 14px;
}

.self-limit-left {
    margin: 10px 0 0;
    color: var(--text-secondary);
    font-size: 13px;
}

/* Отметка на чужой странице. Осуждать человека за перерыв нечего — тон нейтральный. */
.profile-self-pause {
    margin: 6px 0 0;
    padding: 6px 12px;
    display: inline-block;
    border-radius: var(--radius-pill);
    background: rgba(77, 163, 255, .12);
    color: #9FC7F5;
    font-size: 13px;
}

/* Купленный титул — строчка под ником. Это покупка, а не ранг, поэтому выглядит
   иначе: без плашки, курсивом и приглушённо, чтобы не спорить с ранговым значком
   в соседней строке. */
.profile-custom-title {
    margin: 2px 0 6px;
    color: var(--text-secondary);
    font-size: 14px;
    font-style: italic;
}
/* Значки на странице человека.
 *
 * Значок — это заслуга, а не украшение за фотоны, поэтому показан с подписью, а не
 * одной картинкой: без подписи зелёный шестиугольник читается как ранг или как баг
 * вёрстки. Подсказка по наведению объясняет, за что он дан. */

.profile-badges {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin: 8px 0 4px;
}

.profile-badge {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 3px 12px 3px 4px;
    border: 1px solid rgba(61, 220, 132, .35);
    border-radius: var(--radius-pill);
    background: rgba(61, 220, 132, .08);
    color: #9BE8BD;
    font-size: 13px;
    font-weight: 600;
    cursor: default;
}

.profile-badge img {
    width: 28px;
    height: 28px;
    flex: none;
    display: block;
}
/* Админка: кнопки нормального размера и ничего не вылезает за рамку.
 *
 * Найдено не на глаз, а замером отрендеренных страниц на 390 и 1280 пикселях:
 * все действия в админке были .btn-sm высотой 28 px — пальцем в них не попасть,
 * а мышью промахиваешься мимо «Удалить» в соседнюю «Скрыть». Модуль стоит последним
 * и сужен до .admin-container, чтобы не трогать мелкие кнопки на остальном сайте. */

.admin-container .btn-sm {
    min-height: 36px;
    padding: 8px 14px;
    font-size: 13.5px;
    line-height: 1.2;
    white-space: nowrap;
}

@media (max-width: 768px) {
    .admin-container .btn-sm {
        min-height: 42px;
        padding: 10px 16px;
    }
}

/* Сетка карточек пользователей: минимум 340 px при контейнере в 342 px с отступами
   выдавливал карточку за правый край на любом телефоне. min() не даёт колонке
   стать шире самого контейнера. */
.admin-users-grid {
    grid-template-columns: repeat(auto-fill, minmax(min(340px, 100%), 1fr));
}

.admin-user-card {
    min-width: 0;
}

/* Длинное имя без пробелов растягивало страницу вбок: строка не переносилась,
   и в заказах магазина появлялась горизонтальная прокрутка всего окна. */
.shop-admin-order-head,
.shop-admin-order-note,
.admin-user-card,
.photon-user-name {
    overflow-wrap: anywhere;
}

.shop-admin-order-what {
    min-width: 0;
}

/* Фильтры заказов: «В работе» переносилось на две строки и кнопка становилась
   вдвое выше соседних. Надпись держится в строке, а ряд переносится целиком. */
.admin-container .dice-side {
    flex-wrap: wrap;
}

.admin-container .dice-side-btn {
    white-space: nowrap;
}

/* «Выдано» и «Отказ и возврат» на телефоне вставали столбиком разной ширины.
   Поле комментария — на всю строку, кнопки — поровну под ним. */
@media (max-width: 768px) {
    .shop-admin-order {
        grid-template-columns: 32px 1fr;
    }

    .shop-admin-order-actions {
        grid-column: 1 / -1;
    }

    .shop-admin-order-actions .form-input {
        flex: 1 1 100%;
        min-height: 42px;
    }

    .shop-admin-order-actions .btn {
        flex: 1 1 0;
    }
}

/* Подсказка у одобрения: что именно сайт выдаст сам, когда нажмут кнопку. */
.shop-admin-order-grant {
    flex-basis: 100%;
    color: #7fe0a0;
    font-size: 12px;
}

/* Форма товара: поля ракеты шириной в 140 px сюда не годятся — название обрезалось
   до «Цветное имя н». В сетке формы поле занимает всю ширину своей колонки. */
.shop-admin-form-grid .rocket-field input,
.shop-admin-form-grid .rocket-field select,
.shop-admin-form-grid .rocket-field textarea {
    width: 100%;
    min-width: 0;
}

/* Крестик «убрать фото» был 20 px — промахнуться легко. 28 px превью ещё не закрывают. */
.shop-admin-photo-x {
    min-width: 28px;
    min-height: 28px;
    align-items: center;
    justify-content: center;
}
/* Карточные игры: дурак и покер.
 *
 * Карты — не картинки, а разметка: так они не весят ничего, одинаково чёткие на любом
 * экране и не требуют набора из 52 файлов. Лицо светлое, как у настоящих карт, — на тёмном
 * сукне оно читается мгновенно, а красные масти отличаются от чёрных цветом, а не только
 * значком. */

.cards-page {
    max-width: 960px;
    margin: 0 auto;
    padding: 16px 16px 40px;
}

.cards-balance {
    margin-left: auto;
    color: var(--gold);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.cards-head h1 {
    margin: 12px 0 6px;
}

.cards-head p {
    margin: 0 0 16px;
    color: var(--text-secondary);
    line-height: 1.55;
    max-width: 680px;
}

.cards-create {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 12px;
    padding: 16px;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--bg-secondary);
}

/* Ряд вариантов внутри формы стола обязан переноситься. .dice-side приехал от кубика,
   где вариантов два, а у покера пять блайндов: на 375 пикселях ряд не помещался, страница
   ехала вбок на 90 пикселей, и «500/1000» оказывался за краем экрана. */
.cards-create .dice-side {
    flex-wrap: wrap;
}

.cards-create .rocket-field {
    min-width: 0;
}

.cards-lobby {
    margin-top: 20px;
}

.cards-lobby h2 {
    font-size: 17px;
    margin: 0 0 10px;
}

.cards-lobby-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    margin-bottom: 8px;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--bg-secondary);
}

.cards-lobby-what {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    flex: 1;
}

.cards-lobby-what span {
    color: var(--text-secondary);
    font-size: 13px;
    overflow-wrap: anywhere;
}

.cards-lobby-state {
    color: var(--text-muted);
    font-size: 12px;
    white-space: nowrap;
}

.cards-lobby-state.playing {
    color: var(--gold);
}

/* ─── Карта ─── */

/* Индекс в углу, как у настоящих карт: когда битую карту накрывает покрывающая, в углу
   остаётся видно, что именно били. С одной большой надписью по центру отбивающийся
   не видел масти атакующей карты. */
.playing-card {
    --card-w: 62px;
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--card-w);
    height: calc(var(--card-w) * 1.42);
    flex: none;
    padding: 0;
    border: 1px solid #cfc8b8;
    border-radius: 8px;
    background: linear-gradient(160deg, #fbf8f1, #ece6d8);
    color: #1f2230;
    font-family: inherit;
    line-height: 1;
    box-shadow: 0 2px 6px rgba(0, 0, 0, .45);
    user-select: none;
    transition: transform var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease),
                border-color var(--dur-fast) var(--ease);
}

.playing-card.red {
    color: #c9362f;
}

.pc-corner {
    position: absolute;
    top: 5px;
    left: 6px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
}

.pc-corner b {
    font-size: calc(var(--card-w) * .27);
    font-weight: 800;
}

.pc-corner i {
    font-style: normal;
    font-size: calc(var(--card-w) * .22);
}

.pc-pip {
    font-size: calc(var(--card-w) * .52);
    margin-top: calc(var(--card-w) * .18);
    opacity: .9;
}

button.playing-card {
    cursor: pointer;
}

button.playing-card:disabled {
    cursor: default;
}

/* Козыри в руке помечены полоской снизу: при восьми картах их не найти глазами,
   а ошибиться козырем в дураке — проиграть раздачу. */
.playing-card.trump::after {
    content: "";
    position: absolute;
    inset: auto 10px 5px;
    height: 3px;
    border-radius: 2px;
    background: var(--gold);
}

.cards-hand button.playing-card:hover:not(:disabled) {
    transform: translateY(-6px);
}

.playing-card.selected {
    transform: translateY(-14px);
    border-color: var(--accent);
    box-shadow: 0 0 0 2px var(--accent), 0 10px 18px rgba(0, 0, 0, .5);
}

.playing-card.target {
    box-shadow: 0 0 0 3px var(--green), 0 4px 12px rgba(0, 0, 0, .5);
    animation: card-target 1.2s ease-in-out infinite;
}

@keyframes card-target {
    50% { box-shadow: 0 0 0 5px rgba(61, 220, 132, .45), 0 4px 12px rgba(0, 0, 0, .5); }
}

/* Рубашка: тот же космический градиент, что в логотипе, — узнаётся как «наша колода». */
.card-back {
    --card-w: 62px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--card-w);
    height: calc(var(--card-w) * 1.42);
    flex: none;
    border: 2px solid #f4efe4;
    border-radius: 8px;
    background:
        repeating-linear-gradient(45deg, rgba(255, 255, 255, .08) 0 4px, transparent 4px 9px),
        var(--grad-cosmic);
    box-shadow: 0 2px 6px rgba(0, 0, 0, .45);
    color: #fff;
    font-weight: 800;
    text-shadow: 0 1px 2px rgba(0, 0, 0, .6);
}

.card-back.small {
    --card-w: 16px;
    border-width: 1px;
    border-radius: 3px;
    margin-left: -6px;
    box-shadow: none;
}

.card-back.small:first-child {
    margin-left: 0;
}

/* ─── Соперники ─── */

.durak-seats {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 10px;
    margin: 14px 0;
}

.durak-seat {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    min-width: 120px;
    padding: 10px 14px;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--bg-secondary);
}

.durak-seat.acting {
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent), var(--shadow-accent);
}

.durak-seat.out {
    opacity: .55;
}

.cards-seat-name {
    font-weight: 700;
    font-size: 14px;
    max-width: 160px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.durak-seat-cards {
    display: flex;
    align-items: center;
    min-height: 23px;
}

.durak-seat-more {
    margin-left: 4px;
    color: var(--text-secondary);
    font-size: 12px;
}

.durak-seat-role {
    color: var(--text-secondary);
    font-size: 12px;
    min-height: 15px;
}

/* ─── Сукно ─── */

.cards-felt {
    display: grid;
    grid-template-columns: 140px 1fr 90px;
    grid-template-areas: "deck table discard";
    align-items: center;
    gap: 12px;
    min-height: 200px;
    padding: 18px;
    border: 1px solid #1f4a3a;
    border-radius: 22px;
    background:
        radial-gradient(ellipse at 50% 30%, rgba(61, 220, 132, .12), transparent 70%),
        linear-gradient(180deg, #123127, #0b211a);
    box-shadow: inset 0 2px 24px rgba(0, 0, 0, .55);
}

.durak-deck { grid-area: deck; position: relative; height: 100px; }
.durak-table { grid-area: table; }
.durak-discard { grid-area: discard; }

/* Козырь лежит под колодой поперёк и выглядывает из-под неё — так, чтобы индекс в его
   углу был виден. Раньше колода закрывала его почти целиком. */
.durak-trump {
    position: absolute;
    left: 44px;
    top: 12px;
    transform: rotate(90deg);
}

.durak-stock {
    position: absolute;
    left: 0;
    top: 0;
    z-index: 1;
}

.durak-trump-suit {
    font-size: 44px;
    color: #f4efe4;
    text-align: center;
}

.durak-trump-suit.red {
    color: #ff7a72;
}

.durak-table {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 18px 30px;
    min-height: 120px;
}

.durak-table-empty {
    color: rgba(238, 241, 247, .45);
    font-size: 14px;
}

/* Покрывающая карта сдвинута вправо и вниз ровно настолько, чтобы угол битой — ранг
   и масть — оставался открытым. */
.durak-pair {
    position: relative;
    width: 88px;
    height: 112px;
}

.durak-pair > .playing-card:first-child {
    position: absolute;
    left: 0;
    top: 0;
}

.durak-defense {
    position: absolute;
    left: 26px;
    top: 24px;
    transform: rotate(10deg);
}

.durak-discard {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    color: rgba(238, 241, 247, .55);
}

.durak-discard .card-back {
    --card-w: 44px;
    transform: rotate(-8deg);
    filter: saturate(.4) brightness(.8);
}

/* ─── Ход, рука, кнопки ─── */

.cards-status {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    min-height: 40px;
    margin: 12px 0 4px;
    color: var(--text-primary);
    font-size: 15px;
    text-align: center;
}

.cards-timer {
    padding: 2px 10px;
    border-radius: var(--radius-pill);
    background: var(--bg-tertiary);
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
    font-size: 13px;
}

.cards-timer.hurry {
    background: rgba(255, 92, 92, .18);
    color: var(--red);
}

/* Рука переносится, а не прокручивается вбок. Прокрутка тут врёт так же, как ряд ссылок
   в шапке: после «беру» на руке бывает восемнадцать карт, треть из них уезжала за правый
   край, и отличить «карт больше нет» от «карты за краем» было нельзя. Перенос делает
   лишний ряд — зато видно всё и по каждой карте можно попасть пальцем. */
.cards-hand {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 8px;
    padding: 18px 6px 8px;
}

.cards-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 10px;
    margin: 10px 0;
}

.cards-waiting {
    color: var(--text-secondary);
}

.cards-fair {
    margin-top: 22px;
    padding: 12px 14px;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--bg-secondary);
    color: var(--text-secondary);
    font-size: 13px;
}

.cards-fair summary {
    cursor: pointer;
    color: var(--text-primary);
    font-weight: 600;
}

.cards-fair code {
    overflow-wrap: anywhere;
    font-size: 12px;
}

/* ─── Телефон ─── */

@media (max-width: 640px) {
    /* Колода и отбой — одной строкой сверху, стол — под ними во всю ширину: столбиком
       из трёх частей сукно занимало весь экран, и руку приходилось прокручивать. */
    .cards-felt {
        grid-template-columns: 1fr auto;
        grid-template-areas:
            "deck discard"
            "table table";
        padding: 14px 10px;
    }

    .durak-deck {
        height: 80px;
    }

    .playing-card,
    .card-back {
        --card-w: 48px;
    }

    .durak-trump {
        left: 34px;
        top: 8px;
    }

    .durak-pair {
        width: 70px;
        height: 90px;
    }

    .durak-defense {
        left: 20px;
        top: 20px;
    }

    .durak-discard {
        flex-direction: row;
    }

    .durak-discard .card-back {
        --card-w: 30px;
    }

    /* Восемь карт в ряд на телефоне не влезают — рука ложится веером внахлёст, но угол
       с индексом у каждой карты остаётся открытым. */
    .cards-hand {
        justify-content: flex-start;
        gap: 0;
        padding-left: 12px;
    }

    .cards-hand .playing-card {
        margin-left: -16px;
    }

    .cards-hand .playing-card:first-child {
        margin-left: 0;
    }

    .cards-lobby-row {
        flex-wrap: wrap;
    }

    /* Название стола и список сидящих — на всю строку, кнопка и состояние — под ними:
       рядом с кнопкой имена сжимались в столбик по одной букве. */
    .cards-lobby-what {
        flex-basis: 100%;
    }

    .cards-lobby-row .btn-ghost {
        margin-left: auto;
    }
}

.cards-timer {
    white-space: nowrap;
}
/* Покер. Карты, рубашка, сукно и лобби — из 42-cards.css; здесь только то, чего нет в дураке:
 * места с фишками и кнопкой дилера, пять слотов стола и торговля. */

.poker-hint {
    flex-basis: 100%;
    margin: 0;
    color: var(--text-muted);
    font-size: 12px;
}

.poker-seats {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(150px, 100%), 1fr));
    gap: 10px;
    margin: 14px 0;
}

.poker-seat {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 5px;
    min-width: 0;
    padding: 10px 12px 14px;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--bg-secondary);
}

.poker-seat.me {
    border-color: rgba(77, 163, 255, .45);
}

.poker-seat.acting {
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent), var(--shadow-accent);
}

.poker-seat.folded {
    opacity: .5;
}

.poker-seat-top {
    display: flex;
    align-items: center;
    gap: 6px;
    max-width: 100%;
}

/* Кнопка дилера — белая шайба с «D», как на живом столе: по ней видно, откуда идут блайнды
   и кто говорит последним. */
.poker-dealer {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    flex: none;
    border-radius: 50%;
    background: #f4efe4;
    color: #1f2230;
    font-size: 11px;
    font-weight: 900;
}

.poker-stack {
    color: var(--gold);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.poker-shown {
    display: flex;
    gap: 3px;
    min-height: 43px;
}

.playing-card.mini,
.card-back.mini {
    --card-w: 30px;
    border-radius: 4px;
    box-shadow: none;
}

.card-back.mini {
    border-width: 1px;
}

.playing-card.mini .pc-corner {
    top: 2px;
    left: 3px;
}

.playing-card.mini .pc-pip {
    display: none;
}

.poker-hand-name {
    color: var(--green);
    font-size: 12px;
    font-weight: 600;
}

.poker-seat-state {
    min-height: 15px;
    color: var(--text-secondary);
    font-size: 12px;
}

/* Ставка улицы висит под местом фишкой — так видно, кто сколько вложил, не складывая в уме. */
.poker-bet {
    position: absolute;
    bottom: -11px;
    left: 50%;
    transform: translateX(-50%);
    padding: 2px 10px;
    border-radius: var(--radius-pill);
    background: var(--gold);
    color: #2a1d00;
    font-size: 12px;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.poker-felt {
    grid-template-columns: 1fr;
    grid-template-areas: none;
    justify-items: center;
    gap: 14px;
    min-height: 170px;
    margin-top: 18px;
}

.poker-board {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 8px;
}

.poker-slot {
    width: 62px;
    height: 88px;
    border: 1px dashed rgba(238, 241, 247, .18);
    border-radius: 8px;
}

.poker-pot {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    color: #f4efe4;
}

.poker-pot strong {
    color: var(--gold);
    font-variant-numeric: tabular-nums;
}

.poker-pot small {
    color: rgba(238, 241, 247, .5);
    font-size: 12px;
}

.playing-card.big {
    --card-w: 76px;
}

.poker-hole {
    justify-content: center;
}

.poker-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 10px;
    margin: 8px 0 4px;
}

.poker-raise {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 8px;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--bg-secondary);
}

.poker-raise input {
    width: 120px;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--bg-primary);
    color: var(--text-primary);
    font-size: 15px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.poker-raise-presets {
    display: flex;
    gap: 6px;
}

.poker-raise-presets .btn-ghost {
    padding: 8px 10px;
    font-size: 13px;
}

@media (max-width: 640px) {
    /* Шесть мест по одному в строку занимали весь экран, и до своих карт приходилось
       листать. Втрое уже — и стол виден целиком. */
    .poker-seats {
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: 6px;
    }

    .poker-seat {
        padding: 6px 4px 13px;
        gap: 3px;
    }

    .poker-seat .cards-seat-name {
        font-size: 12px;
    }

    .poker-stack {
        font-size: 13px;
    }

    .poker-shown {
        min-height: 32px;
    }

    .playing-card.mini,
    .card-back.mini {
        --card-w: 22px;
    }

    /* Две карманные карты — не веер: внахлёст их прячет правило руки дурака. */
    .poker-hole {
        justify-content: center;
        gap: 8px;
        padding-left: 6px;
    }

    .poker-hole .playing-card {
        margin-left: 0;
    }

    .poker-slot {
        width: 48px;
        height: 68px;
    }

    .poker-board {
        gap: 5px;
    }

    .playing-card.big {
        --card-w: 64px;
    }

    .poker-actions .btn-rocket {
        flex: 1 1 40%;
        min-width: 0;
    }

    .poker-raise {
        flex-basis: 100%;
    }
}
/* Подтверждение почты и разрешение для лаунчера.
 *
 * Классы в разметке этих двух страниц были с самого начала, а правил под ними не было.
 * Сброс из 00-base обнуляет отступы у всего, поэтому каждый блок прилипал к соседнему
 * вплотную, маркеры списков висели за левым краем текста, а длинный адрес почты
 * выталкивал карточку за край экрана телефона. Страницы открываются до входа — это первое,
 * что видит новый человек и тот, кто пускает на сайт свой лаунчер. */

.auth-lead {
    margin-bottom: 20px;
    font-size: 14px;
    line-height: 1.55;
    color: var(--text-secondary);
    text-align: center;
}

.auth-lead strong {
    color: var(--text-primary);
    overflow-wrap: anywhere;
}

.auth-hint {
    font-size: 13px;
    line-height: 1.5;
    color: var(--text-secondary);
}

.resend-form {
    margin-top: 10px;
}

.resend-form .btn {
    width: 100%;
}

.auth-help {
    margin-top: 20px;
    padding: 14px 16px;
    background: var(--bg-tertiary);
    border-radius: var(--radius-md);
    font-size: 13px;
    line-height: 1.55;
    color: var(--text-secondary);
}

.auth-help strong {
    color: var(--text-primary);
}

.auth-help ul {
    margin: 8px 0 0;
    padding-left: 18px;
}

.auth-help li + li {
    margin-top: 4px;
}

.auth-help a {
    overflow-wrap: anywhere;
}

.auth-help-warning {
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px solid var(--border-strong);
    color: var(--gold);
}

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

.consent-lead {
    font-size: 15px;
    line-height: 1.5;
    text-align: center;
    overflow-wrap: anywhere;
}

.consent-account {
    padding: 10px 12px;
    background: var(--bg-tertiary);
    border-radius: var(--radius-sm);
    font-size: 14px;
    color: var(--text-secondary);
    overflow-wrap: anywhere;
}

.consent-account strong {
    color: var(--text-primary);
}

.consent-scopes {
    padding-left: 20px;
    font-size: 14px;
    line-height: 1.5;
}

.consent-scopes li + li {
    margin-top: 6px;
}

.consent-scopes li::marker {
    color: var(--accent);
}

.consent-actions {
    display: flex;
    gap: 10px;
}

.consent-actions .btn {
    flex: 1;
}

.auth-error + .auth-hint {
    text-align: center;
}
