/* Токены семи тем новой оболочки.
 *
 * Значения перенесены из прототипа frontend/dev/app.html БЕЗ ИЗМЕНЕНИЙ, кроме
 * одного: --ft на светлых темах затемнён, потому что не проходил порог 3:1 для
 * второстепенного текста (замерено 2.79 / 2.79 / 2.89). Проверяется прогоном
 * `bun frontend/dev/v2-checks.mjs`.
 *
 * ТРИ ПРАВИЛА, каждое из которых уже стоило ошибки:
 *
 * 1. Медиазапрос ловит `:not([data-theme])`, а НЕ `:not([data-theme="light"])`.
 *    Со вторым системная тьма перебивала бы осознанно выбранную «Бумагу».
 * 2. Каждая тема переопределяет ВЕСЬ набор цветовых токенов. Половинчатый
 *    набор даёт текст одной темы на фоне другой.
 * 3. Цветовые токены — во всех темах; нецветовые (--radius-*, --transition-*)
 *    живут в старом base.css и здесь не дублируются. Тема меняет только цвет,
 *    никогда форму.
 * 4. Каждый блок ловит И `:root[data-theme=…]`, И просто `[data-theme=…]` на
 *    ЛЮБОМ узле. Это нужно образцам в выборе темы: они показывают палитру,
 *    которой на странице сейчас нет. С селектором только по `:root` все семь
 *    образцов красились текущей темой и выглядели одинаково — выбирать
 *    пришлось бы по названию вслепую.
 */

:root { /* светлая; она же «как в системе» при светлой ОС */
  --pg: #EAEAEC;
  --pg-2: #DEDEE1;
  --pg-line: #C9C9CE;
  --pg-tx: #17171A;
  --pg-dim: #67676E;
  --bg: #FFFFFF;
  --s2: #F3F3F5;
  --s3: #E8E8EC;
  --ln: #DDDDE2;
  --tx: #17171A;
  --dim: #6E6E77;
  --ft: #94949D;
  --br: #C2562A;
  --onbr: #FFFFFF;
  --brs: #F9E5DA;
  --mine: #2F6F62;
  --minetx: #FFFFFF;
  --mine-dim: rgba(255,255,255,.62);
  --other: #F3F3F5;
  --danger: #C0392B;
  --warn: #B8860B;
  --live: #2E8B57;
  --sh: 0 2px 6px rgba(20,20,24,.07), 0 22px 56px rgba(20,20,24,.13);
}

:root[data-theme="light"],
[data-theme="light"] {
  --pg: #EAEAEC;
  --pg-2: #DEDEE1;
  --pg-line: #C9C9CE;
  --pg-tx: #17171A;
  --pg-dim: #67676E;
  --bg: #FFFFFF;
  --s2: #F3F3F5;
  --s3: #E8E8EC;
  --ln: #DDDDE2;
  --tx: #17171A;
  --dim: #6E6E77;
  --ft: #94949D;
  --br: #C2562A;
  --onbr: #FFFFFF;
  --brs: #F9E5DA;
  --mine: #2F6F62;
  --minetx: #FFFFFF;
  --mine-dim: rgba(255,255,255,.62);
  --other: #F3F3F5;
  --danger: #C0392B;
  --warn: #B8860B;
  --live: #2E8B57;
  --sh: 0 2px 6px rgba(20,20,24,.07), 0 22px 56px rgba(20,20,24,.13);
}

:root[data-theme="paper"],
[data-theme="paper"] {
  --pg: #E7E1D6;
  --pg-2: #DBD4C6;
  --pg-line: #C6BDAB;
  --pg-tx: #231E16;
  --pg-dim: #6B6153;
  --bg: #FBF8F2;
  --s2: #F1ECE1;
  --s3: #E6DFD0;
  --ln: #DBD3C2;
  --tx: #231E16;
  --dim: #6F6555;
  --ft: #998F81;
  --br: #B4502A;
  --onbr: #FFF8F2;
  --brs: #F3E1D2;
  --mine: #2C6A5C;
  --minetx: #FFFFFF;
  --mine-dim: rgba(255,255,255,.62);
  --other: #F1ECE1;
  --danger: #A93226;
  --warn: #9A7215;
  --live: #2E8B57;
  --sh: 0 2px 6px rgba(35,30,22,.08), 0 22px 56px rgba(35,30,22,.14);
}

:root[data-theme="dark"],
[data-theme="dark"] {
  --pg: #09090A;
  --pg-2: #131315;
  --pg-line: #242427;
  --pg-tx: #E9E9EC;
  --pg-dim: #8C8C93;
  --bg: #141416;
  --s2: #1C1C1F;
  --s3: #252529;
  --ln: #2D2D33;
  --tx: #EDEDF0;
  --dim: #96969E;
  --ft: #6C6C74;
  --br: #E8964F;
  --onbr: #160E04;
  --brs: #33251A;
  --mine: #2E6E62;
  --minetx: #F1FFFA;
  --mine-dim: rgba(241,255,250,.6);
  --other: #1C1C1F;
  --danger: #FF6B6B;
  --warn: #E8B44F;
  --live: #4FBF7A;
  --sh: 0 2px 8px rgba(0,0,0,.55), 0 26px 66px rgba(0,0,0,.5);
}

:root[data-theme="midnight"],
[data-theme="midnight"] {
  --pg: #070912;
  --pg-2: #0E1220;
  --pg-line: #1C2233;
  --pg-tx: #E5E9F3;
  --pg-dim: #8992A8;
  --bg: #0D111C;
  --s2: #151B2A;
  --s3: #1D2435;
  --ln: #242C40;
  --tx: #E9EDF6;
  --dim: #96A0B6;
  --ft: #69728A;
  --br: #E8964F;
  --onbr: #130C04;
  --brs: #332619;
  --mine: #2A6C7E;
  --minetx: #EFFBFF;
  --mine-dim: rgba(239,251,255,.6);
  --other: #151B2A;
  --danger: #FF7A7A;
  --warn: #E9C06A;
  --live: #4FC08D;
  --sh: 0 2px 8px rgba(0,0,0,.6), 0 26px 66px rgba(0,0,0,.55);
}

:root[data-theme="coal"],
[data-theme="coal"] {
  --pg: #000000;
  --pg-2: #0A0A0A;
  --pg-line: #1B1B1B;
  --pg-tx: #F2F2F2;
  --pg-dim: #8A8A8A;
  --bg: #000000;
  --s2: #101010;
  --s3: #1A1A1A;
  --ln: #242424;
  --tx: #F5F5F5;
  --dim: #9A9A9A;
  --ft: #6A6A6A;
  --br: #F0A15A;
  --onbr: #140A00;
  --brs: #2A1C0E;
  --mine: #1F6A5C;
  --minetx: #EAFFF9;
  --mine-dim: rgba(234,255,249,.6);
  --other: #101010;
  --danger: #FF6E6E;
  --warn: #E4B056;
  --live: #4FCB86;
  --sh: 0 2px 8px rgba(0,0,0,.8), 0 26px 66px rgba(0,0,0,.7);
}

:root[data-theme="warm"],
[data-theme="warm"] {
  --pg: #0F0D0B;
  --pg-2: #181513;
  --pg-line: #2A2522;
  --pg-tx: #EDEAE5;
  --pg-dim: #968E85;
  --bg: #17140F;
  --s2: #211C17;
  --s3: #2C261F;
  --ln: #342D25;
  --tx: #F1EEE8;
  --dim: #9C948A;
  --ft: #736C63;
  --br: #E8964F;
  --onbr: #1A1105;
  --brs: #3A2A1A;
  --mine: #3E8C7B;
  --minetx: #08120F;
  --mine-dim: rgba(8,18,15,.55);
  --other: #211C17;
  --danger: #E86A5A;
  --warn: #D9A441;
  --live: #5CBF83;
  --sh: 0 2px 8px rgba(0,0,0,.5), 0 26px 66px rgba(0,0,0,.45);
}

/* ── СТАРЫЕ ЦВЕТА ────────────────────────────────────────────────────────────
   Две темы с прежним синим акцентом (#5865F2) — тем самым, что был в старой
   оболочке до перехода на тёплую палитру. Заведены по просьбе Вадима: кому
   привычнее прежний вид, тому не обязательно мириться с новым.

   ⚠️ Это НЕ копия старой оболочки, а её АКЦЕНТ в новой раскладке. Полностью
   воспроизводить старую палитру незачем: серые поверхности новой оболочки
   выверены под её же геометрию, а меняет ощущение именно акцент.

   ⚠️ `--onbr` — цвет ТЕКСТА НА акценте, и он белый в обеих: синий #5865F2
   тёмный, чёрные подписи на нём не читаются. У тёплых тем там наоборот
   тёмный, потому что оранжевый светлый. Это не мелочь — на этом ломается
   читаемость главной кнопки экрана.
   ───────────────────────────────────────────────────────────────────────── */

:root[data-theme="indigo"],
[data-theme="indigo"] {
  --pg: #0A0A0C;
  --pg-2: #131318;
  --pg-line: #24242B;
  --pg-tx: #E9E9EF;
  --pg-dim: #8C8C97;
  --bg: #141418;
  --s2: #1C1C22;
  --s3: #25252D;
  --ln: #2D2D37;
  --tx: #EDEDF2;
  --dim: #9696A2;
  --ft: #6C6C78;
  --br: #5865F2;
  --onbr: #FFFFFF;
  --brs: #232A5C;
  --mine: #3C45A5;
  --minetx: #FFFFFF;
  --mine-dim: rgba(255,255,255,.62);
  --other: #1C1C22;
  --danger: #FF6B6B;
  --warn: #E8B44F;
  --live: #4FBF7A;
  --sh: 0 2px 8px rgba(0,0,0,.55), 0 26px 66px rgba(0,0,0,.5);
}

:root[data-theme="indigo-light"],
[data-theme="indigo-light"] {
  --pg: #E9E9EE;
  --pg-2: #DDDDE4;
  --pg-line: #C7C7D0;
  --pg-tx: #17171C;
  --pg-dim: #66666F;
  --bg: #FFFFFF;
  --s2: #F3F3F7;
  --s3: #E7E7EE;
  --ln: #DCDCE4;
  --tx: #17171C;
  --dim: #6E6E79;
  --ft: #94949F;
  --br: #5865F2;
  --onbr: #FFFFFF;
  --brs: #E4E6FD;
  --mine: #4752C4;
  --minetx: #FFFFFF;
  --mine-dim: rgba(255,255,255,.66);
  --other: #F3F3F7;
  --danger: #C0392B;
  --warn: #B8860B;
  --live: #2E8B57;
  --sh: 0 2px 6px rgba(20,20,24,.07), 0 22px 56px rgba(20,20,24,.13);
}

/* «Как в системе» при тёмной ОС. Отдельного data-theme у неё нет. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --pg: #09090A;
    --pg-2: #131315;
    --pg-line: #242427;
    --pg-tx: #E9E9EC;
    --pg-dim: #8C8C93;
    --bg: #141416;
    --s2: #1C1C1F;
    --s3: #252529;
    --ln: #2D2D33;
    --tx: #EDEDF0;
    --dim: #96969E;
    --ft: #6C6C74;
    --br: #E8964F;
    --onbr: #160E04;
    --brs: #33251A;
    --mine: #2E6E62;
    --minetx: #F1FFFA;
    --mine-dim: rgba(241,255,250,.6);
    --other: #1C1C1F;
    --danger: #FF6B6B;
    --warn: #E8B44F;
    --live: #4FBF7A;
    --sh: 0 2px 8px rgba(0,0,0,.55), 0 26px 66px rgba(0,0,0,.5);
  }
}

/* ─────────────────────────────────────────────────────────────────────────
   ОБЩИЕ ПОВЕРХНОСТИ СТАРОЙ ОБОЛОЧКИ ПОД НОВОЙ ПАЛИТРОЙ.

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

   ⚠️ ОДНО ОТОБРАЖЕНИЕ ПОКРЫВАЕТ ВСЕ СЕМЬ ТЕМ, потому что справа стоят
   переменные, а не значения: тема меняет `--s2`, и вместе с ним меняется
   `--bg-primary`. Прописать семь блоков по 30 строк значило бы завести семь
   мест, где можно рассинхронизироваться.

   ⚠️ Только при `body.v2-active`. Без гейта это перекрасило бы всю старую
   оболочку у людей, которые её и выбрали.
   ───────────────────────────────────────────────────────────────────────── */
body.v2-active {
  --bg-base:      var(--pg);
  --bg-primary:   var(--bg);
  --bg-secondary: var(--s2);
  /* ⚠️ `--bg-tertiary` НЕ `--pg-2`, хотя по смыслу «самая тёмная поверхность»
     это он. В новой оболочке `--pg-2` — фон ПОЛЕЙ страницы, а старые оверлеи
     кладут на этот токен карточки с текстом. На «Бумаге» подсказка получила
     контраст 2.16 — нечитаемо. Карточке нужна поднятая поверхность. */
  --bg-tertiary:  var(--s2);
  --bg-input:     var(--s3);
  --bg-elev:      var(--s3);
  --bg-hover:     var(--s3);

  --text-primary:   var(--tx);
  --text-secondary: var(--dim);
  /* ⚠️ И `--text-muted` НЕ `--ft`. `--ft` — самый бледный уровень, он
     рассчитан на плейсхолдеры поверх ярких поверхностей; на карточке
     настроек он давал 2.16 на «Бумаге» и 2.7 на светлой. Третий уровень
     серого здесь теряется, зато подсказки читаются: 4.86 против 2.7. */
  --text-muted:     var(--dim);

  --accent:       var(--br);
  --accent-hover: var(--br);
  /* ⚠️ Текст НА акценте берём из `--onbr`, а не оставляем белым: у светлых тем
     акцент светлый, и белым по нему не прочитать ничего. */
  --accent-tint-strong: var(--brs);
  --accent-tint-soft:   var(--brs);

  --border:        var(--ln);
  --border-faint:  var(--ln);
  --border-strong: var(--ln);
  --shadow:        var(--sh);
}

/* ⚠️ Тёмный текст `--onbr` годится ТОЛЬКО там, где под ним ЗАЛИВКА акцентом.
   Первый заход покрасил заодно активную вкладку настроек — а у неё фон
   тёмный, не акцентный, и надпись стала нечитаемой (замер: #160E04 на
   #131315). Здесь только те, у кого фон действительно акцентный. */
body.v2-active .btn-primary,
body.v2-active .theme-toggle-btn.active { color: var(--onbr); }
/* У активной вкладки настроек фон свой, тёмный — ей нужен акцентный ТЕКСТ. */
body.v2-active .settings-nav-item.active { color: var(--br); }

