/* Приёмка компаний — дизайн в стиле Apple (SF-типографика, стеклянная шапка,
   белые карточки на #f5f5f7, pill-кнопки, мягкие тени). Без фреймворков. */

/* АТРИБУТ `hidden` ОБЯЗАН ПРЯТАТЬ — правило на весь проект.

   У браузера `[hidden]` это всего лишь `display: none` в таблице умолчаний, и
   ЛЮБОЕ своё `display` его перебивает. В этом проекте на грабли наступали
   ТРИЖДЫ: список адресатов в чате (`.chat-suggest`), кнопки «Сохранить» и
   «Показать» в «Оплатах» (24.08.2026) и — уже с общим правилом — таблетка
   периода со списком периодов, которые без скриптов оставались на экране
   (найдено 27.08.2026 на снимке для заказчика). Точечные правила ловили по
   одному случаю за раз, поэтому теперь правило одно и с `!important`: оно
   обязано бить любое `display`, где бы тот ни стоял.

   Показать спрятанное `hidden` из CSS после этого нельзя — и не нужно: в
   проекте `hidden` всегда снимают скриптом, атрибутом. */
[hidden] { display: none !important; }

:root {
  /* Строка с плашкой этапа над карточкой: её высота и отступ под ней. Правая
     колонка сдвигается ровно на эту сумму, чтобы верх её первого блока совпал
     с верхом первого блока слева (25.07.2026). Одно место на оба правила. */
  --round-btn: 34px;            /* круглая кнопка: чат, «Назад», отправка */
  --company-head-h: 27px;
  --company-head-gap: 14px;
  --bg-page: #f5f5f7;
  --bg-elevated: #ffffff;
  --bg-fill: #e8e8ed;
  --bg-fill-subtle: rgba(120, 120, 128, 0.08);
  --text: #1d1d1f;
  --text-secondary: #56565c;
  --text-tertiary: #6e6e73;
  --separator: rgba(0, 0, 0, 0.08);
  --hairline: #d2d2d7;
  --accent: #0071e3;
  --accent-hover: #0077ed;
  --accent-tint: rgba(0, 113, 227, 0.12);
  --link: #0066cc;
  --green: #34c759;
  --green-text: #1e7a35;
  --green-tint: rgba(52, 199, 89, 0.12);
  --red: #ff3b30;
  --red-text: #c9000f;
  --red-tint: rgba(255, 59, 48, 0.08);
  --orange: #ff9500;
  --orange-text: #b53000;
  --orange-tint: rgba(255, 149, 0, 0.14);
  /* Цвета этапов — из палитры заказчика (20.07.2026). Подписи на них тёмные:
     белый текст на этих заливках даёт 2.1–2.6:1 и не проходит WCAG AA,
     тёмный — 5.8–8.0:1. Мягкий вариант используется как фон колонки. */
  --stage-sbor: #3fa9f5;
  /* знак на светлой подложке: сам --stage-sbor на ней не читается (2.2:1),
     потемнённый даёт 4.6:1 — тот же приём, что у --green-text и --red-text */
  --stage-sbor-ink: #1663a0;
  --stage-sbor-soft: rgba(63, 169, 245, 0.10);
  --stage-kopiya: #7ac943;
  --stage-kopiya-soft: rgba(122, 201, 67, 0.12);
  --stage-realny: #22b14c;
  --stage-realny-soft: rgba(34, 177, 76, 0.10);
  /* «ЭЦП физлиц» (22.07.2026): сиреневый в ряд к голубому и зелёному.
     Контраст тёмного текста на заливке 6.2:1 — WCAG AA с запасом. */
  --stage-ecp: #ab8fe0;
  --stage-ecp-soft: rgba(171, 143, 224, 0.12);
  --stage-arhiv: #c7c7cc;
  --stage-arhiv-soft: rgba(120, 120, 128, 0.08);
  --radius-s: 8px;
  --radius-m: 12px;
  --radius-l: 18px;
  --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.06);
  --shadow-lift: 0 6px 20px rgba(0, 0, 0, 0.10);
  --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display",
          "Segoe UI Variable Text", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}

* { box-sizing: border-box; }
/* Место под полосу прокрутки держим всегда. Живой поиск на доске меняет
   высоту страницы: полоса то появляется, то исчезает, и вместе с ней на
   ширину полосы прыгает ВСЁ содержимое, включая строку поиска по центру
   (01.08.2026) */
html { scrollbar-gutter: stable; }
body {
  margin: 0;
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.45;
  color: var(--text);
  background: var(--bg-page);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
main { padding: 20px 28px 64px; max-width: 1400px; margin: 0 auto; }
h1 { font-size: 28px; font-weight: 700; letter-spacing: -0.02em; margin: 10px 0 18px; }
h2 { font-size: 17px; font-weight: 600; letter-spacing: -0.01em; margin: 0 0 14px; }
a { color: var(--link); text-decoration: none; }
a:hover { text-decoration: underline; }
hr { border: none; border-top: 1px solid var(--separator); margin: 18px 0; }

/* --- Шапка: стеклянная, липкая --- */
.topbar {
  position: sticky; top: 0; z-index: 100;
  display: flex; align-items: center; gap: 28px;
  padding: 12px 28px;
  background: rgba(251, 251, 253, 0.82);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid var(--separator);
}
.topbar nav { display: flex; align-items: center; gap: 15px; flex: 1; }
/* Пункты навигации — такие же прозрачные чипы, как кнопки загрузки файлов
   (21.07.2026): та же высота 34px и контур, ширина — по надписи. */
.topbar nav a {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 34px; padding: 6px 14px; border-radius: 980px;
  border: 1px solid var(--hairline);
  color: var(--text-secondary); font-size: 14px; text-decoration: none;
  /* фиксируем межстрочный: с наследованным 1.45 чип выходил 34.3px и стоял
     на 0.3px выше «Выйти», у которой свой line-height (24.07.2026) */
  line-height: 1.4; white-space: nowrap;
  transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
.topbar nav a:hover {
  color: var(--text); text-decoration: none;
  border-color: var(--text-tertiary);
}
.topbar nav a:focus-visible {
  outline: none; color: var(--text);
  border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-tint);
}

/* Круглой кнопки «+» с подсказкой больше нет (21.07.2026): вместо неё обычная
   ссылка «Добавить ООО / ИП», оформленная как остальные пункты навигации. */
.userbox { display: flex; align-items: center; gap: 12px; font-size: 13px; color: var(--text-secondary); }
/* «Выйти» — обычного размера кнопок шапки: те же 34px, что у чипов навигации
   и кнопок загрузки файлов (24.07.2026) */
.userbox .btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 34px; padding: 6px 16px; font-size: 14px;
}
/* «Пользователи» рядом с «Выйти»: выглядит так же, как ссылки левой навигации */
/* Подчёркнута (25.07.2026): рядом стоит имя с ролью — обычный текст, и без
   подчёркивания ссылку от него на глаз не отличить. Подчёркивание чуть
   отодвинуто от букв, чтобы не задевать их нижние выносные. */
.userbox-link {
  color: var(--text); opacity: 0.72; font-size: 14px;
  text-decoration: underline; text-underline-offset: 3px;
  transition: opacity 0.2s ease;
}
.userbox-link:hover { opacity: 1; text-decoration: underline; }

/* --- Flash --- */
.flashes { max-width: 1400px; margin: 14px auto 0; padding: 0 28px; }
.flash { padding: 11px 16px; border-radius: var(--radius-m); margin-bottom: 8px; font-size: 14px; }
.flash-error { background: var(--red-tint); color: var(--red-text); }
.flash-success { background: var(--green-tint); color: var(--green-text); }
/* Предупреждение о кончающемся диске (24.07.2026): не ошибка действия, а
   состояние сервера — поэтому янтарное, а не красное, и висит постоянно. */
.flash-warning {
  background: rgba(255, 149, 0, 0.14); color: #8a4b00;
  box-shadow: inset 0 0 0 1px rgba(255, 149, 0, 0.35);
}
.flash-note { display: block; margin-top: 3px; font-size: 12.5px; opacity: 0.8; }

/* --- Кнопки: pill --- */
.btn {
  display: inline-block; padding: 8px 18px;
  border-radius: 980px; border: none;
  background: var(--bg-fill-subtle); color: var(--text);
  cursor: pointer; font-family: inherit; font-size: 14px; font-weight: 500;
  text-decoration: none; line-height: 1.4;
  transition: background 0.2s ease, transform 0.15s ease, box-shadow 0.2s ease;
}
.btn:hover:not(:disabled) { background: rgba(120, 120, 128, 0.16); text-decoration: none; }
.btn:active:not(:disabled) { transform: scale(0.97); }
.btn-primary { background: var(--accent); color: #fff; }
.btn-primary:hover:not(:disabled) { background: var(--accent-hover); }
.btn-danger { background: var(--red); color: #fff; }
.btn-danger:hover:not(:disabled) { background: #e0352b; }
.btn-small { padding: 5px 13px; font-size: 13px; }
/* Админские действия карточки: «Удалённые» и удаление — в один ряд, с зазором */
.admin-actions { display: flex; flex-wrap: wrap; gap: 10px; }

/* --- Подтверждение (21.07.2026) ------------------------------------------
   Своё окно вместо браузерного confirm: тот показывал «localhost:5000» и
   кнопки операционной системы. Разметку строит confirm-dialog.js. */
.modal-overlay {
  position: fixed; inset: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center; padding: 20px;
  background: rgba(0, 0, 0, 0.28);
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
  animation: modal-fade 0.15s ease;
}
.modal {
  /* Воздух НАД кнопкой и ПОД ней — одна величина (правка заказчика
     01.09.2026: «Сделай высоту под кнопкой такую же, как над кнопкой»).
     Было 20 сверху и 18 снизу; разницу в два пикселя он увидел на глаз.
     Связано переменной, а не двумя числами: двумя они разъедутся снова —
     тот же приём, что у кнопки «Закрыть» в окне выбора эскиза. */
  --modal-gap: 20px;
  width: 100%; max-width: 420px; padding: 22px 24px var(--modal-gap);
  background: var(--bg-elevated); border-radius: var(--radius-l);
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.22);
  animation: modal-rise 0.18s cubic-bezier(0.2, 0.8, 0.3, 1);
}
.modal-text {
  margin: 0 0 var(--modal-gap); font-size: 15px; line-height: 1.45;
  color: var(--text);
  text-align: center;
  /* длинное имя файла не должно распирать окно */
  overflow-wrap: anywhere;
}
.modal-buttons { display: flex; justify-content: center; gap: 10px; }
/* Перечень в окне-сообщении (31.07.2026): «чего не хватает для карточки».
   Заголовок по центру, а пункты — по левому краю: список из трёх строк,
   выровненный по центру, читается как стихи. */
.modal-list {
  margin: -8px 0 var(--modal-gap); padding: 0 0 0 20px;
  font-size: 14px; line-height: 1.5; color: var(--text-secondary);
  text-align: left; overflow-wrap: anywhere;
}
.modal-list li { margin: 4px 0; }

/* --- Окно выбора эскиза печати (14.08.2026) ------------------------------
   Кнопка «Эскиз печати» показывает ВСЕ шаблоны сразу, человек выбирает
   лучший. Окно шире обычного: печатей четырнадцать, и разглядывать их надо
   не по одной. Цвета и скругления — те же токены, что у карточек. */
.modal-wide { max-width: 980px; }
/* Воздух над и под кнопкой «Закрыть» РАВЕН высоте самой кнопки (правка
   заказчика 31.08.2026). Связано одной величиной, а не тремя числами: тремя
   числами это разъехалось бы при первой же правке .btn — а заказчик видит
   разницу меньше пикселя. Отсюда и высота кнопки: своей она была 35.59px
   (8+8 padding и строка 19.6px), и «равно» получалось бы приблизительным. */
.eskiz-pick { --pick-close: 36px; padding: 24px 24px var(--pick-close); }
.eskiz-pick .modal-buttons .btn { height: var(--pick-close); }
.eskiz-pick-head { text-align: center; margin-bottom: 16px; }
.eskiz-pick-title {
  margin: 0; font-size: 19px; font-weight: 600; color: var(--text);
}
.eskiz-grid {
  display: grid; gap: 14px; margin-bottom: var(--pick-close);
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  /* печатей много: сетка прокручивается внутри себя, а окно остаётся на
     месте — иначе кнопка «Закрыть» уезжает за нижний край экрана */
  max-height: min(62vh, 560px); overflow-y: auto; padding: 2px;
}
.eskiz-pick-wait {
  grid-column: 1 / -1; margin: 28px 0; text-align: center;
  font-size: 14px; color: var(--text-tertiary);
}
.eskiz-card {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 10px 10px 12px; border-radius: var(--radius-m);
  border: 1px solid var(--hairline); background: var(--bg-elevated);
  cursor: pointer; font: inherit; color: var(--text-secondary);
  transition: border-color 0.15s ease, box-shadow 0.15s ease,
              transform 0.15s ease;
}
.eskiz-card img { width: 100%; height: auto; display: block; }
.eskiz-card-label { font-size: 12.5px; }
.eskiz-card:hover {
  border-color: var(--accent); box-shadow: var(--shadow-lift);
  transform: translateY(-2px);
}
.eskiz-card:focus-visible {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}
/* тот, что стоит в слоте сейчас: обведён и подписан — иначе в сетке из
   четырнадцати одинаковых кругов не найти, какой уже выбран */
.eskiz-card-current {
  border-color: var(--accent); background: var(--accent-tint);
  color: var(--text);
}
/* выбранный сейчас сохраняется: гасим остальные плитки, чтобы клик по
   второй не подменил файл, пока едет первый ответ */
.eskiz-pick-busy .eskiz-card { pointer-events: none; opacity: 0.45; }
.eskiz-pick-busy .eskiz-card-picked {
  opacity: 1; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-tint);
}
@keyframes modal-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes modal-rise {
  from { opacity: 0; transform: translateY(8px) scale(0.98); }
  to { opacity: 1; transform: none; }
}
.btn:disabled { background: var(--bg-fill); color: var(--text-tertiary); cursor: not-allowed; }
.btn:focus-visible { outline: none; box-shadow: 0 0 0 4px var(--accent-tint); }

/* --- Вход --- */
.login-box {
  max-width: 380px; margin: 96px auto; background: var(--bg-elevated);
  padding: 40px 36px; border-radius: var(--radius-l); box-shadow: var(--shadow-lift);
}
.login-box h1 { text-align: center; font-size: 26px; margin: 0 0 24px; }
.login-box label { display: block; margin-bottom: 16px; }
.login-box input { width: 100%; margin-top: 6px; }
.login-box .btn { width: 100%; margin-top: 8px; padding: 11px 18px; font-size: 15px; }

/* --- Формы --- */
input, select, textarea {
  font: inherit; color: var(--text);
  padding: 9px 12px; border: 1px solid var(--hairline);
  border-radius: var(--radius-m); background: var(--bg-elevated); width: 100%;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
/* Пока файл уходит на сервер, плитка не принимает кликов: иначе повторный
   клик открывал бы проводник и слал второй такой же запрос (31.07.2026) */
.slot.uploading .slot-add { pointer-events: none; opacity: 0.65; }

/* Ошибка под полем (app.js): красным, тем же тоном, что и звёздочка «обязательно» */
.field-error { font-size: 12px; line-height: 1.35; color: var(--red-text); }

/* Название банка по БИК — подпись под строкой р/с (30.07.2026), не поле.
   order: 1 — всегда НИЖЕ поля и ниже красной ошибки (её app.js дописывает в
   конец плитки, а подпись поля поднята order: -1).
   width: 0 + min-width: 100% — длинное название не должно раздувать сетку:
   .card-grid меряется по содержимому (width: max-content). */
.tile-note {
  order: 1;
  width: 0; min-width: 100%;
  font-size: 12px; line-height: 1.35; color: var(--text-secondary);
  overflow-wrap: anywhere;
}
/* БИК не введён — карточка выглядит ровно как раньше, без пустой полосы */
.tile-note:empty { display: none; }
/* «не найден» / «справочник не загружен» — тем же кеглем, но глуше: это не
   ошибка, ничего не блокируется */
.tile-note-miss { color: var(--text-tertiary); }

/* Правило для всего проекта: длинное значение в поле ввода не растягивает
   вёрстку и не показывает свой хвост — видно начало, конец срезан многоточием.
   Прокрутку поля к началу возвращает app.js (браузер оставляет её в конце). */
input { text-overflow: ellipsis; }
/* Галочка выпадающего списка.
   Родную стрелку Chrome рисует вплотную к краю и на padding-right не реагирует,
   поэтому прячем её (appearance: none) и рисуем свою фоном — её положение
   задаётся точно. Сдвиг от края = обычный отступ плюс ширина самой галочки. */
select {
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%236e6e73' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  /* галочка отступает от края поля как обычный текстовый отступ */
  background-position: right 12px center;
  padding-right: 30px;
}
/* у Firefox остаётся пунктирная подсветка текста в закрытом списке */
select:-moz-focusring { color: transparent; text-shadow: 0 0 0 var(--text); }
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-tint);
}
input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--accent); }
input:disabled, select:disabled, textarea:disabled {
  background: var(--bg-page); color: var(--text-secondary);
}
input.saving, select.saving, textarea.saving { border-color: var(--orange); box-shadow: 0 0 0 4px var(--orange-tint); }
input.saved, select.saved, textarea.saved { border-color: var(--green); box-shadow: 0 0 0 4px var(--green-tint); }
label { font-size: 13px; color: var(--text-secondary); display: block; }
label input, label select, label textarea { margin-top: 6px; }
.tile label input, .switch input { margin-top: 0; }
/* Страница «Пользователи» (25.07.2026): заголовки и блок добавления по
   центру — таблица наверху широкая, и прижатая к левому краю форма рядом с
   ней выглядела обрезком страницы. */
.page-title { text-align: center; }
.create-form {
  max-width: 540px; background: var(--bg-elevated); padding: 28px;
  border-radius: var(--radius-l); box-shadow: var(--shadow-card);
  margin-left: auto; margin-right: auto;
  /* поля по два в ряд (25.07.2026): «Логин · Пароль», «Имя · Почта»,
     «Роль» одна слева. Промежуток по горизонтали 18px, по вертикали его
     даёт margin-bottom у подписи. minmax(0,…) — чтобы длинная подпись
     («Электронная почта») не растягивала свою колонку шире половины */
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 18px;
}
/* воздух вокруг заголовка формы: сверху отбивка от таблицы, снизу — столько
   же, сколько между полями внутри формы */
.create-form-title { margin: 40px 0 16px; }
.create-form label { margin-bottom: 16px; }
/* Кнопка — своей строкой во всю ширину и по центру. Сверху 28px, как и снизу
   до края блока (padding формы): 16px даёт margin-bottom подписи, ещё 12
   добираем здесь (25.07.2026). */
.create-form button[type="submit"] {
  grid-column: 1 / -1; justify-self: center; margin-top: 12px;
}

@media (max-width: 560px) {
  /* на телефоне два поля в ряд не помещаются — возвращаем колонку */
  .create-form { grid-template-columns: 1fr; }
}
/* форма добавления компании: плитки как на карточке, без белой подложки */
/* кнопка — по центру, под отцентрированной сеткой плиток */
/* «Добавить» переехала внутрь .card-grid (плитка .tile-action-pill), поэтому
   правила для кнопки — прямого потомка формы — здесь больше нет. */
.hint { color: var(--text-tertiary); font-size: 13px; }

/* --- Подсказки (31.07.2026) ------------------------------------------------
   Свои вместо браузерных: наш шрифт и кегль, скругление и тень как у карточек,
   переносы по \n. Рисует tooltip.js, разметка страниц не меняется — берётся
   обычный title. Тёмная плашка: подсказка должна читаться поверх любого фона. */
