/* /crm/chat — мастер/деталь: список и активный диалог как отдельные экраны
   (один за раз) на узких режимах. Класс chat-show-conv на #chat-panes
   переключает видимость.

   Порог — ниже Tailwind-lg (<1024px), а не 767px: глобальный сайдбар CRM
   в потоке уже отъедает 15rem (или 4rem свёрнутый), и на планшете в портрете
   двум панелям ленте достаётся «огрызок». Двухпанельный режим включаем только
   с lg, где ленте остаётся ≥~28rem (1024 − 240 сайдбар − 48 отступы − 288
   список ≈ 448px). Ниже lg — по одной панели на всю контентную ширину. */
@media (max-width: 1023.98px) {
  #chat-panes:not(.chat-show-conv) #chat-conversation { display: none; }
  #chat-panes.chat-show-conv #chat-list-pane { display: none; }
}

/* ═══════════════════════════════════════════════════════════════════════
   ЛЕНТА: грамматика письма (этап Ч2, 03.08.2026)

   Канон — жанр I макета В «Письмо, журнал и поля»: подпись отправителя и
   время НАД пузырём, своё справа на охряной подложке, чужое слева на белой,
   статус доставки строкой под пузырём, дни разделены капсовой подписью.
   Кадры: Макеты/Чат_v2/В_Письмо_и_журнал_{десктоп,тёмная}_клиент.png.

   ПОЧЕМУ CSS, А НЕ УТИЛИТЫ. Кегли и трекинг канона (11 px при трекинге
   1.5 px, 11.5 px моно, 15.5 px/1.55, радиус 14/4) в шкалу Tailwind не
   ложатся, а разметка письма живёт в ОДНОМ партиале на четыре поверхности —
   значит и стиль уместно держать в одном месте. Файл подключён во всех
   четырёх базах (CRM, ЛК, PWA — сторожа в тестах чата).

   ТЁМНАЯ ТЕМА приходит сама: ни одного литерального цвета, только токены
   --neutral-* / --brand-* / --surface-0 из бандла (этап Т). Литералом
   остаётся одна тень — её альфа в тёмной другая по смыслу.

   ОТДЕЛКИ КАНАЛОВ ЗДЕСЬ НЕТ (кант, бумага, сетка журнала — задача Ч2-Б):
   она наслаивается модификатором на контейнер ленты и разметку письма не
   трогает.
   ═══════════════════════════════════════════════════════════════════════ */

/* Гарнитуры чата — на общем предке, а не на .chat-thread.
   Узким капсом набраны не только атомы письма: тем же начертанием живут
   штамп канала и кнопки переключателя (Ч2-4), а они лежат СНАРУЖИ ленты —
   .chat-thread приезжает внутрь поля канала фрагментом _messages.html.
   Кастомные свойства наследуются только вниз, поэтому объявление внутри
   ленты давало снаружи invalid-at-computed-value-time: капс оставался, а
   гарнитура молча становилась обычной (шире задуманного). Один источник
   на все три места — здесь. */
:root {
  --chat-face-narrow: 'PT Sans Narrow', 'PT Sans', ui-sans-serif, sans-serif;
  --chat-face-mono: 'PT Mono', ui-monospace, SFMono-Regular, monospace;
}

.chat-thread {
  /* Ширина ленты, а не окна: в треде CRM рядом стоит список диалогов, в
     карточке объекта лента вовсе в колонке — окно о них не знает. */
  container-type: inline-size;
  --chat-shadow: 0 1px 2px rgb(15 23 42 / 0.05);
}
/* Тень на тёмном — не осветление, а провал: альфа .05 на графите не видна
   вовсе. Оба тёмных контекста, как во всём бандле (явный data-theme и
   режим «как в системе»). */
html.themable[data-theme="dark"] .chat-thread { --chat-shadow: 0 1px 2px rgb(0 0 0 / 0.45); }
@media (prefers-color-scheme: dark) {
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-thread {
    --chat-shadow: 0 1px 2px rgb(0 0 0 / 0.45);
  }
}

/* ── Письмо ─────────────────────────────────────────────────────────── */
.chat-msg { max-width: 640px; margin-bottom: 22px; }
.chat-msg:last-child { margin-bottom: 0; }
.chat-msg[data-mine="1"] { margin-left: auto; }

.chat-msg-head { display: flex; align-items: baseline; gap: 9px; margin-bottom: 6px; }
.chat-msg[data-mine="1"] .chat-msg-head { justify-content: flex-end; }

/* Автор и время — те самые атомы, что держат три жанра одним продуктом:
   автор всегда узким капсом, время всегда моноширинным. */
.chat-msg-who {
  font-family: var(--chat-face-narrow);
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.5px;
  /* Узкий капс с трекингом съедает пробел — зазор возвращаем ВСЕМ
     многословным атомам, а не только штампу канала: «ИВАНОВ ИВАН
     ИВАНОВИЧ» слипается ровно так же, как «КЛИЕНТ ЭТОГО НЕ ВИДИТ».
     Значение одно на весь чат (.chat-channel-stamp, .chat-thread-stage,
     .chat-thread-next b, .chat-earlier, .chat-daysep span). */
  word-spacing: 0.28em;
  text-transform: uppercase;
  color: rgb(var(--neutral-900) / 1);
  min-width: 0;
  overflow-wrap: break-word;
}
.chat-msg-at {
  font-family: var(--chat-face-mono);
  font-size: 11.5px;
  line-height: 1;
  color: rgb(var(--neutral-500) / 1);
  font-variant-numeric: tabular-nums;
  flex: none;
}

/* ПУЗЫРЬ ПО СОДЕРЖАНИЮ, а не по колонке (ЧР-4, ревью M-1; эталон
   НСБ_хаб.html .bub). 640 px — это КАП письма, а не его ширина, и до правки
   разница стёрлась: `.chat-msg-bubble` был обычным блоком и растягивался на
   всю ширину родителя. Замер ревью: реплики в 34, 40 и 20 знаков — все ровно
   640 px, в обеих темах, в карточке и в личном диалоге. Это вторая половина
   находки Ф-2 «однострочная реплика растянута в пустой брус».

   fit-content = min(max-content, max(min-content, доступное): короткая
   реплика садится по буквам, длинная упирается в max-width: 100% (то есть в
   те же 640 родителя) и переносится как раньше. */
.chat-msg-bubble {
  width: fit-content;
  max-width: 100%;
  background: rgb(var(--surface-0) / 1);
  border: 1px solid rgb(var(--neutral-200) / 1);
  border-radius: 14px;
  border-top-left-radius: 4px;   /* угол «прикреплён» к подписи слева */
  padding: 13px 17px 14px;
  font-size: 15.5px;
  line-height: 1.55;
  color: rgb(var(--neutral-900) / 1);
  box-shadow: var(--chat-shadow);
}
/* Своё письмо: подложка бренда, срезанный угол справа — под подписью.
   margin-left: auto обязателен вместе с fit-content (эталон .bub того же
   письма): статья своего письма прижата вправо на все 640 px, а пузырь
   внутри неё теперь узкий — без этой строки короткая реплика стояла бы у
   ЛЕВОГО края своей же колонки, то есть посреди ленты, и зеркальность
   читалась бы наоборот. Подпись и статус прижаты своими flex-правилами. */
.chat-msg[data-mine="1"] .chat-msg-bubble {
  margin-left: auto;
  background: rgb(var(--brand-100) / 1);
  border-color: rgb(var(--brand-300) / 1);
  border-top-left-radius: 14px;
  border-top-right-radius: 4px;
}

.chat-msg-body { white-space: pre-wrap; overflow-wrap: break-word; }

/* Вложение — карточка внутри письма, а не голая ссылка: у неё есть вес,
   как у скрепки в конверте. */
.chat-msg-att {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 9px;
  padding: 5px 11px;
  border: 1px solid rgb(var(--neutral-200) / 1);
  border-radius: 6px;
  background: rgb(var(--surface-0) / 1);
  color: rgb(var(--brand-500) / 1);
  font-size: 13px;
  line-height: 1.3;
  text-decoration: none;
}
.chat-msg-att:hover {
  border-color: rgb(var(--brand-300) / 1);
  color: rgb(var(--brand-600) / 1);
}

/* ── Статус доставки ────────────────────────────────────────────────── */
.chat-msg-status {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 5px;
  margin-top: 6px;
  font-size: 12.5px;
  color: rgb(var(--neutral-500) / 1);
}
.chat-status-icon { display: inline-flex; align-items: center; gap: 5px; }
.chat-status-icon svg { width: 16px; height: 12px; flex: none; }
/* Прочитано — единственный статус, который красится: у письма появился
   читатель. Отправлено и доставлено остаются служебной серостью. */
.chat-status-icon[data-status="read"] svg { color: rgb(var(--brand-500) / 1); }

/* ── Разделитель дня ────────────────────────────────────────────────── */
.chat-daysep { display: flex; align-items: center; gap: 12px; margin: 4px 0 18px; }
.chat-daysep::before,
.chat-daysep::after { content: ""; height: 1px; flex: 1; background: rgb(var(--neutral-200) / 1); }
.chat-daysep span {
  flex: none;
  font-family: var(--chat-face-narrow);
  font-size: 10.5px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 2px;
  /* Трекинг здесь самый крупный в чате, а подпись самая многословная —
     «СЕГОДНЯ · 2 АВГУСТА» без зазора читается одним словом. */
  word-spacing: 0.28em;
  text-transform: uppercase;
  color: rgb(var(--neutral-500) / 1);
}
/* Между письмами разделитель дышит: 22 px снизу у письма + свои 10 px. */
.chat-msg + .chat-daysep { margin-top: 10px; }

/* ── «Показать раньше» и пустая лента ───────────────────────────────── */
.chat-earlier {
  display: block;
  margin: 0 auto 18px;
  padding: 6px 14px;
  border: 1px solid rgb(var(--neutral-200) / 1);
  border-radius: 999px;
  background: rgb(var(--surface-0) / 1);
  font-family: var(--chat-face-narrow);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 1.6px;
  word-spacing: 0.28em;                 /* «ПОКАЗАТЬ РАНЬШЕ» — два слова */
  text-transform: uppercase;
  color: rgb(var(--neutral-500) / 1);
  cursor: pointer;
}
.chat-earlier:hover {
  border-color: rgb(var(--brand-300) / 1);
  color: rgb(var(--brand-600) / 1);
}

.chat-empty {
  padding: 26px 0;
  text-align: center;
  font-size: 14px;
  color: rgb(var(--neutral-400) / 1);
}

.chat-earlier:focus-visible,
.chat-msg-att:focus-visible {
  outline: 2px solid rgb(var(--brand-500) / 1);
  outline-offset: 2px;
}

/* ── Узкая лента (телефон прораба, ЛК в портрете, колонка карточки) ──
   Письмо перестаёт быть колонкой в 640 px и становится долей ширины,
   кегль садится на полпункта — плотность канона для 390 px. */
@container (max-width: 560px) {
  .chat-msg { max-width: 88%; margin-bottom: 18px; }
  .chat-msg-bubble { font-size: 14.5px; padding: 11px 13px 12px; }
  .chat-daysep { margin: 2px 0 14px; }
  .chat-msg + .chat-daysep { margin-top: 8px; }
}

/* ═══════════════════════════════════════════════════════════════════════
   КАНАЛ: переключатель, поле канала, строка-признак (этап Ч2-4, 03.08.2026)

   Режим 1 защиты (Ч2-Р4): у канала ПОСТОЯННЫЕ цвет и подпись — без
   настройки и без модалок. Лестница из ЧИТАЙ_ПЕРЕД_МАКЕТАМИ: наружу —
   тёплая охра бренда, внутрь — холодный графит. Один и тот же цвет несут
   активная кнопка переключателя, поле ленты и штамп над композером;
   перепутать канал, глядя только на поле ввода, нельзя — он назван трижды.

   ТОКЕН-ПЕРЕМЕННЫЕ, А НЕ ЦВЕТА В ПРАВИЛАХ. Отделка каналов (кант, бумага,
   сетка журнала — задача Ч2-Б) наслаивается модификатором на .chat-channel
   и переопределяет ровно --chat-chan-*; разметку и правила ниже трогать
   не придётся. Третий канал «Офис» (Ч2-В) добавляется одним блоком
   .chat-channel--staff и парой правил переключателя.

   ОБЕ ТЕМЫ БЕЗ ВТОРОГО НАБОРА ПРАВИЛ. Токены записаны парами шкалы, и
   инверсия бандла переворачивает их сама: --surface-0 в светлой это белый
   лист, в тёмной — панель раздела; --neutral-900 в светлой почти-чёрный
   текст, в тёмной почти-белый. Так же ведёт себя активная кнопка канала.

   ═══ ПЛАШКА ПРИЗНАКА — ОДИН НЕЙТРАЛЬНЫЙ ЛИСТ (приёмка ЧР, вердикт В2) ═══
   До приёмки заливку плашки назначали и КАНАЛ (--neutral-200 объекту,
   --neutral-800 офису), и ОТДЕЛКА (кант красил её цветом канала, бумага
   делала прозрачной и подкладывала свой лист). Из-за этого развод чернил
   читателей (ЧР-7: имена полной силой, служебные слова тише) наступал
   ровно там, где плашка и так была светлой, а под умолчанием прода
   («Кант») не наступал вовсе: who == lbl == --brand-on по построению
   (ЧР7-m-1). Эталон НСБ_хаб.html держит под кантом ГРАММАТИЧЕСКУЮ плашку,
   а не кантовую, и вердикт User 13.08 привёл прод к эталону, распространив
   его на все каналы: заливка плашки — --surface-0 ВЕЗДЕ. В светлой это
   белый лист, в тёмной — штатный лист темы (#262219, панель раздела), а не
   «белое в тёмной».

   Что канал теперь на плашке говорит — ШТАМП и ЛИНЕЙКА: гриф печатается
   чернилами канала (--chat-chan-stamp-ink), закрытые каналы отбиты двойной
   линейкой (правило ниже). Режим 1 (канал назван трижды) цел: кнопка
   переключателя, поле ленты и штамп. Заливка плашки четвёртым голосом не
   была — она была ФОНОМ под перечнем читателей, то есть мешала ровно тому,
   ради чего строку и читают.
   ═══════════════════════════════════════════════════════════════════════ */

/* Канал «Клиенту» — значения по умолчанию: наружу пишут на чистом,
   поле ленты нейтральное, штамп охряным контуром (тревожить нечем —
   клиенту и положено это читать). */
.chat-channel {
  --chat-chan-bg: rgb(var(--neutral-50) / 1);
  --chat-chan-edge: rgb(var(--neutral-200) / 1);
  /* ЕДИНСТВЕННОЕ объявление листа плашки на весь файл: ни канал, ни отделка
     его больше не переопределяют (В2). Эталон Н-СБ давал плашке ступени по
     каналам — отклонение записано в НСБ_СБОРКА.md §3-Е вместе с ценой
     (заливка плашку от ленты не отделяет: 1,00…1,55 светлая · 1,00…1,13
     тёмная; отделяют гриф и линейка). */
  --chat-chan-flag-bg: rgb(var(--surface-0) / 1);
  --chat-chan-flag-fg: rgb(var(--neutral-500) / 1);
  /* ОДИН токен чернил вместо прежней тройки bg/fg/bd (ЧР-4, §3-А сборки
     Н-СБ): гриф — контур без заливки, и «цвет плашки» ему больше не нужен.
     Пока тройка была, каждый канал объявлял два одинаковых цвета (рамка =
     заливка) и один противоположный (текст) — три числа там, где смысл
     один: чем печатают штамп. Клиент печатает охрой --brand-600: 6,6:1 по
     панели в светлой, 5,4:1 в тёмной (замеры бланка, секция «Бумага»). */
  --chat-chan-stamp-ink: rgb(var(--brand-600) / 1);
  /* ЧЕРНИЛА ПЕРЕЧНЯ ЧИТАТЕЛЕЙ — отдельный токен (ЧР-7, хвост ЧР4-m-5;
     эталон НСБ_хаб.html разводит их: `--chat-chan-who-fg`). До ЧР-7 имена
     и служебные слова строки-признака шли ОДНИМ цветом, и под бумагой с
     журналом это был --neutral-500: сама ценная часть строки — кто именно
     прочитает письмо — набиралась самым тихим текстом продукта (4,76:1 в
     светлой), а служебное «ЧИТАЮТ» звучало ровно так же громко.
     Разбор всех сцен и порог AA — сторож tests/browser/test_chat_ink.py. */
  --chat-chan-who-fg: rgb(var(--neutral-900) / 1);
}

/* Канал «Исполнителям» — графит: поле ленты плотнее клиентского, плашка
   признака общая нейтральная (В2), служебные слова на ней — вторичный текст
   на ступень громче клиентских: закрытый канал говорит настойчивее. */
.chat-channel--object {
  --chat-chan-bg: rgb(var(--neutral-100) / 1);
  --chat-chan-edge: rgb(var(--neutral-300) / 1);
  --chat-chan-flag-fg: rgb(var(--neutral-700) / 1);
  /* Гриф исполнителей печатается графитом по нейтральному листу
     (--neutral-800 по --surface-0): 14,63:1 в светлой, 11,61:1 в тёмной.
     Плитой он был до ЧР-4 — теперь плита снята из грамматики целиком. */
  --chat-chan-stamp-ink: rgb(var(--neutral-800) / 1);
  --chat-chan-who-fg: rgb(var(--neutral-900) / 1);
}

/* Канал «Офис» (Ч2-В) — третья ступень той же лестницы: штаб, которого нет
   ни в ЛК, ни в приложении прораба. Поле ленты — третья ступень шкалы
   (--neutral-200 после --neutral-50 клиента и --neutral-100 исполнителей):
   поле обязано отличаться от соседей, потому что оно одно из ТРЁХ мест, где
   канал назван (кнопка переключателя, поле ленты, штамп — Режим 1, Ч2-Р4).
   Дальше офис отличают штамп чернилами самого дна шкалы и собственные блоки
   во всех трёх отделках.

   ЗАЛИТОЙ ЦЕЛИКОМ строка-признак у офиса БЫЛА до приёмки ЧР (--neutral-800,
   светлые буквы 14,63:1) — «последняя строка перед полем ввода, и на ней
   написано, что дальше буквы не уйдут никуда». Вердикт В2 плиту снял вместе
   с остальными заливками плашки: под плитой перечень читателей набирался
   единственной парой, какая на ней была (--surface-0 и там, и там), то есть
   развод ЧР-7 у офиса не наступал по построению. Теперь лист общий, а
   «дальше не уйдут» говорят гриф и двойная линейка.

   Гриф офиса печатается по НЕЙТРАЛЬНОМУ листу, и чернила ему нужны тёмные:
   --neutral-900 по --surface-0 — 17,85:1 в светлой, 13,21:1 в тёмной. До В2
   он был --surface-0 (светлым по плите), и на новом листе исчез бы совсем
   (1,00:1) — это тот случай, о котором вердикт говорил отдельно: «если штамп
   терял контраст на нейтральном листе — привести по AA». Пара
   --neutral-900 / --surface-0 переворачивается инверсией сама, и второго
   набора правил ей, в отличие от прежней плиты, не нужно. */
.chat-channel--staff {
  --chat-chan-bg: rgb(var(--neutral-200) / 1);
  --chat-chan-edge: rgb(var(--neutral-300) / 1);
  /* Служебные слова — тот же вторичный текст, что у исполнителей: лист один,
     и третьей ступени чернил на нём взять неоткуда. Канал различает штамп. */
  --chat-chan-flag-fg: rgb(var(--neutral-700) / 1);
  --chat-chan-stamp-ink: rgb(var(--neutral-900) / 1);
  --chat-chan-who-fg: rgb(var(--neutral-900) / 1);
}

/* Тёмная: ПОЛЕ офиса — единственная пара, которую инверсия ломает.
   --neutral-200 в тёмной это #3d372a (рамки): поле стало бы светлее панели,
   ровно перевёрнутая лестница из уроков Ч2Б-3/4. Опускаем на --neutral-50
   (#1e1b15, ПОД панелью #262219 — тот же вынужденный низ шкалы, что у
   служебной бумаги и разворота журнала). Что поле офиса совпало в тёмной с
   клиентским (#1e1b15) — свойство палитры, а не выбор: ниже --neutral-50
   только --scrim, а он затемнение под модалками. Опаснее было бы совпасть в
   СВЕТЛОЙ, где поля и различают три канала; там ступени три и все свои.

   ПЛАШКИ, ЧЕРНИЛ И ГРИФА ЗДЕСЬ БОЛЬШЕ НЕТ (В2): все три токена стали парами
   шкалы (--surface-0, --neutral-700, --neutral-900) и переворачиваются
   инверсией сами. Именно эта тёмная пара весила (0,3,1) и перебивала блоки
   отделок (0,2,0) — из-за неё бумага и кромка повторяли свои чернила по
   второму разу. Повторов ниже тоже не осталось. Оба тёмных контекста, как у
   --chat-shadow (строки 60-65). */
html.themable[data-theme="dark"] .chat-channel--staff {
  --chat-chan-bg: rgb(var(--neutral-50) / 1);
}
@media (prefers-color-scheme: dark) {
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-channel--staff {
    --chat-chan-bg: rgb(var(--neutral-50) / 1);
  }
}

/* ПРАВИЛА «ШТАМП ОФИСА ТЕРЯЕТ ПЛАШКУ» ЗДЕСЬ БОЛЬШЕ НЕТ (ЧР-4, §3-А сборки
   Н-СБ). Оно снимало заливку у офисного признака, потому что плита
   --neutral-900 по плите --neutral-800 давала 1,22:1. Теперь заливки нет ни
   у одного канала: гриф — контур, и это свойство САМОГО ШТАМПА (см. базовое
   правило .chat-channel-stamp ниже). Снимать стало нечего, а правило-заплатка
   без своей болезни — второй источник правды о том, как выглядит штамп. */

/* Хвост Ч2: признак закрытого канала отделяется двойной линейкой — приём
   служебной бумаги, общий для всех отделок. Правило стоит в грамматике, а
   не в отделке: линейка называет КАНАЛ (закрытый), а не вкус читателя, и
   переезжать из отделки в отделку ей незачем. Цвет — тот же --chat-chan-edge,
   что у обычной линейки признака, поэтому отделки перекрашивают её вместе
   со всем остальным, не зная о ней. Офис (Ч2-В) закрыт ещё плотнее объекта —
   линейка ему та же и по той же причине. */
.chat-channel--object .chat-channel-flag,
.chat-channel--staff .chat-channel-flag { border-top: 3px double var(--chat-chan-edge); }

.chat-channel-feed { background: var(--chat-chan-bg); }

/* ── Строка-признак «кто увидит» ────────────────────────────────────────
   Не подсказка и не тултип: строка стоит между лентой и полем ввода
   всегда и называет читателей ПОИМЁННО (chat.channel_audience). На узкой
   колонке карточки перечень переносится на вторую строку, а не прячется —
   он здесь и есть самое ценное. */
.chat-channel-flag {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 11px;
  padding: 9px 16px;
  background: var(--chat-chan-flag-bg);
  border-top: 1px solid var(--chat-chan-edge);
  color: var(--chat-chan-flag-fg);
  font-size: 12.5px;
  line-height: 1.4;
}
/* ГРИФ — теперь свойство ШТАМПА, а не отделки «Бумага» (ЧР-4, §3-А сборки
   Н-СБ). Рисунок один: двойная рамка (контур 2 px плюс кольцо 1 px, отбитое
   на 3 px) с поворотом −1,1°, без заливки, всё одними чернилами.

   ПОЧЕМУ ПОДНЯТ ИЗ ОТДЕЛКИ. Вердикт гейта ЧР-2 требует плашку «наклонную, в
   двойной рамке» и требует её ВИДЕТЬ. В проде этот рисунок жил ровно в одном
   месте — в правиле «Бумаги» (секция отделок ниже), — а умолчание прода
   «Кант» (ui_prefs.CHAT_SKIN_DEFAULT) рамку у штампа снимало СОВСЕМ. Под
   умолчанием вердикт не выполнился бы ни разу. Гриф — знак того, куда уйдёт
   письмо; вкус читателя (отделка) на него влиять не должен.
   (Имена классов отделок здесь не набираются даже в комментарии: сторож
   test_skin_rules_never_address_base_chat_classes читает файл ТЕКСТОМ.)

   ЗАЛИВКИ НЕТ НИ У ОДНОГО КАНАЛА, и это не потеря веса: у закрытых каналов
   плита и так гасла — у офиса её снимала грамматика (плита по плите 1,22:1),
   у канта и бумаги отделки. Контур с кольцом весит не меньше плиты, а читается
   на любом фоне: 6,6:1 клиент, 12,9:1 исполнители, 14,63:1 офис в светлой
   (11,73:1 в тёмной, см. блоки токенов выше).

   ПОВОРОТ И КОЛЬЦО В ПОТОК НЕ ЛЕЗУТ: transform раскладку не двигает, а
   outline рисуется поверх — 3 px отступа укладываются в 9 px паддинга
   строки-признака и на узкой колонке карточки за край не выходят (проверено
   на бумаге с Ч2-Б, геометрия та же). */
.chat-channel-stamp {
  flex: none;
  font-family: var(--chat-face-narrow);
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.4px;
  /* Узкий капс с трекингом съедает пробел: «КЛИЕНТ ЭТОГО НЕ ВИДИТ» на
     кадре читалось как «НЕВИДИТ» (замер 3× после фикса гарнитуры). Слову
     возвращаем зазор — на самой важной строке продукта склейка недопустима. */
  word-spacing: 0.28em;
  text-transform: uppercase;
  padding: 5px 9px 4px;
  background: transparent;
  color: var(--chat-chan-stamp-ink);
  border: 2px solid var(--chat-chan-stamp-ink);
  border-radius: 0;
  outline: 1px solid var(--chat-chan-stamp-ink);
  outline-offset: 3px;
  transform: rotate(-1.1deg);
}

/* Флажок «ЧИТАЮТ» — форма Ч4 (pwa/chat_thread.html, выбор User на гейте
   Ч4-2). Служебное слово стоит СЛЕВА узким капсом и отдельным атомом:
   читателя не заставляют разбирать, где подпись, а где фамилия. До ЧР-4 в
   CRM оно было приклеено к перечню строкой «Читают: Иванов, Петров». */
.chat-channel-lbl {
  flex: none;
  align-self: flex-start;
  padding-top: 2px;
  font-family: var(--chat-face-narrow);
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.7px;
  text-transform: uppercase;
  color: var(--chat-chan-flag-fg);
}
/* ПЕРЕЧЕНЬ ЧИТАТЕЛЕЙ — СВОИ ЧЕРНИЛА (ЧР-7, хвост ЧР4-m-5; эталон
   НСБ_хаб.html: `.chat-channel-who { color: var(--chat-chan-who-fg) }`).

   Правило развода, по которому объявлены ВСЕ сцены: `--chat-chan-flag-fg` —
   чернила СЛУЖЕБНЫХ слов строки («ЧИТАЮТ», хвост-поверхности),
   `--chat-chan-who-fg` — чернила ПОЛНОЙ СИЛЫ той же семьи, которыми набраны
   ИМЕНА. До приёмки ЧР развод наступал НЕ ВЕЗДЕ: там, где плашку заливал
   канал или отделка (кант, плита офиса, сепия, грифель), полнее чернил
   плиты не было, и токены совпадали «по построению» — под умолчанием прода
   имена читались ровно так же тихо, как служебные слова (ЧР7-m-1).

   С вердиктом В2 плашка стала ОДНИМ нейтральным листом во всех каналах и
   под всеми отделками, и совпадений не осталось НИ ОДНОГО: имена везде
   --neutral-900, служебные слова везде вторичный текст шкалы (--neutral-500
   у клиента, --neutral-700 у закрытых каналов). Отдельного «объявлено явно,
   что совпадает» больше не требуется — совпадать нечему.

   Замер всех 26 сцен (3 отделки × 3 канала × 2 темы = 18, плюс восемь
   изводов офиса) — сторож tests/browser/test_chat_ink.py: он строит эти
   сцены В БРАУЗЕРЕ (каскад считает он, а не разбор текста) и меряет
   computed style. Таблица сцен там теперь односторонняя по разводу и
   двусторонняя по ЗАЛИВКЕ: у всех 26 фон плашки обязан быть одним и тем же
   листом темы. */
.chat-channel-who {
  min-width: 0;
  overflow-wrap: break-word;
  color: var(--chat-chan-who-fg);
}
/* Хвост-поверхности — СВОЕЙ строкой под именами (форма Ч4): имена и места,
   где их прочитают, отвечают на разные вопросы, и в одну строку через точку
   они читались как продолжение фамилии. Свой класс, а не тег <s> (ревью
   ЧР-4, m-2): у <s> в HTML есть смысл — «более не актуально», обратный
   тому, что тут написано. */
.chat-channel-tail {
  display: block;
  margin-top: 3px;
  font-family: var(--chat-face-narrow);
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.4px;
  word-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--chat-chan-flag-fg);
}

/* ── Переключатель каналов ──────────────────────────────────────────────
   Грамматика сегмента дашборда (_theme_switch.html): группа в рамке,
   состояние — aria-pressed, а не класс-подсветка. Активная кнопка носит
   цвет своего канала: переключатель работает ключом к полю ленты. */
.chat-seg {
  display: inline-flex;
  /* Перенос (В5б): кнопок в сегменте стало три и подписи у них длиннее
     («ИСПОЛНИТЕЛЯМ», «ЧЕРНОВИК»), а мест у сегмента прибавилось — узкая
     колонка карточки объекта и панель настроек, которая ниже lg занимает
     весь экран. Без переноса inline-flex вылезал бы за рамку группы. */
  flex-wrap: wrap;
  gap: 2px;
  padding: 2px;
  border: 1px solid rgb(var(--neutral-200) / 1);
  border-radius: 8px;
  background: rgb(var(--surface-0) / 1);
}
.chat-seg-btn {
  font-family: var(--chat-face-narrow);
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  padding: 8px 14px 7px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: rgb(var(--neutral-500) / 1);
  cursor: pointer;
  transition: background-color .12s, color .12s;
}
.chat-seg-btn:hover {
  background: rgb(var(--neutral-100) / 1);
  color: rgb(var(--neutral-900) / 1);
}
/* Нажатая кнопка сегмента вообще (переключатель отделки в шапке хаба, Ч2-Б):
   у него нет канала, красить нечем — берём брендовую подложку. Она же и есть
   цвет нажатого канала «Клиенту», поэтому своего правила ему не нужно;
   канальным остаётся один «Исполнителям» — он специфичнее (лишний атрибут
   [data-chat-kind]) и побеждает общее правило. */
.chat-seg-btn[aria-pressed="true"],
.chat-seg-btn[aria-pressed="true"]:hover {
  background: rgb(var(--brand-100) / 1);
  color: rgb(var(--brand-700) / 1);
}
.chat-seg-btn[data-chat-kind="object"][aria-pressed="true"],
.chat-seg-btn[data-chat-kind="object"][aria-pressed="true"]:hover {
  background: rgb(var(--neutral-800) / 1);
  color: rgb(var(--surface-0) / 1);
}
/* Офис (Ч2-В, обязательство ревью В2): нажатая кнопка носит цвет своего
   канала — ту же плиту --neutral-900, что штамп офиса. Без этого правила
   третья кнопка подсвечивалась бы охрой общего сегмента, то есть цветом
   канала «Клиенту», и переключатель врал бы о канале ровно там, где он и
   есть ключ к полю ленты (Режим 1 защиты, Ч2-Р4). Пара самопереворачивается,
   как объектовая; замер — 17,85:1 в светлой, 13,21:1 в тёмной. */
.chat-seg-btn[data-chat-kind="office"][aria-pressed="true"],
.chat-seg-btn[data-chat-kind="office"][aria-pressed="true"]:hover {
  background: rgb(var(--neutral-900) / 1);
  color: rgb(var(--surface-0) / 1);
}
.chat-seg-btn:focus-visible {
  outline: 2px solid rgb(var(--brand-500) / 1);
  outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  .chat-seg-btn { transition: none; }
}

/* Счётчик непрочитанного на кнопке канала (сегмент треда хаба, Ч3-3): в
   карточке объекта его нет — там канал один на странице и счётчик стоял бы
   рядом с самим разговором. В хабе сегмент отвечает и на второй вопрос —
   «где ещё говорят», и число обязано читаться, не притворяясь частью
   подписи: моноширинный набор без трекинга, как у счётчика чипа строки. */
.chat-seg-btn { display: inline-flex; align-items: center; gap: 7px; }
.chat-seg-count {
  font-family: var(--chat-face-mono);
  font-style: normal;
  font-size: 11px;
  letter-spacing: 0;
  padding: 2px 5px 1px;
  border-radius: 3px;
  background: rgb(var(--neutral-100) / 1);
  color: rgb(var(--neutral-900) / 1);
}
/* На нажатой кнопке фон канала уже занят — счётчик переворачивается, беря
   бумагу панели: на чернилах офиса светлый счётчик читается, тёмный сливается
   бы с плитой. Пара самопереворачивается вместе с бандлом. */
.chat-seg-btn[aria-pressed="true"] .chat-seg-count {
  background: rgb(var(--surface-0) / 1);
  color: rgb(var(--neutral-900) / 1);
}

