/* ─────────────────────────────────────────────────────────────────────────────
   Q-Ку — веб-клиент. Палитра совпадает с Android-приложением.
   ───────────────────────────────────────────────────────────────────────────── */

:root {
    /*
     * Цветовые токены веб-клиента.
     *
     * Раньше палитра жила в двух местах: шесть переменных подменял JavaScript,
     * а всё остальное было вписано в правила прямыми значениями. Значения эти
     * рассчитывались на ночной фон, поэтому в светлой теме панели оставались
     * тёмно-синими, белые подсветки исчезали, а текст исходящего сообщения
     * наследовал тёмный цвет и ложился на фиолетовый пузырь.
     *
     * Теперь палитра целиком здесь: :root — тёмная тема, блок ниже — светлая.
     * JavaScript отдаёт только то, что выбирает пользователь: основной цвет,
     * читаемый цвет поверх него и признак темы.
     *
     * Правило: цвет, меняющийся между темами, задаётся здесь и нигде больше.
     */

    /* Фон */
    --backdrop-top:    #070A14;
    --backdrop-mid:    #0E1428;
    --backdrop-bottom: #070A14;
    --solid-backdrop:  #0B1020;

    --aurora-violet: rgba(124, 92, 255, 0.30);
    --aurora-cyan:   rgba(18, 184, 232, 0.20);
    --aurora-pink:   rgba(217, 70, 200, 0.16);

    /* Вуаль поверх своих обоев: в тёмной теме затемняет, в светлой высветляет. */
    --image-scrim: rgba(0, 0, 0, 0.45);

    /* Поверхности */
    --surface:       #161E36;   /* непрозрачная: окна, всплывающие панели */
    --glass:         rgba(255, 255, 255, 0.08);
    --glass-strong:  rgba(255, 255, 255, 0.14);
    --input-surface: rgba(255, 255, 255, 0.10);
    --bar:           rgba(12, 18, 34, 0.80);
    --border:        rgba(255, 255, 255, 0.12);
    --hover:         rgba(255, 255, 255, 0.07);
    --hover-strong:  rgba(255, 255, 255, 0.13);
    --scrim:         rgba(0, 0, 0, 0.70);
    --shadow:        rgba(0, 0, 0, 0.50);

    /* Текст */
    --text:  #F2F4F8;
    --dim:   #AEBBD0;
    --muted: #93A2B8;

    /*
     * Акценты.
     *
     * --accent, --accent-rgb, --accent-lift и --on-accent приходят из
     * appearance.js: основной цвет выбирает пользователь. Значения здесь —
     * запасные, на случай если скрипт ещё не отработал.
     */
    --accent:      #6B46FF;
    --accent-rgb:  107, 70, 255;
    --accent-lift: #8B5CFF;
    --on-accent:   #FFFFFF;

    --info:    #00D4FF;
    --online:  #2DD4A7;

    --danger:        #FF5C77;
    --danger-soft:   rgba(255, 92, 119, 0.14);
    --danger-border: rgba(255, 92, 119, 0.38);

    --warning:        #FFB020;
    --warning-soft:   rgba(255, 176, 32, 0.12);
    --warning-tint:   rgba(255, 176, 32, 0.05);
    --warning-border: rgba(255, 176, 32, 0.30);

    /* Пузыри сообщений */
    --bubble-in:        #1B2340;
    --bubble-in-border: rgba(255, 255, 255, 0.10);
    /*
     * Нейтральная заливка внутри пузыря: цитаты, вложения, реакции.
     * Серый с прозрачностью читается и на акцентном пузыре, и на подложке
     * любой темы — в отличие от прежних белых и чёрных плашек.
     */
    --in-bubble:       rgba(128, 128, 128, 0.22);
    --in-bubble-hover: rgba(128, 128, 128, 0.34);

    /* Плашка со временем поверх фотографии: под ней всегда темно. */
    --media-veil: rgba(0, 0, 0, 0.5);

    /*
     * Блок кода. Остаётся тёмным в обеих темах: подсветка синтаксиса
     * рассчитана на тёмный фон, и в светлой теме пришлось бы заводить
     * второй комплект.
     */
    --code-bg:     #12161F;
    --code-text:   #D6DEEB;
    --code-dim:    rgba(255, 255, 255, 0.55);
    --code-border: rgba(255, 255, 255, 0.10);

    --radius:    14px;
    --radius-sm: 9px;

    --sidebar-w: 340px;
    --ease: cubic-bezier(0.4, 0, 0.2, 1);
}

/*
 * Светлая тема.
 *
 * Не инверсия тёмной. Стекло здесь плотное и белое: полупрозрачный белый
 * на светлом градиенте не виден вообще, а прежние тёмно-синие панели
 * выглядели на нём заплатками из чужого приложения. Границы и вдавленные
 * блоки, наоборот, тёмные с малой прозрачностью.
 *
 * Акценты состояний темнее ночных: неон рассчитан на чёрный фон и на белом
 * расплывается в светлое пятно без формы.
 */
html[data-theme="light"] {
    --backdrop-top:    #F7F8FC;
    --backdrop-mid:    #EBEFF9;
    --backdrop-bottom: #F7F8FC;
    --solid-backdrop:  #F4F5F9;

    --aurora-violet: rgba(124, 92, 255, 0.16);
    --aurora-cyan:   rgba(18, 184, 232, 0.12);
    --aurora-pink:   rgba(217, 70, 200, 0.09);

    --image-scrim: rgba(255, 255, 255, 0.55);

    --surface:       #FFFFFF;
    --glass:         rgba(255, 255, 255, 0.78);
    --glass-strong:  rgba(255, 255, 255, 0.90);
    --input-surface: rgba(18, 24, 38, 0.06);
    --bar:           rgba(255, 255, 255, 0.82);
    --border:        rgba(18, 24, 38, 0.12);
    --hover:         rgba(18, 24, 38, 0.05);
    --hover-strong:  rgba(18, 24, 38, 0.10);
    --scrim:         rgba(20, 25, 40, 0.45);
    --shadow:        rgba(20, 25, 40, 0.16);

    --text:  #121826;
    --dim:   #4B586E;
    --muted: #5E6B81;

    --info:   #0E7490;
    --online: #0A7D57;

    --danger:        #D92D4E;
    --danger-soft:   rgba(217, 45, 78, 0.10);
    --danger-border: rgba(217, 45, 78, 0.32);

    /* Темнее янтарного из тёмной темы: на светлой подложке ленты
       прежний оттенок давал контраст 3.8:1. */
    --warning:        #9A4507;
    --warning-soft:   rgba(245, 158, 11, 0.14);
    --warning-tint:   rgba(245, 158, 11, 0.08);
    --warning-border: rgba(154, 69, 7, 0.34);

    --bubble-in:        #FFFFFF;
    --bubble-in-border: rgba(18, 24, 38, 0.10);
}

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

html, body { height: 100%; }

body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    /* Опорный градиент темы; пятна света рисует body::before поверх него. */
    background: linear-gradient(180deg,
        var(--backdrop-top), var(--backdrop-mid) 50%, var(--backdrop-bottom));
    color: var(--text);
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
    overflow: hidden;
}

button, input, textarea { font: inherit; color: inherit; }
button { cursor: pointer; background: none; border: none; }

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

svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

.screen { height: 100dvh; }
[hidden] { display: none !important; }

/* ── Живой фон ───────────────────────────────────────────────────────────── */
/*
   Тот же приём, что и в приложении: три размытых пятна света медленно
   плывут по своим траекториям. Рисуется градиентами, без изображений,
   поэтому ничего не грузится и не тормозит.
*/
/* Живой градиент показывается только когда выбран именно он. */
html[data-background="solid"] body::before,
html[data-background="image"] body::before { display: none; }

/* Однотонная подложка: спокойнее для глаз и дешевле по отрисовке. */
html[data-background="solid"] body { background: var(--solid-backdrop); }

/* Своя картинка: снимок пользователя с затемнением поверх, иначе на
   светлых обоях текст сообщений не читается. */
html[data-background="image"] body::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background-image: linear-gradient(var(--image-scrim), var(--image-scrim)), var(--wallpaper);
    background-size: cover;
    background-position: center;
}

body::before {
    content: "";
    position: fixed;
    inset: -20%;
    z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(38% 32% at 25% 18%, var(--aurora-violet), transparent 70%),
        radial-gradient(34% 30% at 82% 38%, var(--aurora-cyan), transparent 70%),
        radial-gradient(36% 30% at 45% 88%, var(--aurora-pink), transparent 70%);
    animation: aurora 58s ease-in-out infinite alternate;
}

@keyframes aurora {
    0%   { transform: translate(0, 0) scale(1); }
    50%  { transform: translate(3%, -2%) scale(1.06); }
    100% { transform: translate(-3%, 2%) scale(1.02); }
}

/* ── Заставка ────────────────────────────────────────────────────────────── */

.screen--splash {
    position: fixed;
    inset: 0;
    z-index: 400;
    /* Три способа центрирования подряд: flex работает даже там,
       где place-items почему-то не сработал. */
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--backdrop-top);
    transition: opacity 0.3s var(--ease);
}

.splash {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 24px;
    text-align: center;
}

.splash__logo {
    position: relative;
    display: flex;
    align-items: center;
    gap: 12px;
    animation: splash-in 0.5s var(--ease);
}

/* Пульсирующее свечение под логотипом: экран ожидания не должен
   выглядеть застывшим, иначе кажется, что приложение зависло. */