.tip {
  /* Выше всего в приложении: модальные окна — 200, липкая шапка — 100.
     Ниже них подсказка пряталась под чужой панелью, а нативную мы к этому
     моменту уже сняли — в чате не показывалось вообще ничего (31.07.2026,
     состязательная проверка). Заводите новый слой — держите его ниже 300. */
  position: fixed; z-index: 300; max-width: 300px;
  /* Ширина — ПО ТЕКСТУ, а не по остатку места справа (06.08.2026, заказчик
     про подсказку «ООО не оплачено»). Без width пузырь ужимается до
     свободного места от своего left до края окна, а кругляшок оплаты стоит
     на плашке справа — короткая подсказка ломалась на две строки. Замер при
     окне 1280: посреди экрана 139×41 (одна строка), у правого края 88×60
     (две), с max-content 139×41 в обоих случаях. Длинные подсказки
     по-прежнему переносятся — их держит max-width (замер: 300×97). */
  width: max-content;
  /* Светлая карточка (выбор заказчика 31.07.2026): те же скругление, тень и
     цвета, что у плашек и полей, — подсказка часть интерфейса, а не системное
     окно. Тёмный вариант выглядел «техническим». */
  padding: 10px 14px; border-radius: var(--radius-m);
  background: var(--bg-elevated); color: var(--text);
  border: 1px solid var(--separator); box-shadow: var(--shadow-card);
  font-size: 13px; line-height: 1.45; font-weight: 400; text-align: left;
  pointer-events: none;              /* не перехватывать мышь у элемента */
  opacity: 0; transform: translateY(2px);
  transition: opacity 0.12s ease, transform 0.12s ease;
}
.tip-shown { opacity: 1; transform: none; }

/* --- Поиск и сортировка над доской (31.07.2026) ---------------------------
   Обычная GET-форма: строка поиска слева, порядок справа. Кнопка «Показать»
   нужна без JS; со скриптом список отправляет форму сам (board.js). */
.board-tools {
  /* поиск и порядок стоят по центру страницы (31.07.2026) */
  display: flex; gap: 10px; align-items: center; justify-content: center;
  /* Списков стало четыре (13.08.2026, фильтр чатов), и в одну строку они
     перестали влезать: замер при окне 1280 — панели нужно 1251px в коробке
     1208.7px, поле поиска уезжало за левый край (x = −14), а страница ехала
     вбок. Перенос честнее сжатия: подписи фильтров заказчик задал дословно,
     а ужать их можно было бы только кеглем. На широком экране (от ~1330px)
     всё по-прежнему одной строкой. */
  flex-wrap: wrap;
  /* снизу столько же, сколько сверху: сверху отступ даёт padding-top у main
     (20px), и панель должна стоять между шапкой и доской ровно посередине */
  margin: 0 0 20px;
}
/* Ширина поля — ровно под текст подсказки, и считает её сам браузер: в
   разметке лежит невидимый близнец с тем же текстом, шрифтом и отступами
   (.board-search-sizer), а поле растягивается на его ширину. Фиксированные
   пиксели тут не годятся: тот же текст на другой машине шире, и последнее
   слово обрезалось. Поле не тянется на всю доску — растянутое, оно выглядело
   формой, а не поиском. */
.board-search { position: relative; flex: none; max-width: 100%; }
.board-search-sizer {
  display: block; height: 0; overflow: hidden; visibility: hidden;
  white-space: pre; font-size: 14px;
  /* отступы поля: 38 слева под лупу, справа обычные 14 плюс 2 на рамку поля
     (box-sizing: border-box) и 6 на воздух */
  padding: 0 22px 0 38px;
}
/* Крестик отодвигает ТЕКСТ, но не меняет ширину поля: близнец остаётся
   прежним. Иначе с каждым набранным знаком панель прыгала — крестик
   появлялся, ширина росла на 20px, и поле, стоящее по центру, уезжало
   влево (замер 01.08.2026: 272px против 292px). Близнецу отступ под
   крестик и не нужен: он меряет подсказку, а её при непустом поле не
   видно. */
.board-search-clearable input { padding-right: 34px; }
/* Своя кнопка очистки уже есть (.board-search-clear), а Chrome у
   input[type=search] рисует ещё и свою — она отъедала место у текста */
.board-search input::-webkit-search-cancel-button { display: none; }
.board-search input {
  width: 100%; padding: 8px 14px 8px 38px;
  border-radius: 980px; border: 1px solid var(--hairline);
  background: var(--bg-elevated); font-size: 14px;
}
.board-search input:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}
/* лупа и крестик не должны перехватывать клик по полю */
.board-search-icon {
  position: absolute; left: 13px; top: 50%; transform: translateY(-50%);
  width: 17px; height: 17px; color: var(--text-tertiary); pointer-events: none;
}
/* крестик виден, только когда в поле что-то есть (класс ставит board.js) */
.board-search-clear { display: none; }
.board-search-clearable .board-search-clear {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  width: 24px; height: 24px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  color: var(--text-secondary); text-decoration: none;
}
/* значок занимает середину круга: у прежнего текстового «×» ink сидел выше
   середины строки, и в подложке он смотрелся приподнятым (01.08.2026) */
.board-search-clear svg { width: 12px; height: 12px; display: block; }
.board-search-clear:hover { background: var(--bg-fill); text-decoration: none; }
/* ТАБЛЕТКА ПАНЕЛИ — одна на списки и на отрезок дат (25.08.2026).
   Вид (радиус, рамка, кегль, отступы) объявлен ЗДЕСЬ и только здесь: у
   отрезка дат «с … по …» он обязан совпадать со списками рядом, а две
   похожие копии однажды разъедутся — этим правилом в проекте уже обжигались
   на плашках и кругляшках. */
.board-tools select, .board-tools .board-pill {
  width: auto; padding: 8px 14px; border-radius: 980px;
  font-size: 14px; border: 1px solid var(--hairline);
}
/* у списка справа ещё галочка — место под неё только ему */
.board-tools select { padding-right: 32px; }
/* Подпись и поле внутри таблетки: «с 01.08.2026» читается одной вещью, а не
   подписью и полем порознь. Рамку и кольцо фокуса несёт САМА таблетка —
   иначе внутри неё появилась бы вторая рамка */
.board-pill {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  background: var(--bg-elevated);
}
.board-pill > span { font-size: 14px; color: var(--text-tertiary); }
.board-pill input {
  /* базовое правило полей даёт им 100% ширины, рамку и свой отступ, а внутри
     таблетки нужно ровно обратное */
  width: auto; margin: 0; padding: 0; border: none; background: none;
  font-size: 14px; color: var(--text);
  /* Рост берём У СТРОКИ (1lh), а не у поля: Chrome рисует input[type=date]
     сегментами и делает его коробку на 2px выше строки — замер 22.28 против
     20.3. Из-за этих двух пикселей таблетка выходила на 1.99px выше соседних
     списков, а в этом проекте видно и 0.9. С 1lh поле равно строке, строка
     равна содержимому списка — и высоты совпадают ПО ПОСТРОЕНИЮ, а не
     подгонкой числа: поменяется кегль панели, и всё поедет вместе. */
  height: 1lh;
}
.board-pill input:focus { outline: none; box-shadow: none; }
.board-pill:focus-within {
  border-color: var(--accent); box-shadow: 0 0 0 4px var(--accent-tint);
}
/* пустое поле даты браузер показывает как «дд.мм.гггг» — это подсказка, а не
   значение, и весить она должна как подсказка. Сверяем АТРИБУТ, а не :invalid:
   пустая необязательная дата совершенно допустима, и :invalid тут не наступает */
.board-pill input[value=""] { color: var(--text-tertiary); }

/* ПЕРИОД ОДНИМ ПОЛЕМ (26.08.2026, выбор заказчика): таблетка с итогом, а
   список периодов и оба поля дат — в меню под ней. Без скриптов таблетки нет
   (она `hidden` в разметке), и меню разворачивается в обычный ряд контролов:
   поэтому у него нет ни `position`, ни фона, пока его не открыли. */
.board-period { position: relative; display: flex; gap: 10px; flex: none; }
/* ВЫПАДАЮЩИЕ СПИСКИ ФИЛЬТРОВ (05.09.2026, просьба заказчика «сделай все
   остальные выпадающие меню фильтров в таком же стиле»). Вид у них общий с
   периодом и приходит из ТЕХ ЖЕ правил ниже — здесь только то, чем они от
   периода отличаются: у периода в меню лежит ряд дат, а тут одни пункты. */
/* ФЛЕКС, а не блок: таблетка внутри — inline-элемент, и в блочной обёртке
   под ней остаётся место под свисающие буквы строки. Замер: обёртка на
   4.4px выше кнопки, и поиск рядом вставал ниже таблеток — панель
   переставала быть одной горизонталью. */
.board-drop { position: relative; flex: none; display: flex; }
/* Близнец со ВСЕМИ подписями держит ширину таблетки: иначе она дышала бы от
   выбора и панель прыгала при каждом щелчке. Тот же приём, что у списка СБИС
   (.board-filter-sizer) и у таблетки периода — пиксели тут промахиваются,
   шрифт у каждой машины свой. Строки внутри близнеца блочные: меряем по самой
   длинной, а не по всем подряд в одну строку. */
.board-drop-sizer {
  display: block; height: 0; overflow: hidden; visibility: hidden;
  white-space: nowrap; font: inherit;
}
.board-drop-sizer > span { display: block; }
/* БЛИЗНЕЦ ЗАДАЁТ И ПОТОЛОК, а не только пол. Одного близнеца мало: сама
   подпись навязывает свою ширину, и таблетка РОСЛА вместо обрезки — замер
   223.6 при мере 174. `width: 0` убирает подпись из подсчёта «по
   содержимому», `min-width: 100%` возвращает её на всю коробку. Тот же
   приём, что у прежнего списка СБИС и у полей дат периода. */
.board-drop-label {
  display: block; width: 0; min-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Окно не у́же таблетки: пункты меряются по своему тексту, а таблетка — по
   тому же тексту плюс отступы и стрелка, и без этого окно вышло бы у́же. */
.board-drop-btn:not([hidden]) ~ .board-drop-menu { min-width: 100%; }
.board-period-btn,
.board-drop-btn {
  /* вид приходит из общего правила таблеток панели (.board-pill) — своего
     здесь только курсор и стрелка */
  cursor: pointer; gap: 8px; font: inherit; color: var(--text);
}
.board-period-chevron,
.board-drop-chevron { width: 12px; height: 8px; color: var(--text-tertiary); }
/* ШИРИНА ТАБЛЕТКИ ПОСТОЯННА (просьба заказчика 27.08.2026). Её держит
   невидимый близнец с самой широкой мыслимой подписью — двумя датами
   (rules.OPLATY_PERIOD_SIZER). Без него таблетка дышала от выбора: 149.09 на
   «Текущем месяце» против 196 на отрезке дат, и стрелка прыгала вместе с ней. */
/* `text-align: left` обязателен: у <button> он по умолчанию `center`, и пока
   подпись была обычным элементом по содержимому, центровать было нечего. С
   фиксированной шириной (близнец ниже) текст встал по центру — замер: 38.12px
   пустоты слева против 14px отступа у соседних полей (правка заказчика
   27.08.2026). */
.board-period-text,
.board-drop-text { display: block; white-space: nowrap; text-align: left; }
/* ЦВЕТ — СЕЛЕКТОРОМ С ДВУМЯ КЛАССАМИ, и это не украшательство.

   Подпись периода и сама дата — прямые <span> внутри пилюли, а у таких стоит
   `.board-pill > span { color: var(--text-tertiary) }` (правило писалось для
   подписей «с» и «по»). Первая правка дописала `color` в `.board-period-text`
   — и цвет не изменился: (0,1,0) против (0,1,1) у соперника, класс плюс
   элемент сильнее. Замер после «починки» дал те же rgb(110,110,115).

   Два класса дают (0,2,0) и бьют соперника при любом порядке в файле. Дату
   красим тем же правилом: она попала под то же общее правило и была тусклой
   с 27.08.2026, хотя заказчик писал только про подпись. */
.board-pill > .board-period-text,
.board-pill > .board-drop-text,
.board-pill > .board-period-shown { color: var(--text); }
.board-period-text-sizer {
  display: block; height: 0; overflow: hidden; visibility: hidden;
  white-space: pre; font: inherit;
}
.board-period-menu,
.board-drop-menu { display: flex; gap: 10px; align-items: center; }
/* Со скриптом меню становится выпадающим — до первого открытия его не видно.
   Признак ставит `payments.js`: без него всё остаётся рядом в строке. */
.board-period-btn:not([hidden]) ~ .board-period-menu,
.board-drop-btn:not([hidden]) ~ .board-drop-menu {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 60;
  display: none; flex-direction: column; align-items: stretch; gap: 6px;
  padding: 6px;
  /* Ширины ЧИСЛОМ здесь больше нет. Стояло `min-width: 236px`, и от правого
     поля до края окна оставалось 39.09px против 6 слева — заказчик увидел
     (27.08.2026). Теперь окно меряется по РЯДУ ДАТ (см. правило пунктов
     ниже), и зазоры равны по построению. */
  background: var(--bg-elevated); border-radius: var(--radius-m);
  /* тень как у меню файлов — одинаковые по смыслу вещи берут её из одного
     представления, а не подбираются каждая своя */
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.16), 0 1px 3px rgba(0, 0, 0, 0.08);
}
/* Селектор ТОЙ ЖЕ длины, что у правила скрытия выше, плюс класс
   открытия: иначе он легче по весу, и меню остаётся спрятанным при
   выставленном классе — на этом и попались (26.08.2026). */
.board-period-open .board-period-btn:not([hidden]) ~ .board-period-menu,
.board-drop-open .board-drop-btn:not([hidden]) ~ .board-drop-menu {
  display: flex;
}
/* `width: 0` убирает пункты из подсчёта «по содержимому»: ширину окна задаёт
   только ряд дат, и подписи пунктов на неё не влияют НИ В КАКОМ шрифте
   (просьба заказчика 27.08.2026: «выбор разных опций не должен влиять на
   ширину окна»). `min-width` возвращает их на всю ширину окна. Тот же приём,
   что у полей дат и у списка СБИС. */
.board-period-items,
.board-drop-items { display: flex; flex-direction: column; }
/* `width: 0` — ТОЛЬКО у периода: там ширину окна задаёт ряд дат, и подписи
   пунктов на неё влиять не должны. У обычного фильтра ряда дат нет, и с
   нулевой шириной окно схлопнулось бы в ничто. */
.board-period-items {
  width: 0; min-width: 100%;
}
.board-period-item,
.board-drop-item {
  text-align: left; padding: 8px 12px; border: 0; border-radius: var(--radius-s);
  background: none; font: inherit; font-size: 14px; color: var(--text);
  cursor: pointer;
}
.board-period-item:hover,
.board-drop-item:hover { background: var(--bg-fill); }
.board-period-item.on, .board-period-own.on,
.board-drop-item.on {
  background: var(--accent-tint); color: var(--accent); font-weight: 600;
}
/* Строку «Свой период» и линию над ней заказчик убрал 26.08.2026: своим
   период становится сам, как только правят дату, — подпись об этом только
   занимала место. */
.board-period-dates { display: flex; gap: 8px; align-items: center; }
/* Тире между полями — тот же знак, что в подписи поля (rules.OPLATY_RANGE_JOIN):
   один источник на оба места */
.board-period-dash { color: var(--text-tertiary); }
/* Значок календаря СПРЯТАН (просьба заказчика 26.08.2026), а не выключен:
   выбор по-прежнему родной, его открывает showPicker() из payments.js.
   Гасим и сам значок, и место под него — у разных сборок Chrome это разные
   псевдоэлементы, поэтому перечислены оба. */
.board-period-dates input[type="date"]::-webkit-calendar-picker-indicator,
.board-period-dates input[type="date"]::-webkit-inner-spin-button {
  display: none; -webkit-appearance: none; appearance: none;
  width: 0; margin: 0; padding: 0;
}
/* ПОЛЯ УЖЕ (та же просьба). Ширину задаёт НЕВИДИМЫЙ БЛИЗНЕЦ с тем же текстом
   и шрифтом, а не число в пикселях: шрифт у каждой машины свой, и обрезанная
   дата — это потерянные цифры. Тот же приём и по той же причине стоит у поля
   поиска и у фильтра СБИС на доске. */
.board-period-dates .board-pill {
  /* один видимый ребёнок — поле; ширину коробки задаёт близнец */
  display: inline-block;
  /* ЗНАЧОК ОТРЕЗАЕТСЯ ЗДЕСЬ. Правило `::-webkit-…` работает только в Chrome
     и родне: в Firefox кнопка календаря живёт в теневом дереве, снаружи её не
     достать никаким селектором. Поэтому поле делаем ШИРЕ пилюли (ниже), а
     лишнее срезаем краем — это работает в любом браузере, потому что не знает
     про его внутренности.
     Именно `clip`, а не `hidden`: `hidden` заводит прокрутку, и браузер,
     уводя фокус «в видимую область», утащил бы дату влево за край. `clip`
     прокрутки не создаёт. */
  overflow: clip;
}
/* ДАТУ РИСУЕМ САМИ, поле ложится сверху прозрачным (26.08.2026).
   Заказчик попросил дату «точно посередине по горизонтали и вертикали».
   Средствами самого поля этого не выходит — замеры в ДВУХ движках:
     * Firefox рисует «01.08.2026» шириной 77.25px, Chrome — 66.45, и у
       Firefox своя фора слева ~3.5px. Коробка, центрующая один движок,
       уводит другой (подбор запаса в Firefox: 0.6em -> +4.88px,
       1.2em -> +0.62px, а Chrome при том же запасе уехал бы влево);
     * кнопку календаря в Firefox не убрать НИКАКИМ CSS — проверены
       `::-webkit-…`, `appearance: none`, `appearance: textfield` и
       `::-moz-calendar-picker-indicator` (такого селектора нет вовсе,
       вставка падает с SyntaxError). Все шесть дали ту же ширину 106.98.
   Своей подписью центровка становится обычной flex-задачей и точна везде.
   Клик и правка остаются РОДНЫМИ: поле лежит поверх целиком, просто
   прозрачное, — значение уходит на сервер тем же путём, и вместе с полем
   уезжает его кнопка календаря, которую иначе не достать. */
.board-period-shown { display: none; }   /* без скриптов дату рисует поле */
.board-period-dates.board-period-drawn .board-period-shown {
  display: block; font-size: 14px; white-space: pre;
  font-variant-numeric: tabular-nums; font-feature-settings: "tnum";
}
/* мерит теперь подпись, близнец лишний */
.board-period-dates.board-period-drawn .board-period-sizer { display: none; }
.board-period-dates.board-period-drawn .board-pill {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
}
/* Вес селектора нарочно выше соседнего `.board-period-dates input[type=date]`:
   при равном весе побеждал бы тот, что ниже в файле, и поле оставалось бы
   шире пилюли на свой вынос (замер: 553.73 против 336.73 при лупе 4).
   Невидимому полю это не мешало, но клик и фокус жили бы не там, где видно. */
.board-period-dates.board-period-drawn input[type="date"] {
  position: absolute; inset: 0; width: 100%; min-width: 0; opacity: 0;
  margin: 0; padding: 0; height: auto;
}
/* фокус не должен пропасть вместе с видимым полем */
.board-period-dates.board-period-drawn .board-pill:focus-within {
  outline: 2px solid var(--accent); outline-offset: 1px;
}
.board-period-sizer {
  display: block; height: 0; overflow: hidden; visibility: hidden;
  white-space: pre; font: inherit; font-size: 14px;
  /* Запас поверх текста даты. Поле рисует цифры не впритык к своему краю, и
     сколько именно оно берёт себе — у каждого браузера своё (замер в Chrome:
     11.2px при тексте 66.45). Ноль запаса сошёлся бы только там, где мерили,
     а у заказчика год обрезало. 0.6em — доля от шрифта, а не пиксели. */
  padding-right: 0.6em;
}
/* Цифры РОВНОЙ ширины у поля и у близнеца. Здесь они и так ровные (замер:
   все десять по 7.547px), но шрифт берётся системный — на чужой машине
   подставится другой, и «00.00.0000» у близнеца окажется у́же настоящей даты
   с широкими цифрами. Так близнец меряет наибольшую мыслимую дату по
   построению, а не по удаче. */