/* ═══════════════════════════════════════════════════════════════════════
   ПАНЕЛЬ НАСТРОЕК ЧАТА — компоновка «РАЗВОРОТ»
   (этап ЧР, задача ЧР-6, 07.08.2026)

   Канон — Макеты/Чат_v2/мокапы_ЧР/ПАНЕЛЬ_1_Разворот.html (кадры
   ПАНЕЛЬ_1_Разворот_кадр-{светлая,тёмная}.png, сводка ПАНЕЛИ_README.md),
   выбран User'ом на мини-гейте ЧР-2Б. Все числа ниже сняты РЕНДЕРОМ
   эталона в Chromium 1440 × 900, а не с листа глазами.

   ЧТО ЗДЕСЬ ПЕРЕДЕЛАНО ПРОТИВ В5а. Панель была ящиком 448 px (lg:max-w-md)
   с пятью рядами в столбик; ЧР-6 сделал её РАЗВОРОТОМ: 700 × 803 справа,
   два столбца по два раздела, восемь групп разом — ни прокрутки, ни
   вкладок. Цена записана честно (ПАНЕЛИ_README, «чем платим»): список на
   время примерки сжимается в корешок 88 px, а лента остаётся 412 px —
   самая узкая из трёх компоновок гейта.

   ГАБАРИТ ПАНЕЛИ = РАБОЧЕЕ ПОЛЕ ХАБА, а не окно: панель встаёт РОВНО в
   рамку хаба (топбар сверху, футер снизу), иначе она висела бы над лентой
   и под ней одновременно. Этажи каркаса объявлены на двоих — см. .chat-hub
   в секции каркаса ЧР-3.

   ЗАМЕРЫ ЭТАЛОНА (1440 × 900, обе темы — геометрия у них общая):
     рабочее поле 1200 × 804 · панель 700 × 804 · столбцы 322,5 + 322,5
     при зазоре 18 · шапка 53 · подвал 54,7 · рельс-корешок 88 · лента 412.
   Наши: рабочее поле 1200 × 803 (футер CRM 41 против 40 у эталона),
   панель 700 × 803, лента 412. Сторожа — браузерные, двусторонние.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── ЯЩИК ПРОПУСКАЕТ КЛИКИ МИМО ПАНЕЛИ (ревью ЧР-6, M-1) ────────────────
   #chat-drawer — оверлей fixed inset-0 на все 1440 × 900. Для панели
   «Написать» это правильно: она забирает внимание целиком, и клик мимо
   закрывает. Для настроек — ровно наоборот: хаб под ними обязан быть ЖИВЫМ
   не на вид, а на самом деле.

   Первая редакция ЧР-6 оставила подложку прозрачной и сочла дело сделанным.
   Замер ревью: elementFromPoint над лентой и над списком возвращал подложку,
   клик по ленте ЗАКРЫВАЛ панель, колесо её не прокручивало, строку списка
   нельзя было выбрать. Прозрачность — это про цвет, а не про клики.

   Гейт :has() — та же идиома, что у отложенных рядов офиса: своих классов у
   ящика нет, а заводить их значило бы просить сервер помнить, что он в него
   положил. Панель забирает себе pointer-events обратно: сама она кликается
   как обычно, и Escape/«Готово»/крестик на месте. */
#chat-drawer:has([data-chat-settings]) { pointer-events: none; }
#chat-drawer [data-chat-settings] { pointer-events: auto; }

/* ── Оболочка панели ────────────────────────────────────────────────────
   position: absolute внутри #chat-drawer (он fixed inset-0), поэтому
   top/bottom считаются от окна — те же этажи, что у .chat-hub.

   max-width: 100% — не «на всякий случай»: на 1024…1100 px рабочее поле
   уже 700 px, и панель без ограничителя вылезла бы за экран, унося с собой
   крестик. Узкий режим (<1024) переопределяет всё это ниже. */
.chat-set-panel {
  position: absolute;
  top: var(--chat-hub-top);
  bottom: calc(var(--chat-hub-foot) + env(safe-area-inset-bottom, 0px));
  right: 0;
  width: 700px;
  max-width: 100%;
  min-height: 0;
  background: rgb(var(--surface-0) / 1);
  border-left: 1px solid rgb(var(--neutral-200) / 1);
  box-shadow: -18px 0 44px -26px rgb(var(--scrim) / 0.5);
}
html.themable[data-theme="dark"] .chat-set-panel {
  box-shadow: -18px 0 44px -22px rgb(var(--scrim) / 0.75);
}
@media (prefers-color-scheme: dark) {
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-set-panel {
    box-shadow: -18px 0 44px -22px rgb(var(--scrim) / 0.75);
  }
}

/* Шапка: имя, строка механики, крестик. Строка механики стоит МЕЖДУ ними и
   занимает остаток — она отвечает на вопрос, который панель раньше задавала
   собой («почему закрылось?»), и прятать её в подвал значило бы отвечать
   после того, как спросили. */
.chat-set-top {
  flex: none;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 18px;
  border-bottom: 1px solid rgb(var(--neutral-200) / 1);
}
.chat-set-top h2 {
  flex: none;
  font-size: 16px;
  font-weight: 700;
  color: rgb(var(--neutral-900) / 1);
}
.chat-set-top p {
  flex: 1;
  min-width: 0;
  font-size: 12.5px;
  line-height: 1.3;
  color: rgb(var(--neutral-600) / 1);
}
.chat-set-x {
  flex: none;
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 6px;
  background: none;
  color: rgb(var(--neutral-500) / 1);
  cursor: pointer;
}
.chat-set-x:hover {
  background: rgb(var(--neutral-100) / 1);
  color: rgb(var(--neutral-900) / 1);
}
.chat-set-x svg { width: 17px; height: 17px; }

/* Тело — ДВА СТОЛБЦА и overflow: hidden (эталон). Прокрутки здесь нет
   намеренно: обещание компоновки — «все восемь групп разом», и полоса
   прокрутки означала бы, что обещание не выполнено. Если группы перестанут
   помещаться, чинить надо группы, а не прятать их под скролл — сторож
   ловит это ростом высоты содержимого над высотой тела. */
.chat-set-body {
  flex: 1;
  min-height: 0;
  overflow: hidden;
  padding: 15px 18px 8px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 18px;
  align-content: start;
}
.chat-set-col { min-width: 0; }
/* Ритм столбца посчитан ПОД ВЫСОТУ, а не подобран: восемь групп обязаны
   лечь в 670 px содержимого тела (803 панели − 53 шапки − 55 подвала − 23
   полей). Разъезд на два пикселя здесь стоит прокрутки, то есть главного
   обещания компоновки, — сторож меряет это в браузере. */
.chat-set-col .chat-set-sect + .chat-set-sect { margin-top: 10px; }
.chat-set-sect .chat-set-grp + .chat-set-grp { margin-top: 9px; }

/* Заголовок раздела — ЛИНЕЙКА С ПОДПИСЬЮ, а не кнопка: разделы группируют
   и только. Форма — узкий капс дашборда (подпись секции «Требует
   действия»), продолжение линейкой вправо. */
.chat-set-cap {
  display: flex;
  align-items: center;
  gap: 9px;
  margin: 0 0 7px;
  font-family: var(--chat-face-narrow);
  font-size: 10.5px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 2px;
  word-spacing: 0.28em;
  text-transform: uppercase;
  color: rgb(var(--neutral-500) / 1);
}
.chat-set-cap::after {
  content: "";
  flex: 1;
  height: 1px;
  background: rgb(var(--neutral-200) / 1);
}

/* Голова группы: имя и одна строка про то, ЧТО настраивается (не про то,
   что выбрано, — это говорит подсказка под рядом). */
.chat-set-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 4px;
}
.chat-set-head h3 {
  flex: none;
  font-size: 14px;
  font-weight: 700;
  color: rgb(var(--neutral-900) / 1);
}
/* Пояснение группы — ОДНОЙ СТРОКОЙ И ВСЕГДА. Перенос стоит 24 px, а групп
   восемь: три перенёсшихся заголовка и есть та прокрутка, которой в
   «Развороте» быть не должно. 11 px хватает всем нынешним словам целиком
   (самое длинное — «Чем форма отправки называет свой канал.»), многоточие
   тут страховка от будущей правки слов, а не рабочий режим. */
.chat-set-head p {
  flex: 1;
  min-width: 0;
  font-size: 11px;
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: rgb(var(--neutral-500) / 1);
}
/* Метка «новая» — у трёх групп ЧР-6. Плашка чернилами: она про то, что
   этого раньше не было, и жить ей ровно до следующего этапа. */
.chat-set-new {
  font-style: normal;
  font-family: var(--chat-face-narrow);
  font-size: 9.5px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.3px;
  text-transform: uppercase;
  padding: 3px 5px 2px;
  margin-left: 5px;
  border-radius: 2px;
  background: rgb(var(--neutral-900) / 1);
  color: rgb(var(--surface-0) / 1);
  vertical-align: 2px;
}

/* ПОДСКАЗКА — ОДНА СТРОКА ПОД ГРУППОЙ, У ВЫБРАННОГО (эталон, отличие от
   прода). До ЧР-6 подсказки жили только в title и читались курсором: чтобы
   узнать, что такое «Велень», надо было навести и подождать. Теперь панель
   говорит про выбранное вслух, а title остаётся у каждой кнопки — для
   соседних вариантов, на которые ещё не нажали. */
.chat-set-hint {
  margin-top: 4px;
  padding: 2px 7px;
  border-radius: 4px;
  /* 11 px, как подпись плитки: подсказок восемь, и каждая лишняя строка
     здесь умножается на восемь — ровно из этих строк и складывалась
     прокрутка, которой в «Развороте» быть не должно. */
  font-size: 11px;
  line-height: 1.35;
  background: rgb(var(--neutral-50) / 1);
  color: rgb(var(--neutral-600) / 1);
}

/* Подвал: «Готово» и слова о том, что панель осталась открытой не по
   ошибке. Кнопка закрывает тем же data-chat-drawer-close, что крестик, —
   одна дверь, два входа. */
.chat-set-foot {
  flex: none;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 18px;
  border-top: 1px solid rgb(var(--neutral-200) / 1);
}
.chat-set-done {
  flex: none;
  border: 0;
  border-radius: 6px;
  padding: 9px 18px 8px;
  background: rgb(var(--brand-500) / 1);
  color: rgb(var(--brand-on) / 1);
  font-family: var(--chat-face-narrow);
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 1.4px;
  word-spacing: 0.2em;
  text-transform: uppercase;
  cursor: pointer;
}
.chat-set-done:hover { background: rgb(var(--brand-600) / 1); }
.chat-set-foot span {
  font-size: 12.5px;
  line-height: 1.35;
  color: rgb(var(--neutral-600) / 1);
}
.chat-set-done:focus-visible,
.chat-set-x:focus-visible {
  outline: 2px solid rgb(var(--brand-500) / 1);
  outline-offset: 2px;
}

/* ── Ряд вариантов ──────────────────────────────────────────────────────
   Ряды панели — не сегмент-строка шапки, а ПЛИТКИ: подпись под мини-видом.
   Гейт .chat-set-opts, а не :has(.chat-swatch), как было в В5а: с ЧР-6
   образцы есть у ВСЕХ восьми рядов (вердикт User по «Защите отправки»), и
   различать ряды по наличию плитки больше нечем — да и не нужно. Класс
   стоит на форме панели и только там: сегмент каналов карточки объекта
   (.chat-seg без него) остаётся строкой. */
.chat-set-opts {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.chat-set-opts .chat-seg-btn {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 3px;
  /* 11 px: «С ПОДТВЕРЖДЕНИЕМ» узким капсом 12-м кеглем не влезает в плитку
     88 px и переносится на третью строку — та же причина, что была у
     подписи плитки в В5в. */
  font-size: 11px;
  line-height: 1.2;
  letter-spacing: 1.1px;
  white-space: normal;
  text-align: center;
}

/* ВЫБРАННОЕ ПОМЕЧЕНО ДВАЖДЫ (эталон): охряная рамка И знак-галочка в углу.
   Рамка одна на обе темы, но на тёмной она соседствует с тёмными плитками
   и читается слабее — знак же виден всегда и каким бы ни был образец.
   Дублирование намеренное: выбор вида — единственное место панели, где
   ошибиться значит смотреть не на то, что настраиваешь. */
.chat-set-opts .chat-seg-btn[aria-pressed="true"] {
  box-shadow: inset 0 0 0 1px rgb(var(--brand-500) / 1);
}
.chat-set-opts .chat-seg-btn::before,
.chat-set-opts .chat-seg-btn::after {
  content: "";
  position: absolute;
  display: none;
}
/* Диск знака — ::before, сама галочка — ::after: угол из двух рамок,
   повёрнутый на 45°. Своего элемента в разметке у знака нет и не нужно —
   кнопка обязана оставаться ярлыком, и лишнее слово в её имени читал бы
   вслух скринридер (образец и знак равно декоративны, состояние говорит
   aria-pressed). */
.chat-set-opts .chat-seg-btn::before {
  right: -5px;
  top: -5px;
  width: 17px;
  height: 17px;
  border-radius: 999px;
  background: rgb(var(--brand-500) / 1);
}
.chat-set-opts .chat-seg-btn::after {
  right: 0.5px;
  top: 1px;
  width: 7px;
  height: 4px;
  border-left: 2px solid rgb(var(--brand-on) / 1);
  border-bottom: 2px solid rgb(var(--brand-on) / 1);
  transform: rotate(-45deg);
}
.chat-set-opts .chat-seg-btn[aria-pressed="true"]::before,
.chat-set-opts .chat-seg-btn[aria-pressed="true"]::after { display: block; }

/* ── Ряды офиса при чужой отделке (В5а, перенесено в ЧР-6) ──────────────
   Вариантов офиса шесть, а показан из них тот, чью отделку человек выбрал
   строкой выше. Ряд «Офис в Канте» при выбранной «Бумаге» настраивает то,
   чего сейчас на экране нет, — и обязан выглядеть отложенным, не
   выключенным (менять его законно: сочетание подбирают вживую).

   ГЕЙТ БЕЗ JS: :has() читает aria-pressed соседнего ряда — тот самый
   атрибут, которым состояние уже выражено. С ЧР-6 адресация стала
   ЧЕСТНЫМ АТРИБУТОМ ГРУППЫ (data-chat-set-grp), а не aria-label с
   кавычками-ёлочками: ярлыки — слова продукта, и правка слов не должна
   ронять раскладку панели.

   ПРИГЛУШЕНИЕ — НЕ ПРОЗРАЧНОСТЬ. opacity увела бы пояснение ряда (11 px,
   --neutral-500, 4,76:1 к белому) ниже порога AA. Гаснет ЗНАК ВЫБОРА:
   нажатая кнопка теряет охру и становится нейтральной плашкой
   (--neutral-600 по --neutral-100 = 6,92:1 светлая, 7,06:1 тёмная),
   заголовок садится с --neutral-900 на --neutral-500 (4,76:1 — AA для
   14 px). Контраст держится, а «сейчас работает не это» видно с
   полувзгляда. ОБРАЗЦЫ НЕ ГАСЯТ (решение колоды В5в): состояние приглушать
   можно, информацию — нет. */
[data-chat-settings] [data-chat-set-grp="okant"] .chat-set-head h3,
[data-chat-settings] [data-chat-set-grp="obum"] .chat-set-head h3 {
  color: rgb(var(--neutral-500) / 1);
}
[data-chat-settings] [data-chat-set-grp="okant"] .chat-seg-btn[aria-pressed="true"],
[data-chat-settings] [data-chat-set-grp="obum"] .chat-seg-btn[aria-pressed="true"] {
  background: rgb(var(--neutral-100) / 1);
  color: rgb(var(--neutral-600) / 1);
  box-shadow: none;
}
[data-chat-settings] [data-chat-set-grp="okant"] .chat-seg-btn[aria-pressed="true"]::before,
[data-chat-settings] [data-chat-set-grp="obum"] .chat-seg-btn[aria-pressed="true"]::before {
  background: rgb(var(--neutral-400) / 1);
}
/* Отделка выбрана — ряд оживает. Специфичность :has() считается по самому
   тяжёлому аргументу, поэтому оживляющее правило заведомо тяжелее
   приглушающего. */
[data-chat-settings]:has([data-chat-set-grp="skin"] [value="kant"][aria-pressed="true"]) [data-chat-set-grp="okant"] .chat-set-head h3,
[data-chat-settings]:has([data-chat-set-grp="skin"] [value="bumaga"][aria-pressed="true"]) [data-chat-set-grp="obum"] .chat-set-head h3 {
  color: rgb(var(--neutral-900) / 1);
}
[data-chat-settings]:has([data-chat-set-grp="skin"] [value="kant"][aria-pressed="true"]) [data-chat-set-grp="okant"] .chat-seg-btn[aria-pressed="true"],
[data-chat-settings]:has([data-chat-set-grp="skin"] [value="bumaga"][aria-pressed="true"]) [data-chat-set-grp="obum"] .chat-seg-btn[aria-pressed="true"] {
  background: rgb(var(--brand-100) / 1);
  color: rgb(var(--brand-700) / 1);
  box-shadow: inset 0 0 0 1px rgb(var(--brand-500) / 1);
}
[data-chat-settings]:has([data-chat-set-grp="skin"] [value="kant"][aria-pressed="true"]) [data-chat-set-grp="okant"] .chat-seg-btn[aria-pressed="true"]::before,
[data-chat-settings]:has([data-chat-set-grp="skin"] [value="bumaga"][aria-pressed="true"]) [data-chat-set-grp="obum"] .chat-seg-btn[aria-pressed="true"]::before {
  background: rgb(var(--brand-500) / 1);
}

/* ═══════════════════════════════════════════════════════════════════════
   ОБРАЗЦЫ-ПЛИТКИ (Ф-8 разбора ЧР-1, перерисованы в ЧР-6)

   БЫЛО (В5в): плитка 72 × 44 — абстрактный прямоугольник с одной
   карточкой. Разбор ЧР-1 (находка Ф-8) записал: на светлой теме отделки
   почти неразличимы, и «выбираю по картинке» не работает.

   СТАЛО: плитка — КУСОК ЛЕНТЫ. Поле канала, две реплики со строками
   текста, кант / линейки / клетка бумаги. 90 × 74 у групп, которые красят
   ВЕСЬ экран во всех каналах («Лента» и «Отделка треда»), 88 × 56 у
   остальных — они настраивают по одному месту. Это 2,1 × прежней плитки.

   ВСЯ ЖИВОПИСЬ ЗДЕСЬ, В РАЗМЕТКЕ — ПУСТЫЕ <i>. Ни картинки, ни инлайнового
   стиля: образец обязан брать ТЕ ЖЕ ТОКЕНЫ, что сама лента, иначе
   разойдётся с ней при первой правке отделки, а в тёмной теме — сразу.

   ГЕОМЕТРИЯ В ПРОЦЕНТАХ, а не в единицах-множителях (как было в В5в): один
   набор правил обслуживает и 90 × 74, и 88 × 56, и любую будущую плитку.
   Прежний приём с --chat-swatch-u требовал пересчёта всех множителей при
   каждой смене габарита — и уже один раз молча ронял объявления (px² в
   calc, грабля колоды В5в).

   ПОЧЕМУ ЗДЕСЬ, А НЕ В СЕКЦИИ ОТДЕЛОК. Секция отделок охраняется сплошным
   гейтом: каждый её селектор обязан нести класс отделки, потому что этот
   класс носит контейнер канала в CRM и только он. Образец — кнопка ПАНЕЛИ,
   такого класса у неё нет и быть не должно; место образцов — грамматика,
   до маркера секции (сторож — test_swatch_rules_live_in_the_grammar…).

   СЛАГ ОБРАЗЦА НЕСЁТ ГРУППУ (chat-swatch--<группа>-<слаг>). До ЧР-6 хватало
   слага: они были уникальны на всю панель. Новая группа «Лента» принесла
   второй «Журнал», и одного слага перестало хватать — плитка ленты и
   плитка отделки нарисованы по-разному и обязаны различаться в CSS.
   ═══════════════════════════════════════════════════════════════════════ */

.chat-swatch {
  --chat-sw-w: 88px;
  --chat-sw-h: 56px;
  position: relative;
  display: block;
  flex: none;
  width: var(--chat-sw-w);
  height: var(--chat-sw-h);
  overflow: hidden;
  border-radius: 3px;
  border: 1px solid rgb(var(--neutral-300) / 1);
  /* Поле образца — панель, на которой лежит лента. */
  background-color: rgb(var(--surface-0) / 1);
}
/* Крупная плитка — у двух групп, которые красят весь экран во всех каналах
   (эталон: «Ф-8 назван именно про них»). */
[data-chat-set-grp="lenta"] .chat-swatch,
[data-chat-set-grp="skin"] .chat-swatch {
  --chat-sw-w: 90px;
  --chat-sw-h: 74px;
}
.chat-swatch i {
  position: absolute;
  display: block;
}

/* Реплики. b1 — чужая (белая, скруглена влево), b2 — своя (охряная,
   скруглена вправо): ровно то деление, которым лента и читается. Строки
   текста рисуются рамками псевдоэлементов — своего элемента у строки нет,
   и заводить его ради образца незачем. */
.chat-swatch i::before,
.chat-swatch i::after {
  content: "";
  position: absolute;
  height: 8%;
  min-height: 2px;
  border-radius: 1px;
  background: rgb(var(--neutral-500) / 0.55);
}
.chat-swatch i::before { left: 9%; right: 13%; top: 25%; }
.chat-swatch i::after { left: 9%; right: 45%; top: 57%; }
.chat-swatch .s-b1 {
  left: 16%;
  top: 11%;
  width: 52%;
  height: 36%;
  border: 1px solid rgb(var(--neutral-300) / 1);
  background: rgb(var(--surface-0) / 1);
  border-radius: 6px 6px 6px 1px;
}
.chat-swatch .s-b2 {
  right: 5%;
  top: 53%;
  width: 52%;
  height: 36%;
  border: 1px solid rgb(var(--brand-300) / 1);
  background: rgb(var(--brand-100) / 1);
  border-radius: 6px 6px 1px 6px;
}
.chat-swatch .s-b2::before,
.chat-swatch .s-b2::after { background: rgb(var(--brand-700) / 0.5); }
/* Полосы, линейки и поля строк своих ::before/::after не рисуют. */
.chat-swatch .s-band::before, .chat-swatch .s-band::after,
.chat-swatch .s-rule::before, .chat-swatch .s-rule::after,
.chat-swatch .s-marg::before, .chat-swatch .s-marg::after,
.chat-swatch .s-btn::before, .chat-swatch .s-btn::after,
.chat-swatch .s-stamp::before, .chat-swatch .s-stamp::after,
.chat-swatch [class^="s-q"]::before, .chat-swatch [class^="s-q"]::after,
.chat-swatch [class^="s-s"]::before, .chat-swatch [class^="s-s"]::after,
.chat-swatch .s-chip::after,
.chat-swatch .s-z1::before, .chat-swatch .s-z1::after,
.chat-swatch .s-z3::before, .chat-swatch .s-z3::after { content: none; }

/* ── Группа «Лента»: расстановка писем ─────────────────────────────────
   «Беседа» — реплики по разным сторонам (базовый рисунок выше, левая чуть
   левее). «Журнал» — все три письма ОДНОЙ колонкой слева: ровно то, что
   настройка и делает. */
.chat-swatch--lenta-beseda .s-b1 { left: 6%; width: 56%; }
.chat-swatch--lenta-zhurnal .s-b1 { left: 6%; width: 62%; top: 8%; height: 24%; }
.chat-swatch--lenta-zhurnal .s-b2 {
  left: 6%;
  right: auto;
  width: 62%;
  top: 38%;
  height: 24%;
  border-radius: 6px 6px 6px 1px;
}
.chat-swatch--lenta-zhurnal .s-b3 {
  left: 6%;
  width: 48%;
  top: 68%;
  height: 24%;
  border: 1px solid rgb(var(--neutral-300) / 1);
  background: rgb(var(--surface-0) / 1);
  border-radius: 6px 6px 6px 1px;
}

/* ── Группа «Отделка треда» ────────────────────────────────────────────
   Кант — охряная полоса канала слева и цветные кромки у пузырей. Бумага —
   служебная клетка на тёплом листе, пузыри без скруглений. Журнал —
   холодный разворот: пузырей нет вовсе, записи держатся линейками графы.
   Период клетки сжат к размеру образца намеренно: настоящие 8 px дали бы
   в окошке 90 px одну линию, и «клетка» не прочиталась бы. */
.chat-swatch--skin-kant .s-band {
  left: 0;
  top: 0;
  bottom: 0;
  width: 11%;
  background: rgb(var(--brand-500) / 1);
}
.chat-swatch--skin-kant .s-b1 { border-left: 3px solid rgb(var(--brand-400) / 1); }
.chat-swatch--skin-kant .s-b2 { border-right: 3px solid rgb(var(--brand-500) / 1); }
.chat-swatch--skin-bumaga {
  background-color: rgb(var(--brand-50) / 1);
  background-image:
    repeating-linear-gradient(0deg, transparent 0 7px,
      rgb(var(--brand-400) / 0.3) 7px 8px),
    repeating-linear-gradient(90deg, transparent 0 7px,
      rgb(var(--brand-400) / 0.3) 7px 8px);
}
.chat-swatch--skin-bumaga .s-b1,
.chat-swatch--skin-bumaga .s-b2 { border-radius: 2px; }
.chat-swatch--skin-bumaga .s-b1 { left: 8%; width: 56%; }
.chat-swatch--skin-zhurnal { background-color: rgb(var(--neutral-100) / 1); }
.chat-swatch--skin-zhurnal .s-b1 {
  left: 8%;
  width: 60%;
  top: 8%;
  height: 32%;
  background: none;
  border: 0;
  border-left: 3px solid rgb(var(--neutral-500) / 1);
  border-radius: 0;
}
.chat-swatch--skin-zhurnal .s-b2 {
  right: 6%;
  width: 60%;
  top: 56%;
  height: 32%;
  background: none;
  border: 0;
  border-right: 3px solid rgb(var(--brand-500) / 1);
  border-radius: 0;
}
.chat-swatch--skin-zhurnal .s-rule {
  left: 5%;
  right: 5%;
  top: 48%;
  height: 1px;
  background: rgb(var(--neutral-400) / 1);
}

/* ── Группы офиса: реплики НЕЙТРАЛЬНЫЕ ─────────────────────────────────
   Офисный канал охры не носит (цвет значит канал, а не «моё письмо»), и
   вторая реплика на его плитке серая: различают изводы полоса и сорт
   листа, а не сторона. */
[data-chat-set-grp="okant"] .s-b2,
[data-chat-set-grp="obum"] .s-b2 {
  background: rgb(var(--neutral-100) / 1);
  border-color: rgb(var(--neutral-300) / 1);
}
[data-chat-set-grp="okant"] .s-b2::before,
[data-chat-set-grp="okant"] .s-b2::after,
[data-chat-set-grp="obum"] .s-b2::before,
[data-chat-set-grp="obum"] .s-b2::after {
  background: rgb(var(--neutral-500) / 0.55);
}
[data-chat-set-grp="okant"] .s-band {
  left: 0;
  top: 0;
  bottom: 0;
  width: 11%;
}
.chat-swatch--okant-sepia .s-band { background: rgb(var(--brand-800) / 1); }
.chat-swatch--okant-grifel .s-band { background: rgb(var(--neutral-800) / 1); }
.chat-swatch--okant-kromka .s-band {
  background: rgb(var(--neutral-50) / 1);
  border-right: 2px dashed rgb(var(--neutral-500) / 1);
}
.chat-swatch--obum-chernovik {
  background-color: rgb(var(--brand-200) / 1);
  background-image: repeating-linear-gradient(45deg,
    rgb(var(--neutral-900) / 0.07) 0 3px, transparent 3px 8px);
}
.chat-swatch--obum-velen { background-color: rgb(var(--brand-300) / 1); }
.chat-swatch--obum-pole { background-color: rgb(var(--brand-50) / 1); }
.chat-swatch--obum-pole .s-marg {
  right: 17%;
  top: 0;
  bottom: 0;
  width: 0;
  border-left: 2px dashed rgb(var(--neutral-500) / 1);
}
.chat-swatch--obum-pole .s-b1 { left: 6%; width: 52%; }
.chat-swatch--obum-pole .s-b2 { right: 22%; width: 52%; }
.chat-swatch--obum-chernovik .s-b1,
.chat-swatch--obum-velen .s-b1 { left: 6%; width: 56%; }

/* ── Группы «Композер» и «Защита отправки»: рисуем ФОРМУ ───────────────
   Эти две плитки показывают не ленту, а низ экрана: поле ввода и кнопку.
   Мини-вид ленты на них соврал бы — слова «Чернила» и «Штемпель» отвечают
   на вопрос «как выглядит форма отправки». */
[data-chat-set-grp="comp"] .s-fld,
[data-chat-set-grp="guard"] .s-fld {
  left: 7%;
  right: 32%;
  top: 30%;
  bottom: 22%;
  border-radius: 4px;
  border: 1px solid rgb(var(--neutral-300) / 1);
  background: rgb(var(--surface-0) / 1);
}
[data-chat-set-grp="comp"] .s-btn,
[data-chat-set-grp="guard"] .s-btn {
  right: 7%;
  top: 30%;
  bottom: 22%;
  width: 20%;
  border-radius: 4px;
  background: rgb(var(--brand-500) / 1);
}
.chat-swatch--comp-chernila .s-fld { border: 2px solid rgb(var(--brand-500) / 1); }
.chat-swatch--comp-blank { background-color: rgb(var(--brand-50) / 1); }
.chat-swatch--comp-blank .s-btn { background: rgb(var(--brand-600) / 1); }
.chat-swatch--comp-shtempel .s-rule {
  left: 0;
  right: 0;
  top: 10%;
  height: 9%;
  background: rgb(var(--brand-600) / 1);
}
.chat-swatch--comp-shtempel .s-btn {
  background: none;
  border: 2px solid rgb(var(--brand-600) / 1);
}

/* ЗАЩИТА ОТПРАВКИ ПОЛУЧИЛА ОБРАЗЦЫ (вердикт User, мини-гейт ЧР-2Б).
   До ЧР-6 ряд стоял без плиток намеренно: «её выбор не вид, а ПОВЕДЕНИЕ»,
   и пустая плитка была бы хуже честной строки подписей. На панели 700 px
   место есть, и поведение рисуется ПРЯМО: штамп канала над полем у обоих
   вариантов, а у «С подтверждением» — окно вопроса поверх композера.
   Плитка перестала быть пустой, и довод против неё кончился. */
[data-chat-set-grp="guard"] .s-stamp {
  left: 7%;
  top: 9%;
  width: 34%;
  height: 15%;
  border-radius: 2px;
  border: 1px solid rgb(var(--brand-400) / 1);
}
.chat-swatch--guard-confirm .s-ask {
  left: 18%;
  right: 14%;
  top: 36%;
  bottom: 8%;
  border-radius: 3px;
  background: rgb(var(--surface-0) / 1);
  border: 2px solid rgb(var(--neutral-900) / 1);
  box-shadow: 0 3px 8px rgb(var(--scrim) / 0.3);
}
.chat-swatch--guard-confirm .s-ask::before {
  content: "";
  left: 9%;
  right: 40%;
  top: 22%;
  height: 12%;
  border-radius: 1px;
  background: rgb(var(--neutral-500) / 0.55);
}
.chat-swatch--guard-confirm .s-ask::after {
  content: "";
  left: auto;
  right: 8%;
  top: auto;
  bottom: 15%;
  width: 34%;
  height: 30%;
  border-radius: 2px;
  background: rgb(var(--brand-500) / 1);
}

/* ── Группа «Очередь» ──────────────────────────────────────────────────
   «Наряд» — три строки с охряным номером слева и таймером справа.
   «Сводная строка» — одна плашка со счётчиком наверху, а под ней просто
   список: ровно то, чем формы и различаются. */
[data-chat-set-grp="queue"] .s-q1,
[data-chat-set-grp="queue"] .s-q2,
[data-chat-set-grp="queue"] .s-q3 {
  left: 7%;
  right: 7%;
  height: 17%;
  border-radius: 2px;
  background: rgb(var(--neutral-200) / 1);
}
.chat-swatch--queue-naryad .s-q1 { top: 14%; }
.chat-swatch--queue-naryad .s-q2 { top: 41%; }
.chat-swatch--queue-naryad .s-q3 { top: 68%; }
.chat-swatch--queue-naryad .s-q1,
.chat-swatch--queue-naryad .s-q2,
.chat-swatch--queue-naryad .s-q3 {
  border-left: 5px solid rgb(var(--brand-500) / 1);
  box-shadow: inset -14px 0 0 -10px rgb(var(--brand-600) / 0.55);
}
.chat-swatch--queue-svodka .s-chip {
  left: 7%;
  top: 12%;
  width: 17%;
  height: 18%;
  border-radius: 2px;
  background: rgb(var(--brand-500) / 1);
}
.chat-swatch--queue-svodka .s-chip::before {
  content: "";
  left: 130%;
  right: -230%;
  top: 30%;
  height: 36%;
  border-radius: 1px;
  background: rgb(var(--neutral-400) / 0.8);
}
.chat-swatch--queue-svodka .s-s1,
.chat-swatch--queue-svodka .s-s2,
.chat-swatch--queue-svodka .s-s3 {
  left: 7%;
  right: 7%;
  height: 12%;
  border-radius: 1px;
  background: rgb(var(--neutral-200) / 1);
}
.chat-swatch--queue-svodka .s-s1 { top: 42%; }
.chat-swatch--queue-svodka .s-s2 { top: 60%; }
.chat-swatch--queue-svodka .s-s3 { top: 78%; }

/* ── Группа «Правая колонка» ───────────────────────────────────────────
   План экрана сверху: рельс, лента, колонка контекста. «Скрывать» —
   тот же план без третьей зоны, и лента занимает её место. */
[data-chat-set-grp="ctx"] .s-z1,
[data-chat-set-grp="ctx"] .s-z2,
[data-chat-set-grp="ctx"] .s-z3 { top: 0; bottom: 0; }
[data-chat-set-grp="ctx"] .s-z1 {
  left: 0;
  width: 24%;
  background: rgb(var(--neutral-100) / 1);
  border-right: 1px solid rgb(var(--neutral-300) / 1);
}
[data-chat-set-grp="ctx"] .s-z2 {
  left: 24%;
  right: 0;
  background: rgb(var(--surface-0) / 1);
}
.chat-swatch--ctx-on .s-z2 { right: 28%; }
.chat-swatch--ctx-on .s-z3 {
  right: 0;
  width: 28%;
  background: rgb(var(--brand-100) / 1);
  border-left: 1px solid rgb(var(--brand-300) / 1);
}
[data-chat-set-grp="ctx"] .s-z2::before { top: 30%; left: 12%; right: 16%; }
[data-chat-set-grp="ctx"] .s-z2::after { top: 56%; left: 12%; right: 44%; }

/* ── Тёмные пары образцов ──────────────────────────────────────────────
   Ровно те же, что у настоящих отделок, и по тем же причинам: инверсия
   нейтралей увела бы служебный лист и разворот журнала ВЫШЕ панели, а
   полосу «самого закрытого» канала сделала бы самым светлым пятном экрана
   (уроки Ч2Б-3 и Ч2Б-4). Всё остальное переворачивается само: охра канта
   (--brand-500), поле образца (--surface-0), рамка (--neutral-300).

   Оба тёмных контекста, как у --chat-shadow (строки 60-65): пара,
   объявленная в одном из них, даёт образец, который в половине случаев
   показывает светлые цвета на тёмной панели — то есть врёт о виде ровно
   там, где вид и выбирают. Сторож —
   test_swatch_dark_pairs_stand_in_both_dark_contexts. */
html.themable[data-theme="dark"] .chat-swatch--skin-bumaga,
html.themable[data-theme="dark"] .chat-swatch--skin-zhurnal {
  background-color: rgb(var(--neutral-50) / 1);
}
html.themable[data-theme="dark"] .chat-swatch--okant-sepia .s-band {
  background: rgb(var(--brand-200) / 1);
}
html.themable[data-theme="dark"] .chat-swatch--okant-grifel .s-band {
  background: rgb(var(--neutral-100) / 1);
}
html.themable[data-theme="dark"] .chat-swatch--okant-kromka .s-band {
  background: rgb(var(--neutral-50) / 1);
  border-right-color: rgb(var(--neutral-300) / 1);
}
html.themable[data-theme="dark"] .chat-swatch--obum-chernovik,
html.themable[data-theme="dark"] .chat-swatch--obum-velen,
html.themable[data-theme="dark"] .chat-swatch--obum-pole {
  background-color: rgb(var(--brand-50) / 1);
}
@media (prefers-color-scheme: dark) {
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-swatch--skin-bumaga,
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-swatch--skin-zhurnal {
    background-color: rgb(var(--neutral-50) / 1);
  }
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-swatch--okant-sepia .s-band {
    background: rgb(var(--brand-200) / 1);
  }
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-swatch--okant-grifel .s-band {
    background: rgb(var(--neutral-100) / 1);
  }
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-swatch--okant-kromka .s-band {
    background: rgb(var(--neutral-50) / 1);
    border-right-color: rgb(var(--neutral-300) / 1);
  }
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-swatch--obum-chernovik,
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-swatch--obum-velen,
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-swatch--obum-pole {
    background-color: rgb(var(--brand-50) / 1);
  }
}

/* ── Секция «Чат» карточки объекта: шапка и лента ──────────────────────
   (ЧР-6 — вход в настройки, З-2; ЧР-7 — первый экран, Ф-6)

   Переключатель каналов, ЗАГОЛОВОК РАЗДЕЛА и шестерёнка стоят одной
   строкой: настройка вида ленты — такой же инструмент над лентой, как выбор
   канала, а полоса-заголовок над ними была бы этажеркой хрома (Ф-1) в
   миниатюре — той самой, которую ЧР-3 снял в хабе. Разбор и замеры — в
   crm/objects/sections/_chat.html. */
.chat-obj-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}
/* Заголовок раздела узким капсом чата, а не кеглем 20 px карточки: он тут
   не полоса, а подпись строки инструментов. Тег остаётся <h2> — раздел
   обязан иметь имя и для скринридера, и для оглавления страницы. */
