/* Платформа ЭР — оформление кабинета.
   Все цвета, отступы и шрифты берутся из theme/tokens.css: ни одного «магического» значения. */

*, *::before, *::after { box-sizing: border-box; }

/* Потолок ширины формы. Поля внутри делят ряд поровну и занимают его целиком —
   ограничивать нужно не поле, а форму, иначе на сверхшироком мониторе поле для
   числа растягивается на пол-экрана. Значение подобрано по рендеру: на 1920
   панель шага шире потолка не бывает, поэтому пустоты справа не остаётся. */
:root { --forma-max: 1500px; }

/* Атрибут hidden обязан выигрывать у наших раскладок.
   Браузерное правило [hidden]{display:none} имеет ту же специфичность, что и класс,
   поэтому .login-screen{display:grid} его перебивал — экран входа не скрывался
   и висел поверх рабочей области. */
[hidden] { display: none !important; }

html { background: var(--bg-app); }

body {
  margin: 0;
  font-family: var(--font-brand);
  font-size: var(--fs-text);
  line-height: var(--lh-base);
  color: var(--text);
  background: var(--bg-app);
}

/* ---------- Шкала заголовков ----------
   Уровень задаёт кегль, и наоборот. Базовых правил у h3 и h4 не было, и
   браузер рисовал h3 крупнее (17,5 px), чем наш h2 (15 px): на шаге 2
   «Шаг 2. Открытые данные» (18) стоял рядом с «Реквизиты» (15) — один
   уровень, два вида. Замер 10.09.2026 по одиннадцати экранам: восемь
   видов заголовка на четыре уровня. Здесь их четыре. */
h1 { font-size: var(--fs-h2); font-weight: 600; margin: 0; letter-spacing: -0.01em; }
h2 { font-size: var(--fs-h3); font-weight: 700; margin: 0; }
h3 { font-size: var(--fs-h4); font-weight: 700; margin: 0; }
h4 { font-size: var(--fs-sm); font-weight: 700; margin: 0; }
a { color: var(--link); text-decoration: none; }
a:hover { color: var(--link-hover); }

/* ---------- Фирменный блок ---------- */
/* Отступ 13 px — тот же, что у подписей разделов и пунктов меню: логотип,
   «РАБОТА» и значки пунктов стоят на одной вертикали. */
.brand { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-0); padding: 0 13px; }
.brand-logo { height: 40px; max-width: 100%; object-fit: contain; }
.brand-logo.dark { display: none; }
:root[data-theme="dark"] .brand-logo.light { display: none; }
:root[data-theme="dark"] .brand-logo.dark { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .brand-logo.light { display: none; }
  :root:not([data-theme="light"]) .brand-logo.dark { display: block; }
}
.brand-mark {
  width: 38px; height: 38px; border-radius: var(--radius-sm);
  background: var(--brand); color: var(--text-inverse);
  display: grid; place-items: center; font-weight: 700; flex: none;
}
.brand-name { font-weight: 700; font-size: var(--fs-xs); letter-spacing: 0.02em; }
.brand-sub { font-size: var(--fs-xs); color: var(--text-muted); }

/* ---------- Вход ---------- */
/* Колонкой, а не гридом: у грида форма и переключатель оформления становились
   двумя строками, каждая центрировалась в своей половине экрана — и между
   ними зияло 260 px пустоты. */
.login-screen {
  min-height: 100vh; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: var(--space-2);
  padding: var(--space-3);
}
.login-box {
  width: 100%; max-width: 400px; background: var(--bg); padding: var(--space-4);
  border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow);
  display: flex; flex-direction: column; gap: var(--space-2);
}
.login-box h1 { font-size: var(--fs-h3); margin-top: var(--space-1); }
.login-box .btn-cta { width: 100%; margin-top: var(--space-1); }

/* ---------- Поля формы ---------- */
.field { display: flex; flex-direction: column; gap: var(--space-0); }
.field label { font-size: var(--fs-sm); font-weight: 600; }
.field label em { color: var(--crit); font-style: normal; }
.field small { font-size: var(--fs-xs); color: var(--text-muted); line-height: 1.35; }

/* Значок «!» у подписи: длинное объяснение прячется под ним и раскрывается
   полосой под полем. Постоянный текст в семь строк отодвигал само поле вниз —
   на шаге 3 блок занимал 447 px там, где хватает вдвое меньшего. Круг 24 px —
   минимальная цель нажатия по WCAG 2.2. */
.hint {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; padding: 0; cursor: pointer;
  border: 1px solid var(--line-strong); border-radius: var(--radius-pill);
  background: var(--surface); color: var(--text-muted);
  font-family: inherit; font-size: var(--fs-xs); font-weight: 700; line-height: 1;
}
.hint:hover,
.hint[aria-expanded="true"] {
  border-color: var(--brand); color: var(--brand); background: var(--brand-bg-soft);
}
/* Полосой под полем, а не всплывашкой: всплывашка на узком экране перекрывает
   ровно то поле, ради которого её открыли, и закрывается от случайного касания. */
/* Один значок на экран вместо значка у каждого поля: строка-подпись с «!». */
.hints-all { justify-content: flex-start; gap: var(--space-0); margin: 0 0 var(--space-2); }
.hints-all + .hint-text { margin-bottom: var(--space-2); }
.hint-text p { margin: 0 0 6px; }
.hint-text p:last-child { margin-bottom: 0; }
.hint-text {
  font-size: var(--fs-sm); color: var(--text-muted); line-height: var(--lh-base);
  background: var(--surface); border-left: 3px solid var(--brand);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  padding: var(--space-1) var(--space-2); max-width: 90ch;
}
/* В шапке раздела полосу выравниваем по содержимому раздела, а не по краю рамки. */
.sec > .hint-text { margin: 0 var(--space-2) 6px; }
input, select, textarea {
  font-family: inherit; font-size: var(--fs-text); color: var(--text);
  padding: 9px 11px; border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  background: var(--bg); width: 100%;
}
/* Без этого браузер рисует многострочное поле своим шрифтом и с прямыми углами —
   рядом с остальными полями оно выглядит чужим. */
textarea { resize: vertical; min-height: 64px; line-height: var(--lh-base); }
/* Выпадающий список браузер рисует сам: на Windows это серый треугольник в
   квадратной кнопке у правого края, и перекрасить её нельзя. Двадцать один
   список в программе выглядел деталью чужого интерфейса — на карточке
   проверки, в отборе, в справочнике. Стрелку рисуем свою, размером и цветом
   как остальные значки. Место под неё — 30 px справа, иначе длинный пункт
   («Оборотно-сальдовая ведомость…») уезжает под стрелку. */
select {
  appearance: none; -webkit-appearance: none;
  padding-right: 30px;
  background-image: var(--select-arrow);
  background-repeat: no-repeat;
  background-position: right 11px center;
}
/* Флажок и переключатель — одна и та же вещь во всей программе, значит и размер
   один. Замер браузером 10.09.2026 нашёл четыре: 13 px в справочнике запросов
   (66 штук — браузерный размер по умолчанию; правило `input { width: 100% }`
   выше до них не доходит, у стиля браузера селектор с атрибутом и он
   специфичнее), 16 px в форме выборки, 18 px в задачах и карточке проверки,
   24 px в таблицах отбора. Взято 18 px — размер того места, где галочки ставят
   чаще всего. Фирменный цвет тоже был только в задачах: на остальных экранах
   галочка красилась системным синим. */
input[type="checkbox"], input[type="radio"] {
  width: 18px; height: 18px; margin: 0; flex: 0 0 auto; accent-color: var(--brand);
}
/* Видимый фокус — не украшение: без него человек, идущий по клавише Tab, не знает,
   где находится. У полей он был, у кнопок и ссылок — нет, хотя именно по ним идёт
   основная работа с клавиатуры. Проверено 22.08.2026: у кнопки входа в фокусе
   контур был none. Правило одно на все интерактивные элементы, чтобы не разъезжалось.
   :focus-visible, а не :focus — обводка нужна при работе клавиатурой, а не после
   каждого нажатия мышью. */
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
button:focus-visible,
a:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--brand); outline-offset: 1px;
}
/* Поле даты внутри устроено из нескольких кусков (день, месяц, год, значок
   календаря), и Tab идёт по ним, не выходя из поля. Замер обхода журнала
   клавишами 12.09.2026: на второй остановке в «Период с» фокус уходит на
   значок календаря внутри поля — само поле там уже не :focus и не
   :focus-visible, и контур пропадал, хотя человек всё ещё в поле.
   :focus-within видит и внутренние остановки. */
input[type="date"]:focus-within, input[type="time"]:focus-within {
  outline: 2px solid var(--brand); outline-offset: 1px;
}
.form-error:empty { display: none; }
.form-error {
  background: var(--crit-bg); color: var(--crit); padding: 9px 11px;
  border-radius: var(--radius-sm); font-size: var(--fs-sm);
}

/* ---------- Ряд действий ----------
   Кнопки и подписи в строку. Тринадцать мест держали для этого свои классы,
   и они разошлись: 6, 8, 10 и 15 px между кнопками — на глаз четыре разных
   ряда вместо одного. Канон (docs/06) разрешает только шкалу 10/15/20/30/40,
   берём 10 px. Имена оставлены прежними: разметку это не трогает.
   Ниже по файлу у каждого имени остаётся только то, чем оно правда отличается
   — поля, прижим вправо. */
.head-actions,
.sample-form-actions,
.sample-source,
.review-actions,
.file-pick,
.danger-row,
.tb-form-actions,
.tb-upload-actions,
.notice-action,
.modal-actions,
.doc-bar {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-1);
}
/* Значок подсказки — знак при подписи, а не элемент ряда. Во flex-строке он
   переносился на строку ниже, стоило подписи занять всю ширину: на телефоне 390
   под «ОСВ по всем счетам — одним файлом или пачкой по счетам *» кружок «!»
   висел сам по себе и читался как отдельная кнопка. Теперь идёт за последним
   словом, как знак препинания, и переносится вместе с ним. */
/* Главное здесь — nowrap. Пока ряд стоял в общей группе с flex-wrap: wrap,
   длинная подпись занимала всю ширину и значок «!» съезжал под неё отдельной
   строкой: на 390 px под «ОСВ по всем счетам — одним файлом или пачкой по
   счетам *» он читался как самостоятельная кнопка. Без переноса подпись
   ужимается, а значок остаётся при ней — на всех замеренных ширинах. */
.hint-row {
  display: flex; align-items: flex-start; flex-wrap: nowrap; gap: var(--space-0);
}
.hint-row > .hint { flex: none; }
.hint-row > label, .hint-row > .checkline { min-width: 0; }

/* ---------- Раскладка ----------
   Ширины взяты из утверждённого референса: сайдбар 248, рабочая область резиновая.
   Жёсткого потолка у контента нет намеренно: на 1440 сетка и так даёт референсные
   1136, а на широком мониторе таблицы шага 4 должны пользоваться местом, а не
   упираться в пустое поле справа. */
/* 252 px — ровно как меню в «Проверке контрагентов» (App.tsx, width={252}):
   программы стоят в соседних вкладках, и разная ширина меню бросается в глаза. */
.layout { display: grid; grid-template-columns: 252px minmax(0, 1fr); min-height: 100vh; }
/* Меню закреплено: страницы шага 4 и журнала длиной в несколько экранов, и
   при обычной колонке грида меню уезжало вверх вместе с содержимым, а профиль
   внизу оказывался вне экрана — до него было не долистать и не нажать.
   Своя прокрутка внутри: если пунктов и «Вашей недели» больше, чем высота
   экрана, скроллится сайдбар, а не страница. */
.sidebar {
  background: var(--bg); border-right: 1px solid var(--line);
  padding: 20px 16px; display: flex; flex-direction: column; gap: var(--space-3);
  position: sticky; top: 0; align-self: start;
  height: 100vh; overflow-y: auto; overscroll-behavior: contain;
}
/* Меню повторяет «Проверку контрагентов»: рубрика группы, пункт высотой 40 px
   со скруглением 8, кегль 15 обычного веса. Замеры снял с её стенда. */
.nav { display: flex; flex-direction: column; gap: var(--space-2); }
.nav-group { display: flex; flex-direction: column; gap: var(--space-0); }
.nav-cap {
  font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-muted); font-weight: 600; padding: 0 13px 4px;
}
.nav a {
  min-height: 40px; padding: 0 13px; border-radius: var(--radius-sm);
  color: var(--nav-item); font-size: var(--fs-text); font-weight: 400;
}
.nav a:hover { background: var(--surface-2); color: var(--brand); }
/* Активный пункт — мягкая подсветка, а не индиговая плашка во всю ширину:
   залитый прямоугольник перетягивал внимание с рабочей области на меню. */
.nav a.active { background: var(--info-bg); color: var(--brand); font-weight: 600; }
/* Профиль — не подпись, а кнопка: по ней открывается меню со сменой пароля,
   оформлением и выходом. Раньше три эти вещи лежали в сайдбаре россыпью,
   а на имя нажать было нельзя — владелец пробовал и не понял, почему не жмётся. */
.user { position: relative; }
.user-name { display: block; font-size: var(--fs-sm); font-weight: 600; }
.user-role { display: block; font-size: var(--fs-xs); color: var(--text-muted); }
.content { padding: 24px 28px 40px; min-width: 0; }


/* ---------- Сетки референса ----------
   Две колонки — работа и спутник: спутник ровно 380 на всех экранах, разнобой
   боковых колонок читается как небрежность быстрее всего остального.
   minmax(0, 1fr) обязателен: без него длинное название клиента распирает колонку. */
.grid-2 { display: grid; grid-template-columns: minmax(0, 1fr) 380px; gap: var(--space-2); align-items: start; }
.grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2); align-items: start; }
.grid-4 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-2); align-items: start; }
.col { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
/* Запросы и маршрут — две колонки одного экрана проверки. Пока запросов нет,
   левая карточка схлопывается до трёх строк, и под ней остаётся больше 500 px
   голого фона рядом с маршрутом из восьми шагов — поэтому пустую карточку
   растягиваем, и пустое состояние встаёт по центру.
   А вот карточку С запросами растягивать нельзя: замер 10.09.2026 на 1600 px —
   пять запросов, и внутри белой карточки 330 px пустоты между списком и
   строкой итога. Владелец про такое и написал: «много пустоты». Карточка
   с содержимым кончается там, где кончается содержимое. */
.eng-columns > .card.reqs-none { align-self: stretch; }
/* Правая колонка закреплена: маршрут и «что делать сейчас» нужны, пока читаешь
   длинный список запросов, а он уезжает на два экрана вниз. Держим её отдельно
   от растяжки (`align-self: start`) — иначе колонка тянется во всю высоту ряда
   и прилипать ей некуда. Отступ сверху — высота верхней панели (64 px и линия),
   она тоже закреплена. */
.eng-columns > .col {
  position: sticky; top: calc(65px + var(--space-1)); align-self: start;
  max-height: calc(100vh - 65px - 2 * var(--space-1)); overflow-y: auto;
  scrollbar-width: thin; scrollbar-color: var(--line) transparent;
}
/* «Что делать сейчас» — такая же карточка, как соседи по колонке, только с
   цветной кромкой состояния. Прежний блок во всю ширину экрана занимал 98 px
   над колонками и повторял то, что и так видно в маршруте. */
.next-card { border-left: 4px solid var(--brand); }
.next-card.wait { border-left-color: var(--warn); }
.next-card.problem { border-left-color: var(--crit); }
.next-card.done { border-left-color: var(--ok); }
.next-card .card-head { margin-bottom: var(--space-1); }
.next-card .next-title { font-size: var(--fs-h4); font-weight: 700; margin: 0; }
.next-card .next-hint { font-size: var(--fs-xs); margin-top: 3px; }
.next-card .btn, .next-card .btn-cta { width: 100%; margin-top: var(--space-1); }
.eng-columns > .card { display: flex; flex-direction: column; }
.eng-columns .reqs { flex: 1 1 auto; display: flex; flex-direction: column; }
.eng-columns .reqs > .empty,
.eng-columns .reqs > .empty-inline { margin: auto 0; }
@media (max-width: 1180px) {
  .grid-2, .grid-3, .grid-4 { grid-template-columns: minmax(0, 1fr); }
  /* Колонки встали друг под друга: закреплять правую больше не за что —
     иначе маршрут прилипал бы поверх списка запросов, под которым он стоит. */
  .eng-columns > .col { position: static; max-height: none; overflow: visible; }
}

/* Подпись-рубрика над блоком: мелкая, разряженная, приглушённая. */
.cap {
  font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-muted); font-weight: 600;
}

/* Второстепенное действие строкой, а не кнопкой: на экране должна быть одна жёлтая кнопка. */
/* Класс вешают и на <button>: без сброса браузер рисует свою серую кнопку
   с объёмной рамкой прямо посреди предложения (видно на шаге 4, «загрузите
   выгрузку заново»). Специфичность та же, что у .crow-start ниже, поэтому
   свои поля у отдельных мест сохраняются. */
/* Поле сверху и снизу — не отступ, а цель нажатия: без него кнопка-ссылка
   высотой в кегль (13 px) даёт коробку 62×16 при норме 24×24 (WCAG 2.2, 2.5.8).
   Нашёл браузерный аудит шага «Отчётность и ОСВ» 10.09.2026. По горизонтали
   поля нет намеренно: кнопка стоит внутри предложения и не должна отодвигать
   соседние слова. */
.lnk {
  font-size: var(--fs-sm); color: var(--brand); font-weight: 600;
  white-space: nowrap; cursor: pointer;
  background: none; border: 0; padding: 4px 0; font-family: inherit;
}
.lnk.quiet { color: var(--text-muted); min-height: 32px; }
.lnk:hover { color: var(--brand-700); }

/* Скелетон вместо надписи «Загружаем…»: человек видит, какой формы будет содержимое. */
/* Чипы-фильтры со счётчиками: сколько карточек в каждом состоянии — видно до нажатия. */
.chips { display: flex; flex-wrap: wrap; gap: var(--space-1); margin: 0 0 var(--space-2); }
.chip {
  display: inline-flex; align-items: center; gap: var(--space-0); padding: 7px 14px;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--bg);
  color: var(--text-muted); font-family: var(--font-brand); font-size: var(--fs-sm); cursor: pointer;
}
.chip:hover { border-color: var(--brand-100); }
.chip.on { background: var(--brand-bg-soft); color: var(--brand); border-color: transparent; font-weight: 600; }
/* Приглушать число прозрачностью нельзя: у выбранного набора цвет и без того
   светлый, и 0.75 роняли контраст ниже нормы (axe-core, `color-contrast`,
   10.09.2026 — экран «Клиенты»). Кегль уже отличает счётчик от названия. */
/* Моноширинный для того, что перепечатывают и сверяют посимвольно: пароль,
   код счёта, отметка времени. Класс стоял в разметке одиннадцать раз, а правила
   в CSS не было вовсе — разметка обещала одно, браузер рисовал другое. Там, где
   это оказалась проза («ИНН 7810… · отчётность за 2025 · ведёте вы»), класс
   убран: моноширинный в предложении читается хуже обычного. Слова тоже не его
   дело: «админ» и «рук.» в хвосте меню он растягивал, а «Ctrl K» в строке
   поиска — настолько, что подпись «Поиск по кабинету» вставала в две строки
   и сдвигала всё меню на 14 px (замер сверкой с эталоном 12.09.2026). */
.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.chip-count { font-size: var(--fs-xs); color: inherit; }

/* Шапка карточки проверки: значок клиента, название, строка фактов, действия.
   Мета моноширинным — там цифры (ИНН, шаги, существенность), их сравнивают глазом. */
/* Кнопки стоят напротив названия, а не над ним: при выравнивании по верху они
   висели на 32 px выше строки с именем клиента и ряд читался кривым. */
/* Через `.page-head` — иначе общее правило `.page-head { align-items: flex-start }`
   стоит ниже в файле, и при равной силе селектора выигрывает оно. */
.page-head.eng-page-head { align-items: center; gap: var(--space-2); }
/* flex-basis, а не 0: с нулевой основой имя клиента сжималось до минимума и
   вставало в столбик по одному слову («ООО / «Кабинет- / тест»»), пока справа
   от кнопок шага оставалась пустота. Теперь при нехватке места первой переносится
   вниз навигация по шагам, а название остаётся в строку. */
.eng-ident { min-width: 0; flex: 1 1 340px; }
.eng-name-row { display: flex; align-items: center; gap: var(--space-2); }
/* Отступ нужен только там, где ссылка «назад» осталась внутри шапки (страница
   шага и планирование). На карточке проверки она вынесена выше, и лишние 6 px
   сбивали кнопки с одной линии с названием. */
.back + .eng-name-row { margin-top: 6px; }
.eng-name-text { min-width: 0; }
.eng-head-meta { font-size: var(--fs-sm); color: var(--text-muted); margin-top: 4px; }
.eng-head-actions { display: flex; gap: var(--space-1); flex: none; align-items: center; }
/* Размер кнопок — на ступень меньше основных: это переходы рядом с экраном,
   а не его главное действие. */
.eng-head-actions .btn {
  padding: 7px 12px; font-size: var(--fs-sm); min-height: 34px;
}
/* Кнопка «ещё» стоит в том же ряду и должна быть той же высоты: 28 px рядом с
   34 читались как случайно съехавший элемент. */
.eng-head-actions .icon-btn { min-height: 34px; min-width: 34px; }

/* Шаг маршрута — строка, как в макете: состояние кружком, факт под названием,
   состояние справа остаётся списком, иначе шаг нельзя отметить вручную. */
.step {
  display: flex; align-items: center; gap: var(--space-1);
  padding: 9px 8px; border: 1px solid var(--line);
  border-radius: var(--radius-sm); background: var(--bg); margin-bottom: var(--space-1);
}
.step.active { border-color: var(--brand); box-shadow: inset 3px 0 0 var(--brand); }
/* `font: inherit` обязателен: кнопка без него берёт системный Arial 13.33 px,
   и весь «Маршрут проверки» — восемь строк с названиями и фактами — выпадал
   из шрифта кабинета. На глаз это читается как «шрифты другие», а не как
   ошибка вёрстки. */
/* ---------- Строка в одну линию ----------
   Значок или галочка слева, текст справа, ничего не переносится. Четыре места
   держали для этого свои правила и совпадали по всем общим свойствам —
   расходиться им было только вопросом времени. Ниже у каждого имени осталось
   лишь своё: сброс вида кнопки, растяжка, цель нажатия. */