.board-period-dates input[type="date"],
.board-period-sizer {
  font-variant-numeric: tabular-nums; font-feature-settings: "tnum";
}
.board-period-dates input[type="date"] {
  /* `width: 0` убирает вклад поля в подсчёт «по содержимому» — иначе оно
     навязывает СВОЮ ширину (замер: 77.65px против 66.45 у текста), и близнец
     ничего не решает. `min-width` возвращает поле на коробку И ВЫНОСИТ его
     правый край за пилюлю: там едет значок календаря, и там его срезает
     `overflow: clip` выше. 4em перекрывают значок любого браузера с
     запасом (замер следа значка в Chrome: 8-18px против 47px выноса) —
     срезанному лишку ничего не стоит быть большим. */
  width: 0; min-width: calc(100% + 4em); cursor: pointer;
}
/* в меню поля дат мельче панельных: там они стоят строкой, а не в ряду
   фильтров, и таблеточная высота 38px раздувала бы окошко */
.board-period-open .board-period-dates .board-pill {
  /* поля УЖЕ (просьба заказчика 26.08.2026): отступы 8px вместо 10 плюс
     ширина по близнецу выше. Правим ЭТО правило, а не заводим второе: при
     равном весе побеждало бы нижнее, и два места спорили бы за один отступ */
  height: 32px; padding: 0 8px; border-radius: var(--radius-s);
}
/* Список СБИС — по ПЕРВОМУ пункту, а не по самому длинному (13.08.2026,
   просьба заказчика картинкой). Браузер меряет select по длиннейшему пункту,
   и «СБИС: ЭЦП не установлен в кабинете» раздувал окно до 288.9px — из-за
   него панель и не влезала в строку. Не влезающее обрезается многоточием
   («СБИС: ЭЦП устано…»), а РАСКРЫТЫЙ список браузер рисует сам, по своему
   содержимому и шире окна, — тот же приём, что у поля СБИС на карточке
   (03.08.2026), там он проверен.

   ШИРИНУ ЗАДАЁТ НЕВИДИМЫЙ БЛИЗНЕЦ с текстом первого пункта (.board-filter-
   sizer), а не число в пикселях. Числом уже пробовали — 164px по замеру
   «116.6 текста + 46 полей + 1.2 рамки = 163.8»: запас вышел 0.4px, и у
   заказчика надпись всё равно обрезалась («СБИС: Все стату…»). Тот же приём и
   по той же причине стоит у поля поиска: строку меряет сам браузер, своим
   шрифтом и на своей машине. Не заменяйте близнеца пикселями.

   Тем же приёмом НЕЛЬЗЯ ужать порядок плашек и фильтр чатов: у них пункты
   различаются В КОНЦЕ («…сначала ближние» / «…сначала дальние», «…общие» /
   «…личные»), и многоточие съело бы ровно то слово, которым они и
   отличаются. У СБИСа различие в начале — отсюда и разница. */
/* Внутри близнеца — по строке на каждую подпись, которая обязана влезать
   целиком; ширина берётся по самой длинной из них (13.08.2026) */
.board-filter-sizer > span { display: block; }
.board-filter-sizer {
  display: block; height: 0; overflow: hidden; visibility: hidden;
  white-space: pre; font-size: 14px;
  /* Отступы окна: 14 слева, а справа — зазор до галочки, сама галочка (12) и
     такой же зазор от неё до края, как просил заказчик. Дробное число — это
     ещё 1.18px рамок, которых у близнеца нет: с целыми 37px зазоры разошлись
     на 0.77px, а заказчик видит и 0.9px. Замер после правки — 0.03px. */
  padding: 0 37.8px 0 14px;
}
.board-tools .board-filter select {
  /* width:0 убирает вклад длиннейшего пункта в ширину обёртки (проценты при
     подсчёте «по содержимому» не учитываются), min-width возвращает список на
     всю её ширину — то же решение, что у поля СБИС на карточке */
  width: 0; min-width: 100%;
  /* Порог многоточия отодвинут от галочки на 6px: место под неё считается
     от ПРАВОГО КРАЯ и на видимый зазор не влияет, зато у первого пункта
     появляется запас, которого не хватило при жёсткой ширине. */
  padding-right: 30px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* «Показать» — тихая кнопка: со скриптом ею почти не пользуются */
.btn-quiet { background: var(--bg-fill); }
/* --- Доска --- */
/* minmax(0,…) обязателен: у 1fr нижняя граница — max-content, и колонка с
   длинным названием растянула бы доску за край экрана вместо обрезки (24.07.2026) */
/* Слою связей нужна точка отсчёта: линии лежат абсолютно поверх доски */
.board { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
         gap: 18px; position: relative; }

/* --- Линии связи физлица с его ООО и ИП (02.09.2026) ---------------------
   Просьба заказчика: «Когда мышка наведена на бейдже первого этапа, то
   красиво должны идти две линии, показывающие, в каких ООО и ИП физлицо
   является участником. И наоборот: при наведении на бейджи ООО и ИП второго
   этапа от всех ООО и ИП этого физлица должны идти линии к нему на первый
   этап».

   Ход выбран заказчиком из пяти показанных — «шина»: все связи одного
   человека сходятся в один канал в просвете между колонками и расходятся
   ветками, поэтому линии не пересекаются. Скругление углов 12 px — тоже
   его выбор из четырёх крутизн.

   Линии лежат ПОД плашками: длинная связь ныряет за плашку и выходит с
   другой стороны. Ради этого плашкам доски и задан свой слой. */
.board > svg.svyazi {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; overflow: visible;
}
.board .card { position: relative; z-index: 1; }
.svyazi path {
  fill: none; stroke: var(--stage-ecp); stroke-width: 1.4;
  stroke-linecap: round;
  /* pathLength="1" на самих путях: штриховка считается в ДОЛЯХ пути, и
     короткая связь прорисовывается столько же, сколько длинная */
  stroke-dasharray: 1; stroke-dashoffset: 1;
  transition: stroke-dashoffset 0.46s cubic-bezier(0.32, 0.72, 0, 1);
}
/* Кружка в начале связки нет вовсе (правка заказчика 02.09.2026): он стоял
   центром на краю плашки, а плашки выше слоя связей — наружу торчал
   полукруг. Правило удалено вместе с самим кружком. */
.svyazi-gorit path { stroke-dashoffset: 0; }
/* Обводка связке — тем же сиреневым, что и линия: плашка и линия читаются
   как одно целое. Тень оставляем поднятую, чтобы связка была над соседями */
.card-svyaz { box-shadow: 0 0 0 1.5px var(--stage-ecp), var(--shadow-lift); }
/* ПРИГЛУШЕНИЯ ОСТАЛЬНЫХ ПЛАШЕК НЕТ (правка заказчика 02.09.2026: «не надо
   блюрить остальные бейджи, когда показываются взаимосвязи»). Связку
   показывают линии и обводка на её плашках, а доска остаётся читаемой
   целиком. Правило `.card-gasnet` удалено вместе с классом — мёртвого кода
   за снятой правкой не остаётся. */
/* Юристу видны только два первых этапа (24.07.2026). Столбцы остаются такой же
   ширины, как на полной доске (треть за вычетом промежутков), а пара целиком
   встаёт по центру: 2 × (100% − 2×18px)/3 + 18px = (200% − 18px)/3. */
.board-cols-2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  width: calc((200% - 18px) / 3); margin-left: auto; margin-right: auto;
}
/* Цвет этапа задаётся один раз на колонке, шапка и фон берут его из переменной.
   Новый этап = один такой блок, править .column/.column h2 не нужно. */
.column-ecp { --stage: var(--stage-ecp); --stage-soft: var(--stage-ecp-soft); }
.column-sbor { --stage: var(--stage-sbor); --stage-soft: var(--stage-sbor-soft); }
/* «Принятые» (22.07.2026) — фон и плашка бывшего третьего столбца */
.column-prinyatye { --stage: var(--stage-realny); --stage-soft: var(--stage-realny-soft); }
.column {
  background: var(--stage-soft, var(--bg-fill)); border-radius: var(--radius-l);
  padding: 14px; min-height: 220px;
}
/* шапка колонки — плашка цвета этапа; текст тёмный, иначе не проходит AA */
.column h2 {
  display: flex; justify-content: space-between; align-items: center; gap: 10px;
  background: var(--stage, var(--bg-fill)); color: var(--text);
  border-radius: 980px; padding: 7px 8px 7px 14px;
  font-size: 13px; font-weight: 600; letter-spacing: 0; margin: 0 0 12px;
}
/* Счётчик — того же роста, что «плюс» рядом в шапке (31.07.2026): при 22px и
   min-width 22px однозначное число даёт ровный кружок, а трёхзначное
   растягивает плашку ВЛЕВО — у h2 space-between, и правый край остаётся на
   месте. Отступы по бокам нужны, чтобы длинное число не льнуло к скруглению */
.count {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 22px; height: 22px; box-sizing: border-box; padding: 0 7px;
  background: var(--bg-fill-subtle); color: var(--text-secondary);
  border-radius: 980px; font-size: 12px; font-weight: 600; line-height: 1;
}
/* Название столбца и «плюс» — одной группой слева, счётчик остаётся справа:
   у h2 space-between, и без обёртки три элемента разъехались бы (31.07.2026) */
.column-head { display: flex; align-items: center; gap: 8px; min-width: 0; }
.column-head > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Кнопка-дубль «Добавить …» прямо в шапке этапа: то же действие, что в верхнем
   меню, но под рукой. Полупрозрачная — чтобы не спорить с цветом этапа */