.chat-obj-cap {
  flex: none;
  margin: 0;
  font-family: var(--chat-face-narrow);
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: rgb(var(--neutral-900) / 1);
}
/* РАМКА СЕГМЕНТА — ПО СОДЕРЖИМОМУ (приёмка ЧР, находка User №2).
   Стояло `flex: 1`, и группа растягивалась до самой шестерёнки: на 1440
   рамка тянулась через полполосы, а внутри неё стояла пустота — три бейджа
   слева и воздух справа. Растяжка ставилась в ЧР-7 не ради этого, а ради
   переноса на 390 (В5б): нужно было, чтобы у группы была ИЗВЕСТНАЯ ширина,
   в которую бейджи обязаны уложиться одной строкой.

   Ширину даёт `min-width: 0` вместе с обычной усадкой (flex-shrink: 1 по
   умолчанию), а место у правого края шестерёнке — её собственный
   margin-left: auto. Перенос на 390 от этого не меняется НИ НА ПИКСЕЛЬ, и
   это счёт, а не надежда: при flex:1 группа занимала весь остаток полосы, и
   бейджам приходилось влезать ровно в него; теперь группа берёт свою
   естественную ширину, но усадка ограничена тем же остатком — то есть
   верхняя граница осталась прежней, а нижняя стала меньше. Длинные подписи
   ведут себя так же, как вели: упрутся в остаток и перенесутся, а не
   вылезут за край. Сторож — test_card_segment_holds_one_line_on_the_phone
   (390) и test_card_segment_hugs_its_badges (1440). */
.chat-obj-head .chat-seg { min-width: 0; }
/* Инструменты строки — ОДНОЙ обёрткой (П-2). До ящика находок кнопка была
   одна, и место у правого края ей давал собственный margin-left: auto. Их
   стало две, и на 390 они переезжают на строку заголовка ВМЕСТЕ — одним
   узлом, а не двумя порознь (иначе сегмент, встающий своей строкой, разорвал
   бы их). Обёртка и есть тот узел; правый край она держит тем же приёмом. */
.chat-obj-tools {
  flex: none;
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

/* ── Окно переписки карточки (ЧР-7, Ф-6 и хвост Ф-2) ────────────────────
   Высоту ставит скрипт секции (--chat-obj-feed): она считается от
   фактического верха ленты до нижней кромки окна — а кромка это низ окна
   МИНУС прилипший футер CRM (с md он fixed). 384 px в var() — фолбэк ровно
   на прежнюю утилиту h-96: без скрипта секция выглядит как до ЧР-7, а не
   схлопывается в ноль.

   Поля 18/24/20 и колонка письма 800 px — те же, что у ленты хаба
   (.chat-hub-feed): грамматика окна переписки у продукта одна. Гейт свой, а
   не общий с хабом, потому что хабовый стоит на .chat-thread-pane — панели,
   которой в карточке нет; сводить их в один селектор значило бы завести
   правило, которое красит две разные рамы «за компанию». */
.chat-obj-feed {
  height: var(--chat-obj-feed, 384px);
  overflow-y: auto;
  padding: 18px 24px 20px;
}
.chat-obj-feed > .chat-thread {
  max-width: 800px;
  margin: 0 auto;
}
/* Узкая колонка: поля втрое у́же — на 390 px по 24 с каждой стороны съедали
   бы шестую часть строки письма. Порог тот же, что у отделок. */
@media (max-width: 639.98px) {
  .chat-obj-feed { padding: 14px 12px 16px; }
}

/* ── Полоса каналов карточки на телефоне (ЧР-7, вторая половина Ф-6) ────
   «На 390 сегмент каналов переносится на две строки и залипает над лентой,
   перекрывая верхнее письмо» (разбор ЧР-1). Строка инструментов на 390
   имеет 284 px, из них шестерёнка забирает 34 и зазор 8; трём бейджам
   штатной геометрии (8/14 при трекинге 1,4) нужно 271 — они и переносились.

   Лечится там же, где в хабе (правило .chat-thread-seg ниже), и теми же
   двумя числами: поля кнопки и трекинг. Заголовок раздела уходит на свою
   строку — иначе он забирал бы у бейджей ещё 48 px, и перенос вернулся бы.
   Порог — тот же 640, на котором в узкий режим уходят отделки и подпись
   кнопки отправки. Сторож — tests/browser/test_chat_card.py. */
/* ПЕРЕСТАНОВКА ПОКОЯ НА 390 (П-2, кадр 390-1-покой листа).
   До ящика находок заголовок уходил на свою строку, а сегмент делил вторую
   с одной шестерёнкой (замер стенда: полоса 306, поля 12+12, сегменту
   доставалось 229). Кнопок стало две, и по прежней раскладке сегменту
   осталось бы 198 (282 внутренних − 34 − 34 − 8 − 8) — бейджи снова
   переносились бы в два ряда, то есть возвращалась бы ровно вторая
   половина Ф-6.

   Лист решает это перестановкой, и она ПОСТОЯННАЯ, а не «на время поиска»:
   инструменты встают на строку заголовка (там пусто — «ЧАТ» занимает 23 px
   из 306), а сегмент получает ВСЮ полосу отдельной строкой. Сторожу
   test_card_segment_holds_one_line_on_the_phone от этого только легче:
   вместо 195 px у бейджей вся ширина.

   Цена — строка инструментов растёт с 76 до 94 px (замер стенда: поля
   10+10, строка инструментов 34, зазор 8, сегмент 33). Ленте это не стоит
   НИЧЕГО: её расчёт на 390 и до перестановки упирался в пол FEED_MIN = 220
   (свободного места по формуле выходит отрицательно), и после неё упирается
   в него же — 220 → 220. Замер «до/после» — в браузерном стороже. */
@media (max-width: 639.98px) {
  .chat-obj-head .chat-seg-btn { padding: 7px 9px 6px; letter-spacing: 1px; }
  .chat-obj-tools { order: 1; }
  .chat-obj-head .chat-seg { order: 2; flex-basis: 100%; }
}

/* ═══════════════════════════════════════════════════════════════════════
   ЯЩИК НАХОДОК (П-2) — поиск по письмам объекта, находка приёмки ЧР №3
   Эталон: мокапы_приёмки/поиск_карточки/П2_ящик_находок.html (выбор User
   14.08 — «чистый П-2»: вход по лупе, поля в строке инструментов нет).

   ЗАМЕРЫ ЭТАЛОНА (1440 × 900, рендер листа):
     ящик 520 × 804, ровно в рабочее поле карточки (от топбара 56 до
     прилипшего футера) · шапка 112 (строка 34 + отбивка 11 + поле 40) ·
     поле 482 · сегмент 54 · подвал 32,5 · строка находки 59,8 (одна строка
     текста) / 79,1 (две) · разделитель дня 27,5 · находок видно разом 6.
     Полоса возврата над лентой — 33 (32,5), и это ЕДИНСТВЕННОЕ, что П-2
     стоит ленте: в покое ящик не отнимает у неё ни пикселя.

   ЭТАЖИ — ОБЩИЕ С ХАБОМ И ПАНЕЛЬЮ «РАЗВОРОТ» (--chat-hub-top/foot,
   объявлены ниже одним правилом на троих). Третий набор констант у ящика
   разъехался бы с ними при первой же правке футера — панель повисла бы над
   лентой или под ней. Равенство констант живому хрому стережёт браузерный
   сторож m-4.

   ПОДЛОЖКА У ЯЩИКА ЕСТЬ, и это отличие от «Разворота» осознанное: та панель
   примеряет вид ЖИВОГО хаба и обязана пропускать клики (ЧР-6, M-1), а ящик
   забирает внимание целиком — «на время выбора ленты не видно» записанная
   цена направления. Поэтому правило pointer-events выше гейтится маркером
   [data-chat-settings] и сюда не достаёт.
   ═══════════════════════════════════════════════════════════════════════ */
.chat-find-panel {
  position: absolute;
  top: var(--chat-hub-top);
  bottom: calc(var(--chat-hub-foot) + env(safe-area-inset-bottom, 0px));
  right: 0;
  width: 520px;
  max-width: 100%;
  min-height: 0;
  background: rgb(var(--surface-0) / 1);
  border-left: 1px solid rgb(var(--neutral-200) / 1);
  box-shadow: -18px 0 44px -26px rgb(var(--scrim) / 0.5);
}
html.themable[data-theme="dark"] .chat-find-panel {
  box-shadow: -18px 0 44px -22px rgb(var(--scrim) / 0.75);
}
@media (prefers-color-scheme: dark) {
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-find-panel {
    box-shadow: -18px 0 44px -22px rgb(var(--scrim) / 0.75);
  }
}

/* ── Шапка ящика: имя, адрес объекта, крестик, поле ────────────────────── */
.chat-find-head {
  flex: none;
  padding: 14px 18px 12px;
  border-bottom: 1px solid rgb(var(--neutral-200) / 1);
}
.chat-find-top {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 11px;
}
.chat-find-top h2 {
  flex: none;
  margin: 0;
  font-family: var(--chat-face-narrow);
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.6px;
  word-spacing: 0.26em;
  text-transform: uppercase;
  color: rgb(var(--neutral-900) / 1);
}
/* Адрес объекта: ящик стоит ПОВЕРХ карточки и обязан сказать, чью переписку
   листает. Одной строкой с многоточием — длинный адрес не имеет права
   разломать шапку на два этажа. */
.chat-find-addr {
  min-width: 0;
  font-size: 12.5px;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: rgb(var(--neutral-500) / 1);
}
.chat-find-sp { flex: 1; }

/* Поле поиска. Форма — словарь поиска колоды: рамка ярче обычной (--brand-500
   при наборе), значок слева, счётчик моноширинным справа, крестик очистки. */
.chat-find-field {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 40px;
  padding: 0 8px 0 10px;
  border: 1px solid rgb(var(--neutral-300) / 1);
  border-radius: 8px;
  background: rgb(var(--surface-0) / 1);
  min-width: 0;
}
.chat-find-field:focus-within {
  border-color: rgb(var(--brand-500) / 1);
  box-shadow: 0 0 0 3px rgb(var(--brand-500) / 0.16);
}
.chat-find-field > svg {
  flex: none;
  width: 16px;
  height: 16px;
  color: rgb(var(--neutral-500) / 1);
}
.chat-find-field input {
  flex: 1;
  min-width: 0;
  border: 0;
  outline: none;
  padding: 0;
  background: none;
  font-size: 15px;
  color: rgb(var(--neutral-900) / 1);
}
.chat-find-field input::placeholder { color: rgb(var(--neutral-500) / 1); }
/* Родная кнопка очистки type=search — своя у каждого браузера и в тёмной
   теме чернеет; крестик рисуем сами. */
.chat-find-field input::-webkit-search-cancel-button { display: none; }
.chat-find-cnt {
  flex: none;
  font-family: var(--chat-face-mono);
  font-size: 11.5px;
  line-height: 1;
  white-space: nowrap;
  color: rgb(var(--neutral-600) / 1);
}
.chat-find-cnt b { color: rgb(var(--neutral-900) / 1); font-weight: 400; }
/* «Ничего не нашлось» — служебное слово, а не число: узкий капс, как штампы
   и бейджи (словарь поиска колоды). */
.chat-find-cnt i {
  font-style: normal;
  font-family: var(--chat-face-narrow);
  font-weight: 700;
  font-size: 10.5px;
  letter-spacing: 1.3px;
  word-spacing: 0.26em;
  text-transform: uppercase;
  color: rgb(var(--neutral-500) / 1);
}
.chat-find-clear {
  flex: none;
  width: 22px;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 5px;
  background: none;
  color: rgb(var(--neutral-500) / 1);
  cursor: pointer;
}
.chat-find-clear:hover { background: rgb(var(--neutral-100) / 1); }
.chat-find-clear svg { width: 13px; height: 13px; }

/* ── Тело: сегмент, список, подвал ─────────────────────────────────────── */
.chat-find-body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.chat-find-seg {
  flex: none;
  padding: 9px 18px;
  border-bottom: 1px solid rgb(var(--neutral-200) / 1);
}
.chat-find-list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  background: rgb(var(--neutral-50) / 1);
}
.chat-find-day {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 18px 5px;
  font-family: var(--chat-face-narrow);
  font-size: 10.5px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 2px;
  word-spacing: 0.28em;
  text-transform: uppercase;
  color: rgb(var(--neutral-500) / 1);
}
.chat-find-day::after {
  content: "";
  flex: 1;
  height: 1px;
  background: rgb(var(--neutral-200) / 1);
}

/* СТРОКА НАХОДКИ — ПИСЬМО, А НЕ ССЫЛКА НА НЕГО: автор узким капсом, время
   моно, кусок текста с подсветкой. Корешок слева называет канал цветом —
   именно он вместе с бейджем даёт право мешать здесь три канала (Ч2-Р4
   запрещает мешать их в ЛЕНТЕ, где поле названо цветом канала).

   Элемент — button: адреса у строки нет и быть не может, ящик открывается
   только htmx'ом. Поэтому же сброшены рамка и фон кнопки. */