/* Радиусы и шрифт — язык формы новой оболочки. Тема цвет не трогает. */
body.v2-active .settings-overlay {
  font-family: "Golos Text", "Helvetica Neue", Arial, sans-serif;
}
body.v2-active .settings-overlay .emotes-settings-block { border-radius: 16px; }
body.v2-active .settings-overlay .settings-nav-item,
body.v2-active .settings-overlay .theme-toggle-btn,
body.v2-active .settings-overlay .form-input { border-radius: 12px; }

/* ── плитки выбора темы ─────────────────────────────────────────────────── */
.theme-card-grid {
  display: grid; gap: 8px; width: 100%;
  grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
}
.settings-toggle-row-stack { flex-direction: column; align-items: stretch; gap: 12px; }
.theme-card {
  display: flex; flex-direction: column; align-items: stretch; gap: 7px;
  padding: 7px; border: 1px solid var(--border); border-radius: 12px;
  background: transparent; color: var(--text-secondary);
  font: inherit; font-size: 12.5px; cursor: pointer; text-align: center;
}
.theme-card:hover { background: var(--bg-hover); color: var(--text-primary); }
.theme-card.active { border-color: var(--accent); color: var(--text-primary); }
/* Образец красится СВОЕЙ палитрой: на нём висит собственный data-theme, и
   переменные ниже берутся из его блока, а не из темы страницы. */