.splash__logo::before {
    content: "";
    position: absolute;
    inset: -40px;
    border-radius: 50%;
    background: radial-gradient(circle, rgba(var(--accent-rgb), 0.35), transparent 70%);
    animation: splash-pulse 2.4s ease-in-out infinite;
    pointer-events: none;
}

.splash__logo .logo-mark {
    width: 64px;
    height: 64px;
    font-size: 2.1rem;
    border-radius: 19px;
    animation: splash-float 3.4s ease-in-out infinite;
}
.splash__logo .logo-text { font-size: 2.1rem; }

@keyframes splash-in {
    from { opacity: 0; transform: translateY(12px) scale(0.94); }
    to   { opacity: 1; transform: none; }
}

@keyframes splash-pulse {
    0%, 100% { opacity: 0.35; transform: scale(0.9); }
    50%      { opacity: 0.75; transform: scale(1.08); }
}

@keyframes splash-float {
    0%, 100% { transform: translateY(0) rotate(0deg); }
    50%      { transform: translateY(-6px) rotate(-3deg); }
}

/* Полоса прогресса без процентов: сколько займёт проверка, заранее неизвестно. */
.splash__bar {
    width: 180px;
    height: 3px;
    border-radius: 2px;
    background: var(--input-surface);
    overflow: hidden;
}

.splash__bar i {
    display: block;
    width: 40%;
    height: 100%;
    border-radius: 2px;
    background: linear-gradient(90deg, var(--accent), var(--accent-lift));
    animation: splash-slide 1.3s ease-in-out infinite;
}

@keyframes splash-slide {
    0%   { transform: translateX(-100%); }
    100% { transform: translateX(350%); }
}

.splash__hint {
    font-size: 0.88rem;
    color: var(--dim);
    min-height: 1.2em;
    animation: hint-fade 0.4s var(--ease);
}

@keyframes hint-fade {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* ── Кнопки ──────────────────────────────────────────────────────────────── */

.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 8px;
    padding: 12px 20px;
    border-radius: var(--radius-sm);
    font-size: 0.95rem; font-weight: 600;
    transition: transform 0.12s var(--ease), background 0.2s var(--ease), opacity 0.2s var(--ease);
    border: 1px solid transparent;
}
.btn:active { transform: scale(0.985); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }

/*
 * Цвет надписи на акценте — не всегда белый.
 *
 * Три из восьми основных цветов светлые (бирюзовый, зелёный, янтарный),
 * и белый на них давал контраст около 3:1. Нужный вариант считает
 * appearance.js и отдаёт в --on-accent.
 */
.btn--primary { background: linear-gradient(135deg, var(--accent), var(--accent-lift)); color: var(--on-accent); }
.btn--ghost { background: var(--glass); border-color: var(--border); }
.btn--ghost:hover:not(:disabled) { background: var(--hover-strong); }
.btn--text { background: none; color: var(--dim); font-weight: 500; }
.btn--text:hover { color: var(--text); }
.btn--danger { background: var(--danger-soft); border-color: var(--danger-border); color: var(--danger); }
.btn--block { width: 100%; }
.btn--sm { padding: 7px 14px; font-size: 0.85rem; }

.icon-btn {
    display: grid; place-items: center;
    width: 38px; height: 38px;
    border-radius: 10px;
    color: var(--dim);
    flex-shrink: 0;
    transition: background 0.18s var(--ease), color 0.18s var(--ease);
}
.icon-btn:hover { background: var(--hover); color: var(--text); }

.icon-btn--send { background: linear-gradient(135deg, var(--accent), var(--accent-lift)); color: var(--on-accent); }
.icon-btn--send:hover { filter: brightness(1.12); }
.icon-btn--send svg { fill: currentColor; stroke: none; }

/* ── Поля ────────────────────────────────────────────────────────────────── */

.field { display: block; margin-bottom: 18px; }
.field__label { display: block; font-size: 0.82rem; color: var(--dim); margin-bottom: 7px; }