.chat-find-row {
  position: relative;
  display: block;
  width: 100%;
  text-align: left;
  padding: 9px 18px 11px 21px;
  border: 0;
  border-top: 1px solid rgb(var(--neutral-200) / 1);
  background: rgb(var(--surface-0) / 1);
  cursor: pointer;
  font: inherit;
  color: inherit;
}
.chat-find-day + .chat-find-row { border-top: 0; }
.chat-find-row::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 4px;
}
.chat-find-row--client::before { background: rgb(var(--brand-500) / 1); }
.chat-find-row--object::before { background: rgb(var(--neutral-500) / 1); }
.chat-find-row--office::before { background: rgb(var(--neutral-800) / 1); }
.chat-find-row:hover,
.chat-find-row:focus-visible { background: rgb(var(--brand-100) / 1); }
.chat-find-row:focus-visible { outline: 2px solid rgb(var(--brand-500) / 1); outline-offset: -2px; }
.chat-find-row-top {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 4px;
}
.chat-find-chan {
  flex: none;
  font-family: var(--chat-face-narrow);
  font-size: 9.5px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.4px;
  word-spacing: 0.26em;
  text-transform: uppercase;
  padding: 3px 6px 2px;
  border-radius: 3px;
  background: rgb(var(--neutral-100) / 1);
  color: rgb(var(--neutral-700) / 1);
}
.chat-find-who {
  flex: 1;
  min-width: 0;
  font-family: var(--chat-face-narrow);
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.3px;
  word-spacing: 0.28em;
  text-transform: uppercase;
  color: rgb(var(--neutral-900) / 1);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.chat-find-at {
  flex: none;
  font-family: var(--chat-face-mono);
  font-size: 11.5px;
  line-height: 1;
  color: rgb(var(--neutral-500) / 1);
}
/* Две строки текста — обещание листа. Кусок вокруг совпадения режет сервер
   (_snippet), а этот кэп — страховка на случай, когда в кусок уместилось
   больше двух строк: длинное письмо не имеет права раздуть строку списка и
   выгнать из окна соседние находки. */
.chat-find-txt {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  font-size: 14px;
  line-height: 1.45;
  color: rgb(var(--neutral-700) / 1);
}
/* «Показать в ленте» — у строки, на которую смотрят. Всегда видимая, она
   повторялась бы у каждой находки шестью одинаковыми строчками. */
.chat-find-go {
  display: none;
  align-items: center;
  gap: 6px;
  margin-top: 7px;
  font-family: var(--chat-face-narrow);
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.4px;
  word-spacing: 0.26em;
  text-transform: uppercase;
  color: rgb(var(--brand-700) / 1);
}
.chat-find-go svg { width: 13px; height: 13px; }
.chat-find-row:hover .chat-find-go,
.chat-find-row:focus-visible .chat-find-go { display: inline-flex; }

.chat-find-foot {
  flex: none;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 18px;
  border-top: 1px solid rgb(var(--neutral-200) / 1);
  font-family: var(--chat-face-narrow);
  font-size: 10.5px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.3px;
  word-spacing: 0.26em;
  text-transform: uppercase;
  color: rgb(var(--neutral-500) / 1);
}
.chat-find-foot em {
  font-style: normal;
  font-family: var(--chat-face-mono);
  letter-spacing: 0;
  font-size: 11.5px;
  color: rgb(var(--neutral-900) / 1);
}
.chat-find-empty {
  padding: 26px 22px;
  font-size: 14px;
  line-height: 1.6;
  color: rgb(var(--neutral-600) / 1);
}
.chat-find-empty b {
  display: block;
  font-family: var(--chat-face-narrow);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1.5px;
  word-spacing: 0.26em;
  text-transform: uppercase;
  color: rgb(var(--neutral-900) / 1);
  margin-bottom: 9px;
}
.chat-find-empty p + p { margin-top: 12px; }

/* ── Полоса возврата над лентой (состояние 4 листа) ────────────────────── */
/* ПОЛОСА ЖИВЁТ РОВНО ПРИ ЖИВОМ ЯКОРЕ (правка ревью M-1). Сервер решает,
   рисовать ли её вообще (в неанкерном ответе её нет), но решить за БУДУЩИЙ
   свап он не может: ответ композера подменяет только ленту, а полоса стоит
   рядом с ней. Поэтому видимость гейтится тем же признаком, что и сама
   анкерность, — скрытыми полями якоря внутри ленты. Отправил письмо, якорь
   уехал вместе с прежней разметкой — полоса погасла, и врать «1-е из 12»
   над живой лентой ей больше нечем.

   :has() здесь та же идиома, что у ящика настроек и пустой сводной строки.

   ⚠ display НАЗНАЧАЕТСЯ ТОЛЬКО ЭТОЙ ПАРОЙ ПРАВИЛ и больше нигде. Первая
   редакция правки оставила `display: flex` в основном блоке ниже — и он,
   стоя ПОЗЖЕ при равной специфичности, отменял «none»: полоса продолжала
   висеть над живой лентой и врать «1-е из 10». Поймано браузерным сторожем
   отправки, а не глазами. */
.chat-find-bar { display: none; }
.chat-channel:has([data-chat-anchor]) .chat-find-bar { display: flex; }
.chat-find-bar {
  flex: none;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 14px;
  margin: 0;
  padding: 7px 16px 6px;
  background: rgb(var(--brand-100) / 1);
  border-bottom: 1px solid rgb(var(--brand-300) / 1);
  font-family: var(--chat-face-narrow);
  font-size: 10.5px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.3px;
  word-spacing: 0.26em;
  text-transform: uppercase;
  color: rgb(var(--brand-700) / 1);
}
.chat-find-bar em {
  font-style: normal;
  font-family: var(--chat-face-mono);
  letter-spacing: 0;
  font-size: 11.5px;
  text-transform: none;
  color: rgb(var(--neutral-900) / 1);
}
/* Чип «6-е из 12». Ни лист, ни плита чипа контраста не держат: лист против
   полосы — 1,11 светлая / 1,04 тёмная, рамка --brand-300 против полосы —
   1,40 / 1,55, и это НЕ дефект и не «держится рамкой» (первая редакция
   отчёта утверждала именно так — неверно). Чип не элемент управления и не
   граница, за которую цепляется глаз: смысл несёт ЧИСЛО внутри него, а оно
   набрано моноширинным чернилом --neutral-900 — 17,85 / 13,21 к своему
   листу. Форма чипа здесь ровно то, чем она нарисована на листе, — тихая
   подложка под число. */
.chat-find-bar-n {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 8px 2px;
  border: 1px solid rgb(var(--brand-300) / 1);
  border-radius: 5px;
  background: rgb(var(--surface-0) / 1);
  color: rgb(var(--neutral-700) / 1);
}
.chat-find-bar-sp { flex: 1; }
/* ЕДИНСТВЕННЫЙ ПРИЗНАК КЛИКАБЕЛЬНОСТИ — ПОДЧЁРКИВАНИЕ, значит оно обязано
   быть видимым (правка ревью m-4). Стоял --brand-400: 2,33 светлая / 2,83
   тёмная к полосе — ниже 3:1, которых требует нетекстовый контраст, то есть
   у двери не было различимой ручки. Взят --brand-500: 4,51 / 4,07 к полосе
   (те же чернила, что держат подсветку словаря поиска), и он по-прежнему
   светлее самих чернил кнопки --brand-700, то есть подчёркивание не спорит
   со словом. Клавиатуре — своё кольцо: дверь эта не декоративная. */
.chat-find-back {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  letter-spacing: inherit;
  word-spacing: inherit;
  text-transform: inherit;
  white-space: nowrap;
  color: rgb(var(--brand-700) / 1);
  border-bottom: 1px solid rgb(var(--brand-500) / 1);
  cursor: pointer;
}
.chat-find-back:hover {
  color: rgb(var(--brand-800) / 1);
  border-bottom-color: rgb(var(--brand-700) / 1);
}
.chat-find-back:focus-visible {
  outline: 2px solid rgb(var(--brand-500) / 1);
  outline-offset: 3px;
}
/* ПОЛОСА НА УЗКОМ (правка ревью m-5) — ЧЕСТНЫЕ ЧИСЛА И НИЧЕГО СВЕРХ.
   Замер стенда с полным набором (слово, чип, две названные двери):
   1440 — 32,5 px одной строкой; 390 и 360 — 67,5 px тремя.

   Ужать не вышло, и вот арифметика, а не впечатление: внутренних 274 px
   против кусков 67 (слово) + 77 (чип) + 161 (дверь назад) + 141 (дверь
   вперёд) при зазоре 14. В ряд встаёт только первая пара; каждая дверь
   забирает свой ряд по ширине, а не по чьей-то ошибке.

   ЧТО ПРОБОВАЛ И СНЯЛ — обе попытки, чтобы следующий не ходил тем же кругом:
     · гасить служебные слова «Найдено по» отдельным тегом (приём корешка
       рельса) — 0 px и на 390, и на 360: экономия ширины меньше, чем нужно
       двери, чтобы подняться на ряд выше;
     · гасить распорку (flex: 1 в переносящемся контейнере съедает остаток
       своего ряда) — тоже 0 px: двери и без неё не помещаются парой.
   Раннее «85 px на 360» из моих же замеров оказалось шумом сцены — там был
   другой чип («1-е из 3» против «1-е из 10»), а не другая раскладка.

   ЧТО НЕ СДЕЛАНО И ПОЧЕМУ: короткие изводы дверей («К находкам», «К
   последним») в два ряда укладываются, но обе двери названы ради правки m-7,
   и обрезать им имена значило бы чинить одну находку ревью другой.

   Обрезки слов при этом нет ни у одного узла, и стережёт это САМ ПОРОК
   (scrollWidth против clientWidth), а не видимость. Ленте 67,5 px не стоят
   ничего: её расчёт на узком упирается в пол FEED_MIN = 220 и до полосы, и
   после. Платит страница прокруткой. */

/* ── СЛОВАРЬ ПОИСКА: подсветка найденного слова ────────────────────────────
   Охряная подложка --brand-300 при чернилах --neutral-900 — 11,52 светлая /
   8,14 тёмная. Сама подложка отличается от пузыря слабо (1,40…1,62 — она и
   не должна кричать), поэтому её держит ЧЕРНИЛЬНАЯ КРОМКА --brand-500 снизу:
   5,00 / 4,25 против чужого пузыря и 4,51 / 4,07 против своего — выше 3:1,
   которых требует нетекстовый контраст. Числа считаны по палитре input.css,
   живьём их перемеряет браузерный сторож в обеих темах.

   Правило общее для ящика и для ленты: слово, найденное в списке, остаётся
   крашеным и в письме — иначе после перехода пришлось бы искать его глазами
   второй раз. */
mark.chat-mark {
  background: rgb(var(--brand-300) / 1);
  color: rgb(var(--neutral-900) / 1);
  border-radius: 2px;
  padding: 0 2px;
  box-shadow: inset 0 -2px 0 rgb(var(--brand-500) / 1);
}
/* Само найденное письмо — кольцо у пузыря (текущее совпадение словаря). */
.chat-msg--found > .chat-msg-bubble {
  outline: 2px solid rgb(var(--brand-500) / 1);
  outline-offset: 3px;
}

/* Узкий режим: ящик во весь экран — ТА ЖЕ раскладка, что у панели
   «Разворот» ниже lg (её узкий вид User благословил глазами, вердикт В10).
   Лист рисует ящик под топбаром; здесь он накрывает и его — иначе у чата
   завелись бы две разные полноэкранные панели с разными краями. */
@media (max-width: 1023.98px) {
  .chat-find-panel {
    top: 0;
    bottom: 0;
    width: 100%;
    border-left: 0;
  }
  /* АДРЕС ОБЪЕКТА УХОДИТ ПОД ИМЯ, А НЕ РУБИТСЯ (правка ревью m-6). В строке
     шапки на 390 ему оставалось около 90 px, и многоточие приходилось на
     «ул. Браузерная, 1 · 3 ка…» — обрезка на полуслове, ровно та болезнь,
     которую 13.08 лечили в корешке рельса и здесь же в сегменте каналов.
     Лечится тем же приёмом, что у строки механики панели «Разворот»: своя
     строка вместо ellipsis. Цена — шапка ящика выше на строку. */
  .chat-find-top { flex-wrap: wrap; }
  .chat-find-addr {
    flex-basis: 100%;
    order: 3;
    white-space: normal;
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   ШАПКА ТРЕДА, БЕЙДЖИ КАНАЛОВ, «СЛЕДУЮЩИЙ ЖДУЩИЙ»
   (Ч2-5, 03.08.2026 → переделано ЧР-4, 07.08.2026)

   Канон — сборка НСБ_хаб.html (кадры НСБ_хаб_кадр-{светлая,тёмная}.png,
   спецификация НСБ_СБОРКА.md): состав шапки от Н-3 «Диспетчер», форма от
   Н-2 «Беседа». Блоки 3 и 4 сборки.

   ЧТО ПЕРЕДЕЛАНО В ЧР-4 И ПОЧЕМУ (находка Ф-1 «этажерка хрома»).
   Шапка была ТРЁХЭТАЖНОЙ (адрес + чип этапа + штамп канала + кнопка со
   строкой «кто ждёт»; ниже строка фактов; ниже охряная полоса «следующий
   шаг») и занимала 154 px из 803 рабочего поля — ленте оставалось 523 px,
   58 % экрана. Стало 54 px, одна строка:

     · ШТАМП КАНАЛА уехал вниз, в плашку над композером (.chat-channel-flag).
       Там же он и решает — в сантиметре от кнопки отправки, а не от адреса;
     · СТРОКА ФАКТОВ и ПОЛОСА «СЛЕДУЮЩИЙ ШАГ» уехали в правую колонку
       контекста — ЧР-5 её поставил, секция в конце файла. Правила
       .chat-thread-sub / -next / -mark
       сняты отсюда вместе с разметкой: держать мёртвый CSS «на всякий
       случай» — это ждать, пока он разойдётся с новым;
     · СТРОКА «КТО ЖДЁТ» под кнопкой (имя, чип канала, ожидание) свернулась
       в чип ожидания ВНУТРИ кнопки плюс подсказку с именем и каналом
       (счёт §4 сборки).

   ЗАМЕР ПОСЛЕ (1440×900, тот же скрипт tests/browser/test_chat_frame.py):
   шапка 154 → 54, бейджи каналов 56 → 48, композер 70 → 63, плюс новая
   плашка канала 50 — лента 523 → 589 px, 58,1 % → 65,4 % экрана (эталон
   сборки 588 и 65,3 % при футере 40; у нас футер 41, а полоса бейджей на
   2 px ниже эталонной — отсюда лишний пиксель в нашу пользу).

   ОБЕ ТЕМЫ приходят сами: ни одного литерального цвета, только --brand-* /
   --neutral-* / --surface-0. В тёмной brand-100 — тёплый графит, brand-300 —
   его кант, brand-700 — светлая охра: чип этапа переворачивается вместе с
   бандлом (кадр НСБ_хаб_кадр-тёмная.png).
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Шапка треда: ОДНА строка (форма .thead сборки Н-СБ) ────────────────
   flex:none — шапка не сжимается и не растёт; overflow:hidden — длинный
   адрес режется многоточием внутри своего элемента, а не выпихивает кнопку
   за край панели. Паддинг 11/16 и есть та геометрия, которая даёт 54 px
   при адресе в 18 px и кнопке в 33.

   NOWRAP — КАНОН ЭТАЛОНА (.thead: flex без wrap, адрес flex:0 1 auto с
   многоточием) И ПОЧИНКА ЧР-5. До ЧР-5 тред занимал ~900 px, и перенос не
   случался ни разу; колонка контекста сузила его до 620, и браузерный
   сторож намерил: адрес 169 + этап 126 + распорка 6 + кнопка 262 плюс три
   зазора по 10 = 593 при 588 доступных — «Следующий ждущий» уезжал СВОЕЙ
   СТРОКОЙ, шапка 54 → 84, а лента теряла 30 px (589 → 557). Перенос по
   нехватке места решает МЕСТО, а решать должен ЗАМЫСЕЛ: строка одна, и
   уступает в ней адрес — многоточием, как в эталоне. Осознанный перенос
   ровно один — узкий режим (правило ниже), где распорка получает
   flex-basis:100% и уводит кнопки строкой вниз намеренно.

   CONTAINER-TYPE ЗДЕСЬ БОЛЬШЕ НЕТ (ЧР-5). Он стоял ради узкого режима, но
   узкий режим правит и САМУ шапку (flex-wrap), а элемент не может
   стилизоваться собственным контейнерным запросом — правило молча не
   применялось, и на 390 шапка оставалась nowrap с адресом в
   white-space:normal: буквы вставали в столбик по одной. Все правила узкого
   режима переехали в @media ниже — одним куском, чтобы разъехаться им было
   негде. */
.chat-thread-header {
  flex: none;
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: 6px 10px;
  padding: 11px 16px;
  overflow: hidden;
  background: rgb(var(--surface-0) / 1);
  border-bottom: 1px solid rgb(var(--neutral-200) / 1);
}

/* Адрес: 18 px против прежних 19 — кегль эталона. Строка одна, поэтому
   nowrap с многоточием: перенос вырастил бы шапку вдвое ровно на том
   почтовом адресе, ради которого в СПИСКЕ перенос как раз и оставлен
   (Ф-4). Целиком адрес показывает подсказка ссылки. */
.chat-thread-addr {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
  flex: 0 1 auto;
  font-size: 18px;
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.2px;
  white-space: nowrap;
  color: rgb(var(--neutral-900) / 1);
  text-decoration: none;
}
.chat-thread-addr span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.chat-thread-addr svg {
  width: 13px;
  height: 13px;
  flex: none;
  opacity: 0.45;
  transform: translateY(1px);
}
.chat-thread-addr:hover { color: rgb(var(--brand-600) / 1); }
.chat-thread-addr:hover svg { opacity: 1; }
.chat-thread-addr:hover span { text-decoration: underline; text-underline-offset: 3px; }

/* Чип этапа — тот же жест, что чип статуса в карточке объекта, но набранный
   атомами чата: узкий капс с трекингом. word-spacing обязателен — узкий капс
   с трекингом 1.6 px склеивает «ЗАМЕР И РАСЧЁТ» (та же грабля, что у штампа
   канала). */
.chat-thread-stage {
  flex: none;
  font-family: var(--chat-face-narrow);
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.6px;
  word-spacing: 0.28em;
  text-transform: uppercase;
  padding: 6px 10px 5px;
  border: 1px solid rgb(var(--brand-300) / 1);
  border-radius: 999px;
  background: rgb(var(--brand-100) / 1);
  color: rgb(var(--brand-700) / 1);
}

/* Распорка, а не margin-left:auto у кнопки: на узкой панели кнопка уходит
   своей строкой, и распорка схлопывается сама, тогда как auto-отступ тянул
   бы её вправо и там.

   min-width: 0, а не 6 (ЧР-5). Шесть пикселей эталона были страховкой «чтобы
   кнопка не прилипла к пилюле» — но зазор между ними держит gap: 10 шапки, и
   распорка добавляла к нему свои шесть только тогда, когда места и так не
   хватало. В колонке 620 это ровно те 6 px, которых недоставало адресу: с
   ними «ул. Браузерная, 1» резался многоточием на ровном месте (замер
   сторожа: нужно 174, свободно 170). */
.chat-thread-sp { flex: 1; min-width: 0; }

.chat-thread-addr:focus-visible {
  outline: 2px solid rgb(var(--brand-500) / 1);
  outline-offset: 3px;
  border-radius: 3px;
}

/* ── «Следующий ждущий» (Ч3-3, решение П-7; форма .n3-next-btn Н-3) ──────
   Канон — макет СПИСОК_3_Очередь: выход из разговора ВПЕРЁД, а не назад в
   список. Ожидание — чипом ВНУТРИ кнопки: решение принимается до нажатия, а
   строка шапки остаётся одной. Кто ждёт и в каком канале — в подсказке
   (счёт §4 сборки Н-СБ: в строке 620 px имени не хватает 50 px, а в наряде
   слева оно стоит первой же строкой).

   Кнопка набрана ЧЕРНИЛАМИ, а не охрой: охра в чате значит клиентский канал
   (Режим 1 защиты), и раскрасить ею навигацию значило бы соврать о канале.
   Пара --neutral-900 / --surface-0 самопереворачивается вместе с бандлом: в
   тёмной чернила становятся светлой плитой, подпись — тёмной. */
.chat-next-btn {
  flex: none;
  display: inline-flex;
  align-items: center;
  font-family: var(--chat-face-narrow);
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  white-space: nowrap;
  padding: 9px 12px 8px;
  border-radius: 6px;
  background: rgb(var(--neutral-900) / 1);
  color: rgb(var(--surface-0) / 1);
  text-decoration: none;
  transition: background-color .12s;
}
/* Чип ожидания: моноширинный набор без трекинга — то же различение «слово
   капсом / число моноширинной», что у счётчика бейджа канала и у таймера
   наряда. Подложка — бумага панели, поверх чернил кнопки. */
.chat-next-btn em {
  font-family: var(--chat-face-mono);
  font-style: normal;
  font-size: 11px;
  letter-spacing: 0;
  padding: 2px 5px 1px;
  margin-left: 8px;
  border-radius: 3px;
  background: rgb(var(--surface-0) / 1);
  color: rgb(var(--neutral-900) / 1);
}
.chat-next-btn:hover { background: rgb(var(--neutral-800) / 1); }
.chat-next-btn:focus-visible {
  outline: 2px solid rgb(var(--brand-500) / 1);
  outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  .chat-next-btn { transition: none; }
}

/* ── Полоса бейджей каналов (ЧР-4, блок 3 сборки Н-СБ) ───────────────────
   Своя полоса под шапкой, слева: шапка отвечает на вопрос «какой это
   объект», бейджи — «в каком из его каналов я сейчас», и слипшись, они
   читались бы одной строкой заголовка.

   Паддинг 8/16 против прежних 10/16 и кнопки на пункт у́же (правило ниже) —
   те самые 8 px, из которых складываются 48 вместо 56 (эталон 50; у него
   счётчик в бейдже стоит всегда, у нас — только при непрочитанном). Кнопки
   сегмента стоят ещё в панели настроек и в переключателе отделки, поэтому
   паддинг правится ЗДЕСЬ, в месте полосы, а не в общем .chat-seg-btn: ЧР-6
   разбирает панель отдельно, и менять её геометрию за глаза нельзя. */
.chat-thread-seg {
  flex: none;
  padding: 8px 16px;
  background: rgb(var(--surface-0) / 1);
  border-bottom: 1px solid rgb(var(--neutral-200) / 1);
}
.chat-thread-seg .chat-seg-btn { padding: 7px 12px 6px; }

/* ── Окно переписки треда хаба (ЧР-4, блок 5 сборки Н-СБ) ────────────────
   Поля 18/24/20 — эталонные (.feed), против прежних 16 по кругу утилитой
   p-4: письмо дышит по бокам ровно настолько, чтобы охряный кант отделки не
   упирался в пузырь.

   КОЛОНКА ПИСЕМ 800 px — ТЕПЕРЬ ТОЛЬКО ЗАПАСНОЙ ПОЛ (В9, 14.08.2026).
   С ЧР-5 правая колонка контекста (280 px) сужает тред до 620, и письмо
   становится 572 px само (550 под «Кантом»: его паддинг двигает вправо всё
   поле канала, счёт — в секции ЧР-5 в конце файла). До В9 колонки не было
   у личного диалога, тред занимал всю ширину панели (≈900 px на 1440), и
   кап был единственным, что мешало пузырям разъехаться по краям почти
   метровой строки. Вердикт В9 дал колонку и dm — теперь 572 у личного
   диалога это ТОЖЕ следствие колонки, а не число.

   ПОЧЕМУ ЧИСЛО ВСЁ-ТАКИ ОСТАЛОСЬ И ПОЧЕМУ ОНО 800. Колонку можно выключить
   настройкой «Правая колонка: Скрывать» (ЧР-6) — и тогда без капа лента
   любого треда хаба, личного и канального одинаково, снова стала бы брусом
   в 852 px (замер: колонка треда 900 минус поля окна 24 + 24 — сторож
   test_dm_column_switches_off_by_the_same_setting печатает оба числа).
   Кап держит этот случай и держит ОБЕ ветки одним правилом: под
   выключенной колонкой личное и канальное письмо по-прежнему равны друг
   другу, а вердикт В9 просил равенства мер, а не конкретной цифры. Опускать
   пол до 572 значило бы отменить смысл самой настройки — «скрыть колонку»
   перестало бы что-либо давать ленте.

   ГЕЙТ — .chat-hub-feed, А НЕ .chat-channel-feed (ЧР-5). До ЧР-5 правило
   висело на классе КАНАЛА, и грамматика окна доставалась только каналам:
   личный диалог (канала у него нет, П-4) рисовался полями по 16 и без
   колонки — то есть ровно тем «брусом во всю панель», от которого ЧР-4
   лечил письмо (Ф-2). Теперь цвет поля называет chat-channel-feed, а
   грамматику — chat-hub-feed, и она одна на все треды хаба.

   Второй гейт .chat-thread-pane оставлен: chat.css подключён целиком на
   четырёх поверхностях, а грамматика эта — хабовая (в карточке объекта
   лента и так в колонке, в ЛК и PWA ширина экрана меньше 800 по
   построению). Он же держит специфичность выше утилиты p-4 из бандла.
   Правило целится в .chat-thread — обёртку писем из _messages.html;
   container-type у неё остаётся, а порог узкой ленты (560 px) от 800 не
   зависит. */
.chat-thread-pane .chat-hub-feed { padding: 18px 24px 20px; }
.chat-thread-pane .chat-hub-feed > .chat-thread {
  max-width: 800px;
  margin: 0 auto;
}

/* Пустая правая панель — «день закрыт» (спека, экран 3). Тот же строй, что
   у пустой полосы очереди: сначала итог, потом что будет дальше. */
.chat-conv-empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 24px;
  text-align: center;
}
.chat-conv-empty b {
  font-family: var(--chat-face-narrow);
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.6px;
  word-spacing: 0.28em;
  text-transform: uppercase;
  color: rgb(var(--neutral-900) / 1);
}
.chat-conv-empty span {
  max-width: 34ch;
  font-size: 13px;
  line-height: 1.45;
  /* Тот же чернильный ряд, что у пустого наряда очереди (.chat-nar-empty):
     два «дня закрыто» на одном экране обязаны быть одного тона. */
  color: rgb(var(--neutral-600) / 1);
}

/* ── Узкий режим шапки: две строки вместо одной ────────────────────────
   Адрес садится на два пункта и переносится словами (на 390 многоточие
   съело бы половину адреса), кнопки уходят вниз своей строкой — распорка
   получает flex-basis:100% и разрывает строку намеренно. Это узкий режим
   шапки, а не отмена решения ЧР-4: 54 px посчитаны для 1440, где всё стоит
   рядом.

   ПОРОГ СЧИТАН ПО ПАНЕЛИ ЛЕНТЫ, А НЕ ПО ОКНУ, но записан в @media — и
   поэтому условий два (ЧР-5, ревью собственного замера):

     · до 560 px экрана — телефон и планшет в портрете: рельса нет
       (мастер/деталь), панель ленты равна экрану, и media здесь ровно та же
       величина, что container;
     · 1024…1100 px экрана — планшет в альбоме: рельс уже стоит (300) и
       сайдбар CRM развёрнут (240), так что панели остаётся 484…560 — те же
       узкие условия при широком окне.

   Между ними (560…1024) панель шире 560 всегда, и узкий режим не нужен.
   Контейнерным запросом это выразилось бы честнее, но им нельзя
   стилизовать саму шапку (см. её правило выше), а разъехаться шапке с её
   же детьми нельзя тем более: половина правил в @container, половина в
   @media — это и есть тот баг с буквами в столбик. */
@media (max-width: 560px),
       (min-width: 1024px) and (max-width: 1099.98px) {
  .chat-thread-header { flex-wrap: wrap; }
  .chat-thread-addr { font-size: 16.5px; white-space: normal; }
  .chat-thread-addr span { overflow: visible; overflow-wrap: break-word; }
  .chat-thread-sp { flex-basis: 100%; }
  .chat-next-btn { margin-left: 0; }
  /* «Контекст» на второй строке остаётся ЗНАЧКОМ: со словом строка кнопок
     (110 + 262 + зазор) не влезает в 358 px телефона и ломается на третью.
     Слово несут aria-label и подсказка — вслух и по наведению кнопка
     называется полностью. */
  .chat-ctx-btn { padding: 7px 9px 6px; gap: 0; }
  .chat-ctx-btn span { display: none; }
}

/* ═══════════════════════════════════════════════════════════════════════
   КАРКАС ХАБА «ЧАТ»: рабочее поле, рельс, наряд очереди, строки
   (этап ЧР, задача ЧР-3, 07.08.2026)

   Канон — сборка НСБ_хаб.html (кадры НСБ_хаб_кадр-{светлая,тёмная}.png,
   спецификация НСБ_СБОРКА.md). Взято: голова рельса и строки списка от
   Н-2 «Беседа», вертикальный наряд очереди от Н-1 «Диспетчерская».

   ЧТО ИМЕННО ЗДЕСЬ ПЕРЕДЕЛАНО И ПОЧЕМУ (разбор ЧР-1):

   Ф-1 «этажерка хрома». Раздел открывался полосой-заголовком «Чат»:
   гигантский кегль и две кнопки в пустой строке — ~75 px, самое
   не-дашбордное место продукта. Полоса снята целиком, её кнопки стоят в
   ГОЛОВЕ РЕЛЬСА рядом с поиском, а рабочее поле развёрнуто во всю высоту
   окна: было 680 px, стало 803 (замер на 1440×900).

   Ф-3 «карусель очереди». Полоса «Ждут ответа» была горизонтальной лентой
   карточек в колонке ~290 px: видно полторы карточки, на 390 третья
   резалась краем. Развёрнута в вертикальный НАРЯД — столбик строк с
   номерами, таймерами и линейками ожидания, ничего не обрезано.

   Ф-4 «шумный список». Из строки ушли тройка капс-чипов каналов с
   прочерками-пустышками и метка ожидания; адрес теперь ПЕРЕНОСИТСЯ, а не
   режется многоточием — длинный почтовый адрес читается целиком.

   ЦВЕТ ЗНАЧИТ РОВНО ОДНО — КАНАЛ. Охра (--brand-500/600) — «Клиенту»,
   средний графит (--neutral-500) — «Исполнителям», чернила
   (--neutral-800) — «Офис»: та же лестница «от самого открытого к самому
   закрытому», по которой заведены сами каналы (chat.OBJECT_CHANNEL_KINDS).
   Исключение одно и осознанное: НОМЕР и ТАЙМЕР наряда набраны охрой не как
   канал, а как знак направления Н-1 — «столбик, который тает к дню
   закрытому». Голова наряда держит вес ЧЕРНИЛАМИ (кромка слева, бейдж
   «Сейчас»), у остальных строк отбивка охряная и приглушённая.

   ЗАМЕРЫ КОНТРАСТА (светлая / тёмная), «на панели» = --surface-0:
     заголовок строки   --neutral-900   17.85 / 13.21
     превью и подписи   --neutral-600    7.58 / 7.96
     подпись-подсказка  --neutral-500    4.76 / 5.98
     номер и таймер     --brand-600      6.58 / 5.38
     знак «Клиенту»     --brand-500      5.00 / 4.25   (знак, порог 3:1)
     знак «Исполнит.»   --neutral-500    4.76 / 5.98
     знак «Офис»        --neutral-800   14.63 / 11.61
     бейдж числа/«Сейчас» --surface-0 на --neutral-900  17.85 / 13.21
     счётчик строки     --brand-on на --brand-500       5.00 / 4.60
     «Написать»         --brand-on на --brand-500       5.00 / 4.60
     линейка ожидания   --brand-500 на --neutral-200    4.05 / 3.16 (знак)
   На подложке головы наряда и открытой строки (--brand-100): чернила
   16.10 / 12.65, превью 6.83 / 7.62, номер и таймер 5.93 / 5.15 — подложка
   ничего не роняет ниже AA. Время строки на этой подложке взято
   --neutral-600, а не --neutral-500: последний давал бы 4.29 в светлой.

   ТЁМНАЯ ПРИХОДИТ САМА, своих правил у неё здесь нет ни одного: в секции
   нет ни одного литерального цвета, только токены бандла (--neutral-*,
   --brand-*, --surface-0), а они переворачиваются целиком (input.css,
   блок html.themable[data-theme="dark"]). Пара к каждому правилу — тот же
   токен с другим значением, поэтому дублировать селекторы под тёмную было
   бы не «полнотой», а вторым источником правды.

   ЭТО НЕ ОТДЕЛКА: секция стоит в грамматической части файла, и гейт
   отделок (класс chat-skin--<слаг> в каждом селекторе) ей не нужен и не
   положен — отделка красит ЛЕНТУ канала, а рельс принадлежит разделу
   целиком и один на всех.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Рабочее поле во всю высоту окна (Ф-1) ─────────────────────────────
   Высота считается ВЫЧИТАНИЕМ ЭТАЖЕЙ КАРКАСА CRM, а не подобранной
   константой: топбар — sticky h-14 (crm/_topbar.html, 56 px, число зашито
   и в шапке карточки объекта), футер — py-3 при строке 16 px плюс рамка =
   41 px (crm/base.html). Футер остаётся (вердикт User): он стоит во всём
   CRM, и убирать его ради +40 px ленте значило бы делать чат единственным
   разделом без низа.

   Две декларации подряд — не опечатка: 100vh на мобильных считается от
   окна БЕЗ адресной строки и даёт лишние ~60 px прокрутки, 100dvh считает
   честно. Браузер без dvh просто не поймёт вторую строку и оставит первую
   (тот же приём — в треде кабинета клиента). Из dvh-варианта вычтена и
   безопасная зона: на телефоне футер лежит В ПОТОКЕ и её уже носит.

   min-height 420 — пол на совсем низких окнах: ниже этого лента с
   композером перестаёт быть лентой, и честнее отдать прокрутку странице.

   ЭТАЖИ ОБЪЯВЛЕНЫ НА ТРОИХ (ЧР-6 → П-2). Панель настроек «Разворот» и ящик
   находок встают РОВНО в рабочее поле — тот же верх, тот же низ, — и свой
   набор констант у каждой разъехался бы с этим при первой же правке футера:
   панель повисла бы над лентой или под ней. Правило одно, потребителя три.
   Равенство констант ЖИВОМУ хрому (топбар и прилипший футер) стережёт
   браузерный test_hub_frame_constants_match_the_real_chrome — CSS спросить
   у DOM не умеет, поэтому правда сводится сторожем, а не объявлением. */
.chat-hub,
.chat-set-panel,
.chat-find-panel {
  --chat-hub-top: 56px;
  --chat-hub-foot: 41px;
}
.chat-hub {
  display: flex;
  align-items: stretch;
  min-height: 420px;
  height: calc(100vh - var(--chat-hub-top) - var(--chat-hub-foot));
  height: calc(100dvh - var(--chat-hub-top) - var(--chat-hub-foot)
               - env(safe-area-inset-bottom, 0px));
  overflow: hidden;
  background: rgb(var(--surface-0) / 1);
}

/* ── Рельс ─────────────────────────────────────────────────────────────
   300 px — ширина сборки: каждые лишние 20 px рельса уходят у ленты, а
   меньше 300 голова рельса (поиск + шестерёнка) встаёт в две строки.
   Ниже lg рельс занимает экран целиком: там работает мастер/деталь
   (правило в шапке файла), и панели видны по одной. */
.chat-rail {
  flex: none;
  width: 100%;
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: rgb(var(--surface-0) / 1);
}
/* Панель ленты. С ЧР-5 она РЯД из двух колонок — тред и контекст объекта
   (правила колонки — в секции ЧР-5 ниже); до этого была одной колонкой
   сверху вниз, и шапка с композером стояли её прямыми детьми. Сами эти
   дети переехали внутрь .chat-thread-col, разметка их не меняла. */
.chat-thread-pane {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: row;
  position: relative;
}
@media (min-width: 1024px) {
  .chat-rail {
    width: 300px;
    border-right: 1px solid rgb(var(--neutral-200) / 1);
  }
}

/* ── Голова рельса: поиск · шестерёнка · «Написать» ────────────────────
   Три инструмента списка стоят НАД СПИСКОМ, а не над всем экраном: это и
   есть замена полосе-заголовку «Чат» (Ф-1). Поиск с шестерёнкой в одной
   строке, «Написать» во всю ширину под ними — форма Н-2. */
.chat-railhead {
  flex: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 12px;
  border-bottom: 1px solid rgb(var(--neutral-200) / 1);
}
.chat-railrow {
  display: flex;
  align-items: center;
  gap: 7px;
}
.chat-rsearch {
  position: relative;
  flex: 1;
  min-width: 0;
}
/* Лупа — метка поля, а не кнопка: клики уходят полю под ней. */
.chat-rsearch svg {
  position: absolute;
  left: 9px;
  top: 50%;
  transform: translateY(-50%);
  width: 15px;
  height: 15px;
  color: rgb(var(--neutral-500) / 1);
  pointer-events: none;
}
.chat-rsearch input {
  width: 100%;
  height: 34px;
  padding: 0 6px 0 27px;
  border: 1px solid rgb(var(--neutral-300) / 1);
  border-radius: 6px;
  background: rgb(var(--surface-0) / 1);
  font-size: 13.5px;
  line-height: 1;
  color: rgb(var(--neutral-900) / 1);
}
.chat-rsearch input:focus {
  outline: none;
  border-color: rgb(var(--brand-500) / 1);
  box-shadow: 0 0 0 1px rgb(var(--brand-500) / 1);
}
/* Подпись называет все три вида поиска разом («объект, человек, текст
   сообщений», П-11), а рельс узок: 13.5-м кеглем она обрывается на
   полуслове. Меньше только ПОДПИСЬ, сам ввод остаётся 13.5 — набранное
   важнее подсказки. */
.chat-rsearch input::placeholder {
  font-size: 12px;
  color: rgb(var(--neutral-500) / 1);
}
.chat-rbtn {
  flex: none;
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid rgb(var(--neutral-300) / 1);
  border-radius: 6px;
  background: rgb(var(--surface-0) / 1);
  color: rgb(var(--neutral-500) / 1);
  cursor: pointer;
}
.chat-rbtn:hover {
  color: rgb(var(--neutral-900) / 1);
  background: rgb(var(--neutral-50) / 1);
}
.chat-rbtn svg {
  width: 18px;
  height: 18px;
}
.chat-rnew {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 34px;
  border: 0;
  border-radius: 6px;
  background: rgb(var(--brand-500) / 1);
  color: rgb(var(--brand-on) / 1);
  font-family: var(--chat-face-narrow);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
}
.chat-rnew:hover { background: rgb(var(--brand-600) / 1); }
.chat-rnew svg {
  width: 16px;
  height: 16px;
}

/* ── Наряд «Ждут ответа» (Ф-3) ─────────────────────────────────────────
   Стоит НАД окном списка честной колонкой: список забирает остаток и
   прокручивается сам. Прежний приём (sticky внутри общего скроллера)
   держал то же самое, но наряд считался частью списка и уезжал первым же
   движением колеса на длинной очереди.

   ═══ КЭП ВЫСОТЫ (ревью ЧР-3, C-1) ═══════════════════════════════════
   Первая редакция ставила наряду flex:none — «сколько ждущих, столько и
   строк». В рабочем дне ждущих трое, и на кадрах эталона это верно; но
   очередь не ограничена ничем (chat.awaiting_queue отдаёт всех), а
   .chat-hub режет переполнение. Замер ревью: 8 ждущих → окно списка
   256 px, 12 → 52 px, 20 → наряд обрезан снизу и полосы прокрутки нет
   НИГДЕ: последняя строка невидима и некликабельна. Это ровно Ф-3
   («обрезка = выглядит сломанным»), развёрнутая на 90°.

   Лечится кэпом ПО ВЫСОТЕ, а не по числу строк. Дашборд, донор языка,
   режет свой наряд числом (ACTIONS_LIMIT = 8, «не раздуваем экран») и
   остаток роняет молча — здесь так нельзя: ждущий, выпавший из наряда, и
   есть тот, кому не ответили. Поэтому наряд ограничен высотой и
   прокручивает СВОИ строки, а голова с числом стоит над прокруткой и
   всегда говорит, сколько их на самом деле.

   БАЗА ПРОЦЕНТА — .chat-raillist, окно рельса под головой (803 − 97 =
   706 px на 1440 × 900), а не весь рельс: .chat-nar лежит внутри цели
   свапа, и делить наряду со списком нужно ровно то, что есть у них
   двоих. Все числа ниже — замеры со стенда, не прикидки:

     строка наряда   51 px (одноэтажная по построению — адрес режется)
     голова с чертой 42 px
     наряд эталона   195 px = 3 строки = 27,6 % базы → кэп его НЕ трогает
     кэп 45 %        318 px = голова + 5,4 строки
     остаток списку  388 px = 4,9 строки по 78 px

   СТРОКА СПИСКА 78, А НЕ 74 (приёмка ЧР, вердикт В3 — и заодно починка
   числа). 74 здесь стояло с ЧР-3 и было СРЕДНИМ по окну (512 / 6,9), а не
   строкой: НСБ_СБОРКА §2 меряет строку эталона в «61 (одна строка адреса) …
   115 (почтовый адрес в 4 строки)», и наш стенд до вердикта давал ровно 61.
   Вердикт вернул в строку подзаголовок с именем клиента — 16 px кегля плюс
   1 px отбивки, — и опорная строка стала 78. Пол списка (четыре строки с
   разделителем дня) вырос 4 × 61 + 31 = 275 → 4 × 78 + 31 = 343 px.

   КЭП ОТ ЭТОГО НЕ ДВИНУЛСЯ, и это счёт, а не везение: 388 > 343 с запасом
   45 px, то есть на длинной очереди списку остаётся 4,9 строки вместо
   прежних 6,4. Пробовали 43 % (402 px, запас 59 — прежний по величине), но
   отдавать наряду четверть строки не за что: пол держится, а наряд —
   единственное место, где живёт срок ожидания.

   Замеры при 3 / 8 / 12 / 20 ждущих: наряд 195 / 318 / 318 / 318, окно
   списка 511 / 388 / 388 / 388, прокрутка страницы 0 везде. Сторожа —
   tests/browser/test_chat_frame.py (длинная очередь и короткая).

   flex: 0 1 auto (а не none) — вторая половина того же: на низком окне
   сжимается наряд, а не выдавливается список.
   ═══════════════════════════════════════════════════════════════════ */
.chat-nar {
  flex: 0 1 auto;
  min-height: 0;
  max-height: 45%;
  display: flex;
  flex-direction: column;
  background: rgb(var(--surface-0) / 1);
  border-bottom: 3px double rgb(var(--neutral-800) / 1);
}
/* Голова — ВНЕ прокрутки строк: «ЖДУТ ОТВЕТА · 14» обязано стоять на
   месте, пока листают наряд. Поля секции переехали сюда же: раньше
   .chat-nar держал padding 11/12, а строки выходили из него
   отрицательными полями — внутри скроллера это дало бы горизонтальное
   переполнение и вернуло бы полосу прокрутки вбок, то есть саму Ф-3.
   Геометрия при этом бит-в-бит прежняя: голова от 12 px слева, строка от
   края рельса с отступом 15. */
.chat-nar-head {
  flex: none;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 11px 12px 8px;
}
.chat-nar-rows {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.chat-nar-title {
  font-family: var(--chat-face-narrow);
  font-size: 11.5px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.9px;
  word-spacing: 0.28em;
  text-transform: uppercase;
  color: rgb(var(--neutral-900) / 1);
}
.chat-nar-hint {
  font-family: var(--chat-face-narrow);
  font-size: 10.5px;
  line-height: 1;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: rgb(var(--neutral-500) / 1);
}
.chat-nar-hint::before { content: "·"; margin-right: 6px; }
/* Число — чернилами, а не цветом: см. шапку секции. */
.chat-nar-count {
  margin-left: auto;
  font-family: var(--chat-face-mono);
  font-size: 12px;
  line-height: 1;
  padding: 5px 7px 4px;
  background: rgb(var(--neutral-900) / 1);
  color: rgb(var(--surface-0) / 1);
}

/* Строка наряда: номер · адрес с именем · срок с линейкой. Три колонки
   сетки, две строки — номер идёт через обе (grid-row: 1 / span 2).
   Отрицательные поля выводят строку на края рельса: подложка головы и
   отбивки слева упираются в рамку столбца, как в наряде дашборда. */
.chat-nar-row {
  position: relative;
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr) auto;
  column-gap: 9px;
  align-items: start;
  padding: 7px 10px 7px 15px;
  border-top: 1px solid rgb(var(--neutral-200) / 1);
  color: inherit;
  text-decoration: none;
}
.chat-nar-row:first-of-type { border-top: 0; }
.chat-nar-row:hover { background: rgb(var(--neutral-50) / 1); }
.chat-nar-num {
  grid-row: 1 / span 2;
  padding-top: 1px;
  font-family: var(--chat-face-mono);
  font-size: 15px;
  font-weight: 700;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  color: rgb(var(--brand-600) / 1);
}
.chat-nar-top {
  grid-column: 2;
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
/* Адрес в наряде РЕЖЕТСЯ многоточием — в отличие от строки списка ниже.
   Наряд отвечает на вопрос «за что берёшься», и строка в нём обязана быть
   одинаковой высоты у всех: столбик, который тает, читается только пока
   он столбик. Полный адрес стоит строкой ниже, в списке. */
.chat-nar-addr {
  min-width: 0;
  font-family: var(--chat-face-narrow);
  font-size: 14.5px;
  font-weight: 700;
  line-height: 1.2;
  color: rgb(var(--neutral-900) / 1);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chat-nar-who {
  grid-column: 2;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 7px;
  margin-top: 3px;
  font-size: 12.5px;
  line-height: 1.25;
  color: rgb(var(--neutral-600) / 1);
}
.chat-nar-who span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Таймер охрой — знак направления Н-1, а не цвет канала (см. шапку). */
.chat-nar-time {
  grid-column: 3;
  grid-row: 1;
  font-family: var(--chat-face-mono);
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: rgb(var(--brand-600) / 1);
}
/* Линейка ожидания: длина = сколько ждут, полная шкала — шесть часов
   (chat.QUEUE_FULL_WAIT). Доля приезжает переменной со строки, поэтому ни
   пикселей в разметке, ни JS. */
.chat-nar-bar {
  grid-column: 3;
  grid-row: 2;
  width: 64px;
  height: 3px;
  margin-top: 5px;
  background: rgb(var(--neutral-200) / 1);
}
.chat-nar-bar i {
  display: block;
  height: 100%;
  width: calc(var(--chat-queue-fill, 0) * 100%);
  background: rgb(var(--brand-500) / 1);
}
/* Голова наряда — за неё берёшься сейчас: подложка, чернильная кромка
   слева и бейдж «Сейчас». Вес чернилами, а не цветом (см. шапку). */
.chat-nar-row--now,
.chat-nar-row--now:hover { background: rgb(var(--brand-100) / 1); }
.chat-nar-row--now::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: rgb(var(--neutral-900) / 1);
}
/* Отбивка охрой у прочих строк наряда — как у карточек «Требует действия»
   дашборда: тот же знак, приглушённый до второго плана. */
.chat-nar-row:not(.chat-nar-row--now)::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: rgb(var(--brand-500) / 1);
  opacity: 0.55;
}
.chat-nar-now {
  flex: none;
  font-family: var(--chat-face-narrow);
  font-size: 9.5px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  padding: 4px 5px 3px;
  background: rgb(var(--neutral-900) / 1);
  color: rgb(var(--surface-0) / 1);
}
/* Знак канала — 9×9, та же лестница цветов, что у чипа «Следующего
   ждущего» в шапке треда (.chat-ch i). */
.chat-nar-dot {
  display: block;
  flex: none;
  width: 9px;
  height: 9px;
}
.chat-nar-dot--client { background: rgb(var(--brand-500) / 1); }
.chat-nar-dot--object { background: rgb(var(--neutral-500) / 1); }
.chat-nar-dot--staff { background: rgb(var(--neutral-800) / 1); }

/* Опустел — день закрыт: конец работы, а не пустой экран. Поля секции
   переехали в голову (см. выше), поэтому свои — здесь. */
.chat-nar-empty {
  margin: 0 12px 11px;
  padding: 11px 12px 12px;
  border: 1px dashed rgb(var(--neutral-300) / 1);
}
.chat-nar-empty b {
  display: block;
  font-family: var(--chat-face-narrow);
  font-size: 13.5px;
  font-weight: 700;
  color: rgb(var(--neutral-900) / 1);
}
.chat-nar-empty span {
  display: block;
  margin-top: 2px;
  font-size: 12.5px;
  line-height: 1.35;
  color: rgb(var(--neutral-600) / 1);
}

/* ── Окно списка ───────────────────────────────────────────────────────
   Цель свапа (#chat-dialog-list) — колонка, прокручивается только её
   нижняя часть: наряд закреплён сверху, строки ездят под ним. */
.chat-raillist {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.chat-list-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* ── Разделитель дня ───────────────────────────────────────────────────
   Подпись та же, что в ленте («Сегодня · 4 августа»): один язык дат на
   список и на переписку. Рисунок — линия ВПРАВО, а не с двух сторон:
   в 300 px рельса двусторонняя отбивка съедает саму подпись. */
.chat-list-day {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 13px 12px 5px;
  font-family: var(--chat-face-narrow);
  font-size: 10.5px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 2px;
  word-spacing: 0.28em;
  text-transform: uppercase;
  color: rgb(var(--neutral-500) / 1);
}
.chat-list-day::after {
  content: "";
  flex: 1;
  height: 1px;
  background: rgb(var(--neutral-200) / 1);
}

.chat-list-empty {
  padding: 32px 16px;
  text-align: center;
  font-size: 13px;
  color: rgb(var(--neutral-500) / 1);
}

/* ── Строка списка ─────────────────────────────────────────────────────
   Объектная и личная строки — один рисунок и одна ссылка целиком:
   заголовок с временем и счётчиком, превью под ним. Мишеней внутри строки
   больше нет (чипы каналов сняты, Ф-4), поэтому обёртка с вложенной <a>
   ушла — обе строки теперь просто <a class="chat-row">. */
.chat-row {
  position: relative;
  display: block;
  padding: 9px 12px 10px 15px;
  border-top: 1px solid rgb(var(--neutral-200) / 1);
  color: inherit;
  text-decoration: none;
}
.chat-list-day + .chat-row { border-top: 0; }
.chat-row:hover { background: rgb(var(--neutral-50) / 1); }

/* Открытый разговор — охряный кант слева. Метки ожидания в строке нет:
   срок живёт в наряде и только там (отклонение В сборки), а кант отвечает
   на единственный оставшийся вопрос — «эту строку я сейчас и читаю». */
.chat-row--on,
.chat-row--on:hover { background: rgb(var(--brand-100) / 1); }
.chat-row--on::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 4px;
  background: rgb(var(--brand-500) / 1);
}

.chat-row-top {
  display: flex;
  align-items: baseline;
  gap: 8px;
}
/* Адрес ПЕРЕНОСИТСЯ, а не режется (Ф-4): в 300 px рельса многоточие
   съедало номер дома — самое опознаваемое в адресе. Цена честная и
   посчитанная: длинный почтовый адрес растит строку до ~115 px, обычный
   остаётся в одну строку. */
.chat-row-title {
  flex: 1;
  min-width: 0;
  font-family: var(--chat-face-narrow);
  font-size: 15.5px;
  font-weight: 700;
  line-height: 1.2;
  color: rgb(var(--neutral-900) / 1);
  overflow-wrap: break-word;
}
.chat-row-at {
  flex: none;
  font-family: var(--chat-face-mono);
  font-size: 11.5px;
  color: rgb(var(--neutral-500) / 1);
}
/* На подложке открытой строки --neutral-500 даёт 4.29 в светлой — ниже AA
   для 11.5 px. Время там берёт --neutral-600 (6.83 / 7.62). */
.chat-row--on .chat-row-at { color: rgb(var(--neutral-600) / 1); }
.chat-row-unread {
  flex: none;
  font-family: var(--chat-face-mono);
  font-size: 11px;
  line-height: 1;
  padding: 4px 5px 3px;
  border-radius: 999px;
  background: rgb(var(--brand-500) / 1);
  color: rgb(var(--brand-on) / 1);
}
/* Имя клиента под адресом — тем же приглушённым чернилом, что превью
   (--neutral-600). С приёмки ЧР (вердикт В3) строка стоит в КАЖДОМ объекте
   списка, а не только в результатах поиска: спека называет строку «адрес +
   имя клиента + последнее сообщение». Стоит она 17 px (16 кегля плюс 1
   отбивки) — из этого числа пересчитан пол списка у наряда выше: опорная
   строка 61 → 78. Разбор — _object_row в crm_chat.py. */
.chat-row-sub {
  display: block;
  margin-top: 1px;
  font-size: 12.5px;
  line-height: 1.3;
  color: rgb(var(--neutral-600) / 1);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* «Личное» — приписка В СТРОКЕ заголовка, а не отдельной строкой под ним
   (форма Н-2): третья строка ради одного слова растила бы строку списка. */
.chat-row-kind {
  margin-left: 6px;
  font-family: var(--chat-face-narrow);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  white-space: nowrap;
  color: rgb(var(--neutral-500) / 1);
}
.chat-row-prev {
  display: block;
  margin-top: 4px;
  font-size: 13px;
  line-height: 1.3;
  color: rgb(var(--neutral-600) / 1);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Непрочитанное — превью чернилами: строка, которую ещё не читали, должна
   читаться первой. */
.chat-row--fresh .chat-row-prev { color: rgb(var(--neutral-900) / 1); }

/* ── Знак канала ───────────────────────────────────────────────────────
   Остался ровно у одного потребителя — чипа «Следующего ждущего» в шапке
   треда (_thread_header.html). Из строк списка чипы каналов сняты (Ф-4),
   в наряде свой знак (.chat-nar-dot), и держать здесь их состояния
   (--off / --on) больше не для кого. */
.chat-ch {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
  font-family: var(--chat-face-narrow);
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  white-space: nowrap;
  text-decoration: none;
  color: rgb(var(--neutral-600) / 1);
}
.chat-ch i {
  display: block;
  flex: none;
  width: 9px;
  height: 9px;
}
.chat-ch--client i { background: rgb(var(--brand-500) / 1); }
.chat-ch--object i { background: rgb(var(--neutral-500) / 1); }
.chat-ch--staff i { background: rgb(var(--neutral-800) / 1); }

.chat-row:focus-visible,
.chat-nar-row:focus-visible,
.chat-rbtn:focus-visible,
.chat-rnew:focus-visible {
  outline: 2px solid rgb(var(--brand-500) / 1);
  outline-offset: 2px;
}

/* ═══════════════════════════════════════════════════════════════════════
   ПРАВАЯ КОЛОНКА КОНТЕКСТА И УЗКИЕ РЕЖИМЫ ХАБА
   (этап ЧР, задача ЧР-5, 07.08.2026)

   Канон — сборка НСБ_хаб.html, блок 7 (донор Н-3 «Штаб»): `.ctx` / `.pnl` /
   `.ph` / `.pstep` / `.thumbs` / `.facts` / `.cardlink` — форма бит-в-бит,
   имена наши. Состав по вердикту User: «Следующий шаг» · «Вложения» ·
   «Объект» · «Карточка объекта →»; панели «Читают» нет (её смысл несёт
   плашка над композером — ЧР-4).

   ЧТО ЭТА КОЛОНКА ДЕЛАЕТ С ЛЕНТОЙ. Она ест ШИРИНУ, а не высоту: этажи хрома
   ЧР-4 не тронуты, зона переписки остаётся при своих 589 px (65,4 %) —
   замер того же браузерного сторожа, что и до ЧР-5. Колонки на 1440: рельс
   300 · тред 620 · контекст 280 = 1200 (экран минус сайдбар CRM 240) —
   ровно числа §2 сборки. Ширина письма при этом падает с капа 640 до 572
   сама: 620 − 24 − 24 полей окна. Кап 640 (.chat-msg) не трогали ни на
   пиксель — 572 это СЛЕДСТВИЕ колонки, и сторож в браузерных тестах
   стережёт именно следствие.

   ПОЧЕМУ ПОД «КАНТОМ» ЗАМЕР ДАЁТ 550, А НЕ 572. Не потому, что эталон
   «без канта»: эталон свёрстан РОВНО в канте, и его 572 сняты уже с ним.
   Разница в том, ЧЕМ кант нарисован. У эталона лента (.feed) —
   position: absolute; left: 0; right: 0 внутри .feedwrap, то есть растянута
   по ПАДДИНГ-БОКСУ предка (containing block абсолютного потомка), и охряная
   полоса лежит ПОД прозрачной лентой, ширины не отнимая. У нас кант —
   обычный padding-left: 22px В ПОТОКЕ на контейнере канала
   (правило chat-skin--kant, секция отделок Ч2-Б), и он сдвигает всё разом:
   шапку, полосу бейджей, ленту, плашку канала, композер. Отсюда окно треда
   598 и колонка письма 550 под «Кантом» — против 620 и 572 под «Бумагой» и
   «Журналом».

   Грамматика прод-канта живёт с Ч2-Б, отделки заморожены вердиктом В-1, и
   ЧР-5 их не трогает: переводить кант на эталонную модель (полоса под
   абсолютной лентой) — отдельный вопрос User. Обе величины прибиты
   двусторонним браузерным сторожом ПО ОТДЕЛКАМ (test_chat_frame.py,
   test_letter_column_is_exact_under_every_skin): 550 под кантом, 572 под
   бумагой и журналом, ±1 px.

   ═══ ПОРОГ, НИЖЕ КОТОРОГО КОЛОНКИ НЕТ ══════════════════════════════════
   Порог считается от ТРЕДА, а не от экрана: колонка имеет смысл, пока после
   неё письму остаётся колонка, а не щель. Пол треда — 540 px (у эталона 620,
   письмо 572; при 540 письмо 492 — ещё колонка). Отсюда:

     экран = 540 (тред) + 300 (рельс) + сайдбар CRM
     сайдбар развёрнут (15rem = 240) → 1080 + 240 = 1320 → берём 1360
     сайдбар свёрнут   (4rem  =  64) → 1080 +  64 = 1144 → берём 1184

   ПОЧЕМУ MEDIA, А НЕ CONTAINER. Честная величина здесь — ширина панели
   ленты, и правило вёрстки проекта просит контейнерных единиц. Но
   `container-type` это `contain: layout`, а `contain: layout` делает элемент
   containing block'ом для position:fixed потомков — и модалка подтверждения
   письма клиенту (.chat-confirm, Ч3-4б, режим 2 защиты Р-4) живёт ВНУТРИ
   контейнера треда: её подложка перестала бы закрывать рельс и топбар, а
   само окно центрировалось бы по панели. Терять дисциплину предупреждения о
   письме клиенту ради синтаксиса запроса нельзя, поэтому здесь @media, а
   состояние сайдбара читается его же атрибутом (html[data-sb-state],
   crm/base.html) — это и делает счёт выше точным, а не приблизительным.

   Без атрибута (JS не отработал) действует правило развёрнутого сайдбара —
   то есть более осторожное из двух: колонка появится позже, но никогда не
   раньше, чем ей хватит места.

   ЧТО ВМЕСТО КОЛОНКИ НА УЗКОМ. Приём Н-3 — «складывание в шапку треда»:
   кнопка «Контекст» в шапке открывает ту же колонку поверх ленты. Разметка
   одна, приезжает с тредом всегда, и открытие ничего не грузит.

   ═══ 390: МАСТЕР/ДЕТАЛЬ ════════════════════════════════════════════════
   Правила видимости панелей — в ШАПКЕ ФАЙЛА (класс chat-show-conv), там же
   и порог 1024. Здесь только их следствие для колонки: ниже lg панель ленты
   занимает экран целиком, и третьей колонке места нет по построению — оба
   порога выше 1024, так что отдельного правила не нужно.

   ═══ ТЁМНАЯ ════════════════════════════════════════════════════════════
   Приходит сама: ни одного литерального цвета, кроме ОДНОГО осознанного —
   плашки подписи миниатюры (см. .chat-ctx-thumb span).

   ЗАМЕРЫ КОНТРАСТА (светлая / тёмная), «на панели» = --surface-0:
     шапка панели       --neutral-900   17.85 / 13.21
     подсказка справа   --neutral-500    4.76 /  5.98
     текст шага         --neutral-900   17.85 / 13.21
     подпись факта      --neutral-500    4.76 /  5.98
     значение факта     --neutral-900   17.85 / 13.21
     телефон-ссылка     --brand-600      6.58 /  5.38
     «Карточка объекта» --brand-700      9.00 /  6.39
     кнопка «Контекст»  --neutral-800   14.63 / 11.61
     кромка шага        --brand-500      5.00 /  4.25  (знак, порог 3:1)
     «Ещё N»            --neutral-600 на --neutral-100  6.92 / 7.06
     подпись миниатюры  #fff на плашке-литерале поверх фото: 18.84 на
       чёрном кадре, 12.77 на среднем, 7.66 на белом — плашка держит
       подпись читаемой на ЛЮБОМ снимке, ради чего она и непрозрачна на 74 %

   ИЗВЕСТНАЯ АНОМАЛИЯ, та же, что у ленты (§5 п. 1 сборки): панель
   (--surface-0) на поле колонки (--neutral-50) — 1,05 : 1 в светлой и
   1,08 : 1 в тёмной, то есть панели различает РАМКА (--neutral-200), а не
   заливка. Так нарисован и эталон, и дашборд-камертон; замер приведён,
   чтобы это было решением, а не сюрпризом.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Колонка треда ─────────────────────────────────────────────────────
   Панель ленты (#chat-conversation) с ЧР-5 стала РЯДОМ из двух колонок —
   её правило и flex-direction: row стоят в секции каркаса ЧР-3, там же
   position: relative (опора для колонки, открытой поверх ленты на узком
   экране: она ложится на панель, а не на окно — топбар и рельс не про этот
   разговор). Здесь — сама колонка треда: всё, что было прямыми детьми
   панели до ЧР-5. */
.chat-thread-col {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

/* ── Колонка контекста ─────────────────────────────────────────────────
   По умолчанию её НЕТ: показывают пороги ниже. Умолчание «скрыта» — то же
   решение, что у порогов: не хватило места — колонки нет, и щели вместо
   ленты не будет никогда.

   Крючок для ЧР-6 (переключатель «правая колонка» в панели настроек):
   класс chat-ctx-off на .chat-hub гасит колонку поверх любых порогов —
   серверу останется добавить один класс. */
.chat-ctx {
  display: none;
  flex: none;
  width: 280px;
  flex-direction: column;
  gap: 12px;
  min-height: 0;
  overflow-y: auto;
  padding: 14px;
  background: rgb(var(--neutral-50) / 1);
  border-left: 1px solid rgb(var(--neutral-200) / 1);
}
.chat-hub.chat-ctx-off .chat-ctx { display: none; }

/* Кнопка «Контекст» в шапке треда — тем же строем, что «Следующий ждущий»
   рядом: узкий капс, чернильная рамка, без заливки (она не действие дня, а
   вход в справку). */
.chat-ctx-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
  padding: 7px 11px 6px;
  border: 1px solid rgb(var(--neutral-300) / 1);
  border-radius: 4px;
  background: rgb(var(--surface-0) / 1);
  color: rgb(var(--neutral-800) / 1);
  font-family: var(--chat-face-narrow);
  font-size: 11.5px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.2px;
  word-spacing: 0.24em;
  text-transform: uppercase;
  cursor: pointer;
}
.chat-ctx-btn svg { width: 15px; height: 15px; }
.chat-ctx-btn:hover { border-color: rgb(var(--neutral-500) / 1); }
.chat-ctx-btn:focus-visible,
.chat-ctx-close:focus-visible,
.chat-ctx-cardlink:focus-visible,
.chat-ctx-objrow:focus-visible,
.chat-ctx-thumb:focus-visible {
  outline: 2px solid rgb(var(--brand-500) / 1);
  outline-offset: 2px;
}

/* Пороги (счёт — в шапке секции). Кнопка «Контекст» и колонка — ОДНА пара
   правил: где стоит колонка, там нет кнопки, и наоборот.

   ПОРЯДОК ЗДЕСЬ — ЧАСТЬ ПРАВИЛА. Оба селектора весят одинаково (0,1,0), и
   спор решает место в файле: базовые «колонки нет / кнопка есть» стоят ВЫШЕ,
   пороговые «колонка есть / кнопки нет» — ниже. Первая редакция ЧР-5 держала
   кнопку под порогами, и на 1440 она стояла рядом с уже развёрнутой
   колонкой — поймано браузерным сторожом (ctx_btn на 1440). */
@media (min-width: 1360px) {
  .chat-ctx { display: flex; }
  .chat-ctx-btn { display: none; }
}
@media (min-width: 1184px) and (max-width: 1359.98px) {
  html[data-sb-state="collapsed"] .chat-ctx { display: flex; }
  html[data-sb-state="collapsed"] .chat-ctx-btn { display: none; }
}
/* Свёрнутая колонка настройкой ЧР-6 не оставляет кнопку сиротой: спрятали
   насовсем — значит и входа нет. Порядок: правило стоит ПОСЛЕ порогов. */
.chat-hub.chat-ctx-off .chat-ctx-btn { display: none; }

/* ── Колонка поверх ленты (узкий экран) ───────────────────────────────
   Открывает и закрывает её chat.js классом на #chat-conversation; класс
   уезжает с любым свапом треда — открытый контекст не переживает смену
   разговора (он был про прежний).
   Правило тяжелее порогов по специфичности (два класса против одного),
   поэтому «открыто» побеждает «скрыто» на любой ширине. */
.chat-thread-pane.chat-ctx-open .chat-ctx {
  display: flex;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(320px, 100%);
  z-index: 20;
  box-shadow: -10px 0 24px -12px rgb(15 23 42 / 0.35);
}
.chat-ctx-close { display: none; }
.chat-thread-pane.chat-ctx-open .chat-ctx-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  align-self: flex-end;
  flex: none;
  width: 30px;
  height: 30px;
  margin: -4px -4px 0 0;
  border: 0;
  background: transparent;
  color: rgb(var(--neutral-600) / 1);
  cursor: pointer;
}
.chat-ctx-close svg { width: 18px; height: 18px; }
.chat-ctx-close:hover { color: rgb(var(--neutral-900) / 1); }

/* ── Панель колонки (форма .pnl эталона; цитата дашборда) ──────────────
   Белая плита в рамке, шапка узким капсом с трекингом .13em, подсказка
   рядом обычным начертанием. Акцент у «Следующего шага» — кромка 3 px
   сверху: единственная панель, которая зовёт ДЕЙСТВОВАТЬ. */
.chat-ctx-pnl {
  flex: none;
  background: rgb(var(--surface-0) / 1);
  border: 1px solid rgb(var(--neutral-200) / 1);
  padding: 12px 14px 13px;
}
.chat-ctx-pnl--step {
  border-top: 3px solid rgb(var(--brand-500) / 1);
  padding-top: 11px;
}
.chat-ctx-ph {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 9px;
}
.chat-ctx-ph b {
  font-family: var(--chat-face-narrow);
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.13em;
  word-spacing: 0.24em;
  text-transform: uppercase;
  color: rgb(var(--neutral-900) / 1);
}
.chat-ctx-ph i {
  font-style: normal;
  font-size: 11.5px;
  color: rgb(var(--neutral-500) / 1);
  margin-left: auto;
  white-space: nowrap;
}

/* «Следующий шаг»: знак кровли и одна строка текста. Знак — контур охрой,
   fill:none: это росчерк, а не иконка-плита. */
.chat-ctx-step {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  font-size: 14px;
  line-height: 1.35;
  color: rgb(var(--neutral-900) / 1);
}
.chat-ctx-step svg {
  width: 17px;
  height: 13px;
  flex: none;
  margin-top: 3px;
  stroke: rgb(var(--brand-500) / 1);
  fill: none;
  stroke-width: 6;
}

/* ── Вложения: сетка 2 × 4 ─────────────────────────────────────────────
   Семь плиток и «Ещё N» (§3-Г сборки): после снятия панели «Читают»
   колонка недобирала треть высоты, а тянуть нечем — у «Шага» и «Объекта»
   ровно столько строк, сколько есть. Растягивать плитку по высоте нельзя:
   120 × 80 это пропорция кадра, вытянутая плитка перестаёт читаться
   фотографией. Поэтому колонка не «дотягивается» до низа содержимым, а
   кнопка карточки прижата к нижнему краю — зазор ~110 px осознан. */
.chat-ctx-thumbs {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
}
.chat-ctx-thumb {
  position: relative;
  height: 80px;
  border-radius: 3px;
  overflow: hidden;
  background: rgb(var(--neutral-200) / 1);
  display: block;
  border: 0;
  padding: 0;
}
.chat-ctx-thumb img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
}
/* ПОДПИСЬ МИНИАТЮРЫ — ЕДИНСТВЕННЫЙ ЛИТЕРАЛ СЕКЦИИ, И ЭТО РЕШЕНИЕ (§5 п. 2
   сборки Н-СБ, донор Н-3). Плашка лежит НА ФОТОГРАФИИ, а фотография в
   тёмной теме не инвертируется: возьми она токен — в тёмной под белыми
   буквами оказалась бы светлая плашка, и подпись пропала бы совсем. Тёмная
   плашка + белые буквы читаются в обеих темах на любом кадре. Цена честная
   и названа в сборке: в тёплой тёмной палитре плашка читается синеватой —
   единственное холодное пятно кадра. */
.chat-ctx-thumb span {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 3px 6px 4px;
  font-family: var(--chat-face-mono);
  font-size: 10px;
  line-height: 1;
  background: rgb(15 23 42 / 0.74);
  color: #fff;
}
.chat-ctx-thumb--more {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 80px;
  background: rgb(var(--neutral-100) / 1);
  font-family: var(--chat-face-narrow);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 1.2px;
  word-spacing: 0.24em;
  text-transform: uppercase;
  color: rgb(var(--neutral-600) / 1);
  cursor: pointer;
}
.chat-ctx-thumb--more:hover { color: rgb(var(--neutral-900) / 1); }

/* ── Факты объекта (форма .facts эталона) ──────────────────────────────
   Две колонки: подпись узким капсом и значение. minmax(0,1fr) обязателен —
   длинное имя клиента иначе распирало бы колонку 280 px. */
.chat-ctx-facts {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 7px 10px;
  font-size: 13px;
}
.chat-ctx-facts dt {
  font-family: var(--chat-face-narrow);
  font-size: 10px;
  font-weight: 700;
  line-height: 1.5;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: rgb(var(--neutral-500) / 1);
}
.chat-ctx-facts dd {
  color: rgb(var(--neutral-900) / 1);
  line-height: 1.3;
  overflow-wrap: break-word;
}
.chat-ctx-facts a {
  color: rgb(var(--brand-600) / 1);
  text-decoration: none;
  font-variant-numeric: tabular-nums;
}
.chat-ctx-facts a:hover { color: rgb(var(--brand-700) / 1); }

/* «Карточка объекта →» — прижата к низу (margin-top:auto эталона): справка
   отдельно, действие отдельно. */
.chat-ctx-cardlink {
  margin-top: auto;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 11px 14px;
  text-decoration: none;
  border: 1.5px solid rgb(var(--brand-500) / 1);
  color: rgb(var(--brand-700) / 1);
  font-family: var(--chat-face-narrow);
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.09em;
  word-spacing: 0.24em;
  text-transform: uppercase;
}
.chat-ctx-cardlink:hover { background: rgb(var(--brand-50) / 1); }

/* ═══════════════════════════════════════════════════════════════════════
   НАЧИНКА КОЛОНКИ ДЛЯ ЛИЧНОГО ДИАЛОГА — СОБЕСЕДНИК
   (приёмка ЧР, вердикт В9 от 13.08.2026; направление Д-2 «Мост», лист
   мокапы_приёмки/dm_колонка/Д2_Мост.html, выбран User 14.08)

   ВСЁ, ЧТО НИЖЕ, ЖИВЁТ ВНУТРИ .chat-ctx ЭТАЛОНА. Ширина 280, поле
   --neutral-50, панели .chat-ctx-pnl в рамке, шапка .chat-ctx-ph узким
   капсом, графы .chat-ctx-facts, нижняя кнопка .chat-ctx-cardlink у самой
   кромки, ПОРОГИ ПОКАЗА (1360 / 1184), кнопка «Контекст» в шапке узкого
   треда, гашение настройкой «Правая колонка» и колонка поверх ленты на
   телефоне — НИ ОДНОГО СВОЕГО ПРАВИЛА: у личной колонки своя начинка, а не
   своя грамматика. Настройка chat-ctx-off гасит обе колонки ОДНИМ правилом
   (.chat-hub.chat-ctx-off .chat-ctx) — так и задумано: «правая колонка» это
   про место на экране, а не про то, что в ней сегодня лежит.

   ЧТО ЭТА КОЛОНКА ДЕЛАЕТ С ПИСЬМОМ. То же, что объектная: ест ШИРИНУ. Тред
   личного диалога был ~900 px и держался капом 800 (см. секцию окна
   переписки выше); теперь он 620, и письмо становится 572 — той же мерой,
   что у канальных под «Бумагой» и «Журналом». Это СЛЕДСТВИЕ колонки, а не
   новое число: кап 800 остался ровно там, где стоял, и работает только при
   выключенной колонке (настройка ЧР-6). Известное расхождение: под
   прод-«Кантом» канальное письмо 550, а не 572 (кант — padding в потоке,
   счёт в секции ЧР-5), у личного диалога канала нет вовсе и канта тоже —
   22 px в пользу личного названы на приёмке и приняты (следствие В1).

   НОВЫХ ЦВЕТОВ НЕТ, литералов нет: тёмная приходит сама токенами.
   ЗАМЕРЫ КОНТРАСТА (светлая / тёмная), сняты в живом браузере сторожом
   test_dm_column_ink_passes_aa_in_both_themes; «на панели» = --surface-0,
   у нижней кнопки лист другой — поле колонки --neutral-50:
     шапка панели       --neutral-900   17.85 / 13.21
     счётчик шапки      --neutral-500    4.76 /  5.98
     адрес объекта      --neutral-900   17.85 / 13.21
     роль и этап        --neutral-500    4.76 /  5.98
     метка «в канал»    --brand-600      6.58 /  5.38
     остаток «Ещё N»    --neutral-600    7.58 /  7.96
     подпись графы      --neutral-500    4.76 /  5.98
     значение графы     --neutral-900   17.85 / 13.21
     ссылка в графе     --brand-600      6.58 /  5.38
     текст «доступ»     --neutral-700   10.35 /  9.94
     «Карточка подр.»   --brand-700      8.60 /  6.93  (на поле колонки)
     кромка панели      --brand-500      5.00 /  4.25  (знак, порог 3:1)
     линейка «доступа»  --neutral-800   14.63 / 11.61  (знак, порог 3:1)
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Строка объекта: дверь в канал «Исполнители» ───────────────────────
   Форма — строка списка рельса в миниатюре: адрес узким полужирным, под
   ним мета обычным, метка перехода справа во всю высоту строки. Сетка, а
   не поток: метка обязана стоять на середине двух строк текста, а при
   длинном адресе не съезжать вниз. minmax(0,1fr) обязателен — иначе
   почтовый адрес распирает колонку 280 px. */
.chat-ctx-objrow {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: 9px;
  align-items: center;
  padding: 9px 0 10px;
  text-decoration: none;
  color: inherit;
  border-top: 1px solid rgb(var(--neutral-200) / 1);
}
/* Разделители МЕЖДУ строками, а не вокруг них: у панели уже есть рамка. */
.chat-ctx-objrow:first-of-type { border-top: 0; padding-top: 0; }
.chat-ctx-objrow:last-of-type { padding-bottom: 0; }
.chat-ctx-objrow b {
  grid-column: 1;
  display: block;
  font-family: var(--chat-face-narrow);
  font-size: 13.5px;
  font-weight: 700;
  line-height: 1.2;
  color: rgb(var(--neutral-900) / 1);
  overflow-wrap: break-word;
}
.chat-ctx-objrow span {
  grid-column: 1;
  display: block;
  margin-top: 3px;
  font-size: 11.5px;
  line-height: 1.25;
  color: rgb(var(--neutral-500) / 1);
}
.chat-ctx-objrow em {
  grid-column: 2;
  grid-row: 1 / span 2;
  font-style: normal;
  font-family: var(--chat-face-narrow);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1.2px;
  word-spacing: 0.2em;
  text-transform: uppercase;
  white-space: nowrap;
  color: rgb(var(--brand-600) / 1);
}
.chat-ctx-objrow:hover b { color: rgb(var(--brand-700) / 1); }

/* Остаток списка — подпись, а не мишень (вести его некуда). */
.chat-ctx-objmore {
  display: block;
  margin-top: 10px;
  padding-top: 9px;
  border-top: 1px solid rgb(var(--neutral-200) / 1);
  font-family: var(--chat-face-narrow);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 1.2px;
  word-spacing: 0.24em;
  text-transform: uppercase;
  color: rgb(var(--neutral-600) / 1);
}

/* ── Отключённый доступ ────────────────────────────────────────────────
   Двойная линейка сверху — тот же знак, которым Ч2 отбивает ЗАКРЫТЫЙ
   канал: дверь закрыта, письма не дойдут. Охряной кромки здесь быть не
   может — она означает «действуй», а это предупреждение. */
.chat-ctx-pnl--off {
  border-top: 3px double rgb(var(--neutral-800) / 1);
  padding-top: 11px;
}
.chat-ctx-offnote {
  display: flex;
  gap: 9px;
  font-size: 12.5px;
  line-height: 1.35;
  color: rgb(var(--neutral-700) / 1);
}
.chat-ctx-offnote b { color: rgb(var(--neutral-900) / 1); }
.chat-ctx-offnote svg {
  width: 16px;
  height: 16px;
  flex: none;
  margin-top: 1px;
  color: rgb(var(--neutral-800) / 1);
}

/* ── Полоса бейджей каналов на телефоне (Ф-6) ──────────────────────────
   Три имени в именительном («Клиент · Исполнители · Офис», ЧР-4) держат
   390 px одной строкой — замер в браузерном сторожа
   test_channel_segment_holds_one_line_on_the_phone. Помогает им сужение
   полей полосы: 8/16 → 9/12, то есть строке достаётся 366 px вместо 358.

   Правило переехало сюда из @container-блока шапки (ЧР-4): контейнерного
   предка у полосы нет — она не ребёнок шапки, — и там оно не применялось
   ни разу. Контейнером ленту сделать нельзя (см. счёт порогов выше), а
   условия те же две ширины, что у узкого режима шапки, и по той же
   причине — узкой бывает ПАНЕЛЬ, а не только окно. */
@media (max-width: 560px),
       (min-width: 1024px) and (max-width: 1099.98px) {
  .chat-thread-seg { padding: 9px 12px; }
}

/* ── Композер: подпись кнопки отправки (Ч3-4а) ───────────────────────────
   Кнопка — общая на четыре поверхности (_composer.html), и цвет подписи
   обязан держаться на всех: правило живёт в грамматике, без гейта канала.

   ПОЧЕМУ ТОКЕН, А НЕ text-white. Замер колоды Ч3: #ffffff по bg-brand-500 в
   тёмной даёт 3,73:1 — значок так проходит (порог 3:1), а КАНАЛЬНАЯ ПОДПИСЬ
   («Отправить клиенту», 14 px) уже нет. --brand-on — штатная пара бренда
   (.btn-primary, input.css): в светлой она равна прежнему белому (5,00:1), в
   тёмной даёт 4,60:1. Утилита тянула тот же токен семантической подменой
   `html .bg-brand-500.text-white`, но подмена ищет ПАРУ классов — она
   совместимость со старой разметкой, а не способ красить новую: сменит
   Ч3-4б фон кнопки на канальный, и подпись молча вернулась бы к литеральному
   белому. Здесь цвет назван прямо.

   Заливку, рамку и типографику кнопки по каналам кладёт Ч3-4б (ниже). */
.chat-composer-send { color: rgb(var(--brand-on) / 1); }

/* Подпись — ДОПОЛНЕНИЕ НА ШИРОКОМ ЭКРАНЕ (ЧР-8, канон Ф-11). Значок-самолёт
   стоит на кнопке ВЕЗДЕ и на всех семи поверхностях; слово канала приходит
   к нему от 640 px, где для слова есть место.

   ЧТО ЭТО ОТМЕНЯЕТ. Ч3-4б держала на телефоне КОРОТКУЮ подпись («Клиенту»)
   и ради неё снимала значок (решение User 04.08). Разбор ЧР-1 (вердикт 3,
   находка Ф-11) пересобрал правило: кнопка отправки говорила на четырёх
   языках сразу — «ОТПРАВИТЬ КЛИЕНТУ ➤» в хабе, «КЛИЕНТУ» без значка в
   карточке на 390, значок без слов в PWA и в ЛК. Один язык возможен ровно
   один: значок везде, слово сверх него там, где помещается.

   Канал на телефоне при этом НЕ немеет — его называют трое соседей кнопки:
   нажатый бейдж сегмента («Клиент»), штамп над композером («Видно клиенту» /
   «Только офис») и подсказка поля («Сообщение клиенту…»). Цвет кнопки —
   канальный по-прежнему (изводы Ч3-4б), aria-label остаётся ПОЛНЫМ на любой
   ширине.

   Порог 640 px — тот же sm, на котором кант, бумага и журнал уходят в узкий
   режим. Переключение здесь, а не утилитами: `sm:hidden` в бандле нет вовсе
   (Tailwind собирает только встреченные классы).

   Кнопка на четырёх поверхностях: у ЛК, PWA и личного диалога подписи нет
   вовсе (send_label=None), спана в разметке нет, и правило им не о чем. */
.chat-composer-send-full { display: none; }
@media (min-width: 640px) {
  .chat-composer-send-full { display: inline; }
}

/* Подсказка поля НЕ переносится (Ч3-4б). Поле — textarea в одну строку
   (rows=1), а подсказки канальные и длинные («Сообщение исполнителям…»); на
   узком экране, где кнопка теперь носит слово, подсказка перестала помещаться
   в строку и вторая её половина выезжала под нижнюю границу поля
   полу-обрезанной — «упавшая» вёрстка на ровном месте. Многоточие честнее:
   слов оно не теряет (канал назван и штампом, и кнопкой), а поле остаётся
   полем. Набранный ТЕКСТ переносится по-прежнему — правило только о
   ::placeholder.

   ГЕЙТ .chat-channel — граница этапа (П-12), а не украшение. Причина правила
   выросла в CRM: это КАНАЛЬНАЯ кнопка со словом отняла у строки ввода те
   пикселы, из-за которых подсказка перестала помещаться. В ЛК, PWA и личном
   диалоге хаба кнопка осталась значком, а подсказка — коротким «Написать…»:
   там ни причины, ни замера нет, а поверхности эти чинят Ч4 и Ч5 (тот же
   гейт и по той же причине носят правила кнопки выше). Правило без гейта
   доехало бы туда
   файлом (chat.css один на четыре поверхности) и молча поменяло бы вид
   чужого композера — сторож
   test_composer_placeholder_rule_never_reaches_lk_and_pwa. */
.chat-channel .chat-composer-field::placeholder {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ═══════════════════════════════════════════════════════════════════════
   ИЗВОДЫ КОМПОЗЕРА (Ч3-4б), 04.08.2026

   Канон — колода Макеты/Чат_v2/мокапы_Ч3 (К1_Чернила_канала.html,
   К2_Бланк_канала.html, К3_Линейка_и_штемпель.html + PNG обеих тем и
   ЧИТАЙ_мокапы_Ч3.md с замерами). Вариант не выбирался: User взял ВСЕ ТРИ
   личной настройкой (ui_prefs.chat_composer), и слаг приезжает третьим
   классом на контейнер канала — рядом с .chat-channel--<канал> и классом
   отделки.

   ГЕЙТ. Каждое правило секции несёт .chat-composer--<слаг>: chat.css
   подключён целиком на четырёх поверхностях, композер — ОДИН партиал на все
   четыре, а класс извода носит только контейнер канала в CRM. Правило без
   гейта покрасило бы кнопку в кабинете клиента цветом канала, которого он не
   видит (сторож — test_composer_paint_is_gated_and_never_leaks).

   ПОЧЕМУ ГРАММАТИКА, А НЕ СЕКЦИЯ ОТДЕЛОК. Ту охраняет сплошной гейт «класс
   отделки в каждом селекторе» (и сторож секции читает файл ТЕКСТОМ — оттого
   имя того класса не набирается здесь даже в комментарии), а извод от отделки
   не зависит: «Чернила» лежат на любом листе. Правила, которым отделка
   ВСЁ-ТАКИ важна (К2 под «Бумагой», К3 под «Кантом»), стоят в конце секции
   отделок и несут оба гейта.

   ТРИ РИСУНКА ОДНОЙ ФОРМЫ.
   · К1 «Чернила» — бумага одна на все каналы, меняется перо: кант поля 3 px,
     рамка, скрепка и залитая плита кнопки. У офиса плиты нет — один контур
     («заметка в офис» ничего никуда не отправляет, заливать нечем).
   · К2 «Бланк» — композер целиком встаёт на подложку своего канала, ступенью
     глубже ленты; поле остаётся белой строкой для записи.
   · К3 «Штемпель» — минимум чернил: линейка над композером (сплошная у
     клиента, двойная у закрытых — тот же знак, что у строки-признака), полоса
     подшивки 4 px вдоль формы и кнопка-печать: контур, заливка на hover.

   ХВОСТ Ч2-Б «кнопка под бумагой охряная» закрывается ЗДЕСЬ и по построению:
   до Ч3 кнопка красилась утилитой bg-brand-500 на всех каналах, и под
   «Бумагой» закрытый канал кричал охрой наружу. Теперь заливку задаёт канал,
   и охра остаётся ровно там, где ей место.

   ТИПОГРАФИКА КНОПКИ (общая всем трём, канон .send колоды) — узкий капс с
   трекингом, как у штампа канала и кнопок переключателя. Гейт у неё
   .chat-channel: подпись есть только там, где есть канал, и в ЛК с PWA
   правило не срабатывает, даже доехав файлом.
   ═══════════════════════════════════════════════════════════════════════ */

.chat-channel .chat-composer-send {
  font-family: var(--chat-face-narrow);
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.4px;
  /* Узкий капс с трекингом съедает пробел — тот же зазор, что у .chat-msg-who
     и штампа: «ОТПРАВИТЬ ИСПОЛНИТЕЛЯМ» слипается ровно так же. */
  word-spacing: 0.28em;
  text-transform: uppercase;
  /* ВЫСОТА КНОПКИ И ЕСТЬ ВЫСОТА КОМПОЗЕРА (ЧР-4). Значок 20 px плюс паддинг
     11/10 плюс рамка 2×2 давали 45 px — на 7 px выше строки ввода (38 px), и
     форма вырастала до 70 px там, где эталон держит 63. Паддинг посажен до
     7/6: кнопка становится 37 px, самой высокой снова становится строка
     ввода, и композер даёт ровно 12 + 38 + 12 + 1 = 63 px эталона. Ни один
     из трёх изводов от этого не двигается — они красят, а не меряют. */
  padding: 7px 15px 6px;
  /* Рамку задаёт извод (у К1 и К3 она несёт канал, у К2 подпирает плиту), но
     ШИРИНА её общая: без неё кнопки трёх изводов различались бы высотой, и
     переключение вида дёргало бы строку ввода. */
  border: 2px solid transparent;
}
/* Поля формы — 16 px по горизонтали, как у шапки, бейджей и плашки канала:
   все четыре этажа треда обязаны стоять на одной вертикали. Утилита p-3 в
   разметке остаётся (она держит композер на ЛК и PWA, где канала нет), здесь
   правится только горизонталь — и longhand'ами, чтобы отделка «Бумага»
   (padding-left под поле подшивки, вес 0,3,0) продолжала выигрывать. */
.chat-channel .chat-composer {
  padding-left: 16px;
  padding-right: 16px;
}

/* Телефон: кнопка — ОДИН ЗНАЧОК (ЧР-8, Ф-11). Слово канала приходит к нему от
   640 px правилом подписи выше; здесь остаётся посадка кнопки без слова —
   у неё нет ни трекинга, ни зазора между словами, ни поля под длинную
   строку, и всё освободившееся забирает строка ввода.

   ЗАМЕР СТЕНДА, 390 px, худший канал («ИСПОЛНИТЕЛЯМ» — самая длинная из трёх
   коротких подписей); число — ширина СТРОКИ ВВОДА:

     полная подпись 14 px, до Ч3-4б             90 px  (замер Ч3-4а)
     короткая подпись, кегль 12,5 · со значком  105 px (вариант «и слово, и знак»)
     кегль 11 · трекинг 0,6 · значок снят      156 px (Ч3-4б, было до ЧР-8)
     один значок, слова нет                    250 px (ЧР-8; кнопка 44 px)

   Последняя строка снята сторожем test_send_button_is_an_icon_on_the_phone и
   от канала НЕ зависит вовсе: без слова кнопка у всех трёх одинаковая.

   Гейт .chat-channel остаётся: у ЛК, PWA и личного диалога хаба слова нет и
   на широком экране, посадку кнопки там задаёт разметка (px-4 py-2), и
   трогать её отсюда нечем и незачем. */
@media (max-width: 639.98px) {
  .chat-channel .chat-composer-send {
    padding: 8px 10px;
    letter-spacing: normal;
    word-spacing: normal;
  }
}

/* ── К1 «Чернила канала» ─────────────────────────────────────────────────
   Лестница громкости идёт по составу читателей: наружу письмо уходит
   запечатанным (залитая охра), исполнителям — залитый графит, офису достаётся
   один контур. Поле называет канал само — кант 3 px слева виден, даже если
   смотреть только на строку ввода.

   ЗАМЕРЫ (WCAG по токенам input.css, светлая · тёмная):
   подпись «ОТПРАВИТЬ КЛИЕНТУ» brand-on/brand-500 — 5,00:1 · 4,60:1;
   «ОТПРАВИТЬ ИСПОЛНИТЕЛЯМ» surface-0/neutral-800, в тёмной
   neutral-900/neutral-200 — 14,63:1 · 9,84:1; «ЗАМЕТКА В ОФИС» контуром
   neutral-900/surface-0 — 17,85:1 · 13,21:1. Рамка кнопки офиса (порог 3:1)
   neutral-900, в тёмной neutral-500 — 17,85:1 · 5,98:1. Кант поля:
   клиент 5,00 · 4,25, исполнители 7,58 · 5,98, офис 14,63 · 9,94. */
.chat-composer--chernila {
  --chat-comp-ink: rgb(var(--brand-500) / 1);
  --chat-comp-send-bg: rgb(var(--brand-500) / 1);
  --chat-comp-send-fg: rgb(var(--brand-on) / 1);
  --chat-comp-send-bd: rgb(var(--brand-500) / 1);
  --chat-comp-send-hover: rgb(var(--brand-600) / 1);
}
.chat-composer--chernila.chat-channel--object {
  --chat-comp-ink: rgb(var(--neutral-600) / 1);
  --chat-comp-send-bg: rgb(var(--neutral-800) / 1);
  --chat-comp-send-fg: rgb(var(--surface-0) / 1);
  --chat-comp-send-bd: rgb(var(--neutral-800) / 1);
  --chat-comp-send-hover: rgb(var(--neutral-900) / 1);
}
.chat-composer--chernila.chat-channel--staff {
  --chat-comp-ink: rgb(var(--neutral-800) / 1);
  --chat-comp-send-bg: transparent;
  --chat-comp-send-fg: rgb(var(--neutral-900) / 1);
  --chat-comp-send-bd: rgb(var(--neutral-900) / 1);
  /* Контур на hover не заливается плитой: залитая «Заметка в офис» обещала бы
     отправку, которой нет. Тень чернил на 8% — нажатие видно, обещание не
     меняется. */
  --chat-comp-send-hover: rgb(var(--neutral-900) / 0.08);
}
/* Тёмная. Охра переворачивается сама (--brand-500 → #a67d3f, --brand-on →
   тёмный текст), а нейтральные плиты инверсия отдала бы СВЕТЛЫМИ: --neutral-800
   в тёмной это #e4dcca, почти текст. Кнопка закрытого канала стала бы самым
   светлым пятном экрана и сошлась бы с офисной (1,09:1) — урок Ч2Б-3. Плита
   исполнителей уходит на --neutral-200 (#3d372a — ступень над панелью, но
   глуше охры), различимость к листу держит рамка --neutral-400 (3,39:1).
   Офису и в тёмной остаётся контур. Оба тёмных контекста, как у --chat-shadow
   (строки 60-65). */
html.themable[data-theme="dark"] .chat-composer--chernila.chat-channel--object {
  --chat-comp-ink: rgb(var(--neutral-500) / 1);
  --chat-comp-send-bg: rgb(var(--neutral-200) / 1);
  --chat-comp-send-fg: rgb(var(--neutral-900) / 1);
  --chat-comp-send-bd: rgb(var(--neutral-400) / 1);
  --chat-comp-send-hover: rgb(var(--neutral-300) / 1);
}
html.themable[data-theme="dark"] .chat-composer--chernila.chat-channel--staff {
  --chat-comp-ink: rgb(var(--neutral-700) / 1);
  --chat-comp-send-bd: rgb(var(--neutral-500) / 1);
}
@media (prefers-color-scheme: dark) {
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-composer--chernila.chat-channel--object {
    --chat-comp-ink: rgb(var(--neutral-500) / 1);
    --chat-comp-send-bg: rgb(var(--neutral-200) / 1);
    --chat-comp-send-fg: rgb(var(--neutral-900) / 1);
    --chat-comp-send-bd: rgb(var(--neutral-400) / 1);
    --chat-comp-send-hover: rgb(var(--neutral-300) / 1);
  }
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-composer--chernila.chat-channel--staff {
    --chat-comp-ink: rgb(var(--neutral-700) / 1);
    --chat-comp-send-bd: rgb(var(--neutral-500) / 1);
  }
}

/* Кант поля — 3 px слева, рамка — те же чернила. Разделены на два правила
   намеренно: цвет рамки снимается на :focus (фокус обязан остаться брендовым,
   как во всём продукте), а КАНТ на фокусе остаётся — он не подсветка, а имя
   канала, и пропадать ему в момент набора текста нельзя. */
.chat-composer--chernila .chat-composer-field:not(:focus) {
  border-color: var(--chat-comp-ink);
}
.chat-composer--chernila .chat-composer-field {
  border-left: 3px solid var(--chat-comp-ink);
}
.chat-composer--chernila .chat-composer-attach { color: var(--chat-comp-ink); }
.chat-composer--chernila .chat-composer-send {
  background: var(--chat-comp-send-bg);
  color: var(--chat-comp-send-fg);
  border-color: var(--chat-comp-send-bd);
}
/* Своя пара под hover обязательна: утилита bg-brand-500 hover:bg-brand-600
   осталась на кнопке ради ЛК и PWA, и без этого правила наведение возвращало
   бы охру во все три канала — тот самый хвост Ч2-Б, только по движению мыши. */
.chat-composer--chernila .chat-composer-send:hover {
  background: var(--chat-comp-send-hover);
}

/* ── К2 «Бланк канала» ───────────────────────────────────────────────────
   Композер целиком встаёт на свой бланк — та же лестница, что у ленты, но
   ступенью глубже, чтобы полоса отделилась от ленты над ней. Поле остаётся
   белой строкой для записи: контраст текста не трогается ни в одном канале.

   ЗАМЕРЫ (светлая · тёмная): бланк к панели — клиент 1,06 · 1,09,
   исполнители 1,23 · 1,04, офис 1,48 · 1,24; РАЗВОДКА соседних бланков
   1,16 · 1,14 и 1,20 · 1,19. Рамка поля к бланку офиса (худший, порог 3:1)
   neutral-500/neutral-300, в тёмной neutral-500/brand-200 — 3,21:1 · 4,82:1.
   Подписи кнопок 5,00/4,60 · 14,63/11,61 · 17,85/13,21. */
.chat-composer--blank {
  --chat-comp-bg: rgb(var(--brand-50) / 1);
  --chat-comp-field-bg: rgb(var(--surface-0) / 1);
  --chat-comp-field-bd: rgb(var(--neutral-500) / 1);
  --chat-comp-send-bg: rgb(var(--brand-500) / 1);
  --chat-comp-send-fg: rgb(var(--brand-on) / 1);
  --chat-comp-send-hover: rgb(var(--brand-600) / 1);
}
.chat-composer--blank.chat-channel--object {
  --chat-comp-bg: rgb(var(--neutral-200) / 1);
  --chat-comp-send-bg: rgb(var(--neutral-800) / 1);
  --chat-comp-send-fg: rgb(var(--surface-0) / 1);
  --chat-comp-send-hover: rgb(var(--neutral-900) / 1);
}
.chat-composer--blank.chat-channel--staff {
  --chat-comp-bg: rgb(var(--neutral-300) / 1);
  --chat-comp-send-bg: rgb(var(--neutral-900) / 1);
  --chat-comp-send-fg: rgb(var(--surface-0) / 1);
  --chat-comp-send-hover: rgb(var(--neutral-800) / 1);
}
/* Тёмная: ниже панели в шкале нейтралей одна ступень (#1e1b15), и трём бланкам
   её не хватает — известное свойство палитры (колода Ч2-В). Поэтому лестница
   разворачивается ВВЕРХ и идёт по ТЁПЛОЙ шкале бренда: клиентский бланк
   уходит под панель (--brand-50, #1e1a15), служебный и офисный поднимаются
   тёплыми ступенями (--brand-100 #2b251c, --brand-200 #3a3124). Разводка
   соседей 1,14:1 и 1,19:1 — та же, что в светлой; на нейтралях её получить
   было нельзя (клиент и исполнители сходились в 1,01:1). Ни одна ступень не
   ярче признака офиса, который в проде уже стоит на #302b21 (урок Ч2Б-4:
   закрытый канал не самое яркое пятно). Плиты кнопок переворачиваются сами.
   Клиентский бланк своей пары не имеет вовсе: --brand-50 уже тёплое дно и в
   светлой, и в тёмной. */
html.themable[data-theme="dark"] .chat-composer--blank.chat-channel--object {
  --chat-comp-bg: rgb(var(--brand-100) / 1);
}
html.themable[data-theme="dark"] .chat-composer--blank.chat-channel--staff {
  --chat-comp-bg: rgb(var(--brand-200) / 1);
}
@media (prefers-color-scheme: dark) {
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-composer--blank.chat-channel--object {
    --chat-comp-bg: rgb(var(--brand-100) / 1);
  }
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-composer--blank.chat-channel--staff {
    --chat-comp-bg: rgb(var(--brand-200) / 1);
  }
}

.chat-composer--blank .chat-composer { background: var(--chat-comp-bg); }
.chat-composer--blank .chat-composer-field { background: var(--chat-comp-field-bg); }
.chat-composer--blank .chat-composer-field:not(:focus) {
  border-color: var(--chat-comp-field-bd);
}
.chat-composer--blank .chat-composer-send {
  background: var(--chat-comp-send-bg);
  color: var(--chat-comp-send-fg);
  border-color: var(--chat-comp-send-bg);
}
.chat-composer--blank .chat-composer-send:hover {
  background: var(--chat-comp-send-hover);
  border-color: var(--chat-comp-send-hover);
}

/* ── К3 «Линейка и штемпель» ─────────────────────────────────────────────
   Канал называют две линии и печать. Линейка над композером сплошная у
   клиента и двойная у закрытых — тот же знак, которым в грамматике отбита
   строка-признак закрытого канала. Кнопка не плита, а штемпель: контур в
   чернилах, у закрытых в двойной рамке грифа; заливка приходит на hover.

   ЗАМЕРЫ (светлая · тёмная): «ОТПРАВИТЬ КЛИЕНТУ» brand-600/surface-0 —
   6,58:1 · 5,38:1 (лучший худший случай из трёх изводов);
   «ОТПРАВИТЬ ИСПОЛНИТЕЛЯМ» neutral-700 — 10,35:1 · 9,94:1; «ЗАМЕТКА В ОФИС»
   neutral-900 — 17,85:1 · 13,21:1; штемпель на служебном листе «Бумаги»
   9,45:1 · 10,77:1, на «Черновике» 11,52:1 · 14,42:1; заливка на hover
   (подпись surface-0 по чернилам) 6,58…17,85 · 5,38…13,21.

   Тёмных пар нет ни одной, и это не пропуск: все три чернила
   переворачиваются сами (#75582f→#b79054, #334155→#d5ccb9, #0f172a→#f0eadc) и
   остаются ЧЕРНИЛАМИ — тем же тоном, которым набрано письмо, а не пятном. */
.chat-composer--shtempel { --chat-comp-ink: rgb(var(--brand-600) / 1); }
.chat-composer--shtempel.chat-channel--object { --chat-comp-ink: rgb(var(--neutral-700) / 1); }
.chat-composer--shtempel.chat-channel--staff { --chat-comp-ink: rgb(var(--neutral-900) / 1); }

.chat-composer--shtempel .chat-composer {
  position: relative;
  padding-left: 20px;
  border-top: 3px solid var(--chat-comp-ink);
}
.chat-composer--shtempel.chat-channel--object .chat-composer,
.chat-composer--shtempel.chat-channel--staff .chat-composer {
  border-top: 4px double var(--chat-comp-ink);
}
/* Полоса подшивки вдоль всей формы. Псевдоэлемент, а не рамка: рамка слева
   съела бы padding у поля ввода, а полоса обязана идти и под скрепкой, и под
   кнопкой — от края до края. */
.chat-composer--shtempel .chat-composer::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 4px;
  background: var(--chat-comp-ink);
}
.chat-composer--shtempel .chat-composer-send {
  background: transparent;
  color: var(--chat-comp-ink);
  border-color: var(--chat-comp-ink);
}
/* Двойная рамка грифа у закрытых каналов — тот же приём, что у штампа под
   «Бумагой»: контур плюс отбитый на 3 px внешний. */
.chat-composer--shtempel.chat-channel--object .chat-composer-send,
.chat-composer--shtempel.chat-channel--staff .chat-composer-send {
  outline: 1px solid var(--chat-comp-ink);
  outline-offset: 3px;
}
/* Печать ставится нажатием: на hover штемпель заливается своими чернилами. */
.chat-composer--shtempel .chat-composer-send:hover {
  background: var(--chat-comp-ink);
  color: rgb(var(--surface-0) / 1);
}

/* ── Модалка подтверждения письма клиенту (Ч3-4б, режим 2 Р-4) ───────────
   Разметка — _confirm_modal.html, внутри контейнера канала; сюда она попадает
   только в CRM (в ЛК и PWA этих классов нет ни в одном шаблоне — сторожа
   утечек), поэтому гейта канала правилам не нужно: они срабатывают там, где
   есть разметка, и нигде больше.

   ВИДИМОСТЬ — атрибутом hidden, а не классом и не инлайн-стилем: правило
   .chat-confirm[hidden] (0,2,0) тяжелее базового (0,1,0) и не спорит с ним,
   как спорил бы класс `flex` с атрибутом (грабля модалки реквизитов,
   crm/settings/company.html). Инлайновых стилей нет — CSP.

   РИСУНОК — грамматика чата: узкий капс на кнопках, штамп канала тем же
   макросом, что над лентой (токены --chat-chan-stamp-* он берёт с себя, и в
   модалке выглядит ровно так же, как в строке-признаке). Замеры: подпись
   вопроса --neutral-900 по --surface-0 — 17,85:1 · 13,21:1; «Отмена»
   --neutral-700 по --surface-0 — 10,35:1 · 9,94:1; «Отправить клиенту»
   --brand-on по --brand-500 — 5,00:1 · 4,60:1 (штатная пара бренда). */
.chat-confirm {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
}
.chat-confirm[hidden] { display: none; }
.chat-confirm-scrim {
  position: absolute;
  inset: 0;
  /* --scrim темнит в ЛЮБОЙ теме (input.css): --neutral-900 в тёмной светлый и
     дал бы белую пелену поверх треда. */
  background: rgb(var(--scrim) / 0.5);
}
.chat-confirm-sheet {
  position: relative;
  width: 100%;
  max-width: 26rem;
  padding: 20px 22px 18px;
  border: 1px solid rgb(var(--neutral-300) / 1);
  border-radius: 8px;
  background: rgb(var(--surface-0) / 1);
  box-shadow: 0 12px 32px rgb(var(--scrim) / 0.35);
}
/* Вопрос — обычным набором, а не капсом: капсом здесь только штамп и кнопки,
   а читать предстоит ИМЯ клиента, и узкий капс на имени собственном хуже
   читается ровно там, где важно узнать человека. */
.chat-confirm-body {
  margin: 12px 0 0;
  font-size: 15px;
  line-height: 1.5;
  color: rgb(var(--neutral-900) / 1);
}
.chat-confirm-acts {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 18px;
}
.chat-confirm-no,
.chat-confirm-yes {
  font-family: var(--chat-face-narrow);
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 1.4px;
  word-spacing: 0.28em;
  text-transform: uppercase;
  padding: 11px 15px 10px;
  border: 2px solid transparent;
  border-radius: 6px;
  cursor: pointer;
}
.chat-confirm-no {
  background: transparent;
  border-color: rgb(var(--neutral-300) / 1);
  color: rgb(var(--neutral-700) / 1);
}
.chat-confirm-no:hover { background: rgb(var(--neutral-100) / 1); }
/* Согласие — охра клиентского канала: кнопка модалки и кнопка композера
   отправляют в один канал и обязаны выглядеть одним действием. */
.chat-confirm-yes {
  background: rgb(var(--brand-500) / 1);
  border-color: rgb(var(--brand-500) / 1);
  color: rgb(var(--brand-on) / 1);
}
.chat-confirm-yes:hover {
  background: rgb(var(--brand-600) / 1);
  border-color: rgb(var(--brand-600) / 1);
}
.chat-confirm-no:focus-visible,
.chat-confirm-yes:focus-visible {
  outline: 2px solid rgb(var(--brand-500) / 1);
  outline-offset: 2px;
}


/* ═══════════════════════════════════════════════════════════════════════
   ЖИВАЯ ПРИМЕРКА, СВОДНАЯ ОЧЕРЕДЬ И ЖУРНАЛЬНАЯ ЛЕНТА
   (этап ЧР, задача ЧР-6, 07.08.2026)

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

/* ── Корешок рельса: хаб под открытой панелью настроек ──────────────────
   АРИФМЕТИКА, А НЕ ВКУС. Рабочее поле на 1440 — 1200 px, панель «Разворот»
   отдаёт 700. Останься рельс при своих 300, ленте достались бы 200 px, и
   «живая примерка» примеряла бы то, чего не видно. Рельс сжимается в
   КОРЕШОК 88 px (эталон), лента получает 1200 − 700 − 88 = 412 — ровно
   число эталона.

   Что видно в корешке: номера наряда и диски непрочитанного, то есть
   «сколько дел и где новое». Адреса, превью, поиск и «Написать» уходят —
   в 88 px они не читаются, а обрезанное слово хуже отсутствующего (Ф-3).
   Своей разметки у корешка нет ни одного элемента: это ТА ЖЕ разметка
   рельса под другими правилами — иначе примерка возила бы по сети второй
   список ради двадцати секунд на экране.

   ВЕРХНЯЯ ГРАНИЦА 1659 — приёмка ЧР, 13.08.2026. До неё корешок включался
   на ЛЮБОЙ ширине от 1024, и на мониторе в 2130 px список сжимался в
   полоску там, где полному рельсу хватало места с запасом (лента при нём
   получала бы 890). Корешок — плата за тесноту, а не форма списка при
   панели, и платить её на просторе не за что.

   Порог ПОСЧИТАН, а не выбран: полный рельс оставляем с той ширины, где
   ленте достаётся не меньше эталонных 412 px. Замер стенда (Chromium,
   развёрнутый нав CRM — доминантный случай):

     нав 240 (15rem, border-box) + рельс 300 + панель 700 + лента 412
     = 1652 → округление вверх до десятка = 1660, лента 1660 − 240 − 1000
     = 420 px. На 1659 включён корешок (эталонные 412 при рельсе 88),
     на 1660 — полный рельс с поиском и «Написать».

   Свёрнутый нав (4rem) медиазапросу не виден, и на 1600 со свёрнутым
   навом корешок включится, хотя места хватило бы: у @media нет доступа к
   ширине соседа, а вводить ради этого класс на <html> и JS-замер дороже
   самой правки. Считаем по развёрнутому — он умолчание.

   Только с lg: ниже 1024 работает мастер/деталь, рельс и лента видны по
   одной, и панель на узком экране занимает экран целиком (правила ниже). */
@media (min-width: 1024px) and (max-width: 1659px) {
  .chat-hub.chat-settings-open .chat-rail { width: 88px; }
  /* Голова рельса: остаётся шестерёнка — ею панель и открыли, ею же видно,
     что настройки сейчас на экране. Поиск и «Написать» уходят. */
  .chat-hub.chat-settings-open .chat-rsearch,
  .chat-hub.chat-settings-open .chat-rnew { display: none; }
  .chat-hub.chat-settings-open .chat-railrow { justify-content: center; }
  /* Наряд: номера остаются, слова уходят. Подпись «Ждут ответа» заменена
     счётчиком — он и так стоит в голове наряда и вне прокрутки. */
  .chat-hub.chat-settings-open .chat-nar-title,
  .chat-hub.chat-settings-open .chat-nar-hint,
  .chat-hub.chat-settings-open .chat-nar-top,
  .chat-hub.chat-settings-open .chat-nar-who,
  .chat-hub.chat-settings-open .chat-nar-time,
  .chat-hub.chat-settings-open .chat-nar-bar,
  .chat-hub.chat-settings-open .chat-qsum-link s { display: none; }
  /* Подпись «Ждут ответа» уходит вместе с хвостом (приёмка ЧР). Пряталось
     только уточнение <s>, а сами слова стояли голым текстом в ссылке с
     nowrap + overflow: hidden — и в 88 px «ЖДУТ ОТВЕ…» рубилось на
     полуслове (Ф-3: обрезанное слово хуже отсутствующего). Ради этого
     правила слова и обёрнуты в <i> в _queue_strip.html: спрятать текстовый
     узел без тега CSS не может. Остаётся счётчик — то же, что несёт голова
     наряда во второй форме очереди, и ссылка при нём кликабельна целиком. */
  .chat-hub.chat-settings-open .chat-qsum-link i { display: none; }
  /* Пустая «Сводная строка» уходит СЕКЦИЕЙ. У формы Н-2 головы нет — вся
     она и есть одна фраза, а фраза в 88 px не читается ни в каком кегле;
     числа, которое осталось бы вместо неё (как «0» в голове наряда), у
     ветки нет по построению. Оставить пустую полосу 40 px с линейкой
     значило бы занять место под сообщение, которого нет, — ровно та голая
     полоска, с которой приёмка и началась. Эталонная высота 40,3 у формы
     мерится ВНЕ панели, на полном рельсе (ЧР-6, M-2): там она и живёт.
     Гейт :has() — та же идиома, что у подложки ящика выше. */
  .chat-hub.chat-settings-open .chat-qsum:has(.chat-qsum-empty) { display: none; }
  /* Пустой наряд уходит ЦЕЛИКОМ. Пряталась одна подпись (span), а заголовок
     «Очередь пуста · день закрыт» свёрстан <b> и оставался: в 88 px он
     ломался на четыре строки внутри пунктирной рамки — та самая «страшная»
     полоска приёмки. Счётчик «0» в голове наряда говорит ровно это же и
     стоит вне прокрутки, так что в корешке рамке сообщать нечего. */
  .chat-hub.chat-settings-open .chat-nar-empty { display: none; }
  .chat-hub.chat-settings-open .chat-nar-head,
  .chat-hub.chat-settings-open .chat-nar-row,
  .chat-hub.chat-settings-open .chat-qsum-link {
    justify-content: center;
    gap: 4px;
  }
  /* Строки списка: остаются адресный столбик без слов и диск непрочитанного.
     Превью и время в корешок не влезают ни в каком кегле. */
  .chat-hub.chat-settings-open .chat-list-day,
  .chat-hub.chat-settings-open .chat-row-prev,
  .chat-hub.chat-settings-open .chat-row-sub,
  .chat-hub.chat-settings-open .chat-row-at,
  .chat-hub.chat-settings-open .chat-row-title { display: none; }
  .chat-hub.chat-settings-open .chat-row-top { justify-content: center; }
  .chat-hub.chat-settings-open .chat-row { padding-left: 12px; }
  /* КАЖДАЯ строка несёт знак. У строки с новым его несёт диск непрочитанного,
     а строка без нового отдавала адрес, время и превью и оставалась голой
     полосой фона — по такому списку не видно ни сколько в нём разговоров,
     ни куда целиться. Диск-заместитель: 7 px нейтрального чернила против
     пилюли счётчика — «разговор есть, нового нет». Обрезанных слов канон
     Ф-3 не разрешает, поэтому знак и рисуется, а не пишется.
     Гейт .chat-row--fresh — тот же класс, которым размечена строка с новым
     (_object_row/_dm_row), так что второго условия заводить не пришлось.
     Чернило --neutral-500, как у объектного диска наряда. Замер на стенде:
     4,76 к листу в светлой и 5,98 в тёмной (в ней шкала перевёрнута, и 400
     ушло бы в темноту фона — токен едет за темой, число нет). К подложке
     открытой строки базовой палитры — 4,29 и 5,72. Всё выше порога 3:1 для
     нетекстового знака, а тише акцента диск не цветом, а размером. */
  .chat-hub.chat-settings-open .chat-row:not(.chat-row--fresh) .chat-row-top::before {
    content: "";
    display: block;
    width: 7px;
    height: 7px;
    border-radius: 999px;
    background: rgb(var(--neutral-500) / 1);
  }
}

/* ── Расчистка под панель: на ЛЮБОЙ ширине от 1024 ──────────────────────
   Ширины у этих двух правил своей нет, поэтому и порога корешка у них нет:
   панель-оверлей стоит справа на всех ширинах, где она вообще панель, и
   лента обязана от неё отступать что на 1440, что на 2130.

   ЛЕНТА ОТСТУПАЕТ ОТ ПАНЕЛИ, а не прячется под ней. Панель — оверлей, и
   без этого отступа половина ленты лежала бы под непрозрачным листом:
   видно было бы 412 px, а считалось бы 1112, и всякая колонка письма
   (max-width: 800) центрировалась бы по невидимому центру. Колонка
   контекста при этом уходит целиком — её настройка примеряется по
   образцу, а не на живом экране (честная цена компоновки, ПАНЕЛИ_README). */
@media (min-width: 1024px) {
  .chat-hub.chat-settings-open .chat-thread-col { padding-right: 700px; }
  .chat-hub.chat-settings-open .chat-ctx { display: none; }
}

/* ── Очередь «Сводная строка» (ЧР-6, форма Н-2) ────────────────────────
   Одна строка над списком вместо столбика дел: сколько ждут и кто дольше
   всех. Высота 40 px против ~195 у наряда — разница целиком уходит списку,
   и это единственное, ради чего форму выбирают.

   Строка КЛИКАБЕЛЬНА и ведёт в голову очереди: она не только считает, но и
   открывает то, за что браться, — иначе сводка была бы табличкой, а не
   инструментом. Замер эталона (Н2_хаб.html, рельс 300): 40,3 px. */
.chat-qsum {
  flex: none;
  border-bottom: 1px solid rgb(var(--neutral-200) / 1);
}
.chat-qsum-link,
.chat-qsum-empty {
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 9px 12px 8px;
  white-space: nowrap;
  overflow: hidden;
  text-decoration: none;
  font-family: var(--chat-face-narrow);
  font-size: 11px;
  font-weight: 700;
  /* ИНТЕРЛИНЬЯЖ НЕ СБИВАЕМ (ревью ЧР-6, M-2). Первая редакция ставила тут
     line-height: 1 «как у капса наряда» — единственная строка, которой нет у
     эталона Н-2, и она съедала 5,8 px: строка выходила 34,5 вместо 40,3.
     Высота у этой формы не косметика, а сам предмет выбора: сводку берут
     ради того, что она отдаёт списку, и число обязано быть эталонным. */
  letter-spacing: 1.1px;
  word-spacing: 0.2em;
  text-transform: uppercase;
  color: rgb(var(--neutral-900) / 1);
}
.chat-qsum-link:hover { background: rgb(var(--neutral-50) / 1); }
.chat-qsum-link em {
  flex: none;
  font-style: normal;
  font-family: var(--chat-face-mono);
  font-size: 11.5px;
  letter-spacing: 0;
  padding: 3px 5px 2px;
  border-radius: 3px;
  background: rgb(var(--brand-500) / 1);
  color: rgb(var(--brand-on) / 1);
}
/* Подпись «Ждут ответа» — в своём теге ради корешка (приёмка ЧР): голый
   текстовый узел спрятать нечем. Курсив снят тут же, набор у слов общий со
   строкой — никакой своей типографики <i> не приносит. */
.chat-qsum-link i { font-style: normal; }
/* Хвост «дольше всех — 1 дн 2 ч» набран строчными: он уточняет, а не
   называет. <s> тут не «зачёркнуто» — это тот же приём, что в наряде
   (ЧР-4, m-2): тег взят за краткость, смысл снят text-decoration. */
.chat-qsum-link s {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  text-decoration: none;
  font-weight: 400;
  font-size: 12px;
  letter-spacing: 0.1px;
  word-spacing: 0;
  text-transform: none;
  color: rgb(var(--neutral-500) / 1);
}
.chat-qsum-link b {
  font-family: var(--chat-face-mono);
  font-weight: 400;
  color: rgb(var(--brand-600) / 1);
}
.chat-qsum-empty { color: rgb(var(--neutral-600) / 1); }
.chat-qsum-link:focus-visible {
  outline: 2px solid rgb(var(--brand-500) / 1);
  outline-offset: -2px;
}

/* ── Лента «Журнал» (ЧР-6, форма Н-1) ──────────────────────────────────
   Проигравшая лента направления Н-1 живёт настройкой (вердикт ЧР-2Б):
   письма в ОДНУ колонку слева, время и «Прочитано» — своим столбцом
   справа, и времена выстраиваются по одной вертикали. Зеркальность
   («Беседа», ЧР-4) остаётся умолчанием.

   ЧИСТЫЙ CSS ПОВЕРХ КОНТРАКТА, НИ БУКВЫ РАЗМЕТКИ. Механика — та же, что
   у отделки «Журнал» (Ч2-Б): grid на письме плюс display: contents на его
   шапке. Шапка перестаёт быть боксом, и её дети (автор и время) становятся
   ячейками письма-сетки: автор садится над текстом в левой графе, время
   уезжает в правую. Статус доставки — туда же, под время.

   ГРАФА 132 px и колонка ≤ 70ch — числа эталона (Н1_хаб.html, .jm). Здесь
   колонка задана долей (1fr), а не 70ch: в хабе тред и так ограничен
   800 px (.chat-thread), и второй ограничитель дал бы пустое поле справа
   от колонки писем — при 412 px примерки это была бы половина ленты.

   ГРАММАТИКА, А НЕ ОТДЕЛКА. Правила гейтятся chat-lenta--zhurnal — классом
   САМОЙ ЛЕНТЫ, — и класса отделки в них нет: расстановка писем не свойство
   канала, она одна на все каналы и на личный диалог тоже. Отделка «Журнал»
   (chat-skin--zhurnal на канале объекта) остаётся тяжелее по специфичности
   и продолжает править свой канал сама: там запись уже одноколоночная, и
   спорить им не о чем. */
.chat-lenta--zhurnal .chat-msg {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 132px;
  column-gap: 20px;
  /* Зеркальности нет: margin-left: auto своего письма снимается вместе с
     кэпом колонки — колонка тут задана сеткой, а не max-width письма. */
  max-width: none;
  margin-left: 0;
}
.chat-lenta--zhurnal .chat-msg-head { display: contents; }
.chat-lenta--zhurnal .chat-msg-who {
  grid-area: 1 / 1;
  margin-bottom: 6px;
}
/* Пузырь занимает ГРАФУ ЦЕЛИКОМ, а не ширину текста (эталон Н1_хаб.html:
   .jm-bub равен всей колонке 578 px). Это и есть «журнал»: письма стоят
   ровными карточками одной ширины, и глаз читает столбец, а не рваный
   край реплик.

   Гейт по data-mine обязателен и здесь, и он тяжелее базового правила
   (0,4,0 против 0,3,0): своё письмо в грамматике прижато вправо
   margin-left: auto вместе с width: fit-content, и без снятия ОБОИХ
   охряная реплика осталась бы у правого края графы — то есть зеркальность
   пережила бы настройку, которая её и отменяет. */
.chat-lenta--zhurnal .chat-msg-bubble {
  grid-area: 2 / 1 / span 2 / span 1;
  width: auto;
}
.chat-lenta--zhurnal .chat-msg[data-mine="1"] .chat-msg-bubble {
  margin-left: 0;
}
.chat-lenta--zhurnal .chat-msg-at {
  grid-area: 2 / 2;
  align-self: start;
  padding-top: 14px;
}
.chat-lenta--zhurnal .chat-msg-status {
  grid-area: 3 / 2;
  align-self: start;
  justify-content: flex-start;
  margin-top: 5px;
}
/* Узкая лента — мета возвращается в строку над письмом: графа 132 px плюс
   зазор 20 забирают у колонки письма 152, и ниже 420 px от неё остаётся
   меньше 270 — журнал перестаёт быть журналом и становится двумя обрубками.

   ПОРОГ СВОЙ, А НЕ ОБЩИЙ 560 ГРАММАТИКИ, и это посчитано, а не выбрано:
   колонка треда хаба на 1440 даёт ленте 572 px (550 под «Кантом»), то есть
   ровно ту зону, где общий порог уже сработал бы, а графа ещё помещается —
   на 560 «Журнал» на десктопе выродился бы в «Беседу без зеркала» и
   настройка ничего бы не меняла. Ниже 420 остаются телефон и САМА ПРИМЕРКА
   (лента 412 под панелью 700): там мета честно возвращается в строку, и
   отличие «Журнала» видно по снятой зеркальности. */
@container (max-width: 420px) {
  .chat-lenta--zhurnal .chat-msg { grid-template-columns: minmax(0, 1fr); }
  .chat-lenta--zhurnal .chat-msg-bubble { grid-area: 2 / 1 / span 1 / span 1; }
  .chat-lenta--zhurnal .chat-msg-at {
    grid-area: 1 / 1;
    justify-self: end;
    padding-top: 0;
  }
  .chat-lenta--zhurnal .chat-msg-status { grid-area: 3 / 1; }
}

/* ── Панель настроек на узком экране (ЧР-6) ────────────────────────────
   На 390 «Разворот» разворачивать нечего: два столбца по 322 px не
   помещаются ни в каком виде. Панель занимает экран целиком, группы встают
   ОДНИМ столбцом, и тело получает прокрутку — единственное место, где
   обещание «всё разом» приходится нарушить, и нарушается оно честно, а не
   обрезкой снизу.

   Плитки при этом НЕ уменьшаются: узкий экран — не повод возвращать
   неразличимые образцы (Ф-8), а ряд из трёх плиток 88 px в 390 − 36 полей
   укладывается ровно. */
@media (max-width: 1023.98px) {
  .chat-set-panel {
    top: 0;
    bottom: 0;
    width: 100%;
    border-left: 0;
  }
  .chat-set-body {
    grid-template-columns: 1fr;
    overflow-y: auto;
    padding: 12px 14px 10px;
  }
  .chat-set-col + .chat-set-col { margin-top: 13px; }
  /* Строка механики в шапке на 390 не помещается рядом с именем — она
     уходит под него второй строкой, а не обрезается многоточием: это
     единственное место, где панель объясняет свою новую механику. */
  .chat-set-top { flex-wrap: wrap; }
  .chat-set-top p { flex-basis: 100%; order: 3; }
  /* Подпись у «Готово» на телефоне — тот же текст, что и на 1440, но она
     ниже кнопки: строка в 12,5 px рядом с кнопкой оставила бы ей 120 px. */
  .chat-set-foot { flex-wrap: wrap; }
  .chat-set-foot span { flex-basis: 100%; }
}

/* ═══════════════════════════════════════════════════════════════════════
   ОТДЕЛКИ ТРЕДА (Ч2-Б), 03.08.2026

   Отделка — личная настройка читателя (ui_prefs.chat_skin), второй класс на
   контейнере канала: .chat-channel--<канал> говорит, ЧТО это за канал,
   .chat-skin--<слаг> — как этот канал выглядит у конкретного менеджера.
   Правила ниже переопределяют токен-переменные грамматики (--chat-chan-*) и
   добавляют свои декоративные атомы; разметку письма и контракт Ч2
   (.chat-msg*, .chat-daysep, партиалы) отделки не трогают.

   ПОРЯДОК ВАЖЕН: секция стоит ПОСЛЕ базовой грамматики намеренно —
   модификатор поверх контракта. Специфичность «класс отделки + класс
   канала» (0,2,0) и так выше одиночного .chat-channel (0,1,0), но при
   равных весах спор решает порядок, и здесь он в пользу отделки.

   ГЕЙТ: каждый селектор секции обязан содержать .chat-skin- — chat.css
   подключён целиком на всех четырёх поверхностях, а класс отделки носит
   только контейнер канала в CRM. Правило без гейта протекло бы в ЛК, PWA и
   во фрагмент поллинга, где отменить его нечем. Сторож —
   test_skin_rules_never_address_base_chat_classes.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── А «Кант канала» ────────────────────────────────────────────────────
   Канон — Макеты/Чат_v2/А_Кант_канала.html (кадры _светлая/_тёмная.png):
   вдоль поля канала идёт цветной кант с вертикальной подписью. Лестница
   «чем внутреннее, тем глуше»: наружу ровная охра, внутрь графит в косую
   штриховку с шагом 10 px. Письма остаются на чистом поле: кант красит РАМУ
   канала, не переписку.

   ЗА УГОЛ КАНТ БОЛЬШЕ НЕ СВОРАЧИВАЕТ (приёмка ЧР, вердикт В2). Канон вёл
   заливку от верха ленты до кнопки отправки, и последним её коленом была
   строка-признак; на этой строке стоит поимённый перечень читателей, и на
   охряной плите он читался тем же цветом, что служебное «ЧИТАЮТ». Эталон
   этапа ЧР (НСБ_хаб.html) рисует кант ::before у самой ленты и до признака
   не доводит — вердикт User привёл прод к эталону. Полоса вдоль ленты цела:
   она и есть кант.

   СЛОВА НА ПОЛОСЕ БОЛЬШЕ НЕТ (ЧР-7, находка Ф-7). Полоса несла имя канала
   вертикальным капсом — третий механизм одного и того же смысла «кто это
   увидит» рядом с пилюлей шапки (снята в ЧР-4) и штампом над композером.
   Эталон НСБ_хаб.html говорит об этом прямо: «Отделка "Кант" — умолчание
   прода. Слова на полосе НЕТ: "Видно клиенту" живёт в одном месте (Ф-7)».
   Осталась сама полоса — цвет канала вдоль ленты; называет канал ШТАМП, и
   ровно один раз. Типографика полосы (writing-mode, кегль, трекинг) и её
   чернила --chat-skin-band-fg сняты вместе со словом: мёртвое правило
   отличить от забытого нельзя.

   Элемент в DOM остался (а не стал ::before): полоса рисуется штриховкой у
   исполнителей и пунктирной кромкой у одного из изводов офиса, и правила
   «полоса есть только при канте / ровно одна / не протекает в бумагу, журнал
   и dm» держатся именно на нём — сторожа Ч2-Б. */
.chat-skin--kant { position: relative; padding-left: 22px; }
.chat-skin--kant > .chat-skin-band {
  position: absolute; left: 0; top: 0; bottom: 0; width: 22px;
  background: var(--chat-skin-band-bg);
}

/* Канал «Клиенту» — ровная охра на полосе. ЗА УГОЛ КАНТ БОЛЬШЕ НЕ
   СВОРАЧИВАЕТ (приёмка ЧР, вердикт В2): заливка шла от верха ленты до кнопки
   отправки, и строка-признак была её последним коленом — а на этой строке
   стоит перечень читателей, и на охряной плите он читался тем же цветом, что
   служебное «ЧИТАЮТ» (--brand-on на обоих, ЧР7-m-1). Эталон НСБ_хаб.html
   держит под кантом ГРАММАТИЧЕСКУЮ плашку — полоса у него ::before на самой
   ленте и до признака не достаёт; вердикт User привёл прод к эталону.
   Осталась полоса вдоль ленты — она и есть кант.
   Правило клиентское ПО УМОЛЧАНИЮ (гейт без --client): незнакомый kind
   получит клиентскую охру при fail-closed штампе — отделка здесь fail-open.
   Третий канал свой блок --staff завёл (Ч2-В, В5б — три извода канта сразу
   под этим правилом), и умолчание стало тем, чем должно быть: страховкой на
   канал, которого ещё нет, а не дырой в лестнице. */
.chat-skin--kant.chat-channel {
  --chat-skin-band-bg: rgb(var(--brand-500) / 1);
}

/* Канал «Исполнителям» — графит в косую штриховку, шаг 10 px (канон
   --hatch). Пары --chat-kant-hatch-* — алиасы на токены, а не готовые цвета
   (идиома --scrim / --btn-dark из input.css): тёмная тема переворачивает
   их тремя строками ниже, не переписывая градиент.
   Алиас --chat-kant-ink ушёл вместе с заливкой признака (В2): чернил у
   отделки не осталось — их даёт грамматика, одна пара на все отделки. */
.chat-skin--kant.chat-channel--object {
  --chat-kant-hatch-a: var(--neutral-700);
  --chat-kant-hatch-b: var(--neutral-600);
  --chat-skin-band-bg: repeating-linear-gradient(45deg,
      rgb(var(--chat-kant-hatch-a) / 1) 0 5px, rgb(var(--chat-kant-hatch-b) / 1) 5px 10px);
}

/* Тёмная тема. Охра переворачивается САМА: --brand-500 становится светлой
   охрой (#a67d3f), ровно как в кадре А_Кант_канала_тёмная.png. А графит
   инверсия нейтралей сделала бы СВЕТЛЫМ: --neutral-700 в тёмной — это
   основной текст (#d5ccb9), и «самый внутренний» канал стал бы самым ярким
   пятном экрана — лестница «чем внутреннее, тем глуше» перевернулась бы
   вверх ногами. Берём низкие ступени шкалы (#3d372a / #4e4635 — глухой
   графит НАШЕЙ тёплой гаммы там, где канон брал холодный #414c5e / #4f5b70).
   Оба тёмных контекста, как у --chat-shadow (строки 60-65). */
html.themable[data-theme="dark"] .chat-skin--kant.chat-channel--object {
  --chat-kant-hatch-a: var(--neutral-200);
  --chat-kant-hatch-b: var(--neutral-300);
}
@media (prefers-color-scheme: dark) {
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-skin--kant.chat-channel--object {
    --chat-kant-hatch-a: var(--neutral-200);
    --chat-kant-hatch-b: var(--neutral-300);
  }
}

/* ── Кант канала «Офис» (Ч2-В, В5б) ─────────────────────────────────────
   Долг Ч2-Б закрыт здесь: без блока --staff офис получал бы КЛИЕНТСКУЮ охру
   (правило выше гейтится без --client), то есть цвет канала, из которого его
   и прячут. Изводов три — они личная настройка читателя (ui_prefs
   chat_office_kant), и оба вариант-класса стоят на контейнере всегда:
   гейтит их отделка, а не шаблон. Мокапы и замеры — Макеты/Чат_v2/
   мокапы_Ч2В/офис_отделки.html (кадры kant-{1-grifel,2-kromka,3-sepia}).

   Общее у трёх: изводы красят ПОЛОСУ, лестница «чем внутреннее, тем глуше»
   держится в обеих темах, и ни один извод не берёт --brand-500 — охра занята
   каналом наружу. Строку-признак изводы больше не заливают (приёмка ЧР,
   вердикт В2): плашка — общий нейтральный лист грамматики, и прежние замеры
   «текст признака к заливке» вместе с заливкой ушли. Замер полосы к панели
   остаётся: он и есть про различимость самого канта.

   Тёмные пары обязательны всем трём: --neutral-800 и --brand-800 в тёмной
   это ТЕКСТОВЫЕ ступени (#e4dcca, #c6b192), и полоса «самого закрытого»
   канала вышла бы самым светлым пятном экрана (урок Ч2Б-3). Оба тёмных
   контекста, как у --chat-shadow (строки 60-65). */

/* Извод «Сепий» (дефолт, К-3 колоды) — дно той же шкалы бренда, что даёт
   клиенту охру: «та же охра при закрытых ставнях». Замеры: полоса к панели
   12,27:1 светлая · 1,24:1 тёмная — лучшая различимость в тёмной из трёх
   изводов. Риск (назван в колоде): одна семья с клиентским каналом;
   разводят их три ступени шкалы и графит объекта между ними — L полос 0,036
   (офис) против 0,160 (клиент) в светлой. */
.chat-skin--kant.chat-channel--staff.chat-office-kant--sepia {
  --chat-skin-band-bg: rgb(var(--brand-800) / 1);
}
html.themable[data-theme="dark"] .chat-skin--kant.chat-channel--staff.chat-office-kant--sepia {
  --chat-skin-band-bg: rgb(var(--brand-200) / 1);
}
@media (prefers-color-scheme: dark) {
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-skin--kant.chat-channel--staff.chat-office-kant--sepia {
    --chat-skin-band-bg: rgb(var(--brand-200) / 1);
  }
}

/* Извод «Грифель» (К-1) — ровная плита без фактуры: у клиента заливка, у
   объекта штриховка (она значит ведущуюся работу), у офиса работы, которую
   можно показать, нет — есть разговор о ней. Замеры: полоса к панели
   14,63:1 · 1,13:1 (в тёмной полоса почти сливается с панелью — ступень
   слышна двойной линейкой признака, риск назван в колоде). */
.chat-skin--kant.chat-channel--staff.chat-office-kant--grifel {
  --chat-skin-band-bg: rgb(var(--neutral-800) / 1);
}
html.themable[data-theme="dark"] .chat-skin--kant.chat-channel--staff.chat-office-kant--grifel {
  --chat-skin-band-bg: rgb(var(--neutral-100) / 1);
}
@media (prefers-color-scheme: dark) {
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-skin--kant.chat-channel--staff.chat-office-kant--grifel {
    --chat-skin-band-bg: rgb(var(--neutral-100) / 1);
  }
}

/* Извод «Кромка» (К-2) — заливки нет вовсе: светлая грунтовка и пунктирная
   кромка, признак берёт пунктир сверху и снизу. Единственный извод, ведущий
   себя в двух темах одинаково (полоса к панели 1,10:1 и 1,08:1 — её и там и
   там не видно боковым зрением, риск назван в колоде; держит канал пунктир
   вокруг признака и гриф на нём). Кромка к полосе — 2,34:1 · 1,84:1.
   --chat-kant-edge объявлен парой к теме, а не готовым цветом: пунктир и
   сверху, и снизу, и справа — один источник на три линии. */
.chat-skin--kant.chat-channel--staff.chat-office-kant--kromka {
  --chat-skin-band-bg: rgb(var(--neutral-100) / 1);
  --chat-kant-edge: rgb(var(--neutral-400) / 1);
}
html.themable[data-theme="dark"] .chat-skin--kant.chat-channel--staff.chat-office-kant--kromka {
  --chat-skin-band-bg: rgb(var(--neutral-50) / 1);
  --chat-kant-edge: rgb(var(--neutral-300) / 1);
}
@media (prefers-color-scheme: dark) {
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-skin--kant.chat-channel--staff.chat-office-kant--kromka {
    --chat-skin-band-bg: rgb(var(--neutral-50) / 1);
    --chat-kant-edge: rgb(var(--neutral-300) / 1);
  }
}
/* Пунктир кромки. Верхняя линия признака переписывает двойную линейку
   закрытого канала (грамматика): у извода без заливки двойная линейка была
   бы единственным жирным штрихом на всём канале — она спорила бы с самим
   тезисом «намёток вместо полосы». */
.chat-skin--kant.chat-channel--staff.chat-office-kant--kromka > .chat-skin-band {
  border-right: 1px dashed var(--chat-kant-edge);
}
.chat-skin--kant.chat-channel--staff.chat-office-kant--kromka .chat-channel-flag {
  border-top: 1px dashed var(--chat-kant-edge);
  border-bottom: 1px dashed var(--chat-kant-edge);
}

/* ПРАВИЛА «КАНТ СНИМАЕТ У ШТАМПА РАМКУ» ЗДЕСЬ БОЛЬШЕ НЕТ (ЧР-4, §3-А сборки
   Н-СБ). До ЧР-4 кант набирал признак просто капсом, без коробки, — и ровно
   поэтому под УМОЛЧАНИЕМ прода (CHAT_SKIN_DEFAULT = "kant") вердикт гейта
   ЧР-2 «плашка наклонная, в двойной рамке, сразу бросается в глаза» не
   выполнялся вовсе. Гриф поднят в грамматику (базовое .chat-channel-stamp) и
   одинаков во всех трёх отделках; чернила ему даёт --chat-chan-stamp-ink,
   который кант не трогает.

   ПРАВИЛА «ГРИФ НА КАНТЕ ПЕЧАТАЕТСЯ ЦВЕТОМ САМОЙ СТРОКИ-ПРИЗНАКА» ЗДЕСЬ
   БОЛЬШЕ НЕТ (приёмка ЧР, вердикт В2). Оно доворачивало чернила штампа под
   кантовую заливку признака (`color: inherit` плюс currentColor на рамке и
   кольце): грамматические чернила клиента #75582f по охре #8a6a3a давали
   1,05:1, штамп пропадал совсем. Заливки у признака больше нет — плашка
   стала общим нейтральным листом, и грамматические чернила на ней и есть
   верные: охра --brand-600 по листу 6,58:1 в светлой и 5,38:1 в тёмной,
   графит исполнителей 14,63 · 11,61, чернила офиса 17,85 · 13,21. Заплатка
   без своей болезни — второй источник правды о том, каким цветом печатают
   гриф; снимаем вместе с болезнью. */

/* Узкий экран: кант сужается до 16 px, кегль подписи прежний (канон .phone —
   подпись канала не сокращается ни на одной ширине). */
@media (max-width: 639.98px) {
  .chat-skin--kant { padding-left: 16px; }
  .chat-skin--kant > .chat-skin-band { width: 16px; }
}

/* ── Б «Бумага канала» ──────────────────────────────────────────────────
   Канон — Макеты/Чат_v2/Б_Бумага_канала.html (кадры _кроп_b-*.png): кому
   видно письмо, решает бумага, на которой лежит вся переписка. Клиенту —
   фирменный бланк: чистый лист с охряной линейкой, всё, что уходит наружу,
   печатается на нём. Исполнителям — служебная бумага в клетку с полем
   подшивки: рабочий журнал объекта. Письма на обеих — не пузыри, а карточки
   без радиуса с боковым кантом; штамп признака над полем ввода — гриф в
   двойной рамке с лёгким поворотом.

   НИ ОДНОГО СВОЕГО ЭЛЕМЕНТА В DOM (сторож test_bumaga_is_pure_css): лист
   нарисован слоями background. У канта разметка своя, и шаблоны обязаны
   знать про отделку; бумага не требует от них ничего — разница ответа
   против канта сводится к имени класса на контейнере.

   ЛИСТ ОДИН НА ВСЮ ПЕРЕПИСКУ (канон .feed / .composer, строки 187 и
   259-265): бумага идёт от верха ленты до кнопки отправки. Отсюда две копии
   листа: основная — на КОНТЕЙНЕРЕ канала (она непрерывна, потому что это
   один элемент), и вторая — на самой ленте, с background-attachment: local.
   Лента и есть скролл-контейнер (overflow-y-auto), и её бумага обязана
   ехать вместе с письмами; при значении по умолчанию клетка стояла бы на
   месте, а письма плыли бы по ней. Композер своего фона не имеет вовсе —
   просвечивает контейнерный лист. СТРОКА-ПРИЗНАК с приёмки ЧР (вердикт В2)
   лежит на бумаге своим листом, а не просвечивает её (разбор — у правила
   отступа подшивки ниже).

   ФАЗА ГОРИЗОНТАЛЬНЫХ ЛИНЕЕК на границе ленты и композера не сходится, и
   это неизбежно: лента везёт свой лист под прокрутку, значит её клетка
   каждую секунду в другой фазе. Непрерывно то, за что держится глаз, —
   полоса подшивки, обе линии подшивки и вертикальные линейки: они зависят
   только от X, а X у контейнера, ленты и композера общий.

   ПЛОТНОСТЬ И ШИРИНЫ — ПЕРЕМЕННЫМИ, стопка градиентов — тоже (--chat-bumaga-sheet).
   Слоёв шесть, а мест, где лист рисуется, три (контейнер, лента и узкий
   экран) — шесть чисел в трёх копиях это шесть шансов разъехаться. Узкий
   экран правит ровно два числа, стопка пересобирается сама (канон и требует
   именно этого: «поле подшивки сжимается с 56 до 26 px, шаг клетки остаётся
   прежним» — подпись к кадру кадры/Б_4_телефон390_светлая.png). */
.chat-skin--bumaga.chat-channel--object {
  --chat-bumaga-paper: var(--neutral-100);  /* сам лист — см. тёмную пару ниже */
  --chat-bumaga-cell: 0.085;   /* крупная клетка 40 px — канон rgba(15,23,42,.085) */
  --chat-bumaga-fine: 0.032;   /* мелкая 8 px — она поддерживает крупную, не спорит */
  --chat-bumaga-band: 0.05;    /* поле подшивки — полоса чуть плотнее самого листа */
  --chat-bumaga-bind: 56px;    /* ширина поля подшивки */
  --chat-bumaga-pad: 84px;     /* письма начинаются ЗА линиями подшивки */
  /* Поле подшивки: полоса, линия по её краю и вторая линия в 5 px от первой
     (канон ::before + ::after на 61 px, вторая глуше — .6 от первой). */
  --chat-bumaga-sheet:
    linear-gradient(90deg,
        rgb(var(--neutral-900) / var(--chat-bumaga-band)) 0 var(--chat-bumaga-bind),
        rgb(var(--neutral-500) / 0.55) var(--chat-bumaga-bind) calc(var(--chat-bumaga-bind) + 1px),
        transparent calc(var(--chat-bumaga-bind) + 1px) calc(var(--chat-bumaga-bind) + 5px),
        rgb(var(--neutral-500) / 0.33) calc(var(--chat-bumaga-bind) + 5px) calc(var(--chat-bumaga-bind) + 6px),
        transparent calc(var(--chat-bumaga-bind) + 6px)),
    repeating-linear-gradient(0deg, transparent 0 39px,
        rgb(var(--neutral-900) / var(--chat-bumaga-cell)) 39px 40px),
    repeating-linear-gradient(90deg, transparent 0 39px,
        rgb(var(--neutral-900) / var(--chat-bumaga-cell)) 39px 40px),
    repeating-linear-gradient(0deg, transparent 0 7px,
        rgb(var(--neutral-900) / var(--chat-bumaga-fine)) 7px 8px),
    repeating-linear-gradient(90deg, transparent 0 7px,
        rgb(var(--neutral-900) / var(--chat-bumaga-fine)) 7px 8px);
}

/* Тёмная: служебный лист уходит на ступень ВНИЗ, а не вверх. Инверсия
   нейтралей делает --neutral-100 в тёмной (#302b21) СВЕТЛЕЕ панели
   (--surface-0, #262219) — служебная бумага стала бы ярче клиентского
   бланка, и лестница «чем внутреннее, тем глуше» перевернулась бы (урок
   канта, Ч2Б-3). Канон в тёмной делает ровно наоборот: лист «Исполнителям»
   #14181d темнее панели #1c1a16, и белая карточка письма лежит НА бумаге, а
   не проваливается в неё. --neutral-50 (#1e1b15) держит оба смысла: темнее
   панели, светлее письма. Клетка от этого не страдает — замер ниже. Оба
   тёмных контекста, как у --chat-shadow (строки 60-65). */
html.themable[data-theme="dark"] .chat-skin--bumaga.chat-channel--object {
  --chat-bumaga-paper: var(--neutral-50);
}
@media (prefers-color-scheme: dark) {
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-skin--bumaga.chat-channel--object {
    --chat-bumaga-paper: var(--neutral-50);
  }
}

/* Лист под всем каналом. Клиенту — фирменный бланк: чистый лист, ничего
   больше. Чернила грифа — тем же блоком: канон набирает штамп ОДНИМ цветом
   (color, border и outline у .stamp — все var(--stamp)), и цвет этот у
   бланка охряный (#75582f = --brand-600), а у служебной бумаги графитовый
   (#334155 ≈ --neutral-800). С ЧР-4 у обоих это ровно --chat-chan-stamp-ink
   грамматики: тройка токенов штампа схлопнута в один вместе с отменой
   плашки, и «нужный из двух» выбирать больше не приходится. Алиас
   --chat-bumaga-ink оставлен: у офисных листов чернила СВОИ (--brand-800),
   и три блока бумаги называют их в одном и том же месте.
   Замер контраста грифа к бумаге: светлая — охра #75582f по #ffffff 6,6:1,
   графит #1e293b по #f1f5f9 13,0:1; тёмная — охра #b79054 по #262219 5,4:1,
   графит #e4dcca по #1e1b15 12,6:1.
   Гейт без --client, то есть бланк — умолчание, и незнакомый kind получит
   КЛИЕНТСКИЙ лист при fail-closed штампе (отделка fail-open). Лист офиса
   заведён блоком --staff сразу под этим правилом (Ч2-В, В5б: три сорта
   бумаги и сепиевый гриф), умолчание осталось страховкой. */
.chat-skin--bumaga.chat-channel {
  --chat-bumaga-ink: var(--chat-chan-stamp-ink);
  background-color: rgb(var(--surface-0) / 1);
}

/* ── Бумага канала «Офис» (Ч2-В, В5б) ───────────────────────────────────
   Тезис офисного листа (колода мокапов_Ч2В): он НЕ ПОДШИВАЕТСЯ НИКУДА.
   Клиентский — фирменный бланк, служебный — журнал объекта в клетку с полем
   подшивки, офисный — бумага, которую в дело не кладут. Отсюда у всех трёх
   изводов тонированный лист и ни одной клетки.

   Гриф у всех трёх сепиевый (--brand-800), и это их и различает: у служебной
   бумаги он графитовый (--neutral-800). Алиас --chat-bumaga-ink вычисляется
   на КОНТЕЙНЕРЕ и приезжает вниз готовым цветом — взять «нужный из двух»
   токенов прямо на штампе нельзя, макрос _channel_stamp.html объявляет их
   там заново (та же механика, что у бланка выше).

   ЧЕРНИЛ ПРИЗНАКА ЗДЕСЬ БОЛЬШЕ НЕТ (приёмка ЧР, вердикт В2). Они стояли
   дважды — тут и в тёмной паре ниже, — потому что признак лежал НА листе с
   прозрачным фоном, а грамматика офиса писала его по залитой плите и весила
   (0,3,1) против (0,2,0) этого блока. Плашка стала общим нейтральным листом,
   грамматика офиса теперь и сама пишет вторичным текстом по нему, и
   повторять нечего: два места, где хранились одни и те же чернила,
   схлопнулись в одно. */
.chat-skin--bumaga.chat-channel--staff {
  --chat-bumaga-ink: rgb(var(--brand-800) / 1);
  background-color: rgb(var(--chat-bumaga-paper) / 1);
  background-image: var(--chat-bumaga-sheet);
}

/* Извод «Черновик» (дефолт, Б-3) — смена сорта бумаги: тёплый тон плюс очень
   слабая косая фактура 45° (та же семья, что штриховка канта объекта, но в
   двадцать раз тише: 1,05:1 против 1,19:1 у клетки служебной бумаги) и
   двойная отбивка сверху вместо линейки. Крупнейшая ступень к панели из трёх
   (1,55:1, L листа 0,628) — и лучшая читаемость письма: белая карточка на
   таком листе лежит, а не тонет. Гриф к листу 7,92:1 · 8,33:1. */
.chat-skin--bumaga.chat-channel--staff.chat-office-bumaga--chernovik {
  --chat-bumaga-paper: var(--brand-300);
  --chat-bumaga-sheet:
    repeating-linear-gradient(45deg,
        rgb(var(--neutral-900) / 0.028) 0 3px, transparent 3px 6px);
  --chat-bumaga-rule: 1px solid rgb(var(--neutral-600) / 1);
}

/* Извод «Велень» (Б-1) — гладкий тонированный лист, ни одной линии. Линовка
   всегда значит «лист приготовлен под учёт»; офисный не приготовлен ни подо
   что. Линейка канала карандашная (--neutral-400, 2,04:1 · 3,70:1), лист к
   панели 1,26:1 (L 0,785). Гриф к листу 9,76:1 · 8,33:1. */
.chat-skin--bumaga.chat-channel--staff.chat-office-bumaga--velen {
  --chat-bumaga-paper: var(--brand-200);
  --chat-bumaga-sheet: none;
  --chat-bumaga-rule: 3px solid rgb(var(--neutral-400) / 1);
}

/* Извод «Поле» (Б-2) — весь смысл в одной перестановке: у служебной бумаги
   поле слева (туда попадёт дырокол), у офисной справа и пунктиром (там пишут
   на полях). Письма идут во всю ширину до поля, отсюда снятая мера письма.
   Пунктир к листу 1,83:1 · 2,66:1, линейка канала пунктирная (--neutral-500,
   4,29:1 · 6,53:1), гриф к листу 11,06:1 · 8,33:1. Риск (назван в колоде):
   190 px ширины стоят пустыми, пока в поле нечего писать. */
.chat-skin--bumaga.chat-channel--staff.chat-office-bumaga--pole {
  --chat-bumaga-paper: var(--brand-100);
  --chat-bumaga-field: 190px;
  --chat-bumaga-sheet:
    repeating-linear-gradient(180deg,
        rgb(var(--neutral-400) / 0.75) 0 4px, transparent 4px 9px);
  --chat-bumaga-rule: 3px dashed rgb(var(--neutral-500) / 1);
}

/* Тёмная: все три листа встают на --brand-50 (#1e1a15) — ступень ВНИЗ от
   панели (#262219), как у служебной бумаги (Ч2Б-4). Ниже --brand-50 /
   --neutral-50 в шкале ничего нет, кроме --scrim, а он затемнение под
   модалками, не подложка, — поэтому в тёмной изводы различает СТРУКТУРА
   (фактура, поле, линейка), а не тон: это известное свойство палитры, а не
   недоделка (колода, «Общее ограничение тёмной темы»). Оба тёмных контекста,
   как у --chat-shadow (строки 60-65). Правило стоит ПОСЛЕ изводов: вес у них
   равный (0,3,0), спор решает порядок.

   ЧЕРНИЛА ПРИЗНАКА СНЯТЫ И ОТСЮДА (В2): повтор держался на споре весов с
   грамматической тёмной парой офиса, а той пары больше нет — заливка плашки
   и её чернила стали токенами шкалы и переворачиваются сами. */
html.themable[data-theme="dark"] .chat-skin--bumaga.chat-channel--staff {
  --chat-bumaga-paper: var(--brand-50);
}
@media (prefers-color-scheme: dark) {
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-skin--bumaga.chat-channel--staff {
    --chat-bumaga-paper: var(--brand-50);
  }
}

/* Поле пометок рисуется ОДНИМ слоем на двух копиях листа (контейнер и лента):
   вертикаль зависит только от X, а X у контейнера, ленты, признака и
   композера общий — фаза не разъедется даже под прокруткой (та же логика,
   что у линий подшивки служебной бумаги). Отступ справа держат все три
   поверхности: письмо, признак и поле ввода обязаны кончаться ДО пунктира,
   иначе поле перестанет быть полем. */
.chat-skin--bumaga.chat-channel--staff.chat-office-bumaga--pole,
.chat-skin--bumaga.chat-channel--staff.chat-office-bumaga--pole .chat-channel-feed {
  background-size: 1px 100%;
  background-position: right var(--chat-bumaga-field) top;
  background-repeat: repeat-y;
}
.chat-skin--bumaga.chat-channel--staff.chat-office-bumaga--pole .chat-channel-feed,
.chat-skin--bumaga.chat-channel--staff.chat-office-bumaga--pole .chat-channel-flag,
.chat-skin--bumaga.chat-channel--staff.chat-office-bumaga--pole .chat-composer {
  padding-right: calc(var(--chat-bumaga-field) + 16px);
}
.chat-skin--bumaga.chat-channel--staff.chat-office-bumaga--pole .chat-msg { max-width: none; }

/* Узкая колонка: поле пометок сжимается втрое (канон бумаги сжимает поле
   подшивки 56 → 26 px — то же движение), иначе на 390 px письму осталась бы
   треть ширины. Порог тот же, что у канта и служебной бумаги. */
@media (max-width: 639.98px) {
  .chat-skin--bumaga.chat-channel--staff.chat-office-bumaga--pole {
    --chat-bumaga-field: 64px;
  }
}

/* Исполнителям — служебная бумага в клетку с полем подшивки.

   АЛЬФЫ ОДНИ НА ОБЕ ТЕМЫ, второго набора не нужно: --neutral-900 в тёмной —
   самый светлый токен шкалы (#f0eadc), и та же альфа рисует не тёмную линию
   на светлой бумаге, а светлую на тёмной, ровно как в кадре
   _кроп_b-internal-dark.png. Канон в тёмной альфу поднимал (0.13 против
   0.085) — нам поднимать нечего: замер контраста линии к листу даёт в
   светлой #dee2e7 на #f1f5f9 = 1,19:1, в тёмной #302d26 на #1e1b15 =
   1,25:1. Тёмная клетка и так на ступень заметнее светлой. */
.chat-skin--bumaga.chat-channel--object {
  --chat-bumaga-ink: var(--chat-chan-stamp-ink);
  background-color: rgb(var(--chat-bumaga-paper) / 1);
  background-image: var(--chat-bumaga-sheet);
}

/* Лента: своя копия листа — она едет вместе с письмами (local), потому и
   копия. Линейка сверху называет канал: охра наружу, графит внутрь, а офису
   (Ч2-В, В5б — блок --staff двумя правилами ниже) достался целый СОРТ линии
   на извод. Охряная линейка остаётся умолчанием для всех kind, кроме object
   и staff: fail-open против fail-closed штампа. */
.chat-skin--bumaga .chat-channel-feed {
  border-top: 3px solid rgb(var(--brand-500) / 1);
  background-color: rgb(var(--surface-0) / 1);
}
.chat-skin--bumaga.chat-channel--object .chat-channel-feed {
  border-top-color: rgb(var(--neutral-500) / 1);
  background-color: rgb(var(--chat-bumaga-paper) / 1);
  background-image: var(--chat-bumaga-sheet);
  background-attachment: local;
  padding-left: var(--chat-bumaga-pad);
}

/* Офис (Ч2-В): вторая копия листа — она едет вместе с письмами (local), как
   у служебной бумаги. Линейка канала здесь не цвет, а ЦЕЛАЯ строка
   (--chat-bumaga-rule): изводы спорят не оттенком, а самим сортом линии —
   карандашная сплошная у велени, пунктирная у поля, тонкая графитовая у
   черновика. Отсюда border-top переменной, а не border-top-color: у бланка и
   служебной бумаги линейка одна и та же 3 px solid, у офиса — три разных. */
.chat-skin--bumaga.chat-channel--staff .chat-channel-feed {
  border-top: var(--chat-bumaga-rule);
  background-color: rgb(var(--chat-bumaga-paper) / 1);
  background-image: var(--chat-bumaga-sheet);
  background-attachment: local;
}

/* Двойная отбивка черновика: вторая линия рисуется внутренней тенью, а не
   вторым бордюром — бордюр у ленты один и занят линейкой канала. Отбивка
   к листу 4,89:1 · 8,69:1. */
.chat-skin--bumaga.chat-channel--staff.chat-office-bumaga--chernovik .chat-channel-feed {
  box-shadow: inset 0 3px 0 -2px rgb(var(--neutral-600) / 1);
}

/* КОМПОЗЕР лежит НА листе, своего фона у него нет.
   Композер адресован КЛАССОМ .chat-composer (Ч3-4а, П-8): пункт контракта
   Ч2 «класса у формы нет» снят этапным решением. Адресация по тегу была
   вынужденной — общий партиал на четыре поверхности править было нельзя, —
   и расползлась бы по отделкам (три отделки × три канала × шесть изводов
   офиса). Класс — одна точка, за которую держатся и отделки, и три извода
   композера, и слушатель подтверждения в chat.js. Гейт отделки селектор
   проходит: .chat-skin--bumaga стоит первым, и правило живёт только там, где
   контейнер канала носит класс отделки, то есть в CRM.

   СТРОКА-ПРИЗНАК С ЛИСТА СОШЛА (приёмка ЧР, вердикт В2). Она была
   прозрачной, и лист просвечивал под ней насквозь — из-за этого «плашка» у
   каждого канала оказывалась своего сорта бумаги, а в тёмной вставала на
   #1e1b15 / #1e1a15 вместо штатного листа темы #262219 (наблюдение User по
   «Бумаге»). Теперь у признака своя заливка из грамматики, и он читается
   листом, ПОЛОЖЕННЫМ на бумагу переписки. Клетка под ним не просвечивает —
   зато фаза линеек, которая на границе ленты и признака и так не сходилась
   (см. выше), больше и не видна.
   Отступ подшивки остаётся общим с композером (канон
   .ch-crew .composer { padding-left:84px }): левый край слов признака,
   писем и поля ввода — один, иначе строка выпадала бы из колонки. */
.chat-skin--bumaga.chat-channel--object .chat-channel-flag,
.chat-skin--bumaga.chat-channel--object .chat-composer { padding-left: var(--chat-bumaga-pad); }

/* Письмо на бумаге — не пузырь, а карточка: радиусы сняты, слева боковой
   кант в цвет линеек листа. Своё письмо помечено охрой СПРАВА, у подписи
   (канон .msg.own .bubble).

   Радиус снимается ДВАЖДЫ, и это не копипаста: базовое правило своего
   письма `.chat-msg[data-mine="1"] .chat-msg-bubble` весит 0,3,0 — больше,
   чем гейт отделки на пузыре (0,2,0), — и его border-top-left-radius: 14px
   пережил бы одиночный border-radius: 0 этажом выше. У своих писем остался
   бы ровно один скруглённый угол на всю ленту. */
.chat-skin--bumaga .chat-msg-bubble {
  border-radius: 0;
  border-left: 3px solid rgb(var(--neutral-300) / 1);
  /* Карточка на бумаге плоская: канон тени под письмом не даёт вовсе, а
     наша грамматика в тёмной кладёт под пузырь чёрный провал альфы 0.45 —
     на листе он читается грязью, а не глубиной. */
  box-shadow: none;
}
.chat-skin--bumaga .chat-msg[data-mine="1"] .chat-msg-bubble {
  border-radius: 0;
  border-left-width: 1px;
  border-right: 3px solid rgb(var(--brand-500) / 1);
}

/* САМ РИСУНОК ГРИФА ОТСЮДА УЕХАЛ В ГРАММАТИКУ (ЧР-4, §3-А сборки Н-СБ):
   двойная рамка с поворотом теперь свойство штампа и стоит во всех трёх
   отделках, а не только здесь. Бумаге осталось то, что и правда её: СВОИ
   чернила. У бланка они охряные (--brand-600), у служебной бумаги
   графитовые (--neutral-800), у офисных листов сепиевые (--brand-800) —
   алиас --chat-bumaga-ink вычислен на контейнере тремя блоками выше.
   Замеры грифа к бумаге: светлая — охра #75582f по #ffffff 6,6:1, графит
   #1e293b по #f1f5f9 13,0:1; тёмная — охра #b79054 по #262219 5,4:1, графит
   #e4dcca по #1e1b15 12,6:1.
   Токеном, а не тремя свойствами: рисунок в одном месте, отделка меняет
   только цвет. Вес (0,3,0) бьёт объявление токена на самом штампе (0,1,0 от
   классов канала, которые вешает макрос _channel_stamp.html). */
.chat-skin--bumaga .chat-channel-flag .chat-channel-stamp {
  --chat-chan-stamp-ink: var(--chat-bumaga-ink);
}

/* Узкая колонка: поле подшивки сжимается (канон .ph — 56 → 26 px), шаг
   клетки прежний. Порог тот же, что у канта. */
@media (max-width: 639.98px) {
  .chat-skin--bumaga.chat-channel--object {
    --chat-bumaga-bind: 26px;
    --chat-bumaga-pad: 46px;
  }
}

/* ── В «Письмо и журнал» ────────────────────────────────────────────────
   Канон — Макеты/Чат_v2/В_Письмо_и_журнал.html (кадры
   В_Письмо_и_журнал_{десктоп,тёмная}_{клиент,журнал}.png, _телефон.png).
   Тезис макета: каналы различаются НЕ ЦВЕТОМ, А ФОРМОЙ ЗАПИСИ. Клиенту —
   письма: жанр I канона и есть наша базовая грамматика Ч2, ей нужна только
   тёплая бумага переписки. Исполнителям — вахтенный журнал: записи в
   линейку, моноширинное время в левой графе, вертикальная линейка графы,
   левая подшивка, ни пузырей, ни деления своё/чужое — журнал это регистр,
   а не диалог, и запись админа выглядит как запись прораба.

   ЧТО ИЗМЕНЕНО ПРОТИВ КАНОНА. В макете автор стоит В СТРОКЕ записи
   (.rec-b .who), у нас он строкой НАД текстом: разметка письма — контракт
   Ч2 (article.chat-msg с шапкой .chat-msg-head), в Ч2-Б она неприкосновенна,
   а свести два разных поддерева в одну строку без обёртки нечем. Отклонение
   принято планом П-3. Плотность возвращают сжатые отступы графы: строка
   автора набрана 11 px капсом при интерлиньяже графы, и запись остаётся
   вдвое плотнее письма.

   МЕХАНИКА — grid на письме + display: contents на его шапке. Шапка
   перестаёт быть боксом, и её дети (автор, время) становятся ячейками
   письма-сетки: время уезжает в левую графу и растягивается на всю высоту
   записи, автор и текст — в правую. Ни одного своего элемента в DOM,
   как у бумаги (сторож test_zhurnal_keeps_letter_contract).

   ЖАНР III «заметки на полях» приехал с каналом «Офис» (Ч2-В, В5б) — в
   конце подсекции. Записи там не регистр и не переписка: узкая страница
   заметок, а подпись и время вынесены в поле справа за пунктирную линию,
   как на полях книги. Отделка узнала о канале ровно тогда, когда о нём
   узнал продукт.

   ФОН — ТОЛЬКО НА ЛЕНТЕ, в отличие от бумаги (Ч2Б-4). Канон кладёт жанровые
   подложки на .feed (--paper / --ledger), а композер оставляет панелью с
   собственной полосой признака: у бумаги лист — метафора всего канала, у
   журнала подложка — свойство ЛЕНТЫ, потому что в поле ввода записи ещё
   нет. Отсюда же и штамп признака: он про канал, а не про жанр, и остаётся
   при грамматике — журнал не трогает ни строку-признак, ни композер.

   ЛИЧНЫЙ ДИАЛОГ (dm) отделки не носит вовсе — на его контейнере нет ни
   класса канала, ни класса отделки (П-4, сторож test_hub_dm_thread_has_no_skin).
   Тёплая бумага писем на него не распространяется по построению: правило
   гейтится .chat-skin--zhurnal, а этого класса у dm нет. */

/* Подложки жанров. Клиенту — тёплая бумага переписки (канон --paper
   #fbf9f4 ≈ --brand-50 #faf8f3), исполнителям — холодный разворот журнала
   (--ledger #eef1f6 ≈ --neutral-100 #f1f5f9).
   Офису (Ч2-В) — велень: тёплая тонированная страница заметок (--brand-200,
   L 0,785 против 0,939 у клиентской бумаги и 0,908 у разворота журнала).
   Третья ступень одной лестницы, и на ней третий жанр — блок --staff,
   которого не хватало (минор T2-5, долг «отделки fail-open» закрыт). */
.chat-skin--zhurnal .chat-channel-feed { background: rgb(var(--brand-50) / 1); }
.chat-skin--zhurnal.chat-channel--object .chat-channel-feed {
  background: rgb(var(--chat-zhurnal-ledger) / 1);
}
.chat-skin--zhurnal.chat-channel--staff .chat-channel-feed {
  background: rgb(var(--chat-zhurnal-vellum) / 1);
}

/* Тёмная: разворот журнала уходит на ступень ВНИЗ, а не вверх — тот же урок,
   что у канта (Ч2Б-3) и бумаги (Ч2Б-4). Инверсия нейтралей делает
   --neutral-100 в тёмной (#302b21) СВЕТЛЕЕ панели (--surface-0 #262219):
   самый закрытый канал стал бы самым ярким пятном экрана. --neutral-50
   (#1e1b15) ставит его под панель, как в каноне (--ledger #16171b темнее
   панели #1c1a16). Клиентской бумаге пары не нужно: --brand-50 в тёмной
   сам становится тёплым тёмным фоном (#1e1a15) — письмо на нём лежит
   панелью и не проваливается.
   ЗАМЕР ЛЕСТНИЦЫ по кадрам (shots-task5). Светлая: бумага писем #faf8f3
   (L 0,939), разворот журнала #f1f5f9 (L 0,908) — журнал на ступень глуше,
   как в каноне. Тёмная: #1e1a15 (L 0,0107) против #1e1b15 (L 0,0111) при
   панели #262219 (L 0,0163). Обе подложки ПОД панелью, но между собой
   различаются на 0,8% яркости (1,007:1) — это НИЖЕ порога различимости, в
   тёмной ступени между каналами фактически нет. Ступени вниз в шкале и
   взять неоткуда: --neutral-50 — самый тёмный фон тёмной темы, ниже только
   --scrim (#080705), а он затемнение под модалками, не подложка. Значит
   выбор вынужденный: --neutral-50 обязателен, иначе (на --neutral-100
   #302b21) закрытый канал оказался бы ЯРЧЕ панели. Различает каналы в
   тёмной не яркость, а форма записи — «различаются они не цветом, а формой
   записи», титул канона. Оба тёмных контекста, как у --chat-shadow
   (строки 60-65). */
.chat-skin--zhurnal.chat-channel--object { --chat-zhurnal-ledger: var(--neutral-100); }
html.themable[data-theme="dark"] .chat-skin--zhurnal.chat-channel--object {
  --chat-zhurnal-ledger: var(--neutral-50);
}
@media (prefers-color-scheme: dark) {
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-skin--zhurnal.chat-channel--object {
    --chat-zhurnal-ledger: var(--neutral-50);
  }
}

/* Велень офиса. В светлой это самая глухая подложка из трёх (замер выше), в
   тёмной --brand-200 стало бы #3a3124 — ЯРЧЕ панели, то есть перевёрнутая
   лестница в третий раз (кант Ч2Б-3, бумага Ч2Б-4, разворот журнала выше).
   Ступень вниз — --brand-50 (#1e1a15, L 0,0107 при панели 0,0163): ровно та
   же вынужденная нижняя ступень, что у разворота журнала, и та же, на
   которой стоит клиентская бумага. Значит в тёмной офис от клиента тоном не
   отличается вовсе (1,00:1) — и не должен: «различаются они не цветом, а
   формой записи», титул канона. Форма у офиса своя целиком — жанр III ниже.
   Оба тёмных контекста, как у --chat-shadow (строки 60-65). */
.chat-skin--zhurnal.chat-channel--staff { --chat-zhurnal-vellum: var(--brand-200); }
html.themable[data-theme="dark"] .chat-skin--zhurnal.chat-channel--staff {
  --chat-zhurnal-vellum: var(--brand-50);
}
@media (prefers-color-scheme: dark) {
  html.themable:not([data-theme="light"]):not([data-theme="dark"]) .chat-skin--zhurnal.chat-channel--staff {
    --chat-zhurnal-vellum: var(--brand-50);
  }
}

/* Подшивка журнала — линейка вдоль ВСЕЙ ленты (канон .log), а не вдоль
   каждой записи: она про сшитый том, а не про строку. Лента и есть
   единственный элемент, который живёт столько же, сколько том. */
.chat-skin--zhurnal.chat-channel--object .chat-thread {
  border-left: 2px solid rgb(var(--neutral-300) / 1);
}

/* Запись: две графы (канон .rec — 74 px под время и остаток под запись).
   max-width и margin письма снимаются вместе: в журнале нет ни колонки в
   640 px, ни воздуха между репликами — записи стоят вплотную и делятся
   линейкой. Шорткат margin гасит и margin-left: auto своего письма
   (.chat-msg[data-mine="1"], вес 0,2,0 — меньше здешних 0,3,0): сторон в
   журнале нет. */
.chat-skin--zhurnal.chat-channel--object .chat-msg {
  display: grid;
  grid-template-columns: 74px minmax(0, 1fr);
  max-width: none;
  margin: 0;
  border-bottom: 1px solid rgb(var(--neutral-200) / 1);
}
.chat-skin--zhurnal.chat-channel--object .chat-msg:last-child { border-bottom: 0; }
.chat-skin--zhurnal.chat-channel--object .chat-msg-head { display: contents; }

/* Графа времени. grid-row тянет ячейку на три ряда записи (автор, текст,
   статус), чтобы линейка графы шла во всю высоту записи, а не до конца
   строки времени. Рядов может быть и два (в канале объекта статус не
   рендерится вовсе, см. ниже) — лишний ряд остаётся пустым и нулевой
   высоты, а линейка всё равно кончается ровно на нижней линейке записи. */
.chat-skin--zhurnal.chat-channel--object .chat-msg-at {
  grid-column: 1;
  grid-row: 1 / span 3;
  padding: 10px 10px 10px 12px;
  text-align: right;
  border-right: 1px solid rgb(var(--neutral-300) / 1);
  font-size: 13px;
  line-height: 1.45;
}
/* Интерлиньяж графы — и у автора: капс 11 px при line-height: 1 (грамматика
   письма) вставал на 4 px выше времени, и шапка записи читалась ступенькой. */
.chat-skin--zhurnal.chat-channel--object .chat-msg-who {
  grid-column: 2;
  padding: 10px 0 0 16px;
  line-height: 1.45;
}

/* Текст записи: раскладка и кегль. 86ch — мера канона (.rec-b): в широком
   хабе строка записи иначе уходит на всю ленту и перестаёт читаться. */
.chat-skin--zhurnal.chat-channel--object .chat-msg-bubble {
  grid-column: 2;
  box-shadow: none;
  padding: 2px 4px 11px 16px;
  max-width: 86ch;
  font-size: 14.5px;
  line-height: 1.45;
}

/* Пузырь в журнале и есть то единственное, чего в нём быть не может: ни
   подложки, ни рамки, ни радиуса. Селектора хватает ОДНОГО, и это замер, а
   не надежда: гейт журнала на пузыре
   (.chat-skin--zhurnal.chat-channel--object .chat-msg-bubble) весит 0,3,0 —
   РОВНО столько же, сколько базовое правило своего письма
   (.chat-msg[data-mine="1"] .chat-msg-bubble, тоже 0,3,0: класс + атрибут +
   класс). При равных весах спор решает порядок в файле, а секция отделок
   стоит НИЖЕ грамматики — выигрывают эти три декларации, и охряная подложка
   с обоими радиусами уходят у своих записей тоже.
   Мерка «0,2,0», из-за которой бумага (Ч2Б-4) объявляет радиус дважды, к
   журналу не относится: там гейт одноклассовый (.chat-skin--bumaga
   .chat-msg-bubble = 0,2,0), он базовому правилу и правда проигрывает —
   второе объявление у бумаги вынужденное, здесь оно было бы лишним весом.
   Отдельным правилом, а не строками к раскладке выше, — только для чтения:
   там геометрия записи, здесь снятие пузыря. Селектор у них один и тот же,
   каскаду слияние не мешает. */
.chat-skin--zhurnal.chat-channel--object .chat-msg-bubble {
  background: none;
  border: 0;
  border-radius: 0;
}

/* Вложение остаётся чипом с рамкой (канон .att), но в регистре журнала:
   моноширинный ярлык и плотнее поля. Данные журнал не редит — он меняет
   жанр записи, а не её состав. */
.chat-skin--zhurnal.chat-channel--object .chat-msg-att {
  margin-top: 8px;
  padding: 4px 9px;
  font-family: var(--chat-face-mono);
  font-size: 11.5px;
}

/* Статус доставки — ПРЕДОХРАНИТЕЛЬ, а не отделка. Сегодня в канале объекта
   его не бывает вовсе: chat.message_statuses отдаёт пустой dict для
   группового канала (статус «прочитано» на несколько читателей не
   определён), и партиал письма рендерит строку только при m.status. Но без
   grid-column строка при появлении легла бы АВТОМАТОМ в первую свободную
   ячейку — то есть в графу времени, поверх линейки. Три свойства дешевле
   такой поломки; выравнивание влево — потому что в регистре нет стороны,
   к которой прижимать. */
.chat-skin--zhurnal.chat-channel--object .chat-msg-status {
  grid-column: 2;
  justify-content: flex-start;
  margin: 0;
  padding: 0 4px 10px 16px;
  font-size: 12px;
}

/* Разделитель дня и «показать раньше» остаются собой (канон держит их
   одинаковыми во всех трёх жанрах — это и есть доказательство единства
   продукта). Журналу правится только воздух вокруг дня: канон .log .daysep
   { margin: 12px } — те же 12 px и слева, от линии подшивки. */
.chat-skin--zhurnal.chat-channel--object .chat-daysep { margin: 12px; }

/* Телефон (канон @container 560: .rec block, .rec-t inline, .log сохраняет
   подшивку). Граф нет — есть штемпель времени и запись под ним; линейка
   подшивки и линейки между записями остаются, они и держат жанр на узком
   экране. Порог — тот же viewport-порог, что у канта и бумаги: секция
   отделок меряет экран, грамматика письма — ширину ленты (@container), и
   смешивать две меры внутри одной подсекции было бы хуже, чем разойтись с
   грамматикой на редком стыке. Журнал от этого не ломается: в узкой колонке
   на широком экране графы просто остаются графами. */
@media (max-width: 639.98px) {
  .chat-skin--zhurnal.chat-channel--object .chat-msg {
    display: block;
    padding: 9px 0 10px 12px;
  }
  .chat-skin--zhurnal.chat-channel--object .chat-msg-head {
    display: flex;
    justify-content: flex-start;
    margin-bottom: 2px;
  }
  /* order: -1 — штемпель времени перед автором. Своё письмо равным весом
     (0,3,0) прижимало шапку вправо; здесь спор решает порядок в файле. */
  .chat-skin--zhurnal.chat-channel--object .chat-msg-at {
    order: -1;
    border: 0;
    padding: 0;
    text-align: left;
    font-size: 12.5px;
  }
  .chat-skin--zhurnal.chat-channel--object .chat-msg-who { padding: 0; }
  .chat-skin--zhurnal.chat-channel--object .chat-msg-bubble {
    padding: 0;
    font-size: 14px;
  }
  .chat-skin--zhurnal.chat-channel--object .chat-msg-status { padding: 6px 0 0; }
}

/* ── Жанр III «заметки на полях»: канал «Офис» (Ч2-В, В5б) ──────────────
   Канон — В_Письмо_и_журнал.html, секция 5 (.notes / .note / .note-a):
   узкая страница 760 px по центру, запись в две колонки — текст слева,
   подпись и время в поле справа за пунктирной карандашной линией,
   интерлиньяж 1,7, 30 px воздуха между заметками, ни одной рамки и ни одной
   сплошной линейки. Тезис жанра: офисная запись — не письмо (адресата нет) и
   не запись регистра (учитывать нечего), а пометка на полях чужого дела.

   МЕХАНИКА ТА ЖЕ, ЧТО У ВАХТЕННОГО ЖУРНАЛА: grid на письме плюс
   display: contents на его шапке — шапка перестаёт быть боксом, и её дети
   становятся ячейками сетки. Ни одного своего элемента в DOM (сторож
   test_zhurnal_keeps_letter_contract считает теги). Разница с журналом в
   том, КУДА уезжают ячейки: там время в левую графу, здесь автор и время —
   в правое поле.

   ТРИ РЯДА, И ПОСЛЕДНИЙ 1fr. Заметка почти всегда выше своей подписи, и
   лишнюю высоту надо отдать ОДНОМУ ряду: между авторазмерными рядами grid
   делит добавку поровну (§12.5), и подпись с временем расползлись бы по
   всей высоте заметки. Гибкий ряд забирает её целиком — подпись и время
   стоят вплотную вверху поля, а пунктир доходит до низа записи, потому что
   его тянет ячейка времени, растянутая до конца сетки.

   ДЕЛЕНИЯ СВОЁ/ЧУЖОЕ НЕТ, как и в журнале: max-width с margin сняты вместе
   (шорткат гасит и margin-left: auto своего письма, вес 0,2,0 против
   здешних 0,3,0), пузырь разобран до голого текста. Своё правило пузыря
   весит ровно столько же, сколько базовое `.chat-msg[data-mine="1"]
   .chat-msg-bubble` (0,3,0) — спор решает порядок, а секция отделок ниже
   грамматики (мерка та же, что у журнала).

   ЗАМЕРЫ на велени (светлая #eae5d9 / тёмная #1e1a15): текст и автор
   14,20:1 · 14,42:1; время 6,03:1 · 8,69:1; пунктир поля 2,04:1 · 3,70:1;
   подпись дня 3,79:1 · 6,53:1 (она декоративная и крупная — 10,5 px капсом
   при трекинге 2 px, порог AA к ней не применяется).
   ОТКЛОНЕНИЕ ОТ ГРАММАТИКИ, единственное: время набрано --neutral-600, а не
   --neutral-500 письма. Пятисотый даёт на велени 3,79:1 и порога AA 4,5:1 не
   берёт (замер колоды подтверждён), один шаг по шкале закрывает вопрос и на
   глаз почти незаметен. */
.chat-skin--zhurnal.chat-channel--staff .chat-thread {
  max-width: 760px;
  margin: 0 auto;
}
.chat-skin--zhurnal.chat-channel--staff .chat-msg {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 190px;
  grid-template-rows: min-content min-content 1fr;
  column-gap: 24px;
  max-width: none;
  margin: 0;
}
.chat-skin--zhurnal.chat-channel--staff .chat-msg-head { display: contents; }

/* Текст заметки — левая колонка во всю высоту записи. */
.chat-skin--zhurnal.chat-channel--staff .chat-msg-bubble {
  grid-column: 1;
  grid-row: 1 / span 3;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding: 0 0 30px;
  font-size: 15.5px;
  line-height: 1.7;
}
.chat-skin--zhurnal.chat-channel--staff .chat-msg:last-child .chat-msg-bubble {
  padding-bottom: 4px;
}

/* Поле подписи. Пунктир висит на ОБЕИХ ячейках поля, а не на общей обёртке:
   обёртки нет — шапка письма растворена display: contents. Ячейка времени
   тянется до конца сетки, поэтому линия и доходит до низа заметки. */
.chat-skin--zhurnal.chat-channel--staff .chat-msg-who {
  grid-column: 2;
  grid-row: 1;
  padding: 4px 0 0 18px;
  border-left: 1px dotted rgb(var(--neutral-400) / 1);
}
.chat-skin--zhurnal.chat-channel--staff .chat-msg-at {
  grid-column: 2;
  grid-row: 2 / span 3;
  padding: 7px 0 0 18px;
  border-left: 1px dotted rgb(var(--neutral-400) / 1);
  color: rgb(var(--neutral-600) / 1);
}

/* Статус доставки — ПРЕДОХРАНИТЕЛЬ, как в журнале: сегодня в групповом
   канале его не бывает вовсе (chat.message_statuses отдаёт пустой dict), но
   без явной ячейки строка при появлении легла бы автоматом в первую
   свободную — то есть в поле подписи, поверх пунктира. */
.chat-skin--zhurnal.chat-channel--staff .chat-msg-status {
  grid-column: 1;
  grid-row: 4;
  justify-content: flex-start;
  margin: 0;
  padding: 0 0 10px;
  font-size: 12px;
}

/* Разделитель дня остаётся собой (канон держит его одинаковым во всех трёх
   жанрах) — правится только воздух: у заметок его держит нижний отступ
   текста, и сверху дню добавлять нечего. */
.chat-skin--zhurnal.chat-channel--staff .chat-daysep { margin: 0 0 18px; }

/* Телефон (канон, строки 332-339): поле поворачивается из вертикального в
   горизонтальное — пунктир остаётся пунктиром и становится ЗАПОЛНИТЕЛЕМ
   строки, подпись садится на него справа. Сетка снимается целиком, порядок
   держит flex: текст сверху, подпись под ним (в разметке шапка стоит первой —
   контракт Ч2, менять его нельзя). Заполнитель — псевдоэлемент шапки: своих
   узлов жанр не заводит и на узком экране тоже. Порог — тот же viewport-порог,
   что у канта, бумаги и журнала. */
@media (max-width: 639.98px) {
  .chat-skin--zhurnal.chat-channel--staff .chat-thread { max-width: none; }
  .chat-skin--zhurnal.chat-channel--staff .chat-msg {
    display: flex;
    flex-direction: column;
    padding-bottom: 22px;
  }
  .chat-skin--zhurnal.chat-channel--staff .chat-msg:last-child { padding-bottom: 2px; }
  .chat-skin--zhurnal.chat-channel--staff .chat-msg-bubble {
    order: 1;
    padding: 0 0 9px;
    font-size: 15px;
    line-height: 1.65;
  }
  .chat-skin--zhurnal.chat-channel--staff .chat-msg-head {
    order: 2;
    display: flex;
    align-items: baseline;
    justify-content: flex-start;
    gap: 10px;
    margin: 0;
  }
  .chat-skin--zhurnal.chat-channel--staff .chat-msg-head::before {
    content: "";
    flex: 1;
    border-bottom: 1px dotted rgb(var(--neutral-400) / 1);
    transform: translateY(-4px);
  }
  .chat-skin--zhurnal.chat-channel--staff .chat-msg-who,
  .chat-skin--zhurnal.chat-channel--staff .chat-msg-at {
    border-left: 0;
    padding: 0;
  }
  .chat-skin--zhurnal.chat-channel--staff .chat-msg-status { order: 3; padding: 6px 0 0; }
}

/* ── Изводы композера ПОД отделками (Ч3-4б) ─────────────────────────────
   Два правила на две пары, и оба про сочетание: извод и отделка стоят на
   одном контейнере, поэтому каждое несёт ОБА гейта. Место — секция отделок:
   сплошной гейт «.chat-skin- в каждом селекторе» здесь и есть тот сторож,
   который держит правила подальше от ЛК, PWA и фрагмента поллинга; в
   грамматической части файла такому селектору не место по определению
   (test_skin_rules_never_address_base_chat_classes).

   Подсекция стоит ПОСЛЕДНЕЙ намеренно: извод — модификатор поверх отделки,
   а отделка поверх грамматики, и при равных весах спор решает порядок.
   Атомов ленты (.chat-msg, .chat-thread, .chat-daysep) она не трогает ни
   одним селектором — сторожа журнала это и проверяют. */

/* К2 под «Бумагой»: подложку снимаем совсем. Лист отделки обязан идти под
   композером НЕПРЕРЫВНО — это канон «Бумаги», ради которого лист и рисуется
   на всю высоту канала; бланк канала лёг бы поперёк него заплатой. Извод под
   «Бумагой» вырождается в «поле и кнопку» — цена названа в колоде и принята
   гейтом: кнопка канал по-прежнему называет, а лист остаётся листом. */
.chat-composer--blank.chat-skin--bumaga .chat-composer { background: transparent; }

/* К3 под «Кантом»: полосу подшивки снимаем. Вдоль канала уже идёт полоса
   отделки с вертикальной подписью, и вторая рядом читалась бы сбоем печати, а
   не знаком. Отступ возвращаем к штатному: без своей полосы двадцати пикселям
   слева нечего держать. Линейка над композером остаётся — она называет канал
   сортом линии, а кант его называет словом; повторения тут нет. */
.chat-composer--shtempel.chat-skin--kant .chat-composer::before { display: none; }
.chat-composer--shtempel.chat-skin--kant .chat-composer { padding-left: 16px; }