.step-open,
.cat-all,
.cname-box {
  display: flex; align-items: center; gap: var(--space-1); min-width: 0;
}
/* `font: inherit` обязателен: кнопка без него берёт системный Arial 13,33 px,
   и весь «Маршрут проверки» — восемь строк с названиями и фактами — выпадал
   из шрифта кабинета. На глаз это читается как «шрифты другие», а не как
   ошибка вёрстки. */
.step-open {
  flex: 1; background: none; border: none; padding: 0; cursor: pointer;
  text-align: left; font: inherit; color: inherit;
}
.step-num {
  width: 26px; height: 26px; flex: none; border-radius: 50%;
  display: grid; place-items: center; font-size: 12px; font-weight: 600;
  background: var(--surface-2); color: var(--text-muted);
}
.step.done .step-num { background: var(--brand); color: var(--text-inverse); }
.step.work .step-num { background: var(--btn); color: var(--btn-text); }
.step.wait .step-num { background: var(--warn-bg); color: var(--warn); }
.step.problem .step-num { background: var(--crit-bg); color: var(--crit); }
.step-body { min-width: 0; display: block; }
.step-title {
  display: block; font-size: var(--fs-sm); font-weight: 600; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.step-fact {
  display: block; margin-top: 2px; font-size: var(--fs-xs); color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Состояние шага читается как текст справа — как в референсе. Системная стрелка
   списка занимала 16 px и обрезала название шага («Шаг 4. Проверка элеме…»);
   что строку можно нажать, видно по рамке и фону при наведении. */
.step-state {
  flex: none; appearance: none; -webkit-appearance: none;
  border: 1px solid transparent; background: transparent; text-align: right;
  color: var(--text-muted); font-size: var(--fs-xs); padding: 3px 4px; height: 30px;
  border-radius: var(--radius-sm); cursor: pointer; max-width: 88px;
}
.step-state:hover, .step-state:focus-visible { border-color: var(--line); background: var(--surface-2); }
.step.done .step-state { color: var(--ok); }
.step.work .step-state { color: var(--brand); font-weight: 600; }
.step.wait .step-state { color: var(--warn); }
.step.problem .step-state { color: var(--crit); }

/* «Что делать сейчас» — задача с адресом, а не лозунг: видно проверку, шаг,
   что уже закрыто и куда нажать. Полоса шагов — та же, что на карточке проверки. */
/* Сетка, а не ряд: при переносе кнопки и полоса шагов вставали под значком
   клиента, а не под текстом, к которому относятся — левый край карточки шёл
   в две линии. Значок и кнопки стояли столбиком справа, и блок из-за этого был
   на 50 px выше нужного (замер 17.09.2026: 174 px при тексте в 68). Теперь всё
   в одну строку: значок слева, текст, кнопки справа — полоса шагов начинается
   сразу под текстом. */
.next-top {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  column-gap: var(--space-2); row-gap: var(--space-1); align-items: start;
}
.next-main { min-width: 0; grid-column: 1; }
.next-top.with-badge { grid-template-columns: auto minmax(0, 1fr) auto; }
.next-top.with-badge .cl-badge { grid-column: 1; grid-row: 1; }
.next-top.with-badge .next-main { grid-column: 2; grid-row: 1; }
.next-top.with-badge .next-actions { grid-column: 3; grid-row: 1; align-self: start; justify-self: end; }
.next-top .route-bar { grid-column: 1 / -1; margin-bottom: 0; }
/* Внутри карточки полоса на 24 px уже, чем на странице шага (поля карточки),
   и подписей ей не хватало ровно 34 px: восемь номеров стояли в ряд, а между
   ними зияло по 100 px пустоты. Отыгрываем на боковых полях сегмента. */
/* Высота полосы равна высоте кнопок рядом: сегмент в 40 px делал её на 10 px
   выше «Продолжить шаг», и ряд читался ступенькой. */
.next-top .route-pill { padding: 0 6px; min-height: 32px; }
.next-top .route-bar { padding: 3px; }
.next-top .route-pill-num { width: 22px; height: 22px; }
/* Адрес задачи — первой строкой и ровно одной: «ООО … · отчётность за 2025».
   Раньше тут стояло «ЧТО ДЕЛАТЬ СЕЙЧАС» капслоком, а адрес уезжал под
   пояснение четвёртой строкой и переносился на второй этаж. Что не
   поместилось, видно в подсказке. */
.next-where {
  margin-bottom: 2px; font-size: var(--fs-sm); color: var(--text-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* На телефоне строка не помещается никогда, и обрывается она ровно на годе —
   замер 14.09.2026 на 390 px: «Северная верфь (пример) · отчётность за 2…».
   Какой год проверяем — половина смысла строки, поэтому там разрешаем две. */
@media (max-width: 560px) {
  .next-where {
    white-space: normal; display: -webkit-box; -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
  }
}
.next-actions { display: flex; gap: var(--space-1); align-items: center; grid-column: 2; align-self: center; }

/* До 1100 px кнопки не помещаются рядом с текстом — уходят на свою строку под
   ним, во всю ширину карточки. Значок остаётся слева от текста. */
@media (max-width: 1100px) {
  .next-top { grid-template-columns: minmax(0, 1fr) auto; }
  .next-actions { grid-column: 1 / -1; align-self: start; justify-self: start; }
  .next-top.with-badge { grid-template-columns: auto minmax(0, 1fr); }
  .next-top.with-badge .next-main { grid-column: 2; grid-row: 1; }
  .next-top.with-badge .next-actions { grid-column: 1 / -1; grid-row: 2; justify-self: start; }
}

/* Верхняя панель — как в макете: поиск слева, главное действие справа.
   Живёт над рабочей областью и одинакова на всех экранах, поэтому человек
   всегда знает, где искать и где начать новое. */
/* Шапка закреплена по той же причине, что и меню: поиск и главное действие
   экрана должны оставаться под рукой на длинной странице. */
/* Шапка — тонкая белая полоса высотой 64 px, как в основной программе:
   слева действие текущего экрана, справа тот, кто работает. */
.topbar {
  display: flex; align-items: center; gap: var(--space-2); min-height: 64px;
  margin: -24px -28px var(--space-3); padding: 0 20px;
  background: var(--bg); border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 20;
}
.topbar > .user { margin-left: 0; }
.topbar-actions:empty + .user { margin-left: auto; }
/* Действие экрана и профиль идут одной группой справа — как в основной программе,
   где в шапке справа стоят значки и аккаунт. */
.topbar-actions { margin-left: auto; display: flex; align-items: center; gap: var(--space-1); }
.topbar-actions:empty { display: none; }
/* Поиск стоит в меню под логотипом — на том же месте, что в основной программе. */
.side-search {
  display: flex; align-items: center; gap: var(--space-1); width: 100%;
  padding: 9px 12px; cursor: pointer;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius-sm); color: var(--text-muted); font-family: var(--font-brand);
  font-size: var(--fs-sm); text-align: left;
}
.side-search:hover { border-color: var(--brand-100); }
.side-search-ico { display: inline-flex; color: var(--text-muted); flex: none; }
.side-search-text { flex: 1; min-width: 0; }
.side-search-key {
  font-size: var(--fs-xs); color: var(--text-muted); background: var(--bg);
  border: 1px solid var(--line); border-radius: var(--radius-xs); padding: 1px 6px; flex: none;
}

/* Пункт меню: иконка, название, хвост со счётчиком или меткой доступа. */
.nav a { display: flex; align-items: center; gap: var(--space-1); }
.nav .ico { flex: none; opacity: 0.75; }
.nav a.active .ico { opacity: 1; }
.nav-tail { margin-left: auto; font-size: var(--fs-xs); color: var(--text-muted); }
.nav-tail:empty { display: none; }

/* Знак «ЭР» — только для колонки иконок (см. блок 561–900 px). */
.rail-only { display: none; }

/* Кто работает: инициалы, имя, роль — как в макете. */
.user-line {
  display: flex; align-items: center; gap: var(--space-1);
  /* Кнопке нужен явный цвет: без него браузер берёт свой buttontext, и в тёмной
     теме имя человека было почти неразличимо на тёмной подложке (контраст 1.2). */
  color: var(--text);
  padding: 8px; border: 1px solid transparent; border-radius: var(--radius-sm);
  background: none; font-family: var(--font-brand); font-size: var(--fs-sm);
  text-align: left; cursor: pointer;
}
.user-line:hover { background: var(--surface-2); border-color: var(--line); }
.user-line[aria-expanded="true"] { background: var(--surface-2); border-color: var(--line); }
.user-caret { margin-left: auto; color: var(--text-muted); flex: none; transition: transform .15s; }
.user-line[aria-expanded="true"] .user-caret { transform: rotate(180deg); }

/* Уведомления в шапке: значок с числом нового и лента событий под ним.
   Число рисуем только когда оно есть — нулевой кружок люди читают как ошибку. */
/* Отсчёт панели ведём от шапки, а не от самого значка: значок стоит в середине
   шапки, и панель шириной 360 px уезжала левее окна (замер: left −71 при окне
   632, left −118 при 390). Шапка позиционирована (sticky), поэтому панель
   прижимается к её правому краю и остаётся в экране на любой ширине. */
.bell { position: static; flex: none; }
.bell-btn {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; padding: 0; cursor: pointer;
  color: var(--text); background: none; border: 1px solid transparent; border-radius: var(--radius-sm);
}
.bell-btn:hover, .bell-btn[aria-expanded="true"] { background: var(--surface-2); border-color: var(--line); }
.bell-count {
  position: absolute; top: 2px; right: 2px; min-width: 16px; height: 16px; padding: 0 4px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--fs-xs); font-weight: 700; line-height: 1;
  color: var(--bg); background: var(--crit); border-radius: 8px;
}
.bell-menu {
  position: absolute; right: var(--space-1); top: calc(100% + 6px);
  width: 360px; max-width: calc(100% - var(--space-2)); z-index: 30;
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-md, 0 8px 24px rgba(23,23,37,.12));
  padding: var(--space-2); display: flex; flex-direction: column; gap: var(--space-1);
}
/* На телефоне панель занимает всю ширину шапки: 360 px там сжались бы до
   нечитаемой колонки с переносами в каждой строке. */
@media (max-width: 560px) {
  .bell-menu { left: var(--space-1); right: var(--space-1); width: auto; max-width: none; }
}
.bell-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-2);
  font-weight: 600; font-size: var(--fs-sm); }
.bell-list { display: flex; flex-direction: column; gap: var(--space-0); max-height: 60vh; overflow-y: auto; }
.bell-item {
  display: block; padding: 8px 10px; border-radius: var(--radius-sm);
  color: var(--text); text-decoration: none; border: 1px solid transparent; text-align: left;
  background: none; font-family: var(--font-brand); cursor: pointer; width: 100%;
}
.bell-item:hover { background: var(--surface-2); border-color: var(--line); }
/* Непрочитанное отличается полосой слева, а не цветом текста: цветной текст
   в ленте из десяти строк читается как десять ошибок. */
.bell-item.new { border-left: 3px solid var(--brand); background: var(--brand-bg-soft); }
.bell-title { display: block; font-size: var(--fs-sm); font-weight: 600; }
.bell-sub { display: block; font-size: var(--fs-xs); color: var(--text-muted); margin-top: 2px; }

/* Меню профиля: всплывает над кнопкой, чтобы не упираться в низ экрана. */
.user-menu {
  position: absolute; right: 0; top: calc(100% + 6px); min-width: 240px; z-index: 30;
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-md, 0 8px 24px rgba(23,23,37,.12));
  padding: var(--space-1); display: flex; flex-direction: column; gap: var(--space-0);
}
.user-menu button:not(.btn),
.user-menu a {
  display: block; width: 100%; text-align: left; padding: 8px 10px;
  border: none; background: none; border-radius: var(--radius-sm);
  font-family: var(--font-brand); font-size: var(--fs-sm); color: var(--text); cursor: pointer;
}
.user-menu button:not(.btn):hover,
.user-menu a:hover { background: var(--surface-2); }
.user-menu .theme-switch { padding: 6px 10px 2px; }
.user-menu .menu-sep { height: 1px; background: var(--line); margin: 4px 0; }
.user-menu .danger { color: var(--crit); }
.user-ava {
  width: 32px; height: 32px; flex: none; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--brand-bg-soft); color: var(--brand);
  font-size: var(--fs-xs); font-weight: 700; letter-spacing: 0.02em;
}
.user-who { min-width: 0; }

/* «Ваша неделя»: личный итог в сайдбаре. Пунктирная рамка — чтобы блок не спорил
   с меню: это справка о себе, а не ещё один раздел. */
.week {
  /* Без бокового отступа: с ним карточка стояла на 4 px правее кнопки поиска и
     пунктов меню, и левый край сайдбара шёл лесенкой. Внутренние 12 px рядом
     с рамкой дают те же 13 px до текста, что у кнопки поиска и пунктов меню. */
  margin: auto 0 0; padding: var(--space-1) 12px;
  background: var(--surface-2); border: 1px dashed var(--line); border-radius: var(--radius);
}
/* Строка показателя: число и что оно значит. Число крупнее подписи, но не
   крупнее заголовка блока — иначе сайдбар начинает кричать (см. правило одной
   типографической шкалы). */
.att-row { display: flex; align-items: baseline; gap: var(--space-0); margin-top: 7px; }
/* Колонка чисел одной ширины: без неё подписи «1 запрос…» и «32 запроса…»
   начинались с разных отступов, и левый край блока шёл лесенкой. */
.att-num { min-width: 1.9em; font-size: var(--fs-h4); font-weight: 600; color: var(--text); }
.att-row.late .att-num { color: var(--crit); }
.att-word { font-size: var(--fs-xs); color: var(--text-muted); line-height: 1.3; }
/* Переход к тому, что ждёт дольше всех. Цель нажатия не меньше 24 px по
   WCAG 2.2: с сенсорного экрана в строку текста не попадают с первого раза. */
/* Блочная, а не flex: имя клиента внутри — отдельный узел, и во flex-строке
   оно становилось второй колонкой, из-за чего «Открыть запросы» переносилось
   по слогам рядом с ним. Высота цели нажатия держится min-height. */
.att-link {
  display: block; min-height: 26px; margin-top: 9px; line-height: 1.35;
  font-size: var(--fs-xs); font-weight: 600; color: var(--brand);
  text-decoration: none; overflow-wrap: break-word;
}
.att-link:hover { text-decoration: underline; }
.att-clear { margin-top: 7px; }
.week-note { margin: 9px 0 0; font-size: var(--fs-xs); color: var(--text-muted); line-height: 1.35; }

/* Быстрый переход по Ctrl + K. Строка в шапке — та же палитра для тех,
   кто клавишами не пользуется: без неё функция существует только для знающих. */
.palette {
  position: fixed; inset: 0; z-index: 60; background: rgba(23, 23, 37, 0.35);
  display: flex; justify-content: center; align-items: flex-start; padding: 12vh 16px 16px;
}
.palette[hidden] { display: none; }
.palette-box {
  width: min(620px, 100%); background: var(--bg); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: 0 18px 48px rgba(23, 23, 37, 0.22);
  overflow: hidden;
}
.palette-input {
  width: 100%; border: none; border-bottom: 1px solid var(--line); border-radius: 0;
  padding: 15px 18px; font-size: var(--fs-h4); background: transparent; color: var(--text);
}
.palette-input:focus-visible { outline: none; box-shadow: inset 0 -2px 0 var(--brand); }
.palette-list { max-height: 46vh; overflow-y: auto; contain: paint; }
.palette-item {
  display: flex; align-items: baseline; gap: var(--space-1); padding: 10px 18px; cursor: pointer;
}
.palette-item.on { background: var(--brand-bg-soft); }
.palette-title { font-size: var(--fs-text); font-weight: 600; color: var(--text); }
.palette-sub { margin-left: auto; font-size: var(--fs-xs); color: var(--text-muted); white-space: nowrap; }
.palette-empty { padding: 16px 18px; color: var(--text-muted); font-size: var(--fs-sm); }
.palette-foot {
  display: flex; gap: var(--space-2); padding: 9px 18px; border-top: 1px solid var(--line);
  background: var(--surface-2); color: var(--text-muted); font-size: var(--fs-xs);
}
body.palette-open { overflow: hidden; }

/* Экран ждёт данные: заготовки держат раскладку, чтобы готовый экран не прыгнул. */
.skeleton-screen { max-width: 1136px; }
/* Подпись для тех, кто слушает экран, а не смотрит на него. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

.skeleton {
  border-radius: var(--radius-sm); min-height: 12px;
  background: linear-gradient(90deg, #EDEDF4 25%, #F5F5FA 37%, #EDEDF4 63%);
  background-size: 400% 100%; animation: skeleton 1.4s ease infinite;
}
@keyframes skeleton { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
@media (prefers-reduced-motion: reduce) { .skeleton { animation: none; } }

/* Пустое место, куда что-то встанет: пунктир честнее пустоты — видно, что блок не сломан. */
.empty-box {
  border: 1px dashed var(--line-strong); border-radius: var(--radius);
  padding: var(--space-3); text-align: center; color: var(--text-muted); font-size: var(--fs-sm);
}

/* Метка «новое» — только у того, чего в программе раньше не было. */
.tag-new {
  display: inline-block; padding: 1px 7px; border-radius: var(--radius-pill);
  background: #FFF4C2; border: 1px solid #F0DE8A; color: #7A5B00;
  font-size: 12px; font-weight: 700; letter-spacing: 0.02em; vertical-align: middle;
}

/* Разбор совпадения по признакам: что сошлось, что похоже, что разошлось.
   Плюс и минус в начале строки читались как список покупок — цвет понятнее. */
.match-signals { display: flex; flex-direction: column; gap: var(--space-0); margin: 8px 0 2px; }
.match-signal { display: flex; align-items: baseline; gap: var(--space-1); font-size: var(--fs-sm); }
.match-mark {
  display: inline-grid; place-items: center; flex: none; width: 18px; height: 18px;
  border-radius: 50%; font-size: 12px; font-weight: 700; line-height: 1;
}
.match-signal.ok .match-mark { background: var(--ok-bg); color: var(--ok); }
.match-signal.weak .match-mark { background: var(--warn-bg); color: var(--warn); }
.match-signal.bad .match-mark { background: var(--crit-bg); color: var(--crit); }
.match-signal.bad { color: var(--crit); }

/* ---------- Полоса маршрута на странице шага ----------
   Восемь коротких кнопок вместо колонки: аудитор видит, где он в процессе,
   не теряя ширину рабочей области. Цвета — те же пять состояний, что в списке. */
/* Маршрут проверки — полоса в одну строку тем же языком, что и меню:
   рамка и радиус как у карточки, сегмент как пункт меню (высота 40,
   --radius-sm, активный — мягкая подсветка, а не залитая плашка).
   Сначала это была россыпь пилюль (переносились как придётся), потом сетка
   в четыре ряда на узком экране; таблетка с радиусом 999 из общего языка
   выбивалась. Названия показываем, когда полоса их вмещает: меряем саму
   полосу, а не окно, — при открытом меню и на узком экране ширина разная. */
.route-bar {
  display: flex; align-items: center; gap: var(--space-0); overflow-x: auto;
  padding: var(--space-0); margin-bottom: var(--space-3);
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg);
  scrollbar-width: none;
}
.route-bar::-webkit-scrollbar { display: none; }
/* Зазор между номером и подписью — самый тесный шаг шкалы: на странице шага
   полоса уже, чем в карточке проверки, и лишние 5 px на сегмент отнимали у
   подписей место — примерка снимала их и оставляла восемь голых номеров. */
.route-pill {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-0);
  flex: 1 1 auto; min-width: 24px; min-height: 42px; padding: 0 var(--space-0); cursor: pointer;
  border: 0; border-radius: var(--radius-sm); background: transparent;
  color: var(--nav-item); font-family: inherit; font-size: var(--fs-sm); font-weight: 400;
  transition: background .12s, color .12s;
}
.route-pill:hover { background: var(--surface-2); color: var(--brand); }
.route-pill-num {
  display: inline-grid; place-items: center; width: 22px; height: 22px; flex: none;
  border-radius: 50%; background: var(--surface-2); color: var(--text-muted);
  font-size: var(--fs-xs); font-weight: 600; font-variant-numeric: tabular-nums;
}
/* Пока названия не помещаются, подписан текущий шаг: он и нужен, остальные
   объясняет подсказка при наведении и заголовок под полосой. */
.route-pill-title { display: none; white-space: nowrap; }
.route-pill.active { flex: 0 0 auto; background: var(--info-bg); color: var(--brand); font-weight: 600; }
.route-pill.active .route-pill-title { display: inline; }
.route-pill.active .route-pill-num { background: var(--brand-bg-soft); color: var(--brand); }
.route-pill.done .route-pill-num { background: var(--ok-bg); color: var(--ok); }
.route-pill.work .route-pill-num { background: var(--info-bg); color: var(--brand); }
.route-pill.wait .route-pill-num { background: var(--warn-bg); color: var(--warn); }
.route-pill.problem .route-pill-num { background: var(--crit-bg); color: var(--crit); }
/* Хватает места на все восемь подписей — показываем их: на широком экране
   полоса из одних номеров оставляла между ними по 150 px пустоты.
   Класс вешает js/views/engagement.js: он примеряет подписи и оставляет их,
   если полоса не переполнилась. Порог в пикселях тут не годится — ширина
   подписей зависит от шрифта и масштаба, а контейнерный запрос CSS сработал
   не во всяком браузере: на боевом сервере подписи так и не появились. */
.route-bar.with-titles .route-pill-title { display: inline; }
/* Растут, но не сжимаются: со сжатием сегменты ужимались под ширину полосы,
   подписи налезали на края, а переполнения не возникало — и примерка считала,
   что подписи поместились. */
.route-bar.with-titles .route-pill { flex: 1 0 auto; }
/* Две строки лучше восьми голых номеров. В полуразвёрнутом окне (960 px)
   полосе достаётся 650 px, а подписям нужно 1020: в одну строку они не идут.
   Класс вешает примерка в js/ui.js, когда две строки помещаются. */
.route-bar.route-wrap { flex-wrap: wrap; overflow-x: visible; row-gap: var(--space-0); }
/* В две строки сегменты не растягиваются. С `flex: 1 0 auto` остаток ширины
   делился внутри каждой строки, и второй ряд из трёх шагов расходился по всей
   полосе рядом с плотным первым — ряд читался как брак (снимок 900 px,
   23.09.2026). Своя ширина у каждого сегмента: строки идут как текст.
   Одного правила хватает: классов в нём столько же, сколько у .with-titles
   выше, а при равной специфичности побеждает записанное ниже — проверено
   рендером. */
