/* Облик Флоу.Почты — общий слой под всеми кусками раздела.

   Подключён первым: здесь живут переменные, ритм и примитивы, которыми
   пользуются mail-list, mail-thread, mail-compose, mail-files и mail-phone.
   Частный лист вправе поправить своё, но не заводить своих величин.

   ---------------------------------------------------------------------
   Где проходит граница

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

   Правило простое: **если величина нужна двум листам — она здесь**. Если
   одному — она у него.

   ---------------------------------------------------------------------
   Откуда взяты значения

   Ни одного своего цвета в этом файле нет и быть не должно: палитра Флоу
   лежит в `app.css` переменными, и почта её применяет, а не заводит свою.
   Тёмная тема получается сама — ровно потому, что здесь нет ни одного
   шестнадцатеричного числа.

   Величины ритма взяты у Флоу.Чата и совпадают с ним до точки. Это не
   экономия мысли: почта и чат отвечают на один вопрос — «с кем я
   разговариваю», — и разный вид у них читался бы как две системы в одном
   окне (записка, раздел 80). Человек ходит между разделами по десять раз в
   час, и колонка, которая при переходе прыгает на десять точек, — это то,
   что видно, даже когда не смотришь.

   ---------------------------------------------------------------------
   Поле раздела: 44 точки

   У строки чата (`.room` в app.css) сетка `34px 1fr` с зазором `10px` и
   полями `12px`. То есть текст в списке чатов начинается на 44-й точке от
   края колонки. Почта стоит на той же сетке — и не только список: лицо и
   отступ повторяются в карточке письма и в свёрнутой строчке разговора.

   Отсюда одно наблюдение, ради которого это и сведено в переменные: в
   почте по колонке идут глазами сверху вниз, не читая, — и глаз идёт по
   вертикали имён. Одна вертикаль на весь раздел означает, что этой опоры
   не теряется ни при открытии разговора, ни при переходе к письмам. В
   mail.ru таких вертикалей три, в Gmail две. */

:root {
  /* ---------- ритм ----------

     Поля колонки (`--mail-gutter`) и зазор (`--mail-gap`) — те же, что у
     строки чата. Лицо во всю ширину своей колонки, а не 30 точек в 34,
     как аватар в чате: в списке писем по букве и цвету узнают отправителя
     — это главная примета строки, а в чате главное имя комнаты. */
  --mail-gutter: 12px;
  --mail-gap: 10px;
  --mail-face: 34px;
  /* Поле раздела — та самая вертикаль, на которой стоит весь текст. */
  --mail-margin: calc(var(--mail-face) + var(--mail-gap));

  /* Высота строки списка. Считается, а не подбирается: лицо плюс воздух
     сверху и снизу. Семь точек — минимум, при котором кружки соседних
     строк не слипаются в столбец. Итого 48.

     Строка обязана быть одной высоты и в широком списке (одно письмо в
     ряд), и в узком (имя над темой): список не должен менять рост, когда
     рядом открывается разговор, — иначе письмо, до которого человек
     долистал, уезжает из-под курсора. */
  --mail-row-pad: 7px;
  --mail-row-h: calc(var(--mail-face) + var(--mail-row-pad) * 2);

  /* Ширины колонок. Левая — ящики и фильтры, её ширину задаёт самое
     длинное слово в списке фильтров («Подключить ящик» со значком).
     Средняя — ровно та же, что список чатов: два списка в соседних
     разделах не имеют права стоять по-разному. */
  --mail-rail-w: 232px;
  --mail-list-w: var(--chat-list-w);

  /* Ширина подписи в колонке — числом, и это не лень.

     Подпись не сжимается вместе с колонкой (`flex: 0 0 auto` у
     `.mail__label`): у неё стоит многоточие, и меняющаяся ширина заставляет
     браузер каждый кадр пересчитывать, где его поставить. В меню системы это
     уже ловили глазами: «Роман К.» превращалось в «Роман…», «Ром…», «Р…» и
     гасло — двадцать перерисовок текста за одно нажатие (`app.css`,
     `.nav__label`). Поэтому ширина считается один раз, а обрезает подпись
     край колонки.

     Откуда 132. Колонка 232 без своих полей (10 + 10) даёт 212, без полей
     строки (9 + 9) — 194. Значок с зазором забирает 23, число непрочитанного
     с зазором — 34. Остаётся 137; взято 132, чтобы запас был и у самой
     широкой строки. */
  --mail-label-w: 132px;

  /* Шапка разговора. Нижняя граница — та же, что у шапок трёх колонок
     чата: разделы стоят рядом в меню, и верхняя полоса экрана обязана
     совпадать. Это минимум, а не размер: у почты в шапке две строки —
     тема и чипы, — и она бывает выше. Меньше не бывает никогда, поэтому
     разговор без чипов не роняет границу вверх. */
  --mail-head-h: var(--chat-head-h);

  /* Поле колонки разговора. Равное по кругу, в отличие от чатовского
     (`14px 18px 10px`): у чата в ленте пузыри разной длины, поле там
     работает на края, а не на форму. В почте по ленте едут карточки —
     прямоугольник в прямоугольном поле, и неравные поля у такого видны
     сразу. Этим же числом меряется шапка и строка ответа: письмо, поле
     ответа и тема стоят на одной вертикали. */
  --mail-pad: 16px;

  /* ---------- форма: пузырь и карточка ----------

     Скругление одно на оба вида, и это главное решение файла. Вид
     «перепиской» и вид «письмами» — не два оформления, а две раскладки
     одного и того же: переключая их, человек не должен видеть, что
     сменился стиль. Меняется расстановка и шапка, форма остаётся.

     14 точек — скругление пузыря чата. Хвостик в 5 точек — тоже его: у
     чужого письма он снизу слева, у своего снизу справа, и по одному
     этому углу видно, кто говорит, даже на снимке без цвета. */
  --mail-r: 14px;
  --mail-r-tail: 5px;

  /* Поля пузыря — чатовские до значения. Снизу больше, чем сверху: там
     стоит время. */
  --mail-bubble-pad: 6px 12px 7px;
  /* Шире чатовского (там 560): в письме абзацы, а не реплики, и строка в
     сорок знаков рвёт мысль там, где автор её не рвал. Доля та же — 72%:
     пузырь во всю ширину перестаёт быть пузырём. */
  --mail-bubble-max: min(72%, 640px);

  /* Карточка письма. Поля по горизонтали — 14: столько же у свёрнутой
     строчки разговора, и лица развёрнутого и свёрнутого письма встают на
     одну вертикаль. По вертикали — поле раздела. */
  --mail-card-pad-y: 12px;
  --mail-card-pad-x: 14px;

  /* Тень карточки — системная. Своя, «rgba(0,0,0,.06)», в тёмной теме не
     видна вовсе (подложка темнее тени), а `--shadow` для тёмной темы уже
     посчитан.

     Тень достаётся карточке и **не достаётся пузырю**: у чатовского
     пузыря её нет, и это там объяснено — в переписке пузырь отделяет от
     фона цвет, а не край. Две ленты пузырей в соседних разделах, одна с
     краем и одна без, — ровно то расхождение, которое читается как «две
     системы», даже когда назвать его не получается. */
  --mail-card-shadow: var(--shadow);

  /* Лист и врезка шапки — см. «лицо раздела» ниже. */
  --mail-sheet: var(--surface);
  --mail-sheet-head: var(--surface-2);

  /* ---------- чипы ----------

     Одна форма на все: клиент, заказ, ящик, вложение. Разными были только
     тона, и это правильно — заказ громче клиента, потому что номер в теме
     поставил человек, а клиента вычислила система по адресу. Но разной
     формы у них быть не должно: три формы в одной строке читаются как три
     разных вида объекта, которых на самом деле два.

     Пилюля, а не прямоугольник со скруглением: чип стоит внутри строки
     текста, и полукруглые торцы отделяют его от букв надёжнее любой
     рамки. */
  --mail-chip-r: 999px;
  --mail-chip-pad: 2px 8px;
  --mail-chip-fs: 11.5px;
  --mail-chip-gap: 4px;
  /* Тот же чип внутри строки списка. Форма, радиус и кегль не меняются —
     ужимаются только поля: в колонке шириной 306 точек за место спорят имя
     отправителя и метка заказа, и восемь точек воздуха по бокам метки
     стоят двух букв имени. По колонке имён список просматривают, по метке
     — нет. */
  --mail-chip-pad-sm: 1px 6px;

  /* ---------- состояния ----------

     Наведение и выбор — те же, что везде в системе. Непрочитанное — весом
     и акцентом, без красного: красный в «Графите» значит «сорвалось», а
     непрочитанное письмо ничего не сорвало.

     Приглушённое — то, чего у нас ещё нет: файл, оставшийся в почте.
     0,62 — не «почти невидимо», а «видно, что оно есть, и видно, что оно
     не здесь»: имя файла обязано читаться, иначе человек не поймёт, что
     ему предлагают скачать. */
  --mail-hover: var(--surface-2);
  --mail-active: var(--accent-soft);
  --mail-mark: var(--accent-text);
  --mail-unread-w: 700;
  --mail-dim: .62;

  /* ---------- примета ящика ----------

     Полоска у строки и точка в колонке ящиков. Три точки — толщина, при
     которой цвет читается, но полоска не становится элементом раскладки;
     восемь — диаметр точки, на котором различаются соседние тона. */
  --mail-box-w: 3px;
  --mail-box-dot: 8px;
}

