/* ============================================================
   Moodozvon — redesigned UI
   Tokens + components + screen layouts (mobile-first, responsive).
   ============================================================ */

/* Регистрируем accent-токены как <color>, чтобы CSS-transitions работали
   при смене значения через setProperty (нужно для intro-анимации). */
@property --accent {
    syntax: '<color>';
    inherits: true;
    initial-value: #667eea;
}
@property --accent-ink {
    syntax: '<color>';
    inherits: true;
    initial-value: #ffffff;
}
/* Затемнённый вариант акцента — из него получается --accent-text в светлой
   теме (см. ACCENT_PALETTE.deep в UIManager.js). Регистрируется тоже как
   <color>, чтобы intro-анимация перетекала им синхронно с --accent. */
@property --accent-deep {
    syntax: '<color>';
    inherits: true;
    initial-value: #4B50C0;
}

/* Цветные эмодзи — свои, а не системные. Без этого один и тот же смайлик
   выглядит по-разному у каждого: на Windows блёклый Segoe UI Emoji, на Android
   Noto, на iOS Apple. Twemoji Mozilla — COLRv0-сборка твиттеровского пака
   (v0.7.0, Twemoji 14): обычный шрифт, поэтому работает и в старых Safari/
   Firefox, и не требует подмены текста на <img> в JS.

   unicode-range ОБЯЗАТЕЛЕН, а не оптимизация: в шрифте есть глифы для цифр
   0-9, «#» и «*» (они нужны для клавиш-эмодзи 1️⃣#️⃣), а также ©, ®, ™.
   Первым в стеке и без ограничения он бы утащил в эмодзи всю нумерацию в
   интерфейсе. Диапазоны ниже — только эмодзи-блоки, ASCII исключён целиком.
   Символы, которых в шрифте нет (— … → и прочая типографика), просто
   провалятся в следующий шрифт стека — unicode-range это не ломает. */
@font-face {
    font-family: "Twemoji Mozilla";
    src: url("fonts/twemoji-mozilla-v0.7.0.woff2") format("woff2");
    font-display: swap;  /* 480 КБ: сначала системные, подмена по загрузке */
    unicode-range:
        U+200D, U+20E3, U+FE0F,          /* ZWJ, клавиша, VS16 (эмодзи-вариант) */
        U+203C, U+2049, U+2139,
        U+2190-2BFF,                     /* стрелки, знаки, дингбаты */
        U+3030, U+303D, U+3297, U+3299,
        U+1F000-1FAFF,                   /* основные эмодзи-блоки */
        U+E0020-E007F;                   /* tag-символы (флаги регионов) */
}

/* ============================================================
   ТОКЕНЫ. Тёмная тема живёт в :root, светлая — в
   html[data-theme="light"] ниже. Здесь ЕДИНСТВЕННОЕ место, где
   объявляются цвета: остальные шесть CSS-файлов своих токенов не
   заводят и обязаны обращаться только через var(--…). Хардкод
   допустим лишь там, где фон под элементом цветной или чёрный
   в любой теме (заливка акцентом, лайтбокс, тело трансляции).
   ============================================================ */
:root {
    /* Surface */
    /* Подложка html/body. НЕ равна --bg: её рисует браузер за пределами
       документа (баунс overscroll, подложка свайпа «назад» на iOS), поэтому
       берём на полтона глубже базового фона. */
    --root-bg: #060609;
    --bg: #08080D;
    --bg-2: #0B0B12;
    --surface: #12121C;
    --surface-2: #1A1A26;
    --surface-3: #252533;
    --border: rgba(255, 255, 255, 0.06);
    --border-strong: rgba(255, 255, 255, 0.12);

    /* Слои — «на ступеньку выше» ПОВЕРХ нейтральной поверхности: покоящиеся
       чипы, ховеры кнопок, подсветка строки. На тёмной теме это осветляющий
       белый оверлей, на светлой — затемняющий чёрный: направление «выше»
       переворачивается вместе с темой, поэтому у них и отдельные токены, а не
       голая rgba. Лестница на 4 ступени, от еле заметной до сильного ховера. */
    --layer-1: rgba(255, 255, 255, 0.025);
    --layer-2: rgba(255, 255, 255, 0.05);
    --layer-3: rgba(255, 255, 255, 0.09);
    --layer-4: rgba(255, 255, 255, 0.15);

    /* «Колодец» — вдавленная кнопка на ТОНИРОВАННОЙ акцентом панели
       (войсбар сверху/плавающий/карточка). Там слой идёт вниз, а не вверх, и
       на светлой теме направление сохраняется — меняется только глубина. */
    --well: rgba(0, 0, 0, 0.3);
    --well-hover: rgba(0, 0, 0, 0.46);

    /* Стекло — полупрозрачные плавающие поверхности с backdrop-filter
       (тост, плавающий войсбар). Следуют теме. */
    --glass: rgba(18, 18, 28, 0.86);
    --glass-2: rgba(16, 16, 24, 0.92);

    /* Скрим под модалками. Тёмный в обеих темах (на светлой — мягче):
       затемнение — универсальный способ убрать фон из фокуса. */
    --scrim: rgba(0, 0, 0, 0.55);
    /* Тач-устройства получают плоскую заливку вместо blur — см. .modal-overlay. */
    --scrim-opaque: rgba(0, 0, 0, 0.72);

    /* Тени. Токенизирован только ЦВЕТ, геометрия остаётся у элемента: она
       несёт смысл (насколько высоко висит), а тема меняет лишь плотность —
       на светлом фоне тень обязана быть заметно прозрачнее, иначе грязь. */
    --shadow-1: rgba(0, 0, 0, 0.3);
    --shadow-2: rgba(0, 0, 0, 0.4);
    --shadow-3: rgba(0, 0, 0, 0.55);
    --shadow-4: rgba(0, 0, 0, 0.7);

    /* Пузырь ПОЛУЧЕННОГО сообщения и вложенный в него код. Отдельные токены,
       а не --surface-2/-3 напрямую: пузырь лежит на --bg, а код — на пузыре,
       и в светлой теме лестница переворачивается («выше» = темнее). Если
       красить пузырь через --surface-2, в светлой теме он оказывается на
       полтона СВЕТЛЕЕ фона чата (#F0F0F5 против #EDEDF2) и сливается с ним.
       В тёмной теме значения те же, что были. */
    --bubble: var(--surface-2);
    --bubble-code: var(--surface-3);
    /* Тень пузыря. В тёмной теме её нет (прозрачная): там пузырь отделяет от
       фона сама заливка. В светлой он белый, и глубину держит именно тень —
       заливкой её не передать, белее фона уже некуда. */
    --bubble-shadow: transparent;

    /* Бегунок тумблера/слайдера и его дорожка. Бегунок белый в обеих темах,
       поэтому дорожка в светлой теме не может быть просто --surface-3 (белое
       на светло-сером не читается) — ей нужен свой, заметно серее, токен. */
    --knob: #ffffff;
    --track: var(--surface-3);

    /* Text */
    --text: #F5F5F7;
    --text-2: rgba(255, 255, 255, 0.62);
    --text-dim: rgba(255, 255, 255, 0.38);

    /* Accent — переключается через AccentPicker (12 цветов из handoff).
       --accent-soft / --accent-glow производные от --accent через color-mix —
       автоматически пересчитываются при смене --accent (включая intro-анимацию).
       --accent / --accent-deep ставит JS (setAccent), --accent-text выбирает
       между ними тема: акцент подбирался против почти чёрного фона, и на белом
       Лимон с Мятой как ТЕКСТ дают 1.3:1. Заливки этим не страдают — у них
       свой --accent-ink на каждый цвет, — поэтому расходятся именно роли:
       --accent красит фон, --accent-text красит текст и бордеры. */
    --accent: #667eea;
    --accent-ink: #ffffff;
    --accent-deep: #4B50C0;
    --accent-text: var(--accent);
    --accent-soft: color-mix(in oklab, var(--accent) 16%, transparent);
    --accent-glow: color-mix(in oklab, var(--accent) 50%, transparent);

    /* Status */
    --danger: #FF466B;
    --danger-soft: rgba(255, 70, 107, 0.16);
    --ok: #2ED47A;
    --ok-soft: rgba(46, 212, 122, 0.16);
    --warn: #FFB13B;
    --warn-soft: rgba(255, 177, 59, 0.16);
    --offline: #5E5E72;

    /* Ореол вокруг акцентного ТЕКСТА (заголовки, логотип). Свечение —
       идиома тёмной темы: на светлом фоне тот же ореол читается как
       расфокус, поэтому там он гасится (см. светлый блок). */
    --text-glow: var(--accent-glow);

    /* Radius */
    --r-sm: 12px;
    --r-md: 18px;
    --r-lg: 26px;
    --r-xl: 36px;
    --r-pill: 999px;

    /* "Twemoji Mozilla" стоит ПЕРВЫМ намеренно: он ограничен unicode-range
       эмодзи-блоками (см. @font-face выше), на латиницу и кириллицу не влияет —
       их браузер берёт из следующих шрифтов стека. */
    --font: "Twemoji Mozilla", -apple-system, BlinkMacSystemFont, "SF Pro Display",
        "SF Pro Text", system-ui, "Helvetica Neue", "Inter", sans-serif;
    --mono: "Twemoji Mozilla", "SF Mono", "JetBrains Mono", ui-monospace, Menlo, monospace;
}

/* ============================================================
   СВЕТЛАЯ ТЕМА
   Атрибут (а не класс) ставит инлайн-скрипт в <head> ДО подключения
   стилей — иначе первый кадр будет тёмным. Значение всегда
   разрешённое, "dark" или "light": режим «как в системе» резолвит JS
   через matchMedia и пишет сюда результат. Поэтому prefers-color-scheme
   в CSS не дублируется — один набор токенов, одно место правки.
   Токены объявлены ровно те же, что в :root, и в том же порядке.
   ============================================================ */