.route-bar.route-wrap .route-pill { flex: 0 0 auto; }
/* Шапка страницы шага: заголовок слева, переходы по шагам справа. */
.step-page-head { align-items: flex-start; }
/* Полоса шагов и переходы под ней — один блок: между ними шаг меньше, чем
   до содержимого шага, иначе кнопки читаются как часть карточки ниже. */
.step-route { margin-bottom: var(--space-3); }
.step-route .route-bar { margin-bottom: var(--space-2); }
@media (max-width: 900px) {
  /* Секция внутри секции добавляла свои 15 px с каждой стороны: от окна 700 px
     до таблицы сверки доходило 540, а ей нужно 565 — таблица уезжала под
     прокрутку на пустых полях. Внешняя секция поля уже дала. */
  .sec-body .sec .sec-body { padding-left: 0; padding-right: 0; }
  /* Кнопки шапки проверки («План и риски», «Карточка клиента», …) шли одним
     непереносимым рядом в 483 px и возили вбок всю страницу при 390. */
  .eng-head-actions { flex-wrap: wrap; max-width: 100%; }
  /* На телефоне шаг маршрута читается целиком: «Шаг 4. Проверк…» вместо
     названия — это загадка, а не подпись. Места вниз хватает. */
  .step-title, .step-fact { white-space: normal; overflow: visible; text-overflow: clip; }
  /* Состояние уходит под название: в одной строке на телефоне названию
     оставалось 115 px, и «Отчётность и ОСВ» ломалось на три строки. */
  .step { flex-wrap: wrap; }
  .step-open { flex: 1 1 100%; }
  .step-state { margin-left: 30px; text-align: left; }
}

/* ---------- Заголовок страницы ---------- */
.page-head {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: var(--space-3); flex-wrap: wrap; margin-bottom: var(--space-3);
}
.page-head h1 { letter-spacing: -0.015em; }
.page-sub { color: var(--text-muted); font-size: var(--fs-sm); margin-top: 6px; }
/* Минимальная цель нажатия по WCAG 2.2 — 24×24. Строчная ссылка давала 20 px
   высоты: на сенсорном экране в неё попадают не с первого раза. */
.back { font-size: var(--fs-sm); display: inline-flex; align-items: center;
        gap: var(--space-0); min-height: 24px; margin-bottom: 2px; }

/* ---------- «Что делать сейчас»: одно действие, а не список ---------- */
.next-action {
  background: var(--bg); border: 1px solid var(--line); border-left: 4px solid var(--brand);
  border-radius: var(--radius); padding: var(--space-2) var(--space-3); margin-bottom: var(--space-3);
}
.next-action.wait { border-left-color: var(--warn); }
.next-action.problem { border-left-color: var(--crit); }
.next-action.done { border-left-color: var(--ok); }
.next-label {
  font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--text-muted);
}
.next-title { font-size: var(--fs-h4); font-weight: 700; margin: 3px 0; }
.next-hint { font-size: var(--fs-sm); color: var(--text-muted); }

/* ---------- Карточки и колонки ---------- */
/* Соседи ряда — одной высоты: при align-items: start правая карточка
   обрывалась выше левой, и под ней оставалась дыра в треть экрана. */
.columns {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: var(--space-2); align-items: stretch;
}
.columns > * { min-width: 0; }
/* Маршрут — это семь коротких строк, а запросов бывает под сотню. Делить экран
   пополам расточительно: названия пунктов обрезались на трети слова. */
/* Маршрут держит название шага в одну строку и пояснение под ним. В прежней
   ширине «Проверка элементов» и «Нетиповые операции» переносились, а списку
   состояний оставалась половина карточки. */
.columns-route { grid-template-columns: minmax(340px, 420px) minmax(0, 1fr); }
@media (max-width: 1200px) { .columns-route { grid-template-columns: 1fr; } }
.card {
  background: var(--bg); border: 1px solid var(--line);
  border-radius: var(--radius); padding: var(--space-3); box-shadow: var(--shadow-sm);
}
/* Соседние карточки экрана — одной ширины. Раньше ширина задавалась числом
   прямо в разметке (640 и 900 px), и правые края шли лесенкой. */
.int-card-next { margin-top: var(--space-2); }
/* Короткое числовое поле вне сетки: ширину задаёт правило, а не разметка. */
.catalog-due { max-width: 260px; margin-top: 12px; }

.card-head {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: var(--space-2); flex-wrap: wrap; margin-bottom: var(--space-2);
}
.card-head small { color: var(--text-muted); font-size: var(--fs-xs); flex-basis: 100%; }

/* Тулбар списка проверок: поиск + фильтр + сортировка. */
/* Заголовок списка и фильтры — одной строкой. Раньше заголовок стоял этажом
   выше, а сортировка не помещалась в ряд с поиском и падала на третий этаж:
   до первой проверки уходило 120 px пустоты. */
.list-head {
  display: flex; align-items: center; gap: var(--space-2);
  margin: var(--space-3) 0 var(--space-1); flex-wrap: wrap;
}
.list-head .block-title { margin: 0; flex: none; }
.list-head .eng-toolbar { margin: 0; flex: 1 1 420px; justify-content: flex-end; flex-wrap: nowrap; }
/* Не переносим, а сжимаем: при переносе строка инструментов вставала в два
   этажа уже на 1100 px. Списки ужимаются до 92 px и обрезают подпись
   многоточием — до 700 px всё держится в одну строку. */
.list-head .eng-toolbar select { min-width: 92px; }
.eng-toolbar { display: flex; gap: var(--space-1); flex-wrap: wrap; margin-bottom: var(--space-2); }
.eng-toolbar #engSearch { flex: 1; min-width: 200px; }
/* Поиск клиентов не растягиваем на всю строку: иначе сортировка и счётчик
   переносятся под него и строка инструментов занимает два этажа. */
.eng-toolbar #clientSearch { flex: 0 1 340px; min-width: 200px; }
.eng-toolbar .muted-note { align-self: center; }
/* Списки сжимаются, но не ниже читаемого: на 1100 px три подписи целиком в ряд
   не встают, поэтому текст обрезается многоточием самим браузером. */
.eng-toolbar select { width: auto; min-width: 0; flex: 0 1 auto; text-overflow: ellipsis; }
.list-head .eng-toolbar #engSearch { flex: 1 1 180px; min-width: 120px; }
/* Строка сжимается двумя ступенями, а не переносится. Замер: панели нужно
   606 px (поиск 120 + три списка по 152 + зазоры), заголовок с зазором съедает
   ещё 126.
   1180…1080 — режем ширину списков: «Группировать по состоянию» просило 233 px,
   выбранное значение браузер закрывает многоточием сам.
   Уже 1080 — прячем заголовок «Проверки отдела»: он повторяет то, что и так
   видно по столбцам таблицы под ним. Для чтения с экрана он остаётся на месте. */
@media (max-width: 1180px) {
  .list-head .eng-toolbar select { max-width: 152px; }
  .list-head .eng-toolbar #engSearch { flex: 1 1 120px; }
}
@media (max-width: 1080px) {
  .list-head .block-title {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
}

/* На телефоне одной строкой уже не выходит: заголовок отдельно, фильтры под ним. */
@media (max-width: 700px) {
  .list-head { flex-direction: column; align-items: stretch; gap: var(--space-1); }
  /* flex-basis в колонке читается как ВЫСОТА: 420 px превращали шапку списка
     в пустой блок 450 px. Высоту здесь задаёт содержимое. */
  .list-head .eng-toolbar { flex: 0 0 auto; flex-wrap: wrap; justify-content: flex-start; }
}


/* Навигация по шагам в панели: идём по процессу линейно. */
.step-head { flex-wrap: wrap; gap: var(--space-1); }
/* Заголовок шага и строка «что тут делают» — один блок: иначе навигация
   по шагам прижимается к пояснению и шапка читается как два разных элемента. */
.step-headline { flex: 1 1 320px; min-width: 0; }
.step-lead {
  /* 90ch, а не 70: при 14-м кегле 70 символов — это 490 px, и на широком экране
     пояснение к шагу выглядело узкой колонкой рядом с пустотой. Верхняя граница
     всё равно нужна: строка во весь экран читается хуже. */
  margin-top: 6px; max-width: 90ch;
  font-size: var(--fs-sm); color: var(--text-muted); line-height: var(--lh-base);
}
/* Переносится кнопка целиком, а не текст внутри неё: на 375 px трём элементам
   не хватало 4 px, и подписи рвались по стрелке — «←» на одной строке,
   «Предыдущий» на другой, высота кнопки 60 px вместо 39. */
/* Переход к соседнему шагу — ПОД работой, во всю ширину страницы: «назад» к
   левому краю, «дальше» к правому. Над работой он повторял маршрут, который и
   так стоит выше, а безымянные «Предыдущий» и «Следующий» не говорили, куда
   ведут. Пустой <span> держит место отсутствующей кнопки на крайних шагах,
   иначе одинокая «Дальше» уезжает влево. */
.step-hop {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-1); flex-wrap: wrap;
}
.step-hop-btn { justify-content: center; }
.step-hop-btn.forward { flex-direction: row; }

/* Действие недоступно этой роли (помощник и переписка с клиентом). Кнопку видно,
   но нажать нельзя — иначе человек жмёт и получает отказ вместо результата.
   Метка выбора файла — это <label>, атрибут disabled на неё не действует. */
/* Одна высота у кнопки независимо от тега и вида. Три причины разнобоя, найденные
   замером: <button> и <label class="btn"> считают межстрочную высоту по-разному;
   у .btn есть рамка, а у .btn-cta её не было (ровно 2 px разницы); кнопка выбора
   файла — <label> внутри .field, и ей доставался кегль подписи поля.
   Заодно кнопки стали ниже: 50 px в ряду с полями смотрелись тяжеловесно. */
.btn, .btn-cta { line-height: 1.2; padding: 11px 18px; }
.btn-cta { border: 1px solid transparent; }
.field label.btn { font-size: var(--fs-text); }
.btn[disabled], .btn-cta[disabled], .icon-btn[disabled] { opacity: 0.45; cursor: not-allowed; }
.btn.is-locked { opacity: 0.45; cursor: not-allowed; pointer-events: none; }
/* Знакомство на пустом экране: порядок работы, а не одна кнопка в пустоте. */
.onboarding .onb-list {
  list-style: none; margin: var(--space-3) auto; padding: 0;
  max-width: 560px; text-align: left; display: grid; gap: var(--space-2);
}
.onb-step {
  display: flex; gap: var(--space-2); align-items: flex-start;
  padding: var(--space-2); border: 1px solid var(--line); border-radius: var(--radius);
}
.onb-step.now { border-color: var(--brand); background: var(--brand-bg-soft); }
.onb-step.done { opacity: .7; }
.onb-num {
  flex: 0 0 26px; height: 26px; border-radius: var(--radius-pill);
  display: grid; place-items: center; font-weight: 700; font-size: var(--fs-sm);
  background: var(--surface-2); color: var(--text-muted);
}
.onb-step.now .onb-num { background: var(--brand); color: var(--text-inverse); }
.onb-step.done .onb-num { background: var(--ok-bg); color: var(--ok); }
.onb-title { font-weight: 600; }
.onb-hint { font-size: var(--fs-sm); color: var(--text-muted); margin-top: 2px; }

/* Заголовок со значком клиента: узнать карточку по цвету быстрее, чем прочитать. */
.head-with-badge { display: flex; align-items: center; gap: var(--space-1); }

/* Строка клиента: значок и название одним блоком, ИНН под названием. */
.cname-box > div { min-width: 0; }
/* Название клиента — это ссылка в его карточку, и по ней действительно нажимают.
   Высота у неё была ровно по строке текста (18 px), а по WCAG 2.2 цель нажатия
   не меньше 24×24: с сенсорного экрана в такую строку попадают не с первого раза. */
.cname-box a { display: inline-flex; align-items: center; min-height: 24px; }
/* Тихое действие строки — кнопка без рамки: в строке уже есть «Карточка». */
.crow-start { background: none; border: none; padding: 4px 6px; font-family: inherit; }

/* Значок клиента: инициалы вместо иконки-заглушки. Цвет назначается по названию. */
.cl-badge {
  display: inline-grid; place-items: center; flex: none;
  border-radius: var(--radius-sm); font-weight: 700; letter-spacing: 0.02em;
}
.progress { height: 5px; background: var(--surface-2); border-radius: var(--radius-pill); overflow: hidden; }
.progress > span { display: block; height: 100%; background: var(--brand); }

/* ---------- Шаги маршрута ---------- */
/* Вид шага задан выше (строка с кружком, фактом и состоянием справа) — здесь
   только общее для списка. Старая раскладка карточками убрана: она дублировала
   те же классы и перебивала новую. */
.steps { display: flex; flex-direction: column; gap: var(--space-0); }

/* ---------- Запросы ---------- */
/* 7 px между пунктами читались как одна слипшаяся простыня: рамки соседних
   строк почти касались. Шаг маршрута отбит так же — списки на экране
   должны дышать одинаково. */
.reqs { display: flex; flex-direction: column; gap: var(--space-1); }
/* Внутри раздела пункты стояли вплотную: зазор был только между разделами,
   и десять строк читались одной серой простынёй. */
.reqs .sec-body { display: flex; flex-direction: column; gap: var(--space-1); }
/* Пары «подпись — значение» в две колонки. Живут в профиле сотрудника;
   у пункта запроса эта раскладка была убрана вместе с раскрытием. */
.req-facts { display: grid; grid-template-columns: 9.5rem minmax(0, 1fr); gap: var(--space-0) var(--space-2); margin: 0; }
.req-facts dt { font-size: var(--fs-xs); color: var(--text-muted); }
.req-facts dd { margin: 0; font-size: var(--fs-sm); }

/* Пункт запроса — одна строка, без раскрытия (вариант «A», выбран 03.09.2026).
   Раскрытая карточка повторяла сама себя: название стояло в шапке (обрезанное)
   и тут же целиком в теле, срок и состояние — тоже дважды. А у 18 пунктов из 39
   описания нет вовсе, и раскрытие показывало один повтор названия. */
/* Раскладка флексом с переносом, а не сеткой: ширину строки решает не окно, а
   колонка пункта — при 1280 px в окне ей достаётся 460 px, потому что справа
   закреплён маршрут. Сетка в такой колонке не переносила панель, а выдавливала
   её за край (замер: 23 элемента правее границы строки). С переносом панель
   сама уходит под текст, и никакой примерки на JS для этого не нужно. */
.req-line {
  display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--space-1);
  padding: 9px var(--space-1); margin-bottom: 8px;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--bg);
}
.req-line .req-seq { flex: 0 0 auto; }
.req-line.overdue { border-color: var(--crit); }
/* Номер пункта — тем же шрифтом, что и всё вокруг: моноширинным он выбивался
   из строки. Ровный столбец даёт font-variant-numeric, а не смена гарнитуры. */
.req-seq {
  font-weight: 700; font-size: var(--fs-sm); color: var(--text-muted);
  font-variant-numeric: tabular-nums; line-height: 1.4; padding-top: 1px;
}
/* Пояснение под названием блока — по шкале кабинета: вложенный <small>
   браузер уменьшает от родителя, и выходило 10.8 px. */
.card small, .step-panel small { font-size: var(--fs-xs); }
/* 16rem — та ширина, ниже которой название рвётся по одному-два слова в строку. */
.req-main { flex: 1 1 16rem; min-width: 0; }
/* Название переносится целиком и никогда не обрезается: это единственное, по чему
   пункт узнают. Ограничение в две строки пробовали ради ровной высоты — на 1600 px
   оно съело хвост у «Выписки из реестров акционеров …», и ровность вида оказалась
   дороже смысла. */
.req-name { font-size: var(--fs-sm); font-weight: 600; line-height: 1.4; }
/* Пояснение — одной строкой: у части пунктов оно длиной в два предложения и
   разгоняло строку на три переноса. Целиком открывается подсказкой браузера. */
.req-note {
  font-size: var(--fs-xs); color: var(--text-muted); margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Сколько документов уже приложено — счётчиком у кнопки. Отдельная строка
   «документов пока нет» повторялась почти в каждом пункте и была шумом. */
.req-count {
  display: inline-grid; place-items: center; min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: var(--radius-pill); background: var(--brand-bg-soft); color: var(--brand);
  font-size: var(--fs-xs); font-weight: 700; font-variant-numeric: tabular-nums;
}
/* Панель прижата к верху строки: по центру она уезжала вниз, как только имя
   занимало две строки, и срок переставал стоять на одной линии с названием.
   Ширины у срока и кнопки фиксированы — иначе счётчик документов сдвигает
   соседние элементы, и вертикаль столбцов ломается от строки к строке. */
.req-side {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-1);
  align-self: start; margin-left: auto;
}
/* «просрочен» стоит под датой, а не через точку после неё: в одну строку он делал
   колонку срока шире на 65 px, и в списке, где просрочена часть пунктов, состояния
   и кнопки соседних строк переставали стоять друг под другом. */
.req-side .req-due b { display: block; font-weight: 600; }
.req-side .req-due {
  font-size: var(--fs-xs); color: var(--text-muted); white-space: nowrap;
  line-height: 1.35; min-width: 7.5rem; text-align: right;
}
/* width:auto обязателен: у полей ввода в проекте задана ширина 100%, и в панели
   с переносом список состояний растягивался на всю её ширину, выталкивая
   кнопки на третий ярус (замер: строка 117 px вместо 60). */
.req-side > * { flex: 0 0 auto; }
.req-side select { width: auto; min-width: 9.5rem; font-size: var(--fs-sm); padding: 5px 26px 5px 8px; }
.req-side .btn { min-width: 8.5rem; font-size: var(--fs-sm); padding: 6px 14px; }
/* «Приложить» — это label, и таб его не берёт. Поле файла спрятано приёмом
   .file-input (видимо для клавиатуры, невидимо глазу) и стоит ПЕРЕД кнопкой,
   чтобы соседним селектором подсветить её, когда фокус пришёл на поле. Замер до
   правки: обход табом шёл «состояние → ⋯», приложить с клавиатуры было нечем. */
.req-side .file-input:focus-visible + .btn {
  outline: 2px solid var(--brand); outline-offset: 2px;
}
.text-crit { color: var(--crit); }

/* Что уже приложено — чипами прямо в строке: раньше файлы были видны только
   после раскрытия, и «приложено или нет» приходилось проверять по одному. */
.req-files { list-style: none; margin: 6px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-0); }
.req-file {
  display: inline-flex; align-items: center; gap: var(--space-0);
  padding: 2px 4px 2px 8px; border-radius: var(--radius-pill); background: var(--surface-2);
}
.req-file-name {
  max-width: 22rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: inherit; font-size: var(--fs-xs); color: var(--brand);
  background: none; border: 0; padding: 0; cursor: pointer;
}
.req-file-from { font-size: var(--fs-xs); color: var(--text-muted); }
.req-file .icon-btn { width: 22px; height: 22px; }

/* Меню «⋯»: правка и удаление. В строке им места нет, а рядом с «Приложить»
   они спорили бы с ним по весу — это редкие действия, а прикладывают часто. */
.req-more { position: relative; }
.req-menu {
  position: absolute; right: 0; top: calc(100% + 4px); z-index: 5; min-width: 190px;
  display: flex; flex-direction: column; padding: 4px;
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
}
.req-menu.up { top: auto; bottom: calc(100% + 4px); }
.req-menu button {
  text-align: left; font-family: inherit; font-size: var(--fs-sm); color: var(--text);
  background: none; border: 0; padding: 8px 10px; border-radius: var(--radius-xs); cursor: pointer;
}
.req-menu button:hover, .req-menu button:focus-visible { background: var(--surface-2); }
.req-menu button.danger { color: var(--crit); }
.req-menu-note { margin: 0; padding: 8px 10px; font-size: var(--fs-xs); color: var(--text-muted); }

@media (max-width: 700px) {
  /* На телефоне управление занимает всю ширину: перенос под строку случается сам,
     здесь только растягиваем список и кнопку, чтобы не оставалось рваного края. */
  /* Название встаёт рядом с номером: 16rem на телефоне не помещаются, и заголовок
     уезжал строкой ниже. Срок здесь читается слева — колонок, по которым его
     стоило бы равнять, на узком экране уже нет. */
  .req-main { flex: 1 1 0; }
  .req-side { width: 100%; }
  .req-side .req-due { min-width: 0; text-align: left; }
  .req-side select, .req-side .btn { flex: 1 1 auto; }
}
.icon-btn {
  border: 1px solid var(--line); background: var(--bg); color: var(--text-muted);
  width: 28px; height: 28px; border-radius: var(--radius-sm); cursor: pointer; line-height: 1;
  font-family: inherit; font-size: var(--fs-sm);
}
.icon-btn:hover { border-color: var(--brand); color: var(--brand); }
.icon-btn.danger:hover { border-color: var(--crit); color: var(--crit); background: var(--crit-bg); }
/* Раздел справочника: свёрнут, пока не понадобился. Раскрытыми все 12 разделов
   вываливают 54 строки разом, и найти нужное невозможно. */
.cat-mark {
  font-size: var(--fs-xs); color: var(--ok); font-weight: 600;
  background: var(--ok-bg); border-radius: var(--radius-sm); padding: 1px 6px; margin-left: 8px;
}

/* Пометка «сверх письма» у пунктов справочника, которых нет в официальном перечне. */
.cat-extra { font-size: var(--fs-xs); color: var(--warn); font-weight: 600; margin-left: 6px; }
.req-summary {
  margin-top: var(--space-2); padding-top: var(--space-1); border-top: 1px solid var(--line);
  font-size: var(--fs-xs); color: var(--text-muted);
}

/* ---------- Панель шага ---------- */
/* Страница шага: панель лежит прямо на фоне, как в макетах шагов. Раньше её
   заворачивали в общую карточку, и всё содержимое оказывалось карточкой внутри
   карточки — рабочая колонка шага была 698 px против 740 px на других экранах. */
#stepCard { margin-top: var(--space-2); }
.step-card-head { margin-bottom: var(--space-2); }

/* Шаг открывается кликом: вся строка — кнопка, но выпадающий список остаётся отдельно.
   Раньше здесь стоял `display: contents`, чтобы дети кнопки попали в сетку строки.
   Побочный эффект: кнопка не имела собственной геометрии — размер 0×0. Из-за этого
   к ней нельзя прокрутиться (браузер не знает, где она), а фокусу с клавиатуры нечего
   обводить. Теперь кнопка — сама сетка и занимает всю строку, кроме списка состояния. */