.column-add {
  flex: none; width: 22px; height: 22px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, 0.55); color: var(--text);
  text-decoration: none; transition: background 0.2s ease;
}
.column-add:hover { background: rgba(255, 255, 255, 0.9); text-decoration: none; }
.column-add:focus-visible { outline: 2px solid var(--text); outline-offset: 1px; }
.column-add svg { width: 13px; height: 13px; }
/* внутри цветной плашки нейтральный счётчик теряется — делаем его светлым */
.column h2 .count { background: rgba(255, 255, 255, 0.6); color: var(--text); }
.card {
  display: block; background: var(--bg-elevated); border-radius: var(--radius-m);
  padding: 13px 15px; margin-bottom: 10px; text-decoration: none; color: inherit;
  box-shadow: var(--shadow-card);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.card:hover {
  transform: translateY(-2px); box-shadow: var(--shadow-lift);
  text-decoration: none;
}
/* Плашки всех колонок одной высоты (24.07.2026). Считается по самой высокой —
   «ЭЦП физлиц»: отступы 13+13, строка ФИО с чипом файла (34px), 4px между
   рядами и строка описания с кнопкой чата (34px) = 98. У компаний содержимое
   ниже, поэтому нижний ряд прижимается к низу (margin-top: auto) — кнопки
   чата во всех колонках оказываются на одной линии. */
.column .card { display: flex; flex-direction: column; min-height: 98px; }
.column .card .card-actions { margin-top: auto; }
/* Нижняя строка плашки «ЭЦП физлиц» — ТА ЖЕ, что у плашек компаний.
   С 24.07.2026 здесь была своя сетка 1fr auto 1fr: она держала кнопку файла
   ровно посередине, между днями на этапе и чатом. Кнопки файла в этой строке
   больше нет (09.08.2026, кругляшок уехал в строку ФИО), и средняя ячейка
   сетки досталась ЧАТУ — он съехал из правого нижнего угла в середину
   плашки, что заказчик сразу и заметил. Своего правила у этой строки теперь
   нет вовсе: общий .card-actions (flex) плюс margin-left: auto у .chat-cell
   прижимают чат вправо — ровно как на 2 и 3 этапах. */
/* Длинное название не переносится и не наползает на бейдж «р/с …»: обрезаем
   троеточием справа (24.07.2026). Заодно высота плашки перестаёт зависеть от
   длины названия — все плашки на доске одной высоты. */
.card-name {
  font-weight: 600; letter-spacing: -0.01em; margin-bottom: 4px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.card-meta { font-size: 12.5px; color: var(--text-secondary); }
/* то же для строк под названием: перенос сделал бы плашку выше соседних.
   Межстрочный поджат (31.07.2026): строк стало три — ИНН, дата, файлы, — и с
   обычными 1.45 плашка вырастала со 98 до 107px, а высота меняться не должна */
.column .card-meta {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  line-height: 1.28;
}
/* и сама строка названия — вместе с зазором под ней. Ниже строку названия не
   ужать: её высоту держит бейдж «не хватает». Эти три поджатия укладывают
   четыре строки ровно в прежние 98px */
.column .card-name { line-height: 1.3; }
.column .card-name-row { margin-bottom: 2px; }
.empty-column { color: var(--text-secondary); text-align: center; padding: 28px 0; font-size: 13px; }
.archive { margin-top: 24px; }
.archive summary { cursor: pointer; color: var(--text-secondary); font-weight: 600; font-size: 14px; }
/* срок жизни «Удалённых» — рядом с заголовком, обычным весом (24.07.2026) */
.archive-note { font-weight: 400; color: var(--text-tertiary); margin-left: 6px; }
/* minmax(0,…) — та же ловушка, что у .board: с голым 1fr длинное название
   в одну строку растянуло бы список за край экрана (24.07.2026) */
.archive-list { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; margin-top: 12px; }
.card-archived { opacity: 0.65; }

/* --- Страница компании --- */
/* блок шириной ровно с сетку плиток: всё внутри выравнено по её левому краю */
/* margin-bottom 34px — столько же, сколько над рядом кнопок «+ Карточка» /
   «+ Архив учдоков» (22px у .card-block-plain плюс 12px промежутка сетки),
   чтобы ряд стоял ровно посередине между блоком реквизитов и «Историей»
   (24.07.2026). Значение не складывается с 14px нижнего отступа .card-grid,
   а поглощает их: у .card-block нет ни рамки, ни внутреннего отступа снизу,
   поэтому поля схлопываются и остаётся большее из двух. */
/* Нижний отступ карточки — одним значением на двоих: по нему же правый
   столбец подбирает свой низ, чтобы кончиться с карточкой вровень. Запасное
   34px — для страницы ЭЦП, там .card-block живёт вне .company-layout */
.card-block {
  width: max-content; max-width: 100%;
  margin: 0 auto var(--card-gap-bottom, 34px);
}
.card-block .card-grid { width: auto; margin-left: 0; margin-right: 0; }
/* Высота строки с плашкой этапа задана явно (25.07.2026): по ней правая
   колонка отступает вниз, чтобы её первый блок встал вровень с первым блоком
   слева. Иначе величину пришлось бы угадывать, и при смене кегля плашки они
   разъехались бы молча. */
.company-head {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  min-height: var(--company-head-h); margin-bottom: var(--company-head-gap);
}
/* Бейдж этапа — та же плашка, что и шапка колонки: один язык на доске и в карточке */
.status-badge {
  padding: 4px 13px; border-radius: 980px; font-size: 13px; font-weight: 600;
  background: var(--stage-sbor); color: var(--text);
}
.status-prinyatye { background: var(--stage-realny); }
.status-arhiv { background: var(--stage-arhiv); }
/* «Назад» перед плашкой (03.08.2026): прозрачная — заливка спорила бы с
   плашкой этапа, которая стоит рядом и цветом как раз говорит. Высота
   набрана теми же величинами, что у плашки (13px, 4px сверху и снизу):
   строка обязана остаться ростом --company-head-h, по ней отступает вниз
   правая колонка. */
/* «Назад» — круглая кнопка (06.08.2026, просьба заказчика; была текстовой и
   стояла в строке с плашкой этапа). Размер и контур как у круглых кнопок
   эскиза (.round-btn): один язык на всё приложение. */
.back-round {
  display: flex; align-items: center; justify-content: center; flex: none;
  width: 34px; height: 34px; border-radius: 50%;
  border: 1px solid var(--separator); background: var(--bg-elevated);
  color: var(--text-secondary); box-shadow: var(--shadow-card);
  cursor: pointer; text-decoration: none;
  transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease,
              transform 0.15s ease;
}
.back-round:hover {
  background: var(--bg-fill-subtle); color: var(--text);
  border-color: var(--separator-strong, var(--separator)); text-decoration: none;
}
.back-round:active { transform: scale(0.97); }
.back-round:focus-visible { outline: none; box-shadow: 0 0 0 4px var(--accent-tint); }
.back-round svg { width: 12px; height: 12px; flex: none; }
/* На карточке кнопка ЛИПКАЯ: прокрутили вниз — осталась на экране, вернулись
   вверх — поднялась на своё место. `top` тот же, что у правой колонки, чтобы
   обе останавливались на одной высоте под липкой шапкой. */
/* Кнопки слева от карточки — ОДНИМ столбиком (11.08.2026, просьба
   заказчика): «Назад» сверху, чат ровно под ним. Липкая тут обёртка, а не
   каждая кнопка порознь: иначе при прокрутке они встали бы на одну высоту
   друг на друга. Высоту столбика задаёт company.js по первому блоку
   карточки — тогда верх «Назад» совпадает с верхом блока «Происхождение», а
   низ чата с его низом. Ширину столбик берёт по чату (со счётчиками), и
   перенос ставит его правый край в 34px от карточки — ровно ширина кнопки. */
.company-tools {
  grid-column: 3; grid-row: 1;
  justify-self: start;
  margin-left: 0;
  transform: translateX(calc(-100% - var(--round-btn)));
  margin-top: calc(var(--company-head-h) + var(--company-head-gap));
  display: flex; flex-direction: column; align-items: flex-end;
  justify-content: space-between; gap: 10px;
  position: sticky; top: 74px;
}
/* Кнопка чата — слева от карточки, зеркально доске: там к чату ведёт кнопка
   на плашке, а с самой карточки до сих пор было не попасть вовсе. Стоит в
   распорке между «Назад» и карточкой, прижата к карточке на расстояние, РАВНОЕ
   своей ширине, и опущена на ту же строку (11.08.2026, просьба заказчика). */
/* фон и тень кнопки чата — те же, что у «Назад» (11.08.2026, просьба
   заказчика): на доске кнопка прозрачная и плоская, а здесь стоит в паре с
   «Назад», и отличия бросались в глаза */
.company-tools .chat-btn {
  background: var(--bg-elevated);
  border-color: var(--separator);
  box-shadow: var(--shadow-card);
}
/* на плашке доски ячейка прижата вправо своим margin-left: auto — в столбике
   он уводил бы её к правому краю (замер до правки: 497px мимо) */
.company-tools > .company-chat { margin-left: 0; }
/* На странице записи ЭЦП кнопка своей строкой над ФИО и НЕ липкая (решение
   заказчика того же дня): страница короткая, липучка там не нужна. */
.back-round-top { margin: 10px 0 6px; }

/* ИНН физлица на странице записи ЭЦП (03.08.2026): одна строка — подпись,
   поле, кнопка. Отдельной плиточной сетки тут нет: поле ровно одно */
/* Файл ЭЦП на странице записи (09.08.2026): кругляшок, подпись состоянием и
   кнопки. Формы обычные — скриптов на этой странице нет, — поэтому каждая
   кнопка живёт в своей форме, а строка держит их в ряд. */
.ecp-file-row {
  display: flex; align-items: center; gap: 10px; margin-bottom: 18px;
  flex-wrap: wrap;
}
.ecp-file-row form { display: flex; align-items: center; gap: 8px; margin: 0; }
/* Кругляшок наследует align-self: baseline — он нужен на доске, где строка
   выровнена по базовой линии. Здесь строка выровнена по центру, и кругляшок
   оставался единственным, кто жался к верху: замер 09.08.2026 дал 10px над
   серединой строки (её высоту задаёт поле выбора файла, 41px). */
.ecp-file-row .ecp-mark { align-self: center; }
.ecp-file-name { color: var(--text-secondary); font-size: 14px; }
.ecp-file-row input[type="file"] { font: inherit; font-size: 13px; max-width: 260px; }
.ecp-inn { display: flex; align-items: center; gap: 10px; margin-bottom: 22px; }
.ecp-inn label { color: var(--text-secondary); font-size: 14px; }
.ecp-inn input {
  border: none; border-radius: var(--radius-m); padding: 8px 12px;
  font: inherit; font-size: 14px; width: 15ch;
  background: var(--bg-fill-subtle); color: var(--text);
}
.ecp-inn input:focus { outline: none; box-shadow: 0 0 0 2px var(--accent); }
/* зачем нужен ИНН — рядом с полем, пока оно пустое */
.ecp-inn-note { color: var(--text-tertiary); font-size: 13px; }

/* Бейдж р/с на плашке «Принятых» (22.07.2026): овал в строке названия,
   справа. Цвета прежних стадий, ужатые под надпись; текст тёмный — AA. */
.card-name-row {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; margin-bottom: 4px;
}
.card-name-row .card-name { margin-bottom: 0; min-width: 0; }
/* бейдж прижат к правому краю строки названия */
.card-name-row .card-name { margin-right: auto; }
/* Правила .rs-badge здесь БОЛЬШЕ НЕТ (28.08.2026): текстовые плашки
   «р/с копия» / «р/с реальный» заменил кругляшок «р/с» — голубой вместо
   копии, зелёный вместо реального (см. .rs-mark* ниже). Плашки с 09.08.2026
   были ОБЕ зелёными и различались только надписью, то есть цвет у них
   ничего не говорил; кругляшок говорит то же самое короче и в одном ряду с
   ЭЦП, СБИС и рублём. */
/* Подсказка на плашках «Сбора» (22.07.2026): чего не хватает до «Принятых».
   Полный список — в title, поэтому курсор подсказывает, что можно навести. */
/* Плашки в строке названия. Геометрия ОДНА на все этапы (08.08.2026): у
   «ЭЦП физлиц» плашка «N ООО» обязана выглядеть ровно так же, как «не
   хватает» на соседних столбцах, — заказчик просил «стандартную по размеру».
   Держим их одним правилом, чтобы они не разъехались при следующей правке. */
.todo-badge, .links-badge {
  flex: none; border-radius: 980px; padding: 2px 10px;
  font-size: 11.5px; font-weight: 600; white-space: nowrap; cursor: help;
  background: var(--bg-fill); color: var(--text-secondary);
}
/* Зелёный тот же, что у кнопки с загруженным ЭЦП: «на таком же фоне, как
   добавленный ЭЦП физлица» — дословная просьба заказчика. Серый в правиле
   выше не случайно общий: «ни одного ООО — плашка серая, как „не хватает" на
   втором этапе» (09.08.2026), то есть незакрашенное состояние у обеих одно.
   Плашки «готово к переносу» в этом правиле больше нет: с 04.09.2026
   готовность говорит кругляшок с галочкой (см. .todo-mark ниже). */
.links-badge-any {
  background: var(--green-tint); color: var(--green-text);
}

/* Кругляшок «ЭЦП загружен / не загружен» (03.08.2026): стоит слева от плашки
   и сидит РОВНО как она. Рост и посадка не подогнаны числом, а взяты у самой
   плашки: тот же кегль, те же 2px сверху и снизу, та же строка и та же
   базовая линия. Прибитые 21px с align-self: flex-start ставили круг на
   0.9px выше плашки — заказчик увидел это глазом раньше, чем моя проверка с
   допуском «меньше пикселя» (04.08.2026). Замер после правки: верх и низ
   совпадают в 0.00, круг 20.66×20.65 при плашке 20.65.
   Цвета не свои: зелёный — тот же, что у кнопки с загруженным файлом
   (.slot-filled .slot-add), красный — фон блока «Не хватает» (.missing-block). */
.ecp-mark {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  font-size: 11.5px;
  /* с 09.08.2026 это ещё и button/label — гасим их собственную рамку и
     шрифт, иначе кругляшок-кнопка вырос бы и поехал относительно плашки */
  border: 0; font-family: inherit; line-height: inherit;
  padding: 2px 0;
  /* Ширина равна высоте без выдуманного числа: 1lh — ТА ЖЕ строка, которую
     наследует плашка (замер: 16.675px у обеих), плюс те же 2px сверху и снизу.
     Сменится кегль или межстрочный — круг поедет следом сам. */
  width: calc(1lh + 4px);
  align-self: baseline;
  border-radius: 50%; cursor: help;
}
/* Знак нулевой ширины. Внутри флекса ::before — ЭЛЕМЕНТ, а не текст в строке:
   с пустым содержимым строка не возникает вовсе и высота схлопывается до
   значка (замер: 12.99 вместо 20.65). С этим знаком у круга есть и высота
   строки, и базовая линия — та же, что у плашки. */
.ecp-mark::before { content: "\200B"; width: 0; }
.ecp-mark svg { width: 9px; height: 9px; }
.ecp-mark-yes { background: var(--green-tint); color: var(--green-text); }
/* Кругляшок «готово к переносу» (04.09.2026, выбор заказчика из пяти
   показанных — вариант А). Круг и посадка приходят из .ecp-mark выше: своих
   размеров тут нет вовсе, только заливка и кегль знака. Заливка мягкая, та
   же, что у загруженного ЭЦП, — вариант А и выбран за то, что звучит вровень
   с соседями по строке, а не громче названия компании.
   Галочка 11px против ключа в 9: у ключа рисунок плотный, а галочка — три
   тонких штриха, и в 9px она читалась птичкой размером с точку. */
.todo-mark { background: var(--green-tint); color: var(--green-text); }
.todo-mark svg { width: 11px; height: 11px; }
.ecp-mark-no { background: var(--red-tint); color: var(--red-text); }
/* Ещё два состояния ЭЦП директора (12.08.2026, выбор заказчика из показанных).
   Янтарный за «Перевыпуск» — в проекте он и так значит «идёт, но не готово»
   (полоса «мало места»), и в зрелых консолях переходные состояния красят
   именно им. Тёмно-серый за «Отозвана» — тот же, каким был «ЭЦП отозван» у
   СБИС: статус оттуда убран и переехал сюда вместе с цветом и смыслом.
   Классы СВОИ, а не общие yes/no: те носят кругляшок оплаты и кругляшок
   физлица, и красить их заодно нельзя. Знак один на все четыре — ключ. */
.ecp-mark-reissue { background: var(--orange-tint); color: var(--orange-text); }
.ecp-mark-revoked { background: var(--text-secondary); color: var(--bg-elevated); }
/* Кругляшок СБИС (12.08.2026): тот же круг, что у ЭЦП и оплаты — размер и
   посадка приходят из общего правила выше, здесь только знак и цвета.
   Знак крупнее ключа (12 против 9): птица логотипа узкая и высокая, в 9 px
   она сжималась в чёрточку. Замер: круг 20.66×20.65 — как у соседей, верх и
   низ сходятся с ними и с плашкой «не хватает» в 0.00px. */
.sbis-mark svg { width: 12px; height: 12px; }
/* Белый круг на белой плашке пропал бы, поэтому у него волосяной контур —
   внутренней тенью, а не border: border съел бы 2px у знака (общее правило
   размеров стоит выше и про рамку не знает). Знак гашёный серый — выбор
   заказчика: «неизвестно» не должно выглядеть как фирменная краска. */
.sbis-undefined {
  background: var(--bg-elevated); color: var(--text-tertiary);
  box-shadow: inset 0 0 0 1px var(--hairline);
}
/* Мягкий голубой стадии «Сбор документов» — выбор заказчика из двух
   показанных: полная заливка вставала в один вес с тёмно-серым «отозван» */
.sbis-reg { background: var(--stage-sbor-soft); color: var(--stage-sbor-ink); }
.sbis-yes { background: var(--green-tint); color: var(--green-text); }
.sbis-no { background: var(--red-tint); color: var(--red-text); }
/* Кругляшок «р/с» (28.08.2026, просьба заказчика): четвёртый знак в строке
   названия — состояние расчётного счёта. Круг, размер и посадка приходят из
   общего .ecp-mark выше: своих чисел тут нет, иначе он разъедется с соседями
   при первой же смене кегля. Своё — только начертание знака и цвета.

   Знак ТЕКСТОВЫЙ, а не SVG, поэтому у него свои кегль и вес: 8.5px/600 —
   на этом размере три знака ещё читаются, а высота плашки не растёт.
   Вес 600 выбрал заказчик 28.08.2026 из пяти показанных (800, 700, 600, 500,
   400): при 800 знак выходил тяжелее соседнего ключа ЭЦП и «кричал» громче
   остальных, а 600 — тот же вес, что у шапки таблицы оплат и у подписей.

   ПОДЪЁМ 0.164em — не подгонка на глаз, а замер: строка меряется по
   ascender+descender, и тело строчной «с» из-за этого сидит НИЖЕ середины
   круга. Величина снята в браузере метриками текста
   ((ascent − высота/2) − (краска_вверх − краска_вниз)/2 = 1.39px при кегле
   8.5px) и записана В ДОЛЯХ КЕГЛЯ: тогда смена кегля не сломает посадку, а
   в пикселях сломала бы. Проверено по пикселям снимка: центр краски «с»
   совпал с осью круга в 0.05px. */
.rs-mark b {
  font-size: 8.5px; font-weight: 600; letter-spacing: -0.2px;
  display: inline-block; transform: translateY(-0.164em);
}
/* Цвета берут ТЕ ЖЕ токены, что у соседей, но правила СВОИ: общие
   .ecp-mark-yes/.ecp-mark-no носят кругляшки оплаты и физлица, и красить их
   заодно нельзя. Тот же приём, что у .sbis-* (12.08.2026).
   Голубой у «копии» — выбор по замеру: у янтарного цветовое расстояние до
   зелёного падает до ΔE 4.4 при протанопии, у голубого держится 9.6-10.2 при
   любом зрении. Тёмно-серый у «невозможно» — тот же, что у ЭЦП «Отозвана»:
   одинаковый смысл «конечное, ждать нечего» берёт цвет из одного правила. */
.rs-mark-real { background: var(--green-tint); color: var(--green-text); }
.rs-mark-kopiya { background: var(--stage-sbor-soft); color: var(--stage-sbor-ink); }
.rs-mark-nevozmozhno { background: var(--text-secondary); color: var(--bg-elevated); }
/* Кругляшок «ЧО» на плашке доски (30.08.2026, вариант «Е» — выбор заказчика
   из семи показанных). Кольцо взято У РУБЛЯ один в один: цвет --green, штрих
   2, радиус 9.325 — внешний край штриха ложится ровно на край кругляшка
   (9.325 + 1 = 10.325 = половина коробки 20.65), наружу знак не растёт и
   остаётся ростом с соседями.

   position: relative ОБЯЗАТЕЛЕН: кольцо лежит absolute, и без него оно
   привязалось бы к самой плашке и село в её центр (поймано заказчиком на
   рубле 24.08.2026).

   Буква В ТОН КОЛЬЦУ — это и отличает вариант «Е» от остальных показанных.
   Названная цена: --green на светлой заливке даёт 2.01:1, ниже AA для
   мелкого текста; заказчику это сказано числом, выбор его. Смысл при этом
   несёт не буква, а сам факт кругляшка: он появляется ТОЛЬКО у отмеченной. */
/* «На сайте штамповки» (02.09.2026). Кольца рисуются НА ВЕСЬ кругляшок, а
   не значком 9 px внутри него: внешнее кольцо и есть внешний обод отметки —
   так просил заказчик («периметр внешнего круга должен быть по внешнему
   периметру кругляшка»). Размер берётся от самого кругляшка (100%), а не
   зашит в пикселях, — то же правило, что у кольца ЧО и у рубля. */
.stamp-mark { position: relative; background: var(--green-tint); color: var(--green-text); }
.stamp-mark .stamp-ring {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none;
}
.cho-mark { position: relative; background: var(--green-tint); color: var(--green); }
.cho-mark .cho-ring {
  position: absolute; inset: 0;
  /* размер БЕЗ числа: кругляшок меряется от 1lh, зашитые пиксели разъехались
     бы с ним при первой же смене кегля — то же правило, что у кольца рубля */
  width: 100%; height: 100%;
  color: var(--green); pointer-events: none;
}
.cho-mark b { font-size: 12px; font-weight: 700; line-height: 1; position: relative; }
/* Кругляшок-КНОПКА (09.08.2026, просьба заказчика): им же кладут, скачивают
   и удаляют ЭЦП — «ЭЦП физлица» на первом этапе и «ЭЦП директора» на втором
   и третьем. Роль без прав на документы видит прежнюю отметку: у неё нет ни
   этих классов, ни курсора-руки. Кольцо фокуса — на самом кругляшке, как у
   .slot-add: поле внутри label невидимо, но остаётся в порядке табуляции. */
button.ecp-mark, label.ecp-mark { cursor: pointer; }
button.ecp-mark:hover, label.ecp-mark:hover { filter: brightness(0.96); }
button.ecp-mark:focus-visible, label.ecp-mark:focus-within {
  outline: none; box-shadow: 0 0 0 3px var(--accent-tint);
}
/* Кругляшок оплаты (03.08.2026, вариант заказчика): знак один — рубль, а
   состояние говорит цветом, теми же двумя, что у ЭЦП. Фину он ещё и кнопка */
/* position: relative ОБЯЗАТЕЛЕН: кольцо дуг лежит absolute, и без него оно
   привязывается к ближайшему позиционированному предку — к самой плашке — и
   садится в её центр вместо края кругляшка (поймано заказчиком 24.08.2026). */
.paid-mark { position: relative; }
.paid-mark b { font-size: 12px; font-weight: 700; line-height: 1; position: relative; }
/* Три состояния (24.08.2026, выбор заказчика). Голубой — цвет этапа «Сбор»:
   он у нас уже есть, и заводить ради кругляшка новый оттенок незачем. Фон
   тонирован тем же цветом и той же долей, что у зелёного и красного, —
   одинаковые по смыслу вещи берут цвет по одному правилу. */
.paid-mark-none { background: var(--red-tint); color: var(--red-text); }
.paid-mark-partial {
  background: var(--stage-sbor-soft); color: var(--stage-sbor-ink);
}
.paid-mark-all { background: var(--green-tint); color: var(--green-text); }
/* Кольцо дуг лежит ПОВЕРХ заливки и ровно по краю: кругляшок остаётся
   20.65px и не выбивается из ряда с ЭЦП и СБИС. Цвет дуг свой, зелёный, —
   он про оплаченное, а не про состояние кругляшка целиком. */
.paid-mark .paid-ring {
  position: absolute; inset: 0;
  /* размер БЕЗ числа: кругляшок меряется от 1lh, и зашитые 20.65px разъехались
     бы с ним при первой же смене кегля. viewBox тянет рисунок за коробкой */
  width: 100%; height: 100%;
  color: var(--green); pointer-events: none;
}

/* Плашка «ЭЦП физлица» (22.07.2026): как карточка компании, но с кнопками —
   поэтому это div, а не ссылка; переход по клику отдаёт board.js */
.ecp-card { cursor: pointer; }
/* ФИО на плашках — ПРОПИСНЫМИ (просьба заказчика 21.08.2026): и на «ЭЦП
   физлиц», и у ИП, где на плашке стоит ФИО предпринимателя. Делает это CSS, а
   не разметка: в DOM остаётся то, что вписал человек, поэтому читалка экрана
   читает ФИО словами, а не по буквам, копирование даёт обычный регистр, а
   поиск и алфавит на доске работают по данным. В базе прописных быть не
   должно — 21.08.2026 мы как раз убирали их оттуда, иначе они уезжают в имя
   файла и в подсказку чата.

   Плашки ООО правило НЕ трогает: там название общества, а не ФИО, и в реестре
   оно и так прописными. Одно правило на оба случая — чтобы регистр не
   разъехался, если однажды тронут одно из них. */
.ecp-card .card-name,
.card-ip .card-name { text-transform: uppercase; }
/* Кнопки файла со скрепкой на плашке «ЭЦП физлиц» больше нет (09.08.2026):
   её место занял кругляшок ЭЦП — тот же плюс и минус, что на 2 и 3 этапах
   (просьба заказчика). Правила чипа ушли вместе с ней, чтобы мёртвый CSS не
   притворялся живым; вернуть — в истории git.  */
.ecp-del {
  flex: none; width: 30px; height: 30px; padding: 0;
  border: 1px solid var(--hairline);
  border-radius: 50%; background: none; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--text-tertiary);
  transition: color 0.2s ease, background 0.2s ease, border-color 0.2s ease;
}
.ecp-del svg { width: 14px; height: 14px; }
.ecp-del:hover { color: var(--red-text); background: var(--red-tint); border-color: transparent; }

/* --- Чат плашки (22.07.2026) --------------------------------------------- */
.company-card { cursor: pointer; }
/* Строки описания и кнопка чата — в одном ряду (24.07.2026): отдельной
   строкой кнопка делала плашку компании выше плашки «ЭЦП физлиц». */
.card-actions { display: flex; align-items: flex-end; gap: 10px; }
.card-metas { flex: 1; min-width: 0; }
.chat-cell { display: inline-flex; align-items: center; gap: 6px; margin-left: auto; }
/* Кнопка чата — обычная круглая .round-btn (34px, значок 17px). Прежние 30/15px
   здесь ничего не меняли: .round-btn стоит в файле ниже и с той же
   специфичностью перебивал их — правила убраны, чтобы не вводить в заблуждение
   (24.07.2026). Высота плашки считается по этим 34px. */
.chat-count {
  min-width: 20px; text-align: center; border-radius: 980px; padding: 1px 7px;
  font-size: 11.5px; font-weight: 600;
  background: var(--bg-fill); color: var(--text-secondary);
  /* по счётчику тоже открывается чат (24.07.2026) — курсор об этом говорит */
  cursor: pointer;
}
/* адресовано лично мне — заметнее */
.chat-count-personal { background: var(--accent-tint); color: var(--accent); }

.chat-box {
  width: min(480px, 92vw); max-height: min(600px, 86vh);
  display: flex; flex-direction: column; padding: 18px 20px 16px;
  background: var(--bg-elevated); border-radius: var(--radius-l);
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.22);
  animation: modal-rise 0.18s cubic-bezier(0.2, 0.8, 0.3, 1);
}
/* шапка окна: название слева, крестик справа (24.07.2026) */
.chat-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 12px;
}
.chat-title { font-weight: 700; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.chat-close { flex: none; }
/* размер значка у обеих кнопок задаётся одним правилом ниже (.chat-form
   .round-btn svg): крестик живёт теперь только в нижней строке чата */
.chat-list {
  flex: 1 1 auto; overflow-y: auto; min-height: 120px;
  display: flex; flex-direction: column; gap: 10px; padding-right: 4px;
}
.chat-empty { color: var(--text-tertiary); font-size: 13.5px; text-align: center; padding: 30px 0; }
.chat-msg { background: var(--bg-fill-subtle); border-radius: var(--radius-m); padding: 8px 12px; }
.chat-msg-head { display: flex; justify-content: space-between; gap: 10px; font-size: 12.5px; margin-bottom: 3px; }
.chat-msg-at { color: var(--text-tertiary); font-size: 11.5px; }
.chat-msg-text { font-size: 14px; white-space: pre-wrap; overflow-wrap: anywhere; }
/* Все @имена в ленте выглядят одинаково. Голубой заливки у того, кому
   адресовано, больше нет (11.08.2026, просьба заказчика): в переписке она
   читалась как выделение маркером. Кому лично — видно на плашке счётчиком
   .chat-count-personal, там она к месту. */
.chat-mention { color: var(--accent); font-weight: 600; }
/* Список адресатов: набрал @ — ники встают над полем, по одному в строке,
   как в мессенджере (11.08.2026, просьба заказчика). Раньше они висели
   чипами над полем всегда — три строки ради того, что нужно изредка.
   Вид — той же карточкой, что и подсказки полей (.suggest-menu).
   Ширину список берёт У ПОЛЯ: якорь — обёртка .chat-field, а не форма.
   Считать её как «форма минус кнопки и зазоры» нельзя — первый же замер
   дал 43px вылета вправо, под кнопки. */
.chat-field { position: relative; flex: 1 1 auto; min-width: 0; display: flex; }
.chat-suggest {
  position: absolute; left: 0; right: 0; bottom: calc(100% + 6px);
  z-index: 20; max-height: 190px; overflow-y: auto;
  display: flex; flex-direction: column; padding: 4px;
  background: var(--bg-elevated); border-radius: var(--radius-m);
  box-shadow: var(--shadow-lift);
}
.chat-suggest-row {
  display: flex; align-items: baseline; gap: 8px; min-width: 0;
  border: none; background: none; text-align: left; cursor: pointer;
  padding: 7px 12px; border-radius: var(--radius-s); font: inherit;
  font-size: 14px; color: var(--text); transition: background 0.15s ease;
}
.chat-suggest-row:hover, .chat-suggest-on { background: var(--bg-fill-subtle); }
.chat-suggest-handle { flex: none; color: var(--accent); font-weight: 600; }
.chat-suggest-name {
  min-width: 0; color: var(--text-tertiary); font-size: 13px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Поле ввода и пара круглых кнопок (10.08.2026, просьба заказчика).
   align-items: center — кнопки стоят по вертикальному центру поля, а не по
   его низу: поле в две строки (62px), кнопки по 34px, и «по низу» они
   выглядели прижатыми. Промежуток 10px — тот же, что между чипами адресатов
   и полем, поэтому низ окна читается одной группой. */
.chat-form { display: flex; gap: 10px; margin-top: 10px; align-items: center; }
/* flex: none обязателен: без него поле ввода отбирает у кнопок ширину, и
   круглая «Отправить» становится овальной — замер дал 27.2×33.3 вместо
   34×34 (10.08.2026). Высоту круг держит сам, ширину — только этим. */
.chat-form .round-btn { flex: none; }
/* «Отправить» — единственное действие окна, поэтому цветная; «Закрыть»
   рядом остаётся обычной круглой. Пара одинакового размера, разного веса. */
.chat-send {
  background: var(--accent); border-color: transparent; color: #fff;
}
.chat-send:hover:not(:disabled) {
  background: var(--accent-hover); border-color: transparent; color: #fff;
}
.chat-send:disabled {
  background: var(--bg-fill); border-color: transparent;
  color: var(--text-tertiary); cursor: not-allowed;
}
/* Один размер значка на обе кнопки: толщина линии в SVG зависит от того, во
   сколько раз картинку ужали, поэтому «одинаковая толщина» получается только
   при одинаковых viewBox, stroke-width И размере вывода (10.08.2026). */
.chat-form .round-btn svg { width: 14px; height: 14px; }
.chat-input {
  flex: 1 1 auto; resize: none; border: none; border-radius: var(--radius-m);
  padding: 8px 12px; font: inherit; font-size: 14px;
  background: var(--bg-fill-subtle); color: var(--text);
}
.chat-input:focus { outline: none; box-shadow: 0 0 0 2px var(--accent); }
.days { color: var(--text-tertiary); font-size: 13px; }

/* Три колонки: круглая «Назад» в левом поле, содержимое, правая колонка.
   Первая колонка узкая (auto = ширина кнопки), поэтому карточка сдвигается
   вправо ровно на кнопку с промежутком — как на эскизе заказчика. */
/* Карточка стоит ПОСЕРЕДИНЕ ЭКРАНА, правый блок — у правого края (11.08.2026,
   просьба заказчика). Слева для этого резервируется столько же, сколько
   занимает правый блок: середина средней колонки тогда совпадает с серединой
   окна, а кнопка «Назад» остаётся на прежнем месте — у левого края своей
   колонки. Раньше слева стояла только кнопка (34px), и карточку уводило
   влево на 148px (замер 11.08.2026 при окне 1425px).

   Зеркало ужимается, когда экран узкий: «по центру» и «правый блок у края»
   совмещаются, только пока карточке хватает её 571px (три плитки по 155,
   два зазора по 12 и поля блока 20+20). Дальше зеркало отдаёт место
   карточке, и она просто уезжает вправо настолько, насколько его хватает,
   а не сжимается — сжатая сетка ломает посадку полей. */
.company-layout {
  --company-side: 330px;
  --card-gap-bottom: 34px;          /* низ карточки и низ правого столбца */
  /* Сколько остаётся зеркалу: окно минус карточка (571), минус правый блок,
     минус два зазора сетки (36), минус поля страницы (28+28) и минус полоса
     прокрутки (16): её 100vw считает своей, а раскладка — нет, и без этой
     поправки карточку прижимало бы на её ширину. */
  --company-mirror: clamp(34px,
      calc(100vw - 571px - var(--company-side) - 36px - 56px - 16px),
      var(--company-side));
  /* relative — чтобы правый блок можно было положить абсолютно в его дорожку
     (см. .company-side): иначе он считался бы в высоту строки */
  display: grid; align-items: start; position: relative;
  /* Пять дорожек: зеркало, распорка, карточка ПО СВОЕЙ ширине, распорка,
     правый блок. Карточка в своей дорожке, а не в резиновой, потому что от её
     краёв теперь считается «История» — она тянется от левого края карточки до
     правого края правого блока (11.08.2026, просьба заказчика).
     Зазоры живут в самих распорках (minmax(18px, 1fr)), поэтому column-gap
     здесь нулевой: иначе на узком экране добавились бы лишние 36px, которых
     карточке и так не хватает. */
  grid-template-columns:
      var(--company-mirror) minmax(18px, 1fr) auto minmax(18px, 1fr)
      var(--company-side);
  column-gap: 0; row-gap: 18px;
}
/* Раскладка задана явно, а не самотёком: «История» обязана встать ПОД правым
   блоком, а не рядом с ним. Место кнопки «Назад» — там же, где её липучка
   (см. .company-tools ниже по файлу): одно правило на одну кнопку.
   Обёртка растворяется: её дети встают прямо в сетку страницы — иначе
   «История» осталась бы запертой в колонке карточки */
.company-main { display: contents; }
.company-main > .card-block { grid-column: 3; grid-row: 1; }
.company-main > .company-history {
  grid-column: 3 / -1; grid-row: 2;
  /* без этого широкая таблица распирала бы страницу вбок вместо того, чтобы
     прокручиваться внутри себя */
  min-width: 0;
}
/* место правого блока задано в его собственном правиле ниже (.company-side):
   одно правило на один блок */
/* min-width:0 — иначе колонка не сожмётся уже своего содержимого (широкая таблица
   истории распирала страницу вбок вместо того, чтобы прокручиваться внутри себя) */
.company-main { min-width: 0; }
/* Правый столбец — КОРОБКА во всю строку сетки, а липнет блок ВНУТРИ неё
   (11.08.2026, просьба заказчика). Пока липучка стояла на самом столбце,
   держать её было не за что: коробка кончалась вместе с содержимым, и блок
   уезжал вниз поверх «Истории» — замер на прокрутке 800 показал 175px
   налезания. Теперь коробка тянется до конца строки, и блок останавливается
   ровно на её низу. */
.company-side {
  grid-column: 5; grid-row: 1;      /* последняя дорожка, первая строка */
  /* Коробка тянется на всю строку сетки — по ней и липнет блок внутри.
     Абсолютное позиционирование тут пробовали (11.08.2026) и откатили:
     замер показал, что коробкой для липучки становится вся сетка, и блок
     уезжал в «Историю» на 62px — то есть ровно та беда, от которой лечили. */
  align-self: stretch; min-width: 0;
  /* вниз ровно на строку с плашкой этапа: тогда верх правого блока совпадает
     с верхом первого блока слева на любой стадии (25.07.2026) */
  margin-top: calc(var(--company-head-h) + var(--company-head-gap));
  /* низ коробки ставит company.js: он меряет карточку и задаёт высоту явно —
     вычитать отступы формулой оказалось ненадёжно (11.08.2026) */
}
.company-side > .block {
  position: sticky; top: 74px;
  /* Выше окна не растём (иначе не пролистать) и выше КАРТОЧКИ тоже: пока
     блок ниже её нижней линии, он сам растягивает строку сетки и опускается
     ниже карточки — липучка тут бессильна, она умеет только опускать блок, а
     не поднимать (11.08.2026, заметил заказчик). Высоту карточки в CSS не
     узнать, поэтому её меряет company.js и кладёт в --company-side-max;
     без скриптов остаётся прежнее поведение. */
  max-height: min(calc(100vh - 90px), var(--company-side-max, 100vh));
  overflow-y: auto;
  margin-bottom: 0;       /* иначе блок остановится на 18px выше низа коробки */
}
.block {
  background: var(--bg-elevated); border-radius: var(--radius-l);
  padding: 20px; margin-bottom: 18px; box-shadow: var(--shadow-card);
}

/* --- Плиточная сетка карточки компании ---
   Каждое поле и каждый файловый слот — отдельная плитка в сетке из 3 колонок
   (21.07.2026: было 5, но правая пара везде осталась пустой).
   Ширину плитки задаёт rules.CARD_LAYOUT (класс .tile-1 / .tile-2). */
.card-grid {
  display: grid;
  /* Все колонки одинаковые, а ширину задаёт самая широкая подпись:
     width:max-content сжимает сетку по содержимому, а равные доли 1fr
     подтягивают все колонки к самой требовательной. max-width не даёт вылезти. */
  /* Нижняя граница 155px — ровно min-width поля (.tile-input): без неё колонка
     ужимается по содержимому, и одна и та же сетка выходит разной ширины на
     разных формах. Так «ЭЦП физлица» (колонку там задавал чип, 139px) встала
     вровень с «ИНН» (155px) — колонки одинаковые везде (24.07.2026).
     Верхняя граница 1fr сохранена: колонка с самым широким содержимым по-
     прежнему подтягивает к себе остальные, и все три остаются равными. */
  grid-template-columns: repeat(3, minmax(155px, 1fr));
  width: max-content; max-width: 100%; margin-left: auto; margin-right: auto;
  gap: 12px; margin-bottom: 14px;
  /* плитка с файлами растёт вверх-вниз сама, соседние пустые не тянутся за ней */
  align-items: start;
}
/* Блок карточки (rules.card_blocks): подсетка на всю ширину, колонки те же,
   что у родителя, — плитки внутри блоков остаются выровненными между блоками. */
.card-block-group {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: subgrid;
  gap: 12px;
  align-items: start;
}
/* заголовок блока (rules.BLOCK_TITLES) — на всю ширину, над первой строкой */
.card-block-title {
  grid-column: 1 / -1;
  font-size: 13px; font-weight: 400; color: var(--text-secondary);
  /* до плиток — те же 4px, что от подписи до поля (замер на странице:
     gap 3px плюс дробный остаток строки). Отрицательный отступ гасит
     12px промежутка сетки: 12 − 8 = 4. */
  margin: 0 0 -8px;
}
/* --- ВНЕШНИЙ ВИД БЛОКОВ ---------------------------------------------------
   Убрать это одно правило — и карточка снова сплошная сетка, как раньше:
   разметка и раскладка не изменятся, исчезнут только подложка и отступы. */
.card-block-group {
  background: rgba(120, 120, 128, 0.07);
  border-radius: var(--radius-l);
  padding: 14px;
  /* Периметр — внутренней тенью, а не рамкой: рамка съела бы по пикселю от
     колонок подсетки, и плитки в блоках разъехались бы между собой. */
  box-shadow: inset 0 0 0 1px var(--hairline);
}
/* Блок без подложки (rules.BLOCK_PLAIN). Боковые отступы остаются, чтобы
   плитки стояли на тех же колонках, что и в остальных блоках, а вертикальные
   убраны: без подложки они лишь съедали расстояние. Отступ сверху доводит
   промежуток до 34px — высоты самой кнопки (12px даёт промежуток сетки). */
.card-block-plain {
  background: none; box-shadow: none;
  padding-top: 0; padding-bottom: 0; margin-top: 22px;
}
/* -------------------------------------------------------------------------- */

/* Форма добавления до ввода ИНН: единственное поле стоит без подложки —
   блок вокруг одного поля выглядит пустой рамкой. Класс вешает create.js
   и снимает, как только форма раскрылась. */
.card-grid-flat .card-block-group {
  background: none; box-shadow: none; padding: 0;
}
/* Пустой блок (все плитки скрыты) не занимает строку сетки и не рисуется:
   до ввода ИНН таких блоков пять, и они выглядели пустыми серыми полосами.
   Класс вешает create.js — на :has() полагаться нельзя, в старых браузерах
   всё правило целиком отбрасывается и пустые блоки остаются видимыми. */
.card-block-empty { display: none; }

/* Начало нового ряда: незаполненные ячейки предыдущего остаются пустыми.
   Ширину дублируем в каждом правиле — иначе сокращение grid-column у .tile-N
   ниже по файлу сбросило бы начальную колонку обратно. */
.tile-newrow.tile-1 { grid-column: 1 / span 1; }
.tile-newrow.tile-2 { grid-column: 1 / span 2; }
/* жёсткая привязка к столбцу (сбрасывается в медиазапросах ниже) */
.tile-col2.tile-1 { grid-column: 2 / span 1; }
.tile-col3.tile-1 { grid-column: 3 / span 1; }
.tile-1 { grid-column: span 1; }
.tile-2 { grid-column: span 2; }
.tile {
  position: relative; min-height: 68px;
  display: flex; flex-direction: column; justify-content: center;
  /* отступы под новую начинку: подпись (19px) + 3px + поле (34px) = 56px,
     плюс 6px сверху и снизу — плитка остаётся прежней высоты 68px */
  padding: 6px 14px; background: var(--bg-elevated);
  border-radius: var(--radius-m); box-shadow: var(--shadow-card);
  transition: box-shadow 0.2s ease, background 0.2s ease;
}
/* заметное кольцо фокуса: заливка --accent-tint давала контраст 1.1:1 */
.tile:focus-within { box-shadow: 0 0 0 2px var(--accent), var(--shadow-card); }
/* «или» между полем ИНН и кнопкой контейнера (01.08.2026): это не плитка, а
   развилка — ни подложки, ни тени, ни высоты плитки. Слово стоит по левому
   краю столбца, ровно под полем ИНН */
.tile-or {
  background: none; box-shadow: none; padding: 2px 0; min-height: 0;
  align-self: start; justify-content: center; text-align: center;
  color: var(--text-secondary); font-size: 14px; line-height: 1.4;
}
.tile-or:focus-within { box-shadow: none; }
/* форма создания: до ввода ИНН видны только первые две строки, и раскладка
   ООО/ИП различается — лишние плитки прячет create.js этим классом */
.tile-hidden { display: none; }
/* распорка (rules._spacer): занимает ячейки, но ничего не рисует */
.tile-spacer { background: none; box-shadow: none; padding: 0; min-height: 0; }
/* Строка «Создана … · В текущем статусе с …» и её класс .card-meta-line
   убраны 24.07.2026 — вместе с ними ушёл и отступ, который они давали. */

/* Поля (21.07.2026): подпись стоит НАД прямоугольником поля, а сам белый
   прямоугольник и есть поле — высотой 34px, как кнопки загрузки файлов.
   Плитка-обёртка прозрачная: иначе получился бы прямоугольник в прямоугольнике.
   Прежняя «всплывающая» подпись внутри поля убрана: она требовала
   placeholder=" " и невидимого близнеца-sizer. */
.tile-field {
  background: none; box-shadow: none; padding: 0; min-height: 0;
  justify-content: flex-start; gap: 4px;
  /* Все поля строки равняются ПО ВЕРХУ (01.08.2026, просьба заказчика).
     Выросшая плитка — красная ошибка под ИНН, название банка под счётом,
     двустрочная подпись — должна опускать строки НИЖЕ, а не поднимать свой
     столбец относительно соседних: при выравнивании по центру соседи
     съезжали вниз на половину прибавки (замер: 10px под ИНН).
     Раньше это лечилось точечно классом .tile-top у банковских строк —
     теперь правило одно на все поля, и частный случай не нужен. */
  align-self: start;
}
.tile-input {
  width: 100%; border: none;
  /* Нижняя граница ширины. Сетка меряется по содержимому, и когда в форме
     добавления видно одно поле «ИНН», колонка сжималась до ширины подписи —
     поле превращалось в кружок на 64px. 155px — обычная ширина колонки. */
  min-width: 155px;
  min-height: 34px; padding: 6px 14px;
  border-radius: var(--radius-m); background: var(--bg-elevated);
  box-shadow: var(--shadow-card);
  font: inherit; font-size: 14px; color: var(--text);
  transition: box-shadow 0.2s ease, background 0.2s ease;
}
/* кольцо фокуса теперь на самом поле, а не вокруг прозрачной обёртки */
.tile-input:focus { outline: none; box-shadow: 0 0 0 2px var(--accent), var(--shadow-card); }
.tile-field:focus-within { box-shadow: none; }
.tile-input:disabled { background: var(--bg-elevated); color: var(--text-secondary); }
/* В плитке отступы больше: галочка отходит от края на 14px (обычный отступ
   поля) плюс 12px — ширину самой галочки. Место под неё — в padding-right.
   Картинку приходится повторить: сокращённое background у .tile-input выше
   сбрасывает background-image, заданный в общем правиле для select. */
select.tile-input {
  background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%236e6e73' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  /* тот же отступ от края, что у текста поля */
  background-position: right 14px center;
  padding-right: 32px;
  /* Список НЕ раздвигает столбец сетки (03.08.2026). Сетка меряется по
     содержимому, и длинный пункт («ЭЦП не установлен в кабинете» — 245px
     против 155px у обычного поля) растягивал всю колонку — заказчик это
     сразу увидел на СБИСе. У текстовых полей от этого спасает size="1",
     у select такого нет: width:0 убирает вклад в расчёт колонки, а
     min-width возвращает поле на всю плитку. Длинный пункт в ЗАКРЫТОМ виде
     обрезается многоточием; раскрытый список браузер рисует сам, по своему
     содержимому и шире плитки — так и должно быть. */
  width: 0;
  /* проценты при подсчёте «по содержимому» не учитываются, а при отрисовке
     возвращают поле на всю плитку — поэтому не 155px, иначе список стал бы
     уже соседних полей в широкой колонке */
  min-width: 100%;
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;
}
/* --- Поля-справочники: ввод + список ранее введённого (21.07.2026) --------
   Выглядят как select (та же галочка, тот же отступ), но остаются обычным
   полем ввода: своё значение вписывается поверх подсказок. */
.suggest-wrap { position: relative; display: flex; }
.suggest-wrap .tile-input { padding-right: 32px; }
.suggest-toggle {
  position: absolute; right: 0; top: 0; bottom: 0; width: 32px;
  display: flex; align-items: center; justify-content: center;
  border: none; background: none; cursor: pointer; padding: 0;
  color: var(--text-tertiary);
}
.suggest-toggle svg { width: 12px; height: 8px; }
.suggest-toggle:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--accent); border-radius: var(--radius-s); }
.suggest-menu {
  position: absolute; z-index: 20; max-height: 260px; overflow-y: auto;
  display: flex; flex-direction: column; padding: 4px;
  background: var(--bg-elevated); border-radius: var(--radius-m);
  box-shadow: var(--shadow-lift);
}
.suggest-item { display: flex; align-items: center; gap: 2px; }
.suggest-pick {
  flex: 1 1 auto; min-width: 0; border: none; background: none;
  text-align: left; cursor: pointer; padding: 7px 12px;
  border-radius: var(--radius-s); font: inherit; font-size: 14px;
  color: var(--text); white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; transition: background 0.15s ease;
}
.suggest-pick:hover { background: var(--bg-fill-subtle); }
.suggest-pick:focus-visible, .suggest-del:focus-visible {
  outline: none; box-shadow: 0 0 0 2px var(--accent);
}
.suggest-del {
  flex: 0 0 auto; border: none; background: none; cursor: pointer;
  padding: 6px 8px; border-radius: var(--radius-s); color: var(--red);
  display: flex; align-items: center; transition: background 0.15s ease;
}
.suggest-del svg { width: 15px; height: 15px; }
.suggest-del:hover { background: var(--red-tint); }
.suggest-empty {
  padding: 7px 12px; font-size: 13px; color: var(--text-tertiary);
  white-space: nowrap;
}