/* Цвет ящика приводится к теме там же, где он объявлен.

   Цвет выбирает человек в настройке ящика, и выбирает он его на светлом
   экране: пастельный голубой на белом читается, на подложке тёмной темы
   исчезает. Ровно та же беда была у цветов стадий, и решение оттуда же
   (записка, раздел 72): одна краска в базе, производная — в CSS. На
   светлой притемняем, на тёмной осветляем, смешиваем в oklab — в sRGB
   бирюза по дороге к чёрному уходит в болото.

   Объявлено на тех же узлах, что несут `--box`: скрипт ставит его прямо
   на элемент, и подстановка должна видеть его на своём месте. На `:root`
   это бы не работало — там `--box` ещё нет, и в производную попал бы
   запасной акцент. */
/* `.mail__box-dot` стоит в этом списке наравне со строками, и это не
   избыточность, а починка. Цвет ящика ставится **на самой точке**
   (`dot.style.setProperty('--box', …)` в `mailBoxes.js`), а производная
   считалась только на строке-родителе — там `--box` не задана вовсе, и
   `color-mix` брал запасной `var(--accent)`. Точка наследовала уже
   посчитанный фиолетовый и собственной краски не видела: в колонке все семь
   ящиков горели одним цветом, хотя в базе у каждого свой.

   Объявление обязано стоять там же, где задан `--box`, — иначе производная
   считается раньше, чем появляется то, из чего её считают. */