.step-open:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: var(--radius-sm); }
.step:hover { border-color: var(--brand-300); }
.step.active { border-color: var(--brand); box-shadow: 0 0 0 2px var(--brand-bg-soft); }

.block-title { margin: var(--space-3) 0 var(--space-1); }

/* Список проверок таблицей, а не карточками: восемь карточек занимали три ряда
   по 190 px, те же восемь строк — 320 px и читаются столбцами. Группировка идёт
   строкой-заголовком внутри той же таблицы, чтобы столбцы остались на одной
   вертикали. */
.eng-table { table-layout: auto; }
.eng-table td, .eng-table th { padding: 8px 10px; }
.eng-row { cursor: pointer; }
.eng-row:hover { background: var(--surface-2); }
.eng-row-name {
  display: inline-block; padding: 4px 0;      /* цель нажатия 24 px по высоте */
  font-weight: 600; color: var(--text); text-decoration: none;
}
.eng-row:hover .eng-row-name { color: var(--brand); }
.eng-row-step { color: var(--text-muted); }
.eng-row-late { margin-left: 6px; }
.eng-group > th {
  background: var(--surface-2); font-size: var(--fs-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted);
}
.eng-group-count { margin-left: 8px; font-weight: 400; }

/* Строка под сеткой проверок: сколько показано и кнопка «показать остальные». */
.cards-more { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-2); }
.cards-more:empty { display: none; }
.cards-more .muted-note { margin: 0; }
.cards-more .btn { margin-left: auto; }

/* Список документов шага 3 */
.doc-list { display: flex; flex-direction: column; gap: var(--space-1); }
/* Флекс, а не сетка с колонками. В сетке правая часть (метка + список +
   кнопки) занимала своё место целиком, а колонка с названием сжималась в
   остаток: замерено при окне 1100 px — название документа ужималось до 111 px
   и вставало столбиком по букве, при 760 px — до 41 px, при 420 px кнопки
   уезжали за левый край строки. Флекс переносит лишнее на новую строку. */
/* Строка документа — карточка в три яруса: заголовок с оценкой, объяснение,
   панель действий. Прежняя раскладка (всё в один flex-ряд с переносом) на широком
   экране разъезжалась: кнопки уходили на вторую строку и жались вправо, оставляя
   половину строки пустой, а объяснение читалось мелким шрифтом сбоку. */
.doc-row {
  display: flex; flex-direction: column; gap: var(--space-0);
  padding: 8px 12px; border: 1px solid var(--line); border-radius: var(--radius-sm);
}
.doc-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-2); }
.doc-head .badge { flex: 0 0 auto; margin-top: 2px; }
/* Объяснение читает человек и принимает по нему решение — это не сноска. */
.doc-explain {
  font-size: var(--fs-sm); color: var(--text); white-space: pre-line;
  background: var(--surface-2); border-radius: var(--radius-sm); padding: 8px 10px;
}
.doc-bar select { flex: 1 1 280px; min-width: 220px; }
/* Название с приметами не ужимаем ниже читаемого: 240 px — примерно строка
   «Акт №77 от 03.06.2025». Не влезло остальное — оно переносится, а не давит. */
.doc-main { flex: 1 1 240px; min-width: 0; }
/* Простая строка списка — название слева, метки и кнопки справа.
   Ярусные карточки (у них внутри .doc-head) остаются колонкой. */
.doc-row.line { flex-direction: row; align-items: center; flex-wrap: wrap; }
/* В колонке flex-basis читается как ВЫСОТА: 240 px превращали строку в пустой
   блок 266 px, и «Где стоит работа» с «Открытыми находками» вытягивались
   на 1258 px вместо 200. В колонке высоту задаёт содержимое. */
.doc-row > .doc-main { flex: 0 0 auto; }
.doc-row.line > .doc-main { flex: 1 1 240px; }
.doc-row > .badge, .doc-row > select, .doc-row > input, .doc-row > .muted-note { flex: 0 0 auto; }
/* Кнопки строки — одной группой справа, а не каждая сама по себе: с появлением
   просмотра и удаления они разъезжались по краям строки. Раскладку ряда класс
   держит сам, а не общей группой выше: он единственный из рядов ещё и прижат
   вправо, и двумя правилами на один класс это уже однажды разъехалось. Зазор —
   тот же var(--space-1), что у остальных рядов; за единством следит
   test_зазоры_только_по_шкале. */
.doc-actions {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-1);
  justify-content: flex-end; margin-left: auto;
}
.doc-name { font-size: var(--fs-text); font-weight: 700; overflow-wrap: break-word; line-height: 1.3; }
.doc-note { font-size: var(--fs-xs); color: var(--text-muted); white-space: pre-line; margin-top: 2px; }
.doc-row select { width: auto; min-width: 190px; font-size: var(--fs-sm); padding: 5px 26px 5px 8px; }
/* Замечание прошлой проверки: та же строка, плюс своя подпись «перенесено
   в этот год», которую нельзя разрывать переносом. */
.prev-finding .muted-note { white-space: nowrap; }
/* На телефоне кнопка «Перенести» рядом с двумя метками остаётся в 90 px и
   подпись на ней рвётся — отдаём ей всю ширину строки. Кнопки строки там же
   переносятся в несколько рядов: прижатые вправо, они вставали ступенькой,
   и глазу не за что зацепиться — выравниваем по левому краю. */
@media (max-width: 700px) {
  .prev-finding > button { flex: 1 1 100%; }
  /* На телефоне кнопки строки встают сеткой по две, а не рассыпаются по одной
     в ряд: иначе «Посмотреть» и «Скачать» оказываются на разных строках,
     и строка документа выглядит развалившейся. */
  .doc-bar select { flex: 1 1 100%; }
  .doc-actions { width: 100%; margin-left: 0; display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-0); }
  .doc-actions .btn, .doc-actions .btn-cta { width: 100%; justify-content: center; }
  .doc-actions .btn.secondary { grid-column: 1 / -1; }
}
/* Лоток документов — таблица, а не лента карточек. На сорока файлах карточки
   читались как поток: строки нечем сравнить между собой и нельзя взять пачкой.
   Колонки: галочка, файл, что нашли, элемент выборки, действия. */
.doc-tray { table-layout: auto; }
.doc-tray th, .doc-tray td { vertical-align: top; }
.doc-tray .tray-pick { width: 1%; padding-right: 0; }
.doc-tray .tray-pick input { width: 16px; height: 16px; margin: 3px 0 0; cursor: pointer; }
.doc-tray .tray-file { min-width: 220px; }
.doc-tray .tray-verdict { min-width: 220px; }
.doc-tray .tray-choice { min-width: 240px; }
.doc-tray .tray-choice select { width: 100%; min-width: 200px; font-size: var(--fs-sm); }
.doc-tray .tray-actions { white-space: nowrap; text-align: right; }
.doc-tray .tray-actions .btn { margin-left: 6px; }
/* Суть сверки — то, по чему принимают решение: обычный текст, а не сноска. */
.tray-why { font-size: var(--fs-sm); color: var(--text-muted); margin-top: 4px; line-height: 1.4; }
.tray-row.is-picked td { background: var(--brand-bg-soft); }
/* Врезка с разбором раскрывается под своей строкой — тем же приёмом, что
   расшифровка строки баланса: у неё свой фон и отчёркивание слева. */
/* Ячейка раскрытия: пояснение под строкой таблицы — в лотке нераспознанного
   и в сверке бланка. Правила были посимвольно одинаковыми. */
.tray-details > td,
.tb-line-explain > td {
  background: var(--surface-2); border-left: 3px solid var(--brand);
  padding-top: var(--space-1); padding-bottom: var(--space-1);
}
/* Панель пачки появляется только с выделением и держится на виду при прокрутке:
   таблица длинная, и уезжать за экран действию нельзя. */
.tray-bulk {
  position: sticky; top: 0; z-index: 2;
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  padding: 8px 12px; margin-bottom: 8px; border-radius: var(--radius-sm);
  background: var(--brand-bg-soft); border: 1px solid var(--line);
}
.tray-bulk select { flex: 0 1 420px; min-width: 260px; font-size: var(--fs-sm); }
/* Кнопки врезки начинаются от левого края: в карточке они прижимались вправо
   (margin-left: auto), а в таблице справа от них пустая половина строки. */
.tray-details .doc-actions { margin-left: 0; justify-content: flex-start; }
.tray-bulk .link-btn { margin-left: auto; }
@media (max-width: 700px) {
  .tray-bulk select, .tray-bulk .btn { flex: 1 1 100%; }
  .tray-bulk .link-btn { margin-left: 0; }
}
/* «Выделить все» стоит над таблицей, а не в её шапке: на телефоне карточный
   режим прячет шапку вместе с галочкой — а это ровно то действие, которого
   не хватало владельцу на сорока файлах. */
.tray-all {
  display: inline-flex; align-items: center; gap: var(--space-1); margin-bottom: 8px;
  font-size: var(--fs-sm); color: var(--text-muted); cursor: pointer;
}
.tray-all input { width: 16px; height: 16px; cursor: pointer; }
/* Порог тот же, что у остальных таблиц: пяти колонок в 390 px нет и быть не
   может, а вбок лоток возили при обрезанной посреди слова колонке «Что нашли». */
@media (max-width: 900px) {
  /* Врезка с разбором — обычный блок: табличная ячейка внутри блочной строки
     считается табличным алгоритмом и уезжает за карточку. */
  .doc-tray .tray-details > td { display: block; }
  .doc-tray .tray-file, .doc-tray .tray-verdict, .doc-tray .tray-choice { min-width: 0; }
  .doc-tray .tray-choice select { width: 100%; min-width: 0; }
  .doc-tray .tray-actions { white-space: normal; text-align: left; }
  .doc-tray .tray-actions .btn { margin-left: 0; }
  /* Подпись сверху, значение во всю ширину карточки. Общая раскладка «подпись
     слева — значение справа» рассчитана на короткие значения: в лотке справа
     оставалось 55 % ширины, и список обрезался на «— выберите эле», а причина
     сверки читалась столбиком по три слова. Галочка выбора короткая — её
     оставляем рядом с подписью. */
  .doc-tray td[data-label]:not(.tray-pick) { grid-template-columns: 1fr; gap: var(--space-0); }
  .doc-tray td[data-label]:not(.tray-pick) > * { grid-column: 1; }
}

/* Кандидаты при ничьей: подошло несколько строк выборки, и каждая — кнопка.
   Клик прикрепляет сразу, иначе аудитор ищет ту же строку в списке из тридцати. */
.doc-candidates { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-0); margin-top: 6px; }
.doc-candidates .candidate {
  font-size: var(--fs-xs); padding: 3px 9px; border: 1px solid var(--line);
  border-radius: 999px; line-height: 1.4; max-width: 100%; text-align: left;
}
.doc-candidates .candidate:hover { border-color: var(--link); color: var(--link); }

/* Сообщение о результате действия */
.notice {
  margin-top: var(--space-2); padding: 11px 14px; border-radius: var(--radius-sm);
  background: var(--info-bg); color: var(--text); font-size: var(--fs-sm);
  border-left: 4px solid var(--brand);
}
.notice.warn { background: var(--warn-bg); border-left-color: var(--warn); }
.notice.ok { background: var(--ok-bg); border-left-color: var(--ok); }
.notice ul { margin: 6px 0 0; padding-left: 20px; }
.notice li { margin: 3px 0; }

.inline-input { width: 130px; padding: 5px 8px; font-size: var(--fs-sm); text-align: right; }
.basis-cell { font-size: var(--fs-xs); color: var(--text-muted); max-width: 260px; }
tr.row-problem td { background: var(--crit-bg); }
.muted-note { font-size: var(--fs-xs); color: var(--text-muted); }

/* Ячейка «Ответ контрагента» на шаге 5: имя файла и «убрать» стоят в одной строке,
   поэтому имя обрезаем многоточием — длинный скан иначе растягивал всю таблицу. */
.conf-reply { white-space: nowrap; }
/* Состояние выбирают из списка, и он обязан показывать выбранное целиком: с новой
   колонкой таблица сжала его до «запланирое», а обрезанное слово читается как ошибка. */
select[data-conf-status] { min-width: 170px; }

/* На телефоне таблица подтверждений превращается в карточки: восьми колонок в 390 px
   нет и быть не может, а вбок её возили на 1025 px при видимых 316. Порог тот же,
   что у меню, — на планшете 768 таблица тоже не помещалась. Подпись каждой ячейки
   берётся из data-label — без неё цифры остались бы без объяснения. */
/* Карточный вид таблицы: подписи колонок уходят внутрь строки.
   Порог в пикселях здесь врал так же, как врал он у подписей маршрута.
   Правила висели на `@media (max-width: 900px)`, и таблица сотрудников —
   восемь колонок — в полуразвёрнутом окне 960 px оставалась таблицей: из
   1069 px было видно 608 (замер 13.09.2026). Класс `as-cards` ставит
   примерка `подогнатьТаблицы` в ui.js — ровно тем таблицам, что помечены
   `cards-narrow` и не поместились в свою обёртку. */
/* Блочными должны стать и сама таблица с телом, а не только строки: пока
   элемент остаётся display: table, ширину ему считает табличный алгоритм —
   ячейки разъехались на 1004 px и утащили вбок всю страницу. */
.as-cards, .as-cards tbody { display: block; width: 100%; }
.as-cards thead { display: none; }
.as-cards tr { display: block; border-top: 1px solid var(--line); padding: var(--space-2) 0; }
/* Ширину колонкам таблицы задают правила вроде .clients-table .cname (40%) —
   в карточке они не нужны, поэтому селектор с атрибутом перебивает их. */
.as-cards td[data-label] {
  display: grid; grid-template-columns: minmax(88px, 38%) 1fr; gap: var(--space-2);
  align-items: center; border: none; padding: 5px var(--space-2); width: auto;
}
.as-cards td[data-label]::before {
  content: attr(data-label); color: var(--text-muted);
  font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.03em;
  line-height: 1.3; white-space: normal;
}
/* Кнопка в карточке не ломается на «Приложить / акт»: короткая надпись в две
   строки читается как две разные кнопки. */
.as-cards .btn { white-space: nowrap; }
/* Длинные строки (почта сотрудника, текст ошибки, имя файла) в карточке
   переносятся, а не обрезаются: места по горизонтали нет, зато есть вниз. */
.as-cards .crow-sub, .as-cards .item-sub,
.as-cards .item-file, .as-cards .basis-cell {
  white-space: normal; overflow-wrap: break-word; max-width: 100%;
}
/* Плашка и кнопка внутри карточки — по своему тексту, а не во всю колонку:
   элемент сетки растягивается по умолчанию, и «владелец» расплывался на
   всю ширину, а «Приложить» вставало по центру. */
.as-cards td[data-label] > .badge,
.as-cards td[data-label] > .btn { justify-self: start; }
/* Число и пометка при нём («6» и «(4)» просроченных) — одна строка карточки.
   В сетке каждый ребёнок ячейки занимает свой ряд, и пометка висела отдельной
   строкой без подписи (замер 13.09.2026, экран сотрудников на 960 px). */
.as-cards td.num {
  display: flex; align-items: baseline; gap: var(--space-1); text-align: left;
}
.as-cards td.num::before { flex: 0 0 38%; min-width: 88px; }
/* Всё содержимое ячейки идёт во второй колонке: у ячейки бывает не один
   ребёнок (значок и название, метка и подпись под ней), и второй иначе
   проваливался в колонку подписи и вставал на всю ширину карточки. */
.as-cards td[data-label] > * { grid-column: 2; }
/* Колонка действий подписи не имеет: кнопки идут рядом от левого края. */
.as-cards td[data-label=""] {
  display: flex; flex-wrap: wrap; gap: var(--space-1); text-align: left;
}
.as-cards td[data-label=""]::before { display: none; }
/* Ширина поля и списка — по колонке карточки, а не по своему минимуму:
   из-за min-width списка состояния таблица вылезала за экран на 12 px. */
.as-cards .inline-input, .as-cards select { width: 100%; min-width: 0; }
/* Палец, а не курсор: список состояния был высотой 29 px — по нему
   промахиваешься. 36 px — нижняя граница удобной цели нажатия. */
.as-cards select, .as-cards .btn, .as-cards .icon-btn { min-height: 36px; }
.as-cards.conf-table .conf-reply { white-space: normal; }
.as-cards.conf-table .conf-reply .btn.ghost { max-width: 100%; }
.conf-reply label { cursor: pointer; }
.conf-reply .btn.ghost {
  max-width: 200px; overflow: hidden; text-overflow: ellipsis;
  vertical-align: middle; font-size: var(--fs-xs);
}
.conf-reply .icon-btn { margin-left: var(--space-1); vertical-align: middle; }

/* ---------- Шаг 2: выборка ---------- */
#sampleCard { margin-top: var(--space-2); }

/* Форма выборки идёт группами-шагами, а не потоком полей: раньше первый ряд был
   из четырёх полей, второй из двух, и форма выглядела собранной наспех. */
/* Ширина по содержимому, а не по экрану: на широком мониторе поля упирались
   в потолок 340px, а справа оставалась половина пустой полосы — форма выглядела
   недозаполненной. Три поля ровно делят эту ширину. */
/* Обычная форма — сетка одинаковых полей. Формы, которые раскладывают поля сами
   (выборка: группы-шаги), просят блочный режим и помечаются как grouped. Пока
   блочным был весь класс, поля соседних форм — кабинет клиента, загрузка первички,
   отбор контрагентов — растянулись в один столбец на всю ширину карточки. */
.sample-form {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-2); max-width: var(--forma-max);
}
.sample-form.grouped { display: block; max-width: var(--forma-max); }
/* Длинное поле (комментарий, инструкция клиенту) занимает строку целиком. */
.sample-form > .field.wide { grid-column: 1 / -1; }
/* Ряд действий формы занимает строку целиком: в одной колонке сетки кнопки
   переносились друг под друга и получали разную ширину — ряд выглядел рваным. */
.sample-form > .sample-form-actions { grid-column: 1 / -1; }
/* Одиночному полю ширину задаёт правило «поле слева, пояснение справа» ниже. */
.sample-form .field-grid > .field { max-width: none; }
/* Не больше трёх полей в ряд — иначе форма читается как таблица, — но пустых
   колонок не держим: `auto-fit` схлопывает лишние треки, и группа из одного поля
   занимает ряд целиком. Жёсткие `repeat(3, …)` оставляли справа до 650 px пустоты
   в шагах 4 и 5, где в группе всего одно поле. */
@media (min-width: 900px) {
  .sample-form .field-grid { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
}
.sample-form .field-group:last-of-type { margin-bottom: var(--space-2); }
/* Кнопка стоит рядом с итогом расчёта, а не отдельно внизу: так видно, что
   именно будет сформировано по нажатию. */
/* Кнопка стоит сразу за формой, а пояснение — справа от неё. Раньше ряд был
   space-between: на широком экране пояснение уходило к левому краю, кнопка —
   к правому, и между заполненным полем и действием оставался километр пустоты. */
.sample-form-actions .calc-line { margin: 0; flex: 1; }
.sample-form-actions > .muted-note { order: 9; flex: 1 1 260px; }
@media (max-width: 620px) {
  .sample-form-actions { flex-direction: column; align-items: stretch; }
}
/* Строка живого расчёта существенности — вывод, а не подпись к полю. */
.calc-line {
  margin: var(--space-1) 0 0; padding: 9px 11px; border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--text-muted); font-size: var(--fs-sm);
}
.calc-line.ready { background: var(--brand-bg-soft); color: var(--text); }

/* Карточка клиента: поля сгруппированы по смыслу, каждая группа с подзаголовком. */
.field-group { margin-bottom: var(--space-3); }
.field-group-head { margin-bottom: var(--space-1); padding-bottom: 4px; border-bottom: 1px solid var(--line); }
/* Заголовки групп формы — с номером шага и обычным регистром. Капсом и серым они
   читались как служебные подписи, и владелец не увидел на «Выборке», с чего начать:
   «непонятно, где основной функционал, когда загружаешь документ». */
.sample-form.grouped { counter-reset: шаг-формы; }
/* Заголовок группы — h3 общей шкалы плюс номер шага в кружке. Крупнее
   (18 px) он был вровень с заголовком карточки, и внутри неё группа
   читалась как самостоятельный раздел. Номер и жирность отличают его
   и без прибавки кегля. */
.field-group-head h3 { display: flex; align-items: center; gap: var(--space-1); }
.sample-form.grouped .field-group-head h3::before {
  counter-increment: шаг-формы; content: counter(шаг-формы);
  width: 26px; height: 26px; flex: none; border-radius: 50%;
  background: var(--info-bg); color: var(--brand);
  font-size: var(--fs-sm); font-weight: 700; display: grid; place-items: center;
}
/* Профиль: кнопка отделена от полей, а выбор темы не растягивается на карточку —
   в списке из трёх слов поле во всю ширину читается как незаполненная форма. */
.profile-actions { margin-top: var(--space-2); }
/* Подпись «Имя сохранено» появляется после нажатия. Пока её нет, пустой элемент
   всё равно занимал свою долю строки — на узком экране это 250 px пустоты между
   кнопкой и следующим блоком. */
.profile-actions .muted-note:empty { display: none; }
.profile-theme { max-width: 260px; }

/* Разрыв между смысловыми группами, а не только линия под заголовком.
   Владелец: «некоторые блоки сливаются с друг другом». Так и было: линия шла
   ПОД заголовком группы и привязывала к нему описание, а конец предыдущей
   группы ничем не закрывался — на шагах «Выборка», «Акты сверки» и «Нетиповые
   операции» подсказка одной группы упиралась в заголовок следующей, и где
   кончается первая, было не видно. Линию переносим между группами: заголовок
   здесь и так крупный, с номером в кружке, — подчёркивать его нечем. */
.sample-form.grouped .field-group-head { border-bottom: 0; padding-bottom: 0; }
.sample-form.grouped .field-group + .field-group {
  border-top: 1px solid var(--line);
  padding-top: var(--space-3);
  margin-top: var(--space-3);
}
.field-group-head small { display: block; color: var(--text-muted); margin-top: 2px; }
/* Пояснение группы раскрывается значком «!» в заголовке: постоянный текст в три
   строки отодвигал поля вниз, и на «Выборке» первый экран уходил под объяснения. */
.field-group-head .hint-text { margin-top: 6px; }
.field-group .hint-row .hint-text { margin-top: 6px; }
/* Поля одной ширины, а не «сколько осталось»: при растяжении по остатку
   строка из двух полей выглядела иначе, чем строка из трёх, и форма казалась
   собранной наспех. Широкие поля по-прежнему занимают строку целиком. */