/* Плитка-кнопка (21.07.2026): такой же чип, как кнопки загрузки файлов —
   она стоит с ними в одной строке. Обёртка прозрачная, иначе получилась бы
   кнопка внутри прямоугольника. */
.tile-action {
  background: none; box-shadow: none; padding: 0; min-height: 0;
  /* кнопка во всю ширину столбца и прижата к низу строки — как чипы загрузки */
  align-items: stretch; align-self: end;
}
.tile-action:focus-within { box-shadow: none; }
.action-btn {
  width: 100%; min-height: 34px; padding: 6px 14px;
  border: 1px solid var(--hairline); border-radius: 980px;
  background: none; text-align: center;
  font: inherit; font-size: 14px; color: var(--text-secondary); cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease;
}
.action-btn:hover:not(:disabled) {
  color: var(--text); border-color: var(--text-tertiary);
}
/* Выключенная кнопка не бледнее остальных: заказчик просил одинаковый вид
   (21.07.2026). О том, что кнопка пока не работает, говорят курсор
   not-allowed и подсказка title, но на глаз она от рабочих не отличается. */
.action-btn:disabled { color: var(--text-secondary); cursor: not-allowed; }
.action-btn:focus-visible {
  outline: none;
  border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-tint);
}
/* Эскиз создан: кнопка становится зелёным чипом, как заполненный слот файла,
   и открывает меню «Скачать / Создать заново / Удалить» */