.mail-row,
.dash-mail__row,
.mail__box,
.mail__box-dot,
.mail__view,
.mail-head {
  --box-ink: color-mix(in oklab, var(--box, var(--accent)) 82%, #000);
}
[data-theme="dark"] .mail-row,
[data-theme="dark"] .dash-mail__row,
[data-theme="dark"] .mail__box,
[data-theme="dark"] .mail__box-dot,
[data-theme="dark"] .mail__view,
[data-theme="dark"] .mail-head {
  --box-ink: color-mix(in oklab, var(--box, var(--accent)) 84%, #fff);
}

/* =========================================================
   Ритм раздела
   ========================================================= */

/* Колонки — из переменных. Только на широком экране: на телефоне колонок
   нет вовсе, и правило `app.css` о единственной колонке стоит выше по
   файлу — то есть безусловное правило отсюда его бы перебило и вернуло
   раздел к трём колонкам на 390 точках. Граница 821 — обратная сторона
   `max-width: 820px` там. */
@media (min-width: 821px) {
  .mail { grid-template-columns: var(--mail-rail-w) minmax(0, 1fr); }
  .mail.has-thread {
    grid-template-columns: var(--mail-rail-w) var(--mail-list-w) minmax(0, 1fr);
  }
  /* Вбок колонка не прокручивается никогда. Пока свёрнутый вид рисовался
     своей разметкой, этого не требовалось; теперь подписи в свёрнутом виде
     остаются на местах и торчат за край — обрезать их должен край, а не
     полоса прокрутки. `overflow-y: auto` из `app.css` сам по себе делает
     `overflow-x` прокручиваемым, и в середине разворота колонка получала
     полосу внизу. На телефоне колонка живёт строкой и прокрутка вбок ей
     нужна — поэтому правило заужено шириной окна. */
  .mail__rail { overflow-x: hidden; }
}

/* Ноутбук и планшет: между телефонной раскладкой и полноразмерной.

   Три колонки по 232 и 306 плюс меню системы съедают 718 точек, и на окне
   в тысячу разговору остаётся 280 — снято глазами: адрес отправителя
   переносится по слогам, имя обрезается на «Иван Кука…», а поле ответа
   уезжает под кнопку «Отправить». То есть раздел в этой полосе не то чтобы
   тесен, а не работает.

   Обе неподвижные колонки ужимаются, подвижная забирает разницу. Ящикам
   180 — в них помещается самая длинная подпись («Подключить ящик»);
   списку 252 — в узкой колонке он и так в два яруса, и лишние полсотни
   точек уходили на воздух справа от темы. Прятать колонку ящиков не стали:
   пропавшая панель — это «где мои ящики?», а узкая — просто узкая. */
@media (min-width: 821px) and (max-width: 1180px) {
  /* Подпись — той же арифметикой от 180: 160 − 18 полей строки − 23 значок
     − 34 число = 85, взято 80. */
  /* Не в «Mail Ru»: там третьей колонки нет вовсе, и ужимать колонку папок
     ради места, которое никому не понадобится, значит резать подписи впустую. */
  .mail.has-thread:not(.mail--dive) {
    --mail-rail-w: 180px; --mail-list-w: 252px; --mail-label-w: 80px;
  }
}

/* Высота строки — минимумом, а не размером: пусть частный лист сжимает
   поля, если ему тесно, рост списка от этого не меняется. */
.mail-row { min-height: var(--mail-row-h); }

/* День над письмами стоит на поле раздела — на той же вертикали, что
   имена под ним. Прижатый к краю колонки, он был единственной строкой,
   выпадающей из общего столбца, и читался как заголовок панели, а не как
   отбивка внутри списка. */
.mail-day { padding-left: calc(var(--mail-gutter) + var(--mail-margin)); }

/* Шапка разговора и строка ответа — как в чате: непрозрачные.

   Стекло здесь не давало ничего, а стоило пересчёта фильтра на каждой
   перерисовке ленты. Под шапкой и над строкой ответа ничего не проезжает:
   лента письма прокручивается в своём окне между ними, а не под ними. Тот
   же разбор — у `.chat__head` в app.css, и вывод обязан быть тот же,
   иначе две шапки в соседних разделах выглядят по-разному без причины. */
/* Содержимое — по середине полосы, а не по её верху. Верхом (`flex-start` в
   app.css) оно стояло, пока шапка была двухъярусной и своей высотой мерялась
   сама: тема с чипами занимали её целиком, и прижимать было нечего. С
   07.09.2026 ярус один (`mail-thread.css`), в нём 38 точек содержимого при
   минимуме в 56 — прижатое к верху висело бы над пустотой в семнадцать
   точек. */
.mail-head {
  align-items: center;
  min-height: var(--mail-head-h);
  padding: 8px var(--mail-pad);
  background: var(--surface);
  backdrop-filter: none;
}
.mail-reply {
  padding: 9px var(--mail-pad) 12px;
  background: var(--surface);
  backdrop-filter: none;
}
.mail-feed { padding: var(--mail-pad); }

/* =========================================================
   Пузырь письма и карточка письма
   ========================================================= */

/* Пузырь — чатовский до значения, вместе с хвостиком у нижнего угла.

   Ни рамки, ни тени: в переписке пузырь отделяет от фона цвет, а край
   вокруг каждого письма превращает ленту в список карточек — а список
   карточек у нас уже есть, это второй вид. Тень тут стояла своя,
   `rgba(0,0,0,.06)`, и снята намеренно: у чатовского пузыря её нет. */
.mail-msg__bubble {
  padding: var(--mail-bubble-pad);
  border-radius: var(--mail-r);
  max-width: var(--mail-bubble-max);
  box-shadow: none;
}
.mail-msg:not(.mail-msg--out) .mail-msg__bubble {
  border-bottom-left-radius: var(--mail-r-tail);
}
.mail-msg--out .mail-msg__bubble {
  border-bottom-right-radius: var(--mail-r-tail);
}

/* Подсветка письма, к которому прыгнули: из находки поиска или из списка тем
   в панели переписки. Та же, что в Флоу.Чате (`.msg.is-flash` в `app.css`) —
   разговор во Флоу один, и «вот оно» обязано выглядеть одинаково.

   Слоем поверх, а не сменой фона самого пузыря: у пузыря свой цвет — у своих
   писем один, у чужих другой, — и подменять его анимацией значит однажды
   разъехаться с темой.

   `position` ставится **только на время подсветки**, тем же приёмом, что у
   `.editable.is-flash`: пузырь и карточка ничем не позиционированы, и слой без
   этого растянулся бы по ближайшему предку с позицией — то есть по всей ленте.

   Плотность — как в чате: текст сквозь блик читается. Иначе на полторы
   секунды человек теряет ровно то, ради чего переходил. */
.mail-msg.is-flash .mail-msg__bubble,
.mail-slot.is-flash .mail-letter { position: relative; z-index: 0; }
.mail-msg.is-flash .mail-msg__bubble::after,
.mail-slot.is-flash .mail-letter::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  pointer-events: none;
  background: color-mix(in oklab, var(--accent), transparent 42%);
  animation: msg-flash 1.6s ease-out forwards;
}
@media (prefers-reduced-motion: reduce) {
  /* Без движения слой обязан **исчезнуть**, а не остаться залитым: у него
     нулевая прозрачность нарисована кадрами, и `animation: none` вернул бы
     его непрозрачным поверх письма навсегда (грабля из раздела 112). */
  .mail-msg.is-flash .mail-msg__bubble::after,
  .mail-slot.is-flash .mail-letter::after { animation: none; opacity: 0; }
}

/* Карточка — тот же пузырь, развёрнутый в документ: скругление то же,
   рамка и тень появляются. Это и есть вся разница между видами,
   сказанная формой: **у реплики края нет, у документа есть.** */
.mail-letter {
  padding: var(--mail-card-pad-y) var(--mail-card-pad-x);
  border-radius: var(--mail-r);
  background: var(--mail-sheet);
  box-shadow: var(--mail-card-shadow);
}

/* ---------- лицо раздела: шапка письма — врезка ----------

   Письмо отличается от реплики тем, что у него есть шапка: кто, кому,
   когда. В mail.ru, Gmail и Spike шапка набрана тем же фоном, что и тело,
   и отделена только воздухом — то есть письмо выглядит как абзац с
   жирной первой строкой.

   Здесь шапка утоплена: она лежит на `--surface-2` во всю ширину
   карточки, тело — на листе. Стопка писем разговора после этого читается
   стопкой документов, а не лентой блоков: глаз ловит чередование
   «полоса — лист», и границы писем видны, даже когда текст в них
   одинаковой длины.

   Полосу видно в обеих темах одинаково: на светлой `--surface-2` темнее
   листа, на тёмной светлее — тона в паре разведены самой палитрой.

   Выключается одной строкой в mail-thread.css, если Роман скажет иначе. */
.mail-letter > .mail-letter__head {
  margin:
    calc(-1 * var(--mail-card-pad-y))
    calc(-1 * var(--mail-card-pad-x))
    var(--mail-card-pad-y);
  padding: var(--mail-card-pad-y) var(--mail-card-pad-x);
  background: var(--mail-sheet-head);
  border-bottom: 1px solid var(--border);
  /* На точку меньше скругления карточки: врезка лежит внутри рамки, и
     равный радиус оставил бы по волоску фона в каждом верхнем углу. */
  border-radius: calc(var(--mail-r) - 1px) calc(var(--mail-r) - 1px) 0 0;
}
/* У свёрнутой строчки шапки нет — там своя раскладка в одну строку, и
   врезка ей ни к чему. Правило на всякий случай: строчка и карточка
   живут в одном узле и меняются местами. */
.mail-letter--folded > .mail-letter__head { margin: 0; }

/* =========================================================
   Чипы: одна форма на все
   ========================================================= */

.mail-chip,
.mail-head__chip,
.mail-row__deal,
.mail-row__client,
.mail-row__count {
  display: inline-flex; align-items: center; gap: var(--mail-chip-gap);
  padding: var(--mail-chip-pad);
  border: 1px solid transparent;
  border-radius: var(--mail-chip-r);
  font-size: var(--mail-chip-fs); line-height: 1.45;
  white-space: nowrap; text-decoration: none;
  max-width: 100%;
}

/* Три тона на одну форму.

   Громкий — то, что сказал человек: номер заказа он сам поставил в тему.
   Тихий — то, что вычислила система: клиента она нашла по адресу
   отправителя, число писем сосчитала сама. Призрачный — то, что не факт,
   а действие («привязать к заказу»). */
.mail-chip--loud,
.mail-head__chip,
.mail-row__deal { background: var(--accent-soft); color: var(--accent-text); }

.mail-chip--quiet,
.mail-row__client,
.mail-row__count { background: var(--surface-3); color: var(--text-faint); }

.mail-chip--ghost,
.mail-head__chip--ghost {
  background: transparent;
  border-color: var(--border-strong);
  color: var(--text-faint);
}
.mail-chip--ghost:hover,
.mail-head__chip--ghost:hover { border-color: var(--accent); color: var(--accent-text); }

/* Чипы внутри строки списка — те же, но с ужатыми полями. */
.mail-row__deal,
.mail-row__client,
.mail-row__count { padding: var(--mail-chip-pad-sm); }

/* Длинное имя клиента обрезается внутри чипа, а не растягивает его: имя
   стоит в правом краю строки письма, и растянуться ему некуда. */
.mail-chip > span,
.mail-row__client > span { overflow: hidden; text-overflow: ellipsis; }

/* =========================================================
   Состояния
   ========================================================= */

.mail-row:hover,
.mail-letter--folded:hover { background: var(--mail-hover); }
.mail-row.is-active { background: var(--mail-active); }

/* Непрочитанное. Имя и тема весом, время акцентом — ровно так это уже
   сделано в карточке почты на рабочем столе (`.dash-mail__row.is-unread`
   в app.css). В самом разделе время оставалось серым, и одно и то же
   письмо на столе и в почте выглядело по-разному. */
.mail-row.is-unread .mail-row__name,
.mail-row.is-unread .mail-row__line b {
  font-weight: var(--mail-unread-w); color: var(--text);
}
/* Тема непрочитанного — тоже весом. Роман 07.09.2026: «для непрочитанных писем
   нужно жирным выделять не только имя контакта но также и тему письма».

   Правило рядом с прежним, а не в нём: там подпись `b` — это тема **внутри**
   строки, где она склеена с зачином (так собран список на рабочем столе). А в
   разделе тема лежит своим узлом, и весом её никто не брал. */
.mail-row.is-unread .mail-row__line {
  font-weight: var(--mail-unread-w); color: var(--text);
}
.mail-row.is-unread .mail-row__time { color: var(--mail-mark); font-weight: 600; }

/* Ряд действий в шапке разговора: ответить, переслать, «ещё».

   Своей коробкой внутри ряда значков, а не тремя кнопками вперемешку с
   остальными: эти три относятся к **письму**, а «вернуть в непрочитанные» — к
   разговору. Волосок между ними об этом и говорит.

   Показывается ряд только в виде «Mail Ru», и прячет его `hidden` из скрипта
   (`headBar` в `mailThread.js`) — так он уходит и из обхода табуляцией.
   Поэтому `:not([hidden])`: без него `display: flex` перебил бы `hidden` и
   спрятанный ряд остался бы на экране. */
.mail-head__acts:not([hidden]) {
  display: flex; gap: 2px;
  margin-right: 5px; padding-right: 6px;
  border-right: 1px solid var(--border);
}

/* Точка непрочитанного — в строке списка и в свёрнутой строчке письма.

   Роман 07.09.2026: «важно помечать непрочитанные точкой и жирным
   выделением». Жир тут был с самого начала, точки не было нигде, и жир один
   отвечает на вопрос «новое?» плохо: имя постоянного отправителя в жирном
   начертании отличается от обычного на глаз, только если рядом стоит
   обычное.

   **Место занято всегда, краска приходит и уходит.** Появляйся узел только у
   непрочитанных, имена в колонке стояли бы на двух вертикалях — а список,
   который просматривают сверху вниз, держится ровно на том, что они на
   одной.

   Правило общее для трёх видов и для обеих раскладок: непрочитанное — это
   свойство письма, а не выбранного вида. Краска та же, что у времени
   непрочитанной строки (`--mail-mark`), — иначе одно письмо помечалось бы в
   одной строке двумя разными цветами. */
.mail-letter__new {
  flex: 0 0 auto;
  width: 7px; height: 7px; border-radius: 50%;
  background: transparent;
}
/* Точка в строке списка вынута из сетки: она стоит в левом поле строки, а не
   отдельной колонкой. Колонок в строке от этого не прибавляется — значит лицо,
   имя и тема остаются ровно там, где стояли, и ни одно из правил, считающих
   колонки (`grid-column` в `mail-list.css`), править не пришлось.

   По вертикали — по середине строки, а не по первой её строчке: строка бывает
   и в один ярус, и в три, а точка помечает письмо целиком. */
.mail-row {
  /* Точке нужна точка отсчёта. Раньше `relative` стоял только у строки с
     полоской ящика (`has-box` в `app.css`), то есть у людей с одним ящиком
     строка не была позиционирована вовсе, и точка отсчиталась бы от дальнего
     предка — от колонки или от всей страницы. */
  position: relative;
}
.mail-row__new {
  position: absolute; left: 10px; top: 50%;
  width: 7px; height: 7px; margin-top: -3.5px; border-radius: 50%;
  background: transparent;
}
/* Красится только в виде «Mail Ru». В двух других список стоит узкой колонкой
   (306 точек, а на ноутбуке 252), и место под точку пришлось бы забрать у
   имени — там непрочитанное и без неё видно: имя весом, время акцентом. */
.mail--dive .mail-row.is-unread .mail-row__new { background: var(--mail-mark); }
.mail-row.is-unread .mail-row__new,
.mail-letter--folded.is-new .mail-letter__new { background: var(--mail-mark); }
/* В строчке письма зазор ряда девять точек — для точки это много: она метка
   при имени, а не сосед ему. Забираем половину обратно. */
.mail-letter__new { margin-right: -4px; }

/* ---------- живая точка: нажимается и подсказывает собой ----------

   Роман 08.09.2026, образец — mail.ru: по точке жмут, чтобы прочитать разговор
   не открывая, и жмут по тому же месту, чтобы вернуть его в непрочитанные. У
   прочитанной строки на этом месте ничего нет, и целиться человеку было бы не
   во что — поэтому под курсором там проступает «призрак»: тот же кружок,
   нарисованный одним контуром.

   **Только в виде «Mail Ru», и это про место, а не про вкус.** Там строке
   отведено поле слева в 28 точек (`mail-dive.css`) — колонка точек, о которой
   Роман и просил. В «Gmail» и «Чате» список стоит узкой колонкой, поля слева у
   строки нет вовсе, и мишень в двадцать точек легла бы поверх аватарки: целясь
   в лицо отправителя, человек прочитывал бы разговор. Пометить там
   прочитанным по-прежнему можно правой кнопкой — пункт один и тот же.

   **Мишень крупнее самой точки.** Семь точек — это меньше половины пальца и
   вдвое меньше привычной мишени указателя; попасть в такую с первого раза
   нельзя. Поэтому узел становится прозрачным кружком в двадцать одну точку, а
   видимая точка переезжает в `::after` — по центру мишени, то есть ровно
   туда, где стояла (10 + 3,5 = 13,5 от края строки).

   Оттого же и `background: transparent` первым правилом: выше стоят два
   правила, красящие сам узел, и без снятия краски у непрочитанной строки
   загорелся бы весь кружок мишени. Снимаем **у живой точки**, а не у всех:
   в двух других видах и у черновика точка осталась прежней — она там просто
   метка. */
.mail--dive .mail__rows .mail-row__new.is-live {
  left: 3px; width: 21px; height: 21px; margin-top: -10.5px;
  background: transparent;
  cursor: pointer;
}
.mail--dive .mail__rows .mail-row__new.is-live::after {
  content: ''; position: absolute; left: 50%; top: 50%;
  width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px;
  border-radius: 50%; background: transparent;
  /* Переход тот же, что у фона строки: точка гаснет вместе с жирностью, а не
     отдельно от неё. */
  transition: background var(--speed), box-shadow var(--speed);
}
.mail--dive .mail__rows .mail-row.is-unread .mail-row__new.is-live::after {
  background: var(--mail-mark);
}
/* Призрак: контур вместо заливки и вполсилы. Не бледная заливка — залитый
   кружок читался бы как «непрочитано», то есть врал бы о состоянии письма
   ровно в тот миг, когда человек в него целится. Контур же ни на что в
   системе не похож и говорит одно: «здесь появится точка». */
.mail--dive .mail__rows .mail-row:not(.is-unread) .mail-row__new.is-live:hover::after {
  box-shadow: inset 0 0 0 1.5px var(--mail-mark);
  opacity: .55;
}
/* А у непрочитанной под курсором точка, наоборот, слегка приглушается — тот же
   способ сказать «это нажимается», каким отвечают все мишени раздела. */
.mail--dive .mail__rows .mail-row.is-unread .mail-row__new.is-live:hover::after {
  opacity: .6;
}
@media (prefers-reduced-motion: reduce) {
  .mail--dive .mail__rows .mail-row__new.is-live::after { transition: none; }
}

/* Приглушённое: бумага, которая осталась в почте и приедет по нажатию.
   Не спрятана и не помечена значком «скачать» — просто тише соседей. */
.mail-file--remote,
.mail-att.is-remote { opacity: var(--mail-dim); }
.mail-file--remote:hover,
.mail-att.is-remote:hover { opacity: 1; }

/* Рамка фокуса — общая для всего, по чему ходят с клавиатуры. По списку
   писем ходят стрелками, и рамка обязана доставаться самой строке. */
.mail-row:focus-visible,
.mail-letter__row:focus-visible,
.mail-chip:focus-visible,
.mail-head__chip:focus-visible {
  outline: none; box-shadow: var(--ring);
}

/* =========================================================
   Примета ящика
   ========================================================= */

/* Полоска у строки. Цвет — производный от выбранного человеком, чтобы он
   читался в обеих темах; ширина — из переменной, чтобы полоска на столе и
   полоска в разделе не разъезжались. */
.mail-row.has-box::before {
  width: var(--mail-box-w);
  background: var(--box-ink);
}
.dash-mail__box { background: var(--box-ink); }
/* Точка ящика в колонке. Красится **производной** `--box-ink`, а не сырым
   цветом из карточки: пользовательский оттенок на тёмной теме гаснет.

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

   Исключение для «Всех ящиков»: у них свой конический градиент из всех
   красок сразу, и общий фон затёр бы его — этот лист подключён позже. */
.mail__box-dot {
  width: var(--mail-box-dot); height: var(--mail-box-dot);
}
.mail__box-dot:not(.mail__box-dot--all) { background: var(--box-ink); }

/* Тот же цвет в шапке разговора — тонкой чертой по нижнему краю.

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

   Черта появляется, только когда ящиков больше одного: класс `has-box`
   ставит скрипт, как и у строки. У единственного ящика она отвечала бы на
   вопрос, которого никто не задаёт. */
.mail__view.has-box .mail-head,
.mail-head.has-box {
  border-bottom-color: transparent;
  box-shadow: inset 0 -2px 0 var(--box-ink);
}

/* =========================================================
   Пустые состояния и заставка
   ========================================================= */

/* Пусто в списке — не ошибка и не пропажа, и говорить об этом надо тем же
   голосом, каким система говорит о пустом везде: `.empty` из app.css,
   строка по центру. Своего вида здесь заводить нечего — меняется только
   ритм: поля от полей раздела, кегль от кегля списка, чтобы строка не
   выглядела крупнее писем, которых нет.

   Правило нарочно заужено до самого раздела: тем же классом набраны
   «Подписей пока нет» в окне подписей и заглушки в других местах системы,
   и трогать их отсюда нельзя. */
.mail .empty {
  padding: 28px var(--mail-pad);
  font-size: 13px;
  line-height: 1.5;
}

/* Подсказка в разговоре — плашка на поле разговора, не на всю ширину
   колонки: она стоит среди писем и обязана подчиняться их полям. */
.mail-hint { border-radius: var(--mail-r); }

/* Заставка раздела. Величины у неё чатовские до значения, и это
   проверяется глазами, а не читается: два одинаковых локапа разного кегля
   рядом в меню читаются как небрежность. Сведены в переменные, чтобы
   правка кегля в одном месте не забыла второе. */
.mail-hero__word { font-size: 34px; letter-spacing: -.02em; line-height: 1; }
.mail-hero__dot { width: 7px; height: 7px; }
.mail-hero__sub { font-size: 12.5px; }


/* ---------- во весь экран ----------

   Убираются обе боковые колонки: меню системы прячет само себя (свой класс
   `is-collapsed`), а колонку папок прячем здесь. Список разговоров остаётся:
   без него из «во весь экран» некуда идти, кроме как обратно.

   Прячем шириной, а не `display: none`, — колонка уезжает, а не пропадает
   рывком. Ширина меняется одна: содержимое внутри не пересчитывается, и
   переход остаётся плавным на слабой машине. */
.mail__rail {
  transition: width 200ms ease, padding 200ms ease;
}

/* ---------- живая колонка ----------

   Просьба Романа 27.08.2026: «чтобы выглядело свежо, не убого». Свежесть
   здесь делается не пестротой, а тем, что выбранное **видно сразу**: у
   активной строки мягкий градиент по акценту и своя полоска слева. Цвет
   один, системный; второго акцента в системе нет и заводить его в почте
   значило бы получить два разных «выбрано» в одном окне. */
.mail__filter.is-active, .mail__box.is-active {
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--accent) 22%, transparent) 0%,
    color-mix(in srgb, var(--accent) 9%, transparent) 100%);
  position: relative;
}
.mail__filter.is-active::before, .mail__box.is-active::before {
  content: ''; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 3px; height: 60%; border-radius: 0 3px 3px 0; background: var(--accent);
}
/* Значок выбранной строки чуть ярче слова: он и есть та опора, по которой
   глаз находит место в колонке. */