html[data-theme="light"] {
    /* Лестница поверхностей ПЕРЕВЁРНУТА: на тёмной «светлее = выше», здесь
       «белее = выше». --surface (карточка, модалка, пузырь) — чистый белый и
       самый верхний слой; --surface-2/-3 сидят ПОВЕРХ него (чипы, ховеры
       строк, дорожки) и потому уходят в серое, а не в белое. Базовый фон
       страницы серее карточек — иначе белая карточка на белом фоне исчезает:
       глубину в светлом UI держат бордер и тень, а не заливка. */
    --root-bg: #E7E7EE;
    --bg: #EDEDF2;
    --bg-2: #F3F3F7;
    --surface: #FFFFFF;
    --surface-2: #F0F0F5;
    --surface-3: #E2E2EA;
    /* Обязаны стать тёмными-по-светлому, иначе бордеров просто не видно и
       вся вёрстка «плывёт» — на светлой теме именно они несут структуру. */
    --border: rgba(16, 16, 34, 0.1);
    --border-strong: rgba(16, 16, 34, 0.18);

    /* Слои переворачиваются: «выше» = темнее. */
    --layer-1: rgba(16, 16, 34, 0.03);
    --layer-2: rgba(16, 16, 34, 0.05);
    --layer-3: rgba(16, 16, 34, 0.09);
    --layer-4: rgba(16, 16, 34, 0.14);

    /* Колодец остаётся затемнением (панель под ним светлая), но много мягче:
       0.3 чёрного на светлой панели читалось бы как дырка. */
    --well: rgba(16, 16, 34, 0.06);
    --well-hover: rgba(16, 16, 34, 0.12);

    --glass: rgba(255, 255, 255, 0.86);
    --glass-2: rgba(255, 255, 255, 0.92);

    /* Скрим по-прежнему тёмный (так фон уходит из фокуса), но легче. */
    --scrim: rgba(12, 12, 24, 0.4);
    --scrim-opaque: rgba(12, 12, 24, 0.52);

    /* Та же геометрия у элементов, вчетверо меньше плотности: на белом тень
       тёмной темы выглядит как грязное пятно. Оттенок синеватый, не чёрный —
       нейтральная тень на светлом фоне читается как серая плёнка. */
    --shadow-1: rgba(20, 20, 45, 0.07);
    --shadow-2: rgba(20, 20, 45, 0.1);
    --shadow-3: rgba(20, 20, 45, 0.14);
    --shadow-4: rgba(20, 20, 45, 0.2);

    /* Полученный пузырь — белая карточка с мягкой тенью: «своё» сообщение
       залито акцентом справа, а полученное читается приподнятой плашкой
       слева. Код внутри уходит на ступень глубже, иначе он сливается уже
       с самим пузырём. */
    --bubble: #FFFFFF;
    --bubble-code: #EAEAF1;
    --bubble-shadow: rgba(20, 20, 45, 0.1);

    /* Бегунок остаётся белым, дорожка уходит заметно глубже --surface-3:
       иначе выключенный тумблер не отличить от включённого. */
    --knob: #ffffff;
    --track: #C9C9D6;

    /* Альфы подобраны не «на глаз», а по контрастности тёмной темы: там
       --text-2 даёт 7.3–7.8:1, а --text-dim 3.5:1 (роль «приглушённый»
       намеренно ниже AA — это метки времени и разделители). Прямая инверсия
       0.62/0.38 на светлом фоне давала бы 5.5 и 2.9 — заметно хуже тёмной,
       поэтому чернила плотнее. Проверено против --bg, --surface и --surface-2. */
    --text: #16161C;
    --text-2: rgba(20, 20, 32, 0.76);
    --text-dim: rgba(20, 20, 32, 0.58);

    /* Заливка остаётся ярким --accent (у неё есть свой --accent-ink), а текст
       и бордеры переезжают на затемнённый вариант из палитры. Все 12 значений
       проверены на ≥4.5:1 (WCAG AA) против --surface, --bg и --surface-2. */
    --accent-text: var(--accent-deep);
    /* На белом 16% акцента почти не видно — плашкам нужно больше пигмента. */
    --accent-soft: color-mix(in oklab, var(--accent) 14%, #ffffff);
    --accent-glow: color-mix(in oklab, var(--accent) 40%, transparent);

    /* Статусные — под AA на белом: исходные #FF466B/#2ED47A/#FFB13B дают
       3.1/2.4/2.1:1 и как текст на светлом нечитаемы. Взяты с запасом, чтобы
       проходить и на --bg (он темнее --surface и потому самый строгий), и НА
       СВОЕЙ ЖЕ мягкой плашке: чип «цвет на --*-soft» — самый частый паттерн, и
       тинт съедает ещё пол-ступени контраста. Минимум по набору — 4.58:1. */
    --danger: #B31235;
    --danger-soft: rgba(179, 18, 53, 0.1);
    --ok: #126E34;
    --ok-soft: rgba(18, 110, 52, 0.12);
    --warn: #8A4A00;
    --warn-soft: rgba(138, 74, 0, 0.12);
    /* Он же фон-фолбэк аватара под белыми инициалами — значение выбрано так,
       чтобы проходить с ОБЕИХ сторон: 4.7:1 как текст, 5.5:1 под белым. */
    --offline: #67677A;

    --text-glow: transparent;
}

/* ───────── Неон выключен (тумблер в настройках) ─────────
   Все accent-свечения проекта — box-shadow, text-shadow и radial-gradient в
   style.css / v2.css / admin.css — растут из двух токенов, поэтому точка
   отключения одна: гасим токены, а не сорок правил. Прозрачная тень рисуется
   как ничто, геометрия и раскладка не меняются.
   Блок обязан стоять ПОСЛЕ светлого: у html[data-theme="light"] та же
   специфичность (0,1,1), и спор решает порядок в файле.
   Исключения (свой color-mix вместо токена) — рядом со своими правилами:
   .accent-swatch (см. ниже) и .orbital-glow. */
html[data-neon="off"] {
    --accent-glow: transparent;
    --text-glow: transparent;
}
/* Блоб на посадочной — это чистое свечение (radial-gradient + blur), под
   выключенным неоном ему нечего показывать; орбитальные кольца остаются. */
html[data-neon="off"] .orbital-glow { display: none; }

html {
    /* Фон на корне: overscroll-«резинка» в PWA показывает фон именно html
       (за пределами body), без него в баунсе просвечивал белый холст. */
    background: var(--root-bg);
    /* Вторая половина той же истории — и именно она лечит белую полосу СЛЕВА
       при свайпе «назад» на iOS. Фон html закрывает вертикальный баунс, но
       горизонтальную «резинку» жеста навигации и подложку перехода между
       страницами рисует сам WebKit, а не документ: без объявленной
       color-scheme он берёт светлые UA-дефолты и красит эту область белым.
       Этим же переключателем разом чинятся системные скроллбары и нативные
       контролы. Дублируется инлайн-скриптом в <head> через style.colorScheme —
       он действует ещё до разбора CSS, поэтому первый кадр и снапшот жеста
       тоже уже правильного тона. */
    color-scheme: dark;
    /* Плавное перетекание акцента (нужно для intro-cycle и для пикера в настройках). */
    transition: --accent 0.18s ease, --accent-ink 0.18s ease, --accent-deep 0.18s ease;
}
html[data-theme="light"] { color-scheme: light; }
/* В сессии после intro-анимации ускоряем переходы при ручном выборе цвета. */
html.accent-ready {
    transition: --accent 0.25s ease, --accent-ink 0.25s ease, --accent-deep 0.25s ease;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
    font-family: var(--font);
    color: var(--text);
    background: var(--root-bg);
    min-height: 100vh;                       /* фолбэк для старых браузеров */
    min-height: var(--app-height, 100dvh);   /* iOS: точная видимая высота (см. calibrateViewportHeight) */
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    /* Убираем double-tap-to-zoom (app-like поведение); панорамирование и
       скролл сохраняются. Pinch-zoom отключён через <meta viewport>. */
    touch-action: manipulation;
}
button { font-family: inherit; cursor: pointer; border: 0; background: none; color: inherit; }
input, textarea, select { font-family: inherit; }
a { color: var(--accent-text); }

/* Иконочный SVG-спрайт: контейнер с символами, не должен влиять на поток. */
.icon-sprite {
    position: absolute;
    width: 0;
    height: 0;
    overflow: hidden;
}

/* Утилитарные модификаторы. */
.mz-btn--block { width: 100%; display: flex; }

/* ============================================================
   Shell — full-viewport screen with subtle neon backdrop
   ============================================================ */
body {
    overflow-x: hidden;
}
.app-shell {
    position: relative;
    display: flex;
    flex-direction: column;
    background: var(--bg);
    color: var(--text);
}
.app-shell::before {
    content: "";
    position: fixed;
    inset: -10%;
    background:
        radial-gradient(60% 50% at 14% 0%, color-mix(in oklab, var(--accent) 18%, transparent), transparent 60%),
        radial-gradient(50% 40% at 95% 100%, color-mix(in oklab, var(--accent) 10%, transparent), transparent 60%);
    pointer-events: none;
    z-index: 0;
    filter: saturate(1.1);
}
.app-shell > * { position: relative; z-index: 1; }

/* Screen visibility. */
.screen { display: none; flex-direction: column; }
.screen.active { display: flex; }

/* Setup/invite — растягиваются под viewport, но позволяют скролл, если контента больше. */
#setup-screen.active,
#invite-screen.active {
    min-height: 100vh;
    min-height: var(--app-height, 100dvh);
}

/* Room — строго на высоту viewport, чтобы чат скроллился внутри, а не вся страница. */
#room-screen.active {
    height: 100vh;
    height: var(--app-height, 100dvh);
    overflow: hidden;
}
/* Пока открыт экран комнаты — документ не скроллится (класс ставит app.js).
   Иначе iOS-панорамирование/overscroll сдвигают fixed-экран комнаты и
   прокручивают фон под drawer'ом участников. */
html.room-locked,
html.room-locked body {
    height: 100%;
    overflow: hidden;
    overscroll-behavior: none;
}

/* ============================================================
   Typography helpers
   ============================================================ */
.mz-h1 { font-size: 32px; font-weight: 800; letter-spacing: -0.025em; line-height: 1.05; margin: 0; }
.mz-h2 { font-size: 22px; font-weight: 700; letter-spacing: -0.02em; margin: 0; }
.mz-h3 { font-size: 17px; font-weight: 700; letter-spacing: -0.01em; margin: 0; }
.mz-mono { font-family: var(--mono); letter-spacing: 0; }

.mz-row { display: flex; align-items: center; }
.mz-col { display: flex; flex-direction: column; }
.mz-spacer { flex: 1; }

/* ============================================================
   Buttons
   ============================================================ */
.mz-btn {
    border: 0;
    border-radius: var(--r-pill);
    font-weight: 600;
    letter-spacing: -0.01em;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 12px 20px;
    font-size: 15px;
    height: 46px;
    background: var(--layer-2);
    color: var(--text);
    transition: transform 0.12s ease, background 0.15s ease, box-shadow 0.2s, opacity 0.15s;
    user-select: none;
}
.mz-btn:active { transform: scale(0.97); }
.mz-btn:disabled { opacity: 0.5; cursor: not-allowed; }

.mz-btn--lg { height: 56px; padding: 16px 24px; font-size: 16px; }
.mz-btn--sm { height: 34px; padding: 8px 14px; font-size: 13px; }

.mz-btn--primary {
    background: var(--accent);
    color: var(--accent-ink);
    box-shadow: 0 0 0 1px color-mix(in oklab, var(--accent) 30%, transparent),
        0 10px 30px -10px var(--accent-glow);
}
.mz-btn--primary:not(:disabled):hover {
    box-shadow: 0 0 0 1px color-mix(in oklab, var(--accent) 60%, transparent),
        0 14px 36px -8px var(--accent-glow);
}

.mz-btn--ghost {
    background: var(--layer-2);
    color: var(--text);
    border: 1px solid var(--border-strong);
}
.mz-btn--ghost:not(:disabled):hover { background: var(--layer-3); }

.mz-btn--danger {
    background: var(--danger-soft);
    color: var(--danger);
    border: 1px solid color-mix(in oklab, var(--danger) 30%, transparent);
}
.mz-btn--danger:not(:disabled):hover { background: color-mix(in oklab, var(--danger) 24%, transparent); }