.action-filled {
  background: var(--green-tint); border-color: transparent; color: var(--green-text);
}
.action-filled:hover:not(:disabled) {
  background: var(--green-tint); border-color: transparent; color: var(--green-text);
}
/* пока генерация в пути — кнопку не жмут дважды */
.action-busy { opacity: 0.55; pointer-events: none; }

/* Правил .eskiz-tools здесь больше НЕТ (31.08.2026): три круглые кнопки
   эскиза уехали в меню окна выбора — у ИП место справа от «+ Эскиз печати»
   занял «Лист печатей». Сам .round-btn жив: им пользуются кнопки чата и
   «Назад». */
.round-btn {
  /* размер одним значением: от него же считается зазор до карточки у кнопки
     чата — «расстояние равно ширине кнопки» (11.08.2026, просьба заказчика) */
  width: var(--round-btn); height: var(--round-btn);
  padding: 0; border-radius: 50%;
  border: 1px solid var(--hairline);
  display: inline-flex; align-items: center; justify-content: center;
  background: none;
  color: var(--text-secondary); cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease;
}
.round-btn svg { width: 17px; height: 17px; }
.round-btn:hover { color: var(--text); border-color: var(--text-tertiary); }
.round-btn:focus-visible {
  outline: none;
  border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-tint);
}
.round-btn-danger:hover { color: var(--red-text); background: var(--red-tint); border-color: transparent; }

/* окно просмотра эскиза: та же подложка, что у подтверждений */
.preview-box {
  width: min(480px, 92vw); padding: 14px;
  background: var(--bg-elevated); border-radius: var(--radius-l);
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.22);
  animation: modal-rise 0.18s cubic-bezier(0.2, 0.8, 0.3, 1);
}
.preview-box img {
  display: block; width: 100%; max-height: min(480px, 70vh);
  object-fit: contain; border-radius: var(--radius-m); background: #fff;
}
/* «Добавить» — такая же плитка последней строки, что и кнопки «+ …»:
   те же размеры, но фирменный синий цвет из .btn-primary остаётся */
.tile-action .btn {
  width: 100%; min-height: 34px; padding: 6px 14px;
  font-size: 14px; line-height: 1.4; text-align: center;
}
/* «Добавить» — под сеткой, по центру её ширины. Отступ сверху равен высоте
   самой кнопки (34px); с margin-bottom сетки он схлопывается, поэтому видимое
   расстояние остаётся 34px, а не 34+14. */
.submit-row {
  display: flex; justify-content: center;
  width: max-content; max-width: 100%; margin: 34px auto 18px;
}
.submit-row .btn {
  min-height: 34px; padding: 6px 18px; font-size: 14px; line-height: 1.4;
}
/* до ввода ИНН кнопки ещё нет (create.js) */
.submit-row-hidden { display: none; }

/* поле заморожено (включён «р/с реальный»): данные видны, правка запрещена.
   Заливка на самом поле — обёртка теперь прозрачная */
.tile-locked { background: none; }
.tile-locked .tile-input {
  color: var(--text-secondary); cursor: not-allowed;
  background: var(--bg-page); box-shadow: inset 0 0 0 1px var(--hairline);
}
/* название компании — жирным: оно заменяет собой убранный заголовок страницы */
.tile-input[data-field="name"], .tile-input[name="name"] { font-weight: 700; }
/* Chromium раздвигает точки в дате собственными отступами разделителей */
.tile-input[type="date"]::-webkit-datetime-edit-text { padding: 0; }
.tile-input[type="date"]::-webkit-datetime-edit-day-field,
.tile-input[type="date"]::-webkit-datetime-edit-month-field,
.tile-input[type="date"]::-webkit-datetime-edit-year-field { padding: 0; }
/* Подпись поля — над полем. Порядок в разметке обратный (поле, потом подпись),
   поэтому подпись поднимает order, а не перестановка в двух шаблонах.
   .tile-caption — строка «маленький тумблер + подпись» у полей из FIELD_TOGGLES;
   у остальных полей в ней только подпись. */
.tile-caption { order: -1; display: flex; align-items: center; min-width: 0; }
/* ЗАЗОР ОТ БЕГУНКА ДО ЕГО ПОДПИСИ — один на обе пары в строке (просьба
   заказчика 25.08.2026). Слева это тумблер поля и подпись плитки, справа —
   названный тумблер и его текст; стоят они рядом, и разные зазоры видно
   сразу (замер до правки: 6.99 слева против 10.99 справа). Одно правило на
   обе, а не два одинаковых числа в разных местах. */
.tile-caption, .switch-sm.switch-named { gap: 7px; }
.tile-label {
  order: -1;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* КЕГЛЬ И ЦВЕТ подписи — тоже один на обе (25.08.2026): было 12.5px слева
   против 12px справа, и заказчик увидел разницу в полпункта */
.tile-label, .switch-sm.switch-named .switch-text {
  font-size: 12.5px; color: var(--text-tertiary);
}
/* внутри строки подписи порядок прямой: сначала тумблер, потом текст */
.tile-caption .tile-label { order: 0; }
/* --red #ff3b30 даёт на белом 3.2:1 — для звёздочки берём тёмный вариант */
.tile-label .req { color: var(--red-text); font-weight: 400; margin-left: 2px; }
/* Близнец подписи больше не нужен: подпись стоит в потоке и сама задаёт ширину
   колонки. Оставлен в шаблонах, чтобы не переписывать их ради одной строки. */
.tile-sizer { display: none; }
.tile-label-top { font-size: 12px; color: var(--text-secondary); }

/* списки людей */
.tile-people { justify-content: flex-start; }
.tile-people .tile-input {
  padding: 4px 0 0; font-size: 13.5px; height: auto;
}

/* переключатели вместо галочек */
/* Плитка тумблера (21.07.2026): ни белой подложки, ни видимой подписи —
   только сам бегунок, прижатый к правому краю своего столбца и выровненный
   по центру строки. Что означает тумблер, видно по соседнему полю справа. */
.tile-toggle {
  background: none; box-shadow: none; padding: 0; min-height: 0;
  align-items: flex-end; justify-content: center; align-self: center;
}
/* Подпись невидима, но место занимает: благодаря этому бегунок встаёт ровно
   на ту же горизонталь, что поле ввода справа (у поля сверху своя подпись).
   opacity, а не display:none и не visibility — иначе для скринридера
   переключатель остался бы без имени. */
.tile-toggle .switch { flex-direction: column; align-items: flex-end; gap: 3px; }
.tile-toggle .switch-text { order: -1; opacity: 0; font-size: 12.5px; }
/* добор до высоты поля ввода (34px); лишний пиксель сверху — компенсация
   дробной высоты строки подписи, без него центры расходились на 1px */
.tile-toggle .switch-track { margin: 5px 0 4px; }
/* кольцо фокуса — на самом бегунке (см. .switch input:focus-visible ниже) */
.tile-toggle:focus-within, .tile-toggle:has(:focus-visible) { box-shadow: none; }

.switch { display: flex; align-items: center; gap: 11px; cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch-track {
  flex: none; width: 42px; height: 25px; border-radius: 980px;
  background: var(--bg-fill); position: relative;
  /* граница обязательна: серая заливка на белой плитке давала 1.2:1 — выключенный
     переключатель был почти не виден (WCAG 1.4.11 требует 3:1 для контролов) */
  border: 1px solid #8e8e93;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.switch-track::after {
  content: ''; position: absolute; top: 1px; left: 1px;
  width: 21px; height: 21px; border-radius: 50%; background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25); transition: transform 0.2s ease;
}
.switch input:checked + .switch-track { background: var(--green); border-color: var(--green-text); }
.switch input:checked + .switch-track::after { transform: translateX(17px); }
.switch input:focus-visible + .switch-track { box-shadow: 0 0 0 4px var(--accent-tint); }
.switch input:disabled ~ .switch-text { color: var(--text-secondary); }
.switch input:disabled + .switch-track { opacity: 0.5; }
.switch-text { font-size: 14px; color: var(--text); }

/* Маленький тумблер внутри подписи поля (rules.FIELD_TOGGLES).
   Подпись рядом — 12.5px, поэтому переключатель уменьшен под её рост. */
.switch-sm { flex: none; }
.switch-sm .switch-track { width: 30px; height: 18px; }
.switch-sm .switch-track::after { width: 14px; height: 14px; }
.switch-sm input:checked + .switch-track::after { transform: translateX(12px); }
/* У НАЗВАННОГО тумблера подпись видна: «открытие р/с невозможно» стоит рядом
   с чужим полем («р/с копия»), и без подписи его не прочитать (24.08.2026).
   Правило идёт ПОСЛЕ общего скрытия ниже — иначе оно бы его не перебило. */
/* подпись самого тумблера не показываем — рядом стоит подпись поля,
   но для скринридера имя контрола остаётся */
.switch-sm .switch-text {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  border: 0; overflow: hidden; white-space: nowrap; clip-path: inset(50%);
}
.switch-sm.switch-named .switch-text {
  /* здесь только ПОКАЗЫВАЕМ подпись: кегль и цвет приходят из общего
     правила с .tile-label выше — второй копии быть не должно */
  position: static; width: auto; height: auto; margin: 0;
  overflow: visible; clip-path: none; white-space: nowrap;
}
/* второй тумблер отделён от подписи поля, чтобы строка читалась как две пары */
.switch-named { margin-left: 10px; }
/* «На сайте штамповки» прижат к ПРАВОМУ краю шапки (правка заказчика
   02.09.2026): «слово "штамповки" справа должно заканчиваться на одной
   вертикальной линии с блоками карточки». Линию задаёт не число, а сама
   шапка: она лежит в .card-block, а тот шириной ровно с сетку плиток —
   значит правый край подписи и правый край сетки сходятся сами и не
   разъедутся, когда сетка сменит ширину. Правило идёт ПОСЛЕ .switch-named:
   у них одна весомость, и побеждает последнее. */
.switch-stamping { margin-left: auto; }

/* Файловые слоты — компактные кнопки-чипы (21.07.2026).
   Плитки-карточки у слотов нет: сам слот и есть кнопка, её ширина — по подписи,
   а не по колонке сетки. Позицию в сетке по-прежнему задают tile-* классы. */
.tile-slot {
  background: none; box-shadow: none; padding: 0; min-height: 0;
  /* кнопка занимает всю ширину своего столбца, как поля ввода */
  align-items: stretch; justify-content: center;
  /* Прижимаем к низу строки: у поля рядом сверху подпись, и его прямоугольник
     заканчивается там же, где кнопка. Оба высотой 34px — значит совпадают и
     низ, и центр. По центру строки кнопка висела бы выше поля. */
  align-self: end;
}
/* клик по всей плитке открывает выбор файла (обработчик в company.js/create.js);
   :has — только там, где слот вообще редактируемый */
.tile-slot:has(.upload-input) { cursor: pointer; }
.slot-add {
  /* скрепка и подпись — единой группой по центру кнопки */
  display: flex; width: 100%; align-items: center; justify-content: center; gap: 7px;
  min-height: 34px; padding: 6px 14px; border-radius: 980px;
  border: 1px solid var(--hairline);
  font-size: 14px; color: var(--text-secondary);
  transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
.slot-name { flex: 0 1 auto; }
/* курсор и подсветка — только когда слот реально кликабельный (роль с правами) */
label.slot-add { cursor: pointer; }
label.slot-add:hover { color: var(--text); border-color: var(--text-tertiary); }
/* input больше не hidden, прячем его так, чтобы он оставался в порядке табуляции */
.upload-input {
  position: absolute; width: 1px; height: 1px; opacity: 0;
  padding: 0; border: 0; /* иначе глобальные отступы полей раздувают его до 26x20 */
}
/* кольцо фокуса теперь на самом чипе, а не вокруг невидимой плитки */
.slot-add:focus-within {
  color: var(--text);
  border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-tint);
}
.tile-slot:focus-within { box-shadow: none; }
/* скрепка слева от подписи (order, чтобы не трогать разметку обоих шаблонов) */
.slot-add .clip { order: -1; flex: none; width: 15px; height: 15px; color: var(--text-tertiary); }
/* загружено: заливка вместо контура — состояние видно и без чтения подписи */
.slot-filled .slot-add {
  background: var(--green-tint); border-color: transparent; color: var(--green-text);
}
.slot-filled .slot-add .clip { color: var(--green-text); }
/* Кнопка «ЭЦП директора» на карточке красится теми же четырьмя цветами, что
   её кругляшок на доске (12.08.2026, требование заказчика: «красить
   обязательно»). Подпись у кнопки всегда одна — «ЭЦП директора»; состояние
   говорит цвет и подсказка, второй строки заказчик не хочет.
   Правила стоят НИЖЕ .slot-filled и бьют его при равном весе — иначе
   загруженная, но отозванная ЭЦП осталась бы зелёной. */
.slot-ecp.ecp-mark-yes .slot-add {
  background: var(--green-tint); border-color: transparent; color: var(--green-text);
}
.slot-ecp.ecp-mark-yes .slot-add .clip { color: var(--green-text); }
.slot-ecp.ecp-mark-reissue .slot-add {
  background: var(--orange-tint); border-color: transparent; color: var(--orange-text);
}
.slot-ecp.ecp-mark-reissue .slot-add .clip { color: var(--orange-text); }
.slot-ecp.ecp-mark-revoked .slot-add {
  background: var(--text-secondary); border-color: transparent; color: var(--bg-elevated);
}
.slot-ecp.ecp-mark-revoked .slot-add .clip { color: var(--bg-elevated); }
.slot-name .optional {
  display: block; font-style: normal; font-size: 11.5px; color: var(--text-tertiary);
}
/* Отдельной отметки ✔ больше нет: о наличии файла говорит цвет чипа. */
.slot.dragover { background: none; box-shadow: none; }
.slot.dragover .slot-add {
  background: var(--accent-tint); border-color: var(--accent);
  color: var(--text);
}
.slot.uploading { opacity: 0.55; }
/* Список загруженных файлов под чипом не показываем (21.07.2026): о наличии
   файла говорит зелёный цвет самой кнопки. Разметка списка остаётся видимой
   для JS — из неё берутся id, имена и ссылки для меню «Скачать / Удалить»,
   которое открывается кликом по зелёной кнопке (company.js). */
.tile-slot .file-list { display: none; }

/* Меню зелёной кнопки: единственный путь к самому файлу, раз имя скрыто */
.slot-menu {
  /* ВЫШЕ модального окна (200), но ниже подсказки (300) — правило слоёв
     живёт у .tip. С прежними 20 меню, открытое ИЗ окна (клик по уже
     выбранной печати, 31.08.2026), рисовалось ПОД диалогом: в разметке оно
     есть, а человек не видит его и не может нажать — клик попадает в окно.
     Поймано настоящей мышью; синтетический click «проходил» и здесь. */
  position: absolute; z-index: 250;
  display: flex; flex-direction: column; padding: 4px;
  background: var(--bg-elevated); border-radius: var(--radius-m);
  box-shadow: var(--shadow-lift);
}
.slot-menu-item {
  border: none; background: none; text-align: left; cursor: pointer;
  padding: 7px 12px; border-radius: var(--radius-s);
  font: inherit; font-size: 14px; color: var(--text);
  transition: background 0.15s ease;
  /* Пункт — В ОДНУ СТРОКУ (правка заказчика 30.08.2026): «Отметить как ЧО»
     переносилось надвое. Меню лежит absolute у body и ужимается по месту
     СПРАВА ОТ КНОПКИ, а столбец «ЧО» крайний правый — места там нет вовсе.
     Перенос запрещаем здесь, а за край экрана меню не пускает slot-menu.js:
     у правого края оно разворачивается влево. */
  white-space: nowrap;
}
.slot-menu-item:hover { background: var(--bg-fill-subtle); }
.slot-menu-item:focus-visible {
  outline: none; box-shadow: 0 0 0 2px var(--accent);
}
.slot-menu-danger { color: var(--red-text); }
.slot-menu-danger:hover { background: var(--red-tint); }
/* отступ сверху — только когда файлы есть; иначе пустой список сдвигает подпись
   вверх и она перестаёт стоять по центру плитки */
.file-list { list-style: none; margin: 0; padding: 0; }
.file-list li:first-child { margin-top: 6px; }
.file-list li { display: flex; align-items: center; gap: 7px; padding: 3px 0; font-size: 13px; flex-wrap: wrap; }
/* длинное имя файла не растягивает плитку на пол-экрана; полное — в title */
.file-list a, .file-list .file-name {
  flex: 1 1 auto; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.file-meta { color: var(--text-tertiary); font-size: 11.5px; }
.btn-del {
  border: none; background: none; color: var(--red); cursor: pointer;
  font-size: 13px; padding: 2px 8px; border-radius: 980px;
  transition: background 0.2s ease;
}
.btn-del:hover { background: var(--red-tint); }

/* --- Переходы --- */
.transition { margin-bottom: 14px; }
/* У ПОСЛЕДНЕГО перехода отступа снизу нет: он разделяет переходы, а
   разделять ему нечего (26.08.2026). Из-за него блок был на 12px выше
   своего содержимого и с `overflow: auto` показывал полосу прокрутки
   там, где прокручивать нечего. */
.transition:last-child { margin-bottom: 0; }
.btn-transition { width: 100%; padding: 11px 18px; font-size: 14px; }
.missing-block {
  /* 20px — столько же, сколько отступ блока сверху: раньше «Не хватает»
     жалось к кнопке, хотя над кнопкой воздуха было вдвое больше
     (25.07.2026) */
  margin-top: 20px; background: var(--red-tint);
  border-radius: var(--radius-m); padding: 10px 13px;
}
/* «Сменить статус» отходит от поля на те же 20px, что и остальные блоки
   правой колонки друг от друга (25.07.2026) */
.force-status button { margin-top: 20px; }
.missing-title { font-size: 12.5px; font-weight: 600; color: var(--red-text); margin-bottom: 5px; }
.missing-list { margin: 0; padding-left: 18px; font-size: 12.5px; color: var(--red-text); }
.missing-list li { margin-bottom: 3px; }
.hidden { display: none; }
/* не видно на экране, но доступно скринридеру (навигация по заголовкам) */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  border: 0; overflow: hidden; white-space: nowrap; clip-path: inset(50%);
}

/* --- История и таблицы --- */
/* таблица прокручивается внутри себя, иначе на узком экране едет вся страница */
.table-scroll { overflow-x: auto; }
/* Дата со временем, имя человека и название действия — всегда в одну строку
   (11.08.2026, просьбы заказчика двумя шагами): перенос делал из
   «11.08.2026 09:00» лесенку, из «Принудительная смена статуса (админ)» —
   три строки, а из «Лара Крофт» — две. Переносится только «Описание»: оно
   длинное, и ему перенос не мешает. Если строка не влезает, таблица
   прокручивается внутри себя (.table-scroll), а не рвёт слова. */
.history .col-when, .history .col-who,
.history .col-action { white-space: nowrap; }
.history, .users-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.history th, .history td, .users-table th, .users-table td {
  text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--separator); vertical-align: middle;
}
.history tbody tr:hover, .users-table tbody tr:hover { background: rgba(0, 0, 0, 0.02); }
.history th, .users-table th {
  color: var(--text-tertiary); font-weight: 600; font-size: 12px;
}
.users-table input, .users-table select { font-size: 13px; padding: 5px 8px; }
/* столбец «Статус»: галочка ровно под серединой подписи (25.07.2026) */
.users-table th.col-status, .users-table td.col-status { text-align: center; }