.theme-card-swatch {
  display: flex; height: 40px; border-radius: 8px; overflow: hidden;
  border: 1px solid var(--pg-line, var(--border));
}
.theme-card-swatch i { flex: 1; display: block; }
.theme-card-swatch .tc-a { background: var(--pg, #888); }
.theme-card-swatch .tc-b { background: var(--bg, #999); }
.theme-card-swatch .tc-c { background: var(--br, #aaa); flex: 0 0 26%; }
.theme-card-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* «Как в системе» — образец из двух половин: у него нет своей палитры, и
   показать одну значило бы соврать про вторую. */
.theme-card[data-theme-pick=""] .theme-card-swatch { border-color: #8a8a90; }
.theme-card[data-theme-pick=""] .tc-a { background: #EAEAEC; }
.theme-card[data-theme-pick=""] .tc-b { background: #09090A; }
.theme-card[data-theme-pick=""] .tc-c { background: linear-gradient(180deg, #C2562A 50%, #E8964F 50%); }
/* Раскладка обеих оболочек.
 *
 * ⚠️ ВСЕ классы несут префикс v2- и живут под корневым .v2-shell. В прототипе
 * они голые (.row, .chip, .body), но v2.css подключён ГЛОБАЛЬНО — и на
 * страницах со старой оболочкой тоже. Такие короткие имена столкнулись бы с
 * components.css. В существующем коде это правило уже соблюдается
 * (.player-expanded .pe-*, .video-pip .vp-*); v2 его не нарушает.
 */

/* ⚠️ СЛОЙ ПЕРЕЛЁТА ОБЯЗАН БЫТЬ НЕПРОЗРАЧНЫМ. Своего фона у него не было вовсе —
   ни у слоя, ни у летящего вида, — и въезжающий экран просвечивал: на экране
   наслаивались две страницы текста, а старая «пропадала под» новой по мере
   движения. Замер во время полёта дал `rgba(0,0,0,0)` у обоих.

   Раньше это не было заметно, потому что летала ТОЛЬКО беседа: у ChatArea свой
   фон. Стоило пустить в полёт наши экраны — меню «Я», список настроек, — и
   дыра открылась. Фон живёт на СЛОЕ, а не на видах: тогда он есть у всего, что
   когда-либо полетит, а не у тех видов, о которых вспомнили.

   ⚠️ `var(--bg)` — тот же, что у `.v2-shell` ниже. Летящий экран обязан
   выглядеть продолжением оболочки, а не карточкой поверх неё. */
.v2-slide { background: var(--bg); }

.v2-shell {
  position: absolute;
  inset: 0;
  display: flex;
  background: var(--bg);
  color: var(--tx);
  font-family: "Golos Text", "Helvetica Neue", Arial, sans-serif;
  overflow: hidden;
}

/* ── тело под тач ───────────────────────────────────────────────────────── */
.v2-phone { flex-direction: column; }

.v2-phone .v2-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

/* Переписка и дом сервера владеют прокруткой сами — тело только раздаёт им
   высоту. Без этого `.chat-messages` растягивалась на высоту содержимого и
   скроллилась вся оболочка, а не лента сообщений. */
.v2-phone .v2-body.v2-body-fixed {
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.v2-phone .v2-body.v2-body-fixed > * { flex: 1; min-height: 0; }

.v2-tabbar {
  flex: 0 0 auto;
  height: var(--v2-tabbar-h);
  display: flex;
  border-top: 1px solid var(--ln);
  background: var(--bg);
  padding: 6px 4px calc(4px + env(safe-area-inset-bottom, 0px));
}

/* ── Настройка панели вкладок ────────────────────────────────────────────
   ⚠️ ВЫСОТА ПАНЕЛИ НЕ МЕНЯЕТСЯ, хотя подписи и уходят. На `--v2-tabbar-h`
   опираются карточка мини-плеера и лист очереди; сжав панель, пришлось бы
   пересчитывать их положение, а выигрыш — десяток пикселей. Освободившееся
   место отдаём значку: 22 → 30, и цель под палец становится крупнее. */
.v2-tabbar.is-compact .v2-tab-l { display: none; }
.v2-tabbar.is-compact .v2-tab { justify-content: center; }
.v2-tabbar.is-compact .v2-tab-i svg { width: 30px; height: 30px; }
.v2-tabbar.is-compact .v2-tab-av { width: 30px; height: 30px; }

/* Аватар вместо значка у раздела «Я». ⚠️ Кружок опознаётся ободком, а не
   заливкой: у выбранной вкладки значок заливается акцентом, а заливать
   фотографию нечем. */
.v2-tab-av {
  width: 24px; height: 24px;
  border-radius: 50%;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  background: var(--s3);
  font-size: 11px;
  color: var(--dim);
}
.v2-tab-av img { width: 100%; height: 100%; object-fit: cover; display: block; }
.v2-tab-av.is-on { box-shadow: 0 0 0 2px var(--br); }

.v2-tab {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 5px 0 3px;
  border: 0;
  background: transparent;
  border-radius: 12px;
  color: var(--ft);
  cursor: pointer;
  font: inherit;
}
.v2-tab-i { font-size: 21px; line-height: 1; }
.v2-tab-l { font-size: 10px; font-weight: 600; white-space: nowrap; }
.v2-tab[aria-pressed="true"] { color: var(--br); }

/* ── тело под курсор ────────────────────────────────────────────────────── */
.v2-desk { flex-direction: row; }

/* ── общие размеры оболочки ──────────────────────────────────────────────
   ⚠️ ОДНО ЧИСЛО НА ВСЕХ, КТО ОТ НЕГО ЗАВИСИТ. Ширина рельсы участвует в
   отступе мини-плеера, высота шапки — в двух колонках сразу. Разъехавшись на
   два пикселя, они дают ту самую «неопрятность», которую видно, а объяснить
   трудно. */
/* ⚠️ ОБЪЯВЛЕНЫ НА `body`, А НЕ НА `.v2-shell`. Мини-плеер — `position: fixed`
   ребёнок BODY, он НЕ потомок оболочки: переменная, объявленная на `.v2-shell`,
   до него не доходит, `left: var(--v2-rail-w)` становится недействительным, и
   правило молча не применяется. Замер это и показал: плеер остался на `left: 0`
   и по-прежнему накрывал рельсу. Свойство видно только СВЕРХУ ВНИЗ. */
body.v2-active {
  --v2-rail-w: 76px;
  --v2-head-h: 52px;
  /* ⚠️ Высота панели вкладок объявлена ЗДЕСЬ, а не на самой `.v2-tabbar`.
     Там она никому не видна: пользовательские свойства наследуются ВНИЗ, а
     мини-плееру и листу очереди нужно считать своё положение НАД панелью —
     они ей не потомки. Второй копией числа это и разошлось бы. */
  --v2-tabbar-h: 58px;
  /* Метрики плавающей карточки плеера. Их читает и сам плеер, и лист
     очереди, который встаёт над ним. */
  --v2-mini-gap: 6px;
  --v2-mini-h: 62px;
  --v2-mini-side: 8px;
  --v2-mini-bottom: calc(var(--v2-tabbar-h) + var(--v2-mini-gap) + env(safe-area-inset-bottom, 0px));
}

.v2-rail {
  /* Ширина рельсы — переменной: на неё опирается отступ мини-плеера, и два
     числа в разных местах разошлись бы при первой же правке. */
  flex: 0 0 var(--v2-rail-w);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  /* ⚠️ Нижний отступ 5, а не 10: под ним стоит версия, и вместе с её
     собственным полем набегала полоса пустоты в полтора сантиметра. */
  padding: 14px 0 5px;
  background: var(--s2);
  border-right: 1px solid var(--ln);
  overflow-y: auto;
}
.v2-rail-b {
  width: 52px;
  height: 52px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  border: 0;
  border-radius: 17px;
  background: transparent;
  color: var(--ft);
  cursor: pointer;
  font: inherit;
  flex: 0 0 auto;
}
.v2-rail-i { font-size: 21px; line-height: 1; }
.v2-rail-l { font-size: 10px; font-weight: 600; }
.v2-rail-b[aria-pressed="true"] { background: var(--bg); color: var(--br); box-shadow: var(--sh); }

/* ⚠️ Тонкая черта между избранным и группой «есть непрочитанное». Она не
   украшение: группы живут по РАЗНЫМ правилам — верхняя неподвижна, нижняя
   схлопывается по мере прочтения. Без черты нижняя читается как продолжение
   верхней, и человек бьёт мимо, когда она исчезает. */
.v2-rail-sep-soft {
  width: 28px;
  height: 1px;
  margin: 6px auto;
  background: var(--ln);
  opacity: .7;
}

/* Плитка догоняющей группы — приглушена, чтобы отличаться от избранного даже
   без черты (человек может прокрутить панель так, что черта уедет). */
.v2-rail-sp.is-transient { opacity: .78; }
.v2-rail-sp.is-transient:hover { opacity: 1; }

/* ⚠️ Подсказка пустой панели. Панель шириной 76px, поэтому текст переносится
   и центрируется — иначе слова обрезаются по краю и подсказка сама становится
   загадкой. Показывается только когда есть что добавлять (см. servers.js). */
.v2-rail-hint {
  padding: 10px 8px 4px;
  font-size: 11px;
  line-height: 1.3;
  text-align: center;
  color: var(--dim);
  /* ⚠️ Перенос ТОЛЬКО по пробелам. Было `overflow-wrap: anywhere`, и на
     панели шириной 76px последнее слово рвалось посреди себя
     («сообществ / а») — подсказка сама становилась загадкой. Слова подобраны
     так, чтобы каждое влезало в строку; полное объяснение живёт в `title`. */
  overflow-wrap: normal;
  word-break: keep-all;
  hyphens: none;
}
/* ── Индикаторы разделов ─────────────────────────────────────────────────
   ⚠️ БЕЙДЖ ВНУТРИ УЗЛА ЗНАЧКА, А НЕ КНОПКИ. Кнопка рельсы — колонка «значок +
   подпись», и абсолютный бейдж от неё считался бы от всей колонки: у разделов
   с длинной подписью («Сообщества») он уезжал бы к тексту, у коротких — нет.
   Значок же везде 21×21, и якорь получается один.

   ⚠️ РОДИТЕЛЬ ОБЯЗАН БЫТЬ `position: relative` И `inline-block`. У инлайнового
   `line-height: 1` нет своей высоты для отсчёта, и `top` считался бы от строки
   текста, а не от значка.

   ⚠️ `[hidden]` СНИМАЕТСЯ ЯВНО. Мы объявляем `display: grid`, а это перебивает
   стандартное `[hidden] { display: none }` из таблицы браузера — та же ошибка
   уже была с `.settings-nav-item` и с панелью мультивыбора. Объявил
   `display` — объяви и скрытие. */
.v2-rail-i, .v2-tab-i { position: relative; display: inline-block; }
.v2-nb {
  position: absolute; top: -5px; left: 100%;
  transform: translateX(-45%);
  min-width: 16px; height: 16px; padding: 0 4px; box-sizing: border-box;
  border-radius: 8px; background: var(--br); color: var(--onbr);
  font-size: 10.5px; font-weight: 700; line-height: 16px; text-align: center;
  font-variant-numeric: tabular-nums;
  display: grid; place-items: center;
  /* ⚠️ Обводка цветом фона отделяет бейдж от значка под ним: без неё цифра на
     тёмном значке читается как часть рисунка.
     ⚠️ `--s2`, А НЕ ВЫДУМАННОЕ ИМЯ. Несуществующая переменная молча берёт
     запасное значение, и расхождение видно только глазом — в этой оболочке так
     уже вышло с `--v2-accent`, где сорок восемь мест разом стали синими.
     Настоящий список — `css/tokens.css`. */
  box-shadow: 0 0 0 2px var(--v2-nb-ring, var(--s2));
  pointer-events: none;
}
.v2-nb[hidden] { display: none; }
/* ⚠️ ФОН ПОД БЕЙДЖЕМ РАЗНЫЙ У ДВУХ ТЕЛ: рельса залита `--s2`, панель вкладок —
   `--bg`. Один цвет обводки на оба дал бы на телефоне видимое кольцо не в тон.
   Задаём переменной у КОНТЕЙНЕРА, а не двумя копиями правила бейджа. */
.v2-rail { --v2-nb-ring: var(--s2); }
.v2-tabbar { --v2-nb-ring: var(--bg); }
/* Точка: то же место, но без числа — «там что-то есть». */
.v2-nb.is-dot {
  min-width: 9px; width: 9px; height: 9px; padding: 0; border-radius: 50%;
  top: -2px; transform: translateX(-55%);
}
/* ⚠️ АДРЕСНОЕ ОБРАЩЕНИЕ ОТЛИЧАЕТСЯ ЦВЕТОМ. Упоминание и реакция — не «есть
   новое», а «зовут тебя»; общий цвет с обычным непрочитанным стёр бы разницу,
   ради которой у них отдельные счётчики и отдельный пробой мьюта. */
.v2-nb.is-addressed { background: var(--danger); color: #fff; }

/* Плитки сообществ: у них бейдж прижат к правому верхнему углу самой плитки —
   своего узла значка там нет, плитка это и есть значок. */
.v2-rail-sp { position: relative; }
.v2-rail-sp .v2-nb { top: -3px; left: auto; right: -3px; transform: none; }

.v2-rail-sep { width: 30px; height: 1px; background: var(--ln); margin: 8px 0 6px; flex: 0 0 auto; }
.v2-rail-spaces { display: flex; flex-direction: column; align-items: center; gap: 6px; }

.v2-list {
  flex: 0 0 306px;
  min-width: 0;
  background: var(--s2);
  border-right: 1px solid var(--ln);
  overflow-y: auto;
}
.v2-main { flex: 1; min-width: 0; display: flex; flex-direction: column; overflow: hidden; }

/* ── пустые состояния ───────────────────────────────────────────────────── */
.v2-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 100%;
  min-height: 200px;
  padding: 40px 24px;
  text-align: center;
}
.v2-empty-title { font-size: 17px; font-weight: 600; letter-spacing: -.015em; }
.v2-empty-text { font-size: 14px; color: var(--dim); max-width: 34ch; line-height: 1.5; }

/* ── мини-плеер аудио ───────────────────────────────────────────────────── */
/* Он монтируется в <body> вне роутера и может всплыть сам — например, при
   восстановлении воспроизведения с другого устройства. На телефоне прибит к
   низу, туда же встаёт панель вкладок. Не прячем, а поднимаем над ней:
   спрятать значит отобрать управление тем, что уже звучит. */
/* ── мини-плеер в новой оболочке ────────────────────────────────────────── */
/*
   ⚠️ Класс именно `.mini-player`, а не `.player`. Первая версия правила
   целилась в `.player` — селектор ни во что не попадал, и отступ молча не
   применялся: плеер стоял ПОД панелью вкладок. Поймано замером: узел в DOM
   был, стиль — нет.

   ⚠️ А вторая версия просто ПОДНЯЛА полосу на 8px, и это было хуже, чем
   ничего. Старый плеер — полоса `left/right: 0; bottom: 0` во всю ширину;
   поднятая полоса во всю ширину читается как оторвавшаяся плита, а не как
   элемент. Правильный ответ не «сдвинуть», а «сменить форму»: на телефоне это
   КАРТОЧКА с полями, как в прототипе, и стоит она вплотную над вкладками.
*/
@media (max-width: 899px) {
  body.v2-active .mini-player {
    left: var(--v2-mini-side); right: var(--v2-mini-side); width: auto;
    bottom: var(--v2-mini-bottom);
    height: var(--v2-mini-h);
    padding: 0 10px;
    border: 1px solid var(--ln);
    border-radius: 16px;
    background: var(--s2);
    box-shadow: 0 10px 26px -14px rgba(0, 0, 0, .6);
    /* Обрезаем по скруглению: полоса прогресса — прямая линия во всю ширину,
       и на скруглённой карточке она резала углы. Именно это и выглядело криво. */
    overflow: hidden;
  }
  /* Прогресс уезжает на НИЖНЮЮ кромку. Сверху он спорил с содержимым карточки
     и читался как чужой элемент, приклеенный к её краю; снизу — как «карточка
     наполняется», и углы ему больше не мешают. */
  body.v2-active .mini-player .mini-progress { top: auto; bottom: 0; height: 10px; }
  body.v2-active .mini-player .mini-progress-fill { top: auto; bottom: 0; height: 3px; }
  /* ⚠️ КАНАВКУ ЗАБЫЛИ, И РАСХОЖДЕНИЕ БЫЛО ВИДНО ГЛАЗОМ. Полоса — это ТРИ
     слоя: область нажатия `.mini-progress` (10px), заливка `-fill` и
     канавка `::before`, которая живёт в базовом файле с `top: 0`. Правила
     выше увели вниз первые два, а канавка осталась у ВЕРХА десятипиксельной
     области: замер дал разницу в 7px — серая линия у кнопок, оранжевая у
     кромки карточки. Высота та же, что у заливки, иначе одна выглядывает
     из-под другой. */
  body.v2-active .mini-player .mini-progress::before {
    top: auto;
    bottom: 0;
    height: 3px;
  }
  /* Бегунок — affordance для мыши. На карточке под пальцем он только мусорит:
     тянуть можно за всю полосу, а сам он вылезал за обрезку. */
  body.v2-active .mini-player .mini-progress-thumb { display: none; }
  /* Уезжает вниз целиком, вместе с полями — иначе при скрытии видно край. */
  body.v2-active .mini-player.is-hidden { transform: translateY(calc(100% + 70px)); }
}

/* ⚠️ На десктопе панели вкладок НЕТ, и поднимать плеер не от чего: там он
   обязан лежать на нижней кромке, как лежал всегда. Мой прежний `bottom: 8px`
   отрывал его без всякой причины. */
@media (min-width: 900px) {
  /* ⚠️ ПЛЕЕР НЕ НАЕЗЖАЕТ НА РЕЛЬСУ. Полоса `left: 0` во всю ширину накрывала её
     нижнюю кромку — а там профиль и версия, то есть постоянная навигация
     становилась недоступной, пока что-то играет. Рельса это РАМА приложения,
     и временная полоса воспроизведения не вправе её закрывать; поднимать же
     содержимое рельсы значит двигать раму под содержимое. Плеер начинается
     после рельсы. */
  body.v2-active .mini-player {
    bottom: 0;
    left: var(--v2-rail-w);
    /* ⚠️ И `right`, И `width: auto`. У полосы задана ширина 100%, поэтому один
       только сдвиг слева выносил её правый край за окно на те же 76px —
       замер: правая кромка 1476 при окне 1400. */
    right: 0;
    width: auto;
  }
}

/* ── переписка внутри новой оболочки ────────────────────────────────────── */
/* Внутри живёт НАСТОЯЩАЯ ChatArea со своей разметкой. Переоформляем её
   стилями, не трогая структуру: геометрия .chat-messages участвует в расчёте
   высот сообщений (константа инсета 116 калибрована по ней), а имена
   .chat-input-area и .channel-sidebar ищет виджет загрузок. */
/* ⚠️ КОЛОНКА, а не строка. Пока единственным ребёнком был слот переписки,
   направление не имело значения. С появлением театра оно стало решающим:
   строка поставила его РЯДОМ с перепиской, и лента сжалась до 32px. В цифрах
   это видно сразу, на глаз — «почему-то всё поехало». */
.v2-chat { flex: 1; min-height: 0; display: flex; flex-direction: column; }
/* ⚠️ `min-height: 0` обязателен: без него флекс-ребёнок не сжимается ниже
   содержимого — лента вырастала до 6523px при экране 844, и прокрутка уезжала
   на всю оболочку. Пока `.v2-chat` был строкой, высота приходила иначе и это
   не всплывало. */
.v2-chat-mount { flex: 1; min-width: 0; min-height: 0; display: flex; }
/* Раскладку держателя даёт класс `chat-area` из chat.css — своих правил тут
   больше нет намеренно: дубликат разошёлся бы с оригиналом при первой правке. */
.v2-chat-mount > .v2-chat-area { min-width: 0; min-height: 0; }

.v2-shell .chat-messages-column { background: var(--bg); }

.v2-shell .chat-header {
  background: var(--bg);
  border-bottom: 1px solid var(--ln);
  padding: 10px 14px;
  gap: 10px;
  /* ⚠️ ВЫСОТА ШАПКИ — ОДНА НА ДВЕ КОЛОНКИ. Шапка беседы была 52, шапка панели
     вложений 50: разделительные черты не совпадали на два пикселя, и крестик
     панели выглядел «съехавшим». Замер это и показал; на глаз читалось просто
     как неопрятность. Держим числом в одном месте. */
  min-height: var(--v2-head-h);
  box-sizing: border-box;
}
/* Панель вложений/участников — та же высота шапки, что у беседы слева. */
.v2-shell .mg-header {
  min-height: var(--v2-head-h);
  box-sizing: border-box;
  padding: 10px 16px;
}
.v2-shell .chat-header-channel-name { font-size: 17px; font-weight: 600; letter-spacing: -.015em; color: var(--tx); }
.v2-shell .chat-header-subtitle { font-size: 13px; color: var(--dim); }
.v2-shell .channel-hash { color: var(--ft); }
.v2-shell .chat-header-btn { color: var(--dim); border-radius: 12px; }
.v2-shell .chat-header-btn:hover { background: var(--s2); color: var(--tx); }
.v2-shell .chat-search {
  background: var(--s2); border: 1px solid var(--ln); border-radius: 12px;
  color: var(--tx); padding: 7px 12px;
}

/* ⚠️ ПОЛЕ ВВОДА НЕ ПЕРЕОФОРМЛЯЕМ — И ЭТО РЕШЕНИЕ, А НЕ ПРОПУСК. Здесь стояла
   «пилюля»: радиус 20, своя рамка, фон `--s2` и разделительная черта над
   полосой. Вадим сравнил с прежним видом и выбрал прежний. Оригинал живёт в
   `css/pages/chat.css` (радиус 8, без рамки, фон `--bg-input`) и в новой
   оболочке выглядит правильно сам: мост токенов в `tokens.css` уже отображает
   `--bg-input` на `--s3`, а `--text-muted` на `--dim`. Поэтому здесь ПУСТО, а
   не «то же самое другими словами» — второе описание разошлось бы с первым при
   первой же правке. */

/* Кнопка «назад» на телефоне: список остался позади, вернуться надо явно. */
.v2-phone .v2-chat { position: relative; }
.v2-phone .mobile-nav-btn { color: var(--dim); }

/* ⚠️ Запас под плеером. Он `position: fixed` и из потока выпадает — списки
   уезжали под него, и последняя строка была наполовину закрыта. Запас даём
   ТОЛЬКО когда плеер на экране: постоянная дыра внизу пустого экрана выглядит
   как ошибка вёрстки. Класс `has-mini-player` ставит сам плеер. */
@media (max-width: 899px) {
  body.v2-active.has-mini-player .v2-body { padding-bottom: 74px; }
}
@media (min-width: 900px) {
  body.v2-active.has-mini-player .v2-list,
  body.v2-active.has-mini-player .v2-main { padding-bottom: 72px; }
  /* У переписки свой композер внизу — ему запас не нужен, он и так над
     плеером не окажется: колонка отдаёт высоту, а не наезжает. */
  body.v2-active.has-mini-player .v2-main:has(.v2-chat) { padding-bottom: 72px; }
}

/* ── профиль внизу рельса ───────────────────────────────────────────────── */
/* Распорка отжимает его к кромке независимо от числа сообществ выше. */
.v2-rail-gap { flex: 1 1 auto; min-height: 12px; }
.v2-rail-me {
  /* ⚠️ Отступ снизу СНЯТ. Между аватаркой и версией стояли и он, и `gap: 6px`
     самой рельсы — шестнадцать пикселей на стык двух вещей, которые читаются
     как один блок «это ты и это твоя сборка». */
  flex: 0 0 auto; margin: 0; padding: 4px;
  border: 0; background: transparent; border-radius: 14px; cursor: pointer;
  display: grid; place-items: center;
}
.v2-rail-me:hover { background: var(--s2); }
/* Активное состояние — кольцо вокруг аватара, а не заливка кнопки: заливка
   спорила бы с самой картинкой и делала её грязной. */
.v2-rail-me[aria-pressed="true"] .v2-rail-me-av { box-shadow: 0 0 0 2px var(--br); }
.v2-rail-me-av {
  width: 34px; height: 34px; border-radius: 50%; overflow: hidden;
  display: grid; place-items: center;
  color: #fff; font-size: 14px; font-weight: 600;
}
.v2-rail-me-av img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ⚠️ У `.v2-rail-ver` НЕ БЫЛО НИ ОДНОГО ПРАВИЛА — узел создавался, а отступы
   набегали случайно, из `gap` рельсы и поля кнопки профиля. Отсюда и полоса
   пустоты под версией, и великоватый зазор над ней. */
.v2-rail-ver {
  flex: 0 0 auto; margin-top: -2px;
  line-height: 1; text-align: center;
}


/* ─────────────────────────────────────────────────────────────────────────
   ВЫХОД ИЗ БЕСЕДЫ ОБЯЗАН БЫТЬ ВСЕГДА, КОГДА ТЕЛО ТЕЛЕФОННОЕ.

   ⚠️ Кнопка `#mobile-menu-btn` принадлежит СТАРОЙ оболочке, и показывает её
   старый медиазапрос: `max-width: 767px`. Новое тело переключается на 900px.
   В полосе 768-899 получалась дыра: беседа уже во весь экран, панель вкладок
   уже убрана, а кнопки «назад» ещё нет — выйти нечем вообще. Замер на 850px:
   тело `v2-phone`, вкладок 0, у кнопки `display: none`.

   ⚠️ Чиним ЗДЕСЬ, а не сдвигом брейкпоинта. 900px выбран под две колонки и к
   чужому медиазапросу отношения не имеет; подгонять его под цифру из старой
   таблицы стилей значит связать две несвязанные величины. Новое тело само
   отвечает за то, что нужно ему.
   ───────────────────────────────────────────────────────────────────────── */
body.v2-active .v2-phone #mobile-menu-btn { display: inline-flex; }

/* ============================================================================
   ЛИСТ ОЧЕРЕДИ ПОД КАРТОЧКУ ПЛЕЕРА (2026-09-05)
   ============================================================================
   Лист остался от прежней раскладки, где плеер был полосой во всю ширину у
   самой кромки экрана. Замер на 390×844: лист 390 шириной против 374 у
   карточки, скругление только сверху, и нижний край на 772 при верхе
   карточки 718 — то есть он НАКРЫВАЛ плеер на 54px.

   Причина — `bottom: var(--mini-player-shown-h, 64px)` в базовом файле:
   переменная описывает высоту ПОЛОСЫ, а под карточкой занято совсем другое
   расстояние (панель вкладок + зазор + карточка + зазор).

   ⚠️ Ширина, скругление, фон и рамка взяты у карточки НАМЕРЕННО. Две
   плавающие поверхности рядом, где одна стеклянная во всю ширину, а вторая
   плотная с полями, читаются как куски двух разных приложений.            */
@media (max-width: 899px) {
  body.v2-active .mini-queue-sheet {
    left: var(--v2-mini-side);
    right: var(--v2-mini-side);
    width: auto;
    max-width: none;
    bottom: calc(var(--v2-mini-bottom) + var(--v2-mini-h) + var(--v2-mini-gap));
    border-radius: 16px;
    border: 1px solid var(--ln);
    background: var(--s2);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    box-shadow: 0 10px 26px -14px rgba(0, 0, 0, .6);
    /* ⚠️ Потолок считается от занятого низа, а не от голых 70vh: иначе на
       коротком экране верх листа уезжает под шапку. 16px — поле сверху. */
    max-height: min(70vh, calc(100dvh - var(--v2-mini-bottom) - var(--v2-mini-h) - var(--v2-mini-gap) - 16px));
  }
  /* Уезжает вниз ровно за свою карточку, а не за край экрана. */
  body.v2-active .mini-queue-sheet.is-hidden {
    transform: translateY(calc(100% + var(--v2-mini-h) + var(--v2-mini-gap) * 2));
  }
}
/* Строки списка, чипы фильтров, скелет и пустые состояния. */

.v2-view { display: flex; flex-direction: column; min-height: 100%; }

.v2-head { flex: 0 0 auto; padding: 14px 14px 0; }
.v2-head-t { font-size: 20px; font-weight: 700; letter-spacing: -.02em; margin-bottom: 8px; }

.v2-chips { display: flex; gap: 7px; overflow-x: auto; padding-bottom: 10px; }
.v2-chips::-webkit-scrollbar { display: none; }
.v2-chip {
  flex: 0 0 auto; border: 0; border-radius: 999px; padding: 6px 14px;
  background: var(--s2); color: var(--dim); font: inherit; font-size: 13.5px;
  font-weight: 500; white-space: nowrap; cursor: pointer;
}
.v2-chip[aria-pressed="true"] { background: var(--br); color: var(--onbr); font-weight: 600; }

.v2-rows { padding: 0 8px 8px; }

/* ⚠️ СТРОКА — ФЛЕКС, А НЕ СЕТКА ИЗ ДВУХ РЯДОВ. Сетка держала второй ряд
   всегда, и у строк без подписи (вся личка и группы) имя висело в верхней
   половине: список выглядел съехавшим. Текст теперь одна стопка, она
   центрируется сама и при одной строке, и при двух. Ровную высоту даёт
   `min-height` на самой строке, а не пустая строка внутри неё. */
.v2-row {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 66px;
  width: 100%;
  padding: 10px;
  border: 0;
  border-radius: 16px;
  background: transparent;
  color: var(--tx);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.v2-row:hover { background: var(--s2); }

/* Бейдж непрочитанного В СТРОКЕ списка.

   ⚠️ `.v2-nb` РАССЧИТАН НА ПЛИТКУ РЕЛЬСА: он `position: absolute` и цепляется
   за угол значка. У `.v2-row` своего `position` нет, поэтому в списке он
   отсчитывался от ЧУЖОГО предка и уезжал в правый верхний угол — выглядело
   опечаткой вёрстки, а не решением.

   ⚠️ Здесь он обычный элемент флекса: `margin-left: auto` прижимает вправо,
   а `align-items: center` у самой строки центрирует по высоте. Обводку
   снимаем — под ним фон строки, а не значок, и кольцо ни от чего не отделяет. */
.v2-row > .v2-nb {
  position: static;
  transform: none;
  margin-left: auto;
  box-shadow: none;
  flex: none;
}

.v2-av {
  flex: 0 0 auto;
  width: 50px; height: 50px; border-radius: 16px;
  display: grid; place-items: center;
  color: #fff; font-weight: 600; font-size: 18px; line-height: 1;
  position: relative;
}
/* Кружок точки входа «Новая группа»: тот же размер, что у аватарки беседы,
   иначе строка выбивается по высоте из списка на первом же экране. Заливка
   приглушённая — это действие, а не собеседник, и спорить яркостью с живыми
   беседами ему не за чем. */
.v2-av-new {
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  color: var(--accent);
}
/* ⚠️ Подпись НЕ перекрашиваем: `.v2-row-p` уже приглушена, а `--muted` в
   темах v2 НЕ СУЩЕСТВУЕТ (есть `--dim` и `--text-muted`). Выдуманный токен
   молча даёт наследование цвета — та самая ошибка, что уже стоила редактору
   вложений сорока восьми синих заглушек. */
.v2-av-new svg { width: 22px; height: 22px; }

.v2-av-on {
  position: absolute; right: -2px; bottom: -2px;
  width: 13px; height: 13px; border-radius: 50%;
  background: var(--live); border: 2.5px solid var(--bg);
}

/* Стопка текста: имя и, если есть, подпись под ним. */
.v2-row-tx {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; gap: 2px; align-items: flex-start;
}
.v2-row-n {
  max-width: 100%;
  font-size: 15.5px; font-weight: 600; letter-spacing: -.01em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.v2-row-sp { font-style: normal; color: var(--ft); font-weight: 500; }
.v2-row-p {
  max-width: 100%;
  font-size: 14px; color: var(--dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Правый край: время сверху, бейдж под ним. Пустых узлов тут не бывает —
   их не создаёт разметка. */
.v2-row-side {
  flex: 0 0 auto; display: flex; flex-direction: column;
  align-items: flex-end; justify-content: center; gap: 5px;
}
.v2-row-t { font-size: 12.5px; color: var(--ft); white-space: nowrap; }
/* ⚠️ ЗНАЧОК РЕАКЦИЙ ОТЛИЧАЕТСЯ ОТ СЧЁТЧИКА НЕПРОЧИТАННОГО И ФОРМОЙ, И ЦВЕТОМ.
   Одинаковая пилюля рядом с числом сообщений читалась бы как второе число того
   же рода, а это разные сигналы: одно гаснет при чтении, другое — только по
   нажатию на кнопку перехода в самой беседе. */
.v2-row-react {
  display: inline-flex; align-items: center; gap: 3px;
  height: 20px; padding: 0 7px; border-radius: 10px;
  background: color-mix(in srgb, var(--danger) 16%, transparent);
  color: var(--danger);
  font-size: 11.5px; font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.v2-row-react svg { flex: none; }

.v2-row-b {
  min-width: 22px; height: 22px; padding: 0 7px; border-radius: 11px;
  background: var(--br); color: var(--onbr);
  font-size: 12px; font-weight: 700; display: grid; place-items: center;
}

/* Скелет: строки той же высоты, что настоящие — список не прыгает. */
.v2-row-skeleton {
  height: 70px; margin: 0 10px 4px; border-radius: 16px;
  background: linear-gradient(90deg, var(--s2) 25%, var(--s3) 37%, var(--s2) 63%);
  background-size: 400% 100%;
  animation: v2-shimmer 1.4s linear infinite;
}
@keyframes v2-shimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }
@media (prefers-reduced-motion: reduce) { .v2-row-skeleton { animation: none; } }

.v2-offline {
  margin: 14px 10px; padding: 12px 14px; border-radius: 14px;
  background: var(--s2); border: 1px solid var(--ln);
  color: var(--dim); font-size: 14px; text-align: center;
}

.v2-empty-act {
  margin-top: 6px; border: 0; border-radius: 999px; padding: 9px 18px;
  background: var(--br); color: var(--onbr); font: inherit;
  font-size: 14px; font-weight: 600; cursor: pointer;
}

/* ── серверы ────────────────────────────────────────────────────────────── */
.v2-rail-sp {
  width: 52px; height: 52px; border: 0; background: transparent;
  display: grid; place-items: center; cursor: pointer; flex: 0 0 auto; padding: 0;
}
.v2-sp-av {
  width: 44px; height: 44px; border-radius: 15px;
  display: grid; place-items: center;
  color: #fff; font-weight: 600; font-size: 16px; line-height: 1;
}
.v2-rail-sp[aria-pressed="true"] .v2-sp-av {
  box-shadow: 0 0 0 3px var(--s2), 0 0 0 5px var(--br);
}

.v2-spcard { margin: 9px 10px; border: 1px solid var(--ln); border-radius: 18px; overflow: hidden; background: var(--bg); }
.v2-spcard-h {
  display: flex; align-items: center; gap: 13px; padding: 13px 14px; width: 100%;
  border: 0; background: transparent; color: var(--tx); font: inherit; text-align: left; cursor: pointer;
}
.v2-spcard-h .v2-sp-av { width: 52px; height: 52px; border-radius: 17px; font-size: 19px; }
.v2-spcard-tx { min-width: 0; flex: 1; }
.v2-spcard-n { font-size: 16.5px; font-weight: 600; letter-spacing: -.015em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.v2-spcard-s { font-size: 13px; color: var(--ft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.v2-spcard-ar { color: var(--ft); font-size: 17px; flex: 0 0 auto; }

/* ── дом сервера и публичная страница ───────────────────────────────────── */
.v2-space, .v2-page { display: flex; flex-direction: column; min-height: 100%; }

.v2-cover { position: relative; height: 150px; flex: 0 0 auto; }
.v2-cover-bk {
  position: absolute; left: 10px; top: 10px;
  width: 36px; height: 36px; border: 0; border-radius: 12px;
  background: rgba(0,0,0,.4); color: #fff; font-size: 22px; cursor: pointer;
  display: grid; place-items: center; line-height: 1;
}
.v2-cover-t { position: absolute; left: 16px; right: 16px; bottom: 12px; color: #fff; }
.v2-cover-n { font-size: 24px; font-weight: 700; letter-spacing: -.025em; text-shadow: 0 2px 14px rgba(0,0,0,.55); }
.v2-cover-s { font-size: 13.5px; opacity: .92; text-shadow: 0 1px 8px rgba(0,0,0,.5); }

.v2-actions { display: flex; gap: 9px; padding: 13px 14px 4px; flex: 0 0 auto; }
.v2-act-pri, .v2-act-sec {
  flex: 1; border: 0; border-radius: 14px; padding: 11px 16px;
  font: inherit; font-size: 15px; font-weight: 600; text-align: center; cursor: pointer;
}
.v2-act-pri { background: var(--br); color: var(--onbr); }
.v2-act-sec { background: var(--s2); border: 1px solid var(--ln); color: var(--tx); }

.v2-about { padding: 12px 16px 4px; font-size: 14.5px; color: var(--dim); line-height: 1.5; }

.v2-cat {
  padding: 16px 16px 6px; font-size: 11.5px; letter-spacing: .09em;
  text-transform: uppercase; color: var(--ft); font-weight: 600;
}
.v2-chn {
  display: flex; align-items: center; gap: 11px;
  width: calc(100% - 16px); margin: 0 8px; padding: 10px 14px;
  border: 0; border-radius: 13px; background: transparent;
  color: var(--tx); font: inherit; font-size: 15.5px; text-align: left; cursor: pointer;
}
.v2-chn:hover { background: var(--s2); }
.v2-chn-h { color: var(--ft); font-size: 16px; width: 16px; text-align: center; flex: 0 0 auto; }

.v2-members { padding: 0 8px 14px; }
.v2-mem { display: flex; align-items: center; gap: 11px; padding: 7px 8px; border-radius: 11px; }
.v2-mem:hover { background: var(--s2); }
.v2-mem-av {
  width: 34px; height: 34px; border-radius: 12px; display: grid; place-items: center;
  color: #fff; font-weight: 600; font-size: 13px; flex: 0 0 auto;
}
.v2-mem-n { font-size: 14.5px; }

/* Записи страницы — обычная переписка, поэтому поверхность та же. */
.v2-page .v2-chat { min-height: 420px; }

/* Дом и страница на десктопе прокручиваются внутри главной колонки. */
.v2-main > .v2-space, .v2-main > .v2-page { overflow-y: auto; }
@media (prefers-reduced-motion: reduce) { }

/* ── профиль ────────────────────────────────────────────────────────────── */
/* ⚠️ ВЕРХНИЙ ОТСТУП 18, А НЕ 6. Шести хватало, пока над карточкой стояла
   шапка со словом «Я»; сняв её, я получил аватарку вплотную к верхней кромке
   окна — замер дал ровно 0. На телефоне там ещё и системная строка. */
.v2-me-top { display: flex; align-items: center; gap: 15px; padding: 18px 16px; }
.v2-me-av {
  width: 66px; height: 66px; border-radius: 22px; display: grid; place-items: center;
  color: #fff; font-weight: 600; font-size: 24px; flex: 0 0 auto;
}
.v2-me-n { font-size: 20px; font-weight: 700; letter-spacing: -.02em; }
.v2-me-s { font-size: 14px; color: var(--ft); }

/* ⚠️ СОДЕРЖИМОЕ ЭКРАНА ОГРАНИЧЕНО ПО ШИРИНЕ И ПРИЖАТО ВЛЕВО. На десктопе
   строка «Профиль › » растягивалась на 1300px: значок у левого края, стрелка у
   правого, между ними полтора метра пустоты — глазу нечем связать одно с
   другим. Ровно эта же болезнь была у панели редактора вложений (`--ed-max`),
   и лечится так же. Влево, а не по центру: слева рельса, и центрированный
   блок уехал бы от неё, оставив полосу пустоты у самой навигации. */
.v2-me { --me-max: 620px; }
.v2-me > .v2-me-top,
.v2-me > .v2-pgrp,
.v2-me > .v2-me-ver { max-width: var(--me-max); }

/* ── чип статуса под ником ──────────────────────────────────────────────── */
/* ⚠️ ЦВЕТА ИЗ СУЩЕСТВУЮЩИХ ТОКЕНОВ. Своих `--success` в этой оболочке НЕТ —
   есть `--live` (объявлен во всех девяти темах, проверено). Выдумав имя, я бы
   получил сорок восемь падений в запасной цвет, как уже было в редакторе
   вложений: заметно только глазом, ни один зонд этого не ловит. */
.v2-me-st { position: relative; margin-top: 5px; }
.v2-me-st-b {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 3px 9px 3px 8px; border: 1px solid var(--ln); border-radius: 999px;
  background: transparent; color: var(--dim); font: inherit; font-size: 13px;
  cursor: pointer;
}
.v2-me-st-b:hover { background: var(--s2); color: var(--tx); }
.v2-me-st-c { font-size: 10px; color: var(--ft); }
.v2-me-st-d {
  width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto;
  background: var(--ft);
}
/* Цвет точки выводится из значения, а не задаётся в JS: правило одно на кнопку
   и на пункты меню, потому и селектор общий. */
[data-status="online"] > .v2-me-st-d { background: var(--live); }
[data-status="idle"]   > .v2-me-st-d { background: var(--warn); }
[data-status="dnd"]    > .v2-me-st-d { background: var(--danger); }
/* «Невидимка» — кольцо, а не заливка: она отличается от «не в сети» намерением,
   и полая точка это и говорит. Заливка серым читалась бы как «оффлайн». */
[data-status="offline"] > .v2-me-st-d {
  background: transparent; box-shadow: inset 0 0 0 2px var(--ft);
}

.v2-me-st-m {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 30;
  min-width: 190px; padding: 5px;
  border: 1px solid var(--ln); border-radius: 14px;
  background: var(--bg); box-shadow: var(--sh);
}
.v2-me-st-i {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 8px 10px; border: 0; border-radius: 10px;
  background: transparent; color: var(--tx); font: inherit; font-size: 14px;
  text-align: left; cursor: pointer;
}
.v2-me-st-i:hover { background: var(--s2); }
.v2-me-st-i .v2-me-st-n { flex: 1; }
.v2-me-st-v { color: var(--br); font-size: 13px; }

/* ── версия внизу экрана «Я» ────────────────────────────────────────────── */
/* ⚠️ ПРЯЧЕТСЯ НА ДЕСКТОПЕ, А НЕ НЕ СТРОИТСЯ. Там она стоит внизу рельсы, и
   вторая копия на том же экране читалась бы как ошибка. Решать это шириной
   окна в момент сборки нельзя: экран не пересобирается при повороте планшета,
   и после поворота осталось бы либо пусто, либо два раза. */
.v2-me-ver { padding: 4px 0 18px; text-align: center; }
@media (min-width: 900px) { .v2-me-ver { display: none; } }

.v2-pgrp { margin: 0 12px 14px; border: 1px solid var(--ln); border-radius: 16px; overflow: hidden; }
.v2-pit {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: 13px 15px; border: 0; border-bottom: 1px solid var(--ln);
  background: transparent; color: var(--tx); font: inherit; font-size: 15.5px;
  text-align: left; cursor: pointer;
}
.v2-pit:last-child { border-bottom: 0; }
.v2-pit:hover { background: var(--s2); }
/* ⚠️ ОТКРЫТЫЙ ПУНКТ ПОДСВЕЧЕН ЗАЛИВКОЙ И АКЦЕНТНЫМ ЗНАЧКОМ. Только цветом
   текста было бы мало: три строки в узкой колонке читаются как одно пятно, и
   разницу в оттенке подписи глаз не ловит. */
/* ── Выход: отдельная группа, читаемая как ДЕЙСТВИЕ, а не переход ─────────
   ⚠️ КРАСИМ И ЗНАЧОК, И ПОДПИСЬ. Одной подписи мало: значок наследует
   `--dim` от `.v2-pit-i`, и строка вышла бы двухцветной — серый значок рядом с
   красным словом читается как сбой отрисовки, а не как предупреждение.
   ⚠️ `--danger`, А НЕ ВЫДУМАННОЕ ИМЯ. Несуществующая переменная молча даёт
   запасной цвет: в этой оболочке так уже вышло с `--v2-accent`, и сорок восемь
   мест разом стали синими. Список настоящих имён — `css/tokens.css`. */
.v2-pgrp-danger .v2-pit,
.v2-pgrp-danger .v2-pit-i { color: var(--danger); }
.v2-pgrp-danger .v2-pit:hover { background: color-mix(in srgb, var(--danger) 12%, transparent); }

.v2-pit.is-open { background: var(--s2); }
.v2-pit.is-open .v2-pit-i { color: var(--br); }
.v2-pit.is-open .v2-pit-n { font-weight: 600; }
/* ⚠️ ЗНАЧОК ЦЕНТРИРУЕТСЯ СЕТКОЙ, А НЕ `text-align`. Был инлайновым внутри
   строки с `font-size: 17px`, то есть сидел на БАЗОВОЙ ЛИНИИ текста: под ним
   оставалось место под нижние выносные элементы, и оптический центр значка
   уезжал ВЫШЕ центра подписи. Замер дал одинаковые 3.6-4.0px на всех четырёх
   строках — то самое «текст и значки не на одной оси». По горизонтали при этом
   всё сходилось идеально (144px у всех), поэтому глазами это читалось как
   съехавший текст, а не как съехавший значок. */
.v2-pit-i {
  width: 28px; flex: 0 0 auto;
  display: grid; place-items: center; line-height: 0;
  color: var(--dim);
}
.v2-pit-n { flex: 1; }
.v2-pit-r { color: var(--ft); }

/* Строка канала в доме сервера: сам канал плюс звезда закрепления. Звезда
   гасится до наведения — она нужна редко, а мозолит глаза всегда. */
.v2-chn-row { display: flex; align-items: center; gap: 2px; }
.v2-chn-row > .v2-chn { flex: 1; min-width: 0; }
.v2-chn-pin {
  flex: 0 0 auto; border: 0; background: transparent; cursor: pointer;
  color: var(--ft); font-size: 15px; line-height: 1;
  padding: 8px 12px; border-radius: 10px; opacity: 0;
  transition: opacity .12s ease, color .12s ease;
}
.v2-chn-row:hover .v2-chn-pin, .v2-chn-pin:focus-visible { opacity: 1; }
.v2-chn-pin.is-on { opacity: 1; color: var(--ac); }

/* ── играющий трек в медиатеке ──────────────────────────────────────────── */
.v2-tr.is-playing .v2-tr-n { color: var(--ac); }
.v2-tr.is-playing .v2-eq { display: flex; }
/* На паузе полоски замирают — иначе «играет» и «на паузе» неотличимы. */
.v2-tr.is-paused .v2-eq i { animation-play-state: paused; }
@keyframes v2eq { 0%, 100% { transform: scaleY(.45); } 50% { transform: scaleY(1); } }
@media (prefers-reduced-motion: reduce) { }

/* Картинка внутри любого кружка-аватара: одинаково для строки, рельса и «Я».
   ⚠️ `.v2-sp-av` — АВАТАР СООБЩЕСТВА — в этом списке отсутствовал, хотя
   комментарий над ним обещал «одинаково для рельса». Своих размеров кружок
   задаёт, а вот картинке внутри не задавал ничего: значок сообщества
   отрисовывался в НАТУРАЛЬНУЮ величину и вываливался из рельсы вертикальной
   полосой на пол-экрана. Кружок задан в двух местах (рельса 44px, карточка
   52px), а правило вложенной картинки — в одном; поэтому дописан класс, а не
   заведено второе правило. */
.v2-av img, .v2-me-av img, .v2-rail-me-av img, .v2-mem-av img, .v2-sp-av img {
  width: 100%; height: 100%; object-fit: cover; display: block; border-radius: inherit;
}
.v2-av, .v2-me-av, .v2-mem-av, .v2-sp-av { overflow: hidden; }

/* «Найти сообщество» — действие в том же списке, но не сообщество: пунктирный
   кружок вместо аватара разводит их формой, а не только текстом. */
.v2-av-add {
  background: transparent; border: 1px dashed var(--ln);
  color: var(--dim); font-size: 20px; font-weight: 400;
}
.v2-row-add .v2-row-n { color: var(--dim); font-weight: 500; }
.v2-row-add:hover .v2-av-add { border-color: var(--br); color: var(--br); }
.v2-row-add:hover .v2-row-n { color: var(--tx); }

/* ── шапка внутри сообщества ────────────────────────────────────────────── */
/* Стрелка слева, содержимое справа — как в мобильных шапках. */
.v2-head-nav { display: flex; align-items: center; gap: 4px; }
.v2-head-stack { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
/* ⚠️ «Назад» — только стрелка. Подпись рядом с ней («Сообщества») занимала
   отдельную строку над именем ради слова, которое и так понятно из стрелки, и
   отодвигала имя от края. Куда именно вернёт — в подсказке и в aria-label:
   для мыши и для чтения с экрана этого достаточно, для глаза лишнее. */
.v2-back {
  flex: 0 0 auto; width: 34px; height: 34px; margin-left: -6px;
  display: grid; place-items: center;
  border: 0; border-radius: 11px; background: transparent;
  color: var(--dim); font: inherit; font-size: 21px; line-height: 1;
  cursor: pointer;
}
.v2-back:hover { background: var(--s2); color: var(--tx); }
/* Имя сообщества — кнопка. Шеврон после него единственный признак того, что за
   ним что-то есть; без него нажимаемый заголовок ничем не отличается от
   обычного. */
.v2-head-t-btn {
  display: inline-flex; align-items: center; gap: 6px;
  margin: 0 0 0 -8px; padding: 2px 8px; border: 0; border-radius: 10px;
  background: transparent; color: inherit; font: inherit; cursor: pointer;
  max-width: calc(100% + 16px);
}
.v2-head-t-btn:hover { background: var(--s2); }
.v2-head-t-n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.v2-head-t-i { flex: 0 0 auto; color: var(--ft); font-size: 17px; font-weight: 400; }
.v2-head-t-btn:hover .v2-head-t-i { color: var(--br); }
.v2-head-sub { padding-left: 0; font-size: 13px; color: var(--dim); }

/* ── встроенный экран старой оболочки ───────────────────────────────────── */
/* ⚠️ СВОЕЙ ВЁРСТКИ У ОБЁРТКИ ПОЧТИ НЕТ, и это намеренно: внутри чужой экран со
   своими отступами и своей сеткой. Наши правила поверх него разъехались бы с
   его собственными при первой же правке той стороны. Задаём только то, без чего
   он не может жить в колонке: прокрутку и высоту. */
/* ⚠️ ОТСТУП ЗАДАЁТ ОБЁРТКА, ПОТОМУ ЧТО ЧУЖОЙ ЭКРАН ЕГО НЕ ИМЕЕТ. Редактор
   профиля жил внутри `.settings-content` с `padding: 24px 32px`, и своих полей
   у него нет вовсе: замер дал заголовок «Profile» на `top: 0`, вплотную к
   кромке окна. Числа взяты те же — не ради единообразия ради, а чтобы форма
   выглядела одинаково в обоих местах, где она теперь показывается.
   ⚠️ `scrollbar-gutter` оттуда же: без него содержимое дёргается вбок в
   момент появления полосы прокрутки. */
.v2-embed {
  height: 100%; overflow-y: auto;
  padding: 24px 32px; scrollbar-gutter: stable;
}
/* ⚠️ У ДРУЗЕЙ СВОИ ОТСТУПЫ — у них полоса вкладок во всю ширину, и внешнее поле
   отрывало бы её от краёв, чего в старой оболочке не происходит. */
.v2-embed-friends { padding: 0; }
.v2-embed-fail { padding: 24px 16px; color: var(--dim); font-size: 14px; }

/* ── пункт раздела «Я» во весь экран (телефон) ──────────────────────────── */
/* ⚠️ ТЕЛО ОТДАЁТ ВЫСОТУ, СКРОЛЛИТ ВСТРОЕННЫЙ ЭКРАН. Обёртка — колонка на всю
   высоту: шапка сверху фиксирована, остаток забирает содержимое. Без этого
   прокрутки оказалось бы две — у тела и у встроенного экрана внутри, — и
   страница уезжала бы целиком вместе с шапкой. */
.v2-me-full { height: 100%; display: flex; flex-direction: column; min-height: 0; }
.v2-me-full-h {
  flex: 0 0 auto; padding: 10px 12px;
  border-bottom: 1px solid var(--ln); background: var(--bg);
}
.v2-me-full-t { margin: 0; }
.v2-me-full > .v2-embed,
.v2-me-full > .v2-view { flex: 1 1 auto; min-height: 0; }

/* ── «Сохранённое» (заготовка) ──────────────────────────────────────────── */
.v2-bm { height: 100%; overflow-y: auto; padding: 18px 16px 24px; }
/* ⚠️ ШИРИНА ОГРАНИЧЕНА ТЕМ ЖЕ ЧИСЛОМ, что у меню «Я»: на широком мониторе
   сетка из шести карточек разъезжалась бы на весь экран и читалась как витрина,
   а это личная коллекция. */
.v2-bm > * { max-width: 620px; }

.v2-bm-tabs { display: flex; gap: 6px; margin-bottom: 14px; flex-wrap: wrap; }
/* ⚠️ НЕАКТИВНЫЕ, НО ЧИТАЕМЫЕ. Полупрозрачность до неразличимости выглядела бы
   как ошибка отрисовки; здесь нужно «пока нельзя», а не «сломано». */
.v2-bm-tab {
  padding: 6px 13px; border: 1px solid var(--ln); border-radius: 999px;
  background: transparent; color: var(--ft); font: inherit; font-size: 13.5px;
  cursor: default;
}
.v2-bm-tab.is-on { background: var(--s2); color: var(--dim); }

.v2-bm-soon {
  display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap;
  margin-bottom: 16px;
}
.v2-bm-soon-b {
  flex: 0 0 auto; padding: 2px 9px; border-radius: 999px;
  background: var(--br); color: var(--onbr);
  font-size: 11.5px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase;
}
.v2-bm-soon-t { font-size: 14px; color: var(--dim); line-height: 1.45; }

.v2-bm-grid {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
}
/* ⚠️ ЗАГОТОВКИ НЕ МЕРЦАЮТ. Пульсация означает «грузится сейчас» — а здесь
   грузить нечего и не будет; движение обещало бы, что вот-вот появится
   содержимое. И `prefers-reduced-motion` тогда пришлось бы уважать отдельно. */
.v2-bm-card {
  border: 1px solid var(--ln); border-radius: 14px; padding: 10px;
  background: var(--s2);
}
.v2-bm-card-th { height: 84px; border-radius: 9px; background: var(--s3); }
.v2-bm-card-l1, .v2-bm-card-l2 {
  height: 9px; border-radius: 5px; background: var(--s3); margin-top: 9px;
}
.v2-bm-card-l2 { width: 58%; }

/* ── список разделов настроек ───────────────────────────────────────────── */
/* Шапка та же, что у пункта «Я» во весь экран: стрелка слева, заголовок рядом.
   Своих чисел не заводим — расхождение двух шапок заметно сразу. */
.v2-set-head { padding: 14px 12px 10px; }
.v2-set-head-t { margin: 0; }
/* ⚠️ Список настроек — ЕДИНСТВЕННОЕ содержимое колонки, поэтому прокрутка на
   нём самом: у раздела «Я» сверху ещё карточка, и там скроллит вид целиком. */
.v2-set-list { height: 100%; overflow-y: auto; display: flex; flex-direction: column; }
.v2-set-list > .v2-pgrp { flex: 0 0 auto; }

/* Аватарка сообщества в шапке. ⚠️ `overflow: hidden` обязателен: без него
   картинка вылезет квадратом из скруглённого кружка — та же болезнь, что была у
   значка сообщества в рельсе. */
.v2-head-av {
  flex: 0 0 auto; margin-right: 9px;
  width: 30px; height: 30px; border-radius: 10px; overflow: hidden;
  display: grid; place-items: center;
  color: #fff; font-size: 13px; font-weight: 600; line-height: 1;
}
.v2-head-av img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ── плашка «Скоро» у пустого раздела ───────────────────────────────────── */
/* Та же, что у «Сохранённого»: раздел в работе выглядит одинаково везде. */
.v2-soon { margin-bottom: 10px; }
.v2-soon-b {
  display: inline-block; padding: 2px 9px; border-radius: 999px;
  background: var(--br); color: var(--onbr);
  font-size: 11.5px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase;
}

/* ── разделы настроек в колонке ─────────────────────────────────────────── */
/* ⚠️ ОДНА ШИРИНА НА ВСЕ ШЕСТЬ РАЗДЕЛОВ. Замер: четыре идут по 600px (у
   `.role-editor` есть свой предел), а «Приватность» и «Хранилище» — по 946,
   потому что у `.settings-section` и `.emotes-settings-block` предела нет.
   Переключаешь разделы — и содержимое прыгает по ширине на треть экрана.
   600 взято не с потолка: это предел `.role-editor`, то есть ширина
   большинства.

   ⚠️ Правило живёт в стилях НОВОЙ оболочки и действует только внутри её
   колонки. В оверлее старой ширину задаёт `.settings-content`, и трогать её
   отсюда нельзя. */
.v2-embed-settings > * { max-width: 600px; }

/* ── канал внутри сервера: строка, а не карточка ────────────────────────── */
/* ⚠️ БЕЗ ПЛИТКИ-АВАТАРКИ. У канала своей картинки нет; плитка рисовала решётку
   на цветном фоне, выведенном из имени, — украшение, притворяющееся данными. И
   растягивала строку вдвое: на экран влезало вчетверо меньше каналов, чем в
   старой оболочке, где там просто `# имя`. */
/* ⚠️ `min-height` ПЕРЕБИВАЕТ ПАДДИНГИ, и это выяснилось замером: строка
   осталась 66px при `padding: 7px`. У `.v2-row` минимум задан под КАРТОЧКУ с
   плиткой 50px; у канала плитки нет, и минимум надо снимать явно — иначе на
   экран влезает вдвое меньше каналов, чем в старой оболочке, ради пустоты. */
.v2-row-chn { padding-top: 6px; padding-bottom: 6px; gap: 8px; min-height: 0; }
.v2-row-h {
  flex: 0 0 auto; color: var(--ft); font-weight: 600; font-size: 15.5px;
  line-height: 1;
}
/* ⚠️ ОДНА СТРОКА, А НЕ СТОПКА: второй строки у канала внутри сервера нет
   намеренно, и `flex-direction: column` оставлял бы под неё место. */
.v2-row-chn .v2-row-tx { flex-direction: row; align-items: baseline; gap: 6px; }
.v2-row-chn[aria-pressed="true"] .v2-row-h,
.v2-row-chn.is-open .v2-row-h { color: var(--br); }

/* ── сворачиваемая категория ────────────────────────────────────────────── */
/* ⚠️ ЗАГОЛОВОК СТАЛ КНОПКОЙ. Сворачивание было в старой оболочке и пропало при
   переносе; у сервера с четырьмя категориями без него список не уместить. */
.v2-cat-b {
  display: flex; align-items: center; gap: 5px; width: 100%;
  border: 0; background: transparent; cursor: pointer;
  font: inherit; text-align: left;
  color: var(--ft); font-size: 11.5px; letter-spacing: .09em;
  text-transform: uppercase; font-weight: 600;
}
.v2-cat-b:hover { color: var(--dim); }
/* Стрелка не крутится анимацией, а подменяется символом: поворот `transform`
   на глифе даёт разную оптическую высоту у ▸ и ▾, и заголовок дёргается. */
.v2-cat-a { flex: 0 0 auto; font-size: 9px; line-height: 1; }
.v2-cat-n { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── медиатека: чужой экран целиком ─────────────────────────────────────── */
/* ⚠️ БЕЗ ВНЕШНИХ ОТСТУПОВ, в отличие от настроек. У библиотеки своя шапка во всю
   ширину, свои полосы и сетки с собственными полями; наш `padding: 24px 32px`
   отрывал бы их от краёв и ломал её же раскладку. */
.v2-embed-library { padding: 0; }

/* ── Скрытые сообщества рельса ───────────────────────────────────────────
   Кнопка со счётчиком и всплывающий список возврата.

   ⚠️ Возврат живёт ЗДЕСЬ, а не в настройках: место, где вещь исчезла, и
   место, где её достают, должны совпадать. Иначе «убрать из рельса» —
   ловушка без выхода. */
.v2-rail-hidden {
    display: grid;
    place-items: center;
}
.v2-rail-hidden-n {
    display: grid;
    place-items: center;
    width: 48px;
    height: 48px;
    border-radius: 16px;
    background: var(--s3);
    color: var(--dim);
    font-size: 13px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}
.v2-rail-hidden:hover .v2-rail-hidden-n { background: var(--s2); color: var(--tx); }

.v2-rail-hidden-menu {
    position: fixed;
    z-index: 400;
    min-width: 180px;
    max-height: 60vh;
    overflow-y: auto;
    padding: 6px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    background: var(--s2);
    border: 1px solid var(--ln);
    border-radius: 12px;
    box-shadow: 0 12px 32px var(--sh);
}
.v2-rail-hidden-item {
    text-align: left;
    padding: 9px 11px;
    border-radius: 8px;
    color: var(--tx);
    font-size: 14px;
    background: none;
    border: 0;
    cursor: pointer;
}
.v2-rail-hidden-item:hover { background: var(--s3); }

/* Перетаскивание в рельсе: призрак и место вставки. Классы ставит
   `makeDraggable`, здесь только вид. */
.v2-rail-sp.dragging { opacity: .35; }
.v2-rail-sp.drag-charging .v2-sp-av { transform: scale(.92); transition: transform .18s; }

/* Меню строки беседы: закрепить / открепить. Позиционируется по курсору
   или по точке касания, поэтому `fixed` и координаты из JS. */
.v2-row-menu {
    position: fixed;
    z-index: 400;
    min-width: 168px;
    padding: 6px;
    background: var(--s2);
    border: 1px solid var(--ln);
    border-radius: 12px;
    box-shadow: 0 12px 32px var(--sh);
}
.v2-row-menu-item {
    display: block;
    width: 100%;
    text-align: left;
    padding: 10px 12px;
    border: 0;
    border-radius: 8px;
    background: none;
    color: var(--tx);
    font-size: 14px;
    cursor: pointer;
}
.v2-row-menu-item:hover { background: var(--s3); }

/* Меню элемента рельса. Позиция от курсора, поэтому fixed и координаты из JS. */
.v2-rail-menu {
    position: fixed;
    z-index: 400;
    min-width: 196px;
    padding: 6px;
    background: var(--s2);
    border: 1px solid var(--ln);
    border-radius: 12px;
    box-shadow: 0 12px 32px var(--sh);
}
.v2-rail-menu-item {
    display: block;
    width: 100%;
    text-align: left;
    padding: 10px 12px;
    border: 0;
    border-radius: 8px;
    background: none;
    color: var(--tx);
    font-size: 14px;
    cursor: pointer;
}
.v2-rail-menu-item:hover { background: var(--s3); }
/* Убирающее действие отделено цветом — оно единственное здесь что-то прячет. */
.v2-rail-menu-item.is-danger { color: var(--danger); }
.v2-rail-menu-item.is-danger:hover { background: color-mix(in srgb, var(--danger) 14%, transparent); }

/* Вложенное подменю меню сообщества: разворачивается ВНУТРИ, а не рядом.
   ⚠️ Не второй плавающий слой — у стороннего пикера своя подложка на весь
   экран, и два таких слоя перехватывают клики друг у друга. */
/* ⚠️ КАСКАД ВПРАВО, а не список внутри. Вложенный вариант растягивал меню на
   две трети высоты экрана при том, что сбоку места вдоволь. Отсчёт идёт от
   САМОЙ СТРОКИ (`position: relative` ниже) — от меню подменю уехало бы к его
   верхнему краю. Своей подложки у него нет: она была бы вторым слоем поверх
   первого и перехватывала бы клики, как это делал сторонний пикер. */
.v2-rail-menu-item { position: relative; }
.v2-rail-submenu {
    position: absolute;
    left: calc(100% + 6px);
    top: -6px;
    z-index: 1;
    min-width: 210px;
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: 6px;
    background: var(--s2);
    border: 1px solid var(--ln);
    border-radius: 12px;
    box-shadow: 0 12px 32px var(--sh);
    max-height: 340px;
    overflow-y: auto;
}
.v2-rail-submenu.is-left { left: auto; right: calc(100% + 6px); }
.v2-rail-submenu .v2-rail-menu-item { font-size: 13px; padding: 8px 10px; }
.v2-rail-submenu-sep { height: 1px; background: var(--ln); margin: 4px 8px; }

/* Булавка в правой колонке строки, под временем. Приглушена намеренно: это
   признак состояния, а не действие, и спорить с именем за внимание он не
   должен.
   ⚠️ Своя форма `pinned`, а не повёрнутый `pin`: тот нарисован «лёжа», и при
   14 пикселях с поворотом на 45° читался стрелкой. Замерено на живом списке. */
.v2-row-pin {
    display: grid;
    place-items: center;
    color: var(--ft);
    flex: none;
    margin-top: 2px;   /* поворот НЕ нужен: значок нарисован вертикально */
}
.v2-row-pin svg { display: block; }

/* Коробка закреплённых бесед. Существует ТОЛЬКО ради ограничения зоны
   перетаскивания: движок владеет контейнером, на который повешен, и пока им был
   весь список, закреплённую можно было утащить под последнюю беседу.

   ⚠️ НИКАКОЙ ВИДИМОЙ ГРАНИЦЫ У НЕЁ НЕТ. Сначала здесь стояла линия снизу — она
   резала список надвое и читалась как заголовок раздела, которого мы намеренно
   не заводили. Границу закреплённых обозначает булавка на строке, а не черта.
   ⚠️ И НЕ `display: contents`: движок меряет контейнер, а у такого элемента
   габариты нулевые. Обычный блок без единого украшения — в списке он
   невидим, для перетаскивания существует. */
.v2-pinned {
    display: flex;
    flex-direction: column;
}

/* ── Разделители между беседами ──────────────────────────────────────────
   ⚠️ ЕДВА ЗАМЕТНЫЕ И НЕ ДО КРАЯ. Список из одинаковых блоков без единой линии
   читается плоским — глазу не за что зацепиться при беглом просмотре. Но
   полноценная черта режет его на карточки; нужна ровно та подсказка, что
   строки разные, и не больше. Поэтому прозрачность, а не цвет: линия ложится
   на любой из семи фонов одинаково.

   ⚠️ Отступ слева равен ширине аватара с зазором — линия начинается от текста,
   как в мессенджерах, и не подчёркивает картинку. */
.v2-rows > .v2-row + .v2-row::before,
.v2-pinned > .v2-row + .v2-row::before,
.v2-pinned > .v2-row:first-child::before {
    content: "";
    position: absolute;
    left: 68px;
    right: 12px;
    top: 0;
    height: 1px;
    background: currentColor;
    /* ⚠️ ЦИФРА ПОДОБРАНА ЗАМЕРОМ, а не на глаз. При .07 разница яркости линии
       и фона выходила 15 единиц из 255 — линия честно видна, и список из-за
       неё дробился. У телеги эта разница около пяти: линия «как бы есть, но
       почти нет». .03 даёт 6-7 на тёмной теме и столько же на светлой, потому
       что берётся не фиксированный цвет, а `currentColor`: он сам меняет
       сторону вместе с темой. */
    opacity: .03;
    pointer-events: none;
}
.v2-row { position: relative; }
/* Редактор вложения: обрезка, поворот, отражение. */

.v2-ed {
    position: fixed;
    inset: 0;
    z-index: 4000;          /* выше листа медиа (3100) и театра */
    display: flex;
    flex-direction: column;
    background: var(--bg);
    outline: none;
}

/* Шапка редактора: держит верх на месте при переходе из просмотра. */
.v2-ed-head {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    background: var(--s2);
}
.v2-ed-ico {
    display: grid;
    place-items: center;
    width: 38px;
    height: 38px;
    color: var(--tx);
    background: transparent;
    border: 0;
    border-radius: 10px;
    cursor: pointer;
}
.v2-ed-ico:hover { background: var(--s3); }
.v2-ed-title { color: var(--tx); font-size: 14px; font-weight: 500; }

/* ── ОДНА КАРТОЧКА НА ПРОСМОТР И РЕДАКТОР ────────────────────────────────────
   ⚠️ Раньше просмотр на десктопе был карточкой, а редактор разворачивался во
   весь экран: переход прыгал по размеру, и без анимации это читалось как
   мерцание и резкая перерисовка чужого интерфейса. Геометрия должна совпадать
   с `.v2-vw` ДО ПИКСЕЛЯ — тогда на экране меняется только низ, ради чего всё и
   затевалось. Меняешь тут — меняй и там. */
@media (min-width: 900px) {
    .v2-ed {
        inset: 5vh 8vw;
        border: 1px solid var(--ln);
        border-radius: 16px;
        /* ⚠️ СВОЕЙ ТЕНИ ТОЖЕ НЕТ — по той же причине, что и подложки. Просмотр
           лежит ровно под редактором и рисует точно такую же; две одинаковые
           тени складывались и делали край вокруг окна темнее ровно на время
           правки. Рамку оставляем: непрозрачный фон редактора закрывает рамку
           просмотра, и без своей карточка потеряла бы контур. */
        overflow: hidden;
    }
    /* ⚠️ СВОЕЙ ПОДЛОЖКИ У РЕДАКТОРА НЕТ, И ЭТО ИСПРАВЛЕНИЕ. Она была — и
       складывалась с подложкой просмотра, который остаётся под ним: при входе
       в правку экран заметно темнел, а при выходе одна из двух исчезала
       мгновенно, отчего затемнение скачком светлело. Вадим описал это как
       «сильнее затемнение, а на выходе резко через мерцание».

       Затемняет ВСЕГДА просмотр, он под редактором и никуда не девается.
       Редактор открывается только из него — случая «редактор без просмотра»
       не существует. */
}

/* ⚠️ Появление БЫСТРОЕ и короткое: 130мс. Длиннее — и переход из просмотра в
   редактор начинает ощущаться как загрузка, а он должен ощущаться как смена
   панели. Масштаб еле заметный (0.985): его задача — дать глазу зацепку, что
   слой новый, а не показать полёт. */
@keyframes v2-edit-in {
    from { opacity: 0; transform: scale(.985); }
    to   { opacity: 1; transform: scale(1); }
}
/* ⚠️ МАСШТАБ — ТОЛЬКО У ПРОСМОТРА, И ЭТО НЕ ПРИДИРКА. Просмотр появляется НАД
   ПРИЛОЖЕНИЕМ: там «слегка вырос» — верная подсказка, что пришёл новый слой.
   Редактор появляется НАД ТОЧНО ТАКОЙ ЖЕ КАРТОЧКОЙ, совпадающей с ним до
   пикселя, и ровно тот же масштаб читается как «сверху выросла вторая копия».
   Вадим описал это дословно: «наслаивается сверху и расширяется из ничего».
   Одна анимация на два слоя была ошибкой: жест один, а смысл разный. */
.v2-vw { animation: v2-edit-in .13s ease-out; }
@keyframes v2-edit-fade {
    from { opacity: 0; }
    to   { opacity: 1; }
}
.v2-ed { animation: v2-edit-fade .13s ease-out; }

/* ⚠️ Кто просил не двигать — тому не двигаем. Не «уменьшить», а убрать: для
   вестибулярной чувствительности половина движения не помогает. */
@media (prefers-reduced-motion: reduce) {
    .v2-ed, .v2-vw { animation: none; }
}

.v2-ed-stage {
    position: relative;
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
    background:
        repeating-conic-gradient(#1a1d22 0% 25%, #14171b 0% 50%) 50% / 22px 22px;
}

.v2-ed-canvas { position: absolute; }

.v2-ed-frame {
    position: absolute;
    box-sizing: border-box;
    border: 1px solid rgba(255, 255, 255, .92);
    box-shadow: 0 0 0 100vmax rgba(0, 0, 0, .55);
    cursor: move;
    touch-action: none;
}

/* Треть-линии: помогают ставить кадр, не мешая видеть картинку.
   ⚠️ РАЗМЕР СЛОЯ — РОВНО ЛИНИЯ, а проценты отдаются позиции. Первая версия
   задавала слою высоту в 33% и 67%, и обе линии сошлись в одной точке на 22%:
   процент в `background-position` считается от РАЗНИЦЫ размеров контейнера и
   слоя, а не от контейнера. На экране это выглядело как «одна линия вместо
   четырёх» — в коде не видно совсем. */
.v2-ed-frame::before,
.v2-ed-frame::after {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    background-repeat: no-repeat;
}
.v2-ed-frame::before {
    background-image:
        linear-gradient(rgba(255, 255, 255, .3), rgba(255, 255, 255, .3)),
        linear-gradient(rgba(255, 255, 255, .3), rgba(255, 255, 255, .3));
    background-size: 100% 1px;
    background-position: 0 33.33%, 0 66.66%;
}
.v2-ed-frame::after {
    background-image:
        linear-gradient(rgba(255, 255, 255, .3), rgba(255, 255, 255, .3)),
        linear-gradient(rgba(255, 255, 255, .3), rgba(255, 255, 255, .3));
    background-size: 1px 100%;
    background-position: 33.33% 0, 66.66% 0;
}

/* ⚠️ Ручки крупнее, чем выглядят: 12px видимого квадрата и 44px области
   нажатия. На телефоне палец в 8px не попадает, а увеличить сам квадрат
   значило бы закрыть им угол кадра — то самое место, которое ставят. */
.v2-ed-h {
    position: absolute;
    width: 12px;
    height: 12px;
    background: #fff;
    border-radius: 2px;
    touch-action: none;
}
.v2-ed-h::after {
    content: '';
    position: absolute;
    inset: -16px;
}
.v2-ed-h-nw { left: -6px;  top: -6px;    cursor: nwse-resize; }
.v2-ed-h-ne { right: -6px; top: -6px;    cursor: nesw-resize; }
.v2-ed-h-sw { left: -6px;  bottom: -6px; cursor: nesw-resize; }
.v2-ed-h-se { right: -6px; bottom: -6px; cursor: nwse-resize; }

/* ⚠️ СОДЕРЖИМОЕ ПАНЕЛИ ОГРАНИЧЕНО ПО ШИРИНЕ И ПО ЦЕНТРУ. На широком экране
   ряды растягивались во всю ширину окна: ползунок яркости выходил под 1270px —
   на диапазон от −100 до 100 это не даёт ни капли точности, только лишний путь
   для руки. И картинка при этом по центру, а органы управления по краям, что
   разрывает экран надвое.

   Правило ОДНО на всю панель, а не по режиму: так «Кадр», «Рисование» и «Свет»
   встают одинаково, и добавленный завтра ряд встанет туда же сам. */
.v2-ed-bar {
    flex: 0 0 auto;
    display: block;
    /* ⚠️ ВЫСОТА ПАНЕЛИ — ЕДИНСТВЕННОЕ, ЧТО ДВИЖЕТСЯ ПРИ ПЕРЕХОДЕ. Рамка окна,
       шапка и верх сцены у просмотра и редактора совпадают до пикселя; расходится
       только низ — от 27 до 85 пикселей в зависимости от режима и ширины. Раньше
       он менялся одним кадром, спрятанным под затуханием слоя. Теперь панель
       доезжает от чужой высоты к своей, а сцена и картинка следуют за ней сами,
       потому что забирают остаток.

       ⚠️ Саму высоту ставит JS (`морфНиза` в editor.js) — из CSS её не взять:
       начальное значение это высота ЧУЖОЙ панели, панели просмотра под нами. */
    transition: height .13s ease-out, padding .13s ease-out;
    --ed-max: 1000px;
    padding: 12px max(16px, calc((100% - var(--ed-max)) / 2))
             calc(12px + env(safe-area-inset-bottom));
    background: var(--s2);
    border-top: 1px solid var(--ln);
}

/* Зона инструментов: всё по центру, переносится по мере надобности. */
.v2-ed-rows {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 10px;
}
/* Зона решений: размер слева, кнопки справа. */
.v2-ed-foot {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 10px;
}
.v2-ed-ratios,
.v2-ed-tools,
.v2-ed-actions { display: flex; gap: 8px; align-items: center; }
.v2-ed-actions { margin-left: auto; }

.v2-ed-chip,
.v2-ed-tool,
.v2-ed-btn {
    font: inherit;
    color: var(--tx);
    background: var(--s3);
    border: 1px solid transparent;
    border-radius: 8px;
    padding: 8px 12px;
    min-height: 38px;
    cursor: pointer;
}
.v2-ed-tool { min-width: 42px; font-size: 17px; line-height: 1; }
.v2-ed-chip.is-on { border-color: var(--br); color: var(--br); }
.v2-ed-btn-main { background: var(--br); color: #fff; }
.v2-ed-btn:disabled { opacity: .55; cursor: default; }

.v2-ed-note {
    font-size: 13px;
    color: var(--dim);
    font-variant-numeric: tabular-nums;
}
.v2-ed-note.is-bad { color: var(--danger); }

/* На телефоне панель встаёт в две строки: инструменты сверху, кнопки снизу. */
@media (max-width: 620px) {
    /* ⚠️ Зоны поджимаются, а не только отступы по краям. Разведя панель на две
       зоны ради ширины на десктопе, я добавил между ними отступ — и на телефоне
       панель выросла со 165 до 193px, то есть ровно туда, откуда её только что
       вытаскивали. Тридцать пикселей здесь — это тридцать пикселей у картинки. */
    /* ⚠️ Шапка на телефоне ПОДЖАТА, и ровно так же поджата шапка просмотра.
       Появившись ради совпадения геометрии на десктопе, она забрала у картинки
       7% высоты на телефоне — то есть отняла часть того, что мы там только что
       отвоевали. Высоты обеих шапок обязаны СОВПАДАТЬ: разойдутся — картинка
       начнёт прыгать при переходе, а от этого всё и затевалось. */
    .v2-ed-head { padding: 5px 8px; }
    .v2-ed-ico { width: 34px; height: 34px; }
    .v2-ed-bar { gap: 8px; padding-top: 10px; }
    .v2-ed-rows { gap: 8px; }
    .v2-ed-foot { margin-top: 8px; gap: 8px; }
    .v2-ed-tune { gap: 4px; }
    .v2-ed-tune-row { gap: 6px; }
    /* ⚠️ Подвал в ДВЕ СТРОКИ. В одну на 390px не влезали ни размер, ни
       «Сбросить всё»: оба переносились ПО СЛОВАМ, и подвал выглядел мятым.
       Размер отдельной строкой, кнопки во всю ширину — по ним ещё и попадать
       пальцем удобнее. */
    .v2-ed-foot { flex-wrap: wrap; }
    .v2-ed-note { flex: 1 1 100%; }
    .v2-ed-actions { flex: 1 1 100%; margin-left: 0; }
    .v2-ed-actions .v2-ed-btn { flex: 1 1 auto; white-space: nowrap; }
    .v2-ed-actions { margin-left: 0; width: 100%; }
    .v2-ed-btn { flex: 1 1 0; }
}

/* ⚠️ Ряда с переключателем режимов БОЛЬШЕ НЕТ: режим задаётся дверью в
   просмотре. На телефоне этот ряд съедал высоту у картинки, а сам выбор
   дублировал двери. Отмена переехала в ряд решений. */
.v2-ed-tool:disabled { opacity: .35; cursor: default; }
.v2-ed-actions .v2-ed-tool { min-width: 42px; }

/* Переключатель режимов и ряд рисования. */
.v2-ed-modes { display: flex; gap: 6px; }
.v2-ed-paint { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.v2-ed-tool.is-on {
    border-color: var(--br);
    color: var(--br);
}

/* ⚠️ В режиме рисования рамка ВИДНА, но нажатий не ловит. Видна — чтобы не
   рисовать в то, что отрежется; не ловит — иначе рисовать поверх неё нечем.
   Рисовать ЗА рамкой можно: штрих просто срежется, а рамку могут расширить. */
.v2-ed.is-draw .v2-ed-frame { pointer-events: none; }
.v2-ed.is-draw .v2-ed-h { display: none; }
/* ⚠️ Без этого первое же движение пальцем прокрутит страницу вместо линии. */
.v2-ed.is-draw .v2-ed-canvas { touch-action: none; cursor: crosshair; }

/* Палитра и толщина. */
.v2-ed-colors { display: flex; gap: 6px; align-items: center; }
.v2-ed-sw {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    border: 2px solid transparent;
    padding: 0;
    cursor: pointer;
    /* ⚠️ Площадь нажатия больше видимого кружка: 26px пальцем не всегда берётся,
       а увеличивать сам кружок значит съесть строку на телефоне. */
    position: relative;
}
.v2-ed-sw::after { content: ''; position: absolute; inset: -6px; }
.v2-ed-sw.is-on {
    border-color: var(--tx);
    box-shadow: 0 0 0 2px var(--s2), 0 0 0 4px var(--br);
}
.v2-ed-wrange { width: 110px; accent-color: var(--br); }
.v2-ed-wdot {
    display: inline-block;
    border-radius: 50%;
    flex: 0 0 auto;
    /* Обводка, чтобы белый образец был виден на тёмной панели. */
    box-shadow: 0 0 0 1px var(--ln);
}

/* Свет и цвет: подпись, ползунок, число — по колонкам, чтобы ручки
   выстроились по вертикали и не прыгали от длины подписи. */
.v2-ed-tune {
    display: grid;
    gap: 6px;
    width: 100%;
    /* ⚠️ Ползунки УЖЕ остальной панели, и намеренно: точность руки на отрезке
       в 400px и в 1000px одинакова, а тянуться через весь экран приходится
       по-разному. Список подписанных ползунков к тому же читается как блок,
       а растянутый — как четыре не связанные полосы. */
    max-width: 560px;
    margin-inline: auto;
    /* Своя строка внутри зоны инструментов: четыре подписанных ползунка рядом
       с чем-то ещё читались бы как продолжение чужого ряда. */
    flex: 1 1 100%;
}
.v2-ed-tune-row {
    display: grid;
    grid-template-columns: 108px 1fr 40px;
    align-items: center;
    gap: 10px;
}
.v2-ed-tune-name { font-size: 13px; color: var(--dim); }
.v2-ed-tune-range { width: 100%; accent-color: var(--br); }
.v2-ed-tune-val {
    font-size: 13px;
    color: var(--dim);
    text-align: right;
    /* Цифры одной ширины — иначе ползунок дёргается, пока тянешь. */
    font-variant-numeric: tabular-nums;
}
@media (max-width: 620px) {
    .v2-ed-tune-row { grid-template-columns: 92px 1fr 34px; gap: 8px; }
}

/* Затухание при закрытии. Чуть быстрее появления: уход не должен задерживать. */
@keyframes v2-edit-out {
    from { opacity: 1; transform: scale(1); }
    to   { opacity: 0; transform: scale(.99); }
}
@keyframes v2-edit-fade-out {
    from { opacity: 1; }
    to   { opacity: 0; }
}
.v2-vw.is-leaving { animation: v2-edit-out .11s ease-in forwards; }
/* Редактор уходит без масштаба — по той же причине, по какой без него
   приходит: под ним стоит его же карточка, и ей незачем «сжиматься». */
.v2-ed.is-leaving { animation: v2-edit-fade-out .13s ease-in forwards; }
@media (prefers-reduced-motion: reduce) {
    .v2-ed.is-leaving, .v2-vw.is-leaving { animation: none; opacity: 0; }
    .v2-ed-bar { transition: none; }
}


/* ============================================================================
   ПОЛОСА ОТПРАВКИ В ПРОСМОТРЕ (2026-09-05)
   ============================================================================
   Просмотр перестаёт быть тупиком: подпись и отправка тут же, возвращаться в
   переписку не нужно. Поле ввода — ТОТ ЖЕ узел `.chat-input`, переехавший
   сюда на время (см. `takeInput`/`giveInputBack` в `viewer.js`).            */
.v2-vw-send {
  flex: none;
  display: flex;
  align-items: flex-end;
  gap: 8px;
  padding: 9px 10px calc(10px + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--ln);
  background: var(--s2);
}
.v2-vw-inslot {
  /* ⚠️ `min-width: 0` обязателен: поле ввода растягивается по содержимому, и
     без этого длинная строка вытолкнет кнопку за край. */
  flex: 1;
  min-width: 0;
}
/* ⚠️ Вид поля НЕ переопределяем — это то же самое поле, и человек должен его
   узнать. Задаём только то, что нужно новому месту. */
.v2-vw-inslot > .chat-input {
  width: 100%;
  max-height: 120px;
}
.v2-vw-sendbtn {
  flex: none;
  width: 34px; height: 34px;
  border: 0; border-radius: 50%;
  background: var(--br); color: var(--onbr);
  display: grid; place-items: center;
  cursor: pointer;
}

/* ⚠️ ПОКА ОТКРЫТ РЕДАКТОР, ПОЛОСЫ НЕ ВИДНО И ДО НЕЁ НЕ ДОБРАТЬСЯ. Редактор —
   отдельный слой ПОВЕРХ просмотра, а не внутри него, поэтому селектор идёт от
   `body`, а не от родителя. Без `pointer-events` и `visibility` в поле можно
   попасть табом и печатать в невидимое.
   ⚠️ `:has` тут по слою, который ещё не гаснет: у уходящего стоит
   `data-leaving`, и без этого уточнения полоса не вернулась бы до конца
   анимации. */
body:has(.v2-ed:not([data-leaving])) .v2-vw-send {
  visibility: hidden;
  pointer-events: none;
}
/* Просмотр вложенного до отправки. */

.v2-vw {
    position: fixed;
    inset: 0;
    z-index: 3900;            /* ниже редактора (4000), выше листа медиа (3100) */
    display: grid;
    /* ⚠️ `minmax(0, 1fr)`, А НЕ `1fr`. У строки `1fr` неявный минимум — по
       содержимому, поэтому она вырастала под натуральную высоту картинки, а
       `max-height: 100%` начинал считаться от УЖЕ ВЫРОСШЕЙ строки. Замер:
       картинка 761px в сцене 607px, нижний край срезан панелью действий.

       ⚠️ И рядов ТРИ: шапка, кадр, низ. Лента с действием съехались в одну
       панель — четыре горизонтальные полосы читались как несвязанные куски. */
    grid-template-rows: auto minmax(0, 1fr) auto;
    /* ⚠️ ТА ЖЕ ОШИБКА ЭТАЖОМ ЛЕВЕЕ, И ОНА ДОРОЖЕ. Колонки не были объявлены
       вовсе, то есть единственная колонка была `auto` — а `auto` не меньше
       СОДЕРЖИМОГО. Содержимое здесь — имя файла в шапке: `white-space: nowrap`
       делает его минимальную ширину равной всей строке целиком, и никакой
       `min-width: 0` этого не снимает (это пол, а не потолок). Имя вида
       `4c81467d9eed15b59ca4ed6a2c600d36c6c5a104.png` растягивало КОЛОНКУ, а с
       ней и все три ряда: замер на окне 443px — слой 443, а шапка, кадр и
       нижняя панель по 492. Третья дверь уходила за правый край экрана, и
       картинка вместе с ней. Ряды я в своё время починил, а колонку не
       объявил — и та же причина ждала длинного имени файла. */
    grid-template-columns: minmax(0, 1fr);
    background: var(--bg);
    outline: none;
}

.v2-vw-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    /* ⚠️ Без разделительной черты: шапка отделена от кадра ФОНОМ. Линия поверх
       тёмной подложки читается как шов — Вадим так это и назвал, «кое-как
       склеенный». Черта нужна там, где иначе границу не понять; здесь понятно. */
    background: var(--s2);
}
.v2-vw-x {
    font: inherit;
    font-size: 18px;
    line-height: 1;
    color: var(--tx);
    background: transparent;
    border: 0;
    padding: 8px 10px;
    border-radius: 8px;
    cursor: pointer;
}
.v2-vw-x:hover { background: var(--s3); }
.v2-vw-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--tx);
    font-size: 14px;
    font-weight: 500;
}
.v2-vw-head-acts {
    display: flex;
    gap: 2px;
    flex: 0 0 auto;
}
.v2-vw-ico {
    display: grid;
    place-items: center;
    width: 38px;
    height: 38px;
    color: var(--tx);
    background: transparent;
    border: 0;
    border-radius: 10px;
    cursor: pointer;
}
.v2-vw-ico:hover { background: var(--s3); }
/* ⚠️ УДАЛЕНИЕ КРАСНЕЕТ ПОД ПАЛЬЦЕМ, А НЕ В ПОКОЕ (решение Вадима 2026-09-05).
   Смысл «единственное необратимое действие» никуда не делся, но постоянный
   насыщенный красный был ЕДИНСТВЕННЫМ не-акцентным цветом на экране, и глаз
   шёл к нему вперёд самой фотографии. Отделяет его теперь МЕСТО (своя группа
   справа) плюс цвет в момент касания.

   ⚠️ `:active` РАБОТАЕТ И НА СЕНСОРНОМ — палец вниз красит кнопку. На
   наведение полагаться было бы нельзя: на телефоне его нет вовсе, и там
   кнопка не покраснела бы никогда. Поэтому правила ДВА, а не одно. */
.v2-vw-ico.is-danger:hover,
.v2-vw-ico.is-danger:active {
    color: var(--danger);
    background: rgba(255, 107, 107, .14);
}
.v2-vw-ico.is-on { color: var(--br); }

.v2-vw-count {
    flex: 0 0 auto;
    font-size: 13px;
    color: var(--dim);
    font-variant-numeric: tabular-nums;
}

.v2-vw-stage {
    position: relative;
    min-height: 0;
    display: grid;
    place-items: center;
    padding: 20px;
    overflow: hidden;
    /* Кадр стоит на своей подложке, а не «в воздухе»: у картинки появляется
       поле, и она перестаёт упираться в края. */
    background: var(--pg);
}
.v2-vw-img {
    max-width: 100%;
    max-height: 100%;
    /* ⚠️ И ЗДЕСЬ ТОЖЕ. Тот же неявный минимум, что и у строки сетки, только
       этажом ниже: у элемента сетки `min-height: auto` означает «не меньше
       содержимого», а для картинки содержимое — её натуральный размер. Этот
       минимум СИЛЬНЕЕ `max-height`, поэтому картинка 1200×800 занимала 761px
       в сцене высотой 607 и уходила под панель действий. Замер: с `min-height: 0`
       та же картинка встаёт в 575. Два разных места, одна причина — оба
       исправления нужны, ни одного из них по отдельности не хватает. */
    min-height: 0;
    min-width: 0;
    object-fit: contain;
    border-radius: 8px;
    box-shadow: var(--sh);
}
.v2-vw-file {
    display: grid;
    gap: 8px;
    place-items: center;
    color: var(--dim);
}
.v2-vw-ext {
    font-size: 28px;
    font-weight: 700;
    letter-spacing: .04em;
    color: var(--tx);
    padding: 22px 26px;
    border: 1px solid var(--ln);
    border-radius: 12px;
}

/* ⚠️ СТРЕЛКИ ПОЯВЛЯЮТСЯ ПО НАВЕДЕНИЮ. Способов перейти к соседнему вложению
   было три сразу — стрелки, лента и счётчик; лишние подсказывают, что автор
   сам не решил, каким пользоваться. Лента остаётся главной, стрелки — быстрый
   путь для того, кто уже держит указатель на картинке. На сенсорном экране
   наведения нет, поэтому там они видны всегда. */
.v2-vw-stage:hover .v2-vw-nav,
.v2-vw-nav:focus-visible { opacity: 1; }
@media (hover: none) { .v2-vw-nav { opacity: 1; } }

/* ⚠️ НЕАКТИВНАЯ СТРЕЛКА НЕ ПОКАЗЫВАЕТСЯ ВОВСЕ. Правило `:disabled` с
   прозрачностью .25 стояло отдельно и перебивало скрытие: на первом вложении
   было видно ровно ту стрелку, которая не работает, а рабочая пряталась. То
   есть подсказка показывала тупик и умалчивала о выходе. Мёртвый орган
   управления — шум; край ленты и так виден. */
.v2-vw-nav:disabled { display: none; }

.v2-vw-nav {
    opacity: 0;
    transition: opacity .14s;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    font-size: 24px;
    line-height: 1;
    color: var(--tx);
    background: var(--s2);
    border: 1px solid var(--ln);
    box-shadow: var(--sh);
    border-radius: 999px;
    width: 42px;
    height: 42px;
    cursor: pointer;
}
.v2-vw-nav:hover { border-color: var(--br); }
.v2-vw-prev { left: 10px; }
.v2-vw-next { right: 10px; }

/* ── НИЖНЯЯ ПАНЕЛЬ ───────────────────────────────────────────────────────────
   Слева навигация по вложениям, справа действие. Одна полоса вместо двух:
   раньше лента приткнулась в углу, а кнопка висела по центру над ней, и связи
   между ними не читалось. Референс — WhatsApp и Google Photos. */
.v2-vw-bottom {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    /* То же ограничение, что и у панели редактора: на широком экране лента и
       двери расползались к противоположным краям и переставали читаться как
       одна панель. */
    --vw-max: 1000px;
    padding: 12px max(14px, calc((100% - var(--vw-max)) / 2))
             calc(12px + env(safe-area-inset-bottom));
    background: var(--s2);
    min-height: 68px;
}
/* Единственное вложение — ленты нет, и кнопке незачем жаться к краю. */
.v2-vw-bottom:not(:has(.v2-vw-strip)) { justify-content: center; }

.v2-vw-acts {
    display: flex;
    gap: 10px;
    flex: 0 0 auto;
}

/* Главные действия — значок НАД подписью: так кнопка читается с одного
   взгляда и не превращается в ряд одинаковых прямоугольников с текстом. */
.v2-vw-act {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 5px;
    font: inherit;
    font-size: 12px;
    color: var(--tx);
    background: transparent;
    border: 1px solid transparent;
    border-radius: 12px;
    padding: 10px 16px;
    min-width: 76px;
    cursor: pointer;
    transition: background .12s, border-color .12s;
}
.v2-vw-act:hover { background: var(--s3); }
.v2-vw-act:active { transform: translateY(1px); }

/* Отмена и сброс — тише главных действий: значок и подпись в строку, мельче.
   Ряд появляется, только когда есть что отменять. */
.v2-vw-undo {
    display: flex;
    gap: 8px;
    justify-content: center;
    padding: 0 14px 12px;
}
.v2-vw-act-quiet {
    flex-direction: row;
    gap: 7px;
    font-size: 13px;
    color: var(--dim);
    padding: 7px 14px;
    min-width: 0;
    border-radius: 999px;
    background: var(--s3);
}
.v2-vw-act-quiet:hover { color: var(--tx); }

.v2-vw-strip {
    display: flex;
    gap: 8px;
    overflow-x: auto;
    /* Отступов нет: их даёт панель, внутри которой лента теперь живёт. */
    padding: 0;
    min-width: 0;
    /* Полосу прокрутки прячем: лента короткая, а системная полоса перечёркивает
       нижний край плиток. */
    scrollbar-width: none;
}
.v2-vw-strip::-webkit-scrollbar { display: none; }
.v2-vw-th {
    flex: 0 0 auto;
    width: 52px;
    height: 52px;
    padding: 0;
    border: 2px solid transparent;
    border-radius: 8px;
    overflow: hidden;
    background: var(--s3);
    cursor: pointer;
    display: grid;
    place-items: center;
}
.v2-vw-th img { width: 100%; height: 100%; object-fit: cover; display: block; }
.v2-vw-th.is-on { border-color: var(--br); }
/* Правка помечается ОБВОДКОЙ, а не значком: три угла плитки уже заняты. */
.v2-vw-th.is-edited { box-shadow: inset 0 0 0 2px var(--warn); }
.v2-vw-th-ext { font-size: 11px; font-weight: 700; color: var(--dim); }

/* На широком экране просмотр — карточка, а не захват всего окна. */
@media (min-width: 900px) {
    .v2-vw {
        inset: 5vh 8vw;
        border: 1px solid var(--ln);
        border-radius: 16px;
        /* ⚠️ ЗАТЕМНЕНИЕ ФОНА — ВТОРОЙ ТЕНЬЮ, А НЕ ПОДЛОЖКОЙ-ПСЕВДОЭЛЕМЕНТОМ.
           Была `::before` с `position: fixed; inset: 0; z-index: -1` — и это
           красило фон ПОВЕРХ СОБСТВЕННОЙ РАМКИ карточки: по порядку отрисовки
           дети с отрицательным z-index идут ПОСЛЕ фона и рамки родителя, а не
           до. Рамка просмотра выходила притушенной до 26% (замер: (12,12,13)
           вместо токенных (45,45,51)), у редактора сверху — полной, и на
           входе в правку по краю окна вспыхивала лишняя светлая обводка.
           Вадим так это и описал: «появляется обводка ярче, чем было».

           Тень со `spread: 100vmax` красит только СНАРУЖИ border-box и
           уважает скругление — рамка остаётся своего цвета. Приём в проекте
           уже есть: так затемнена область вокруг рамки обрезки (edit.css). */
        box-shadow:
            0 24px 80px rgba(0, 0, 0, .6),
            0 0 0 100vmax rgba(0, 0, 0, .74);
        overflow: hidden;
    }
}

/* На телефоне действия не помещаются в строку и переносятся. Одинокая кнопка
   по центру второго ряда читается как ошибка вёрстки, поэтому ряды делятся
   поровну: две колонки. */
@media (max-width: 620px) {
    /* На телефоне лента и кнопка в один ряд не помещаются: кнопка ужалась бы
       до значка, а лента до двух плиток. Кладём в две строки, кнопка во всю
       ширину — большим пальцем в неё попадаешь не глядя. */
    .v2-vw-bottom {
        flex-direction: column;
        align-items: stretch;
        gap: 10px;
    }
    .v2-vw-acts { justify-content: stretch; gap: 6px; }
    .v2-vw-act { flex: 1 1 0; min-width: 0; padding: 9px 4px; }
    /* ⚠️ Та же высота, что у шапки редактора (см. edit.css). Разойдутся —
       картинка прыгнет при переходе между просмотром и правкой. */
    .v2-vw-head { padding: 5px 8px; }
    .v2-vw-ico { width: 34px; height: 34px; }
    .v2-vw-name { font-size: 14px; }
}

/* ⚠️ Дверей снова ТРИ, и каждая ведёт ровно в свой инструмент. Единственная
   «Править» открывала редактор с переключателем внутри — на телефоне он съедал
   целый ряд, а двери от него становились условными. Референс — Телеграм. */
.v2-vw-act[data-act="crop"],
.v2-vw-act[data-act="draw"],
.v2-vw-act[data-act="tune"] {
    background: var(--s3);
    border-radius: 12px;
}
.v2-vw-act[data-act="crop"]:hover,
.v2-vw-act[data-act="draw"]:hover,
.v2-vw-act[data-act="tune"]:hover { border-color: var(--br); color: var(--br); }

/* ⚠️ Дверь, за которой что-то сделано, — подсвечена. Референс — Телеграм: без
   этого, закрыв редактор, о собственных правках узнаёшь только заходя в каждый
   инструмент по очереди. Точка, а не заливка: заливка спорила бы с оранжевой
   главной кнопкой, а тут важно «тут есть правка», а не «нажми сюда». */
.v2-vw-act.is-done { color: var(--br); }
.v2-vw-act.is-done::after {
    content: '';
    position: absolute;
    top: 7px;
    right: 11px;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--br);
}
.v2-vw-act { position: relative; }

/* ⚠️ ЭСКИЗ НЕ РАСТЯГИВАЕТСЯ. Пока оригинал едет со склада, показан микро-эскиз
   192×256; растянув его во весь кадр, мы показали бы мыло, которое читается как
   испорченный файл. Маленькая картинка честнее: видно, что это ещё не всё.
   Класс снимает `поднятьОригинал`, подменив источник. */
.v2-vw-img.is-placeholder {
    image-rendering: auto;
    opacity: .55;
    filter: blur(1px);
}