.field input, .field textarea, .sidebar__search input {
    width: 100%;
    padding: 13px 15px;
    background: var(--input-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    transition: border-color 0.18s var(--ease);
    resize: none;
}
.field input:focus, .field textarea:focus { border-color: var(--accent); outline: none; }
.field textarea { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-size: 0.9rem; }

/*
   Выпадающий список.

   Раньше он рисовался инлайновым стилем, где был задан только фон:
   цвет текста оставался системным, то есть тёмным на тёмном. Прочесть
   выбранного человека было нельзя. Цвет задаётся обоим — и полю, и
   пунктам списка: пункты рисует система, и свой цвет от поля они
   наследуют не везде.
*/
.select {
    width: 100%;
    padding: 12px 14px;
    margin-top: 14px;
    background: var(--input-surface);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font: inherit;
    cursor: pointer;
}
.select:focus { border-color: var(--accent); outline: none; }
.select option {
    /* Непрозрачная поверхность: пункты списка рисует система поверх
       своего фона, и полупрозрачный цвет там снова даёт нечитаемое. */
    background: var(--surface);
    color: var(--text);
}

/* Кнопки выбора лица: фотография и смайлик — в одну строку под кружком. */
.profile-face {
    display: flex;
    justify-content: center;
    gap: 6px;
    margin-bottom: 16px;
    flex-wrap: wrap;
}

.checkbox { display: flex; align-items: center; gap: 10px; margin-bottom: 18px; font-size: 0.9rem; color: var(--dim); cursor: pointer; }
.checkbox input { width: 17px; height: 17px; accent-color: var(--accent); cursor: pointer; }

/* ── Экран входа ─────────────────────────────────────────────────────────── */

.screen--auth { display: grid; place-items: center; padding: 24px; overflow-y: auto; }

.auth { width: 100%; max-width: 400px; }

.auth__logo { display: flex; align-items: center; justify-content: center; gap: 10px; margin-bottom: 38px; }

.logo-mark {
    display: grid; place-items: center;
    width: 46px; height: 46px;
    background: linear-gradient(135deg, var(--accent), var(--accent-lift));
    border-radius: 13px;
    font-weight: 800; font-size: 1.5rem;
    /* Знак лежит на насыщенной заливке, а не на подложке темы. */
    color: var(--on-accent);
    box-shadow: 0 6px 26px rgba(var(--accent-rgb), 0.38);
}
.logo-text { font-size: 1.6rem; font-weight: 700; }

.auth h1 { font-size: 1.6rem; font-weight: 700; margin-bottom: 10px; }
.auth__hint { color: var(--dim); font-size: 0.94rem; margin-bottom: 26px; }
.auth__note { color: var(--dim); font-size: 0.85rem; margin-top: 20px; text-align: center; }

.auth__error {
    margin-top: 18px; padding: 13px 15px;
    background: var(--danger-soft);
    border: 1px solid var(--danger-border);
    border-radius: var(--radius-sm);
    color: var(--danger);
    font-size: 0.9rem;
}

.auth__warning {
    margin-top: 34px; padding-top: 20px;
    border-top: 1px solid var(--border);
    color: var(--dim); font-size: 0.8rem; text-align: center;
}

.recovery {
    background: var(--surface);
    border: 1px solid rgba(var(--accent-rgb), 0.4);
    border-radius: var(--radius);
    padding: 18px;
    margin-bottom: 20px;
    text-align: center;
}
.recovery code {
    display: block;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.83rem;
    line-height: 1.75;
    word-break: break-all;
    color: var(--text);
    margin-bottom: 14px;
    user-select: all;
}

/* ── Раскладка приложения ────────────────────────────────────────────────── */

/*
   minmax(0, 1fr), а не 1fr.
   
   Дорожка 1fr по умолчанию не уже своего содержимого, и одно длинное имя
   группы в списке чатов растягивало колонку, а вместе с ней и всю страницу:
   появлялась горизонтальная прокрутка, а обрезание имени многоточием так
   и не срабатывало — обрезать было нечего, место «нашлось».
*/
.screen--app { display: grid; grid-template-columns: var(--sidebar-w) minmax(0, 1fr); }

/* ── Боковая панель ──────────────────────────────────────────────────────── */

/*
   Панели «стеклянные»: полупрозрачная заливка поверх живого фона плюс
   размытие. Так же устроен интерфейс приложения — сплошная заливка
   перекрыла бы градиент и выглядела бы иначе.
*/
.sidebar {
    display: flex; flex-direction: column;
    /* Иначе длинное имя в строке чата задаёт ширину всей колонке. */
    min-width: 0;
    background: var(--bar);
    backdrop-filter: blur(18px);
    border-right: 1px solid var(--border);
    min-height: 0;
}

.sidebar__head {
    display: flex; align-items: center; gap: 12px;
    padding: 14px 16px;
    border-bottom: 1px solid var(--border);
}

.sidebar__me { flex: 1; min-width: 0; }
.sidebar__name { font-weight: 600; font-size: 0.95rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sidebar__status { font-size: 0.78rem; color: var(--dim); }
.sidebar__status.is-online { color: var(--online); }
.sidebar__status.is-offline { color: var(--danger); }

.sidebar__search { padding: 12px 16px; }
.sidebar__search input { padding: 10px 14px; font-size: 0.9rem; }

.tabs { display: flex; padding: 0 16px; gap: 6px; border-bottom: 1px solid var(--border); }

.tab {
    padding: 10px 14px;
    font-size: 0.88rem; font-weight: 600; color: var(--dim);
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    transition: color 0.18s var(--ease), border-color 0.18s var(--ease);
}
.tab:hover { color: var(--text); }
.tab--active { color: var(--text); border-bottom-color: var(--accent); }

.list { flex: 1; overflow-y: auto; padding: 8px; min-height: 0; }

.list__empty { padding: 40px 20px; text-align: center; color: var(--dim); font-size: 0.88rem; }

/* ── Строка чата ─────────────────────────────────────────────────────────── */

.row {
    display: flex; align-items: center; gap: 12px;
    width: 100%;
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    text-align: left;
    transition: background 0.15s var(--ease);

    /*
     * Закрепление вызывается удержанием, а браузер на телефоне отвечает на
     * то же движение своим: выделяет текст и показывает всплывающее меню
     * «Копировать». Строка списка — не текст для копирования, и меню поверх
     * неё только мешает попасть по чату.
     */
    -webkit-touch-callout: none;
    user-select: none;
    -webkit-user-select: none;
}
.row:hover { background: var(--hover); }
.row--active { background: rgba(var(--accent-rgb), 0.18); }
.row--active:hover { background: rgba(var(--accent-rgb), 0.26); }

.row__body { flex: 1; min-width: 0; }
.row__top { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.row__name { font-weight: 600; font-size: 0.92rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row__time { font-size: 0.72rem; color: var(--muted); flex-shrink: 0; }
.row__preview {
    font-size: 0.84rem; color: var(--dim);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    display: flex; align-items: center; gap: 5px;
}

/* Группа: фиолетовая полоса слева и лёгкая подсветка — как в приложении. */
.row--group {
    position: relative;
    background: rgba(var(--accent-rgb), 0.06);
}
.row--group::before {
    content: "";
    position: absolute;
    left: 0; top: 22%;
    width: 3px; height: 56%;
    border-radius: 2px;
    background: var(--accent);
}

/*
 * Метка типа чата в списке.
 *
 * Класс раньше назывался .chip и совпадал с классом кнопок в окне
 * оформления. Правило кнопок стояло в файле ниже и с той же силой
 * перебивало и цвет, и отступы: метки «Группа» и «Новости» теряли свой
 * цвет и раздувались до размера кнопки.
 */
.tag {
    flex-shrink: 0;
    padding: 1px 7px;
    border-radius: 100px;
    font-size: 0.62rem;
    font-weight: 800;
    letter-spacing: 0.03em;
    text-transform: uppercase;
}
.tag--group {
    color: var(--accent);
    background: rgba(var(--accent-rgb), 0.14);
    border: 1px solid rgba(var(--accent-rgb), 0.32);
}

/* Квадратный аватар группы */
.avatar--group { border-radius: 15px; }
.avatar--group.avatar--sm { border-radius: 11px; }

.badge {
    min-width: 20px; height: 20px;
    padding: 0 6px;
    background: var(--accent);
    color: var(--on-accent);
    border-radius: 10px;
    font-size: 0.72rem; font-weight: 700;
    display: grid; place-items: center;
    flex-shrink: 0;
}

/* ── Аватары ─────────────────────────────────────────────────────────────── */

.avatar {
    position: relative;
    display: grid; place-items: center;
    width: 44px; height: 44px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--accent), var(--accent-lift));
    font-weight: 700; font-size: 1rem;
    flex-shrink: 0;
    overflow: hidden;
    color: #fff;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }

/*
   Смайлик вместо фотографии.

   Вписывается целиком (contain) и с полем: смайлы не квадратные — от
   20x20 до 57x26, — и обрезка по кругу съела бы у широких половину.
*/
/* Селектор с типом, а не один класс: правило .avatar img выше по
   специфичности и иначе перебило бы contain обратно на cover. */
.avatar img.avatar__emoji {
    width: 78%; height: 78%;
    object-fit: contain;
}
.avatar--sm { width: 34px; height: 34px; font-size: 0.85rem; }
.avatar--btn { cursor: pointer; }

/* Аватар группы красится основным цветом, а тот бывает и светлым:
   буквы на нём берут посчитанный читаемый цвет, а не всегда белый. */
.avatar--group { color: var(--on-accent); }

.avatar[data-online="true"]::after {
    content: "";
    position: absolute; right: 1px; bottom: 1px;
    width: 11px; height: 11px;
    background: var(--online);
    border: 2px solid var(--surface);
    border-radius: 50%;
}

/* ── Область чата ────────────────────────────────────────────────────────── */

.chat { display: flex; flex-direction: column; min-width: 0; min-height: 0; background: transparent; }

.chat__empty { flex: 1; display: grid; place-content: center; justify-items: center; gap: 18px; color: var(--dim); }
.chat__empty-mark {
    display: grid; place-items: center;
    width: 76px; height: 76px;
    background: var(--glass);
    border: 1px solid var(--border);
    border-radius: 22px;
    font-size: 2.1rem; font-weight: 800;
    color: var(--accent);
}

.chat__inner { flex: 1; display: flex; flex-direction: column; min-height: 0; }

.chat__head {
    display: flex; align-items: center; gap: 12px;
    padding: 12px 16px;
    background: var(--bar);
    backdrop-filter: blur(18px);
    border-bottom: 1px solid var(--border);
}
.chat__back { display: none; }
.chat__title { flex: 1; min-width: 0; }
.chat__name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat__sub {
    font-size: 0.78rem; color: var(--dim);
    /* Состав группы бывает длиннее шапки — обрезаем, как и название. */
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chat__sub.is-typing { color: var(--info); }

/* ── Сообщения ───────────────────────────────────────────────────────────── */

/* ── Закреплённое сообщение ────────────────────────────────────────────── */

/*
 * Полоса над лентой. Не всплывающая и не поверх сообщений: закреплённое
 * должно быть видно постоянно, но не закрывать разговор.
 */
.pinned {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 6px 10px;
    border-bottom: 1px solid var(--border);
    background: var(--glass);
}

.pinned[hidden] { display: none; }

.pinned__body {
    flex: 1;
    /* minmax-ловушка та же, что была у названия группы: без min-width
       длинная строка растягивает всю колонку чата. */
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1px;
    padding: 2px 6px;
    border: 0;
    border-left: 3px solid var(--accent);
    border-radius: 0 8px 8px 0;
    background: none;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.pinned__label {
    color: var(--accent);
    font-size: 11px;
    font-weight: 600;
}

.pinned__text {
    max-width: 100%;
    color: var(--dim);
    font-size: 13px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Скрепка у закреплённого чата в списке: без неё непонятно, почему давняя
   переписка стоит выше свежей. */
.row__pin {
    flex: none;
    color: var(--dim);
    font-size: 11px;
    line-height: 1;
}

.messages {
    flex: 1;
    overflow-y: auto;
    padding: 20px 16px;
    display: flex; flex-direction: column; gap: 3px;
    scroll-behavior: smooth;
}

/*
   Сообщения не сжимаются.

   Элементы flex-колонки по умолчанию уменьшаются, когда содержимое не влезает
   в контейнер. Текст держится за счёт минимальной высоты строки, а картинка
   покорно сплющивается — стоило открыть клавиатуру или уменьшить окно,
   и фотографии превращались в узкие полоски.
*/
.messages > * { flex-shrink: 0; }

.day {
    align-self: center;
    margin: 16px 0 10px;
    padding: 4px 13px;
    background: var(--glass-strong);
    border: 1px solid var(--border);
    border-radius: 100px;
    font-size: 0.74rem;
    color: var(--dim);
}

.msg {
    max-width: min(72%, 560px);
    padding: 9px 13px;
    border-radius: 16px;
    font-size: 0.92rem;
    /* Плотный интервал: при 1.45 на экран помещалось заметно меньше
       сообщений, а читаемость от этого не выигрывала. */
    line-height: 1.25;
    word-wrap: break-word;
    position: relative;
    animation: msg-in 0.22s var(--ease);
}

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

.msg--in {
    align-self: flex-start;
    background: var(--bubble-in);
    border: 1px solid var(--bubble-in-border);
    border-bottom-left-radius: 5px;
}

/*
 * Своё сообщение.
 *
 * Цвет текста задаётся явно. Раньше он наследовался от body: в светлой теме
 * это тёмно-синий, и на фиолетовом пузыре сообщение почти не читалось.
 */
.msg--out {
    align-self: flex-end;
    background: linear-gradient(135deg, var(--accent), var(--accent-lift));
    color: var(--on-accent);
    border-bottom-right-radius: 5px;
}

/*
   Сообщение только с вложением рисуется без пузыря: рамка и подложка вокруг
   фотографии выглядят лишними, изображение само по себе прямоугольное.
   Время накладывается поверх нижнего угла с тенью, чтобы читалось
   на любом снимке.
*/
/*
   Ширина задаётся явно, а не через max-width.

   Пузырь сжимается по содержимому, а у изображения стоит width: 100% —
   процент от ширины родителя, которая сама определяется содержимым.
   Пока рядом был текст, ширину задавал он; без текста ссылка становится
   круговой и блок схлопывается в ноль — картинка просто исчезает.
*/
.msg--media-only {
    background: none !important;
    border: none !important;
    padding: 0;
    position: relative;
    overflow: hidden;
    border-radius: 14px;
    /* Высота задаётся содержимым и не урезается контейнером. */
    flex-shrink: 0;
}

.msg--media-only .msg__media { margin-bottom: 0; }

/* Кружок видеосообщения сохраняет свой размер. Класс ставится в разметке,
   а не через :has() — так правило работает и в старых браузерах. */
.msg--round { width: auto !important; overflow: visible; }
.msg--round .msg__media { width: auto; }

.msg--media-only .msg__meta {
    position: absolute;
    right: 8px;
    bottom: 8px;
    margin: 0;
    padding: 3px 8px;
    border-radius: 100px;
    background: var(--media-veil);
    color: #fff;
    backdrop-filter: blur(4px);
    opacity: 1;
}

.msg + .msg--in  { margin-top: 0; }
.msg--in  + .msg--out,
.msg--out + .msg--in { margin-top: 10px; }

.msg__author { font-size: 0.78rem; font-weight: 600; color: var(--info); margin-bottom: 3px; }

.msg__quote {
    /* Полоска цитаты берёт цвет текста пузыря: он разный у своих и чужих. */
    border-left: 3px solid currentColor;
    padding-left: 9px;
    margin-bottom: 6px;
    font-size: 0.84rem;
    opacity: 0.8;
}

/* ── Вложения ────────────────────────────────────────────────────────────── */

/*
   Миниатюра, а не полный размер. Изображение с телефона — это 1080×2400,
   и в ленте оно занимало пол-экрана, разрывая переписку. Ограничиваем
   высоту и обрезаем по центру: получается ровная карточка, а оригинал
   открывается по клику.
*/
/*
   width: auto, а не 100%.

   Пузырь сжимается по содержимому, поэтому процентная ширина картинки
   ссылается на ширину, которую сама же и определяет. Пока рядом был текст,
   ширину задавал он и всё выглядело нормально; в сообщении с одним лишь
   вложением зависимость становится круговой и блок схлопывается в ноль —
   фотография просто исчезает. С auto изображение берёт свой натуральный
   размер, ограниченный max-width, и родитель обхватывает его.
*/
.msg__media {
    display: block;
    width: auto;
    max-width: 260px;
    max-height: 300px;
    /* contain, а не cover: обрезка искажала кадр, а вписывание сохраняет
       пропорции — высокие снимки просто становятся уже. */
    object-fit: contain;
    border-radius: 12px;
    margin-bottom: 6px;
    background: var(--in-bubble);
    /* Никаких рамок и обводок — только само изображение. */
    border: none;
    outline: none;
}

.msg__media--clickable { cursor: zoom-in; transition: filter 0.18s var(--ease); }
.msg__media--clickable:hover { filter: brightness(1.08); }

/* Видеосообщение-кружок */
.msg__media--round {
    width: 220px;
    height: 220px;
    border-radius: 50%;
    object-fit: cover;
}

.msg__audio { width: 240px; max-width: 100%; margin-bottom: 4px; }

/* Заметка вместо не открывшегося вложения. */
.msg__broken {
    padding: 14px 16px;
    border-radius: 12px;
    background: var(--danger-soft);
    border: 1px dashed var(--danger-border);
    color: var(--danger);
    font-size: 0.85rem;
    white-space: nowrap;
}

.msg__file {
    display: inline-block;
    padding: 8px 12px;
    background: var(--in-bubble);
    border-radius: 10px;
    margin-bottom: 6px;
    color: inherit;
    text-decoration: none;
}
.msg__file:hover { background: var(--in-bubble-hover); text-decoration: none; }

/* ── Новости ─────────────────────────────────────────────────────────────── */

.news {
    align-self: stretch;
    padding: 14px 16px;
    margin-bottom: 8px;
    border-radius: 16px;
    background: var(--warning-soft);
    border: 1px solid var(--warning-border);
    animation: msg-in 0.22s var(--ease);
}

/* Подпись источника: в ленте несколько сайтов, и без неё непонятно,
   кто написал заголовок. */
.news__source {
    display: flex; align-items: center; gap: 6px;
    margin-bottom: 7px;
    font-size: 0.72rem; font-weight: 700; letter-spacing: 0.02em;
    color: var(--warning);
}
.news__icon { width: 14px; height: 14px; border-radius: 3px; object-fit: contain; }

/* Иллюстрация к заголовку. Пропорции задаём заранее: без них лента
   прыгает по мере загрузки картинок. */
.news__figure {
    display: block;
    margin-bottom: 10px;
    border-radius: 12px;
    overflow: hidden;
    background: var(--glass);
    aspect-ratio: 16 / 9;
}
.news__image { display: block; width: 100%; height: 100%; object-fit: cover; }

/*
   Заголовок и анонс приходят с чужого сайта.

   Там встречаются адреса без пробелов и слова в сотню букв — такая строка
   не переносится сама и растягивала ленту сообщений вширь, вместе со всей
   страницей. Разрываем в любом месте: обрезать новость многоточием нельзя,
   её читают.
*/
.news__source,
.news__title,
.news__summary,
.news__link { overflow-wrap: anywhere; }

.news { min-width: 0; }
.news__title { font-size: 0.98rem; font-weight: 650; line-height: 1.35; margin-bottom: 6px; }
.news__summary { font-size: 0.86rem; line-height: 1.5; color: var(--dim); }

.news__footer {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    margin-top: 10px;
}
.news__link { font-size: 0.8rem; font-weight: 600; color: var(--warning); }

/* Кнопка «отложить» держится правого края, время — перед ней. */
.news__save {
    margin-left: auto;
    padding: 2px 6px;
    border: 0;
    border-radius: 8px;
    background: none;
    font-size: 0.95rem;
    line-height: 1;
    cursor: pointer;
    opacity: 0.55;
    transition: opacity 0.15s;
}
.news__save:hover, .news__save:focus-visible { opacity: 1; }
.news__time { font-size: 0.72rem; color: var(--dim); }

/* Метка и акценты ленты в списке чатов */
.row--news { position: relative; background: var(--warning-tint); }
.row--news::before {
    content: "";
    position: absolute;
    left: 0; top: 22%;
    width: 3px; height: 56%;
    border-radius: 2px;
    background: var(--warning);
}

.tag--news {
    color: var(--warning);
    background: var(--warning-soft);
    border: 1px solid var(--warning-border);
}

.avatar--news { border-radius: 12px; background: var(--warning); }
/* Избранное: своя форма и цвет — это не человек и не лента. */
.avatar--saved { border-radius: 14px; background: var(--info); }
.avatar--news img { object-fit: contain; padding: 8px; }

/* Панель ленты вместо поля ввода: писать в новости некуда. */
.news-bar {
    display: flex; align-items: center; justify-content: center; gap: 10px;
    padding: 16px;
    background: var(--bar);
    backdrop-filter: blur(18px);
    border-top: 1px solid var(--border);
    color: var(--dim);
    font-size: 0.85rem;
}

/* ── Полноэкранный просмотр ──────────────────────────────────────────────── */

.lightbox {
    position: fixed;
    inset: 0;
    z-index: 300;
    display: grid;
    place-items: center;
    padding: 40px 20px;
    /* Под снимком всегда темно, в любой теме: так его видно целиком. */
    background: rgba(0, 0, 0, 0.92);
    backdrop-filter: blur(6px);
    animation: lightbox-in 0.18s var(--ease);
    cursor: zoom-out;
}

@keyframes lightbox-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/*
   Размер считается от окна, а не от родителя.

   Раньше стояло max-height: 100%, но контейнер — grid, и его высота сама
   определялась содержимым: сто процентов равнялись натуральной высоте
   изображения, то есть ограничения не было вовсе. Высокие фотографии
   выходили за экран.
*/
.lightbox__media {
    max-width: calc(100vw - 40px);
    max-height: calc(100dvh - 80px);
    width: auto;
    height: auto;
    object-fit: contain;
    border-radius: 8px;
    cursor: default;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
}

.lightbox__close,
.lightbox__download {
    position: absolute;
    top: 18px;
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.14);
    color: #fff;
    cursor: pointer;
    transition: background 0.18s var(--ease);
}
.lightbox__close { right: 18px; }
.lightbox__download { right: 74px; }

.lightbox__close:hover,
.lightbox__download:hover { background: rgba(255, 255, 255, 0.2); }

.msg__meta {
    display: flex; align-items: center; justify-content: flex-end; gap: 5px;
    font-size: 0.68rem;
    opacity: 0.72;
    margin-top: 3px;
}

/* ── Реакции ─────────────────────────────────────────────────────────────── */

.reactions { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }

/* ── Оформление текста сообщений ─────────────────────────────────────────── */

/* Переносы строк сохраняются: без этого вставленный текст
   схлопывался в одну простыню. */
.msg__text p { margin: 0 0 4px; white-space: pre-wrap; }
.msg__text p:last-child { margin-bottom: 0; }
.msg__text h1, .msg__text h2, .msg__text h3 {
    margin: 6px 0 3px;
    font-size: 1.04em;
    font-weight: 700;
    line-height: 1.2;
}
.msg__text ul { margin: 2px 0 4px; padding-left: 18px; }
.msg__text li { margin: 1px 0; }
/*
 * Ссылка подбирается под пузырь, а не под тему: своё сообщение лежит на
 * акценте, чужое — на подложке темы, и один цвет на оба случая не годится.
 */
.msg__text a { text-decoration: underline; word-break: break-all; }
.msg--in .msg__text a { color: var(--info); }
.msg--out .msg__text a { color: var(--on-accent); }
.msg__text blockquote {
    margin: 4px 0;
    padding-left: 9px;
    border-left: 3px solid currentColor;
    font-style: italic;
    opacity: 0.9;
}
.msg__text code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.88em;
    background: var(--in-bubble);
    border-radius: 4px;
    padding: 1px 4px;
}
/* Код не переносится по словам: перенос ломает отступы и делает его
   нечитаемым. Вместо этого блок прокручивается вбок. */
.msg__text pre {
    margin: 5px 0;
    padding: 8px 10px;
    border-radius: 10px;
    background: var(--code-bg);
    color: var(--code-text);
    overflow-x: auto;
}
.msg__text pre code {
    background: none;
    padding: 0;
    white-space: pre;
    font-size: 0.84em;
    line-height: 1.35;
}

/* ─── Опрос ──────────────────────────────────────────────────────────────────
   Собственных цветов у карточки нет. Она лежит либо на акцентном пузыре,
   либо на подложке темы, поэтому текст берётся у пузыря через currentColor,
   а заливки — из --in-bubble: серый с прозрачностью читается на обоих.
   Из-за этого карточке не нужен отдельный блок для светлой темы.
   ────────────────────────────────────────────────────────────────────────── */

/* Опрос занимает заданную ширину, а не подстраивается под самый длинный
   вариант: у опроса из «Да» и «Нет» пузырь съёживался до столбика,
   а вопрос переносился по одному слову в строку. */
.msg--poll {
    width: min(400px, 86%);
    max-width: min(400px, 86%);
}

.poll {
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-width: 0;
}

.poll__question {
    font-weight: 600;
    line-height: 1.35;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.poll__note {
    font-size: 0.74rem;
    opacity: 0.7;
}

.poll__options { display: flex; flex-direction: column; gap: 4px; }

.poll__option {
    position: relative;
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 8px 10px;
    border-radius: 10px;
    background: var(--in-bubble);
    color: currentColor;
    font-size: 0.9rem;
    text-align: left;
    cursor: pointer;
    overflow: hidden;
    transition: background 0.15s var(--ease);
}

.poll__option:hover:not(:disabled) { background: var(--in-bubble-hover); }
.poll__option:disabled { cursor: default; }

/* Свой выбор виден обводкой, а не заливкой: заливка уже занята полосой
   результата, и два оттенка одного цвета друг друга съедали. */
.poll__option--mine { box-shadow: inset 0 0 0 1.5px currentColor; }
.poll__option--right { box-shadow: inset 0 0 0 1.5px var(--online); }
.poll__option--wrong { box-shadow: inset 0 0 0 1.5px var(--danger); }

/* Содержимое строки лежит над полосой результата: иначе полупрозрачная
   полоса ложилась поверх текста и приглушала именно тот вариант,
   за который проголосовали чаще всего. */
.poll__mark,
.poll__label,
.poll__count { position: relative; z-index: 1; }

.poll__mark { flex-shrink: 0; opacity: 0.85; }

.poll__label {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 6px;
}

.poll__text { overflow-wrap: anywhere; }
.poll__by { font-size: 0.72rem; opacity: 0.6; }

.poll__count {
    flex-shrink: 0;
    font-size: 0.78rem;
    opacity: 0.8;
    font-variant-numeric: tabular-nums;
}

/* Полоса лежит под содержимым строки и показывает долю голосов. */
.poll__bar {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    z-index: 0;
    background: var(--in-bubble);
    pointer-events: none;
    transition: width 0.25s var(--ease);
}

.poll__foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    font-size: 0.76rem;
    opacity: 0.85;
}

.poll__action {
    color: currentColor;
    font-size: 0.76rem;
    text-decoration: underline;
    text-underline-offset: 2px;
    cursor: pointer;
    opacity: 0.8;
}
.poll__action:hover { opacity: 1; }

.poll__voters {
    display: flex;
    flex-direction: column;
    gap: 2px;
    font-size: 0.76rem;
    opacity: 0.75;
    overflow-wrap: anywhere;
}

/* ─── Дуэль ──────────────────────────────────────────────────────────────── */

.duel__row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 10px;
    border-radius: 10px;
    background: var(--in-bubble);
    font-size: 0.9rem;
}

.duel__name { flex: 1; min-width: 0; overflow-wrap: anywhere; }

.duel__score {
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* Результат, не сошедшийся с повтором, зачёркивается, а не прячется:
   человек должен видеть, что пришло и почему это не засчитано. */
.duel__score--bad { text-decoration: line-through; opacity: 0.6; }

.duel__mark { font-size: 0.72rem; opacity: 0.7; }

.duel__play { padding: 6px 16px; font-size: 0.82rem; }

/* ─── Мафия ──────────────────────────────────────────────────────────────── */

.mafia__me {
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: 8px 10px;
    border-radius: 10px;
    background: var(--in-bubble);
    font-size: 0.84rem;
}

.mafia__role { font-weight: 600; }
.mafia__dead { opacity: 0.7; }
.mafia__note { opacity: 0.9; overflow-wrap: anywhere; }

.mafia__player {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 7px 10px;
    border-radius: 10px;
    background: var(--in-bubble);
    color: currentColor;
    font-size: 0.9rem;
    text-align: left;
}

/* Кнопкой строка становится, только когда по ней можно ходить. */
button.mafia__player { cursor: pointer; }
button.mafia__player:hover { background: var(--in-bubble-hover); }

.mafia__player--picked { box-shadow: inset 0 0 0 1.5px currentColor; }
.mafia__player--out { opacity: 0.5; }

.mafia__name { flex: 1; min-width: 0; overflow-wrap: anywhere; }

.mafia__tag {
    flex-shrink: 0;
    font-size: 0.72rem;
    opacity: 0.75;
}

.mafia__log {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding-top: 4px;
    font-size: 0.78rem;
    opacity: 0.75;
}
.mafia__log-line { overflow-wrap: anywhere; }

/* Фишка в карточке настольной партии: цвет игрока виден без подписи. */
.board__disc {
    flex-shrink: 0;
    width: 13px;
    height: 13px;
    border-radius: 50%;
}
.board__disc--b { background: #12182B; box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.45); }
.board__disc--w { background: #F2F4F8; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.25); }

/* Игра занимает экран целиком поверх чата. Отдельная страница в рамке:
   ровно та же лежит в assets приложения и открывается там в WebView. */
.game-overlay {
    position: fixed;
    inset: 0;
    z-index: 500;
    background: var(--solid-backdrop);
    display: flex;
    align-items: center;
    justify-content: center;
}

.game-overlay__frame {
    width: min(100%, 480px);
    height: 100%;
    border: 0;
}

/* Окно создания опроса */
.poll-form { display: flex; flex-direction: column; gap: 12px; }
.poll-form__options { display: flex; flex-direction: column; gap: 6px; }

.poll-form__row { display: flex; align-items: center; gap: 8px; }
.poll-form__row .field { flex: 1; }

/* Кружок выбора правильного ответа появляется только в режиме викторины. */
.poll-form__right {
    flex-shrink: 0;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--in-bubble);
    color: var(--text);
    cursor: pointer;
}
.poll-form__right--on { background: var(--online); color: var(--solid-backdrop); }

.poll-form__drop {
    flex-shrink: 0;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    color: var(--muted);
    cursor: pointer;
}
.poll-form__drop:hover { background: var(--hover-strong); color: var(--danger); }

.poll-form__switches { display: flex; flex-direction: column; gap: 8px; }

.poll-form__switch {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 0.86rem;
    color: var(--dim);
    cursor: pointer;
}

.reaction {
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
    gap: 4px;
    padding: 2px 8px;
    border-radius: 100px;
    font-size: 0.82rem;
    background: var(--in-bubble);
    border: 1px solid transparent;
    cursor: pointer;
    transition: transform 0.12s var(--ease), background 0.18s var(--ease);
}
.reaction:hover { transform: scale(1.08); }
.reaction--mine { border-color: currentColor; background: var(--in-bubble-hover); }
.reaction__count { font-size: 0.72rem; opacity: 0.8; }

/*
   Кнопка реакции живёт внутри строки со временем.

   Первая версия ставила её снаружи пузыря (right: -34px), а видимость
   зависела от .msg:hover. Стоило подвести курсор к кнопке — он покидал
   сообщение, hover пропадал, кнопка гасла. Нажать её было невозможно.
   Внутри пузыря такой проблемы нет по построению.
*/
.msg { position: relative; }

.msg__react {
    width: 20px;
    height: 20px;
    margin-right: 2px;
    border-radius: 50%;
    color: currentColor;
    font-size: 0.8rem;
    line-height: 1;
    opacity: 0.45;
    cursor: pointer;
    transition: opacity 0.15s var(--ease), background 0.15s var(--ease), transform 0.12s var(--ease);
}

.msg:hover .msg__react { opacity: 0.9; }
.msg__react:hover { opacity: 1; background: var(--in-bubble-hover); transform: scale(1.15); }

/* У вложения без подписи строка времени лежит на плашке поверх снимка,
   поэтому кнопка там же и всегда различима. */
.msg--media-only .msg__react { opacity: 0.85; }

.picker-emoji {
    /*
       Положение задаёт fitPicker() в ui.js, отсчитывая от кнопки и от
       краёв экрана. Раньше здесь было absolute с bottom: 100%, и панель
       зависела от того, к чему её привязали: у пузыря сообщения всё
       сходилось, а в окне профиля «сто процентов» отсчитывались от окна
       во весь экран — панель уезжала под нижний край.
    */
    position: fixed;
    top: 0;
    left: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 4px;
    /*
       Ширину задаёт содержимое, а не пузырь сообщения. Без этого панель
       у короткой реплики («Ага») сжималась до ширины пузыря и давила
       смайлы. На узком экране лишнее переносится на следующую строку.
    */
    width: max-content;
    max-width: min(92vw, 356px);
    padding: 6px;
    border-radius: 14px;
    background: var(--surface);
    border: 1px solid var(--border);
    box-shadow: 0 10px 30px var(--shadow);
    /* Выше всего, включая окна и полноэкранный просмотр: панель всегда
       открывается последней и поверх того, из чего её позвали. */
    z-index: 600;
    animation: msg-in 0.15s var(--ease);
}

.picker-emoji__row { display: flex; flex-wrap: wrap; gap: 2px; }

/* Дополнительные реакции: сетка, чтобы длинный список не уезжал за экран. */
.picker-emoji__grid {
    display: grid;
    grid-template-columns: repeat(6, 46px);
    gap: 2px;
    padding-top: 4px;
    border-top: 1px solid var(--border);
}

.picker-emoji__more { font-size: 1rem; color: var(--dim); }

/*
   Анимированные эмодзи из набора приложения.

   Картинки разной формы — от 20×20 до 57×26 в исходнике. Пропорции задаёт
   разметка, здесь только запрет на сжатие: в строке реакций контейнер
   флексовый, и без этого широкие смайлы сплющивались.

   Раньше стояло image-rendering: pixelated — исходники были по 20-30
   пикселей, и сглаживание превращало их в мыло; пиксельные ступеньки
   казались меньшим злом. Набор emojis_x4 увеличен заранее (xBR, x4), и
   ступенек больше нет: обычное сглаживание теперь работает в плюс.
*/
.emoji {
    display: inline-block;
    vertical-align: -0.15em;
    flex-shrink: 0;
    object-fit: contain;
}

.picker-emoji__grid { max-height: 240px; overflow-y: auto; }

.picker-emoji__item {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 46px;
    height: 42px;
    flex-shrink: 0;
    border-radius: 10px;
    font-size: 1.15rem;
    line-height: 1;
    cursor: pointer;
    transition: transform 0.12s var(--ease), background 0.15s var(--ease);
}
.picker-emoji__item:hover { transform: scale(1.2); background: var(--hover); }

.msg__tick { letter-spacing: -2px; }
.msg--read .msg__tick { color: var(--on-accent); opacity: 1; }
.msg--failed .msg__meta { color: var(--on-accent); opacity: 1; font-weight: 600; }

.msg--service {
    align-self: center;
    background: none;
    color: var(--dim);
    font-size: 0.8rem;
    max-width: 80%;
    text-align: center;
}

/* ── Поле ввода ──────────────────────────────────────────────────────────── */

.composer {
    display: flex; align-items: flex-end; gap: 8px;
    padding: 12px 16px;
    background: var(--bar);
    backdrop-filter: blur(18px);
    border-top: 1px solid var(--border);
}

.composer textarea {
    flex: 1;
    max-height: 140px;
    padding: 11px 15px;
    background: var(--input-surface);
    border: 1px solid transparent;
    border-radius: 20px;
    resize: none;
    overflow-y: auto;
}
.composer textarea:focus { outline: none; border-color: var(--accent); }

/* ── Модальные окна ──────────────────────────────────────────────────────── */

.modal { position: fixed; inset: 0; z-index: 100; display: grid; place-items: center; padding: 20px; }

.modal__backdrop { position: absolute; inset: 0; background: var(--scrim); backdrop-filter: blur(3px); }

.modal__box {
    position: relative;
    width: 100%; max-width: 430px;
    max-height: 86dvh;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    display: flex; flex-direction: column;
    animation: modal-in 0.2s var(--ease);
}

@keyframes modal-in {
    from { opacity: 0; transform: translateY(12px) scale(0.98); }
    to   { opacity: 1; transform: none; }
}

.modal__head {
    display: flex; align-items: center; justify-content: space-between; gap: 16px;
    padding: 18px 20px;
    border-bottom: 1px solid var(--border);
}
.modal__head h2 { font-size: 1.05rem; font-weight: 650; }

.modal__body { padding: 20px; overflow-y: auto; }
.modal__body p { color: var(--dim); font-size: 0.9rem; margin-bottom: 14px; }
.modal__body .btn + .btn { margin-top: 9px; }

.member {
    display: flex; align-items: center; gap: 11px;
    padding: 9px 4px;
}
.member__name { flex: 1; font-size: 0.9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.member__role { font-size: 0.76rem; color: var(--dim); }

.picker { max-height: 260px; overflow-y: auto; margin-bottom: 18px; }
.picker label { display: flex; align-items: center; gap: 11px; padding: 9px 4px; cursor: pointer; }
.picker input { width: 17px; height: 17px; accent-color: var(--accent); }

/* ── Уведомления ─────────────────────────────────────────────────────────── */

.toasts { position: fixed; bottom: 22px; left: 50%; transform: translateX(-50%); z-index: 200; display: flex; flex-direction: column; gap: 9px; align-items: center; }

.toast {
    padding: 11px 20px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 100px;
    font-size: 0.88rem;
    line-height: 1.4;
    text-align: center;
    box-shadow: 0 8px 28px var(--shadow);
    animation: toast-in 0.25s var(--ease);
    max-width: min(90vw, 460px);
}

/*
 * Сообщение об ошибке.
 *
 * Подложкой был только полупрозрачный оттенок --danger-soft, и поверх
 * затемнённого модального окна текст едва читался. Красим в два слоя:
 * снизу непрозрачная поверхность, сверху оттенок.
 *
 * Скругление меньше: длинная ошибка переносится на две-три строки, и на
 * «таблетке» со скруглением 100px строки упирались в закругления.
 */
.toast--error {
    background: linear-gradient(var(--danger-soft), var(--danger-soft)), var(--surface);
    border-color: var(--danger-border);
    color: var(--danger);
    border-radius: 16px;
    z-index: 1;
}
.toast--ok { border-color: rgba(var(--accent-rgb), 0.45); }

@keyframes toast-in {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: none; }
}

/* ── Мобильная раскладка ─────────────────────────────────────────────────── */

@media (max-width: 780px) {
    .screen--app { grid-template-columns: minmax(0, 1fr); }

    .sidebar { grid-area: 1 / 1; }
    .chat { grid-area: 1 / 1; }

    .screen--app:not(.is-chat-open) .chat { display: none; }
    .screen--app.is-chat-open .sidebar { display: none; }

    .chat__back { display: grid; }

    .msg { max-width: 84%; }

    /* Вырез камеры и полоса жестов: без учёта безопасных зон нижняя
       панель ввода уезжает под системную полосу и её невозможно нажать. */
    .sidebar__head,
    .chat__head { padding-top: max(12px, env(safe-area-inset-top)); }

    .composer {
        padding-bottom: max(12px, env(safe-area-inset-bottom));
        padding-left: max(12px, env(safe-area-inset-left));
        padding-right: max(12px, env(safe-area-inset-right));
    }

    /* Пальцем в 38 пикселей попадать неудобно — минимум 44. */
    .icon-btn { width: 44px; height: 44px; }
    .tab { padding: 12px 16px; }
    .row { padding: 12px; }

    /* На телефоне поля со шрифтом меньше 16px заставляют браузер
       увеличивать масштаб при фокусе. */
    .field input,
    .field textarea,
    .sidebar__search input,
    .composer textarea { font-size: 16px; }

    /* Миниатюры под ширину экрана, но не во всю высоту. */
    .msg__media { max-width: min(220px, 60vw); max-height: 260px; }
    .msg__media--round { width: 180px; height: 180px; }
    .msg__audio { width: min(220px, 60vw); }

    /* Полноэкранный просмотр: кнопки крупнее и ниже выреза камеры. */
    .lightbox { padding: 16px; }
    .lightbox__close,
    .lightbox__download {
        top: max(16px, env(safe-area-inset-top));
        width: 48px;
        height: 48px;
    }
    .lightbox__download { right: 78px; }

    /* Модальное окно занимает почти весь экран и не обрезается сверху. */
    .modal { padding: 12px; align-items: end; }
    .modal__box { max-height: 88dvh; }

    .toasts { bottom: max(16px, env(safe-area-inset-bottom)); width: 100%; padding: 0 16px; }
    .toast { width: 100%; text-align: center; border-radius: 14px; }
}

/* Совсем узкие экраны */
@media (max-width: 380px) {
    .msg { max-width: 90%; }
    .messages { padding: 16px 10px; }
    .avatar { width: 40px; height: 40px; }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
    .messages { scroll-behavior: auto; }
}


/* ── Ответ и правка ───────────────────────────────────────────────────────── */

.compose-ctx {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 14px;
    background: var(--hover);
    border-top: 1px solid var(--border);
}

.compose-ctx__body {
    flex: 1;
    min-width: 0;
    border-left: 3px solid var(--accent);
    padding-left: 9px;
    line-height: 1.25;
}

.compose-ctx__kind {
    display: block;
    font-size: 0.76rem;
    font-weight: 600;
    color: var(--accent);
}

.compose-ctx__text {
    display: block;
    font-size: 0.84rem;
    color: var(--dim);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.msg__edited {
    font-size: 0.72rem;
    opacity: 0.55;
    font-style: italic;
}

/* ── Список действий над сообщением ──────────────────────────────────────── */

.sheet-backdrop {
    position: fixed;
    inset: 0;
    z-index: 300;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    background: var(--scrim);
    animation: fade-in 0.15s var(--ease);
}

.sheet {
    width: min(420px, 100%);
    margin: 12px;
    border-radius: 16px;
    overflow: hidden;
    background: var(--surface);
    border: 1px solid var(--border);
    animation: sheet-up 0.18s var(--ease);
}

.sheet__item {
    display: block;
    width: 100%;
    padding: 15px 18px;
    text-align: left;
    font-size: 0.95rem;
    border-bottom: 1px solid var(--border);
    transition: background 0.15s var(--ease);
}
.sheet__item:hover { background: var(--hover); }
.sheet__item--danger { color: var(--danger); }
.sheet__item--cancel { color: var(--dim); border-bottom: none; font-weight: 600; }

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


/* ── Блок кода ───────────────────────────────────────────────────────────── */

.msg__text pre.code {
    position: relative;
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0 10px;
    margin: 6px 0;
    padding: 10px 12px;
    border-radius: 10px;
    background: var(--code-bg);
    /*
     * Цвет текста задаётся явно.
     *
     * Без этого он наследовался от пузыря сообщения: в светлой теме — тёмный,
     * и на тёмной подложке кода всё нераскрашенное просто исчезало. Видны
     * оставались только ключевые слова и строки, у которых цвет свой.
     */
    color: var(--code-text);
    border: 1px solid var(--code-border);
    overflow-x: auto;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.82rem;
    line-height: 1.45;
}

/* Номера строк отдельной колонкой: в текст их класть нельзя, иначе они
   попадут в буфер обмена вместе с кодом. */
.msg__text .code__gutter {
    display: flex;
    flex-direction: column;
    text-align: right;
    user-select: none;
    color: var(--code-dim);
    border-right: 1px solid var(--code-border);
    padding-right: 10px;
}

.msg__text .code__body { display: flex; flex-direction: column; white-space: pre; }
.msg__text .code__line { min-height: 1.45em; }

.msg__text .code__lang {
    position: absolute;
    top: 6px;
    right: 10px;
    font-size: 0.7rem;
    color: var(--code-dim);
    user-select: none;
}

/* Палитра близка к тёмной теме редакторов: ключевые слова холодные,
   строки тёплые, комментарии приглушены. */
.tok--key   { color: #C792EA; }
.tok--str   { color: #C3E88D; }
.tok--num   { color: #F78C6C; }
.tok--com   { color: #5F7E97; font-style: italic; }
.tok--punct { color: #89DDFF; }


/* ── Выбор оформления ────────────────────────────────────────────────────── */

.chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; }

/* Кнопка выбора в окне оформления. Раньше называлась .chip и молча
   перекрашивала метки типа чата — теперь у них разные имена. */
.choice {
    padding: 9px 14px;
    border-radius: 10px;
    font-size: 0.85rem;
    color: var(--dim);
    background: var(--input-surface);
    transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.choice:hover { background: var(--hover-strong); color: var(--text); }
.choice--on { background: var(--accent); color: var(--on-accent); }
.choice--on:hover { background: var(--accent); color: var(--on-accent); }

.swatches { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 18px; }

.swatch {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    border: 2px solid transparent;
    transition: transform 0.12s var(--ease);
}
.swatch:hover { transform: scale(1.08); }
.swatch--on { border-color: var(--text); }


/* ── Выбор новостей по интересам ─────────────────────────────────────────── */

.hint {
    font-size: 0.78rem;
    line-height: 1.5;
    color: var(--muted);
    margin: 0 0 12px;
}

/* Блок «каталог не загрузился»: объяснение и кнопка повтора. */
.notice { margin-bottom: 18px; }

.url-row { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
.url-row input {
    flex: 1;
    padding: 11px 13px;
    border-radius: 11px;
    border: 1px solid var(--border);
    background: var(--input-surface);
    color: var(--text);
    font-size: 0.9rem;
}
.url-row input:focus { border-color: var(--accent); outline: none; }

.src-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 18px; }

/* Строка источника: галочка слева, название и адрес — справа. */
.src {
    display: flex; align-items: center; gap: 11px;
    width: 100%;
    padding: 9px 12px;
    border-radius: 12px;
    text-align: left;
    background: var(--glass);
    border: 1px solid transparent;
    transition: background 0.15s var(--ease), border-color 0.15s var(--ease);
}
.src:hover:not(:disabled) { background: var(--hover-strong); }
.src--on { border-color: var(--accent); }
.src--locked { opacity: 0.6; }

.src__check {
    flex: 0 0 auto;
    width: 20px; height: 20px;
    display: flex; align-items: center; justify-content: center;
    border-radius: 6px;
    border: 1.5px solid var(--border);
    font-size: 0.72rem; line-height: 1;
    color: var(--on-accent);
}
.src--on .src__check { background: var(--accent); border-color: var(--accent); }

.src__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.src__name { font-size: 0.9rem; color: var(--text); }
.src__site {
    font-size: 0.75rem; color: var(--muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Сломанный источник виден сразу: иначе кажется, что не работает вся лента. */
.src__site--bad { color: var(--danger); white-space: normal; }

.src__remove {
    flex: 0 0 auto;
    width: 28px; height: 28px;
    border-radius: 8px;
    color: var(--dim);
    font-size: 0.8rem;
}
.src__remove:hover { background: var(--danger-soft); color: var(--danger); }

/*
 * Точечных правок под светлую тему здесь больше нет.
 *
 * Раньше их приходилось дописывать по одной, когда очередной элемент
 * оказывался нечитаемым: пузырь собеседника, встроенный код, цитата.
 * Теперь каждая роль описана в обеих палитрах наверху файла, и добавлять
 * исключения не нужно.
 */

/* ── «Наташа думает» ──────────────────────────────────────────────────────

   Над полем ввода: состояние, а не реплика, и в ленте ему делать нечего.
   Точки живые намеренно — неподвижная надпись через полминуты сама
   начинает выглядеть зависшей, то есть ровно наоборот смыслу. */
.thinking {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 16px;
    color: var(--dim);
    font-size: 0.82rem;
}

.thinking[hidden] { display: none; }

.thinking__dots { display: inline-flex; gap: 3px; }

.thinking__dots i {
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--info);
    animation: thinking-dot 1.2s infinite ease-in-out;
}
.thinking__dots i:nth-child(2) { animation-delay: 0.15s; }
.thinking__dots i:nth-child(3) { animation-delay: 0.3s; }

@keyframes thinking-dot {
    0%, 60%, 100% { opacity: 0.25; transform: translateY(0); }
    30%           { opacity: 1;    transform: translateY(-2px); }
}

.thinking__text {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ── Реплика бота ─────────────────────────────────────────────────────────

   Собеседница должна быть отличима от человека с первого взгляда. Метка
   рядом с именем плюс мягкая рамка: перекрашивать пузырь целиком не стоит,
   иначе бот выглядит важнее людей в той же комнате. */
.msg__botmark {
    margin-left: 6px;
    padding: 1px 5px;
    border-radius: 6px;
    background: var(--glass-strong);
    color: var(--dim);
    font-size: 0.68rem;
    font-weight: 600;
    text-transform: lowercase;
    vertical-align: middle;
}

.msg--bot.msg--in { border: 1px solid var(--info); }

/* Предупреждение о выходе из шифрования — не реплика в разговоре,
   а объявление: шире, спокойнее и без хвостика пузыря. */
.msg--bot-notice.msg--in {
    max-width: 100%;
    border-style: dashed;
    border-color: var(--warning);
    background: var(--warning-tint);
    color: var(--dim);
    font-size: 0.85rem;
    line-height: 1.45;
}

/* ── Широкий монитор ──────────────────────────────────────────────────────

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

   Ограничение ставится на содержимое, а не на саму колонку: подложка,
   разделители и фон по-прежнему во всю ширину, иначе окно выглядело бы
   обрезанным. */
@media (min-width: 1280px) {
    :root { --sidebar-w: 380px; }

    .messages {
        /* Отступы по бокам вместо жёсткой ширины: так центрируется
           и лента, и всё, что в ней, включая карточки новостей. */
        padding-left: max(16px, calc((100% - 900px) / 2));
        padding-right: max(16px, calc((100% - 900px) / 2));
    }

    .composer,
    .compose-ctx,
    .pinned {
        padding-left: max(16px, calc((100% - 900px) / 2));
        padding-right: max(16px, calc((100% - 900px) / 2));
    }

    /* Пузырь может стать шире: 560 пикселей на большом экране выглядели
       узкой лентой, хотя места вокруг было вдоволь. */
    .msg { max-width: min(72%, 640px); }
}

@media (min-width: 1800px) {
    :root { --sidebar-w: 420px; }

    /* Дальше расширять ленту незачем — предел ставит чтение, а не экран.
       Растёт только поле вокруг неё. */
    .messages,
    .composer,
    .compose-ctx,
    .pinned {
        padding-left: max(16px, calc((100% - 1000px) / 2));
        padding-right: max(16px, calc((100% - 1000px) / 2));
    }
}

/* ── Написать по номеру ──────────────────────────────────────────────────────
 *
 * Единственный способ начать переписку с новым человеком: общего каталога
 * пользователей у сервиса нет.
 */
.add-peer {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 8px;
    padding: 10px 12px 12px;
    border-bottom: 1px solid var(--border);
}

.add-peer input {
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: var(--navy);
    color: inherit;
    font: inherit;
    min-width: 0;
}

.add-peer input:focus { border-color: var(--accent); outline: none; }

.add-peer__hint {
    grid-column: 1 / -1;
    margin: 0;
    font-size: 0.76rem;
    color: var(--muted);
}

.add-peer__hint code {
    font-size: 0.95em;
    word-break: break-all;
}

/* ── Ход отправки вложения ────────────────────────────────────────────────
 *
 * Полоса поверх сообщения, пока файл уходит на сервер. Раньше отправка
 * большого файла ничем себя не выдавала: сообщение стояло в ленте, и было
 * непонятно, идёт что-то или уже сорвалось.
 */
.msg__upload {
    position: absolute;
    inset: auto 0 0 0;
    height: 3px;
    background: color-mix(in srgb, var(--accent) 22%, transparent);
    border-radius: 0 0 var(--radius-md, 12px) var(--radius-md, 12px);
    overflow: hidden;
    z-index: 2;
}

.msg__upload-bar {
    height: 100%;
    width: 2%;
    background: linear-gradient(90deg, var(--accent), var(--accent-lift, var(--accent)));
    transition: width .18s ease-out;
}

.msg--uploading { position: relative; }
.msg--uploading .msg__media { opacity: .72; }

/* Показано превью вместо полного файла: он не открылся или истёк по сроку. */
.msg__media--preview {
    filter: saturate(.9);
    outline: 1px dashed color-mix(in srgb, var(--text-secondary, #888) 45%, transparent);
    outline-offset: -1px;
}


/* ═══ Экран звонка ════════════════════════════════════════════════════════════
 *
 * Поверх всего и на весь экран. Разговор — это состояние, в котором
 * остальной интерфейс не нужен: на нём нельзя ни писать, ни листать, а
 * попытка уместить звонок в уголок означала бы, что кнопку «завершить»
 * приходится искать.
 *
 * Тёмная подложка одна на обе темы — так же, как в кино и в камере: на
 * светлом фоне видео выглядит как окно в стене, а лица уходят в серость.
 */
.call {
    position: fixed;
    inset: 0;
    z-index: 300;
    display: flex;
    flex-direction: column;
    background:
        radial-gradient(120% 80% at 50% 0%, rgba(var(--accent-rgb), .22), transparent 62%),
        radial-gradient(90% 70% at 80% 100%, rgba(0, 212, 255, .10), transparent 60%),
        #070B14;
    color: #F2F4F8;
    animation: call-in .22s ease-out;
    /* Панель управления не должна прятаться под вырезом и жестовой полосой. */
    padding-bottom: env(safe-area-inset-bottom, 0);
}

@keyframes call-in {
    from { opacity: 0; transform: scale(1.02); }
    to   { opacity: 1; transform: none; }
}

body.is-calling { overflow: hidden; }

.call__stage {
    position: relative;
    flex: 1;
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

/* ── Шапка: кто и сколько идёт разговор ──────────────────────────────────── */
.call__head {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    padding: calc(env(safe-area-inset-top, 0) + 22px) 20px 34px;
    text-align: center;
    /* Затемнение сверху: без него белая подпись пропадает на светлом кадре. */
    background: linear-gradient(180deg, rgba(0, 0, 0, .55), transparent);
    pointer-events: none;
}

.call__title {
    font-size: 21px;
    font-weight: 650;
    letter-spacing: -.01em;
    text-shadow: 0 1px 8px rgba(0, 0, 0, .5);
}

.call__status {
    margin-top: 4px;
    font-size: 14px;
    color: rgba(242, 244, 248, .72);
    font-variant-numeric: tabular-nums;
}

.call__warn {
    display: inline-block;
    margin-top: 10px;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
    color: #1A1200;
    background: var(--warning, #FFB020);
}

/* ── Голосовой звонок: портрет ───────────────────────────────────────────── */
.call__portrait {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 18px;
}

.call__ava {
    position: relative;
    width: 148px;
    height: 148px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    overflow: hidden;
    font-size: 58px;
    font-weight: 600;
    color: #fff;
    background: linear-gradient(140deg, var(--accent), var(--accent-lift));
    box-shadow: 0 18px 60px rgba(var(--accent-rgb), .45);
}

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

/*
 * Пульсация, пока идёт дозвон.
 *
 * Экран без движения на десятой секунде ожидания читается как зависший —
 * человек кладёт трубку и пробует снова, обрывая работающий вызов.
 */
.call__ava--pulse::after {
    content: '';
    position: absolute;
    inset: -18px;
    border-radius: 50%;
    border: 2px solid rgba(var(--accent-rgb), .55);
    animation: call-pulse 1.9s ease-out infinite;
}

@keyframes call-pulse {
    0%   { transform: scale(.92); opacity: .85; }
    70%  { transform: scale(1.25); opacity: 0; }
    100% { transform: scale(1.25); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .call__ava--pulse::after { animation: none; opacity: .4; }
    .call { animation: none; }
}

.call__members { font-size: 14px; color: rgba(242, 244, 248, .66); }

/* Звук собеседника: элемент нужен, но показывать его нечего. */
.call__sound { display: none; }

/* ── Видео ───────────────────────────────────────────────────────────────── */
.call__grid {
    width: 100%;
    height: 100%;
    display: grid;
    gap: 4px;
}

.call__grid--1 { grid-template-columns: 1fr; }
.call__grid--2 { grid-template-columns: 1fr; grid-template-rows: 1fr 1fr; }
.call__grid--3,
.call__grid--4 { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; }

@media (min-width: 760px) {
    .call__grid--2 { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr; }
}

.call__tile {
    position: relative;
    background: #0B1020;
    overflow: hidden;
    border-radius: 14px;
}

.call__video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.call__tile-name {
    position: absolute;
    left: 10px;
    bottom: 10px;
    display: flex;
    align-items: center;
    gap: 6px;
    max-width: calc(100% - 20px);
    padding: 4px 10px;
    border-radius: 999px;
    font-size: 12px;
    background: rgba(0, 0, 0, .55);
    backdrop-filter: blur(8px);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/*
 * Своё изображение — маленьким окном в углу.
 *
 * Зеркальное: человек привык видеть себя в зеркале, и незеркальная
 * картинка сбивает — трудно понять, в какую сторону подвинуться.
 */
.call__self {
    position: absolute;
    right: 14px;
    bottom: 14px;
    width: 108px;
    aspect-ratio: 3 / 4;
    object-fit: cover;
    border-radius: 14px;
    border: 1px solid rgba(255, 255, 255, .18);
    box-shadow: 0 10px 30px rgba(0, 0, 0, .55);
    transform: scaleX(-1);
    background: #0B1020;
    z-index: 2;
}

@media (min-width: 760px) { .call__self { width: 160px; } }

/* ── Панель управления ───────────────────────────────────────────────────── */
.call__bar {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 16px;
    padding: 22px 18px 26px;
    background: linear-gradient(0deg, rgba(0, 0, 0, .62), transparent);
}

.call__bar--incoming { gap: 64px; }

/*
 * Кнопки крупные намеренно.
 *
 * Отвечают на звонок вслепую, на ходу, одной рукой. Стандартные 40 пикселей
 * здесь означают промах, а промах — это сброшенный вместо принятого звонок.
 */
.call__btn {
    width: 60px;
    height: 60px;
    border: none;
    border-radius: 50%;
    display: grid;
    place-items: center;
    cursor: pointer;
    color: #F2F4F8;
    background: rgba(255, 255, 255, .14);
    backdrop-filter: blur(10px);
    transition: transform .12s ease, background .15s ease;
}

.call__btn:hover  { background: rgba(255, 255, 255, .22); }
.call__btn:active { transform: scale(.93); }

.call__btn:focus-visible {
    outline: 2px solid var(--accent-lift);
    outline-offset: 3px;
}

/* Выключено — заливка светлая, значок тёмный: состояние видно издалека
   и не сводится к одному лишь перечёркнутому значку. */
.call__btn--toggle.is-off {
    background: #F2F4F8;
    color: #121826;
}

.call__btn--end,
.call__btn--decline {
    width: 68px;
    height: 68px;
    background: #F2415F;
    color: #fff;
    box-shadow: 0 10px 30px rgba(242, 65, 95, .45);
}

.call__btn--end:hover,
.call__btn--decline:hover { background: #FF5C77; }

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

.call__btn--accept {
    width: 68px;
    height: 68px;
    background: #2DD4A7;
    color: #062018;
    box-shadow: 0 10px 30px rgba(45, 212, 167, .42);
    animation: call-ring 1.4s ease-in-out infinite;
}

@keyframes call-ring {
    0%, 100% { transform: none; }
    50%      { transform: scale(1.07); }
}

@media (prefers-reduced-motion: reduce) {
    .call__btn--accept { animation: none; }
}

/* ── Код безопасности ────────────────────────────────────────────────────────
 *
 * Числа набраны моноширинным и разрежены: их сверяют голосом, а не читают.
 */
.safety {
    margin: 18px 0;
    padding: 14px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: rgba(255, 255, 255, 0.03);
}

.safety h4 { margin: 0 0 6px; font-size: 0.95rem; }

.safety__hint,
.safety__warn,
.safety__none {
    margin: 0;
    font-size: 0.84rem;
    line-height: 1.45;
    color: var(--dim);
}

.safety__warn { color: var(--warning, #ffb020); }

.safety__code {
    display: block;
    margin-top: 12px;
    padding: 12px;
    border-radius: 8px;
    background: var(--navy);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 1rem;
    letter-spacing: 0.06em;
    text-align: center;
    word-spacing: 0.35em;
}

/*
 * Строка устройства в списке «Мои устройства».
 *
 * Кнопка «Забыть» прижата вправо и не растягивается: в списке из трёх
 * телефонов взгляд ищет не её, а строку «последний раз».
 */
.device {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    margin-top: 8px;
    border-radius: 12px;
    background: var(--input-surface);
}

.device__body {
    flex: 1;
    min-width: 0;
}