.mail__filter.is-active .icon, .mail__box.is-active .icon { opacity: 1; }
.mail__filter .icon { opacity: .75; }

/* Кнопка «Написать» — единственное место в колонке, где градиент уместен:
   она одна такая, и спорить ей не с чем.

   В свёрнутой колонке кнопка — квадрат, и квадрат получается **сужением**, а
   не утолщением. Первым заходом я нарастил высоту с 36 до 45 под ширину,
   которую даёт колонка, и Роман поправил: «я не просил увеличить толщину, я
   просил её уменьшить».

   Он прав и по существу: кнопка стоит над рогаткой папок, и её высота задаёт
   ритм всей колонки. Раздутая на треть, она спорит со строками под собой.
   Поэтому сторона квадрата — собственная высота кнопки, а свёрнутая ширина к
   ней подтягивается; колонке остаётся поле по бокам, и кнопка встаёт по её
   середине.

   Высота при этом одна в обоих состояниях: меняется только ширина, а её и так
   везёт переход колонки — во время свёртки кнопка не толстеет и не худеет. */
.mail__write {
  --write-side: 36px;
  min-height: var(--write-side);
  /* **Своей ширины у кнопки нет ни в одном состоянии — только колонки.**
     Это и есть та правка, о которой Роман сказал 21.09.2026: «анимация
     закрытия этого меню разная для папок и фильтров и для кнопки сверху. это
     очень режет глаз».

     Здесь стоял переход по ширине — из `100%` в 36 точек. Написан он был
     ради предыдущей жалобы (01.09.2026, «кнопка опережает сворачивание»), и
     обгон он не вылечил, а спрятал: проценты браузер считает от полосы
     колонки, а она в этот самый миг едет. Кнопка получалась произведением
     двух движений и обгоняла колонку вдвое — замерено стендом
     `check_mail_fold.py`: в середине свёртки кнопка занимала 0,59 полосы
     вместо единицы, а в конце вылезала за неё на 9% (36 точек в полосе 33).

     Теперь ширина одна на оба состояния — `100%`, — и меняет её только сетка
     раздела. Кнопка едет ровно тем же движением, что строки папок: у них
     своей ширины тоже нет. Анимировать здесь больше нечего, и разъехаться
     нечему.

     Квадрат в свёрнутой колонке от этого никуда не делся: полоса колонки там
     33 точки при высоте кнопки 36 — тот же квадрат со скруглением, только
     считается он из полей колонки, а не задаётся числом. Телефон правило
     перебивает своим (`mail-phone.css`, круглая кнопка в 52 точки). */
  width: 100%;
  background-image: linear-gradient(135deg,
    color-mix(in srgb, var(--accent) 88%, white) 0%, var(--accent) 55%,
    color-mix(in srgb, var(--accent) 88%, black) 100%);
  transition: transform 140ms ease, box-shadow 140ms ease,
              column-gap var(--mail-fold, 220ms) var(--ease, ease);
}
.mail__write:hover:not(:disabled) {
  transform: translateY(-1px);
  box-shadow: 0 6px 18px color-mix(in srgb, var(--accent) 35%, transparent);
}
.mail__write:active:not(:disabled) { transform: translateY(0); }