@media (max-width: 1000px) {
  /* колонка одна: кнопка встаёт своей строкой сверху и липкой быть перестаёт —
     держать её на узком экране не за что */
  .company-layout { grid-template-columns: 1fr; }
  /* колонка одна — раскладываем всё по порядку, иначе явные номера дорожек
     создали бы колонки за краем экрана */
  .company-tools,
  .company-main > .card-block,
  .company-main > .company-history,
  .company-side { grid-column: 1; grid-row: auto; }
  /* в одну колонку кнопки встают строкой над карточкой, а не столбиком */
  .company-tools {
    justify-self: start; margin: 0 0 10px; transform: none;
    height: auto; flex-direction: row; align-items: center;
    position: static;
  }
  /* колонка одна — правый блок идёт под левым, и выравнивать по верху нечего;
     липнуть ему тоже не за что */
  .company-side { margin-top: 0; margin-bottom: 0; }
  .company-side > .block {
    position: static; max-height: none; overflow-y: visible;
  }
  .board { grid-template-columns: 1fr; }
  /* на узком экране колонки и так в одну — центровать нечего */
  .board-cols-2 { width: auto; }
  .archive-list { grid-template-columns: 1fr; }
  /* плитки: 3 колонки → 2, широкие занимают всю строку */
  .card-grid { grid-template-columns: repeat(2, 1fr); }
  /* столбцов меньше — привязка к 3-му создала бы лишнюю колонку за краем */
  .tile-col3.tile-1 { grid-column: auto / span 1; }
}
@media (max-width: 560px) {
  .card-grid { grid-template-columns: 1fr; }
  /* на узком экране отступы блока и длинные подписи в одну строку вместе
     вылезали за край — ужимаем первое и разрешаем перенос второму */
  .card-block-group:not(.card-block-plain) { padding: 12px 8px; }
  .card-block-plain { padding-left: 8px; padding-right: 8px; }
  .tile-label { white-space: normal; }
  .tile-2 { grid-column: span 1; }
  .tile-col2.tile-1, .tile-col3.tile-1 { grid-column: auto / span 1; }
  .tile-newrow.tile-1, .tile-newrow.tile-2 { grid-column: auto / span 1; }
  /* на узком экране шапка переносится, а не вылезает за край */
  .topbar { flex-wrap: wrap; gap: 10px 16px; padding: 10px 16px; }
  .topbar nav { flex: 1 1 100%; }
  main { padding: 16px 16px 48px; }
}

/* --- Раздел «Оплаты» (24.08.2026) ---------------------------------------
   Учёт услуг за период: прайс, фильтры, итоги, таблица. Ссылка на раздел
   стоит слева от «Пользователей», а у кого их нет — слева от имени. */
/* Ширина блока = ширина таблицы + его отступы (20 слева и справа): 1176 + 40.
   С появлением столбца «ЧО» (30.08.2026) таблица выросла с 1140 до 1176, и
   прежние 1180 сделали бы ей ВЕЧНУЮ горизонтальную прокрутку — потолок здесь
   жёсткий, шире блок не становился ни при каком окне (замер: обёртка 1140 и
   при 1400, и при 1460). Раздвинули блок, а не поделили 36px между прежними
   столбцами: их ширины заказчик зафиксировал отдельной просьбой 27.08.2026.
   31.08.2026 просвет перед «Юрадресом» вырос на 19px, но ширина таблицы
   ОСТАЛАСЬ 1176 — см. пояснение у столбца «ЧО».
   03.09.2026 столбцы услуг стали по 180 вместо 174, таблица выросла до 1200,
   и блок — до 1240 теми же 20+20. */
.oplaty-block { max-width: 1240px; margin: 0 auto; }
/* Кнопка выгрузки идёт СРАЗУ ЗА фильтрами, тем же зазором 10px, что между
   ними, — а не прижимается к правому краю (правка заказчика 24.08.2026:
   сперва она была с `margin-left: auto`, и на широком экране улетала от
   фильтров в пустоту). Размер и вид приходят из общего `.round-btn`, здесь
   только неспадаемость и размер значка. */
.oplaty-export { flex: none; }
.oplaty-export svg { width: 17px; height: 17px; }

.oplaty-price {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 14px;
  padding: 14px 16px; margin-bottom: 18px;
  background: var(--bg-fill-subtle); border-radius: var(--radius-m);
}
/* ПРАЙС ПЕРЕЕХАЛ В ОКНО (25.08.2026, просьба заказчика): блока сверху
   таблицы больше нет, цены открываются кликом по имени поставщика В САМОЙ
   ТАБЛИЦЕ. Внутри окна заливка ни к чему — окно и так отделено подложкой. */
.oplaty-price-modal .oplaty-price {
  background: none; padding: 0; margin-bottom: 18px;
}
/* Имя поставщика — ссылка, но НЕ синяя: это столбец данных, и синевой он
   спорил бы с названиями компаний рядом. Что по нему кликают, говорит
   пунктир и курсор-рука — тот же приём, что у «чего не хватает» на плашке. */
.oplaty-price-link {
  color: inherit; text-decoration: none;
  border-bottom: 1px dashed var(--hairline); cursor: pointer;
}
.oplaty-price-link:hover {
  color: var(--accent); border-bottom-color: var(--accent); text-decoration: none;
}
.oplaty-price-link:focus-visible {
  outline: none; color: var(--accent); border-bottom-color: var(--accent);
}
/* Окно шире обычного: в строке прайса поставщик и три суммы */
.oplaty-price-modal { max-width: 640px; }
/* Кнопка выгрузки прайса — тот же значок и размер, что у экспорта в
   ряду фильтров (.oplaty-export): одинаковые по смыслу кнопки берут вид
   из одного правила, иначе однажды разъедутся */
.oplaty-price-export svg { width: 17px; height: 17px; }
.oplaty-price-modal .modal-buttons { align-items: center; }
.oplaty-price-title {
  margin: 0 0 16px; font-size: 19px; font-weight: 600; color: var(--text);
}
/* В ОКНЕ заголовок по центру (просьба заказчика 25.08.2026) — там же, где
   стоит ряд кнопок под формой: у окна своя ось симметрии, и заголовок у
   левого края её ломал. На СТРАНИЦЕ прайса он остаётся слева: там обычная
   вёрстка страницы с левым краем у всего остального. */
.oplaty-price-modal .oplaty-price-title { text-align: center; }
/* На СТРАНИЦЕ прайса (путь без скриптов) заголовок тот же, но своим кеглем
   заголовка страницы — правило одно, отличается только тег */
h1.oplaty-price-title { font-size: 23px; margin-bottom: 18px; }
/* Выбор поставщика стоит ПЕРВЫМ в строке прайса и отделён от сумм: прайс
   принадлежит ему, а не строке вообще (правка заказчика 24.08.2026). Список
   той же таблеткой, что фильтры, — правило `.board-tools select` сюда не
   достаёт, поэтому вид задан здесь. */
.oplaty-price-who select {
  height: 34px; padding: 0 30px 0 12px; font: inherit; font-size: 14px;
  border: 1px solid var(--hairline); border-radius: 980px;
  /* background-COLOR, а не сокращённый background: сокращение стирает
     background-image, которым нарисована галочка списка в общем правиле
     `select`, — и галочка пропадала (24.08.2026). Та же ловушка уже описана
     выше по файлу у `.board-filter-sizer`. */
  background-color: var(--bg-elevated); color: var(--text);
  /* ширину меряет содержимое, но столбец раздела не раздуваем */
  max-width: 240px;
}
.oplaty-price-who { margin-right: 8px; }
.oplaty-price-label {
  font-size: 12px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-tertiary); align-self: center;
}
.oplaty-price-item { display: flex; flex-direction: column; gap: 4px; }
.oplaty-price-item span { font-size: 12px; color: var(--text-secondary); }
.oplaty-price-item input {
  width: 120px; height: 34px; padding: 0 10px; font: inherit; font-size: 14px;
  border: 1px solid var(--hairline); border-radius: var(--radius-m);
  background: var(--bg-elevated); text-align: right;
}
.oplaty-price-item input:focus-visible { outline: none; border-color: var(--accent); }

/* Панель фильтров раздела «Оплаты» берёт вид У ДОСКИ (просьба заказчика
   24.08.2026): в разметке стоят те же классы `board-tools` и `board-search`,
   поэтому таблетки, высота и поле поиска приходят из ОДНОГО правила. Здесь
   остаётся только то, чем раздел отличается от доски: панель прижата ВЛЕВО,
   а не по центру — под ней таблица с левым краем, и центрованная панель
   висела бы отдельно от неё. */
/* «Показать» ростом ниже таблеток панели (35.59 против 38.30), и при
   выравнивании по центру её верх уходил на 1.3px вниз — в этом проекте такое
   видно. Тянем кнопку по высоте СТРОКИ, а не вписываем 38.3px числом:
   поменяется кегль панели — кнопка поедет следом сама. */
.oplaty-filters .btn { align-self: stretch; }

/* Итоги идут ПЕРЕД таблицей: раздел открывают ради них, а не ради строк */
.oplaty-totals {
  display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 18px;
}
.oplaty-total {
  display: flex; flex-direction: column; gap: 2px;
  padding: 10px 14px; border-radius: var(--radius-m);
  background: var(--bg-fill-subtle); min-width: 150px;
  /* с 25.08.2026 плашка — ссылка-фильтр: гасим подчёркивание и цвет ссылки,
     иначе итоги стали бы синими */
  color: inherit; text-decoration: none;
  border: 1px solid transparent;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.oplaty-total:hover { text-decoration: none; border-color: var(--hairline); }
/* включённый фильтр виден рамкой, а не заливкой: заливку у свода уже носит
   его собственный цвет, и второй краской они бы спорили */
.oplaty-total-on { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-tint); }
.oplaty-total:focus-visible {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-tint);
}
/* «Оплачено» и «Долг» — главные числа раздела, и с 25.08.2026 они стоят на
   СВОИХ фонах: те же green-tint и red-tint, что у зелёных кнопок и красных
   блоков во всём приложении. Раньше цвет нёс только текст, а фон был общий
   серый — свод не отличался от разбивки по услугам. */
.oplaty-total-good { background: var(--green-tint); }
.oplaty-total-debt { background: var(--red-tint); }
/* СВОД ПЕРВЫМ И КРУПНЕЕ (27.08.2026, выбор заказчика из четырёх показанных
   картинкой вариантов): «Оплачено» и «Долг» — ответ раздела, услуги — его
   разбивка. Прежний порядок 24.08 «услуги слева, свод справа» отменён им же;
   правило .oplaty-total-apart ушло вместе с ним. */
/* Вертикальная набивка как у услуг: без скриптов высоты ближе друг к другу,
   со скриптами точную высоту свода ставит замер в payments.js */
.oplaty-total-big { padding: 10px 18px; justify-content: center; }
.oplaty-total-big .oplaty-total-sum { font-size: 20px; font-weight: 700; }
/* Плашки услуг тише свода: кегль суммы 14 против 20 — вес убывает вместе с
   порядком чтения */
.oplaty-total-usluga .oplaty-total-sum { font-size: 14px; }
.oplaty-total-usluga .oplaty-total-sum small { font-size: 11px; }
/* Строка подписи держит место под чип ВСЕГДА: высота плашки не должна
   зависеть от того, есть долг или нет (просьба заказчика 27.08.2026) */
.oplaty-total-top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; min-height: 20px;
}
/* Просвет между подписью и чипом долга ужат с 10 до 8 — только у плашки,
   стоящей НАД столбцом: там ширина задана, и эти 2px идут в запас верхней
   строке. У свободного ряда просвет прежний. */
.oplaty-totals-aligned .oplaty-total-usluga .oplaty-total-top { gap: 8px; }
/* Долг — чипом: с суммой «15 000 ₽», без — слово «долг». Геометрия — как у
   плашек-счётчиков доски, цвета из общих токенов */
.oplaty-total-chip {
  font-size: 11px; line-height: 1.35; padding: 1px 8px; border-radius: 980px;
  background: var(--red-tint); color: var(--red-text); white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* Невписанный ноль ТЕРЯЕТ ВЕС: ряд читается «учёт не начат», а не «всё
   сломано». Именно text-secondary, а не tertiary: tertiary на сером фоне
   плашки даёт ~4.3:1 — ниже AA для мелкого. Правило стоит НИЖЕ
   .oplaty-total-sum и при равном весе побеждает порядком. */
.oplaty-total-zero { color: var(--text-secondary); font-weight: 400; }
/* Выравнивание плашек услуг НАД их столбцами таблицы: payments.js меряет th
   и ставит left/width — ширину столбца в CSS не узнать. Класс вешает скрипт,
   и только пока таблица без горизонтальной прокрутки; без него (и без
   скриптов вовсе) ряд остаётся обычным флексом. */
.oplaty-totals { position: relative; }
.oplaty-totals-aligned .oplaty-total-usluga {
  position: absolute; top: 0; min-width: 0;
  /* БОКОВЫЕ ОТСТУПЫ УЖАТЫ с 14 до 8 (и просвет верхней строки с 10 до 8), и
     это не украшательство. Свободный ряд сам растягивал плашку под
     содержимое; над столбцом ширина ЗАДАНА — столбец минус зазор ряда, 168px.
     Внутри при отступах 14 остаётся 138, а верхней строке «Открытие р/с» —
     самой длинной подписи — с чипом долга нужно 139.8 (замер в Firefox:
     подпись 74.23, просвет 8, чип 57.63): она и разъезжалась на две строки,
     с чего заказчик и начал.

     Почему 8, а не 10: при 10 внутри 146, и шестизначный долг (чип 63.57)
     оставляет запас 0.2px — то есть держится на округлении. При 8 внутри 150
     и запас 4.2. Семизначный долг (чип 72.52) не влезает и так — подпись уйдёт
     на две строки; шире столбец сделать нельзя, порог прокрутки уже 1271 при
     1280-пиксельных экранах. Арифметику стережёт проверка в смоуке — она
     считает те же величины, а не сверяет картинку. */
  padding-left: 8px; padding-right: 8px;
}
.oplaty-total-label { font-size: 12px; color: var(--text-tertiary); }
.oplaty-total-sum { font-size: 17px; font-weight: 600; font-variant-numeric: tabular-nums; }
.oplaty-total-sum small { font-size: 12px; font-weight: 400; color: var(--text-tertiary); }
.oplaty-good { color: var(--green-text); }
.oplaty-debt { color: var(--red-text); }

/* Носитель подсказки в таблице оплат показывает КУРСОР-ВОПРОС (27.08.2026,
   просьба заказчика): наведение здесь даёт сведения — даты, причину
   прочерка, — а не действие. Настоящим кнопкам и ссылкам курсор возвращается:
   у них наведение обещает клик. Подсказки живут в data-tip (tooltip.js
   забирает title сразу), [title] оставлен на путь без скриптов. */
.oplaty-table [data-tip], .oplaty-table [title] { cursor: help; }
.oplaty-table a[data-tip], .oplaty-table a[title],
.oplaty-table button[data-tip], .oplaty-table button[title],
.oplaty-table label[data-tip], .oplaty-table label[title] { cursor: pointer; }
/* Таблица «строка — компания» (правка заказчика 24.08.2026). Услуги идут
   столбцами; в ячейке сумма, тумблер и под ними даты начисления и оплаты. */
.oplaty-grid thead th { vertical-align: bottom; }
.oplaty-who { color: var(--text-secondary); }
/* Длинные «Поставщик» и «ООО / ИП» СОКРАЩАЮТСЯ многоточием справа (просьба
   заказчика 27.08.2026): столбцы зафиксированы, и длинному имени расти
   больше некуда. text-overflow на самих td работает потому, что раскладка
   таблицы fixed — при auto ячейка сперва растянула бы столбец. */
.oplaty-who, .oplaty-name {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* .oplaty-cell { min-width: 150px } убран 27.08.2026: при фиксированной
   раскладке ширину столбца задаёт th, и второй источник ширины на td лишь
   спорил бы с первым */
/* Красной заливки у неоплаченной ячейки БОЛЬШЕ НЕТ (27.08.2026, просьба
   заказчика). Сам класс в разметке остался: по нему скрипт понимает состояние
   ячейки, а проверки находят строку с долгом. Красить он больше ничего не
   должен — то же решение, что у `.rs-badge-kopiya` на доске (09.08.2026):
   признак в разметке живёт, цвета у него нет. Состояние теперь говорит
   КРУГЛЯШОК справа от суммы. */
/* Пара «сумма + кругляшок» прижата к ПРАВОМУ краю столбца (03.09.2026):
   тогда кругляшки встают ровно под подписью столбца, а правый край столбца
   читается один на всю таблицу. Просвет между суммой и кругляшком при этом
   не плавает — коробка суммы задана.
   Плашка итога сверху к этой вертикали НЕ относится: её содержимое заказчик
   вернул к левому краю 04.09.2026, а над столбцом её держит коробка —
   payments.js ставит ей `left` и `width` по замеру шапки. */
.oplaty-cell-top {
  display: flex; align-items: center; justify-content: flex-end;
  gap: var(--oplaty-flag-gap);
}
.oplaty-cell .oplaty-amount {
  /* поле по содержимому, а не на всю ячейку: рядом стоит кругляшок.
     Ширину берёт общая величина — по ней же равняется текст у ролей без
     права правки, и вертикаль у всех одна */
  width: var(--oplaty-sum-w); height: 30px; padding: 0 8px;
  font: inherit; font-size: 14px;
  text-align: right; font-variant-numeric: tabular-nums;
  border: 1px solid var(--hairline); border-radius: var(--radius-s, 8px);
  background-color: var(--bg-elevated);
}
/* Кругляшок рубля стоит ПЕРЕД названием (25.08.2026): по столбцу сразу видно,
   кто должен. Размер и посадка приходят из общего .ecp-mark — своего числа
   тут нет, иначе он разъехался бы с доской. */
/* Кругляшок оплаты стоит СВОИМ узким столбцом (26.08.2026, просьба
   заказчика): в общей ячейке с названием он сдвигал имена, и те
   переставали стоять в одну вертикаль. Шапка у столбца пустая —
   подписывать знак незачем, он говорит цветом и подсказкой. */
.oplaty-mark-col {
  white-space: nowrap;
  padding-right: 0;   /* ширина столбца зафиксирована ниже, у раскладки */
}
.oplaty-mark-col .paid-mark { vertical-align: middle; }
.oplaty-cell-when {
  display: flex; align-items: center; gap: 4px; margin-top: 4px;
  font-size: 12px; color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
}
/* Строка под суммой ГОВОРИТ состояние (25.08.2026): раньше стояли две голые
   даты, и какая из них оплата, было видно только по наведению. Цвет тот же,
   что у остальных «оплачено»/«не оплачено» в приложении. */
.oplaty-when-paid { color: var(--green-text); }
.oplaty-when-debt { color: var(--red-text); }
.oplaty-when-check { width: 11px; height: 11px; flex: none; display: none; }
.oplaty-when-paid .oplaty-when-check { display: block; }
.oplaty-none { color: var(--text-tertiary); }
/* «ожидается» — услуга, которой срок ещё не пришёл. Прочерк остался только у
   «не положена»: раньше один знак стоял на два разных смысла */
.oplaty-wait { color: var(--text-tertiary); }
/* роль без права правки видит состояние точкой, а не тумблером */
.oplaty-flag {
  display: inline-block; width: var(--oplaty-flag-w); height: 10px;
  border-radius: 50%;
  background: var(--red); flex: none;
}
.oplaty-flag-yes { background: var(--green); }
/* Метка «ЧО» (30.08.2026, просьба заказчика): кружок ТОГО ЖЕ диаметра, что у
   оплаты, но неотмеченный — ПУСТОЙ, серым контуром. Красный тут значил бы
   «не сделано, надо сделать» (так он читается у услуг), а ЧО — это список, а
   не долг: не отмечена — просто не отмечена. Отмеченная берёт тот же
   --green, что оплаченная услуга: одинаковый по смыслу зелёный приходит из
   одного токена. Контур — внутренней тенью, а не border: рамка съела бы
   диаметр, а он обязан совпадать с соседними кружками. */
.oplaty-cho { background: transparent; box-shadow: inset 0 0 0 1.5px var(--hairline); }
.oplaty-cho.oplaty-flag-yes { background: var(--green); box-shadow: none; }
/* Финансисту тот же кругляшок, но КНОПКОЙ: клик открывает меню с одним
   пунктом (27.08.2026, вместо бегунка). Размер и цвет приходят из правила
   выше — второй копии величин быть не должно; здесь только то, что нужно
   снять у <button>, и попадание под палец: сам круг 10px, а нажимается
   область 22px — прозрачным отступом, поэтому соседи не двигаются. */
.oplaty-flag-btn {
  border: 0; padding: 0; cursor: pointer; position: relative;
}
.oplaty-flag-btn::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 22px; height: 22px; transform: translate(-50%, -50%);
}
.oplaty-flag-btn:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 3px;
}