/* Square / circular icon button */
.mz-icon-btn {
    width: 40px;
    height: 40px;
    padding: 0;
    border-radius: var(--r-pill);
    background: var(--layer-2);
    border: 1px solid var(--border);
    color: var(--text);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s, color 0.15s, box-shadow 0.2s;
    flex-shrink: 0;
}
.mz-icon-btn:not(:disabled):hover { background: var(--layer-3); }
.mz-icon-btn.is-active {
    background: var(--accent-soft);
    color: var(--accent-text);
    border-color: color-mix(in oklab, var(--accent) 35%, transparent);
    box-shadow: 0 0 16px -4px var(--accent-glow);
}
.mz-icon-btn.is-danger {
    background: var(--danger-soft);
    color: var(--danger);
    border-color: color-mix(in oklab, var(--danger) 30%, transparent);
}
.mz-icon-btn svg { width: 20px; height: 20px; }
.mz-icon-btn--sm { width: 32px; height: 32px; }
.mz-icon-btn--sm svg { width: 18px; height: 18px; }

/* ============================================================
   Inputs
   ============================================================ */
.mz-input {
    width: 100%;
    background: var(--surface-2);
    border: 1px solid var(--border);
    color: var(--text);
    border-radius: var(--r-md);
    padding: 12px 14px;
    font-size: 15px;
    outline: 0;
    transition: border-color 0.15s, background 0.15s;
}
.mz-input::placeholder { color: var(--text-dim); }
.mz-input:focus { border-color: color-mix(in oklab, var(--accent) 60%, transparent); }

.mz-input--mono {
    font-family: var(--mono);
    letter-spacing: 0.3em;
    text-transform: uppercase;
    text-align: center;
}

.mz-input--pill {
    border-radius: var(--r-pill);
    padding: 14px 22px;
}

/* ── Чекбоксы (кастомные, в стиле проекта) ────────────────────────────── */
input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    margin: 0;
    border: 1px solid var(--border-strong);
    border-radius: 6px;
    background: var(--surface-2);
    cursor: pointer;
    display: inline-grid;
    place-items: center;
    transition: background 0.15s ease, border-color 0.15s ease;
}
input[type="checkbox"]:hover {
    border-color: color-mix(in oklab, var(--accent) 55%, var(--border-strong));
}
input[type="checkbox"]:checked {
    background: var(--accent);
    border-color: var(--accent-text);
}
input[type="checkbox"]:checked::after {
    content: "";
    width: 5px;
    height: 9px;
    border: solid var(--accent-ink);
    border-width: 0 2px 2px 0;
    /* translateY компенсирует оптический сдвиг «галочки» вниз при повороте. */
    transform: translateY(-1px) rotate(45deg);
}
input[type="checkbox"]:focus-visible {
    outline: 2px solid var(--accent-soft);
    outline-offset: 2px;
}
input[type="checkbox"]:disabled { opacity: 0.5; cursor: not-allowed; }

/* ── Number-инпуты: прячем нативные стрелки, свой степпер −/+ ──────────── */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
input[type="number"] { -moz-appearance: textfield; appearance: textfield; }

.mz-stepper {
    display: inline-flex;
    align-items: stretch;
    width: 100%;
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    background: var(--surface-2);
    overflow: hidden;
}
.mz-stepper:focus-within { border-color: color-mix(in oklab, var(--accent) 60%, transparent); }
/* Инпут внутри степпера — без своей рамки/фона/радиуса (их несёт обёртка). */
.mz-stepper > input[type="number"] {
    flex: 1;
    min-width: 0;
    width: auto;
    border: 0;
    background: transparent;
    border-radius: 0;
    text-align: center;
    outline: 0;
}
.mz-stepper__btn {
    flex-shrink: 0;
    width: 40px;
    border: 0;
    background: transparent;
    color: var(--text-2);
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
    display: grid;
    place-items: center;
    user-select: none;
    transition: background 0.15s ease, color 0.15s ease;
}
.mz-stepper__btn:first-child { border-right: 1px solid var(--border); }
.mz-stepper__btn:last-child { border-left: 1px solid var(--border); }
.mz-stepper__btn:hover:not(:disabled) { background: var(--accent-soft); color: var(--accent-text); }
.mz-stepper__btn:disabled { opacity: 0.35; cursor: default; }

.mz-select {
    width: 100%;
    background: var(--surface-2);
    border: 1px solid var(--border);
    color: var(--text);
    border-radius: var(--r-md);
    padding: 12px 14px;
    font-size: 14px;
    outline: 0;
    appearance: none;
    background-image:
        linear-gradient(45deg, transparent 50%, var(--text-dim) 50%),
        linear-gradient(135deg, var(--text-dim) 50%, transparent 50%);
    background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
    padding-right: 36px;
}
.mz-select:focus { border-color: color-mix(in oklab, var(--accent) 60%, transparent); }
.mz-select:disabled { opacity: 0.5; cursor: not-allowed; }

.mz-range {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: 6px;
    background: var(--track);
    border-radius: 3px;
    outline: 0;
}
.mz-range::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: var(--knob);
    /* Обводка в 1px — не декор: бегунок белый в обеих темах, и на светлой
       дорожке его держит только она (тень на светлом фоне почти не видна,
       а --accent-soft вокруг — бледная плашка того же тона). */
    box-shadow: 0 0 0 1px var(--border-strong), 0 4px 12px var(--shadow-2),
        0 0 0 4px var(--accent-soft);
    cursor: pointer;
}
.mz-range::-moz-range-thumb {
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: var(--knob);
    border: 0;
    /* Обводка в 1px — не декор: бегунок белый в обеих темах, и на светлой
       дорожке его держит только она (тень на светлом фоне почти не видна,
       а --accent-soft вокруг — бледная плашка того же тона). */
    box-shadow: 0 0 0 1px var(--border-strong), 0 4px 12px var(--shadow-2),
        0 0 0 4px var(--accent-soft);
    cursor: pointer;
}

/* ============================================================
   Avatar
   ============================================================ */
.mz-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: #fff;
    position: relative;
    flex-shrink: 0;
    border-radius: 50%;
    background: var(--offline);
    /* NB: без overflow:hidden — иначе обрезается статус-точка и speaking-ring,
       которые сидят чуть снаружи круга. Картинка скругляется сама
       (.mz-avatar__img { border-radius: inherit }). */
}
.mz-avatar--squircle { border-radius: 32%; }

/* Аватар-картинка участника комнаты (наполняет mz-avatar). */
.mz-avatar__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    border-radius: inherit;
}

.mz-status-dot {
    position: absolute;
    width: 28%;
    aspect-ratio: 1;
    bottom: -2%;
    right: -2%;
    border-radius: 50%;
    border: 2px solid var(--bg);
    background: var(--offline);
    /* Выше speaking-ring (z-index:1), иначе кольцо активного голоса
       накладывается на статус-точку и перекрывает её. */
    z-index: 2;
}
.mz-status-dot.online {
    background: var(--accent);
    box-shadow: 0 0 8px var(--accent-glow);
}

/* Speaking ring — animated neon halo (added as child element) */
.mz-speaking-ring {
    position: absolute;
    inset: -6%;
    border-radius: 50%;
    border: 2px solid var(--accent);
    pointer-events: none;
    animation: mzPulse 1.4s ease-out infinite;
}
.mz-speaking-ring::after {
    content: "";
    position: absolute;
    inset: -4px;
    border-radius: 50%;
    border: 2px solid var(--accent);
    opacity: 0.4;
    animation: mzPulse 1.4s ease-out infinite 0.4s;
}
@keyframes mzPulse {
    0%   { box-shadow: 0 0 0 0 var(--accent-glow); opacity: 1; }
    70%  { box-shadow: 0 0 0 14px transparent; opacity: 0.4; }
    100% { box-shadow: 0 0 0 0 transparent; opacity: 0; }
}

/* ============================================================
   Voice wave (mini bars)
   ============================================================ */
.mz-wave {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    height: 22px;
}
.mz-wave > i {
    display: block;
    width: 3px;
    background: var(--accent);
    border-radius: 2px;
    animation: mzWave 0.9s ease-in-out infinite;
}
.mz-wave > i:nth-child(1) { height: 6px;  animation-delay: 0s; }
.mz-wave > i:nth-child(2) { height: 12px; animation-delay: 0.1s; }
.mz-wave > i:nth-child(3) { height: 9px;  animation-delay: 0.2s; }
.mz-wave > i:nth-child(4) { height: 14px; animation-delay: 0.05s; }
.mz-wave > i:nth-child(5) { height: 7px;  animation-delay: 0.15s; }
.mz-wave--big {
    gap: 6px;
    height: 40px;
}
.mz-wave--big > i { width: 5px; }
.mz-wave--big > i:nth-child(1) { height: 12px; }
.mz-wave--big > i:nth-child(2) { height: 26px; }
.mz-wave--big > i:nth-child(3) { height: 18px; }
.mz-wave--big > i:nth-child(4) { height: 30px; }
.mz-wave--big > i:nth-child(5) { height: 14px; }
@keyframes mzWave {
    0%, 100% { transform: scaleY(0.4); }
    50% { transform: scaleY(1.4); }
}

/* ============================================================
   Chips
   ============================================================ */
.mz-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border-radius: var(--r-pill);
    background: var(--surface-2);
    font-size: 12px;
    font-weight: 600;
    color: var(--text-2);
    border: 1px solid var(--border);
}
.mz-chip.is-accent {
    background: var(--accent-soft);
    color: var(--accent-text);
    border-color: color-mix(in oklab, var(--accent) 40%, transparent);
}
.mz-chip.is-danger {
    background: var(--danger-soft);
    color: var(--danger);
    border-color: color-mix(in oklab, var(--danger) 30%, transparent);
}
.mz-chip-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--offline);
}
.mz-chip.is-accent .mz-chip-dot {
    background: var(--accent);
    box-shadow: 0 0 6px var(--accent-glow);
}
.mz-chip.is-warn .mz-chip-dot {
    background: var(--warn);
    box-shadow: 0 0 6px color-mix(in oklab, var(--warn) 50%, transparent);
    animation: mzPulseSmall 1.5s ease-in-out infinite;
}
.mz-chip.is-danger .mz-chip-dot {
    background: var(--danger);
    box-shadow: 0 0 6px color-mix(in oklab, var(--danger) 50%, transparent);
}
@keyframes mzPulseSmall {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.4; }
}

/* ============================================================
   Cards / glass
   ============================================================ */
.mz-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
}
.mz-glass {
    background: color-mix(in srgb, var(--surface) 65%, transparent);
    backdrop-filter: blur(24px);
    -webkit-backdrop-filter: blur(24px);
    border: 1px solid var(--border-strong);
}

/* Custom scrollbar */
.mz-scroll::-webkit-scrollbar { width: 6px; height: 6px; }
.mz-scroll::-webkit-scrollbar-thumb { background: var(--surface-3); border-radius: 3px; }
.mz-scroll::-webkit-scrollbar-track { background: transparent; }

/* ============================================================
   Wordmark
   ============================================================ */
.mz-wordmark {
    font-size: 22px;
    font-weight: 800;
    letter-spacing: -0.03em;
    line-height: 1;
    display: inline-flex;
    align-items: baseline;
}
.mz-wordmark--accent {
    color: var(--accent-text);
    text-shadow: 0 0 18px var(--text-glow);
}
.mz-wordmark--ink { color: var(--text); }