/* Поля одного ряда стоят на одной линии — подпись к подписи, поле к полю.
   Раньше каждое поле было отдельной колонкой сетки со своей внутренней вёрсткой:
   подпись в две строки («Краткое наименование клиента из карточки») опускала своё
   поле ниже соседних, а пояснение под одним полем сдвигало весь следующий ряд.
   На раскрытом письме это читалось как кривая форма. Через subgrid дорожки
   подписи, поля и пояснения общие для всего ряда. */
@supports (grid-template-rows: subgrid) {
  .sample-form:not(.grouped) > .field,
  .tb-form > .field,
  .field-grid > .field:not(:only-child) {
    display: grid;
    grid-row: span 3;
    grid-template-rows: subgrid;
    row-gap: var(--space-0);
  }
  /* Пояснения нет — дорожка остаётся пустой, ряд от этого не съезжает. */
  .sample-form:not(.grouped) > .field > *:last-child,
  .tb-form > .field > *:last-child,
  .field-grid > .field:not(:only-child) > *:last-child { margin-bottom: 0; }
}

.field-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: var(--space-2);
}
/* Поля делят ряд поровну и занимают его целиком. Потолок ширины поля здесь был
   дважды (340, потом 480) — и оба раза оставлял справа пустую ступеньку, когда
   полей в ряду меньше, чем колонок. Разбегание полей на сверхшироком мониторе
   держит не потолок поля, а потолок самой формы (--forma-max). */
.field-grid > .field { max-width: none; }
.field-grid > .field.wide { max-width: none; }
/* Длинные значения (наименование, адрес, должность) — на всю ширину, чтобы не обрезались. */
.field-grid .field.wide { grid-column: 1 / -1; }

/* Группа из одного поля: пояснение переезжает вправо от ввода. Иначе справа
   оставалась пустая половина ряда — на широком экране до 650 px «дыры»,
   и блок выглядел недозаполненным. */
@media (min-width: 900px) {
  .field-grid > .field:only-child {
    max-width: none;
    display: grid; column-gap: var(--space-2); align-items: start;
    grid-template-columns: minmax(0, 420px) minmax(0, 1fr);
  }
  /* Подпись поля — либо сам <label>, либо ряд «подпись + значок подсказки».
     Пока в первой строке ждали только <label>, ряд со значком попадал в общий
     разбор «всё остальное» и ложился в ту же клетку, что и кнопка: на шаге 5
     подпись «ОСВ по счёту расчётов» была нарисована поверх «Выбрать ведомость». */
  .field-grid > .field:only-child > label,
  .field-grid > .field:only-child > .hint-row { grid-column: 1; grid-row: 1; }
  .field-grid > .field:only-child > small { grid-column: 2; grid-row: 1 / span 2; align-self: center; }
  .field-grid > .field:only-child > :not(label):not(small):not(.hint-row) { grid-column: 1; grid-row: 2; }
  /* Выбор файла: первая колонка по содержимому, а не жёсткие 420 px. Кнопка
     с именем файла занимала 257 px, и между ней и пояснением зияли 163 px —
     подпись читалась как чужая, приклеенная к правому краю. */
  .field-grid > .field.file-field:only-child {
    grid-template-columns: fit-content(420px) minmax(0, 1fr);
  }
}

.sample-summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--space-2); }
/* Ряд заполняется целиком: `auto-fit` при 900 px оставлял три плитки в ряду
   и одну сиротой на втором этаже. Четыре показателя делятся на 2×2 без остатка. */
@media (max-width: 1180px) {
  .sample-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* На шаге «Итог» показателей пять, и пятый вис сиротой в половину ряда
     (снимок боевого вида 23.09.2026, ширина 900). Последняя плитка при
     нечётном числе занимает ряд целиком — ряды снова заполнены. */
  .sample-summary > .sum-card:last-child:nth-child(odd) { grid-column: 1 / -1; }
}
@media (max-width: 560px) {
  .sample-summary { grid-template-columns: minmax(0, 1fr); }
}
.sum-card { position: relative; border: 1px solid var(--line); border-radius: var(--radius); padding: var(--space-1) var(--space-2); background: var(--bg); box-shadow: var(--shadow-sm); }
.sum-card.accent { border-color: #E7C9C4; background: var(--crit-bg); }
.sum-card.accent .sum-value { color: var(--crit); }
/* Плитка показателя повторяет плитку основной программы: подпись обычным
   регистром, крупное число в том же шрифте с ровными цифрами, значок в углу.
   Капс и моноширинный шрифт делали наши плитки узнаваемо «другими». */
.sum-label { font-size: var(--fs-sm); font-weight: 500; color: var(--text-muted); padding-right: 34px; }
/* Число на ступень ниже: 24 px рядом с заголовком блока в 13 px кричали,
   и полоса показателей забирала первый экран. */
.sum-value { font-size: var(--fs-h3); font-weight: 600; font-variant-numeric: tabular-nums;
             margin: 2px 0 1px; letter-spacing: -0.01em; }
/* Показатель словами, а не числом: моноширинный шрифт делал из фразы телеграмму.
   Кегль тот же, что у чисел: в одном ряду «черновик» на ступень мельче тройки
   читался как менее важный, хотя это главный показатель плана. */
.sum-value.words { font-family: var(--font-brand); font-weight: 600; }
.sum-hint { font-size: var(--fs-xs); color: var(--text-muted); }
/* В кабинете подписи показателей идут обычным регистром, как в референсе:
   четыре заглавных строки подряд на первом экране читались как шум. В панелях
   шага заглавные остаются — там табло стоит среди таблиц и его надо отделить. */
.sample-summary.plain .sum-label { text-transform: none; letter-spacing: 0; font-weight: 600; font-size: var(--fs-sm); }

/* Границы машинной проверки: что проверено, что программа не проверяет вовсе и
   что осталось незакрытым — тремя колонками рядом. Раньше это лежало в свёрнутом
   разделе и в жёлтой плашке: список ограничений видели единицы, а он и есть
   главная оговорка проверки. */
.scope-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--space-2); margin-top: 16px; }
.scope-card {
  border: 1px solid var(--line); border-radius: var(--radius); padding: var(--space-2);
  background: var(--bg); box-shadow: var(--shadow-sm);
}
.scope-card h3 { margin: 0 0 8px; }
/* Одна карточка в ряду: авто-раскладка растянула бы её на три колонки шириной
   в экран, и короткий список читался бы как обрывок таблицы. */
.scope-cards.one { grid-template-columns: 1fr; }
.scope-card.ok { border-color: var(--ok-bg); }
.scope-card.warn { border-color: #E7C9C4; background: var(--crit-bg); }
.scope-card .help-limits { margin: 0; }

/* «Почему выбраны эти документы» — подробности прячем, но они всегда под рукой */
/* Своё у «почему так посчитали» — только цвет заголовка и отступы текста:
   рамка и стрелка общие для всех раскрывающихся блоков. */
.sample-why > summary { font-weight: 600; font-size: var(--fs-sm); color: var(--link); }
.sample-why > summary::before { color: var(--link); }
.sample-why p { font-size: var(--fs-sm); color: var(--text-muted); margin: var(--space-1) var(--space-2) var(--space-2); }
.sample-why .muted-note { font-size: var(--fs-xs); }

/* contain: paint обязателен. Без него таблица выборки (сто с лишним строк, ~10 000 px)
   хоть и прокручивается внутри себя, но добавляет свою полную высоту к прокрутке
   страницы: аудитор крутил колесо и уезжал на девять экранов в пустое поле.
   Проверено замером: scrollHeight страницы 10 671 px при высоте содержимого 1 316 px. */
/* Список выше своего окна и режет нижнюю строку пополам — со стороны это брак
   вёрстки, а не «дальше есть ещё». Внутренняя тень у края появляется, только пока
   ниже что-то осталось: класс ставит подсказкаПрокрутки() из js/ui.js.
   Фоновым градиентом это не решается — у строк таблицы свой фон, он его закрывает. */
.sample-table { max-height: 460px; overflow-y: auto; contain: paint; }
.sample-table.has-more { box-shadow: inset 0 -16px 14px -14px rgba(30, 26, 64, 0.35); }
.sample-table th { position: sticky; top: 0; z-index: 1; }
.item-doc { font-weight: 600; }
.item-sub { font-size: var(--fs-xs); color: var(--text-muted); margin-top: 2px; }
.sample-table select { width: auto; min-width: 150px; font-size: var(--fs-sm); padding: 4px 25px 4px 7px; }
/* Смена исходной выгрузки — у названия файла, под плитками: внизу, под таблицей
   в шестьсот строк, эту кнопку просто не находили и считали, что новую выборку
   сделать нельзя. */
.sample-source { margin: var(--space-2) 0; }
.sample-actions { margin-top: var(--space-2); }

/* Выгрузка рабочего документа шага: кнопка и объяснение, что за лист получится.
   Отдельным блоком, а не в строке формы: в узкой колонке строка формы сжимала
   подпись до одного слова в строке. */
.workpaper-block {
  margin-top: var(--space-3); padding-top: var(--space-2);
  border-top: 1px solid var(--line);
}
.workpaper-block .muted-note { margin: var(--space-1) 0 0; }

/* ---------- Значки состояния ---------- */
.badge {
  display: inline-flex; align-items: center; padding: 3px 9px; border-radius: var(--radius-pill);
  font-size: var(--fs-xs); font-weight: 700; white-space: nowrap;
}
.badge.idle { background: var(--idle-bg); color: var(--idle); }
.badge.work { background: var(--info-bg); color: var(--info); }
.badge.wait { background: var(--warn-bg); color: var(--warn); }
.badge.done { background: var(--ok-bg); color: var(--ok); }
.badge.problem { background: var(--crit-bg); color: var(--crit); }

/* Призрачная кнопка в токенах — прозрачная рамка и серый текст: в ряду рядом с
   обычной кнопкой она читалась как подпись, а не как действие. На шаге «Итог»
   из-за этого было неясно, что «Выполнено» и «Не применимо» вообще нажимаются.
   Тонкая рамка возвращает кнопке вид кнопки, не утяжеляя экран. */
.btn.ghost { border-color: var(--line); color: var(--text); }
.btn.ghost:hover, .btn.ghost:focus-visible { border-color: var(--line-strong); background: var(--surface); }

/* Опасное действие: удаление. Не кричит цветом в покое, краснеет при наведении. */
.btn.ghost.danger { color: var(--crit); }
.btn.ghost.danger:hover { background: var(--crit-bg); border-color: var(--crit); }

/* ---------- Таблица и панель инструментов ---------- */
.scroll-x { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg); }
.table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.table th {
  background: var(--surface-2); color: var(--text-muted); font-size: var(--fs-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.03em; text-align: left; padding: 9px 12px;
}
.table td { padding: 10px 12px; border-top: 1px solid var(--line); }
.table td.num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
/* Заголовок числового столбца стоит над своими числами: шапка прижималась
   влево, а числа вправо — «Пунктов» и «6» читались как разные столбцы. */
.table th.num { text-align: right; }
/* Строка проверяемого года — та, ради которой аудитор и смотрит таблицу. */
.table tr.row-accent td { background: var(--brand-bg-soft); font-weight: 600; }

/* ---------- Переключатель оформления ---------- */
.theme-switch { margin: var(--space-2) 0; }
.theme-switch label {
  display: block; font-size: var(--fs-xs); color: var(--text-muted); margin-bottom: 4px;
}
.theme-select { width: 100%; font-size: var(--fs-sm); }
/* На экране входа переключатель стоит под карточкой — своей ширины, по центру. */
.login-screen .theme-switch { max-width: 400px; width: 100%; margin: var(--space-2) auto 0; }
/* В кабинете клиента — служебная мелочь у правого края над подвалом, а не
   отдельный блок во всю ширину: клиент пришёл сдавать документы, а первым
   самостоятельным элементом под списком у него стоял выбор оформления. */
.portal .theme-switch {
  display: flex; align-items: center; justify-content: flex-end; gap: var(--space-1);
  margin: var(--space-3) 0 0;
}
.portal .theme-switch label { margin-bottom: 0; }
.portal .theme-select { width: auto; min-width: 150px; }

/* ---------- Справочник пунктов запроса (шаг 0) ---------- */
/* Отступ между разделами справочника задаёт `gap`. Общая маржа `.cat-section`
   здесь складывалась с ним: у колонки-флекса маржи не схлопываются, и между
   строками высотой 48 px оказывалось 35 px воздуха — на экран влезало семь
   разделов из двадцати. */
.catalog { display: flex; flex-direction: column; gap: var(--space-1); }
.catalog > .cat-section { margin: 0; }
/* Раздел справочника: рамка и заголовок общие, свой только фон — он отделяет
   разделы от списка пунктов внутри. */
.cat-section { background: var(--surface); }
.cat-section > .cat-head { font-weight: 700; font-size: var(--fs-sm); }
/* min-width:0 обязателен: без него длинное название раздела не сжимается, строка
   заголовка становится шире экрана и на телефоне вся страница уезжает вбок
   (замер на 390 px: прокрутка 28 px из-за счётчика, вытолкнутого за край). */
/* Отмечают раздел нажатием по всей строке, а высота у неё была по тексту: у коротких
   названий («12. Прочее») — 20 px, у длинных, в две строки, — 40. Цель нажатия
   не должна зависеть от длины названия и не должна быть меньше 24 px (WCAG 2.2). */
.cat-all { cursor: pointer; flex: 1; min-height: 24px; }
/* break-word, а не anywhere: от вылезания за экран защищают оба одинаково, но
   anywhere вдобавок объявляет наименьшей возможной шириной одну букву — и flex
   честно сжимал заголовок раздела до неё, отдавая место метке «обязательный
   минимум». На телефоне это выглядело как столбик из слогов: «Учред итель ные».
   Замер 10.09.2026: 12 разорванных слов в колонке 40 px. */
.cat-all > span { min-width: 0; overflow-wrap: break-word; }
.cat-count { color: var(--text-muted); font-weight: 400; font-size: var(--fs-xs); }
/* Метка и счётчик — одним куском справа от названия. Пока они были двумя
   самостоятельными элементами строки, на телефоне приходилось выбирать между
   «плашка во всю ширину» и «название столбиком по слогам». */
.cat-meta { display: flex; align-items: center; gap: var(--space-1); flex: none; }
@media (max-width: 700px) {
  /* На телефоне метка «обязательный минимум» занимала 150 px из 326 и не
     сжималась, а название раздела сжималось (flex: 1, min-width: 0) — до
     колонки в 40 px, где «Учредительные» ложится столбиком по слогам.
     Название важнее: отдаём ему всю строку, метку и счётчик уводим под него. */
  .cat-section > .cat-head { flex-wrap: wrap; }
  /* min-content — граница, ниже которой название сжимать нельзя: ширина самого
     длинного слова. Упёрлись в неё — метка со счётчиком уходят на строку ниже,
     а короткие названия остаются в одну строку. */
  .cat-all { min-width: min-content; }
  .cat-meta { margin-left: auto; }
  .cat-mark { margin-left: 0; }
}
.cat-items { padding: 0 var(--space-2) var(--space-2); display: flex; flex-direction: column; gap: var(--space-0); }
.cat-item {
  display: flex; gap: var(--space-1); align-items: start; padding: 6px 8px;
  border-radius: var(--radius-sm); font-size: var(--fs-sm); cursor: pointer;
  min-height: 24px;
}
.cat-item:hover { background: var(--surface-2); }
.cat-item.done { opacity: .6; cursor: default; }
/* Размер флажка здесь не задаём: он общий на всю программу (18 px, раздел
   «Поля формы»). Раньше стояло `width: auto` против растягивания общим
   правилом `input { width: 100% }` — оно до флажков и не доходило, зато
   держало их браузерными 13 px, вдвое мельче тех же галочек в задачах. */
.cat-item input { margin-top: 3px; }
.cat-body { flex: 1; min-width: 0; }
.cat-title { display: block; }
.cat-note { display: block; font-size: var(--fs-xs); color: var(--text-muted); }

.letter { margin-top: var(--space-2); }
.letter-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
.letter textarea {
  width: 100%; font-family: var(--font-mono); font-size: var(--fs-sm); line-height: 1.5;
  padding: var(--space-2); border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); color: var(--text); resize: vertical;
}

/* ---------- Переписка с клиентом ----------
   Общие стили: их использует и кабинет клиента (portal.html), и панель шага 0
   у аудитора. Держим в одном месте, чтобы переписка выглядела одинаково. */
/* Та же мера строки, что у пояснения к шагу (.step-lead): без потолка второй
   абзац растягивался на всю ширину экрана, стоял под узким первым — и пара
   абзацев читалась как сбитая вёрстка. */
.pitem-desc { font-size: var(--fs-sm); color: var(--text-muted); margin-top: 6px; max-width: 90ch; }
.pmsgs { margin-top: var(--space-2); display: flex; flex-direction: column; gap: var(--space-0); }
.pmsg { font-size: var(--fs-sm); padding: 8px 11px; border-radius: var(--radius-sm); max-width: 90%; }
.pmsg.client { background: var(--surface-2); align-self: flex-end; }
.pmsg.auditor { background: var(--info-bg); }
.pmsg-who { font-size: var(--fs-xs); color: var(--text-muted); margin-bottom: 2px; }
/* Отступ сверху — из шкалы: рядом соседние блоки пункта отбиты на 15, и
   восьмёрка читалась как случайная. Зазор между полем и кнопкой — свой. */
.pmsg-form { display: flex; gap: var(--space-1); margin-top: var(--space-2); }
.pmsg-form input { flex: 1; }

.thread {
  display: flex; flex-direction: column; gap: var(--space-0); background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: var(--space-2); margin-bottom: var(--space-2);
}
/* Разговор не про конкретный пункт — первым и с пометкой: среди обсуждений
   по файлам вопрос «можно привезти оригиналы?» иначе теряется. */
.thread-general { border-left: 3px solid var(--brand); padding-left: var(--space-2); }
.thread-general .thread-title { color: var(--brand); }

.thread-title { font-size: var(--fs-sm); font-weight: 700; }
/* У аудитора стороны зеркальны: клиент — собеседник слева, мы — справа. */
.thread .pmsg.client { align-self: flex-start; }
.thread .pmsg.auditor { align-self: flex-end; }

/* ---------- Пустые состояния и загрузка ---------- */
.empty, .loading { text-align: center; padding: var(--space-5) var(--space-3); color: var(--text-muted); }
.empty h2 { color: var(--text); margin-bottom: 6px; }
.empty p { margin: 0 auto var(--space-2); max-width: 46ch; }
/* Строка «пока ничего нет» тянулась во всю ширину карточки — на 1240 px строка
   читается хуже абзаца книги. Держим её колонкой по центру. */
/* Пустое состояние — такой же блок, как всё остальное на экране, и по левому
   краю. Центрированный абзац без рамки висел посреди белого поля: соседний текст
   идёт слева, а этот — по центру, и глазу непонятно, к чему он относится. */
.empty-inline {
  color: var(--text-muted); font-size: var(--fs-sm);
  padding: var(--space-2) var(--space-3); max-width: 68ch;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface);
}

/* ---------- Диалог ---------- */
.modal-overlay {
  position: fixed; inset: 0; background: rgba(30, 26, 64, 0.45); z-index: var(--z-modal);
  display: grid; place-items: center; padding: var(--space-3);
}
.modal {
  background: var(--bg); border-radius: var(--radius-lg); padding: var(--space-4);
  width: 100%; max-width: 460px; box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column; gap: var(--space-2); max-height: 90vh; overflow-y: auto;
}
.modal-desc { color: var(--text-muted); font-size: var(--fs-sm); margin: 0; }
.modal form { display: flex; flex-direction: column; gap: var(--space-2); }
.modal-actions { justify-content: flex-end; margin-top: var(--space-1); }

/* ---------- Уведомление ---------- */
.toast {
  position: fixed; bottom: var(--space-3); left: 50%; transform: translateX(-50%) translateY(20px);
  background: var(--text); color: var(--bg); padding: var(--space-1) var(--space-3); border-radius: var(--radius);
  font-size: var(--fs-sm); font-weight: 600; box-shadow: var(--shadow-lg);
  opacity: 0; pointer-events: none; transition: opacity 0.2s, transform 0.2s; z-index: var(--z-toast);
  max-width: 90vw;
}
.toast.visible { opacity: 1; transform: translateX(-50%) translateY(0); }
.toast.ok { background: var(--ok); color: var(--text-inverse); }
.toast.err { background: var(--crit); color: var(--text-inverse); }
.toast.warn { background: var(--warn); color: var(--text-inverse); }