@media (prefers-reduced-motion: reduce) {
  .mail__rail, .mail__write, .mail-msg__wait-cancel { transition: none; }
  .mail__write:hover:not(:disabled) { transform: none; }
  .mail__rail-fold, .mail__rail-fold .icon { transition: none; }
  .mail__rail-fold:hover .icon { transform: none; }
  .mail-rail-off .mail__rail-fold:hover .icon { transform: rotate(180deg); }
  /* Подписи не едут и не тают: пропадают вместе с шириной колонки. Ширину
     кнопке «Написать» тоже не анимируем — она у неё меняется, а не гаснет. */
  .mail__label, .mail-rail-off .mail__label,
  .mail__write .mail__label { transition: none; }
  .mail-rail-off .mail__label { transform: none; }
}


/* ---------- свёрнутая колонка почты ----------

   Прячется не целиком: остаётся полоска с той же кнопкой, которой её
   свернули. Кнопка, прячущая сама себя, оставляет человека без способа
   вернуть спрятанное — и он ищет его перезагрузкой страницы. */
.mail__rail-fold {
  align-self: flex-end; display: grid; place-items: center;
  width: 26px; height: 26px; margin: -2px 0 2px; padding: 0;
  border: 0; border-radius: 8px; background: transparent;
  color: var(--text-faint); cursor: pointer;
  transition: background var(--speed), color var(--speed);
}
.mail__rail-fold:hover { background: var(--surface-3); color: var(--text); }

/* Ширину колонки задаёт **сетка раздела**, а не сама колонка: ужать колонку
   мало — место на её месте осталось бы пустым, потому что дорожка сетки
   считается по `--mail-rail-w`. Меняем переменную, и разницу забирает список
   писем с разговором, как и просил Роман: «вместе со скрытием должно
   расширяться поле справа».

   **Длительность свёртки — одна на все её части, и живёт она переменной.**
   Роман 01.09.2026: «кнопка нового письма опережает сворачивание других
   пунктов меню — сделай синхронно». Опережала она потому, что её ширины в
   переходе не было вовсе: колонка ехала свои 220 мс, а кнопка схлопывалась в
   квадрат первым же кадром. Числа, стоявшие в трёх местах порознь (220 у
   сетки, 200 у подписи кнопки, 200 у зазора), разъезжались бы и дальше —
   поэтому оно здесь одно, а остальные его наследуют. */
.mail { --mail-fold: 220ms; transition: grid-template-columns var(--mail-fold) var(--ease, ease); }

/* Свёрнутая колонка **остаётся рабочей**: папки, фильтры и ящики стоят в ней
   значками, как в свёрнутом меню системы (просьба Романа 27.08.2026: «в
   сайдменю иконки не отображаются»).

   **Разметка одна на оба состояния, свёртка делается стилями.** Это главная
   правка 28.08.2026 и прямое указание Романа: «по анимации сайдменю —
   посмотри как это реализовано в основном меню Флоу — вот то же самое
   реализуй».

   До неё у колонки было два набора разметки: развёрнутый и свой свёрнутый
   (`renderRailFolded` из значков `.mail__mini`). Оттуда росли все три жалобы
   разом. Кнопка «Написать» в свёрнутом виде оставалась кнопкой с подписью, и
   подпись из неё вылезала. «Подключить ящик» и «Подписи» в свёрнутом виде не
   рисовались вовсе, а при развороте появлялись рывком. И самое главное:
   переход шёл не от одного вида к другому, а от одних узлов к другим —
   анимировать здесь нечего в принципе, узел заменился, начальное состояние
   потеряно.

   Теперь узлы те же, а свёртка — ровно две вещи, как в меню системы: уходит
   ширина колонки (её ведёт сетка раздела) и гаснут подписи, уезжая влево на
   десять точек. Значки не двигаются вовсе — ни по вертикали, ни по
   горизонтали, — и никаких пустых строк «на месте заголовка» для этого
   больше не нужно: заголовок стоит на своём месте сам, просто без слова.

   **Ширина 53, а не круглые 52.** Значок стоит на 26,5 от края колонки
   (10 полей колонки + 9 полей строки + половина значка в 15), и колонка ровно
   вдвое шире этого числа — только тогда столбец значков стоит по середине
   полосы. Число здесь считается из полей, а не подбирается на глаз: поправят
   поля строки — центр уедет, и это будет видно сразу. */
/* Второй селектор — не для красоты. Ноутбучная полоса (821–1180) ужимает
   колонку своим правилом `.mail.has-thread:not(.mail--dive)`, и оно весит
   три класса против двух — то есть перебивало свёртку насовсем. На таком
   окне с открытым разговором колонка не сворачивалась вообще: подписи
   гасли, кнопка схлопывалась в квадрат, а полоса оставалась 180 точек —
   поймано стендом `check_mail_fold.py` 21.09.2026, до него это не всплывало,
   потому что оба прежних захода смотрели на широкое окно. */
.mail-rail-off .mail,
.mail-rail-off .mail.has-thread:not(.mail--dive) { --mail-rail-w: 53px; }

/* Подпись строки. Всё как у `.nav__label` в меню системы и по той же причине:
   ширина не анимируется (`flex: 0 0 auto` плюс потолок числом), иначе браузер
   пересчитывает многоточие каждый кадр.

   Возврат с оттяжкой в 90 мс и мягче ухода: сперва едет край колонки, потом
   на освободившееся место встают слова. Уход быстрый и без задержки — колонке
   незачем ждать слово, которое она и так вот-вот срежет краем. */
.mail__label {
  flex: 0 0 auto; max-width: var(--mail-label-w);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  transition: opacity 200ms var(--ease, ease) 90ms,
              transform 280ms var(--spring, ease) 90ms;
}
.mail-rail-off .mail__label {
  opacity: 0; transform: translateX(-10px); pointer-events: none;
  transition: opacity 110ms linear, transform 150ms var(--ease, ease);
}

/* Ящику с ошибкой подпись короче: рядом с ней в строке стоит ещё и
   восклицательный знак, и без этой уступки он вылезал бы за край строки
   вместе с числом непрочитанного. */
.mail__box.has-error .mail__label { max-width: calc(var(--mail-label-w) - 24px); }

/* Строка обрезает себя сама, в обоих состояниях.

   Без этого подписи, которые в свёрнутом виде вчетверо шире колонки, лезли бы
   на соседей в те кадры, пока колонка ещё не сузилась, а `scrollWidth` самой
   колонки говорил бы про 170 точек содержимого в полосе шириной 53. Обрезка
   по строке, а не по колонке, — единственное, что делает переполнение
   измеримым нулём в любой момент перехода. */