/* ============================================================
   SETUP SCREEN (landing)
   ============================================================ */
#setup-screen {
    padding: 64px 22px 22px;
    max-width: 560px;
    margin: 0 auto;
    width: 100%;
}

.setup-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

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

.setup-main {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 24px;
    padding: 16px 0;
}

.setup-orb {
    position: relative;
    height: 220px;
    display: grid;
    place-items: center;
}
.setup-orb::before {
    content: "";
    position: absolute;
    width: 200px;
    height: 200px;
    border-radius: 50%;
    background: radial-gradient(circle at 35% 30%,
        color-mix(in oklab, var(--accent) 80%, transparent),
        color-mix(in oklab, var(--accent) 10%, transparent) 60%,
        transparent 75%);
    filter: blur(2px);
    pointer-events: none;
}
.setup-orb .mz-wave {
    position: relative;
    transform: scale(2.2);
    z-index: 1;
}

.setup-headline { margin: 0; }
.setup-title {
    font-size: 36px;
    font-weight: 800;
    letter-spacing: -0.025em;
    line-height: 1.05;
    margin: 0 0 12px;
}
.setup-title-accent {
    color: var(--accent-text);
    text-shadow: 0 0 22px var(--text-glow);
}
.setup-subtitle {
    color: var(--text-2);
    font-size: 16px;
    line-height: 1.4;
    margin: 0;
}

.setup-actions {
    display: flex;
    flex-direction: column;
    gap: 12px;
}
.setup-join-row {
    display: flex;
    gap: 10px;
}
.setup-join-row .mz-input { flex: 1; }
.setup-join-row .mz-btn { flex-shrink: 0; }

.setup-bullets {
    display: none;
    list-style: none;
    margin: 0;
    padding: 0;
    gap: 24px;
    color: var(--text-dim);
    font-size: 13px;
}
.setup-bullets li {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}
.setup-bullet-dot {
    display: inline-block;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 8px var(--accent-glow);
}

/* Orbital visual — только на десктопе, на мобайле скрыт. */
.setup-orbital {
    display: none;
    position: relative;
    min-height: 560px;
    flex: 1;
}
.orbital-glow {
    position: absolute;
    inset: 12% 12%;
    border-radius: 50%;
    background: radial-gradient(circle at 35% 30%,
        color-mix(in oklab, var(--accent) 90%, transparent),
        color-mix(in oklab, var(--accent) 12%, transparent) 55%,
        transparent 80%);
    filter: blur(2px);
    pointer-events: none;
}
.orbital-ring {
    position: absolute;
    border-radius: 50%;
    pointer-events: none;
}
.orbital-ring--outer {
    inset: 8% 8%;
    border: 1px dashed var(--border);
}
.orbital-ring--inner {
    inset: 20% 20%;
    border: 1px dashed var(--border-strong);
}
.orbital-center {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 22px;
    border-radius: var(--r-pill);
    box-shadow: 0 30px 60px -20px var(--accent-glow);
    z-index: 2;
}
.orbital-center__name {
    font-size: 14px;
    font-weight: 600;
}
.orbital-center__meta {
    font-size: 12px;
    color: var(--text-dim);
}

.orbital-avatar {
    position: absolute;
    z-index: 1;
}
.orbital-avatar .mz-avatar {
    width: 100%;
    height: 100%;
    font-size: 0;
}
.orbital-avatar--1 { left: 8%;  top: 12%; width: 64px; height: 64px; --avatar-bg: linear-gradient(135deg, oklch(0.62 0.16 40), oklch(0.42 0.18 80)); }
.orbital-avatar--2 { left: 78%; top: 6%;  width: 56px; height: 56px; --avatar-bg: linear-gradient(135deg, oklch(0.62 0.16 200), oklch(0.42 0.18 240)); }
.orbital-avatar--3 { left: 4%;  top: 70%; width: 56px; height: 56px; --avatar-bg: linear-gradient(135deg, oklch(0.62 0.16 320), oklch(0.42 0.18 360)); }
.orbital-avatar--4 { left: 82%; top: 60%; width: 60px; height: 60px; --avatar-bg: linear-gradient(135deg, oklch(0.62 0.16 150), oklch(0.42 0.18 190)); }
.orbital-avatar--5 { left: 60%; top: 88%; width: 48px; height: 48px; --avatar-bg: linear-gradient(135deg, oklch(0.62 0.16 280), oklch(0.42 0.18 320)); }
.orbital-avatar--6 { left: 20%; top: 40%; width: 44px; height: 44px; --avatar-bg: linear-gradient(135deg, oklch(0.62 0.16 100), oklch(0.42 0.18 140)); }
.orbital-avatar .mz-avatar {
    background: var(--avatar-bg, var(--surface-2));
}
/* Двум аватаркам — пульсирующий ring как «говорят». */
.orbital-avatar--1::after,
.orbital-avatar--4::after {
    content: "";
    position: absolute;
    inset: -6%;
    border-radius: 50%;
    border: 2px solid var(--accent);
    animation: mzPulse 1.6s ease-out infinite;
}
.orbital-avatar--4::after { animation-delay: 0.5s; }

.setup-footer {
    text-align: center;
    color: var(--text-dim);
    font-size: 12px;
    padding-top: 8px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
}

.setup-live-pill {
    align-self: center;
    padding: 6px 14px;
    cursor: default;
    font-size: 12px;
    font-weight: 600;
}
.setup-live-pill.hidden { display: none; }

.setup-install-pill {
    background: var(--layer-2);
    border-color: var(--border-strong);
    color: var(--text);
    padding: 6px 14px;
    cursor: pointer;
    transition: background 0.15s;
}
.setup-install-pill:hover { background: var(--layer-3); }
.setup-install-pill.hidden { display: none; }

/* Recent rooms — мини-список последних 5 комнат.
   Под main-блоком. Появление через max-height + opacity, чтобы не дёргать центрирование. */
.recent-rooms {
    overflow: hidden;
    max-height: 0;
    opacity: 0;
    margin-top: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
    /* min-width:0 обязателен: как flex-item контейнер иначе растёт под nowrap-
       содержимое списка и растягивает страницу вбок вместо внутреннего скролла. */
    min-width: 0;
    max-width: 100%;
    transition: max-height 0.35s ease, opacity 0.25s ease 0.05s, margin-top 0.3s ease;
}
.recent-rooms.is-visible {
    max-height: 200px;
    opacity: 1;
    margin-top: 14px;
}
.recent-rooms__label {
    font-size: 11px;
    color: var(--text-dim);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
    padding-left: 4px;
}
.recent-rooms__list {
    display: flex;
    /* Один ряд + горизонтальный скролл: раньше был flex-wrap:wrap, и при
       нескольких рядах последний прятался под overflow:hidden/max-height
       родителя (особенно на мобилке). Теперь лишние пилюли уезжают вбок. */
    flex-wrap: nowrap;
    gap: 8px;
    /* По левому краю — в одну линию с .recent-rooms__label; padding-left
       повторяет отступ лейбла. Это вид для АВТОРИЗОВАННОГО (страница комнат,
       .recent-rooms--page): блок стоит в общей левой сетке страницы.
       На посадочной у анонима выключка центральная — см. v2.css,
       .mz-landing--centered. */
    justify-content: flex-start;
    padding-left: 4px;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;   /* инерционный скролл на iOS */
    /* Десктоп: тонкий скроллбар в стиле проекта (как .mz-scroll) — иначе на
       мыши оверфлоу не докрутить. На тач-устройствах прячем (ниже). */
    scrollbar-width: thin;                        /* Firefox */
    scrollbar-color: var(--surface-3) transparent;
    padding-bottom: 6px;
}
.recent-rooms__list::-webkit-scrollbar { height: 6px; }
.recent-rooms__list::-webkit-scrollbar-thumb { background: var(--surface-3); border-radius: 3px; }
.recent-rooms__list::-webkit-scrollbar-track { background: transparent; }
/* Тач (мобилка/планшет): полоску прячем — скролл пальцем и так очевиден. */
@media (hover: none) and (pointer: coarse) {
    .recent-rooms__list { scrollbar-width: none; -ms-overflow-style: none; padding-bottom: 0; }
    .recent-rooms__list::-webkit-scrollbar { display: none; }
}
.recent-room-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 14px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--r-pill);
    color: var(--text);
    font-family: var(--mono);
    font-size: 13px;
    letter-spacing: 0.12em;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
    /* Не сжимаемся и не переносим текст — иначе вместо скролла пилюли
       схлопнулись бы по ширине. */
    flex-shrink: 0;
    white-space: nowrap;
}
.recent-room-pill:hover {
    background: var(--surface-3);
    border-color: var(--border-strong);
}
.recent-room-pill__remove {
    width: 18px;
    height: 18px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--text-dim);
    font-family: var(--font);
    letter-spacing: 0;
    font-size: 16px;
    line-height: 1;
}
.recent-room-pill__remove:hover { color: var(--danger); }

/* ============================================================
   INVITE SCREEN
   ============================================================ */
#invite-screen {
    padding: 48px 22px 24px;
    max-width: 560px;
    margin: 0 auto;
    width: 100%;
}
.invite-card {
    margin-top: auto;
    margin-bottom: auto;
    padding: 32px 24px;
    text-align: center;
}
.invite-card .mz-h2 { margin-bottom: 8px; }
.invite-card__desc {
    color: var(--text-2);
    font-size: 14px;
    margin: 6px 0 0;
}
.invite-room-id {
    display: inline-block;
    margin: 16px 0 24px;
    padding: 10px 18px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--r-pill);
    font-family: var(--mono);
    font-size: 18px;
    letter-spacing: 0.3em;
    color: var(--accent-text);
}
.invite-card .mz-btn { width: 100%; }

/* Для именованных комнат: название + владелец под room-id. */
.invite-room-meta {
    margin: -8px 0 20px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    align-items: center;
}
.invite-room-meta.hidden { display: none; }
.invite-room-meta__name {
    font-size: 20px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text);
    line-height: 1.2;
    word-break: break-word;
}
.invite-room-meta__owner {
    font-size: 13px;
    color: var(--text-dim);
}
.invite-room-meta__error {
    margin: -8px 0 18px;
    padding: 10px 14px;
    background: var(--danger-soft);
    border: 1px solid color-mix(in oklab, var(--danger) 30%, transparent);
    border-radius: var(--r-sm);
    color: var(--danger);
    font-size: 13px;
    line-height: 1.4;
    text-align: left;
}
.invite-room-meta__error.hidden { display: none; }

/* ============================================================
   ROOM SCREEN
   ============================================================ */
#room-screen {
    padding: 0;
    flex-direction: column;
    min-height: 100svh;
}