/* Пустая таблица остаётся МЕРОЙ для плашек услуг, но глазу не видна
   (01.09.2026, заказчик: «Почему у тебя сдвигаются 4 правых плашки влево,
   когда нет никаких данных в таблице?»). visibility, а не display: none —
   у скрытого display'ем элемента нет геометрии, и мерить было бы нечего.
   Тот же приём, что у невидимого близнеца таблетки периода. */
.oplaty-table-wrap-empty {
  height: 0; visibility: hidden; overflow: hidden;
}
/* Слева у обёртки ЕСТЬ МЕСТО — 8px, — и таблица от этого не двигается:
   тем же margin её возвращают назад. Место нужно «плюсу» зазора: он стои́т
   ЦЕНТРОМ на левом конце серой линии строки (просьба заказчика 08.09.2026:
   «центр кругляшка должен быть на центре окончания серой горизонтальной
   линии слева»), то есть половиной выходит за край таблицы. Обёртка
   прокручивается вбок и обрезает по ВНУТРЕННЕМУ краю padding — внутри
   padding содержимое видно, за ним нет. */
.oplaty-table-wrap { overflow-x: auto; padding-left: 8px; margin-left: -8px; }
/* ШИРИНЫ СТОЛБЦОВ ЗАФИКСИРОВАНЫ (просьба заказчика 27.08.2026): «ширина не
   должна масштабироваться». Раньше таблица шла width: 100% и растягивалась
   с окном — у каждого экрана столбцы выходили своими. Числа сняты замером
   при эталонном окне 1400 (коробка блока 1140): 113.92 / 44.65 / 284.67 /
   174.2×4, округлены до целых и В СУММЕ ДАЮТ РОВНО ШИРИНУ ТАБЛИЦЫ —
   114 + 45 + 285 + 174×4 = 1140. Раскладка fixed: при auto заданные ширины
   были бы лишь пожеланием, и длинное имя растягивало бы свой столбец.
   fixed работает только при заданной ширине самой таблицы — поэтому 1140px
   на .oplaty-table обязателен, width: 100% сюда не возвращать. На окне уже
   1180 обёртка прокручивается (overflow-x выше), и выравнивание плашек
   payments.js снимает — это прежнее правило, оно не менялось. Плашки услуг
   вверху меряют th и потому зафиксированы ВМЕСТЕ со столбцами. */
.oplaty-table {
  /* Размер «+» в зазоре этапов: он же задаёт отступ кругляшка от левого края
     (просьба заказчика 08.09.2026 — отступ равен ширине кругляшка). */
  --etap-plus: 14px;
  width: 1200px; table-layout: fixed;
  border-collapse: collapse; font-size: 14px;
  /* Коробка под сумму — ОДНА на поле ввода финансиста и на текст у остальных
     ролей (правка заказчика 03.09.2026: «не нравится, что зелёные и красные
     кругляшки могут идти не ровно по вертикали»). До этого поле было 92px с
     выключкой вправо, а текст — по содержимому, и каждый лишний разряд
     сдвигал кругляшок на 7.5px: у финансиста вертикаль была, у остальных
     ролей её не было. Одна величина на обе ветки — разъехаться нечему. */
  --oplaty-sum-w: 92px;
  /* Внутренний отступ поля ввода: его рамка 1px плюс собственные 8px. При
     глобальном box-sizing: border-box они съедают ширину ИЗНУТРИ, и текст
     в поле кончается на 92−9=83. Тот же отступ получает и текст у остальных
     ролей — иначе кругляшки совпадали бы, а цифры расходились на эти 9px. */
  --oplaty-sum-inset: 9px;
  /* Хвост ячейки — кругляшок и зазор до него. По нему отступают от правого
     края те, у кого кругляшка нет вовсе (прочерк, «ожидается»), — иначе они
     встали бы на вертикаль кругляшков, а не сумм. */
  --oplaty-flag-w: 10px;
  --oplaty-flag-gap: 8px;
  --oplaty-tail: calc(var(--oplaty-flag-w) + var(--oplaty-flag-gap));
}
.oplaty-table .oplaty-col-who { width: 114px; }
.oplaty-table .oplaty-mark-col { width: 45px; }
.oplaty-table .oplaty-col-name { width: 247px; }
/* Шапки четырёх столбцов услуг равняются по ПРАВОМУ КРАЮ ЧИСЛА, а не по
   краю столбца (просьба заказчика 03.09.2026). Разница не косметическая:
   справа от числа стоят зазор строки и кругляшок, и по краю столбца подпись
   висела бы на 70px правее сумм. Отступ поэтому СЧИТАЕТСЯ из тех же трёх
   величин, что задают раскладку ячейки, — ширины столбца, отступа ячейки и
   коробки суммы; проверка сверяет, что он из них и складывается. */
/* Шапки четырёх столбцов услуг выключены ВПРАВО (просьба заказчика
   03.09.2026). Отступ обычный, как у всех ячеек, — значит подпись встаёт
   ровно над кругляшками, а сами суммы идут своей вертикалью на хвост левее.
   Считать тут больше нечего: правый край один и тот же у шапки, у кругляшка
   и у плашки итога сверху.

   ШИРИНА 180 вместо 174 — его же просьба «сделай шире, чтобы "Открытие р/с"
   в третьей плашке не разбивалось на две строки». Замер: верхней строке той
   плашки нужно 139.8px, а при 174 в ней было 132. Шесть добавленных пикселей
   и ужатые отступы самой плашки (см. `.oplaty-totals-aligned`) дают внутри
   150 — влезает и шестизначный чип долга.

   ШИРЕ БРАТЬ НЕЛЬЗЯ, и это не осторожность: при 192 порог боковой прокрутки
   уезжает с 1247 до 1319, то есть экраны 1280 начинают прокручиваться, — а
   при прокрутке payments.js СНИМАЕТ выравнивание плашек, ровно то, ради чего
   всё это и делалось. При 180 порог 1271, и 1280 по-прежнему целиком. */
.oplaty-table th[data-kind] { width: 180px; text-align: right; }
/* Столбец «ЧО» (30.08.2026) — УЗКИЙ: ровно под заголовок и кружок (правка
   заказчика в тот же день — «как будто можно сделать уже»). Содержимого 24px
   плюс отступы: «ЧО» занимает 20.25px, запас 3.75 — на случай другого
   системного шрифта у заказчика. Отступы здесь свои, 6 вместо общих 12: с
   общими столбец ниже 48 не ужать, а пустота справа от кружков и была тем,
   что он увидел.

   31.08.2026 он дважды просил отодвинуть столбец от «Юрадреса», по 5 мм за
   раз. 5 мм при 96 dpi это 18.9px, взято 19 — целым пикселем, чтобы кромка
   не размывалась. Итого 38: ширина столбца 36 -> 74, и весь прирост ушёл в
   ПРАВЫЙ отступ (6 слева, 44 справа). Коробка содержимого от этого остаётся
   ровно 24px и стоит в 6px от ЛЕВОГО края столбца — поэтому кружок с
   заголовком держатся у названия, а растёт только просвет справа. Замер
   просвета от края кружка до столбца «Юрадрес»: было 13, стало 51.

   ЭТИ 38px ВЗЯТЫ У «ООО / ИП» (285 -> 247), а не ростом таблицы, и вот
   почему. Рост на 19px сдвигает порог прокрутки с ~1287 до ~1306: на окне
   1280 появляется горизонтальная полоса, которой не было. А `payments.js`
   при прокрутке СНИМАЕТ выравнивание плашек — то есть рост ломал бы ровно
   то соответствие плашек столбцам, ради которого всё и делалось. Замер:
   при таблице 1195 окна 1280, 1291 и 1300 прокручиваются, при 1176 — нет.

   Ширины ЧЕТЫРЁХ СТОЛБЦОВ УСЛУГ и таблицы не тронуты: плашки меряют th и
   стоят над своими столбцами как стояли (сдвиги 0/6/6/6). Названия ООО от
   этого обрезаются на 38px раньше — многоточием справа, как заказчик просил
   ещё 27.08.2026 и подтвердил 31.08; цена названа ему числом. */
.oplaty-table .oplaty-col-cho { width: 74px; }
/* Заголовок и кружки — ПО ОДНОЙ ОСИ, по центру столбца (его же правка):
   у th по умолчанию text-align: left, и «ЧО» стояло на 5px правее оси
   кружков — замер до правки 1198.1 против 1193. */
.oplaty-table th.oplaty-col-cho,
.oplaty-table td.oplaty-cho-cell {
  padding-left: 6px; padding-right: 44px; text-align: center;
}
/* Шапка ОБЫЧНЫМ регистром, жирным и ТЕМ ЖЕ кеглем, что текст таблицы
   (просьбы заказчика 27.08.2026; были капители 12px с разрядкой). Свой
   font-size у th не задаётся вовсе — кегль наследуется от .oplaty-table,
   и смена кегля таблицы не разведёт шапку с телом. */
.oplaty-table th {
  text-align: left; padding: 8px 12px; font-weight: 600;
  color: var(--text-tertiary);
  border-bottom: 1px solid var(--separator); white-space: nowrap;
}
.oplaty-table td {
  padding: 9px 12px; border-bottom: 1px solid var(--separator);
  vertical-align: middle;
}
.oplaty-when, .oplaty-sum { white-space: nowrap; font-variant-numeric: tabular-nums; }
.oplaty-sum {
  display: inline-block; width: var(--oplaty-sum-w);
  padding-right: var(--oplaty-sum-inset);
  text-align: right; font-variant-numeric: tabular-nums;
}
/* Прочерк и «ожидается» равняются ВМЕСТЕ с суммами, а не с кругляшками: у
   них кругляшка нет вовсе, поэтому к отступу суммы добавляется хвост —
   кругляшок и зазор до него. */
.oplaty-none, .oplaty-wait {
  display: block; text-align: right;
  padding-right: calc(var(--oplaty-tail) + var(--oplaty-sum-inset));
}

/* --- Этапы в разделе «Оплаты» (07.09.2026) --------------------------------
   Строка-разделитель (вариант Д, выбор заказчика): светлая заливка во всю
   ширину, слева надпись со стрелками, справа итог того этапа, что кончается.
   Своих цветов тут нет — всё из токенов, которыми в разделе уже покрашены
   плашки итогов и красный долг. */
.etap-note {
  font-size: 12.5px; color: var(--text-tertiary); margin: 0 0 8px;
}
/* Строка НАД разделителем отдаёт ему свою нижнюю линию: иначе их было бы
   две подряд — её --separator и наша --hairline, — и сверху полоса выглядела
   вдвое толще, чем снизу (заказчик увидел это сразу, 07.09.2026). */
.oplaty-table tr.etap-pered td { border-bottom: 0; }
/* Зазор между ними своей линии не рисует и так (border: 0 выше). */
.oplaty-table tr.etap-stroka td {
  background: var(--bg-fill-subtle); padding: 8px 12px;
  /* Линии СВЕРХУ И СНИЗУ — ОДНИМ СПОСОБОМ, обе рамкой (просьба заказчика
     07.09.2026, второй заход). Сперва верхнюю рисовали внутренней тенью,
     чтобы не спорить со схлопыванием, — и на глаз она вышла толще нижней:
     тень и рамка округляются по-разному, когда масштаб экрана не 100%
     (1px CSS это 1.25 физического). Спорить теперь не с чем: строка над
     разделителем свою нижнюю линию отдала (.etap-pered выше), поэтому
     обычный border-top выигрывает схлопывание и округляется ровно так же,
     как border-bottom. */
  /* ОБЕ ЛИНИИ РИСУЕТ САМА ЯЧЕЙКА, фоном, а не рамками (07.09.2026, третий
     заход по этой линии). Рамкой сверху и рамкой снизу они принадлежат
     РАЗНЫМ краям разных строк, и при дробном масштабе экрана — а у заказчика
     он не 100% — округляются врозь: замер дал сверху 0 px и снизу 1 px при
     125%, и наоборот при 150%. Два градиента внутри одного элемента лежат в
     одной системе округления, поэтому выходят одинаковыми на любом масштабе. */
  border-top: 0; border-bottom: 0;
  background-image:
    linear-gradient(var(--hairline), var(--hairline)),
    linear-gradient(var(--hairline), var(--hairline));
  background-size: 100% 1px, 100% 1px;
  background-position: top left, bottom left;
  background-repeat: no-repeat;
}
.etap-in { display: flex; align-items: baseline; justify-content: space-between; }
/* Подпись ПРИЛИПАЕТ к левому краю видимой области: таблица 1200px, порог
   боковой прокрутки 1271, и на экране уже 1280 столбец «Поставщик» уезжает
   первым — вместе с ним ушла бы и надпись (замер 07.09.2026: при окне 1100
   подпись оказывалась на x = −131, от полосы оставался один итог справа). */
/* Кегль в строке разделителя СВОЙ НЕ ЗАДАЁТСЯ вовсе (просьба заказчика
   07.09.2026: «сделай шрифт в этой строке такой же, как в столбце
   „Поставщик"»). Он наследуется от .oplaty-table — ровно тот же путь, каким
   его берут ячейки таблицы, — и при смене кегля таблицы не разойдётся. */
.etap-name {
  font-weight: 600; color: var(--text-secondary);
  position: sticky; left: 12px;
}
.etap-itog { color: var(--text-tertiary); white-space: nowrap; }
.etap-dolg { color: var(--red-text); }

/* Зазор между строками: СВОЕЙ ВЫСОТЫ У НЕГО НЕТ. Мишень лежит поверх стыка
   строк абсолютом, поэтому таблица не растёт ни на пиксель, а наводиться
   есть куда. Видна только админу — разметка её остальным и не рисует. */
.oplaty-table tr.etap-zazor td {
  padding: 0; border: 0; height: 0; position: relative;
}
/* У НИЖНЕГО зазора высота настоящая: 12px пустой полоски под последней
   строкой. Это не украшение, а место для мишени. Нулевая высота хороша ровно
   там, где под стыком есть следующая строка: мишень ложится поверх неё и
   таблица не растёт. Под последней строкой лежать не на чем, и мишень с «+» и
   подписью свисала за нижний край таблицы — а обёртка прокручивается вбок,
   и по правилам CSS её overflow-y тоже становится `auto`: вылет включал
   вертикальную полосу прокрутки, та съедала 15px, и следом появлялась
   горизонтальная (замер 08.09.2026: 1200 -> 1185 при таблице в 1200).
   14px берутся с запасом от самой глубокой части — подписи при наведении:
   она 20.7px и лежит по центру стыка, то есть уходит вниз на 10.35px
   (замер 08.09.2026, уже с общим шрифтом кнопки). */
.oplaty-table tr.etap-zazor-niz td { height: 14px; }
/* ВЕРХНЯЯ мишень СВОИХ ПРАВИЛ НЕ ИМЕЕТ — она такая же, как все прочие:
   лежит ПОВЕРХ линии, серединой на ней, и своей высоты строке не даёт.
   Сперва ей дали высоту 14px, чтобы «+» не заходил на шапку таблицы, — и
   заказчик сразу это увидел (08.09.2026): «в самом верху кругляшок ниже
   горизонтальной линии, а должен быть на нём; из-за этого самая верхняя
   строка по высоте больше, чем другие». Так и было: строка добавляла 14px,
   а «+» опускался на 7px ниже линии. Зеркальность с нижним зазором тут
   мнимая: снизу под стыком НЕТ НИЧЕГО и мишень свисала за край таблицы,
   а сверху над стыком есть шапка или сама черта — заходить на них можно. */
.etap-vstavka {
  position: absolute; left: 0; right: 0; top: -6px; height: 12px;
  display: flex; align-items: center;
  /* `font: inherit` — как у всех кнопок раздела (.oplaty-who-menu,
     .slot-menu-item): без него браузер рисует «+» и подпись СВОИМ шрифтом
     (замер 08.09.2026: Arial 11px вместо шрифта таблицы). */
  font: inherit;
  background: none; border: 0; padding: 0; cursor: pointer;
}
/* Линия и подпись — при наведении. А ЗНАК «+» ВИДЕН ВСЕГДА: пока его не
   было, мишень была невидимой, и заказчик дважды не смог её найти
   (07.09.2026). Бледный и маленький — двадцать таких плюсов в столбце не
   шумят, но говорят, что между строками что-то можно. */
/* СПРАВА линия доходит до края таблицы (просьба заказчика 08.09.2026:
   «синюю разделительную линию доводи справа до конца серой линии»). Слева
   она начинается под «+», который её и закрывает своим кругляшком. */
.etap-vstavka::before {
  content: ""; position: absolute; left: 0; right: 0; top: 50%;
  height: 2px; margin-top: -1px; background: var(--accent); border-radius: 2px;
  opacity: 0; transition: opacity .12s;
}
.etap-vstavka:hover::before,
.etap-vstavka:focus-visible::before { opacity: 1; }
/* ОТСТУП «+» ОТ ЛЕВОЙ ЛИНИИ РАВЕН ЕГО ШИРИНЕ (просьба заказчика
   08.09.2026, дословно: «он должен отступать от левой вертикальной линии на
   ширину этого же кругляшка»). Поэтому и отступ, и размер берутся из ОДНОЙ
   величины: числами это правило разъехалось бы при первой правке размера
   (было 12 при ширине 14). */
.etap-plus {
  /* ЦЕНТРОМ на левом конце серой линии: отступ в МИНУС половину своей
     ширины (замер до правки: центр стоял на 21px правее края таблицы). */
  position: relative; margin-left: calc(var(--etap-plus) / -2);
  width: var(--etap-plus); height: var(--etap-plus); border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-fill); color: var(--text-tertiary);
  font-size: 11px; line-height: 1; font-weight: 600;
  transition: background .12s, color .12s;
}
.etap-vstavka:hover .etap-plus,
.etap-vstavka:focus-visible .etap-plus { background: var(--accent); color: #fff; }
.etap-vstavka-znak {
  position: relative; margin-left: 6px;
  background: var(--accent); color: #fff; font-size: 11.5px;
  padding: 2px 10px; border-radius: 980px; white-space: nowrap;
  opacity: 0; transition: opacity .12s;
}
.etap-vstavka:hover .etap-vstavka-znak,
.etap-vstavka:focus-visible .etap-vstavka-znak { opacity: 1; }

/* Фильтр поставщика — кнопкой прямо в шапке столбца (вариант В). Вид берётся
   у самой шапки: кегль и цвет наследуются, своих чисел нет. */
.oplaty-who-menu {
  font: inherit; font-weight: inherit; color: inherit;
  background: none; border: 0; padding: 0; cursor: pointer;
  display: inline-flex; align-items: center; gap: 5px; max-width: 100%;
}
.oplaty-who-menu-label { overflow: hidden; text-overflow: ellipsis; }
.oplaty-who-menu svg { width: 9px; height: 6px; flex: none; }
.oplaty-row-debt td { background: var(--red-tint); }