.mail__filter, .mail__box, .mail__add { overflow: hidden; position: relative; }

/* Кружок ящика занимает место значка, а не своё.

   Кружок 8 точек, значок 15 — и до этой правки подписи ящиков стояли на семь
   точек левее подписей папок, а в свёрнутой колонке на те же семь разъезжался
   столбец. Поля по 3,5 дают кружку ширину значка: и подписи встают на одну
   вертикаль, и центр кружка совпадает с центром значка. */
.mail__box > .mail__box-dot { margin-left: 3.5px; margin-right: 3.5px; }

/* «Написать» — тот же узел в обоих состояниях (Роман: «текста вылезают в
   кнопке „написать“»). Подпись здесь не гаснет на месте, а схлопывается по
   ширине вместе с зазором, и это единственное отступление от правила «ширину
   подписи не трогаем». Причина в том, что кнопка выравнивает содержимое по
   середине: подпись, оставшаяся в потоке нулевой прозрачности, утащила бы
   значок влево за край колонки. Платить за это нечем — многоточия у кнопки
   нет вовсе (`white-space: nowrap` у `.btn`), пересчитывать браузеру нечего.

   Ширина у кнопки остаётся во всю колонку, поля и рамка — свои. Значок при
   этом встаёт ровно на ту же вертикаль, что и значки строк: содержимое
   центруется, а середина у кнопки и у колонки одна. */
.mail__write .mail__label {
  max-width: 90px;
  /* Ширина схлопывается тем же временем, что едет колонка: слово,
     схлопнувшееся раньше края кнопки, — это тот же обгон, только внутри неё.

     **Прозрачность — двумя правилами, как у подписей строк**, и это вторая
     половина правки 21.09.2026. Здесь стояло одно быстрое `110ms linear` на
     оба конца, и на развороте слово на кнопке просыпалось за пять кадров до
     слов под ней: у тех возврат с оттяжкой в 90 мс (сперва едет край
     колонки, потом встают слова), а у этого оттяжки не было вовсе. Поймано
     стендом `check_mail_fold.py`. Уход остаётся быстрым — он ниже, в правиле
     свёрнутой колонки. */
  transition: max-width var(--mail-fold, 220ms) var(--ease, ease),
              opacity 200ms var(--ease, ease) 90ms;
}
/* Кнопка обрезает себя сама, как и строки. Ловится это только замером и
   только в один кадр: в тот миг, когда колонку разворачивают, класс свёртки
   снимается разом, а ширина колонки ещё узкая — подпись возвращается на своё
   место целиком и на 22 точки вылезает за край колонки. Нарисовать её край
   колонки не даёт, но `scrollWidth` про эти 22 точки честно говорит, а в
   свёрнутой колонке переполнения быть не должно вовсе — ни видимого, ни
   измеримого. */
.mail__write { overflow: hidden; }
/* В свёрнутой колонке у кнопки меняется ровно одно — зазор между значком и
   схлопнувшейся подписью. Ширину не трогаем вовсе: она у кнопки от колонки,
   как и у строк (разбор выше, у `.mail__write`). */
.mail-rail-off .mail__write { column-gap: 0; }
.mail-rail-off .mail__write .mail__label {
  max-width: 0; opacity: 0; transform: none;
  /* Уход — быстрый и без задержки, ровно как у подписей строк: слово должно
     погаснуть раньше, чем его срежет край, а не смазаться вместе с ним. */
  transition: max-width var(--mail-fold, 220ms) var(--ease, ease),
              opacity 110ms linear;
}

/* Уголок у «Всех ящиков» — тот самый выключатель списка (разбор в
   `buildRail`). В свёрнутой колонке его нет: места под него в полосе шириной
   53 точки не остаётся, а строка там и без него делает оба дела нажатием.

   `margin-left: auto` прижимает его к правому краю строки, `flex: 0 0 auto` —
   чтобы длинная подпись соседнего ящика не сжимала его в полоску. */
.mail__box-fold {
  margin-left: auto; flex: 0 0 auto;
  display: grid; place-items: center; width: 18px; height: 18px;
  border-radius: 6px; color: var(--text-faint);
  transition: color 140ms ease, background-color 140ms ease;
}
.mail__box-fold:hover { color: var(--text); background: var(--surface-3); }
.mail-rail-off .mail__box-fold { display: none; }

/* Граница между фильтрами и ящиками. Раньше её рисовал заголовок «ЯЩИКИ», а
   в свёрнутой колонке — короткая черта на его месте; 03.09.2026 Роман прислал
   снимок этой черты со стрелкой: «совсем непонятный UX». Заголовка больше
   нет, а граница нужна — ящики отличаются от фильтров кружком вместо значка,
   и в столбце из одних значков она читается хуже прочих. Теперь это поле с
   чертой над группой: то же самое, но без подписи, которую нечем объяснить. */
.mail__group--boxes {
  margin-top: 6px; padding-top: 8px; border-top: 1px solid var(--border);
}

/* Кнопка свёртки — квадрат тридцать шесть на тридцать шесть и фиолетовая.

   **Квадрат, потому что раньше он им не был.** Размер стоял, а `flex` — нет,
   и в узкой раскладке, где колонка становится строкой, кнопку сжимало
   соседями до девятнадцати пикселей по ширине при тридцати трёх по высоте.
   Роман: «иногда кнопка сворачивания становится не квадратной». Теперь у неё
   `flex: 0 0 auto` — сжимать её больше нечем, ни в строке, ни в столбце.
   В свёрнутой колонке квадрат становится 33 на 33 — по ширине, которая
   остаётся внутри полей узкой колонки; почему именно столько, написано ниже
   у самого правила. Высоту она ни с кем не делит: стоит под всеми и ничьих
   строк не двигает.

   **Фиолетовая, потому что её ищут.** Серая обведённая кнопка внизу колонки
   читалась как служебная и терялась; это единственное, чем колонку возвращают,
   и она должна быть видна с первого взгляда. В покое — мягкая заливка акцентом,
   под курсором — сплошной акцент с тенью того же цвета.

   **Движение.** Поворот на пол-оборота остался — он и говорит, что произошло,
   — но идёт пружиной, а не ровным замедлением: стрелка встаёт на место, а не
   доезжает. Под курсором она подаётся на два пикселя в ту сторону, куда уедет
   колонка, — обещание раньше нажатия. Нажатие вдавливает саму кнопку. */