.room-header {
    display: flex;
    align-items: center;
    gap: 10px;
    /* viewport-fit=cover в meta делает страницу проникающей под status bar
       iPhone. Без safe-area-inset-top контент шапки оказывается под часами/
       индикаторами. */
    padding: max(16px, calc(env(safe-area-inset-top) + 8px)) 16px 12px;
    border-bottom: 1px solid var(--border);
    background: color-mix(in srgb, var(--bg) 60%, transparent);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
}
.room-header__info {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.room-header__title {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 17px;
    font-weight: 700;
    color: var(--text);
    cursor: pointer;
}
.room-header__title svg { color: var(--accent-text); width: 16px; height: 16px; }
/* Название + кнопка настроек комнаты в одну строку (кнопка слева, у имени). */
.room-header__titlerow {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
}
.room-header__titlerow .room-header__title { min-width: 0; }
/* Компактная кнопка настроек рядом с названием — меньше стандартной icon-btn. */
/* Оригинальное название комнаты рядом со своим (см. custom_name). */
.room-header__orig {
    font-size: 12px;
    color: var(--text-dim);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 40%;
}
.room-header__orig[hidden] { display: none; }
.room-header__titlerow .room-settings-btn {
    width: 30px;
    height: 30px;
    flex-shrink: 0;
}
.room-header__titlerow .room-settings-btn svg { width: 17px; height: 17px; }
.room-header__meta {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
    font-size: 11px;
    color: var(--text-dim);
}
.room-header__meta .mz-chip {
    padding: 3px 8px;
    font-size: 11px;
}
.room-header__meta .mz-chip-dot {
    width: 6px;
    height: 6px;
}

.room-status {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}
.room-status__btn {
    display: none;
    font-size: 12px;
    padding: 2px 6px;
    background: var(--layer-3);
    border-radius: var(--r-pill);
    color: var(--text-2);
    cursor: pointer;
}
.room-status__btn:not(.hidden) { display: inline-flex; }

/* Speaker strip — horizontal scroll of participants */
.speaker-strip {
    position: relative;
    padding: 14px 16px 16px;
    overflow-x: auto;
    overflow-y: hidden;
    border-bottom: 1px solid var(--border);
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    min-height: 110px;
    transition: opacity 0.2s, filter 0.2s;
}
.speaker-strip::-webkit-scrollbar { display: none; }

#participants-list {
    display: flex;
    gap: 14px;
    margin: 0;
    padding: 0;
    list-style: none;
    width: max-content;
}

/* Когда голос не подключён — список приглушаем, поверх показываем pill-плейсхолдер. */
.speaker-strip__placeholder {
    position: absolute;
    inset: 0;
    margin: auto;
    display: none;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 6px 14px;
    background: color-mix(in srgb, var(--bg) 90%, transparent);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border: 1px solid var(--border-strong);
    border-radius: var(--r-pill);
    color: var(--text-2);
    font-size: 11px;
    font-weight: 600;
    width: max-content;
    height: max-content;
    pointer-events: none;
    z-index: 2;
}
.speaker-strip__placeholder svg { color: var(--text-dim); }

#room-screen[data-voice-state="disconnected"] .speaker-strip { opacity: 0.4; filter: saturate(0.5); }
#room-screen[data-voice-state="disconnected"] .speaker-strip__placeholder { display: inline-flex; }
#room-screen[data-voice-state="connecting"] .speaker-strip { opacity: 0.6; }

.speaker-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    min-width: 72px;
    text-align: center;
}
.speaker-tile__avatar {
    position: relative;
    width: 56px;
    height: 56px;
}
.speaker-tile__avatar .mz-avatar { width: 100%; height: 100%; font-size: 21px; }
.speaker-tile__name {
    font-size: 11px;
    color: var(--text-2);
    max-width: 72px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.speaker-tile.is-local .speaker-tile__name { color: var(--accent-text); }
.speaker-tile__mic {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--surface-2);
    color: var(--text-2);
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.speaker-tile__mic svg { width: 12px; height: 12px; }
.speaker-tile.is-muted .speaker-tile__mic {
    background: var(--danger-soft);
    color: var(--danger);
}
/* Юзер в чате, но не подключён к голосу — приглушаем тайл (но не прячем). */
.speaker-tile.is-voice-off {
    opacity: 0.55;
}
.speaker-tile.is-voice-off .speaker-tile__mic {
    background: var(--layer-2);
    color: var(--text-dim);
}

/* Chat container */
.chat-container {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
    padding: 0;
    /* Якорь для плавающей кнопки «вниз» (.chat-scroll-down). */
    position: relative;
}

/* Плавающая кнопка «к последним сообщениям». Видна, только когда список
   уведён вверх от низа; прячется через opacity/pointer-events (не display) —
   чтобы появление/исчезновение анимировалось. Сидит над typing-indicator. */
.chat-scroll-down {
    position: absolute;
    right: 16px;
    bottom: 44px;
    z-index: 5;
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    cursor: pointer;
    box-shadow: 0 10px 26px -10px var(--shadow-4);
    transition: opacity 0.15s ease, transform 0.15s ease, background 0.15s ease;
}
.chat-scroll-down:hover { background: var(--surface-2); }
.chat-scroll-down.hidden {
    opacity: 0;
    transform: translateY(8px) scale(0.9);
    pointer-events: none;
}
/* Счётчик новых сообщений, пришедших пока юзер читал историю. */
.chat-scroll-down__badge {
    position: absolute;
    top: -4px;
    right: -4px;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: var(--r-pill);
    background: var(--accent);
    color: var(--accent-ink);
    font-size: 11px;
    font-weight: 700;
    line-height: 18px;
    text-align: center;
}
.chat-scroll-down__badge.hidden { display: none; }
.chat-messages {
    flex: 1;
    overflow-y: auto;
    padding: 14px 16px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    /* Скролл чата не «перетекает» на фон/экран комнаты. (БЕЗ
       -webkit-overflow-scrolling: внутри position:fixed он давал инверсию/рывки
       скролла на iOS.) */
    overscroll-behavior: contain;
}
.chat-messages .message.system {
    align-self: center;
    font-size: 11px;
    color: var(--text-dim);
    padding: 4px 12px;
    background: var(--surface-2);
    border-radius: var(--r-pill);
    font-style: italic;
}
.chat-messages .message {
    display: flex;
    flex-direction: column;
    max-width: 78%;
}
.chat-messages .message.is-mine { align-self: flex-end; align-items: flex-end; }
.chat-messages .message.is-other { align-self: flex-start; align-items: flex-start; }
.chat-messages .message .meta {
    font-size: 11px;
    color: var(--text-dim);
    margin: 0 12px 2px;
}
.chat-messages .message .text {
    padding: 10px 14px;
    border-radius: 18px 18px 18px 4px;
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--text);
    font-size: 14px;
    line-height: 1.4;
    word-wrap: break-word;
}
.chat-messages .message.is-mine .text {
    background: var(--accent);
    color: var(--accent-ink);
    border: none;
    border-radius: 18px 18px 4px 18px;
    box-shadow: 0 6px 20px -8px var(--accent-glow);
}
.chat-messages .message .time {
    font-size: 10px;
    color: var(--text-dim);
    margin: 4px 12px 0;
}

/* Typing indicator — между chat-messages и composer */
.typing-indicator {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 18px 8px;
    font-size: 12px;
    color: var(--text-dim);
    min-height: 24px;
    /* Однострочно — высота строки стабильна при любом числе печатающих. */
    white-space: nowrap;
    overflow: hidden;
}
/* Место под индикатор зарезервировано всегда: прячем только содержимое
   (visibility, не display) — появление не сдвигает и не перекрывает
   последнее сообщение в чате. */
.typing-indicator.hidden { visibility: hidden; }
.typing-indicator__dots {
    display: inline-flex;
    gap: 3px;
    align-items: center;
}
.typing-indicator__dots i {
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--text-dim);
    animation: mzTypingDot 1.2s infinite ease-in-out;
}
.typing-indicator__dots i:nth-child(2) { animation-delay: 0.15s; }
.typing-indicator__dots i:nth-child(3) { animation-delay: 0.3s; }
@keyframes mzTypingDot {
    0%, 80%, 100% { opacity: 0.3; transform: translateY(0); }
    40% { opacity: 1; transform: translateY(-2px); }
}

/* Voice rail + composer */
.room-footer {
    padding: 12px 14px max(16px, env(safe-area-inset-bottom));
    border-top: 1px solid var(--border);
    background: color-mix(in srgb, var(--bg) 85%, transparent);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    display: flex;
    flex-direction: column;
    gap: 10px;
}

/* Voice rail: strip подключения виден только пока голос не активен (по data-voice-state на #room-screen) */
.voice-rail__disconnected {
    display: none;
    align-items: center;
    gap: 10px;
}
#room-screen[data-voice-state="disconnected"] .voice-rail__disconnected { display: flex; }

.voice-rail__cta {
    flex: 1;
    height: 48px;
    font-size: 15px;
    font-weight: 700;
}
.voice-rail__disconnected-hint {
    display: none;
    flex-direction: column;
    flex: 1;
    min-width: 0;
}
.voice-rail__disconnected-hint strong {
    font-size: 13px;
    font-weight: 700;
    color: var(--text);
}
.voice-rail__disconnected-hint span {
    font-size: 11px;
    color: var(--text-2);
}

@media (min-width: 720px) {
    .voice-rail__disconnected {
        padding: 10px 14px;
        background: color-mix(in oklab, var(--accent) 8%, var(--surface));
        border: 1px solid color-mix(in oklab, var(--accent) 28%, var(--border));
        border-radius: 16px;
    }
    .voice-rail__disconnected-hint { display: flex; }
    .voice-rail__cta { flex: 0 0 auto; height: 40px; padding: 0 18px; font-size: 14px; }
}
/* Офлайн (чат-сокет лежит, класс ставит UIManager.setOfflineMode): вход в эфир
   невозможен — LiveKit берёт токен с сервера по HTTP, тап привёл бы к падению
   подключения с непонятной ошибкой. Гасим оба входа: strip в футере и чип-статус
   в шапке. Чип — только пока голос НЕ активен: в эфире он показывает состояние
   (и связь могла отвалиться уже после подключения), гасить его нечестно. */
html.mz-offline #connect-voice-btn,
html.mz-offline #room-screen[data-voice-state="disconnected"] #voice-status {
    opacity: 0.45;
    filter: grayscale(1);
    pointer-events: none;
    cursor: not-allowed;
}

/* Комната открыта без связи, а сохранённой истории на устройстве нет — иначе
   пользователь видит просто пустой чат и не понимает, пусто в комнате или
   «не загрузилось». Оверлей поверх списка: не участвует в потоке сообщений и
   не влияет на скролл (см. UIManager.setChatOfflineNote). */
.chat-offline-note {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 24px 28px;
    text-align: center;
    pointer-events: none;
    color: var(--text-2);
}
.chat-offline-note[hidden] { display: none; }
.chat-offline-note__icon { color: var(--text-3, var(--text-2)); opacity: 0.7; }
.chat-offline-note strong {
    font-size: 15px;
    font-weight: 700;
    color: var(--text);
}
.chat-offline-note span {
    max-width: 320px;
    font-size: 12.5px;
    line-height: 1.45;
}

.voice-rail__sliders {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
}
.voice-slider-row {
    display: flex;
    align-items: center;
    gap: 8px;
}
.voice-slider-row__icon {
    flex-shrink: 0;
    width: 22px;
    height: 22px;
    color: var(--text-dim);
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.voice-slider-row__icon svg { width: 16px; height: 16px; }
.voice-slider-row .mz-range { flex: 1; }
.voice-slider-row + .voice-slider-row { margin-top: 8px; }

.chat-form {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-pill);
}
.chat-form .mz-input {
    background: transparent;
    border: 0;
    padding: 8px 14px;
    height: 38px;
}
.chat-form .mz-input:focus { border-color: transparent; }
.chat-form button[type="submit"] {
    flex-shrink: 0;
}