/* ---------- Узкий экран ---------- */
@media (max-width: 900px) {
  .layout { grid-template-columns: 1fr; }
  /* Высоту в 100vh и прилипание меню на узком экране надо снимать явно: без этого
     горизонтальная полоса меню всё равно занимала целый экран (замер на 390 px:
     900 px высоты, содержимое начиналось только под ним — до работы приходилось
     пролистывать всё меню). */
  .sidebar {
    flex-direction: row; align-items: center; gap: var(--space-2); flex-wrap: wrap;
    height: auto; position: static; overflow: visible;
    border-right: none; border-bottom: 1px solid var(--line);
    padding: var(--space-2);
    /* Элемент сетки по умолчанию не сжимается ниже своего содержимого: без
       min-width: 0 полоса меню растягивала колонку до 1078 px, и вбок уезжала
       вся страница, а не прокручивалась сама полоса. */
    min-width: 0;
  }
  .content { min-width: 0; }
  /* Пункты идут одной прокручиваемой строкой, а не переносятся: с переносом
     и рубриками меню занимало 348 px, и работа начиналась ниже экрана.
     Прокрутка внутри полосы страницу вбок не тянет. */
  /* min-width: 0 обязателен: без него полоса требует ширину по содержимому
     (748 px), растягивает меню и уводит вбок всю страницу — прокрутка внутри
     полосы работает только у сжимаемого блока. */
  .nav {
    flex-direction: row; flex-wrap: nowrap; overflow-x: auto;
    gap: var(--space-1); min-width: 0; flex: 1 1 100%;
  }
  .nav-group { flex-direction: row; gap: var(--space-0); }
  .nav-cap { display: none; }
  .nav a { white-space: nowrap; }
  /* Полоса меню уезжает вправо на 688 px: три пункта («Интеграции», «Журнал
     действий», «Как работать») не видно совсем. Тень у правого края — знак,
     что полосу можно двигать; она снимается, когда докрутили до конца. */
  /* Затухание вместо внутренней тени: тень ложилась поверх пункта серым пятном,
     а обрез всё равно приходился на середину слова («Интегра|ции») и читался
     как брак вёрстки. Маска растворяет край — видно, что полосу двигают. */
  .nav.has-more {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 44px), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 44px), transparent);
  }
  /* Полосу прокрутки браузер рисует здесь системной: серый жёлоб с кнопками
     по краям под самыми вкладками. Тонкая в цвет линий выглядит частью меню
     и не отнимает у полосы возможность прокрутки мышью. */
  /* Пункт не режется пополам при прокрутке: полоса останавливается по началу
     вкладки. proximity, а не mandatory — иначе полосу «дёргает» при касании. */
  .nav { scrollbar-width: thin; scrollbar-color: var(--line) transparent;
    scroll-snap-type: x proximity; }
  .nav a { scroll-snap-align: start; }
  /* «Ваша неделя» на телефоне — строкой в меню: график и пояснение занимали
     186 px перед содержимым, а показатель в них один. */
  /* margin: auto 4px 0 из базового правила сдвигало строку недели на 4 px
     правее бренда, поиска и вкладок — левый край полосы меню шёл лесенкой. */
  .week { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--space-0); margin: 0;
    padding: 0; border: none; background: none; }
  /* Строкой, а не карточкой: этаж под блоком отодвигал работу ещё на 35 px,
     а на 390 px строка рвалась надвое. Число здесь рядом со словом — колонка
     в 1.9em из боковой раскладки оставляла между ними дыру. */
  .week .att-row { margin-top: 0; gap: var(--space-0); }
  .week .att-num { min-width: 0; font-size: var(--fs-text); font-weight: 600; }
  .week .att-link { min-height: 24px; margin-top: 0; }
  /* Имя клиента и пояснение уводят строку на второй этаж, а действие понятно
     и без них: проверка всё равно одна — та, где ждём дольше всех. */
  .att-link-client, .week-note { display: none; }
  .user { margin-top: 0; margin-left: auto; flex-direction: row; align-items: center; gap: var(--space-1); }
  .user-role { display: none; }
  .columns { grid-template-columns: 1fr; }
  .content { padding: var(--space-2) var(--space-2) var(--space-3); }
  /* Имя человека занимало 196 px из 568 — половину строки рядом с действием
     экрана. Остаются инициалы и стрелка, имя целиком — в подсказке и в самом
     меню профиля, куда эта кнопка и ведёт. */
  .user-who { display: none; }
  /* Профиль встаёт к правому краю: после того как имя скрыли, он остался
     стоять сразу за кнопкой и висел посреди строки с пустотой в 241 px. */
  .topbar .user { margin-left: auto; }
  /* Внутренний отступ карточки в 24 px съедал десятую часть узкой колонки. */
  .next-action { padding: var(--space-2); }
  /* Кнопки уходят на свою строку, но остаются в столбце текста. */
  .next-top { grid-template-columns: minmax(0, 1fr) auto; }
  .next-actions { grid-column: 1 / -1; align-self: start; justify-self: start; }
  .next-actions .btn { padding: 9px 14px; font-size: var(--fs-sm); }
  /* Полоса шагов — одни номера: восемь названий сюда не помещаются, а сама
     полоса на восемь сегментов встаёт и в 342 px. Название — в подсказке
     и в заголовке карточки шага. */
  .next-top .route-pill { padding: 0 4px; }
  /* Верхняя панель растянута на поля страницы отрицательными полями. На узком
     экране поля вдвое меньше, а панель считала их прежними и вылезала на 26 px
     за край — вся страница получала горизонтальную прокрутку. */
  .topbar { margin-inline: calc(-1 * var(--space-2)); padding-inline: var(--space-2); }
  /* Меню здесь схлопнуто в верхнюю полосу, и в шапке остаётся один профиль.
     Прижатый вправо, он висел один на пустом этаже в 64 px — между полосой
     меню и содержимым зияла дыра, а левый край страницы прерывался. Ставим
     профиль на ту же левую линию и снимаем высоту, рассчитанную на действия
     экрана рядом с ним. */
  .topbar { min-height: 0; padding-block: 4px; }
  .topbar-actions:empty + .user { margin-left: 0; }
  /* Действие экрана уводит вправо тот же auto-отступ: при 632 px «Начать
     проверку» с профилем висели у правого края, а всё остальное на странице
     шло от левого — между ними оставалась дыра в 135 px. */
  .topbar-actions { margin-left: 0; }
  .topbar-actions .btn { white-space: nowrap; }
}
@media (max-width: 560px) {
  /* Подписи кнопок рвались по слогам: «Начать / проверку», «Открыть /
     проверку», «Продолжить / шаг 0». Кнопке хватает места, если разрешить
     ряду переноситься — переносим кнопку целиком, а не слова внутри неё. */
  .topbar { flex-wrap: wrap; }
  /* «Показать остальные» — 131 px и подпись в два этажа. */
  .cards-more .btn { white-space: nowrap; }
  /* Пара «Открыть проверку» + «Продолжить шаг 0» делила 300 px пополам.
     На телефоне каждая занимает строку целиком — и подпись помещается,
     и цель нажатия крупнее. */
  .next-actions { flex-wrap: wrap; }
  .next-actions .btn { flex: 1 1 100%; white-space: nowrap; }
  /* На телефоне подпись «Дальше: нетиповые операции» в одну строку не влезает:
     кнопки встают друг под друга во всю ширину. */
  .step-hop-btn { flex: 1 1 100%; }
  /* Строка шага переносит список состояния под себя: строка теперь flex, а не сетка. */
  .step { flex-wrap: wrap; }
  .step-state { max-width: 140px; }
  .req-facts { grid-template-columns: 1fr; gap: var(--space-0) var(--space-2); }
  .req-facts dd { margin-bottom: 6px; }
}

/* Ссылка в кабинет клиента — её копируют и отправляют */
.link-box {
  margin-top: 6px; font-family: var(--font-mono); font-size: var(--fs-xs);
  background: var(--surface-2); border: 1px solid var(--line); width: 100%;
}
/* Ссылка в кабинет: имя и кнопки по верхней линии, сама ссылка — строкой под
   ними. Пока кнопки стояли сбоку, они центрировались по высоте всей карточки
   и не совпадали ни с именем, ни со ссылкой (замечание владельца 10.09). */
.portal-link .doc-head { align-items: flex-start; gap: var(--space-2); }
.portal-link .doc-actions { margin-left: auto; }
.portal-link .link-box { margin-top: 0; }
/* На телефоне кнопки рядом с именем оставляли ему 120 px, и подпись срока
   вставала в три строки. Там они уходят под имя парой равных кнопок —
   граница та же, что у остальных строк документов (700 px). */
@media (max-width: 700px) {
  .portal-link .doc-head { flex-direction: column; align-items: stretch; }
  /* В колонке flex-basis читается как ВЫСОТА: 240 px оставляли между подписью
     и кнопками пустой блок в пол-экрана. */
  .portal-link .doc-head > .doc-main { flex: 0 0 auto; }
  /* «Скопировать» здесь не главнее «Отозвать»: обе в своей колонке, а не одна
     во всю строку, как в лотке документов. */
  .portal-link .doc-actions .btn.secondary { grid-column: auto; }
}

/* ---------- Согласование итога ---------- */
.review-card { margin-bottom: var(--space-3); }

/* ---------- Заметки в карточке клиента ---------- */
.notes-area {
  width: 100%; font-family: inherit; font-size: var(--fs-text); line-height: 1.5;
  padding: 9px 11px; border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  background: var(--bg); color: var(--text); resize: vertical;
}

/* ---------- Сворачиваемые разделы панели шага ---------- */
/* Раскрывающиеся блоки — один вид на всю программу.
   Их набралось пять: общий раздел, раздел справочника, «Добавить свой бланк»,
   «Почему так посчитали» и карточка письма. У каждого была своя рамка, свой
   отступ заголовка и своя (или ничья) стрелка — рядом это и читалось как
   «съезжает». Общее держим здесь, частное — ниже у каждого блока. */
.sec,
.cat-section,
.upload-tpl,
.sample-why {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--bg); margin: var(--space-2) 0;
}
.sec > .sec-head,
.cat-section > .cat-head,
.upload-tpl > summary,
/* Оговорка о неполном бланке стоит вплотную к таблице: она относится к суммам
   ниже, а не к списку контролей выше. */
.tb-form-gap { margin: var(--space-3) 0 var(--space-2); }
.tb-checks-ok > summary,
.sample-why > summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  padding: 11px var(--space-2); list-style: none;
}
/* В этих трёх шапках внутри один элемент — подпись. При space-between она
   улетала к правому краю, а треугольник оставался у левого: между ними на
   широком экране зияло больше тысячи пикселей, и строка читалась как брак.
   Там, где в шапке есть второй элемент (счётчик), space-between остаётся. */
.upload-tpl > summary,
.tb-checks-ok > summary,
.sample-why > summary { justify-content: flex-start; }
/* Курсор-палец — только там, где нажимается вся строка. В шапке раздела и
   справочника нажимаются разные вещи: кнопка раскрытия, значок «Зачем это»,
   галка «весь раздел». */
.upload-tpl > summary,
.tb-checks-ok > summary,
.sample-why > summary { cursor: pointer; }
.upload-tpl > summary::-webkit-details-marker,
.tb-checks-ok > summary::-webkit-details-marker,
.sample-why > summary::-webkit-details-marker { display: none; }
/* Стрелка сама показывает, свёрнут раздел или раскрыт. */
.sec-toggle::before,
.cat-toggle::before,
.upload-tpl > summary::before,
.tb-checks-ok > summary::before,
.sample-why > summary::before {
  content: "▸"; color: var(--text-muted); font-size: var(--fs-sm); flex: none;
  transition: transform .15s;
}
.sec-toggle[aria-expanded="true"]::before,
.cat-toggle[aria-expanded="true"]::before,
.upload-tpl[open] > summary::before,
.tb-checks-ok[open] > summary::before,
.sample-why[open] > summary::before { transform: rotate(90deg); }
/* Кнопка раскрытия — отдельная цель нажатия 24×24 (WCAG 2.2). Раньше раскрытием
   ведал <summary>, а внутри него лежала галка: axe-core считал это вложенным
   интерактивом, и до галки нельзя было добраться с клавиатуры. */
.cat-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; flex: none; padding: 0;
  border: 0; background: none; cursor: pointer; border-radius: var(--radius-sm);
}
.cat-toggle:hover { background: var(--surface-2); }
/* Нажимается вся строка заголовка, а не только стрелка: кнопка растянута
   на всю свободную ширину, как раньше был растянут <summary>. */
.sec-toggle {
  display: flex; align-items: center; gap: var(--space-2); flex: 0 1 auto; min-width: 0;
  padding: 0; border: 0; background: none; cursor: pointer; text-align: left;
  font: inherit; color: inherit; min-height: 24px;
}
/* Кнопка по своему тексту, а не на всю строку: значок «Зачем это» стоит сразу
   за названием раздела, как стоял внутри <summary>. Растянутая кнопка отжимала
   его к правому краю, где он читается как значок счётчика, а не как пояснение
   к заголовку. Счётчик по-прежнему прижат вправо. */
.sec > .sec-head { justify-content: flex-start; }
.sec-title { font-weight: 700; font-size: var(--fs-sm); min-width: 0; }
.sec-count {
  margin-left: auto;
  background: var(--surface-2); color: var(--text-muted); border-radius: var(--radius-pill);
  padding: 1px 9px; font-size: var(--fs-xs); font-weight: 700;
}
.sec-hint { margin: 0 var(--space-2) 6px; }
.sec-body { padding: 0 var(--space-2) var(--space-2); }

/* ---------- Библиотека писем ----------
   Прежний вид — раскрывающаяся карточка письма — заменён таблицей. Правила от
   него удалены: одно из них называлось так же, как живой блок с текстом письма
   («.letter»), и вешало на него рамку и фон, которых в замысле не было. */
/* Пометка автополя и подсветка незаполненного в редактируемых полях письма. */
.auto-tag { font-size: var(--fs-xs); color: var(--text-muted); font-weight: 400; text-transform: none; }
.field .warn-note { color: var(--crit); }
.field.missing input { border-color: var(--crit); }

/* ── Таблица документов (письма-запросы) ─────────────────────────────────── */
/* Поле, заполненное из непроверенного источника: видно, что его надо сверить. */
.field.unverified input { border-color: var(--warn); }

/* Таблица «что нашли в документе»: аудитор видит находку и то, что уже в карточке. */
/* Пояснение, таблица и кнопка — три части одного разговора, но браузер
   ставил их вплотную: у `.scroll-x` и у строки действий своих отступов нет.
   Шаг задаём контейнером — тем же, что между блоками во всей программе. */
.found-review { display: flex; flex-direction: column; gap: var(--space-2); }
.found-review .notice { margin-top: 0; }
.found-table .pick { width: 56px; text-align: center; }
.found-table td { vertical-align: top; }
.found-context { display: block; margin-top: 3px; max-width: 34rem; }

/* ---------- Экран «Как работать» ---------- */
/* min(340px, 100%) — иначе на телефоне колонка требует 340 px, которых нет,
   и вся страница уезжает вбок. */
/* Две колонки, а не «сколько поместится»: подсказок восемь, и при трёх колонках
   последний ряд оставался с двумя карточками и пустым местом справа. По две в ряд
   ряды всегда полные, а текст со списком шагов читается свободнее. */
/* Маршрут в «Как работать» — список раскрывающихся строк, а не стена карточек.
   Восемь карточек по две в ряд давали 2400 px, из которых человек читал одну:
   ту, на шаге которой он застрял. Теперь видно весь маршрут, подробности — по клику. */
.help-list { display: flex; flex-direction: column; }
.help-item { border-top: 1px solid var(--line); }
.help-item:first-child { border-top: none; }
.help-item > summary {
  display: flex; align-items: baseline; gap: var(--space-1);
  padding: var(--space-2) 0; cursor: pointer; list-style: none;
  overflow: hidden;
}
.help-item > summary::-webkit-details-marker { display: none; }
.help-item > summary::after {
  content: '›'; margin-left: auto; color: var(--text-muted);
  transform: rotate(90deg); transition: transform .15s; flex: none;
}
.help-item[open] > summary::after { transform: rotate(-90deg); }
.help-item > summary:hover .help-name { color: var(--brand); }
.help-name { font-weight: 600; font-size: var(--fs-text); min-width: 0; }
/* Цель шага — рядом с названием, а не отдельным абзацем: так строка списка
   отвечает на «что я получу» без раскрытия. На узком экране прячем. */
.help-goal-short {
  color: var(--text-muted); font-size: var(--fs-sm); flex: 1 1 40%; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
@media (max-width: 1100px) { .help-goal-short { display: none; } }
.help-body { padding: 0 0 var(--space-2) calc(var(--space-3) + 12px); }
.help-body p { margin: 0; font-size: var(--fs-sm); }
.help-note { font-size: var(--fs-sm); color: var(--text-muted); margin: 0 0 var(--space-1); }
/* Две колонки внизу — равной высоты, иначе ряд читается рваным. */
.help-columns { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: stretch; }
.help-columns > .card { display: flex; flex-direction: column; }
/* Ярлык шага — цельный: на 390 px «1 · без кабинета» ломался на три строки
   (замер 14.09.2026: высота 3 строки при ширине 76 px) и читался как абзац.
   Одна ширина у всех ярлыков — чтобы названия шагов начинались с одной
   вертикали: от «Шаг 1» до «Шаг 6–7» разбег доходил до 86 px. */
.help-step {
  display: inline-block; min-width: 62px; text-align: center;
  font-size: var(--fs-xs); font-weight: 700; color: var(--brand);
  background: var(--brand-bg-soft); border-radius: var(--radius-sm); padding: 2px 7px; margin-right: 6px;
  white-space: nowrap;
}
.help-steps { margin: 0 0 var(--space-1); padding-left: 1.2rem; font-size: var(--fs-sm); }
.help-steps li { margin-bottom: 5px; }
.help-watch { font-size: var(--fs-xs); color: var(--text-muted); margin: 0; }
.help-limits { margin: 0; padding-left: 1.2rem; font-size: var(--fs-sm); }
.help-limits li { margin-bottom: 4px; }

/* Текст сбоя обрезаем тремя строками: полный SQL занимает 200 px, и строки
   журнала расходились по высоте от 39 до 200 px — таблица читалась кашей.
   Целиком текст остаётся в подсказке при наведении. */
.err-text {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
  overflow: hidden;
}

/* ---------- Плитки показателей (шаг 1: открытые данные) ---------- */
.figures {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--space-2); margin-bottom: var(--space-2);
}
.figure { border: 1px solid var(--line); border-radius: var(--radius-sm); padding: var(--space-2); }
/* Единственный показатель занимал всю ширину экрана серой полосой — держим его
   в размере обычной плитки. */
.figures > .figure:only-child { max-width: 420px; }
.figure-label { font-size: var(--fs-xs); color: var(--text-muted); margin-bottom: 4px; }
.figure-value { font-size: var(--fs-h4); font-weight: 700; font-variant-numeric: tabular-nums; }
.figure-value.negative { color: var(--crit); }
.figure-delta { font-size: var(--fs-xs); margin-top: 4px; font-weight: 600; }
.figure-delta.up { color: var(--ok); }
.figure-delta.down { color: var(--crit); }
.figure-delta.watch { color: var(--warn); }
.figure-delta.flat { color: var(--text-muted); font-weight: 400; }
.figure-hint { font-size: var(--fs-xs); color: var(--text-muted); margin-top: 6px; }

/* Добавление своего бланка — редкое действие, поэтому свёрнуто и не мешает работе. */
/* «Добавить свой бланк» — пунктир: место, куда кладут файл, а не список. */
/* «Добавить свой бланк» — редкое действие, поэтому стоит последним и свёрнут
   в одну строку: пунктир отличает его от разделов с письмами. */
.upload-tpl { border-style: dashed; margin-top: var(--space-2); }
.upload-tpl > summary { padding-top: 8px; padding-bottom: 8px; }
.upload-tpl > summary { font-size: var(--fs-sm); font-weight: 600; color: var(--brand); }
.upload-tpl > summary::before { color: var(--brand); }
.upload-tpl-body { padding: 0 var(--space-2) var(--space-2); }
.upload-tpl-body #tplResult { margin-top: var(--space-2); }

/* Прокручивается таблица, а не страница: горизонтальная прокрутка всего документа
   ломает чтение всех остальных блоков. */
.table-wrap { overflow-x: auto; }

/* Журнал действий: время и клиент — узкие колонки, «что сделал» забирает остаток.
   Без этого столбец времени растягивался на четверть экрана. */
/* Отбор записей: три поля в ряд, как в остальных формах программы. */
.jl-filters {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--space-2); margin-bottom: var(--space-2);
}
/* Распознанный текст под строкой документа: показываем по кнопке, поэтому
   он занимает всю ширину строки и не спорит с кнопками справа. */
.doc-text {
  flex-basis: 100%; margin-top: 8px; padding: 10px 12px;
  background: var(--surface-2); border-radius: var(--radius-sm);
  font-size: var(--fs-sm); color: var(--text-muted); line-height: 1.5;
  max-height: 220px; overflow-y: auto; white-space: pre-wrap;
}
.journal-table .jl-when {
  width: 1%; white-space: nowrap; color: var(--text-muted); font-family: var(--font-mono);
  font-size: var(--fs-xs);
}
.journal-table .jl-action { font-weight: 600; }
/* Строка журнала — одна строка. Имя сотрудника переносилось на второй ряд, и
   двести записей растягивались на четырнадцать тысяч пикселей. */
.journal-table td { vertical-align: middle; padding-top: 7px; padding-bottom: 7px; }
.journal-table .jl-who { white-space: nowrap; }
.doc-table { width: 100%; min-width: 560px; border-collapse: collapse; font-size: var(--fs-sm); }
.doc-table thead th {
  text-align: left; font-weight: 600; color: var(--text-muted);
  font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .03em;
  padding: var(--space-1) var(--space-2); border-bottom: 1px solid var(--line);
}
.doc-table td { padding: var(--space-2); border-bottom: 1px solid var(--line); vertical-align: top; }
/* Строка письма раскрывается по клику — показываем это курсором, иначе
   про такую возможность не догадаться. */
.doc-table tr.lrow { cursor: pointer; }
.doc-table tr.lrow:hover td { background: var(--surface-2); }
/* Раскрытая строка помечается полосой слева, а НЕ заливкой: заливка совпадала
   с фоном раскрытой панели, и шапка письма сливалась с полями под ней. */
.doc-table tr.lrow[aria-expanded="true"] > td:first-child {
  box-shadow: inset 3px 0 0 var(--brand);
}
.doc-table tr.lrow[aria-expanded="true"] > td { border-bottom-color: transparent; }
.doc-table .lpick { width: 34px; vertical-align: middle; }
/* Флажок без подписи — сам себе цель нажатия, и 13×13 по умолчанию до нормы
   WCAG 2.2 (24×24) не дотягивает вдвое. Здесь отмечают письма к выгрузке и поля
   к переносу в карточку — промах стоит лишнего действия, поэтому флажок крупнее. */
.doc-table .lpick input[type="checkbox"],
.found-table .pick input[type="checkbox"] { width: 24px; height: 24px; }
.doc-table .lnum { width: 46px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.doc-table .lname { font-weight: 500; }
.doc-table .lfile { font-weight: 400; margin-top: 2px; }
.doc-table .lstatus { width: 130px; vertical-align: middle; }
/* Кнопки — по центру строки. Ячейки таблицы выровнены по верху (так читается
   название с именем файла под ним), но кнопки от этого висели выше середины. */
.doc-table .lacts { width: 1%; white-space: nowrap; text-align: right; vertical-align: middle; }
/* Кнопки со словами, а не со значком: .icon-btn задаёт жёсткие 28×28 px, из-за чего
   «Документ», «Скачать» и «Заполнить» обрезались и налезали друг на друга. */
.doc-table .lacts .icon-btn {
  width: auto; height: auto; min-height: 28px; padding: 4px 10px;
  margin-left: var(--space-1); font-size: var(--fs-xs); white-space: nowrap;
}
/* Полоса выгрузки стоит НАД списком и липнет к верху экрана: раньше кнопка
   «Выгрузить отмеченные» пряталась под двумя таблицами, и отметив письмо в
   начале списка, аудитор не видел ни счётчика, ни кнопки — галочки выглядели
   бессмысленными. Снизу липкой её сделать нельзя: под последним элементом
   контейнера прилипать не к чему, браузер оставляет её на месте. */
/* Панель выгрузки стоит ПОД списками — сначала выбираешь письма, потом
   выгружаешь. Раньше она вместе с «Добавить свой бланк» занимала верх блока,
   и до самих писем приходилось прокручивать мимо двух непонятных панелей.
   Липкой её не делаем: прилипшая полоса ложится поверх строк списка и
   закрывает письмо, которое человек как раз собирался отметить. */
.pick-bar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  flex-wrap: wrap; margin: var(--space-1) 0;
  padding: var(--space-1); border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface);
}
/* Размер кнопки — отдельный модификатор, а не правило одного места: иначе в
   системе заводятся кнопки-исключения (замер показал 32 px против 51 у всех
   остальных, и объяснялось это только историей одной панели). */