.mail__rail-fold {
  align-self: flex-start; flex: 0 0 auto;
  /* **Липкая, а не просто последняя в столбце.** Прежде кнопка стояла в
     потоке под ящиками, и стоило колонке не поместиться в окно — при зуме
     браузера или на коротком экране, — как она уезжала за нижний край и
     колонку становилось нечем вернуть (Роман 30.08.2026, со снимком).
     `sticky` оставляет её в потоке — своё место под ящиками она сохраняет,
     — но не даёт уйти ниже видимой части: длинный список едет под ней.

     `bottom` равен нижнему полю колонки (12), а не нулю: тогда всплывшая
     кнопка стоит ровно там же, где лежит в покое, и в конце прокрутки не
     дёргается на высоту поля.

     Размер один на оба состояния — 32. Прежде было 36, а свёрнутой колонке
     отдельно 32: столько остаётся внутри её полей (53 ширины минус 20 полей
     минус линия справа). Разные числа означали скачок на самой перемене, а
     36 в свёрнутом виде вылезало бы за поля — край срезал бы скругления.
     Одно число снимает и то, и другое. */
  position: sticky; bottom: 12px; z-index: 2;
  width: 32px; max-width: 100%; height: 32px; border-radius: 10px;
  /* Заливка непрозрачная: под всплывшей кнопкой едут строки ящиков, и
     полупрозрачный акцент показывал бы их сквозь себя. Цвет тот же —
     акцент, домешанный не в пустоту, а в подложку колонки. */
  border: 0; background: color-mix(in srgb, var(--accent) 15%, var(--surface-2));
  color: var(--accent-text);
  /* Тень — только чтобы отделить слой, не чтобы её заметили. */
  box-shadow: 0 2px 10px color-mix(in srgb, #000 22%, transparent);
  transition: background-color 140ms ease, color 140ms ease,
              box-shadow 160ms ease, transform 140ms var(--spring, ease);
}
.mail__rail-fold:hover {
  background: var(--accent); color: var(--on-accent); border-color: transparent;
  /* Обе тени разом: своя — акцентом, и та же отделяющая, что в покое. */
  box-shadow: 0 4px 14px color-mix(in srgb, var(--accent) 34%, transparent),
              0 2px 10px color-mix(in srgb, #000 22%, transparent);
}
.mail__rail-fold:active { transform: scale(.93); transition-duration: 80ms; }
.mail__rail-fold:focus-visible { outline: none; box-shadow: var(--ring); }
.mail__rail-fold .icon {
  transition: transform 280ms var(--spring, ease);
}
.mail__rail-fold:hover .icon { transform: translateX(-2px); }
/* **Свёрнутая колонка кнопку не двигает.** Раньше она вставала по центру
   узкой полосы, и на самой перемене это читалось скачком вправо: колонка
   ещё едет, а кнопка уже улетела на её будущий край (Роман 30.08.2026:
   «кнопка прыгает на правый край этого меню — получается скачок. она может
   не двигаться?»). Левый край колонки при свёртке стоит на месте — уезжает
   только правый, — поэтому кнопке хватает остаться у левого поля: экранная
   точка у неё не меняется вовсе.

   Поворот стрелки и подсветка при этом остались: спорным было положение, а
   не то, чем кнопка отвечает на нажатие. */
.mail-rail-off .mail__rail-fold .icon { transform: rotate(180deg); }
.mail-rail-off .mail__rail-fold:hover .icon { transform: rotate(180deg) translateX(-2px); }

/* Кнопка свёртки живёт у нижнего края колонки — под ящиками, как просил
   Роман. Отступ сверху отвечает за короткую колонку: пока ящики помещаются,
   кнопка прижата к низу, а не висит сразу за списком посреди пустоты.
   За длинную отвечает `sticky` выше — вместе они держат её у нижнего края
   в обоих случаях. Раньше здесь стояло только это правило, и оговорка
   «абсолютным положением кнопка наезжала бы на ящики» была верна: наезд
   стал допустимым намеренно, потому что кнопка, ушедшая за край, хуже
   кнопки, накрывшей строку. */
.mail__rail-fold { margin-top: auto; }

/* Свёрнутый список: на виду остаётся одна строка «Все ящики» — она же и
   выключатель. */
.mail__group.is-folded > *:not(.mail__box--all) { display: none; }

/* Длинное слово в строке колонки обрезаем многоточием, как у ящиков: колонка
   узкая, и переносящаяся строка ломала бы шаг всего списка. */
.mail__filter span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Числа в колонке — «где меня ждут». Просьба Романа 28.08.2026: «можешь
   грамотно подсвечивать уведомления по разделам в сайдменю Флоу.Почты? чтобы
   было понятно, какие разделы открывать».

   **Их два вида, и это главное в оформлении.** Громкое — подложкой акцента:
   непрочитанное во «Входящих», оно же у ящика и у фильтра «Новые». Это
   требование: там лежит письмо, которого человек не видел. Приглушённое —
   просто число серым: «Рассылки» и «Черновики». Это справка: рассылку читают,
   когда сами за ней приходят, а черновик пишет сам человек, и торопить его
   своим же недописанным письмом незачем.

   Разводить их обязательно. Ряд одинаковых красных кружков читается как один
   сплошной шум, и через неделю перестают замечать и тот, что вправду важен, —
   ровно поэтому же в разделе нет и деления ленты на «важное» и «прочее»
   (раздел 96).

   Ширина у громкого числа не фиксирована, а держится полями: «1» и «99+»
   должны читаться одинаково, а не растягивать строку. */
.mail__mark {
  margin-left: auto; flex: 0 0 auto;
  font-style: normal; font-variant-numeric: tabular-nums; line-height: 1;
}
.mail__mark--calm {
  padding-left: 6px; font-size: 11px; color: var(--text-faint);
}
.mail__filter.is-active .mail__mark--calm { color: inherit; opacity: .8; }
.mail__mark--loud {
  padding: 0 6px; min-width: 20px; height: 18px;
  display: inline-grid; place-items: center;
  border-radius: 20px; background: var(--accent); color: #fff;
  font-size: 11px; font-weight: 700;
  /* Отрицательные поля по вертикали — чтобы строка от значка не выросла.
     Кружок высотой 18 при строке в 32,84 её не трогает, но `line-height`
     ставит вокруг него свои полповысоты; та же грабля, что ловилась на
     счётчике писем в строке разговора (раздел 101). */
  margin-top: -1px; margin-bottom: -1px;
}

/* В свёрнутой колонке число ставится **поверх** значка: подписи там нет, а
   раздвинуть строку нельзя — вся ценность свёрнутого вида в том, что значки
   стоят на тех же высотах, что строки развёрнутого. Поэтому абсолютом, как
   это сделано у счётчика в свёрнутом меню системы (`.nav__badge`).

   Приглушённые числа в свёрнутом виде не показываем вовсе: рядом со значком
   серая цифра читается как часть значка. Раньше это решал скрипт (`mini` в
   `markNode`), теперь — лист: разметка одна, и второго набора классов ради
   свёртки заводить не надо.

   Переход снят намеренно, и в меню системы у `.nav__badge` он снят тоже.
   Число перескакивает от края строки на значок в один кадр — анимировать
   такой перелёт значило бы вести кружок через всю ширину колонки поверх
   подписей, которые в этот момент гаснут. */
.mail-rail-off .mail__mark--calm { display: none; }
.mail-rail-off .mail__mark--loud {
  position: absolute; top: 0; right: 1px; margin: 0;
  min-width: 16px; height: 15px; padding: 0 4px;
  font-size: 10px; box-shadow: 0 0 0 2px var(--surface-2);
  transition: none;
}

@media (prefers-reduced-motion: reduce) {
  .mail { transition: none; }
}


/* ---------- заглушка раздела ----------

   Колонка папок в живом разделе стоит на приглушённом фоне, и серые блоки
   заглушки на нём почти не видны: контраст между `--surface-2` и краской
   блока меньше, чем глаз замечает на движущемся блике. На время заглушки
   колонка встаёт на общий фон — блоки на нём читаются так же, как в списке.

   Пузырей у заглушки больше нет вовсе, и правило, красившее их нейтральным,
   ушло вместе с ними (Роман 29.08.2026: «уж лучше как в Флоу.Чат сделать
   просто проявление писем и пузырей»). От панели разговора остаётся рамка —
   шапка сверху, строка ответа снизу, между ними пусто: обе стоят на своих
   местах и в живом разговоре с первого мига, а лента между ними проявляется
   готовой. Разбор — у `.mail-feed.is-swapping` в `mail-thread.css`. */
.mail--skel .mail__rail { background: var(--surface); }


/* =========================================================
   Открытие разговора
   ========================================================= */

/* Роман 28.08.2026: «при открытии треда из любого режима почты (чатовый,
   список) — прошу добавить приятную анимацию: сейчас окно просмотра треда
   просто резко появляется и смещает широкий список писем влево. придумай,
   как это обыграть красиво и smooth».

   Что было до правки — не на глаз, а замером наблюдателем по разметке:
   между нажатием и переходом ровно **один кадр**. Список 1028 → 306,
   панель `display: none` → `flex`, и справа встаёт пустой прямоугольник,
   в котором ещё через полтораста миллисекунд проявляется письмо. За один
   кадр меняются две трети экрана, и ни одна из них не говорит, откуда и
   куда она поехала.

   ---------------------------------------------------------------------
   Почему не переходом по самой сетке

   Соблазн очевиден: у раздела уже есть `transition: grid-template-columns`
   — им плавно уезжает колонка ящиков, и хочется тем же ходом увести список.
   Не выйдет, и не по недосмотру. Свёртка колонки меняет **число** в первой
   дорожке (232 → 53), дорожек как было три, так и осталось; открытие
   разговора меняет сетку с двух дорожек на три, а `minmax(0, 1fr)` во
   второй превращается в 306 точек. Разное число дорожек браузер ведёт
   скачком; долю против точек — тоже скачком. То есть переход там объявлен,
   но не проигрывается ни разу, и скачок из жалобы Романа — это он.

   Подогнать число дорожек можно, дописав третью нулевой. Долю к точкам —
   нельзя ничем. А если бы и вышло, раскладку в полторы тысячи точек с
   тремя сотнями строк пришлось бы перекладывать каждый кадр. Поэтому
   раскладка меняется как менялась, в один кадр, а плавность делают
   `transform` и `opacity`: они идут мимо раскладки, их считает видеокарта,
   и длина списка на них не влияет.

   ---------------------------------------------------------------------
   Что человек видит вместо скачка

   **Панель въезжает справа**, проявляясь, — на два поля раздела. Она
   оттуда и пришла, из-за края окна, и въезд отвечает на вопрос «откуда это
   взялось» раньше, чем вопрос успевает возникнуть. Тридцать две точки, а не
   пол-окна: это не переход между экранами, а появление соседа, и уезжающая
   через треть экрана панель читалась бы как смена раздела. Одного поля
   (шестнадцати точек) при этом мало — снято глазами и покадрово: за 220 мс
   такой путь глаз не ловит вовсе, и остаётся одно проявление.

   **Список не трогаем вовсе** — и это правка по живому отзыву.

   Сперва он проявлялся заново, от трети к единице: строка в узкой колонке
   собрана иначе, чем в широкой (`mail-list.css` — имя встаёт над темой, чипы
   уходят к имени, тема обрезается многоточием), и затухание пряталo тот кадр,
   в котором форма подменяется.

   Роман 28.08.2026, посмотрев живьём: «анимация открытий просмотра писем —
   кайф. но мигает список писем при открытии… там вроде ширина цветных полос
   меняется на пару пикселей ещё».

   Обе жалобы — про одно. Затухание вводит колонку в **отдельный слой**: пока
   прозрачность не равна единице, браузер рисует список отдельно и потом
   накладывает. Отсюда и мигание, и та самая пара точек — примета ящика у
   строки шириной ровно три точки (`--mail-box-w`), а тонкая линия в
   отдельном слое округляется не так, как на общем полотне. В разметке она не
   меняется ни на волос; меняется способ, которым её рисуют.

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

   Проявление — той же длительностью и той же манерой, что затухание между
   разговорами (`mail-thread.css`), между вкладками списка (`mail-list.css`)
   и в ленте чата (`app.css`): 150 мс. Четвёртое движение в одной системе не
   имеет права идти иначе трёх остальных.

   ---------------------------------------------------------------------
   Чего это стоит

   Ничего, и это замерено, а не предположено. Длины кадров считаны самой
   страницей (`requestAnimationFrame`) на списке в 440 строк: в покое 16,7 мс,
   во время перехода — те же 16,7 мс, кроме двух кадров в начале (50 и 67 мс).
   Эти два — перекладка четырёхсот строк из широкой формы в узкую, и они
   ровно те же, если движение выключить совсем (`prefers-reduced-motion`).
   То есть добавленного расхода нет: прозрачность и сдвиг раскладку не
   трогают, ими занят композитор.

   ---------------------------------------------------------------------
   Почему ключевыми кадрами, а не переходом

   Переходу нужно прежнее значение, а прежнего здесь нет вовсе: до открытия
   панель `display: none`, и ни прозрачности, ни сдвига, от которых идти, у
   неё не существует. Ровно эта грабля описана в разделе 101 про занавес
   списка. Ключевым кадрам прежнее значение не нужно — они идут от того
   мига, когда правило начало применяться.

   Отсюда же и главное их свойство: **проигрываются только на открытии.**
   Узлы панели и списка живут всю жизнь раздела, класс `has-thread` висит
   на оболочке — пока он не менялся, не меняется и то, какие правила
   применяются. Перерисовки разговора (ответили, приехало письмо, открыли
   панель переписки) и перерисовки списка (пачка, приехавшая на прокрутке)
   ключевых кадров не запускают. Переход между двумя разговорами — тоже:
   класс стоит как стоял, и затухание там своё, в `mail-thread.css`.

   Имена у двух проявлений разные не для красоты: ключевые кадры с одним
   именем браузер считает тем же самым движением, и на обратном ходе — когда
   разговор закрывают — оно бы не пошло заново. */
@keyframes mail-pane-in {
  from { opacity: 0; transform: translateX(calc(var(--mail-pad) * 2)); }
  to { opacity: 1; transform: none; }
}
.mail.has-thread .mail__pane { animation: mail-pane-in 220ms var(--ease, ease); }

/* Уход — то же движение назад. Роман 30.08.2026: «появление этого просмотра
   прекрасно… можно сделать аналогично назад? той же анимацией просто реверс?»

   Путь тот же до точки: панель уезжает вправо ровно на то расстояние, с
   которого пришла, и гаснет. **Короче прихода** — 160 против 220, по правилу
   кнопки рабочего дня (раздел 111): приход человек ждёт, а уход уже нет, и
   реверс один в один читается тягуче.

   Кривая другая, и это важнее самой длительности: приход тормозит и встаёт
   (`--ease`), уход разгоняется прочь (`ease-in`). На одной кривой это
   читается как сдвиг туда-обратно, а не как «ушло» — то же самое различали
   у перелистывания вкладок (раздел 112).

   `forwards` обязателен: без него панель на последнем кадре прыгнула бы
   обратно в полную видимость и так и осталась бы на экране лишние
   мгновения — пока скрипт не снимет `has-thread`. */
@keyframes mail-pane-out {
  from { opacity: 1; transform: none; }
  to { opacity: 0; transform: translateX(calc(var(--mail-pad) * 2)); }
}
.mail.has-thread.is-thread-closing .mail__pane {
  animation: mail-pane-out 160ms ease-in forwards;
}

/* ---------- телефон ----------

   На узком экране колонок нет вовсе: разговор ложится слоем поверх списка
   (`mail-phone.css`), непрозрачным и во весь раздел. Сжимать здесь нечего,
   и правила стола сюда не годятся оба.

   Список не гасим: он лежит **под** панелью, и его затухание было бы видно
   сквозь неё, пока она едет, — два движения на одном месте вместо одного.

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

   240 мс, а не 220: путь у слоя во весь экран, и на коротком отрезке
   времени он читался бы рывком. */
@media (max-width: 820px) {
  @keyframes mail-pane-slide {
    from { transform: translateX(100%); }
    to { transform: none; }
  }
  .mail.has-thread .mail__pane { animation: mail-pane-slide 240ms var(--ease, ease); }

  /* Назад — за тот же правый край, откуда пришла. Это и есть привычка
     всякого телефона: слой пришёл справа — значит, назад уходит вправо, и
     смахивание от края отвечает тому, что человек видел.

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

     190 против 240 — тот же перекос в пользу ухода, что и на столе. */
  @keyframes mail-pane-slide-out {
    from { transform: none; }
    to { transform: translateX(100%); }
  }
  .mail.has-thread.is-thread-closing .mail__pane {
    animation: mail-pane-slide-out 190ms ease-in forwards;
  }
}

/* Кому движение мешает — тому его нет: раскладка меняется тем же одним
   кадром, просто без сглаживания. Так же снят и переход по сетке выше. */
@media (prefers-reduced-motion: reduce) {
  /* Селектор повторяет исходный слово в слово — включая `is-thread-closing`.
     Короткая запись проиграла бы по силе правилу с двумя классами, и панель
     уезжала бы даже при выключенном движении (грабля из раздела 113). */
  .mail.has-thread .mail__pane,
  .mail.has-thread.is-thread-closing .mail__pane { animation: none; }
}

/* ---------- выключатель «солнце — месяц» ----------

   Один на два окна почты: написание письма и настройка подписей. Показывает
   не «что нажать», а **где сейчас стоит письмо**: ползунок под месяцем —
   оформление Флоу, под солнцем — белый лист получателя.

   Ползунок ездит `transform`, а не сменой стороны: движение считает
   композитор, и переключение читается ходом, а не подменой. */
.mail-skin {
  position: relative; flex: none;
  display: inline-grid; grid-auto-flow: column; align-items: center;
  gap: 2px; padding: 3px;
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface-2); color: var(--text-faint);
  cursor: pointer;
}
.mail-skin__side {
  position: relative; z-index: 1;
  width: 22px; height: 20px; display: grid; place-items: center;
  transition: color var(--speed);
}
.mail-skin__side:first-of-type { color: var(--text); }
.mail-skin.is-light .mail-skin__side:first-of-type { color: var(--text-faint); }
.mail-skin.is-light .mail-skin__side:last-of-type { color: var(--text); }
.mail-skin__knob {
  position: absolute; top: 3px; left: 3px; z-index: 0;
  width: 22px; height: 20px; border-radius: 999px;
  background: var(--surface-4, var(--surface-3));
  transition: transform var(--speed) var(--ease);
}
.mail-skin.is-light .mail-skin__knob { transform: translateX(24px); }
.mail-skin:hover { border-color: var(--border-strong, var(--border)); }
.mail-skin:focus-visible { outline: none; box-shadow: var(--ring); }
@media (prefers-reduced-motion: reduce) {
  .mail-skin__knob, .mail-skin__side { transition: none; }
}

/* ---------- лист подписи в её настройке ----------

   Белым он был всегда — довод был верный: письмо у получателя стоит на белом.
   Но окно правки стоит внутри тёмного Флоу, и белый прямоугольник в нём
   слепит так же, как слепил в окне письма. Теперь вид выбирают выключателем
   выше, и по умолчанию подпись показана в оформлении Флоу.

   Цвет букв прибит в самом html (`mail_markup.to_html` пишет его строчным
   стилем — иначе почтовые клиенты его выбросят), поэтому в тёмном виде его
   перебивает сам лист, через `!important`. Тем же приёмом, что и лист подписи
   в окне письма: два одинаковых листа обязаны быть устроены одинаково. */
.mail-sign-preview--app { background: var(--surface); border-color: var(--border); }
.mail-sign-preview--app .mail-sign-html,
.mail-sign-preview--app .mail-sign-html * { color: var(--text) !important; }
.mail-sign-preview--app .mail-sign-html a,
.mail-sign-preview--app .mail-sign-html a * { color: var(--accent-text) !important; }
.mail-sign-preview--light { background: #FFF; border-color: #E2E2EE; }
/* Логотип на тёмном листе: у большинства он нарисован тёмным по прозрачному и
   на тёмном пропадает совсем. Подкладываем белое поле — то же, что делает
   окно письма. */
.mail-sign-preview--app .mail-sign-html img {
  background: #fff; border-radius: 6px; padding: 4px;
}
.mail-sign-bar { align-items: center; gap: 8px; margin-top: 10px; }