/* ============================================================
   МОДАЛКИ — bottom sheet на мобиле, центрированный диалог на десктопе.
   Разметку даёт Jinja-макрос partials/_modal.html, поведение — js/modal.js.
   Структура у всех одна:
       .modal-overlay > .modal-content > .modal-handle
                                      + .modal-header (закреплена сверху)
                                      + .modal-body   (единственный скролл)
   ============================================================ */
.modal-overlay {
    position: fixed;
    inset: 0;
    /* Поднимаем нижнюю границу overlay'я над клавиатурой. --kb-height пишет
       app.js из window.visualViewport (см. calibrateViewportHeight). Без этого
       bottom-sheet (justify-content: flex-end) уходит под клавиатуру и юзер
       вынужден скроллить, чтобы увидеть, что за модалка. */
    bottom: var(--kb-height, 0px);
    z-index: 1000;
    background: var(--scrim);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    pointer-events: auto;
}
/* Тач-устройства (Android и т.п.): backdrop-filter на fixed fullscreen
   оверлее у части Android-браузеров/GPU мигает неразмытым фоном при
   создании compositing-слоя. Плоская заливка без blur — дешёвый слой,
   снимок фона под ним не нужен, глюка нет. */
@media (hover: none) and (pointer: coarse) {
    .modal-overlay {
        background: var(--scrim-opaque);
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
    }
}
/* .hidden — жёсткий display:none, БЕЗ анимации прозрачности overlay'я.
   Раньше .hidden оставлял overlay в DOM постоянно (только opacity:0), и
   backdrop-filter-слой копился на каждой из 12 модалок сразу на любой
   странице — на слабых Android GPU это приводило к чёрным областям/чёрному
   экрану. display:none полностью убирает слой из compositing, пока модалка
   закрыта. Overlay появляется мгновенно (без фейда), плавно анимируется
   только .modal-content (slide-up, ниже). */
.modal-overlay.hidden {
    display: none;
    pointer-events: none;
}
@starting-style {
    .modal-overlay:not(.hidden) .modal-content {
        transform: translateY(20px);
    }
}
/* Карточка модалки — flex-колонка БЕЗ собственного скролла: шапка и ручка
   закреплены сверху, скроллится только .modal-body. Так контент не подныривает
   под шапку, а скроллбар не лезет на скругления (сверху его закрывает шапка,
   снизу — padding-bottom). Горизонтальные отступы живут на шапке и теле,
   вертикальные — здесь (это отступ до скруглений). */
.modal-content {
    background: var(--surface);
    border-top: 1px solid var(--border-strong);
    border-radius: 32px 32px 0 0;
    /* Снизу — не меньше системного inset'а: шторка упирается в край экрана. */
    padding: 12px 0 max(28px, env(safe-area-inset-bottom));
    box-shadow: 0 -20px 60px var(--shadow-4);
    max-height: 90vh;                              /* фолбэк */
    /* --app-height уже учитывает клавиатуру (visualViewport.height). 0.95 от
       него даёт небольшой зазор сверху, чтобы шапка модалки не упиралась
       впритык в верх экрана. */
    max-height: calc(0.95 * var(--app-height, 100vh));
    display: flex;
    flex-direction: column;
    overflow: hidden;           /* контейнер не скроллит; клип по border-radius */
    transform: translateY(0);
    transition: transform 0.25s ease;
}
.modal-overlay.hidden .modal-content { transform: translateY(20px); }
/* Карточку фокусируем программно при открытии (диалог для скринридера) —
   рамка фокуса на ней не нужна. */
.modal-content:focus { outline: none; }

.modal-handle {
    width: 38px;
    height: 4px;
    border-radius: 2px;
    background: var(--layer-4);
    margin: 4px auto 14px;
    flex: 0 0 auto;
}
/* Шапка: чип-иконка + заголовок/подзаголовок + крестик. Одна на все модалки. */
.modal-header {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 14px;
    margin: 0;
    padding: 0 22px 12px;
    border-bottom: 1px solid var(--border);
}
/* Единственная скроллящаяся зона модалки. */
.modal-body {
    flex: 1 1 auto;
    min-height: 0;              /* иначе flex-элемент не отдаёт высоту под скролл */
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 14px 22px 0;
    /* Firefox: тонкий скролл в тон surface. */
    scrollbar-width: thin;
    scrollbar-color: var(--surface-3) transparent;
}
/* WebKit/Chromium: тонкий скролл с отступом (border-trick) + подсветка на hover. */
.modal-body::-webkit-scrollbar { width: 10px; }
.modal-body::-webkit-scrollbar-track { background: transparent; }
.modal-body::-webkit-scrollbar-thumb {
    background: var(--surface-3);
    border-radius: 6px;
    border: 2px solid transparent;
    background-clip: padding-box;
}
.modal-body::-webkit-scrollbar-thumb:hover {
    background: color-mix(in oklab, var(--surface-3) 55%, var(--text-2));
    background-clip: padding-box;
}
.modal-section {
    margin-bottom: 16px;
}
.modal-section:last-child { margin-bottom: 0; }
/* Версия приложения в подвале настроек — тихая, справочная подпись. */
.settings-version {
    margin-top: 18px;
    padding: 14px 0 4px;
    border-top: 1px solid var(--border);
    text-align: center;
    font-size: 12px;
    font-family: var(--mono);
    letter-spacing: 0.02em;
    color: var(--text-dim);
}
.settings-version:empty { display: none; }
/* Скрытие секции (напр. «Ваше имя» у авторизованных — имя из профиля). */
.modal-section.hidden { display: none; }
.modal-section__label {
    display: block;
    font-size: 12px;
    color: var(--text-2);
    margin-bottom: 8px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-weight: 600;
}
.input-with-button {
    display: flex;
    gap: 8px;
    align-items: center;
}
.input-with-button .mz-input { flex: 1; }

/* VU-meter — 30 столбиков, заполняются пропорционально уровню микрофона */
.vu-meter {
    margin-top: 10px;
    display: flex;
    gap: 2px;
    height: 6px;
}
.vu-meter > span {
    flex: 1;
    border-radius: 1px;
    background: var(--surface-3);
    transition: background 0.05s linear;
}
.vu-meter > span.is-low { background: var(--accent); }
.vu-meter > span.is-mid { background: color-mix(in oklab, var(--accent) 70%, var(--warn)); }
.vu-meter > span.is-high { background: var(--warn); }

/* Селектор режима шумоподавления + пояснение под ним */
.noise-mode-label {
    display: block;
    font-size: 14px;
    color: var(--text);
    margin: 4px 0 8px;
}
.noise-mode-desc {
    margin: 8px 4px 0;
    font-size: 11px;
    line-height: 1.4;
    color: var(--text-dim);
    min-height: 2.4em; /* держим высоту, чтобы список не «прыгал» при смене */
}
.noise-strength-row { margin-top: 10px; margin-bottom: 10px; }
.noise-strength-row[hidden] { display: none; }
.noise-strength-row .noise-mode-label { margin-bottom: 6px; }
.noise-strength-row .mz-range { width: 100%; }

/* Кнопка выбора клавиши push-to-talk */
#ptt-key-row[hidden] { display: none; }
#ptt-key-btn {
    min-width: 96px;
    padding: 6px 16px;
    font-size: 13px;
    font-weight: 600;
}
#ptt-key-btn.is-capturing {
    color: var(--accent-text);
    border-color: color-mix(in oklab, var(--accent) 60%, var(--border-strong));
    background: color-mix(in oklab, var(--accent) 12%, transparent);
}

/* Toggle switch */
.toggle-row {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 12px 4px;
    border-top: 1px solid var(--border);
}
.toggle-row:first-of-type { border-top: 0; padding-top: 4px; }
.toggle-row__label {
    flex: 1;
    font-size: 14px;
    color: var(--text);
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.toggle-row__hint {
    font-size: 11px;
    color: var(--text-dim);
    font-weight: 400;
}
.toggle-row__hint kbd {
    display: inline-block;
    padding: 1px 6px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 4px;
    font-family: var(--mono);
    font-size: 10px;
    color: var(--text-2);
    margin: 0 2px;
}

.mz-toggle {
    width: 44px;
    height: 26px;
    border-radius: 13px;
    background: var(--track);
    position: relative;
    cursor: pointer;
    transition: background 0.15s;
    border: 0;
    padding: 0;
    flex-shrink: 0;
}
.mz-toggle::before {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--knob);
    box-shadow: 0 0 0 1px var(--border-strong), 0 2px 6px var(--shadow-1);
    transition: left 0.15s, background 0.15s;
}
.mz-toggle.is-active { background: var(--accent); }
.mz-toggle.is-active::before { left: 20px; }

/* Секция «Звуки» в настройках: громкость + строка на каждое событие. */
.sound-detail[hidden] { display: none; }
.sound-detail { margin-top: 4px; }
.sound-output-row { margin-top: 10px; margin-bottom: 10px; }
.sound-output-row[hidden] { display: none; }
.sound-output-row .noise-mode-label { margin: 0 0 6px; }

/* Спойлер на нативном <details> — общий компонент для длинных списков в
   настройках (сейчас единственный житель — «Звуки по событиям»). Состояние,
   клавиатуру и aria-expanded даёт сам <details>, наш только внешний вид. */
.mz-fold {
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    background: var(--surface-2);
    overflow: hidden;
}
.mz-fold__head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 11px 12px;
    cursor: pointer;
    /* Убираем дефолтный треугольник: list-style — для Chrome/FF,
       ::-webkit-details-marker — для Safari (там list-style не действует). */
    list-style: none;
    transition: background 0.15s;
}
.mz-fold__head::-webkit-details-marker { display: none; }
.mz-fold__head:hover { background: var(--surface-3); }
.mz-fold__head:focus-visible {
    outline: 2px solid var(--accent-text);
    outline-offset: -2px;
}
.mz-fold__title { flex: 1; min-width: 0; font-size: 14px; color: var(--text); }
.mz-fold__meta { font-size: 11px; color: var(--text-dim); font-family: var(--mono); }
.mz-fold__chevron {
    display: inline-grid;
    place-items: center;
    flex-shrink: 0;
    color: var(--text-2);
    transition: transform 0.18s ease;
}
.mz-fold__chevron svg { width: 18px; height: 18px; display: block; }
.mz-fold[open] .mz-fold__chevron { transform: rotate(180deg); }
/* Раскрытие: высоту <details> без JS не анимировать, поэтому «мягкость» даёт
   контент — короткий fade-in при открытии. */
.mz-fold[open] > :not(summary) { animation: mzFoldIn 160ms ease both; }
@keyframes mzFoldIn {
    from { opacity: 0; transform: translateY(-4px); }
}
@media (prefers-reduced-motion: reduce) {
    .mz-fold[open] > :not(summary) { animation: none; }
    .mz-fold__chevron { transition: none; }
}