.btn-sm { padding: 7px 15px; font-size: var(--fs-sm); }
/* Кнопка-ярлык шириной по тексту: раньше это задавалось стилем прямо в разметке. */
.btn-inline { width: fit-content; cursor: pointer; }
.pick-bar.active { border-color: var(--brand); box-shadow: var(--shadow); }
.pick-count { color: var(--text-muted); font-size: var(--fs-sm); }
.pick-bar.active .pick-count { color: var(--text); font-weight: 500; }
.pick-bar .btn-cta:disabled { opacity: .55; cursor: not-allowed; }

/* Панель продолжает ту же полосу, что помечает раскрытую строку: так шапка
   письма и поля под ней читаются как одно целое, а не как два соседних блока. */
/* Карточка клиента разложена по вкладкам, двух колонок больше нет. Осталась
   стопка: на вкладке «Реквизиты» под формой идут архив и удаление. */
.col-stack { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }

/* Шапка карточки клиента: имя слева, действия справа одной группой.

   Ссылка «Все клиенты» стоит НАД шапкой, а не внутри левой колонки: пока она
   была внутри, общее правило `.page-head { align-items: flex-start }` равняло
   кнопки по её строке, и «Начать проверку» висела выше названия клиента.
   Карточка проверки решает это тем же способом — держим один вид. */
.page-head.client-head { align-items: center; }
.client-head .head-with-badge { align-items: center; }
/* На телефоне три кнопки в ряд не помещаются и ложатся «1 + 2»: одна кнопка
   в первой строке, две во второй — ряд выглядит сломанным. Ставим их
   столбиком во всю ширину: одинаковые кнопки, одинаковая цель нажатия. */
@media (max-width: 560px) {
  .head-actions { width: 100%; flex-direction: column; align-items: stretch; }
  .head-actions .btn { width: 100%; justify-content: center; }
}
/* Чипы внутри шапки идут в один ряд с кнопками — нижний отступ там лишний. */
.head-actions .chips { margin: 0; }

/* Реквизиты — не форма-простыня, а список «поле — значение», как в референсе.
   Прежний вид (подпись сверху, поле во всю ширину, пояснение под каждым) давал
   18 полей по 75 px: карточка вытягивалась на 2300 px и читалась инструкцией,
   а не карточкой. Здесь строка занимает 34 px, а пояснение показывается, когда
   человек встал в поле, — оно нужно ровно в этот момент. */
.kv-group { margin-bottom: var(--space-2); }
.kv-group-head { margin-bottom: 4px; }
.kv-group-head small { font-size: var(--fs-xs); color: var(--text-muted); }
/* Зазора между колонками нет намеренно: линии соседних полей смыкаются в одну
   черту через всю карточку, и блок читается таблицей. С зазором каждая строка
   рвалась на два отрезка разной длины, а поле во всю ширину давало третью
   длину — от этой ряби карточка выглядела неровной. Воздух между колонками
   даёт правый отступ самого поля. */
.kv-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0; }
.kv.wide { grid-column: 1 / -1; }
/* Поле без пары в ряду: линия должна идти через всю карточку, а само поле —
   остаться шириной с левый столбец. Пустая колонка в 50% ряда и держит ширину. */
.kv.alone { grid-column: 1 / -1; grid-template-columns: 150px minmax(0, 1fr) 50%; }
.kv {
  display: grid; grid-template-columns: 150px minmax(0, 1fr); align-items: center;
  gap: var(--space-0) var(--space-1); padding: 4px 18px 4px 0; border-bottom: 1px solid var(--line);
}
.kv > label { font-size: var(--fs-sm); color: var(--text-muted); }
.kv > label em { color: var(--crit); font-style: normal; }
.kv > input {
  border: 1px solid transparent; background: transparent; padding: 5px 8px;
  font-size: var(--fs-text); border-radius: var(--radius-sm); min-width: 0;
}
.kv > input:hover { border-color: var(--line-strong); }
/* Контур фокуса не гасим: обход клавишами 13.09.2026 нашёл в карточке
   клиента пятнадцать полей, где о месте курсора говорил только цвет
   рамки в 1 px. Цвет рамки остаётся, но поверх — общий контур. */
.kv > input:focus { border-color: var(--brand); background: var(--bg); }
/* Пояснение остаётся в разметке всегда (его читает aria-describedby), но глазами
   видно только у поля, в котором стоит курсор. */
.kv > small {
  grid-column: 2; position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}
.kv:focus-within > small {
  position: static; width: auto; height: auto; clip: auto; white-space: normal;
  font-size: var(--fs-xs); color: var(--text-muted); line-height: 1.35; padding-bottom: 4px;
}
@media (max-width: 900px) {
  .kv-grid { grid-template-columns: minmax(0, 1fr); }
  .kv.alone { grid-template-columns: 150px minmax(0, 1fr); }
}
@media (max-width: 700px) {
  .kv { grid-template-columns: 1fr; }
  /* Класса два, поэтому правило .kv.alone перебивает это по специфичности —
     повторяем его явно, иначе на телефоне одинокое поле ужималось до 140 px
     и вставало в строку с подписью, когда все соседние идут колонкой. */
  .kv.alone { grid-template-columns: 1fr; }
  .kv > small { grid-column: 1; }
}

/* Необратимое действие — отдельной карточкой внизу столбца, а не в общем ряду. */
.danger-zone { border-color: var(--crit-bg); }
.danger-row .muted-note { flex: 1 1 200px; }

/* Источники заполнения: кнопка сверху, пояснение под ней. Раньше текст стоял
   сбоку от кнопки и обтекал её, налезая на следующую строку. */
.fill-source { margin-bottom: var(--space-2); display: flex; flex-direction: column; gap: var(--space-0); align-items: flex-start; }
.fill-source small { font-size: var(--fs-xs); color: var(--text-muted); line-height: 1.35; }

/* Выбор файла — один вид на всю программу. Системная кнопка браузера
   («Выберите файл · Файл не выбран») выглядела чужой рядом с нашими. */
.file-btn { cursor: pointer; }
.file-name { font-size: var(--fs-sm); color: var(--text-muted); }
.file-name.chosen { color: var(--text); font-weight: 500; }
/* Поле уводим с глаз, но не через hidden: на скрытом поле браузер не может
   показать «заполните это поле» и молча отменяет отправку формы. */
.file-input {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Список клиентов: имя с ИНН одной группой, дальше — состояние работы. */
.clients-table .cname { width: 34%; }
.clients-table .cname a { font-weight: 600; }
.clients-table .cdir { width: 22%; vertical-align: middle; }
.clients-table .cwork { width: 24%; vertical-align: middle; }
.clients-table .cfill { width: 1%; white-space: nowrap; vertical-align: middle; }
/* Две ссылки в ячейке действий не держим в одну строку: в полуразвёрнутом окне
   (960 px) они распирали таблицу до 903 px при доступных 652, и четверть строки
   уезжала под горизонтальную прокрутку. */
/* nowrap здесь обязателен: без него на широком экране «Карточка» и «Начать
   проверку» переносятся внутри колонки в 147 px и налезают друг на друга —
   axe-core 13.09.2026 насчитал 49 нарушений target-size, по одному на строку.
   В узком окне перенос разрешён отдельно, ниже в медиазапросе. */
.clients-table .cacts { width: 1%; white-space: nowrap; text-align: right; vertical-align: middle; }
/* В полуразвёрнутом окне таблица распирается содержимым: на 960 px ей нужно
   817 px при доступных 652, и «Реквизиты» с действиями уезжают под прокрутку.
   Раскладка по объявленным долям (34/22/24) заставляет текст переноситься
   внутри ячеек — колонки остаются все, горизонтальной прокрутки нет. */
@media (max-width: 1200px) {
  .clients-table { table-layout: fixed; }
  /* Доли на всю ширину: при раскладке по долям колонка в 1 % ужимается до
     65 px, и метка «заполнено 8 из 18» вылезала из неё наружу — таблица
     помещалась, а содержимое всё равно уезжало под прокрутку. */
  .clients-table .cname { width: 28%; }
  .clients-table .cdir { width: 16%; }
  .clients-table .cwork { width: 18%; }
  .clients-table .cfill { width: 20%; white-space: normal; }
  .clients-table .cacts { width: 18%; }
  /* Метка «заполнено 8 из 18» шире своей колонки на 6 px: она вылезала в
     соседнюю и вставала вплотную к кнопке. Пусть переносится. */
  .clients-table .cfill .badge { white-space: normal; }
  /* «Начать проверку» — кнопка-ссылка с nowrap: 117 px не влезали в колонку и
     торчали за край таблицы. В узком окне подпись переносится на две строки. */
  .clients-table .cacts .lnk { white-space: normal; }
  /* Две кнопки в ячейке переносились вразнобой и вставали вплотную к метке
     соседней колонки — читалось как «заполнено 8 из 18 Начать проверку».
     Столбик делаем блочными кнопками, а не flex: display: flex на <td> выводит
     ячейку из табличной раскладки, и соседняя колонка наезжает на неё. */
  .clients-table .cacts .icon-btn, .clients-table .cacts .lnk {
    display: block; width: fit-content; margin: 0 0 0 auto;
  }
  .clients-table .cacts .lnk { margin-top: var(--space-0); }
  /* Имя сотрудника в журнале держится одной строкой ради высоты списка:
     двести записей в две строки — это четырнадцать тысяч пикселей. Но в
     полуразвёрнутом окне «Проверяющий Стенд Иванович» просит 221 px и уводит
     таблицу под горизонтальную прокрутку — здесь перенос дешевле. */
  .journal-table .jl-who { white-space: normal; }
  /* Числа в денежной колонке не рвём никогда, а вот текстовая заглушка
     «не с чем сравнить» держала колонку в 136 px на пустом месте. */
  .table td.num .muted-note { white-space: normal; }
  /* Сверка с отчётностью — шесть колонок, три из них денежные, и числа рвать
     нельзя. Единственный запас — боковые отступы ячеек: 12 px против 8 px
     на шести колонках это 48 px, ровно те, которых не хватало на 960. */
  .tb-table th, .tb-table td { padding-left: 8px; padding-right: 8px; }
}
/* Строка клиента открывает карточку — как строка письма раскрывает письмо. */
.clients-table tr.crow { cursor: pointer; }
.clients-table tr.crow:hover td { background: var(--surface-2); }
.clients-table .cacts .icon-btn { width: auto; height: auto; min-height: 28px; padding: 4px 10px; margin-left: var(--space-1); font-size: var(--fs-xs); }
.crow-sub { font-size: var(--fs-xs); color: var(--text-muted); margin-top: 2px; }
.clients-table .cwork .crow-sub { margin-top: 4px; }

.doc-table tr.ledit > td {
  background: var(--surface-2);
  box-shadow: inset 3px 0 0 var(--brand);
}
/* Поля и кнопки раскрытого письма стоят на одном отступе от края таблицы:
   раньше по бокам оставалось вдвое меньше, чем сверху, и блок казался съехавшим. */
/* `width: 0` + `min-width: 100%` — единственный способ не дать раскрытому письму
   растянуть таблицу. Ячейка обычной таблицы шириной считается по содержимому, и
   тринадцать полей письма растягивали её до 1420 px в блоке шириной 908: страница
   уезжала вбок, и аудитор возил её горизонтально вместо того, чтобы читать поля.
   С нулевой шириной ячейка ничего у таблицы не просит, а `min-width` возвращает
   блоку всю доступную ширину — поля переносятся вниз, как и должны. */
.ledit-box { width: 0; min-width: 100%; padding: var(--space-3) var(--space-2) var(--space-2); }
/* Кнопки под полями были вдвое выше кнопок в самой строке (52 против 28) —
   рядом это читалось как два разных интерфейса. Уравниваем. */
.ledit-box .sample-form-actions {
  display: flex; gap: var(--space-1); flex-wrap: wrap;
  margin-top: var(--space-3); padding-top: var(--space-2);
  border-top: 1px solid var(--line);
}
.ledit-box .sample-form-actions .btn,
.ledit-box .sample-form-actions .btn-cta {
  padding: 7px 14px; font-size: var(--fs-sm); min-height: 34px;
}
/* На телефоне таблица писем перестаёт быть таблицей: 560 px её минимальной ширины
   не помещаются в 360, и весь список приходилось возить вбок. Строка становится
   карточкой — отметка и название сверху, состояние и кнопки под ними. */
@media (max-width: 700px) {
  .doc-table { min-width: 0; }
  .doc-table thead { display: none; }
  .doc-table tr.lrow {
    display: grid; grid-template-columns: 34px 1fr; align-items: start;
    padding: var(--space-2) 0; border-bottom: 1px solid var(--line);
  }
  .doc-table tr.lrow > td { border: none; padding: 2px var(--space-2) 2px 0; }
  .doc-table tr.lrow[aria-expanded="true"] > td:first-child { box-shadow: none; }
  .doc-table .lnum { display: none; }
  .doc-table .lstatus, .doc-table .lacts { grid-column: 2; width: auto; text-align: left; }
  /* Кнопки сеткой по две. Третья тянется во всю ширину только когда она
     последняя: иначе она висит в ряду одна и карточка выглядит развалившейся.
     У своего бланка кнопок четыре («Удалить»), и тогда они идут двумя парами. */
  /* В строке одна кнопка «Действия» с меню — во всю ширину карточки, меню под ней. */
  .doc-table .lacts { white-space: normal; margin-top: var(--space-1); }
  .doc-table .lacts .icon-btn { width: 100%; margin: 0; justify-content: center; }
  .doc-table .lacts .req-menu { left: 0; right: auto; }
  .doc-table tr.ledit > td { padding: var(--space-2) 0; }
  .ledit-box { padding: var(--space-2) var(--space-2) var(--space-2) 0; }
}
.ledit-box .letter-preview-slot { margin-top: var(--space-2); }
.ledit-box textarea {
  width: 100%; font-family: var(--font-mono); font-size: var(--fs-xs);
  border: 1px solid var(--line); border-radius: var(--radius-sm); padding: var(--space-2);
}

/* ── Просмотр .docx как документа (модальное окно) ────────────────────────── */
.docview-overlay {
  position: fixed; inset: 0; z-index: 1000; display: flex;
  align-items: flex-start; justify-content: center;
  background: rgba(0, 0, 0, .55); padding: var(--space-3); overflow: auto;
}
.docview {
  background: var(--surface); border-radius: var(--radius);
  box-shadow: var(--shadow-lg); width: min(920px, 100%);
  display: flex; flex-direction: column; max-height: calc(100vh - 2 * var(--space-3));
}
.docview-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--space-2) var(--space-3); border-bottom: 1px solid var(--line);
  position: sticky; top: 0; background: var(--surface); border-radius: var(--radius) var(--radius) 0 0;
}
.docview-close {
  border: 0; background: transparent; cursor: pointer; font-size: 18px;
  color: var(--text-muted); line-height: 1; padding: 4px 8px; border-radius: var(--radius-sm);
}
.docview-close:hover { background: var(--surface-2); color: var(--text); }
/* Документ и поля рядом: правишь поле — лист слева перерисовывается. */
.docview.with-aside { width: min(1400px, 100%); }
.docview-split { display: flex; min-height: 0; flex: 1; }
.docview-split > .docview-body { flex: 1; min-width: 0; }
.docview-aside {
  width: 340px; flex: 0 0 340px; overflow: auto; padding: var(--space-3);
  border-left: 1px solid var(--line); background: var(--surface);
}
.docview-aside-title { margin: 0 0 4px; }
.docview-aside-foot { margin-top: var(--space-2); padding-top: var(--space-2); border-top: 1px solid var(--line); }
/* В узком окне панель полей забирала 340 px из 620, документу оставалось 280 —
   лист ужимался до нижней границы масштаба и читать его было нельзя. Ставим
   поля под документ: сначала видно, что получилось, следом — чем это править. */
@media (max-width: 900px) {
  .docview-split { flex-direction: column; }
  .docview-split > .docview-body { flex: 1 1 auto; min-height: 45vh; }
  .docview-aside {
    width: auto; flex: 0 0 auto; max-height: 45vh;
    border-left: 0; border-top: 1px solid var(--line);
  }
}
.docview-body { overflow: auto; padding: var(--space-3); background: var(--bg); }
.docview-note { color: var(--text-muted); padding: var(--space-4); text-align: center; }
.docview-note.error { color: var(--crit); }
/* Присланные клиентом сканы и PDF — показываем тем же окном, что и письма. */
.docview-image { display: block; max-width: 100%; margin: 0 auto; }
.docview-frame { width: 100%; height: 75vh; border: 0; background: var(--bg); }
/* Текстовый файл: выписка из банка или акт в CSV. Длинные строки переносим —
   горизонтальная прокрутка внутри окна просмотра читается как поломка. */
.docview-text {
  margin: 0; padding: var(--space-3); white-space: pre-wrap; word-break: break-word;
  font-family: var(--font-mono); font-size: var(--fs-sm); line-height: 1.5; color: var(--text);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm);
}
/* Страницы PDF рисует pdf.js — каждая своим полотном, столбиком с прокруткой. */
.docview-pages { display: flex; flex-direction: column; align-items: center; gap: var(--space-2); }
.docview-page { max-width: 100%; height: auto; box-shadow: var(--shadow-sm); background: #fff; }
.docview-more { display: flex; justify-content: center; padding: var(--space-2) 0; }
/* Лист документа: белая страница с тенью независимо от темы — как в Word. */
.docview-body .docx-wrapper { background: transparent; padding: 0; display: flex; flex-direction: column; align-items: center; gap: var(--space-2); }
/* max-width здесь не место: он ужимал коробку листа, а содержимое оставалось
   прежней ширины и уезжало за правый край — в узком окне обрезало адресата
   посреди ФИО. Лист вписывает в окно `вписатьЛистВОкно` в js/docview.js. */
.docview-body .docx-wrapper > section.docx {
  background: #fff; color: #111; box-shadow: 0 2px 14px rgba(0, 0, 0, .25);
  margin: 0;
}

/* Шаг 2: номер по порядку и файлы клиента прямо в строке выборки. */
.item-seq { color: var(--text-muted); font-variant-numeric: tabular-nums; width: 1%; }
.item-files { min-width: 200px; }
.item-file {
  font-size: var(--fs-xs); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  max-width: 220px; color: var(--ok);
}
.item-attach { display: inline-block; margin-top: 4px; cursor: pointer; font-size: var(--fs-xs); }

/* Шаг 1: сверка отчётности с оборотно-сальдовой ведомостью. */
/* Ни отбивки, ни черты: блок живёт внутри своего раздела с заголовком и
   рамкой. Они остались с тех пор, когда он приклеивался последним куском шага
   к таблицам ГИР БО, и давали 70 px пустоты сразу под названием раздела. */
.tb-block { margin-top: 0; }
/* Сетка, а не flex с выравниванием по нижнему краю: пояснение слева в семь
   строк, справа в четыре — и подписи полей стояли на разной высоте, ряд выглядел
   съехавшим. В сетке подпись, поле и пояснение делят общие дорожки ряда.
   Третья дорожка — кнопка загрузки: владелец просил «выбрать» и «загрузить»
   на одной строке, и стоять ей отдельным рядом больше незачем. */
.tb-form {
  display: grid; grid-template-columns: minmax(240px, 1.4fr) minmax(200px, 0.8fr) auto;
  gap: var(--space-2);
  padding: var(--space-2); margin-bottom: var(--space-2);
  background: var(--surface-2); border-radius: var(--radius);
}
.tb-form > .field { min-width: 0; }
.tb-form small { max-width: 42ch; }
/* Пустая строка вместо подписи, а не отступ числом: кнопка встаёт ровно на
   уровень поля и селекта и остаётся на нём, если подпись подрастёт. Считать
   тем же ряд нельзя — раскрытое пояснение под полем сдвинуло бы кнопку вниз. */
.tb-submit .hint-row { min-height: 24px; }
.tb-submit .btn-cta { white-space: nowrap; }
.tb-note { margin: 0 0 var(--space-2); }
@media (max-width: 900px) {
  .tb-form { grid-template-columns: minmax(0, 1fr); }
  .tb-submit .hint-row { display: none; }
}
/* Виды объектов — плитками по три в ряд. Строкой во всю ширину между названием
   и кнопкой оставалось до 900 px пустоты, и список читался рваным. */
.tb-uploads {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-1); margin-bottom: var(--space-2); align-items: stretch;
}
/* Число колонок задаёт разметка по количеству плиток: пустых мест в ряду
   не остаётся, сирот в последнем ряду тоже. */
.tb-uploads.cols-1 { grid-template-columns: minmax(0, 1fr); }
.tb-uploads.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* Ниже 1200 px плитки становятся строками: в двух колонках нечётное число
   оставляло сироту в последнем ряду, а строка во всю ширину ряд не рвёт. */
@media (max-width: 1200px) {
  .tb-uploads, .tb-uploads.cols-2, .tb-uploads.cols-3 { grid-template-columns: minmax(0, 1fr); }
  .tb-uploads .tb-upload {
    flex-direction: row; align-items: center; justify-content: space-between;
    gap: var(--space-2);
  }
  .tb-uploads .tb-upload > .tb-upload-actions { margin-top: 0; flex: 0 0 auto; }
}
.tb-upload {
  display: flex; flex-direction: column; gap: var(--space-1); justify-content: space-between;
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--line); border-radius: var(--radius);
}
.tb-upload-main { min-width: 0; }
/* Имя выгрузки из 1С длиннее строки всегда: это подпись, а не заголовок —
   одна строка с многоточием, полное имя в подсказке. */