.sound-events { display: flex; flex-direction: column; padding: 0 8px 4px; }
.sound-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 4px;
    border-bottom: 1px solid var(--border);
}
.sound-row:last-child { border-bottom: none; }
.sound-row__info {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.sound-row__label { font-size: 14px; color: var(--text); }
.sound-row__hint { font-size: 11px; color: var(--text-dim); }
.sound-row__play {
    width: 32px;
    height: 32px;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    border: 1px solid var(--border);
    background: var(--surface-2);
    color: var(--text-2);
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s;
}
.sound-row__play:hover { color: var(--accent-text); border-color: var(--accent-text); }
.sound-row__play svg { width: 16px; height: 16px; }

/* Voice rail PTT indicator */
.voice-rail__ptt-hint {
    font-size: 10px;
    color: var(--text-dim);
    padding: 2px 8px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--r-pill);
    font-family: var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}
.voice-rail__ptt-hint.is-active {
    background: var(--accent-soft);
    color: var(--accent-text);
    border-color: color-mix(in oklab, var(--accent) 35%, transparent);
}
.voice-rail__ptt-hint.hidden { display: none; }

/* Accent color picker — ОДНА строка с горизонтальным скроллом.
   Была сетка 6×2 ровно под 12 цветов: любой новый акцент либо начинал третий
   ряд с двумя сиротами, либо (при repeat(auto-fit)) ужимал свотчи. Теперь
   палитра просто едет вправо — дописывать цвета в ACCENT_PALETTE можно без
   оглядки на вёрстку. */
.accent-picker {
    display: flex;
    align-items: center;
    gap: 12px;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;    /* инерционный скролл на iOS */
    /* Запас вокруг свотчей — оверфлоу-контейнер режет всё, что вылезло за
       падинг-бокс, а вылезают три вещи разом:
         • неон: box-shadow 0 0 14px → примерно 7px наружу (blur/2);
         • :hover scale(1.1) → +1.9px с каждой стороны от 38px;
         • кольцо активного (inset:-5px), в hover ≈5.5px.
       Берём 16px — с запасом к худшему случаю (~9px), чтобы ореол нигде не
       упирался в край. Снизу +8px под дорожку скроллбара: WebKit вычитает её
       из падинг-бокса, так что свои 16px просвета свечение сохраняет.
       Отрицательные margin: по бокам возвращают первый свотч на край секции
       (в .modal-body по 22px паддинга — влезает), по вертикали срезают часть
       прибавки — визуально её занимает само свечение. */
    padding: 16px 16px 24px;
    margin: -4px -16px;
    /* Свайп по ряду не должен ни скроллить модалку, ни дёргать браузерный
       «назад» на краю. */
    overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    /* Firefox */
    scrollbar-width: thin;
    scrollbar-color: color-mix(in oklab, var(--accent) 55%, var(--surface-3)) transparent;
}
/* WebKit/Chromium: дорожка-пилюля в тон подложки, палец подкрашен акцентом —
   полоска остаётся видимой и на тач-устройствах, это подсказка «тут есть ещё». */
.accent-picker::-webkit-scrollbar { height: 8px; }
.accent-picker::-webkit-scrollbar-track {
    background: var(--surface-2);
    border-radius: var(--r-pill);
}
.accent-picker::-webkit-scrollbar-thumb {
    background: color-mix(in oklab, var(--accent) 55%, var(--surface-3));
    border-radius: var(--r-pill);
    border: 1px solid transparent;
    background-clip: padding-box;
}
.accent-picker::-webkit-scrollbar-thumb:hover {
    background: var(--accent);
    background-clip: padding-box;
}
.accent-swatch {
    flex: 0 0 auto;
    scroll-snap-align: center;
    width: 38px;
    height: 38px;
    border-radius: 50%;
    cursor: pointer;
    padding: 0;
    border: 0;
    position: relative;
    transition: transform 0.15s;
    background: var(--swatch-color, var(--accent));
    /* Свечение у свотча своё, от ЕГО цвета, а не от --accent-glow (в ряду 13
       разных цветов), поэтому и гасится отдельным правилом — токен на :root
       такое элементное объявление не переопределит. */
    --swatch-glow: color-mix(in oklab, var(--swatch-color, var(--accent)) 50%, transparent);
    box-shadow: 0 0 14px var(--swatch-glow);
}
html[data-neon="off"] .accent-swatch { --swatch-glow: transparent; }
.accent-swatch:hover { transform: scale(1.1); }
.accent-swatch.is-active::after {
    content: "";
    position: absolute;
    inset: -5px;
    border-radius: 50%;
    border: 2px solid var(--text);
    pointer-events: none;
}

/* Переключатель темы — сегмент-контрол на три позиции. Живёт в той же секции
   настроек, что и пикер акцента. */
.theme-picker {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 4px;
    padding: 4px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--r-md);
}
.theme-option {
    padding: 9px 6px;
    border-radius: calc(var(--r-md) - 6px);
    font-size: 13px;
    font-weight: 600;
    color: var(--text-2);
    background: transparent;
    /* Прозрачный бордер держит геометрию: у активного он появляется, и без
       заглушки кнопки прыгали бы на 1px. */
    border: 1px solid transparent;
    transition: background 0.15s, color 0.15s;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.theme-option:hover { color: var(--text); background: var(--layer-2); }
.theme-option.is-active {
    background: var(--accent-soft);
    border-color: color-mix(in oklab, var(--accent-text) 35%, transparent);
    color: var(--accent-text);
}
/* Три подписи в ряд не влезают на узком экране — там колонкой. */
@media (max-width: 380px) {
    .theme-picker { grid-template-columns: 1fr; }
}

/* ============================================================
   Command palette (⌘K)
   ============================================================ */
.cmdk-overlay {
    justify-content: center;
    align-items: flex-start;
    padding-top: 14vh;
    background: var(--scrim);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
}
@media (hover: none) and (pointer: coarse) {
    .cmdk-overlay {
        background: var(--scrim-opaque);
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
    }
}
/* Палитра — единственная модалка без шапки: её «шапка» это строка поиска.
   Геометрию задаём с повышенной специфичностью, иначе десктопный блок
   .modal-content (ниже по файлу) навязывает ей свои паддинги и ширину. */
.modal-overlay .cmdk-content {
    width: min(560px, calc(100% - 32px));
    max-width: none;
    max-height: 70vh;
    padding: 0;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--r-lg);
    box-shadow: 0 30px 60px -20px var(--shadow-4);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    transform: translateY(-8px);
    transition: transform 0.2s ease;
}
.cmdk-overlay:not(.hidden) .cmdk-content { transform: translateY(0); }
@starting-style {
    .cmdk-overlay:not(.hidden) .cmdk-content { transform: translateY(-8px); }
}

.cmdk-input-wrap {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 18px;
    border-bottom: 1px solid var(--border);
}
.cmdk-input-icon {
    color: var(--text-dim);
    display: inline-flex;
}
.cmdk-input {
    flex: 1;
    background: transparent;
    border: 0;
    outline: 0;
    color: var(--text);
    font-size: 16px;
    font-family: var(--mono);
    letter-spacing: 0.18em;
    text-transform: uppercase;
}
.cmdk-input::placeholder {
    color: var(--text-dim);
    font-family: var(--font);
    letter-spacing: 0;
    text-transform: none;
}
.cmdk-esc {
    font-family: var(--mono);
    font-size: 11px;
    color: var(--text-2);
    padding: 3px 8px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 6px;
}

.cmdk-list {
    flex: 1;
    overflow-y: auto;
    padding: 6px;
}
.cmdk-list::-webkit-scrollbar { width: 6px; }
.cmdk-list::-webkit-scrollbar-thumb { background: var(--surface-3); border-radius: 3px; }
.cmdk-section-title {
    font-size: 10px;
    color: var(--text-dim);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    padding: 10px 12px 6px;
}
.cmdk-item {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 10px 12px;
    background: transparent;
    border: 0;
    border-radius: var(--r-md);
    color: var(--text);
    text-align: left;
    cursor: pointer;
    transition: background 0.1s;
}
.cmdk-item:hover,
.cmdk-item.is-focused {
    background: var(--surface-2);
}
.cmdk-item__icon {
    width: 32px;
    height: 32px;
    border-radius: 10px;
    background: var(--surface-2);
    color: var(--accent-text);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.cmdk-item.is-focused .cmdk-item__icon {
    background: var(--accent-soft);
}
.cmdk-item__label {
    flex: 1;
    font-size: 14px;
    font-weight: 500;
}
.cmdk-item__id {
    font-family: var(--mono);
    letter-spacing: 0.18em;
    font-size: 14px;
}
.cmdk-item__hint {
    font-size: 11px;
    color: var(--text-dim);
}
.cmdk-empty {
    padding: 18px 12px;
    text-align: center;
    color: var(--text-dim);
    font-size: 13px;
}

/* ============================================================
   Error toast
   ============================================================ */
/* Стек всплывающих уведомлений (снизу по центру). Контейнер прозрачен и не
   ловит клики — по нему клик проходит насквозь; сами плашки (.mz-notice)
   кликабельны (клик = закрыть). Типы: error/success/info. */
#error-message {
    position: fixed;
    bottom: 24px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1100;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    max-width: 90%;
    pointer-events: none;
}
#error-message.hidden { display: none; }
.mz-notice {
    pointer-events: auto;
    cursor: pointer;
    padding: 12px 20px;
    border-radius: var(--r-pill);
    border: 1px solid transparent;
    font-size: 14px;
    font-weight: 500;
    max-width: 100%;
    text-align: center;
    box-shadow: 0 10px 30px var(--shadow-2);
    animation: mz-notice-in 0.18s ease;
}
@keyframes mz-notice-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}
.mz-notice--error {
    background: var(--danger-soft);
    color: var(--danger);
    border-color: color-mix(in oklab, var(--danger) 30%, transparent);
}
.mz-notice--success {
    background: var(--ok-soft);
    color: var(--ok);
    border-color: color-mix(in oklab, var(--ok) 30%, transparent);
}
.mz-notice--info {
    background: var(--surface-2);
    color: var(--text);
    border-color: var(--border-strong);
}

/* ============================================================
   Tooltip
   ============================================================ */
#tooltip {
    position: fixed;
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    color: var(--text);
    padding: 8px 12px;
    border-radius: var(--r-md);
    font-size: 13px;
    z-index: 1200;
    pointer-events: none;
    box-shadow: 0 8px 24px var(--shadow-2);
}
#tooltip.hidden { display: none; }

/* ============================================================
   Responsive — компактная вёрстка для узких экранов (≤ 640px).
   Цель: лендинг должен влезать во вьюпорт телефона без скролла.
   Дефолтные размеры рассчитаны на «комфортный» мобайл/планшет,
   на iPhone SE/8 они переполняют экран.
   ============================================================ */
@media (max-width: 640px) {
    #setup-screen {
        padding:
            max(12px, env(safe-area-inset-top))
            18px
            max(12px, env(safe-area-inset-bottom));
    }
    .setup-main {
        /* Прижимаем контент к верху, чтобы избежать большого провала
           между «Войти» и футером. flex: 1 на main удалять не надо —
           setup-content всё ещё распределяет пространство. */
        justify-content: flex-start;
        gap: 12px;
        padding: 8px 0 0;
    }
    .setup-orb {
        height: 110px;
    }
    .setup-orb::before {
        width: 110px;
        height: 110px;
    }
    .setup-orb .mz-wave {
        transform: scale(1.3);
    }
    .setup-title {
        font-size: 26px;
        margin-bottom: 8px;
    }
    .setup-subtitle {
        font-size: 14px;
        line-height: 1.3;
    }
    .setup-actions {
        gap: 10px;
    }
    .mz-btn--lg {
        height: 50px;
        font-size: 15px;
    }
    .recent-rooms.is-visible {
        max-height: 120px;
    }
    .setup-footer {
        font-size: 11.5px;
        padding-top: 4px;
    }
    /* Room screen — компактнее на мобиле, чтоб поле «Сообщение в чат»
       не уезжало под home indicator. */
    .room-header {
        padding: max(12px, calc(env(safe-area-inset-top) + 4px)) 14px 8px;
    }
    .speaker-strip {
        padding: 8px 16px 10px;
        min-height: 72px;
    }
    .room-footer {
        /* Отступы с учётом safe-area: инпут не лезет под home-indicator/скруглённые
           края и не мешает системному свайпу. Снизу всегда ≥14px (с клавиатурой
           safe-area=0, но небольшой зазор над ней остаётся). */
        padding: 8px max(12px, env(safe-area-inset-right)) max(8px, env(safe-area-inset-bottom)) max(12px, env(safe-area-inset-left));
        gap: 8px;
    }
}

/* ============================================================
   Responsive — desktop tweaks (≥ 1024px)
   ============================================================ */
@media (min-width: 1024px) {
    #setup-screen {
        max-width: 1280px;
        padding: 28px 56px 24px;
    }
    #invite-screen {
        max-width: 720px;
        padding-top: 80px;
    }

    .setup-content {
        display: grid;
        grid-template-columns: 1.05fr 1fr;
        grid-template-rows: 1fr auto;
        gap: 24px 56px;
        align-items: center;
    }
    .setup-main {
        gap: 28px;
        padding: 0;
        justify-content: center;
        grid-column: 1;
        grid-row: 1;
    }
    .setup-orb--inline { display: none; }
    .setup-orbital {
        display: block;
        grid-column: 2;
        grid-row: 1;
    }
    .recent-rooms {
        grid-column: 1 / -1;
        grid-row: 2;
        max-height: 0;
    }
    .recent-rooms.is-visible {
        max-height: 220px;
    }

    .setup-title { font-size: 72px; letter-spacing: -0.04em; line-height: 0.95; }
    .setup-subtitle { font-size: 19px; max-width: 520px; line-height: 1.45; }

    .setup-actions {
        flex-direction: row;
        gap: 14px;
        align-items: stretch;
    }
    #create-room-btn { flex-shrink: 0; padding-left: 24px; padding-right: 24px; }
    .setup-join-row {
        flex: 1;
        padding: 4px;
        background: var(--surface);
        border: 1px solid var(--border);
        border-radius: var(--r-pill);
        gap: 4px;
        max-width: 360px;
    }
    .setup-join-row .mz-input {
        background: transparent;
        border: 0;
        height: 48px;
    }
    .setup-join-row .mz-btn { height: 48px; }

    .setup-bullets { display: flex; }

    .room-header {
        padding: 20px 32px;
    }
    .room-header__title { font-size: 19px; }

    .speaker-strip {
        padding: 18px 32px 20px;
    }

    .chat-messages {
        padding: 20px 32px;
        max-width: 920px;
        margin: 0 auto;
        width: 100%;
    }

    .room-footer {
        padding: 16px 32px 24px;
        max-width: 920px;
        margin: 0 auto;
        width: 100%;
    }

    .modal-overlay {
        justify-content: center;
        align-items: center;
    }
    .modal-content {
        max-width: 480px;
        width: 90%;
        border-radius: var(--r-xl);
        border: 1px solid var(--border-strong);
        padding: 24px 0 28px;
    }
    .modal-header { padding: 0 28px 14px; }
    .modal-body { padding: 16px 28px 0; }
    .modal-handle { display: none; }
}

/* ============================================================
   CONSENT MODAL — баннер согласия с политикой конфиденциальности.
   Mobile-first: bottom-sheet на узких экранах, центрированный
   диалог на ≥640px.
   ============================================================ */

/* Блокировка скролла под модалкой. */
html.consent-locked,
html.consent-locked body {
    overflow: hidden;
    touch-action: none;
}

.consent-overlay {
    position: fixed;
    inset: 0;
    z-index: 9999; /* поверх всего: app-shell, settings, cmdk */
    display: flex;
    align-items: flex-end;
    justify-content: center;
    background: var(--scrim-opaque);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    padding: 16px;
    animation: consent-fade-in 0.22s ease-out;
}

.consent-overlay.hidden { display: none; }

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

.consent-card {
    width: 100%;
    max-width: 520px;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--r-xl) var(--r-xl) var(--r-md) var(--r-md);
    padding: 24px 22px max(20px, env(safe-area-inset-bottom));
    display: flex;
    flex-direction: column;
    gap: 18px;
    box-shadow: 0 -20px 60px var(--shadow-3);
    animation: consent-slide-up 0.28s cubic-bezier(0.2, 0.7, 0.2, 1);
    max-height: calc(100vh - 32px);
    overflow-y: auto;
}

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

.consent-card__head {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.consent-card__brand {
    font-size: 18px;
    font-weight: 700;
    letter-spacing: -0.02em;
}

.consent-card__title {
    font-size: 22px;
    line-height: 1.25;
    letter-spacing: -0.015em;
    margin: 0;
    color: var(--text);
}

.consent-card__body {
    color: var(--text-2);
    line-height: 1.55;
    font-size: 14.5px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.consent-card__body p { margin: 0; }

.consent-card__body a {
    color: var(--accent-text);
    text-decoration: none;
    border-bottom: 1px solid color-mix(in oklab, var(--accent) 40%, transparent);
}
.consent-card__body a:hover { border-bottom-color: var(--accent-text); }

.consent-card__bullets {
    list-style: none;
    padding: 0;
    margin: 4px 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
    font-size: 14px;
    color: var(--text-2);
}

.consent-card__bullets li {
    display: flex;
    align-items: center;
    gap: 10px;
}

.consent-bullet-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--accent);
    flex-shrink: 0;
}

.consent-card__fineprint {
    font-size: 12.5px;
    color: var(--text-dim);
    line-height: 1.5;
    margin: 0;
}

.consent-card__actions {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 4px;
}

.consent-card__leave {
    text-align: center;
    font-size: 12px;
    color: var(--text-dim);
    margin: 0;
}

/* Desktop / planshet: центрированный диалог. */
@media (min-width: 640px) {
    .consent-overlay {
        align-items: center;
        padding: 32px;
    }

    .consent-card {
        border-radius: var(--r-xl);
        padding: 32px 32px 28px;
        gap: 20px;
        max-height: calc(100vh - 64px);
    }

    .consent-card__title {
        font-size: 26px;
    }

    .consent-card__body {
        font-size: 15px;
    }
}

/* ============================================================
   AUTH — слот в шапке (Войти / @username) + модалки логин/регистрация.
   Использует существующий .modal-overlay/.modal-content паттерн,
   стилизуя только содержимое формы.
   ============================================================ */

.auth-slot {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
}

.auth-slot {
    gap: 8px;
}
.auth-slot[data-auth-state="anonymous"] .auth-slot__chip,
.auth-slot[data-auth-state="anonymous"] .auth-slot__rooms,
.auth-slot[data-auth-state="anonymous"] .auth-slot__admin { display: none; }
.auth-slot[data-auth-state="authenticated"] .auth-slot__login { display: none; }

/* Админка видна только для role=admin. */
.auth-slot__admin { display: none; }
.auth-slot[data-auth-role="admin"] .auth-slot__admin { display: inline-flex; }
.auth-slot__admin {
    height: 32px;
    font-size: 13px;
    padding: 0 12px;
    text-decoration: none;
    align-items: center;
    background: color-mix(in oklab, var(--accent) 12%, transparent);
    border-color: color-mix(in oklab, var(--accent) 40%, transparent);
    color: var(--accent-text);
}
@media (max-width: 640px) {
    .auth-slot__admin { display: none !important; }
}

.auth-slot__rooms {
    height: 32px;
    font-size: 13px;
    padding: 0 12px;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
}
@media (max-width: 640px) {
    /* На узком экране оставляем только аватар-чип, скрываем длинную ссылку. */
    .auth-slot__rooms { display: none !important; }
}

.auth-slot__login {
    height: 32px;
    font-size: 13px;
    padding: 0 14px;
}

.user-chip {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 3px 6px 3px 3px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--r-pill);
    color: var(--text);
    font-size: 13px;
    max-width: 60vw;
}

.user-chip__avatar {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    flex-shrink: 0;
    background:
        linear-gradient(135deg,
            color-mix(in oklab, var(--accent) 50%, var(--surface-3)),
            color-mix(in oklab, var(--accent) 15%, var(--surface-3)));
    background-size: cover;
    background-position: center;
    color: var(--accent-ink);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    font-weight: 700;
    text-decoration: none;
}

.user-chip__name {
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 140px;
    text-decoration: none;
    color: inherit;
}

.user-chip__name::before {
    content: "@";
    color: var(--text-dim);
    margin-right: 1px;
}
.user-chip__name:hover { color: var(--accent-text); }

.user-chip__action {
    width: 26px;
    height: 26px;
    padding: 0;
    border-radius: var(--r-pill);
    background: transparent;
    border: 0;
    color: var(--text-2);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background 0.12s, color 0.12s;
}
.user-chip__action svg { width: 14px; height: 14px; }
.user-chip__action:hover {
    background: var(--danger-soft);
    color: var(--danger);
}

/* --- Модалки логина/регистрации --- */

.auth-modal { max-width: 440px; }

.auth-form {
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding: 4px 0 8px;
}

.auth-field {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.auth-field__label {
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-dim);
    font-weight: 600;
}

.auth-field__hint {
    font-size: 12px;
    color: var(--text-dim);
    line-height: 1.3;
}

.auth-field .mz-input {
    width: 100%;
}

.auth-form__error {
    margin: 0;
    padding: 10px 12px;
    background: var(--danger-soft);
    border: 1px solid color-mix(in oklab, var(--danger) 30%, transparent);
    border-radius: var(--r-sm);
    color: var(--danger);
    font-size: 13px;
    line-height: 1.4;
}
.auth-form__error.hidden { display: none; }

.auth-form__switch {
    margin: 4px 0 0;
    text-align: center;
    font-size: 13px;
    color: var(--text-2);
}

.auth-form__switch a {
    color: var(--accent-text);
    text-decoration: none;
    border-bottom: 1px solid color-mix(in oklab, var(--accent) 40%, transparent);
}
.auth-form__switch a:hover { border-bottom-color: var(--accent-text); }

@media (max-width: 640px) {
    .auth-slot__login {
        height: 30px;
        font-size: 12.5px;
        padding: 0 12px;
    }
    .user-chip {
        font-size: 12.5px;
        padding-left: 10px;
    }
    .user-chip__name { max-width: 110px; }
}