.tb-upload-file {
  margin-top: 4px; font-size: var(--fs-xs); color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Список файлов пачки: раскрывается по нажатию — и мышью, и с клавиатуры.
   Прежде имена лежали в атрибуте title, а он недоступен ни с клавиатуры,
   ни на телефоне: аудитор не мог проверить, все ли счета приложены. */
.tb-upload-files { margin-top: 4px; font-size: var(--fs-xs); color: var(--text-muted); }
.tb-upload-files > summary { cursor: pointer; }
.tb-upload-files > ul {
  margin: var(--space-1) 0 0; padding-left: 1.1em;
  display: flex; flex-direction: column; gap: var(--space-0);
}
.tb-upload-files > ul > li { overflow-wrap: break-word; }
/* Единственная плитка растянута на всю ширину: там уместнее строка — слева
   год и организация, справа действия, — чем колодец пустоты справа. */
.tb-uploads.cols-1 .tb-upload {
  flex-direction: row; align-items: center; justify-content: space-between;
  gap: var(--space-2);
}
.tb-uploads.cols-1 .tb-upload > .tb-upload-actions { margin-top: 0; flex: 0 0 auto; }
/* На телефоне строка «слева текст, справа кнопки» ужимала текст до 100 px:
   замер 14.09.2026 на 390 px — от «8 файлов: Оборотно-сальдовая…» оставалось
   71 px из 375. Плитку складываем столбиком, в том числе единственную: её
   правило из трёх классов перебивает это по силе, а не по порядку. */
@media (max-width: 760px) {
  .tb-uploads .tb-upload,
  .tb-uploads.cols-1 .tb-upload { flex-direction: column; align-items: stretch; }
  .tb-uploads.cols-1 .tb-upload > .tb-upload-actions { margin-top: auto; }
  /* Полное имя файла лежит в подсказке, но на телефоне её нечем открыть —
     палец не наводится. Замер 14.09.2026 на 390 px: из подписи видно 296 px
     из 375, и обрывается она на названии ведомости. Даём две строки. */
  .tb-upload-file {
    white-space: normal; display: -webkit-box; -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
  }
}
/* Кнопка и метка прижаты к низу плитки: названия видов разной длины, и без
   этого действие у соседей по ряду оказывалось на разной высоте. */
.tb-upload > .tb-upload-actions { margin-top: auto; }
.tb-upload.is-current { border-color: var(--brand); }
/* Контроли — по две плашки в ряд: три подряд во всю ширину занимали больше
   половины экрана, а текста в каждой на две строки (выбор владельца 10.09). */
.tb-checks {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-1); margin: var(--space-2) 0; align-items: start;
}
@media (max-width: 900px) { .tb-checks { grid-template-columns: minmax(0, 1fr); } }
.tb-check { padding: var(--space-1) var(--space-2); border-radius: var(--radius); border-left: 3px solid var(--line); background: var(--surface-2); }
/* Пояснение показываем двумя строками; полный текст открывает «подробнее».
   Кнопку ставит скрипт и только там, где текст правда не поместился. */
.tb-check .pitem-desc {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden; margin-top: 4px;
}
.tb-check.раскрыт .pitem-desc { -webkit-line-clamp: unset; }
.tb-check-more { margin-top: 4px; font-size: var(--fs-xs); }
/* Нечётная последняя плашка занимает ряд целиком: половина ряда пустой
   выглядит забытым местом, а не раскладкой. Считаем по плашкам, а не по детям:
   ниже них лежит полка «Сошлось», и с `:last-child` единственная плашка ряда
   переставала растягиваться — рядом с ней зияло 600 px пустоты. */
.tb-checks > .tb-check:last-of-type:nth-child(odd) { grid-column: 1 / -1; }
.tb-check.done { border-left-color: var(--ok); }
.tb-check.wait { border-left-color: var(--warn); }
.tb-check.problem { border-left-color: var(--crit); background: var(--crit-bg); }
.tb-check-title { display: flex; gap: var(--space-1); align-items: center; font-weight: 600; font-size: var(--fs-sm); }
/* Сошедшиеся соотношения свёрнуты: их восемь, и рядом с проверками сверки они
   превращали шаг в стену одинаковых плашек. */
.tb-checks-ok {
  display: flex; flex-direction: column; gap: var(--space-1);
  /* Полка «Сошлось: N» лежит во всю ширину сетки контролей: это не плашка. */
  grid-column: 1 / -1;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface-2);
}
.tb-checks-ok > summary { font-weight: 600; font-size: var(--fs-sm); color: var(--text-muted); }
.tb-checks-ok > .tb-check { margin: 0 var(--space-2); background: var(--surface); }
.tb-checks-ok > .tb-check:last-child { margin-bottom: var(--space-2); }
/* Строка бланка раскрывается: из каких счетов она сложилась. Аудитор не
   подписывается под числом, происхождение которого не видит. */
.tb-line { cursor: pointer; }
.tb-line:hover td { background: var(--surface-2); }
.tb-line:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.tb-line[aria-expanded="true"] td { background: var(--brand-bg-soft); }
/* «В том числе» — отдельная строка бланка: сумма стоит в колонке сумм, а не
   висит посреди названия. */
.tb-subrow-row td { color: var(--text-muted); font-size: var(--fs-sm); padding-top: 2px; }
.tb-subrow-row td:nth-child(2) { padding-left: calc(var(--space-2) + 12px); }
.tb-subrow-row.tb-subrow-warn td { background: var(--warn-bg); }
/* Корректировка аудитора — не часть учёта клиента, и выглядеть должна иначе:
   своя полоса у строки бланка и учётная сумма следом, чтобы читалось «было — стало». */
.tb-correction-row td { font-size: var(--fs-sm); background: var(--info-bg); }
.tb-correction-row td:nth-child(2) { padding-left: calc(var(--space-2) + 12px); }
.tb-correction-row .link-btn { margin-left: 8px; font-size: var(--fs-xs); }
.tb-correction-row.tb-correction-base td { color: var(--text-muted); }
.tb-line-corrected td:first-child { box-shadow: inset 3px 0 0 var(--info); }
/* Пустые строки бланка: в форме они есть всегда (приказ 66н — 22 строки ф.2),
   но по умолчанию свёрнуты. Кнопка под таблицей снимает этот класс: строку надо
   уметь раскрыть, чтобы внести «Прочее» корректировкой. */
.tb-hide-empty .tb-line-empty { display: none; }
.tb-line-empty td { color: var(--text-muted); }
/* Ввод правки живёт во врезке расшифровки: сначала видно, из чего сложилась
   строка, и там же её исправляют. */
.tb-correction-form {
  display: flex; gap: var(--space-2); align-items: flex-end; flex-wrap: wrap;
  margin-top: var(--space-2); padding-top: var(--space-2); border-top: 1px solid var(--line);
}
.tb-correction-form label { display: flex; flex-direction: column; gap: var(--space-0); font-size: var(--fs-sm); }
.tb-correction-form input { min-width: 180px; }
.tb-correction-form input[name="comment"] { min-width: 320px; }
.tb-correction-form .muted-note { flex-basis: 100%; margin: 0; }

/* Файлы, которые не попали в ведомость: аудитор обязан увидеть их сразу. */
.tb-skipped {
  border: 1px solid var(--warn); border-left-width: 3px; border-radius: var(--radius);
  background: var(--warn-bg); padding: var(--space-2) var(--space-3); margin-bottom: var(--space-3);
}
.tb-skipped ul { margin: var(--space-1) 0 var(--space-1) var(--space-3); padding: 0; }
/* Сданный бланк файлом — запасной путь на случай, когда в ГИР БО нужного года ещё
   нет. Свёрнут по умолчанию: обычный путь — снимок ФНС, и раскрытая форма загрузки
   спорила бы с ним за внимание. */
details.tb-statement > summary {
  cursor: pointer; padding: 6px 0; color: var(--link); font-size: var(--fs-sm);
}
.tb-statement { margin-top: var(--space-2); }
.tb-statement .field { margin-top: var(--space-2); max-width: 320px; }
.tb-statement-warn { margin: var(--space-1) 0 0; padding-left: var(--space-3); font-size: var(--fs-sm); }
.tb-statement-warn li { margin-bottom: 2px; }
.tb-skipped li { margin-bottom: 2px; }
.tb-skipped p { margin: 0; }
/* Врезка расшифровки не должна читаться как продолжение бланка: у неё свой фон
   и отчёркивание слева, иначе слагаемые выглядят как строки формы. */
/* Действие над бланком стоит над таблицей, у вкладок: сначала выбираешь форму,
   потом решаешь, что с ней делать. */
.tb-form-actions { margin: var(--space-2) 0; }
/* Бланки идут подряд: у каждого своя шапка с названием формы и выгрузкой.
   Вкладками раньше переключались все четыре бланка, и баланс с отчётом о
   финансовых результатах нельзя было увидеть рядом — а читают их вместе. */
.tb-form-one + .tb-form-one { margin-top: var(--space-3); }
/* Заголовок формы в сверке: без него строки баланса и отчёта о финансовых
   результатах читались одним списком. */
.tb-form-title { margin: var(--space-2) 0 var(--space-1); }
.tb-form-title:first-of-type { margin-top: 0; }
.tb-form-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-2); flex-wrap: wrap; margin: var(--space-2) 0 var(--space-1);
}
/* Вердикт по итоговым строкам — первое, что нужно прочитать в сверке.
   Раньше его не было вовсе: аудитор искал расхождения глазами по всей таблице. */
.tb-verdict {
  display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap;
  margin: 0 0 var(--space-2); padding: var(--space-1) var(--space-2);
  border-left: 3px solid var(--ok); border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  background: var(--ok-bg); font-size: var(--fs-sm);
}
.tb-verdict.bad { border-left-color: var(--crit); background: var(--crit-bg); }
/* Часть строк сверить не с чем: это не «сошлось» и не «расхождение» — */
/* третье состояние, и цветом оно обязано отличаться от зелёного. */
.tb-verdict.warn { border-left-color: var(--warn); background: var(--warn-bg); }
/* Номер строки — своя колонка: слитно с названием он читался как часть текста,
   а глазами ищут именно код. Звёздочка помечает итоговые строки формы. */
/* Адрес в тексте — единая строка без пробелов: на экране «такой страницы нет»
   ссылка длиной 477 px уводила вбок всю страницу телефона (замер 13.09.2026).
   Даём такой строке переноситься. Именно break-word, не anywhere: anywhere
   объявляет минимальной шириной элемента один символ, и соседи во flex
   схлопываются — этим уже обжигались на шаге 0. */
code { overflow-wrap: break-word; }
.tb-table .tb-code { white-space: nowrap; font-variant-numeric: tabular-nums; width: 1%; }
.tb-star { margin-left: 4px; color: var(--brand); }
.tb-parts-title { font-size: var(--fs-xs); color: var(--text-muted); margin: 0 0 4px; }
.tb-parts { display: flex; flex-direction: column; gap: var(--space-0); padding: 6px 0; }
.tb-part {
  display: grid; grid-template-columns: 14px minmax(0, 1fr) auto; gap: var(--space-1);
  align-items: baseline; font-size: var(--fs-sm);
}
.tb-part-sign { color: var(--text-muted); text-align: center; }
.tb-part-sum { font-variant-numeric: tabular-nums; }
.tb-part-total { border-top: 1px solid var(--line); padding-top: 5px; font-weight: 600; }
.tb-table tr.row-diff td { background: var(--crit-bg); }
.tb-table tr.row-accent.row-diff td { background: var(--crit-bg); }
.text-err { color: var(--crit); }
.text-warn { color: var(--warn); }

/* Нижний ярус главной: «где стоит работа» и находки рядом, а не длинной лентой. */
/* Две равные колонки и одинаковая высота карточек: списки в них разной длины
   (шагов бывает два, уровней находок всегда четыре), и без выравнивания низ
   экрана уходил ступенькой. */
.home-columns {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2); align-items: stretch;
}
@media (max-width: 900px) { .home-columns { grid-template-columns: minmax(0, 1fr); } }
.home-columns > .card { display: flex; flex-direction: column; }
.home-columns .block-title { margin: 0 0 var(--space-1); }
/* Четыре уровня находок — сеткой 2×2, а не лентой: лентой карточка выходила на
   67 px выше соседней, и под коротким списком «где стоит работа» зияла пустота.
   Две колонки при четырёх уровнях всегда заполняются целиком, сироты не бывает. */
.find-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-1); }
.find-cell {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-1);
  padding: 8px 12px; border: 1px solid var(--line); border-radius: var(--radius-sm);
}
.find-cell .find-name { font-size: var(--fs-sm); color: var(--text-muted); }
@media (max-width: 480px) { .find-grid { grid-template-columns: minmax(0, 1fr); } }

/* Числовые колонки в таблице просрочек узкие, иначе на широком экране цифра
   стоит посреди пустоты, далеко от своего заголовка. */
.overdue-table th.num, .overdue-table td.num { width: 145px; }
.overdue-table th:last-child, .overdue-table td:last-child { width: 90px; }
.overdue-table td:last-child a { display: inline-block; padding: 4px 0; }
/* Действия внутри предупреждения: кнопки в ряд, пояснение целой строкой под ними —
   встроенная в абзац кнопка терялась среди текста, а два действия рядом читаются
   как выбор из двух путей. */
.notice-action .muted-note { flex: 1 1 100%; max-width: 80ch; }
/* Кнопка-ссылка внутри подписи к полю: действие рядом с текстом, но не отдельным
   блоком — иначе строка подсказки разрывается пополам. */
.link-btn {
  background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--brand); font: inherit; text-decoration: underline;
}
.link-btn:hover, .link-btn:focus { color: var(--brand-strong, var(--brand)); }
/* Галочка с подписью в одну строку: сама галочка не растягивается на всю ширину
   поля, а текст рядом остаётся кликабельным. */
.checkline { display: flex; align-items: center; gap: var(--space-1); font-weight: 600; cursor: pointer; min-height: 24px; }


/* ---------- Сотрудники и доступ ---------- */
/* Отключённый сотрудник остаётся в списке: его действия в журнале никуда не
   делись, и понимать, кто это был, нужно. Раньше строку гасили `opacity: 0.55`
   — axe-core 13.09.2026 насчитал 11 провалов контраста: прозрачность бьёт по
   всему разом, включая инициалы в кружке. Приглушаем фоном и цветом подписей,
   а сигнал «отключён» и без того стоит словами в колонке «Доступ». */
.row-off td { background: var(--surface-2); }
.row-off .crow-sub { color: var(--text-muted); }
.row-actions { white-space: nowrap; text-align: right; }
.row-actions .btn { padding: 7px 10px; font-size: var(--fs-xs); min-height: 32px; }
/* Цель нажатия в таблице — не ниже 32 px: списком состояния промахиваются
   мышью, а на телефоне и подавно. */
td select, td input:not([type=checkbox]):not([type=radio]) { min-height: 32px; }
/* Пароль показывается один раз — крупно, чтобы его можно было прочитать вслух
   и не ошибиться в символе. */
.pass-shown {
  font-size: var(--fs-h3); letter-spacing: 0.04em; text-align: center;
  background: var(--surface-2); border: 1px dashed var(--line);
  border-radius: var(--radius); padding: var(--space-2); margin: var(--space-2) 0;
  user-select: all; word-break: break-all;
}

/* ---------- Единый вертикальный ритм страницы ----------
   Зазоры между секциями набегали из margin-ов самих блоков и гуляли 0/10/15/20 px:
   на «Как работать» три карточки подряд стояли вплотную и читались одной кашей,
   на «Сотрудниках» таблица прилипала к плиткам. Ритм задаёт страница, а не блок. */
#view { display: flex; flex-direction: column; gap: var(--space-3); }
#view > * { margin-top: 0; margin-bottom: 0; }

/* Журнал сбоев на «Интеграциях» — свёрнут, пока сбои есть: 20 строк технических
   сообщений превращали страницу настроек в лог-файл, а ключ DaData терялся сверху. */
.int-errors > summary {
  display: flex; align-items: center; gap: var(--space-1);
  cursor: pointer; list-style: none; overflow: hidden;
}
.int-errors > summary::-webkit-details-marker { display: none; }
.int-errors > summary::after {
  content: '›'; margin-left: auto; color: var(--text-muted); transform: rotate(90deg);
}
.int-errors[open] > summary::after { transform: rotate(-90deg); }
.int-sources .sum-card { height: 100%; }
.int-sources { grid-template-columns: repeat(2, minmax(0, 1fr)); }

/* Значок плитки — в мягком фирменном квадрате, один и тот же размер на всех
   карточках ряда (замер с основной программы: 28×28, скругление 8). */
.sum-ico {
  position: absolute; top: 14px; right: 14px;
  width: 28px; height: 28px; border-radius: var(--radius-sm);
  display: grid; place-items: center;
  background: var(--info-bg); color: var(--brand);
}
.sum-card.accent .sum-ico { background: var(--crit-bg); color: var(--crit); }

/* ---------- Вкладки внутри шага ----------
   Шаг 0 держал шесть блоков одной простынёй на 2100 px: перечень, письма, кабинет,
   напоминание, переписку и присланные файлы. Владелец: «куча блоков и пунктов, и
   непонятно что и как и где». Вкладка показывает одну задачу за раз. */
.step-tabs {
  display: flex; gap: var(--space-0); flex-wrap: wrap;
  border-bottom: 1px solid var(--line); padding-bottom: 0; margin-bottom: var(--space-2);
}
.step-tab {
  display: inline-flex; align-items: center; gap: var(--space-0);
  padding: 9px 14px; border: none; background: none; cursor: pointer;
  font-family: var(--font-brand); font-size: var(--fs-text); color: var(--text-muted);
  border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.step-tab:hover { color: var(--text); }
.step-tab.active { color: var(--brand); font-weight: 600; border-bottom-color: var(--brand); }
.tab-count {
  font-size: var(--fs-xs); font-weight: 600; color: var(--text-muted);
  background: var(--surface-2); border-radius: var(--radius-pill); padding: 1px 7px;
}
.step-tab.active .tab-count { background: var(--info-bg); color: var(--brand); }
/* Панель вкладки — обычная колонка; кабинет клиента и перечень были в сетке 2×1,
   теперь каждая занимает всю ширину и ничего не жмётся в 380 px. */
.step-pane { display: flex; flex-direction: column; gap: var(--space-2); }

/* Поля карточки клиента видны всегда. Прозрачная рамка «до наведения» выглядела
   как обычный текст: владелец не понял, что сюда можно писать. */
.kv > input {
  border: 1px solid var(--line); background: var(--bg); padding: 5px 8px;
  font-size: var(--fs-text); border-radius: var(--radius-sm); min-width: 0;
}
.kv > input:hover { border-color: var(--line-strong); }
.kv > input:focus { border-color: var(--brand); }
.kv > input::placeholder { color: var(--text-muted); opacity: 0.7; }

/* Ряд шагов маршрута в карточке клиента — это кнопки, а не метки состояния.
   Владелец нажимал и не понимал, что произойдёт: над рядом стоит подпись, а
   подсветку при наведении даёт сама полоса (`.route-pill:hover`). */
.client-route-hint { font-size: var(--fs-xs); color: var(--text-muted); margin: 0 0 6px; }

/* ------------------------------------------------------------------
   Полусвёрнутое окно (561–900 px): меню остаётся сбоку, колонкой иконок.

   Владелец 02.09.2026: «а почему нельзя сделать меню сбоку, как в других
   моих программах». Раньше на этой ширине меню схлопывалось в верхнюю
   полосу и съедало 254 px до содержимого — четверть экрана. Колонка в
   64 px забирает столько же по ширине, но не сдвигает работу вниз и
   держит тот же порядок пунктов, что на большом экране.

   Ниже 561 px колонка остаётся полосой: там 64 px из 390 — заметная доля
   ширины, а строк меню всего шесть.
   ------------------------------------------------------------------ */
@media (min-width: 561px) and (max-width: 900px) {
  .layout { grid-template-columns: 64px minmax(0, 1fr); }
  .sidebar {
    flex-direction: column; flex-wrap: nowrap; align-items: center;
    gap: var(--space-2); padding: 14px 8px;
    height: 100vh; position: sticky; top: 0; overflow-y: auto;
    border-right: 1px solid var(--line); border-bottom: none;
  }
  /* Логотип со словами в 48 px не читается — на его месте фирменный знак. */
  .brand { flex-direction: column; align-items: center; gap: 0; width: 100%; margin: 0; }
  .brand-logo, .brand-sub { display: none; }
  .rail-only { display: grid; }

  .nav { flex-direction: column; flex-wrap: nowrap; overflow: visible;
    width: 100%; flex: 0 1 auto; gap: var(--space-0); }
  .nav-group { flex-direction: column; gap: var(--space-0); }
  .nav.has-more { -webkit-mask-image: none; mask-image: none; }
  .nav a { justify-content: center; width: 40px; padding: 0; margin: 0 auto; }
  /* Подпись прячем от глаза, но не от программы чтения с экрана и не от
     поиска по странице: у пункта должно остаться имя. Что это за иконка,
     человек узнаёт из подсказки на наведение (её ставит main.js). */
  .nav-label, .nav-tail, .side-search-text, .side-search-key {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  /* Все цели в колонке одного размера и на одной оси: поиск был высотой
     17 px при иконках в 40 — колонка выглядела рваной, а по WCAG 2.2 это
     ещё и цель нажатия меньше нормы в 24 px. */
  .side-search { width: 40px; height: 40px; padding: 0; justify-content: center; margin: 0 auto; }
  .brand-mark.rail-only { width: 40px; height: 40px; }
  /* На колонке иконка — единственный опознавательный знак пункта: приглушать
     её нечем, подписи рядом всё равно нет. */
  .nav .ico { opacity: 1; }

  /* «Требует внимания» на колонке — одно число со ссылкой: сколько ждёт
     действия. Полную фразу показывает подсказка на наведение. */
  .week { margin: auto 0 0; padding: 0; border: none; background: none; }
  .week-head, .att-row, .week-note { display: none; }
  .att-link {
    display: flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; margin: 0 auto; font-size: 0;
    border-radius: var(--radius-sm); background: var(--info-bg);
  }
  .att-link::before {
    content: attr(data-count);
    font-size: var(--fs-text); font-weight: 600; color: var(--brand);
  }
  .att-link[data-kind="late"] { background: var(--crit-bg); }
  .att-link[data-kind="late"]::before { color: var(--crit); }

}

/* Ссылки под сводкой «Моя работа»: две кнопки в ряд, без своей полосы отступов. */
.profile-links { display: flex; flex-wrap: wrap; gap: var(--space-1); margin-top: var(--space-2); }

/* Одиночный чип в шапке (например, «Показывать сделанные») на телефоне тянулся
   во всю ширину и читался как поле ввода — держим его по содержимому. */
.head-actions > .chip { align-self: start; width: auto; }

