/* ============================================================================
   doc-tool — дизайн-система «Тихий инструмент»
   Данные — герой, интерфейс — рама. Цвет только там, где несёт смысл:
   действие, статус, ошибка, компания-исполнитель.

   ШКАЛЫ. Из них не выходить — «пляшет» получается именно отсюда.
     размеры шрифта: 30 · 24 · 20 · 16 · 14 · 13 · 12 (полутоновых не бывает)
     отступы: кратны 4 (--sp-1…--sp-8)
     радиусы: 6 контролы · 10 карточки и панели · 999 чипы (четвёртого нет,
              «50%» — это 999)
     высоты контролов: 24 метка · 32 малый · 36 обычный · 44 палец
     тени: три токена; «0 0 0 Npx» — не тень, а обводка, считается отдельно
     точки останова: 768 · 1080 · 1400

   КНОПКА ПО РОЛИ ДЕЙСТВИЯ. Одна залитая на экран — не больше.
     главное действие экрана или панели   .btn        (в мастере .btn.lg)
     второстепенное в том же ряду         .btn.secondary
     действие уровня панели, в её шапке   .btn.secondary.sm
     действие внутри строки таблицы       .btn.ghost.sm / .btn.ghost.icon.sm
     разрушительное (удалить, очистить,
     начать заново, выдать новый пароль)  .btn.danger (+ confirmDialog)
     переключение показанного ниже        .filter-chip
     закрыть панель или окно              .btn.ghost.icon.lg с aria-label

   ЦВЕТ СТАТУСА. Один смысл — один цвет во всём портале.
     зелёный ok      сделано, задан, активен
     жёлтый warn     ждёт, не начат, не задан
     синий info      в работе, служебная пометка, «отличается от заготовки»
     красный danger  ошибка, просрочено, отказ
     серый mute      нейтральное, снято, аннулировано
     Словарь один и живёт в base.html (`STATUS_TONE` + `statusTone`). Было три
     независимых, и «Отклонён» красился в danger в «Документах», а тот же по
     смыслу `rejected` в «Запросах» — в mute. Своя таблица статусов допустима
     только под другим именем (`WORK_TONE` в passport.html): две константы
     `STATUS_TONE` в одной области видимости молча убивают весь <script>.

   ЧИСЛО, КОТОРОМУ НЕЛЬЗЯ ВЕРИТЬ. .sum-bad — красное с волнистым подчёркиванием.
     Ставится суммой выше SUM_SANE_LIMIT (10^8 ₽ на документ). Не прячем:
     молча выброшенная строка хуже видимой неправильной. Один такой документ
     (48 006 433 400 ₽ вместо 76 600) двое суток стоял на столе руководителя
     обычным весом и не был замечен.

   ФОКУС У ТОГО, ЧТО РАБОТАЕТ КНОПКОЙ. Всё, что делается мышью, делается и
     с клавиатуры, а значит показывает, где сейчас фокус:
     .dropzone:focus-visible            зона загрузки файлов
     table.data tr.clickable:focus-visible  строка, открывающая карточку
     Обводится сама строка, а не ячейка: иначе непонятно, что откроется.

   ВЫБОР ПЕРИОДА. Один на портал, строит `wireDateRange` из base.html:
     .pp-filters       ряд «готовые кнопки + свой период»
     .filters          контейнер кнопок [data-period]
     .date-range-f     блок «Свой период» — две даты и «Показать»
     Разметку не копировать в шаблон: четыре раздела спрашивают одно и то же,
     и копии разойдутся, как разошлись словари статусов.

   ТАБЛИЦА. Обёртка выбирается по тому, помещается ли таблица:
     .table-wrap         в рамке, со своей прокруткой — вложенные и короткие
     .table-wrap.flat    помещается в экран: без рамки, шапка липнет к странице
     .table-wrap.capped  не помещается никогда: своя прокрутка в коробке
                         фиксированной высоты — полоса всегда под рукой,
                         шапка липнет к коробке
     tfoot tr.expander   «Показать ещё N» / «Свернуть» — кнопка в подвале
                         таблицы (`wireExpand`), не итог: без заливки и черты

   ПОРЯДОК СТРОК — по смыслу, а не по порядку источника. Таблица, пришедшая
     из чужого файла, приходит в порядке «кого раньше завели»; читать её
     глазами в поисках отстающих — работа, которую должен делать портал.
     Сортирует сервер, а не шаблон (один порядок всем потребителям), и порядок
     называется словами под таблицей — иначе он выглядит случайным.
     Сейчас так в «Продажах»: менеджеры по убыванию выполнения плана.
   ========================================================================= */

:root {
  /* нейтральные */
  --n0:#FFFFFF;
  --n25:#F7F8FA;
  --n50:#F1F3F6;
  --n100:#E9ECF1;
  --n200:#E2E5EA;
  --n300:#C6CCD6;   /* границы полей: 3.1:1 к белому */
  --n500:#5C6675;   /* вторичный текст: 6.2:1 */
  --n700:#333D4B;
  --n900:#101828;

  /* бренд */
  --brand:#00707D;        /* белый на нём — 4.6:1 */
  --brand-700:#005C67;
  --brand-50:#E6F4F6;
  --navy:#14305E;

  /* смысловые */
  --ok:#0F7B45;      --ok-bg:#E7F6EE;   --ok-br:#B7E2C9;
  --warn:#8A5200;    --warn-bg:#FFF6E5; --warn-br:#F0D5A3;
  --danger:#B3261E;  --danger-bg:#FDECEA; --danger-br:#F3B9B4;
  --info:#175CD3;    --info-bg:#EAF2FE; --info-br:#B6D2F7;

  /* компании-исполнители */
  --co-aso:#00707D;
  --co-cdo:#14305E;
  /* Оранжевый АПрофПК на своей же десятипроцентной подложке давал 4,48 при
     норме 4,5 — единственное нарушение контраста во всём портале. Прошлая
     проверка его не увидела: считала фон непрозрачным и получала ерунду.
     Потемнение на один шаг даёт 6,2 и не меняет цвет на другой. */
  --co-apk:#9A3412;

  --shadow-sm:0 1px 2px rgba(16,24,40,.06);
  --shadow-md:0 4px 12px rgba(16,24,40,.08);
  --shadow-lg:0 12px 32px rgba(16,24,40,.14);

  --r-sm:6px;    /* контролы */
  --r-md:10px;   /* карточки, панели */
  --r-full:999px;

  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px;
  --sp-5:24px; --sp-6:32px; --sp-7:48px; --sp-8:64px;

  --topbar-h:56px;
  --side-w:216px;
  --font:"Inter", -apple-system, "Segoe UI Variable", "Segoe UI", Roboto, sans-serif;
  --mono:"JetBrains Mono", "Cascadia Mono", Consolas, monospace;
}

* { margin:0; padding:0; box-sizing:border-box; min-width:0; }

/* Микроотклики. Интерфейс был полностью статичным: строка меняла статус,
   панель открывалась, число пересчитывалось — и всё это происходило мгновенно,
   так что заметить смену было нечем. Длительности короткие: движение здесь
   отвечает на вопрос «что сейчас произошло», а не украшает.
   `prefers-reduced-motion` ниже отключает всё разом. */
.btn, .nav-item, .filter-chip, .chip-select, table.data tbody tr, .card, .metric {
  transition: background-color .09s linear, border-color .09s linear,
              color .09s linear, box-shadow .12s ease-out;
}
.btn:active { transform:translateY(1px); }
.drawer { transition: transform .18s cubic-bezier(.2,.8,.2,1); }
/* строка, которая только что изменилась, подсвечивается и гаснет — видно,
   какая именно из полусотни */
@keyframes row-changed {
  from { background:var(--brand-50); }
  to   { background:transparent; }
}
tr.just-changed td { animation: row-changed .5s ease-out; }

html { -webkit-text-size-adjust:100%; }

body {
  font-family:var(--font);
  font-size:14px; line-height:1.45;
  background:var(--n25); color:var(--n900);
  -webkit-font-smoothing:antialiased;
}

a { color:var(--brand-700); text-underline-offset:2px; }
a:hover { color:var(--brand); }

:focus-visible {
  outline:2px solid #0B7285; outline-offset:2px; border-radius:var(--r-sm);
}

/* ---------------------------------------------------------------- типографика */
h1,h2,h3,h4 { line-height:1.2; font-weight:600; color:var(--n900); }
/* Заголовок экрана. Обычно это h1, но на курсе h1 — название курса в боковой
   колонке, а заголовком экрана работает название модуля (h2): страница — курс,
   модуль — часть внутри него. Размер задаёт класс, а не тег. */
.page { font-size:24px; letter-spacing:-.015em; }
h2 { font-size:20px; }
h3, .panel-title { font-size:16px; }
p.sub { color:var(--n500); font-size:14px; margin-top:var(--sp-1); }
.muted { color:var(--n500); }
.overline {
  font-size:12px; font-weight:600; letter-spacing:.06em; text-transform:uppercase;
  color:var(--n500);
}
.num { font-variant-numeric:tabular-nums; }
.mono { font-family:var(--mono); font-variant-numeric:tabular-nums; }

/* ---------------------------------------------------------------- каркас */
.topbar {
  position:sticky; top:0; z-index:40;
  height:var(--topbar-h); background:var(--n0); border-bottom:1px solid var(--n200);
  display:flex; align-items:center; gap:var(--sp-5); padding:0 var(--sp-5);
}
.topbar .brand { display:flex; align-items:center; gap:var(--sp-2); text-decoration:none; flex:none; }
.topbar .brand img { height:28px; width:auto; }
.topbar .brand span { font-weight:600; color:var(--navy); font-size:14px; white-space:nowrap; }
/* ---------------------------------------------------------------- навигация

   Сбоку, а не сверху. Одиннадцать пунктов в строку требуют 1111 px только на
   текст, и при 1440 px названия наезжали друг на друга: `nowrap` не давал
   переносить, `flex-shrink` сжимал ссылку, `overflow:visible` выпускал текст
   за её границы. Многоточия при этом не появлялось — текст просто ложился на
   соседний пункт. Сбоку ширина перестаёт быть ограничением: следующий раздел
   добавляется вниз. */
.sidenav {
  position:fixed; left:0; top:var(--topbar-h); bottom:0; width:var(--side-w);
  z-index:30; background:var(--n0); border-right:1px solid var(--n200);
  padding:var(--sp-3) var(--sp-2) var(--sp-5);
  display:flex; flex-direction:column; gap:var(--sp-1); overflow-y:auto;
}
.nav-group { margin-top:var(--sp-4); display:flex; flex-direction:column; gap:var(--sp-1); }
.nav-title {
  font-size:12px; font-weight:600; letter-spacing:.08em; text-transform:uppercase;
  color:var(--n500); padding:0 var(--sp-3) var(--sp-1);
}
.nav-item {
  display:flex; align-items:center; gap:var(--sp-2); min-height:36px;
  padding:var(--sp-2) var(--sp-3); border-radius:var(--r-sm);
  color:var(--n700); text-decoration:none; font-weight:500; font-size:14px;
}
.nav-item .t { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.nav-item:hover { background:var(--n50); color:var(--n900); }
.nav-item.active { background:var(--brand-50); color:var(--brand-700); font-weight:600; }
.nav-badge {
  margin-left:auto; flex:none; min-width:20px; height:20px; padding:0 6px;
  border-radius:var(--r-full); background:var(--brand); color:var(--n0);
  font-size:12px; font-weight:600; display:flex; align-items:center; justify-content:center;
  font-variant-numeric:tabular-nums;
}
.nav-item.active .nav-badge { background:var(--brand-700); }
/* `display:flex` из правила выше сильнее браузерного `[hidden]{display:none}`,
   и счётчик без числа оставался на экране зелёным кружком без объяснения.
   Атрибут `hidden` должен прятать — возвращаем ему силу явно. */
.nav-badge[hidden] { display:none; }

/* Ссылка для клавиатуры: не мешает мыши, но избавляет от прохода по всему
   меню на каждой странице. */
.skip {
  position:absolute; left:var(--sp-3); top:-60px; z-index:60;
  background:var(--n0); color:var(--brand-700); font-weight:600;
  padding:var(--sp-2) var(--sp-3); border-radius:var(--r-sm);
  border:1px solid var(--brand); box-shadow:var(--shadow-md);
}
.skip:focus { top:var(--sp-2); }
main:focus { outline:none; }

/* Контент сдвигается вправо на ширину навигации. Сдвигаем body, а не .wrap:
   .wrap центрируется внутри оставшегося места и не знает про навигацию.
   Только когда навигация есть: на странице входа её нет, и отступ оставлял
   слева пустую белую полосу в 260 px, из-за которой карточка входа стояла не
   по центру экрана, а правее. */
body.has-nav { padding-left:var(--side-w); }
/* Правый блок сжимается, а не выпирает: имена бывают длинные («Специалист по
   сопровождению клиентов»), и при `flex:none` шапка растягивала страницу шире
   экрана — вбок ехали ВСЕ разделы, включая те, где ничего не меняли.
   Ссылка «выйти» не сжимается никогда, обрезается только имя. */
.topbar .right { display:flex; align-items:center; gap:var(--sp-3); flex:0 1 auto; min-width:0; }
.topbar .who { font-size:13px; color:var(--n500); display:flex; align-items:center;
  gap:var(--sp-2); white-space:nowrap; min-width:0; }
.topbar .who > span { display:flex; align-items:center; gap:var(--sp-2); min-width:0; }
.topbar .who b { color:var(--n900); font-weight:600;
  overflow:hidden; text-overflow:ellipsis; }
.topbar .who a {
  flex:none; display:inline-flex; align-items:center; min-height:24px;
  padding:0 var(--sp-2); border-radius:var(--r-sm);
}
.topbar .who a:hover { background:var(--n50); }
.burger { display:none; }

.wrap { max-width:1280px; margin:0 auto; padding:var(--sp-5); }
.wrap.wide { max-width:1600px; }
.wrap.narrow { max-width:760px; }
/* Экраны без навигации — вход и «раздел не открыт»: одна колонка по центру. */
.wrap.auth { max-width:440px; padding-top:var(--sp-8); }

.page-head {
  display:flex; align-items:flex-start; justify-content:space-between;
  gap:var(--sp-4); margin-bottom:var(--sp-5); flex-wrap:wrap;
}
.page-head .actions { display:flex; gap:var(--sp-2); flex-wrap:wrap; }

.section { margin-bottom:var(--sp-6); }
/* Надзаголовок отбивается сам, а не отступами в разметке: вне .section он
   прилипал к содержимому, а сверху имел то 0, то 24, то 28, то 32. */
.overline { display:block; margin-bottom:var(--sp-3); }
.overline + * { margin-top:0; }
.section > .overline { margin-bottom:var(--sp-3); }
/* Надзаголовок, начинающий новый блок посреди страницы. */
.overline.apart { margin-top:var(--sp-6); }

/* ---------------------------------------------------------------- панели */
.panel {
  background:var(--n0); border:1px solid var(--n200); border-radius:var(--r-md);
  box-shadow:var(--shadow-sm); margin-bottom:var(--sp-4);
}
.panel > header {
  display:flex; align-items:center; gap:var(--sp-3);
  padding:var(--sp-4) var(--sp-5); border-bottom:1px solid var(--n100);
}
.panel > header h2, .panel > header h3 { font-size:16px; flex:1; }
/* Одна шапка на все белые блоки: у предпросмотра и у раскрывающейся строки
   были свои 12/16, и рядом это читалось как разные компоненты. */
.preview-pane > header, details.hour > summary { padding:var(--sp-4) var(--sp-5); }
.panel > header .hint { font-size:13px; color:var(--n500); font-weight:400; }
.panel > .body { padding:var(--sp-5); }
.panel > .body.tight { padding:var(--sp-4); }
.panel.flat { box-shadow:none; }

.cards { display:grid; grid-template-columns:repeat(auto-fit,minmax(240px,1fr)); gap:var(--sp-4); }
.card {
  display:block; background:var(--n0); border:1px solid var(--n200);
  border-radius:var(--r-md); padding:var(--sp-5); text-decoration:none; color:inherit;
  box-shadow:var(--shadow-sm);
}
a.card:hover { border-color:var(--brand); box-shadow:var(--shadow-md); }
/* Уровень заголовка карточки зависит от страницы: на рабочем столе она лежит
   внутри раздела (h2) и потому h3, в «Обучении» разделов нет — карточка сама
   верхний уровень. Вид один и тот же: размер задаёт карточка, а не тег. */
.card h2, .card h3 { font-size:16px; margin-bottom:var(--sp-2); }
.card p { color:var(--n500); font-size:13px; }
.card .ico {
  width:36px; height:36px; border-radius:var(--r-sm); background:var(--brand-50);
  color:var(--brand-700); display:flex; align-items:center; justify-content:center;
  margin-bottom:var(--sp-3);
}
.card .ico svg { width:20px; height:20px; }

/* ---------------------------------------------------------------- кнопки */
.btn {
  display:inline-flex; align-items:center; justify-content:center; gap:var(--sp-2);
  height:36px; padding:0 var(--sp-4); border-radius:var(--r-sm);
  border:1px solid transparent; background:var(--brand); color:#fff;
  font:inherit; font-weight:600; font-size:14px; cursor:pointer;
  text-decoration:none; white-space:nowrap;
}
.btn:hover { background:var(--brand-700); color:#fff; }
.btn:active { transform:translateY(.5px); }
.btn svg { width:16px; height:16px; flex:none; }
.btn.secondary { background:var(--n0); color:var(--brand-700); border-color:var(--n300); }
.btn.secondary:hover { background:var(--n50); color:var(--brand-700); border-color:var(--brand); }
.btn.ghost { background:transparent; color:var(--n500); }
.btn.ghost:hover { background:var(--n50); color:var(--n900); }
.btn.danger { background:transparent; color:var(--danger); border-color:var(--danger-br); }
.btn.danger:hover { background:var(--danger-bg); color:var(--danger); }
.btn.lg { height:44px; padding:0 var(--sp-5); font-size:14px; }
/* Высоты контролов: 24 (метка) · 32 (малый) · 36 (обычный) · 44 (палец).
   Было семь разных, и в одном ряду таблицы стояли кнопки 30 и 36 — ряд
   выходил ступенькой. */
.btn.sm { height:32px; padding:0 var(--sp-3); font-size:13px; }
/* Значковая кнопка — квадрат по высоте своего размера. Раньше задавалась
   только ширина (30), высота приходила от базовой кнопки (36): все значки
   портала были прямоугольниками 30×36 и не совпадали с соседями по строке. */
.btn.icon { width:36px; padding:0; }
.btn.icon.sm { width:32px; height:32px; }
.btn.icon.lg { width:36px; height:36px; }
.btn:disabled, .btn[aria-disabled="true"] {
  background:var(--n100); color:var(--n500); border-color:var(--n200); cursor:default;
}
.btn.loading { position:relative; color:transparent !important; pointer-events:none; }
.btn.loading::after {
  content:""; position:absolute; width:16px; height:16px; border-radius:var(--r-full);
  border:2px solid rgba(255,255,255,.45); border-top-color:#fff; animation:spin .7s linear infinite;
}
.btn.secondary.loading::after { border-color:rgba(0,112,125,.25); border-top-color:var(--brand); }
.btn-row { display:flex; gap:var(--sp-3); flex-wrap:wrap; align-items:center; }

@keyframes spin { to { transform:rotate(360deg); } }

/* ---------------------------------------------------------------- поля */
.grid { display:grid; gap:var(--sp-3) var(--sp-4); align-items:start; }
.grid.c2 { grid-template-columns:repeat(2,minmax(0,1fr)); }
.grid.c3 { grid-template-columns:repeat(3,minmax(0,1fr)); }
.grid.c4 { grid-template-columns:repeat(4,minmax(0,1fr)); }

label.f, .f { display:flex; flex-direction:column; gap:var(--sp-1); font-size:13px; }
label.f > span, .f > .label {
  color:var(--n500); font-weight:500; font-size:13px;
  display:flex; align-items:center; gap:var(--sp-1);
}
label.f .hint, .f .hint { color:var(--n500); font-weight:400; opacity:.85; }
.f .req::after { content:"*"; color:var(--danger); margin-left:2px; }

/* `input:not([type])` — не педантизм: у поля без атрибута тип всё равно text,
   но селектор по атрибуту его не ловит, и оно остаётся с браузерной рамкой
   (серая вдавленная, углы прямые) рядом с нашими скруглёнными. Именно так
   «Сотрудники» и получили половину полей квадратными. */
input:not([type]), input[type=text], input[type=number], input[type=password],
input[type=date], input[type=search], input[type=tel], input[type=email],
select, textarea {
  width:100%; height:36px; padding:0 var(--sp-3);
  border:1px solid var(--n300); border-radius:var(--r-sm);
  background:var(--n0); color:var(--n900); font:inherit; font-size:14px;
}
textarea { height:auto; padding:var(--sp-2) var(--sp-3); line-height:1.45; }
select { padding-right:var(--sp-2); }
input::placeholder { color:#8A94A2; }
input:hover, select:hover, textarea:hover { border-color:#AEB7C2; }
input:focus, select:focus, textarea:focus {
  outline:2px solid #0B7285; outline-offset:1px; border-color:var(--brand);
}
input:disabled, select:disabled, textarea:disabled,
input[readonly] { background:var(--n50); color:var(--n500); }
input.bad, select.bad, textarea.bad { border-color:var(--danger); background:var(--danger-bg); }
.field-error { color:var(--danger); font-size:12px; margin-top:2px; display:block; }
.field-hint { color:var(--n500); font-size:12px; }

/* поле с суффиксом: ₽, %, ч */
.with-suffix { position:relative; }
.with-suffix input { padding-right:26px; text-align:right; }
.with-suffix::after {
  content:attr(data-suffix); position:absolute; right:var(--sp-3); top:50%;
  transform:translateY(-50%); color:var(--n500); font-size:13px; pointer-events:none;
}

/* сегментированный переключатель — выбор компании */
.segmented { display:flex; gap:var(--sp-2); flex-wrap:wrap; }
.segmented label {
  flex:1 1 180px; display:flex; align-items:center; gap:var(--sp-3); cursor:pointer;
  border:1px solid var(--n300); border-radius:var(--r-md); padding:var(--sp-3) var(--sp-4);
  background:var(--n0);
}
.segmented label:hover { border-color:var(--brand); }
.segmented input { position:absolute; opacity:0; width:0; height:0; }
.segmented .dot {
  width:12px; height:12px; border-radius:var(--r-full); background:var(--n300); flex:none;
}
.segmented .t { display:flex; flex-direction:column; gap:1px; min-width:0; }
.segmented .t b { font-size:14px; font-weight:600; }
.segmented .t span { font-size:12px; color:var(--n500); }
.segmented input:checked + .dot { background:var(--co); box-shadow:0 0 0 3px var(--brand-50); }
.segmented label:has(input:checked) { border-color:var(--co); background:var(--n0); box-shadow:inset 0 0 0 1px var(--co); }
.segmented label[data-co="aso"] { --co:var(--co-aso); }
.segmented label[data-co="cdo"] { --co:var(--co-cdo); }
.segmented label[data-co="apk"] { --co:var(--co-apk); }

/* переключатель-галочка в рамке */
.toggle {
  display:inline-flex; align-items:center; gap:var(--sp-2); cursor:pointer;
  border:1px solid var(--n300); border-radius:var(--r-sm);
  padding:var(--sp-2) var(--sp-3); background:var(--n0); font-size:13px; font-weight:500;
}
.toggle:hover { border-color:var(--brand); }
.toggle:has(input:checked) { border-color:var(--brand); background:var(--brand-50); color:var(--brand-700); }
.toggle input { accent-color:var(--brand); }

/* Таблица проверки переноса: 13 колонок, 2333 px при экране 1375 — вправо
   уезжает почти тысяча. Человек вычитывает СНИЛС и даты рождения, и при
   скролле переставал понимать, к какой строке договора относится то, что он
   правит. Первые две колонки — номер и программа с ценой — остаются на месте:
   это и есть ответ на вопрос «чья это строка».
   Ширина `.rn` фиксирована, потому что от неё считается сдвиг второй колонки. */
#rowsTable .rn { width:44px; }
#rowsTable thead th:nth-child(-n+2),
#rowsTable tbody td:nth-child(-n+2) {
  position:sticky; background:var(--n0); z-index:2;
}
#rowsTable thead th:nth-child(-n+2) { z-index:3; background:var(--n50); }
#rowsTable thead th:nth-child(1), #rowsTable tbody td:nth-child(1) { left:0; }
#rowsTable thead th:nth-child(2), #rowsTable tbody td:nth-child(2) { left:44px; }
/* граница отделяет закреплённое от прокручиваемого — иначе при скролле текст
   наезжает на закреплённые ячейки без видимого шва */
#rowsTable thead th:nth-child(2), #rowsTable tbody td:nth-child(2) {
  border-right:1px solid var(--n200);
}
#rowsTable tbody tr:hover td:nth-child(-n+2) { background:var(--n50); }

/* Блок с устаревшими данными: приглушаем числа, чтобы они не читались как
   сегодняшний результат, и оставляем читаемым объяснение рядом. */
.section.stale .metric .v { color:var(--n500); }
.section.stale .panel { border-color:var(--warn-br); }

/* Подробные колонки таблицы: по умолчанию скрыты, открываются кнопкой.
   Двенадцать колонок «Паспорта дня» не помещались на ноутбуке, и первыми за
   край уезжали дата и интервал — то есть контекст, за какой период числа. */
table.data.cols-main .extra, table.cols-main .extra { display:none !important; }

/* Ссылки внутри таблиц и сводок — тоже цели нажатия. «PDF» размером 23x17
   не берётся ни пальцем, ни дрожащей рукой; минимум по WCAG 2.5.8 — 24x24. */
table.data a, .totals a, .file-links a {
  display:inline-flex; align-items:center; min-height:24px;
  padding:0 var(--sp-1); border-radius:var(--r-sm);
}
table.data a:hover, .totals a:hover { background:var(--n50); }

/* Галочки и переключатели. Системные приходят размером 13×13 — это вдвое
   меньше минимума WCAG 2.5.8 (24×24) и совсем мало для пальца: в «Сотрудниках»
   таких целей было 23 штуки в одном столбце. */
input[type=checkbox], input[type=radio] {
  width:24px; height:24px; accent-color:var(--brand); cursor:pointer; flex:none;
}
/* Поля, которым размер не задали явно, берут системный 13.3333px — он не из
   нашей шкалы и заметно мельче соседнего текста. */
input, select, textarea, button { font-family:inherit; font-size:14px; }
/* в плотных таблицах галочка стоит одна в ячейке — даём ей всю ячейку под
   нажатие, чтобы не целиться в квадратик */
table.data td > input[type=checkbox]:only-child { margin:4px 0; }

/* заголовок сортируемой колонки: стрелка появляется при наведении, чтобы
   шапка не пестрела значками, но подсказывала, что по ней можно кликнуть */
.sortable-th { cursor:pointer; user-select:none; position:relative; padding-right:20px; }
.sortable-th::after {
  content:"↕"; position:absolute; right:6px; opacity:0; font-weight:400;
  font-size:12px; color:var(--n500);
}
.sortable-th:hover { color:var(--n900); }
.sortable-th:hover::after { opacity:.6; }
.sortable-th[aria-sort="ascending"]::after { content:"↑"; opacity:1; color:var(--brand); }
.sortable-th[aria-sort="descending"]::after { content:"↓"; opacity:1; color:var(--brand); }

/* набор объектов в карточке доступа: какие курсы, какие компании, какие
   разделы настроек. Галочки переносятся по ширине — названия курсов длинные,
   и в одну строку они выдавили бы колонку со значением по умолчанию. */
.picks { display:flex; flex-wrap:wrap; gap:var(--sp-2) var(--sp-4); margin-top:var(--sp-2); }
/* Столбиком — когда у пунктов не ярлык, а фраза. В ряд такие переносятся
   посреди подписи, и набор читается сплошняком: «Свидетельство ✓ Свидетельство:
   только текст, для печати поверх бланка ✓ Удостоверение». Ряд остаётся для
   коротких ярлыков, как в наборах прав. */
.picks.col { flex-direction:column; gap:var(--sp-2); }
.picks.col .pick { align-items:flex-start; }
.picks.col .pick input { margin-top:2px; }
.picks.col .pick .field-hint { display:block; }
.pick {
  display:inline-flex; align-items:center; gap:var(--sp-2);
  font-size:13px; font-weight:400; cursor:pointer;
}
.pick input { accent-color:var(--brand); margin:0; }
.pick input:disabled { cursor:default; }
/* выключенная галочка внутри «все, включая будущие» остаётся отмеченной —
   бледнеет, чтобы было видно: выбор сделан не здесь */
.pick input:disabled + span { opacity:.5; }

/* ---------------------------------------------------------------- комбобокс */
.combo { position:relative; }
.combo-list {
  position:absolute; left:0; right:0; top:calc(100% + 4px); z-index:30;
  background:var(--n0); border:1px solid var(--n200); border-radius:var(--r-md);
  box-shadow:var(--shadow-lg); max-height:280px; overflow-y:auto; display:none;
  padding:var(--sp-1);
}
.combo-list.open { display:block; }
.combo-list .opt {
  display:flex; align-items:baseline; gap:var(--sp-2); padding:var(--sp-2) var(--sp-3);
  border-radius:var(--r-sm); cursor:pointer; font-size:13px;
}
.combo-list .opt .name { flex:1; min-width:0; }
.combo-list .opt .meta { color:var(--n500); font-size:12px; white-space:nowrap; }
.combo-list .opt mark { background:#FFF1B8; color:inherit; border-radius:2px; }
.combo-list .opt.active, .combo-list .opt:hover { background:var(--brand-50); }
.combo-list .empty, .combo-list .loading {
  padding:var(--sp-3); color:var(--n500); font-size:13px;
}

/* ---------------------------------------------------------------- чипы, бейджи */
.chip:empty { display:none; }
.chip {
  display:inline-flex; align-items:center; gap:var(--sp-1);
  height:24px; padding:0 var(--sp-3); border-radius:var(--r-full);
  font-size:12px; font-weight:600; background:var(--n50); color:var(--n700);
  border:1px solid transparent; white-space:nowrap;
}
/* Нейтральная метка. Класс использовался в двенадцати местах, а правила не
   было — совпадал с базовым чипом по случайности и уехал бы при первой же
   правке базового. */
.chip.mute { background:var(--n50); color:var(--n700); }
.chip.ok { background:var(--ok-bg); color:var(--ok); }
.chip.warn { background:var(--warn-bg); color:var(--warn); }
.chip.danger { background:var(--danger-bg); color:var(--danger); }
.chip.info { background:var(--info-bg); color:var(--info); }
.chip.brand { background:var(--brand-50); color:var(--brand-700); }

/* Статус, который правится прямо в строке. Класс стоял в разметке реестра
   документов, а правил под него не было ни одного: статус там выглядел
   обычным серым полем, тогда как на рабочем столе те же «Принят» и
   «Отклонён» — цветные. Вес селектора не меньше, чем у общего правила для
   полей в таблице, иначе оно вернёт высоту 32 и прямые углы. */
table.data td select.chip-select, select.chip-select {
  height:24px; width:auto; max-width:100%; padding:0 var(--sp-2);
  border-radius:var(--r-full); border:1px solid transparent;
  background:var(--n50); color:var(--n700); font-size:12px; font-weight:600;
}
table.data td select.chip-select.ok, select.chip-select.ok { background:var(--ok-bg); color:var(--ok); }
table.data td select.chip-select.warn, select.chip-select.warn { background:var(--warn-bg); color:var(--warn); }
table.data td select.chip-select.danger, select.chip-select.danger { background:var(--danger-bg); color:var(--danger); }
table.data td select.chip-select.info, select.chip-select.info { background:var(--info-bg); color:var(--info); }
table.data td select.chip-select.brand, select.chip-select.brand { background:var(--brand-50); color:var(--brand-700); }
.chip.co-aso { background:rgba(0,112,125,.10); color:var(--co-aso); }
.chip.co-cdo { background:rgba(20,48,94,.10); color:var(--co-cdo); }
.chip.co-apk { background:rgba(154,52,18,.10); color:var(--co-apk); }
.chip button { border:0; background:none; cursor:pointer; color:inherit; font:inherit; padding:0 0 0 2px; }

/* Причина исключения из сводок. В одну строку с многоточием: полностью она
   бывает длинной («Сумма собралась неверно: объединённая ячейка цены в заявке
   дала 48 006 433 400 вместо 76 600…»), и в узкой колонке статуса разворачивала
   строку таблицы на четыре. Целиком читается по наведению и в журнале. */
.reason-hint {
  display:block;
  max-width:190px;
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
  cursor:help;
}
/* На узком экране таблица становится карточками, ширины колонки больше нет —
   там причину показываем целиком: места хватает, а прятать нечего. */
@media (max-width: 768px) {
  .reason-hint { max-width:none; white-space:normal; }
}

/* Сумма вне правдоподобного диапазона. Не прячем и не молчим: подчёркиваем
   волной и красим в danger — ошибка разбора обязана выглядеть ошибкой, а не
   рекордом. Один такой документ (48 006 433 400 ₽ вместо 76 600 ₽) двое суток
   стоял на рабочем столе руководителя обычным весом и не был замечен. */
.sum-bad {
  color:var(--danger);
  text-decoration:underline wavy var(--danger);
  text-underline-offset:3px;
  cursor:help;
}

/* фильтры-чипы */
.filters { display:flex; gap:var(--sp-2); flex-wrap:wrap; align-items:center; }
.filter-chip {
  display:inline-flex; align-items:center; gap:var(--sp-2); height:32px;
  padding:0 var(--sp-3); border:1px solid var(--n300); border-radius:var(--r-full);
  background:var(--n0); font-size:13px; font-weight:500; color:var(--n700); cursor:pointer;
}
.filter-chip:hover { border-color:var(--brand); }
.filter-chip.on { background:var(--brand-50); border-color:var(--brand); color:var(--brand-700); }

/* ---------------------------------------------------------------- сообщения */
.alert {
  display:flex; gap:var(--sp-3); padding:var(--sp-4);
  border:1px solid var(--n200); border-radius:var(--r-md); background:var(--n0);
  font-size:13px; margin-bottom:var(--sp-4);
}
.alert .ico { flex:none; width:20px; height:20px; }
.alert .body { flex:1; min-width:0; }
.alert b { display:block; margin-bottom:2px; }
.alert ul { margin:var(--sp-2) 0 0 var(--sp-4); }
.alert li { margin-bottom:2px; }
.alert.error { background:var(--danger-bg); border-color:var(--danger-br); color:#7A1B15; }
.alert.error b, .alert.error a { color:var(--danger); }
.alert.warn { background:var(--warn-bg); border-color:var(--warn-br); color:#6B4200; }
.alert.ok { background:var(--ok-bg); border-color:var(--ok-br); color:#0B5C33; }
.alert.info { background:var(--info-bg); border-color:var(--info-br); color:#12408F; }
.alert .actions { display:flex; gap:var(--sp-2); margin-top:var(--sp-3); flex-wrap:wrap; }

/* тосты */
.toasts {
  position:fixed; right:var(--sp-5); bottom:var(--sp-5); z-index:80;
  display:flex; flex-direction:column; gap:var(--sp-2); max-width:380px;
}
.toast {
  display:flex; align-items:flex-start; gap:var(--sp-3);
  background:var(--n900); color:#fff; border-radius:var(--r-md);
  padding:var(--sp-3) var(--sp-4); box-shadow:var(--shadow-lg); font-size:13px;
  animation:toast-in .18s ease-out;
}
.toast.ok { background:#0B5C33; }
.toast.error { background:#8C1E17; }
.toast .x { margin-left:auto; cursor:pointer; opacity:.7; background:none; border:0; color:#fff; }
@keyframes toast-in { from { opacity:0; transform:translateY(6px); } }

/* ---------------------------------------------------------------- таблицы */
.table-wrap {
  overflow:auto; border:1px solid var(--n200); border-radius:var(--r-md);
  background:var(--n0);
}
/* Таблица, которая помещается в экран, не должна жить внутри своей прокрутки.
   `overflow:auto` делает контейнер скроллпортом, и `position:sticky` у шапки
   цепляется за него — а он не прокручивается, прокручивается страница. Итог:
   при сотне строк колонки уезжают вверх и дальше читаешь безымянные ячейки.
   У плоской обёртки шапка липнет к странице, под самую шапку портала. */
.table-wrap.flat { overflow:visible; border:0; border-radius:0; }
.table-wrap.flat table.data th { top:var(--topbar-h); }
/* Широкая таблица, которая не помещается ни на одном ноутбуке (в «Документах»
   восемь колонок просят ~1150 px, а под содержимое остаётся ~860): ей нужна
   своя прокрутка. Но горизонтальная полоса у такой таблицы висела по нижнему
   краю всего списка — чтобы до неё добраться, приходилось листать страницу до
   конца, а при сотне строк ещё и возвращаться наверх, потеряв, чья это строка.

   Ограничение по высоте решает обе беды разом: таблица прокручивается внутри
   своей коробки, полоса всегда под рукой, а липкая шапка цепляется за эту же
   коробку и держит названия колонок. Именно так уже работают три таблицы
   портала — проверка переноса, номенклатура и журнал действий. */
.table-wrap.capped {
  border:0; border-radius:0; overflow:auto;
  max-height:min(70vh, 720px);
}
/* Сеть безопасности для плоских: если такая таблица всё-таки не поместится,
   пусть возит себя сама, а не толкает вбок всю страницу. */
@media (max-width:1080px) {
  .table-wrap.flat { overflow:auto; }
}
table.data { width:100%; border-collapse:separate; border-spacing:0; font-size:13px; }
/* Длинный текст в ячейке — двумя строками, дальше многоточие. Заказчик зовёт
   это «портянкой»: название программы из заявки бывает в три предложения
   («Электробезопасность 3 группа свыше 1000 В Оказание первой помощи
   пострадавшим Использование средств индивидуальной защиты»), и одна такая
   строка раздувает всю таблицу, а соседние колонки сжимаются в столбик по
   слову. Полный текст остаётся в title — навести и прочитать. */
.clamp-2 {
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
  overflow:hidden;
}
/* Строка-выбор: таблица, из которой берут одну запись в форму ниже. Выбранное
   метится тем же `.on`, что у .filter-chip, .drawer и .overlay — одно слово на
   портал, чтобы «выбрано» не называлось в каждом разделе по-своему. */
table.data tr.pickable { cursor:pointer; }
table.data tr.pickable:hover > td { background:var(--n25); }
table.data tr.pickable.on > td { background:var(--brand-50); }
/* таблица-редактор: колонки держат заданную ширину, прокручивается контейнер,
   иначе значения вроде СНИЛС обрезаются прямо в поле */
table.data.fixed { width:max-content; min-width:100%; }
table.data.fixed td, table.data.fixed th { white-space:nowrap; }
table.data.fixed td.wrap { white-space:normal; }
/* подписи под полем не должны вылезать в соседнюю колонку */
table.data.fixed td .field-hint, table.data.fixed td .field-error {
  display:block; max-width:100%; overflow:hidden; text-overflow:ellipsis; }
table.data th {
  position:sticky; top:0; z-index:2; background:var(--n25);
  text-align:left; font-weight:600; font-size:12px; letter-spacing:.04em;
  text-transform:uppercase; color:var(--n500);
  padding:var(--sp-3); border-bottom:1px solid var(--n200); white-space:nowrap;
}
table.data td {
  padding:var(--sp-2) var(--sp-3); border-bottom:1px solid var(--n100);
  vertical-align:middle; color:var(--n700);
}
table.data tr:last-child td { border-bottom:0; }
table.data tbody tr:hover td { background:var(--n25); }
table.data td.money, table.data th.money { text-align:right; font-variant-numeric:tabular-nums; white-space:nowrap; }
/* Класс .money на поле ввода ничего не делал: селектор ловил только ячейку.
   Из-за этого «Кол-во» и «Наценка, %» стояли влево под правыми шапками. */
table.data td input.money { text-align:right; }
table.data td input[type=number], table.data td input.money {
  font-variant-numeric:tabular-nums;
}
table.data td b { color:var(--n900); }
table.data .row-actions { display:flex; gap:var(--sp-1); justify-content:flex-end; opacity:.6; }
table.data tr:hover .row-actions, table.data tr:focus-within .row-actions { opacity:1; }
table.data.rows-tall td { padding:var(--sp-3); }
table.data td input, table.data td select { height:32px; font-size:13px; }
table.data .rn { color:var(--n500); font-size:12px; font-variant-numeric:tabular-nums; width:36px; }
table.data tr.flagged td { background:#FFFBF2; }
table.data tr.flagged:hover td { background:#FFF7E8; }

.totals {
  display:flex; justify-content:flex-end; align-items:center; gap:var(--sp-5);
  padding:var(--sp-3) var(--sp-4); border-top:1px solid var(--n200);
  background:var(--n25); font-size:13px; flex-wrap:wrap;
}
.totals .big { font-size:20px; font-weight:600; color:var(--n900); font-variant-numeric:tabular-nums; }
.totals .split { color:var(--n500); }

/* липкая панель действий внизу формы */
.sticky-bar {
  position:sticky; bottom:0; z-index:20;
  display:flex; align-items:center; gap:var(--sp-4); flex-wrap:wrap;
  background:var(--n0); border:1px solid var(--n200); border-radius:var(--r-md);
  box-shadow:var(--shadow-md); padding:var(--sp-3) var(--sp-4); margin-top:var(--sp-4);
}
.spacer { flex:1; }

/* ---------------------------------------------------------------- состояния */
.empty {
  text-align:center; padding:var(--sp-5); color:var(--n500);
}
/* Просторный вариант — для пустой страницы целиком, а не пустой таблицы. */
.empty.roomy { padding:var(--sp-7) var(--sp-5); }
.empty .ico {
  width:44px; height:44px; margin:0 auto var(--sp-3); border-radius:var(--r-md);
  background:var(--n50); display:flex; align-items:center; justify-content:center; color:var(--n300);
}
.empty h2, .empty h3 { font-size:16px; color:var(--n900); margin-bottom:var(--sp-1); }
.empty p { font-size:13px; margin-bottom:var(--sp-4); }

.skeleton {
  background:linear-gradient(90deg,var(--n50) 25%,var(--n100) 37%,var(--n50) 63%);
  background-size:400% 100%; animation:sk 1.2s ease infinite; border-radius:var(--r-sm);
  height:12px;
}
@keyframes sk { 0% { background-position:100% 50%; } 100% { background-position:0 50%; } }
.skeleton.line { display:block; height:20px; }
/* Колонка из нескольких полей: главное сверху, уточнение снизу. */
.stack { display:flex; flex-direction:column; gap:var(--sp-1); }
/* Плитки сводок — самое верхнее и самое заметное на экране — грузились без
   всякой заметки: до ответа сервера страница стояла белой, и это читалось как
   «данных нет», а не «сейчас будут». Таблица под ними скелет имела. */
.metric.pending .v, .metric.pending .k {
  color:transparent; border-radius:var(--r-sm);
  background:linear-gradient(90deg,var(--n50) 25%,var(--n100) 37%,var(--n50) 63%);
  background-size:400% 100%; animation:sk 1.2s ease infinite;
}
.metric.pending .v { width:72px; }
.metric.pending .k { width:112px; }

/* ---------------------------------------------------------------- оверлей с этапами */
.overlay {
  position:fixed; inset:0; z-index:70; background:rgba(16,24,40,.45);
  display:none; align-items:center; justify-content:center; padding:var(--sp-4);
}
.overlay.on { display:flex; }
.overlay .box {
  background:var(--n0); border-radius:var(--r-md); box-shadow:var(--shadow-lg);
  padding:var(--sp-5); width:min(420px,100%);
}
.overlay h3 { margin-bottom:var(--sp-3); }
.overlay .steps-list { display:flex; flex-direction:column; gap:var(--sp-2); font-size:13px; }
.overlay .steps-list div { display:flex; align-items:center; gap:var(--sp-2); color:var(--n500); }
.overlay .steps-list div.done { color:var(--n900); }
.overlay .steps-list div.now { color:var(--brand-700); font-weight:600; }
.overlay .steps-list .mark { width:16px; text-align:center; }
.overlay .bar { height:8px; border-radius:var(--r-full); background:var(--n100); margin:var(--sp-4) 0 var(--sp-3); overflow:hidden; }
.overlay .bar i { display:block; height:100%; background:var(--brand); width:10%; transition:width .4s ease; }
.overlay .note { font-size:13px; color:var(--n500); }

/* модальное окно */
.modal-back {
  position:fixed; inset:0; z-index:75; background:rgba(16,24,40,.45);
  display:none; align-items:center; justify-content:center; padding:var(--sp-4);
}
.modal-back.on { display:flex; }
.modal {
  background:var(--n0); border-radius:var(--r-md); box-shadow:var(--shadow-lg);
  width:min(460px,100%); padding:var(--sp-5);
}
.modal h3 { margin-bottom:var(--sp-2); }
.modal p { color:var(--n500); font-size:13px; margin-bottom:var(--sp-4); }
.modal .btn-row { justify-content:flex-end; }

/* боковая панель */
.drawer-back {
  position:fixed; inset:0; z-index:60; background:rgba(16,24,40,.35); display:none;
}
.drawer-back.on { display:block; }
.drawer {
  position:fixed; top:0; right:0; bottom:0; width:min(720px,92vw); z-index:61;
  background:var(--n0); box-shadow:var(--shadow-lg); display:none; flex-direction:column;
}
.drawer.on { display:flex; }
.drawer header {
  display:flex; align-items:center; gap:var(--sp-3);
  padding:var(--sp-4) var(--sp-5); border-bottom:1px solid var(--n200);
}
.drawer header h3 { flex:1; }
.drawer .body { flex:1; overflow:auto; padding:var(--sp-5); }
.drawer .body.flush { padding:0; }
.drawer iframe { width:100%; height:100%; border:0; background:var(--n50); }

/* ---------------------------------------------------------------- шаги мастера */
.steps { display:flex; gap:var(--sp-2); margin-bottom:var(--sp-5); flex-wrap:wrap; }
.steps .st {
  display:flex; align-items:center; gap:var(--sp-2);
  padding:var(--sp-2) var(--sp-4); border-radius:var(--r-full);
  background:var(--n0); border:1px solid var(--n200); color:var(--n500);
  font-size:13px; font-weight:500;
}
.steps .st .n {
  width:24px; height:24px; border-radius:var(--r-full); background:var(--n100); color:var(--n500);
  display:flex; align-items:center; justify-content:center; font-size:12px; font-weight:700;
}
.steps .st.done { color:var(--n900); border-color:var(--ok-br); background:var(--ok-bg); }
.steps .st.done .n { background:var(--ok); color:#fff; }
.steps .st.now { color:var(--brand-700); border-color:var(--brand); background:var(--brand-50); }
.steps .st.now .n { background:var(--brand); color:#fff; }

/* зона загрузки */
.dropzone {
  border:1px dashed var(--n300); border-radius:var(--r-md); background:var(--n25);
  padding:var(--sp-6) var(--sp-4); text-align:center; cursor:pointer;
}
.dropzone:hover { border-color:var(--brand); background:var(--brand-50); }
/* Зона загрузки работает кнопкой и с клавиатуры — значит и выглядеть в фокусе
   должна как кнопка. Без этого человек, дошедший до неё табом, не понимает,
   где он находится. */
.dropzone:focus-visible { outline:2px solid var(--brand); outline-offset:2px; }
.dropzone.drag { border-color:var(--brand); background:var(--brand-50); }
.dropzone.off { opacity:.6; pointer-events:none; }
.dropzone b { display:block; font-size:14px; color:var(--n900); margin-bottom:var(--sp-1); }
.dropzone .muted { font-size:13px; }
.file-card {
  display:flex; align-items:center; gap:var(--sp-3);
  border:1px solid var(--n200); border-radius:var(--r-md); padding:var(--sp-3) var(--sp-4);
  background:var(--n0);
}
.file-card .info { flex:1; min-width:0; }
.file-card .name { font-weight:600; font-size:13px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.file-card .meta { color:var(--n500); font-size:13px; }

/* сверка сумм */
.reconcile { display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:1px; background:var(--n200);
  border:1px solid var(--n200); border-radius:var(--r-md); overflow:hidden; }
.reconcile > div { background:var(--n0); padding:var(--sp-4); }
.reconcile .k { font-size:12px; color:var(--n500); text-transform:uppercase; letter-spacing:.04em; font-weight:600; }
.reconcile .v { font-size:16px; font-weight:600; font-variant-numeric:tabular-nums; margin-top:var(--sp-1); }
.reconcile .s { font-size:13px; color:var(--n500); margin-top:2px; }
.reconcile > div.diff { background:var(--warn-bg); }
.reconcile > div.diff .v { color:var(--warn); }
.reconcile > div.match { background:var(--ok-bg); }
.reconcile > div.match .v { color:var(--ok); }

/* метрики на рабочем столе */
.metrics { display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:var(--sp-4); }
.metric {
  background:var(--n0); border:1px solid var(--n200); border-radius:var(--r-md);
  padding:var(--sp-5); box-shadow:var(--shadow-sm);
}
.metric .v { font-size:24px; font-weight:600; font-variant-numeric:tabular-nums; letter-spacing:-.02em; }
.metric .k { font-size:13px; color:var(--n500); margin-top:var(--sp-1); }
.metric.attention { border-color:var(--warn-br); background:var(--warn-bg); }
.metric.attention .v { color:var(--warn); }
a.metric { text-decoration:none; color:inherit; }
a.metric .v { color:var(--n900); }
a.metric.attention .v { color:var(--warn); }
a.metric:hover { border-color:var(--brand); box-shadow:var(--shadow-md); }

/* предпросмотр документа */
.preview-pane {
  position:sticky; top:calc(var(--topbar-h) + var(--sp-4));
  border:1px solid var(--n200); border-radius:var(--r-md); background:var(--n0);
  box-shadow:var(--shadow-sm); overflow:hidden; display:flex; flex-direction:column;
  max-height:calc(100dvh - var(--topbar-h) - var(--sp-8));
}
.preview-pane header {
  display:flex; align-items:center; gap:var(--sp-2);
  padding:var(--sp-3) var(--sp-4); border-bottom:1px solid var(--n100);
}
.preview-pane header .t { font-size:13px; font-weight:600; flex:1; }
.preview-pane header .stamp { font-size:12px; color:var(--n500); }
/* высота задана явно: iframe с height:100% не растягивается по min-height флекса */
.preview-pane .frame { flex:1 1 auto; height:min(74vh, 920px); background:var(--n50); overflow:hidden; }
.preview-pane iframe { width:100%; height:100%; border:0; background:#fff; }
.preview-pane.stale .frame { opacity:.45; }
.preview-tabs { display:flex; gap:var(--sp-1); padding:var(--sp-2) var(--sp-3); border-bottom:1px solid var(--n100); flex-wrap:wrap; }
.preview-tabs button {
  border:1px solid var(--n200); background:var(--n0); border-radius:var(--r-full);
  padding:var(--sp-1) var(--sp-3); font:inherit; font-size:12px; font-weight:600;
  color:var(--n500); cursor:pointer;
}
.preview-tabs button.on { background:var(--brand-50); border-color:var(--brand); color:var(--brand-700); }

.editor { display:grid; grid-template-columns:minmax(0,1.25fr) minmax(0,1fr); gap:var(--sp-5); align-items:start; }

/* вкладки настроек */
.settings-layout { display:grid; grid-template-columns:220px minmax(0,1fr); gap:var(--sp-5); align-items:start; }
.settings-nav { display:flex; flex-direction:column; gap:var(--sp-1); position:sticky; top:calc(var(--topbar-h) + var(--sp-4)); }
.settings-nav button {
  text-align:left; border:0; background:none; font:inherit; font-size:14px; cursor:pointer;
  padding:var(--sp-2) var(--sp-3); border-radius:var(--r-sm); color:var(--n700);
  border-left:2px solid transparent;
}
.settings-nav button:hover { background:var(--n50); }
.settings-nav button.on { background:var(--brand-50); color:var(--brand-700); font-weight:600; border-left-color:var(--brand); }
.settings-pane { display:none; }
.settings-pane.on { display:block; }

/* ---------------------------------------------------------------- адаптив */
@media (max-width:1400px) {
  .editor { grid-template-columns:minmax(0,1fr); }
  .preview-pane { position:static; max-height:none; }
  .preview-pane .frame { height:70vh; }
}
@media (max-width:1080px) {
  .grid.c4 { grid-template-columns:repeat(2,minmax(0,1fr)); }
  .settings-layout { grid-template-columns:minmax(0,1fr); }
  .settings-nav { position:static; flex-direction:row; overflow-x:auto; padding-bottom:var(--sp-2); }
  .settings-nav button { white-space:nowrap; border-left:0; border-bottom:2px solid transparent; }
  .settings-nav button.on { border-bottom-color:var(--brand); }
}
/* До 1080 px боковая навигация уступает место данным: она прячется и
   открывается бургером поверх содержимого. Порог выбран по самой широкой
   таблице портала — «Паспорт дня»: ниже него ей уже нужен весь экран. */
@media (max-width:1080px) {
  body.has-nav { padding-left:0; }
  .sidenav {
    top:var(--topbar-h); width:min(300px, 86vw); display:none;
    box-shadow:var(--shadow-lg);
  }
  .sidenav.open { display:flex; }
  .nav-item { min-height:44px; }
  .burger {
    display:inline-flex; align-items:center; justify-content:center;
    width:44px; height:44px; border:1px solid var(--n300); border-radius:var(--r-sm);
    background:var(--n0); cursor:pointer; order:-1;
  }
}

@media (max-width:768px) {
  .wrap { padding:var(--sp-4); }
  .grid.c2, .grid.c3, .grid.c4 { grid-template-columns:minmax(0,1fr); }
  .topbar { gap:var(--sp-3); padding:0 var(--sp-4); }
  .topbar .who .role { display:none; }
  .btn { height:44px; }
  .btn.sm { height:36px; }
  .page { font-size:20px; }
  .toasts { left:var(--sp-4); right:var(--sp-4); bottom:var(--sp-4); max-width:none; }
  .hide-sm { display:none !important; }
  /* таблица из восьми колонок на телефоне нечитаема — показываем карточками */
  table.data.cardify thead { display:none; }
  table.data.cardify, table.data.cardify tbody, table.data.cardify tr, table.data.cardify td { display:block; width:100%; }
  table.data.cardify tr { border:1px solid var(--n200); border-radius:var(--r-md);
    margin:0 0 var(--sp-2); padding:var(--sp-3); background:var(--n0); }
  table.data.cardify tbody tr:hover td { background:none; }
  table.data.cardify td { border:0; padding:var(--sp-1) 0; text-align:left; }
  table.data.cardify td.money { text-align:left; }
  table.data.cardify td::before {
    content:attr(data-label); display:block; font-size:12px; letter-spacing:.04em;
    text-transform:uppercase; color:var(--n500); margin-bottom:2px; }
  table.data.cardify td[data-label=""]::before, table.data.cardify td:not([data-label])::before { display:none; }
  /* Кнопка «показать ещё / свернуть» карточкой не становится: она не строка
     данных, а действие под списком. */
  table.data.cardify tfoot tr.expander { border:0; padding:0; background:none; }
  table.data.cardify .row-actions { justify-content:flex-start; opacity:1; margin-top:var(--sp-2); }
  /* Карточка со строкой ввода, а не с готовым значением: поля тянутся на всю
     ширину карточки, иначе они остаются шириной колонки, которой больше нет. */
  table.data.cardify td input, table.data.cardify td select,
  table.data.cardify td .with-suffix { width:100%; max-width:none; min-width:0; }
  /* Номер позиции — заголовок карточки, а не ещё одна строка «№ · 1». */
  #itemsTable.cardify td.rn { font-weight:600; font-size:14px; color:var(--n700);
    padding-bottom:var(--sp-2); }
  #itemsTable.cardify td.rn::before { content:"Позиция "; display:inline;
    font-size:inherit; letter-spacing:0; text-transform:none; color:inherit; margin:0; }
  /* Цена, скидка и количество — короткие числа: столбиком карточка позиции
     вырастала до 390 px, и пять позиций КП превращались в две ладони скролла.
     В три колонки та же карточка занимает вдвое меньше. */
  #itemsTable.cardify tbody tr { display:grid; gap:0 var(--sp-2);
    grid-template-columns:repeat(3, minmax(0,1fr)); align-items:end; }
  #itemsTable.cardify td.rn, #itemsTable.cardify td.combo { grid-column:1 / -1; }
  #itemsTable.cardify td.line-total { grid-column:1 / 3; font-weight:600; }
  input:not([type]), input[type=text], input[type=number], input[type=password],
  input[type=date], input[type=search], input[type=tel], input[type=email],
  select, textarea { font-size:16px; }  /* iOS не зумит форму */
}

@media (prefers-reduced-motion:reduce) {
  * { animation-duration:.01ms !important; transition-duration:.01ms !important; }
}

@media print {
  .topbar, .sticky-bar, .toasts, .btn { display:none !important; }
  body { background:#fff; }
}

/* ============================================================================
   Обучение. Курс показывается во фрейме со своими стилями — здесь только
   рама: список модулей, прогресс, аттестация.
   ========================================================================= */

.progress {
  height:8px; background:var(--n100); border-radius:var(--r-full);
  overflow:hidden; margin:var(--sp-3) 0;
}
.progress i { display:block; height:100%; background:var(--brand); border-radius:inherit; }
.progress i.ok { background:var(--ok); }
.progress i.bad { background:var(--danger); }

.ok-text { color:var(--ok); font-weight:600; }
.bad-text { color:var(--danger); font-weight:600; }

.course-meta {
  display:flex; gap:var(--sp-4); flex-wrap:wrap;
  font-size:13px; color:var(--n500); margin-top:var(--sp-2);
}

/* ---------------------------------------------------------------- читалка */
.course-layout {
  display:grid; grid-template-columns:264px minmax(0,1fr);
  gap:var(--sp-6); max-width:1280px; margin:0 auto;
  padding:var(--sp-5) var(--sp-5) var(--sp-8);
}
/* Поля страницы на телефоне у всего портала 16px (см. .wrap ниже);
   курс в этот брейкпоинт не попадал и отбивался иначе, чем соседние разделы. */
@media (max-width:768px) { .course-layout { padding:var(--sp-4) var(--sp-4) var(--sp-6); gap:var(--sp-4); } }
@media (max-width:1080px) { .course-layout { grid-template-columns:1fr; } }

.course-nav { position:sticky; top:calc(var(--topbar-h) + var(--sp-4)); align-self:start; }
.course-nav .back { align-self:flex-start; margin-left:calc(var(--sp-3) * -1); }

.course-nav h1 { font-size:16px; margin:var(--sp-3) 0 0; }
.course-nav-meta { font-size:13px; color:var(--n500); margin:0 0 var(--sp-4); }
.course-nav nav { display:flex; flex-direction:column; gap:var(--sp-1); }
.course-nav nav button {
  display:flex; align-items:center; gap:var(--sp-3); width:100%; text-align:left;
  padding:var(--sp-2) var(--sp-3); border:0; background:none; cursor:pointer;
  border-radius:var(--r-sm); font:inherit; font-size:13px; color:var(--n700);
  border-left:2px solid transparent;
}
.course-nav nav button:hover { background:var(--n50); }
.course-nav nav button.active {
  background:var(--brand-50); color:var(--brand-700);
  font-weight:600; border-left-color:var(--brand);
}
.course-nav nav button .n {
  flex:0 0 20px; font-family:var(--mono); font-size:12px; color:var(--n500);
  font-variant-numeric:tabular-nums;
}
.course-nav nav button .t { flex:1; line-height:1.3; }
.course-nav nav button .tick { color:var(--ok); font-weight:700; opacity:0; }
.course-nav nav button.done .tick { opacity:1; }
.course-nav nav button.done .n { color:var(--ok); }

.course-head {
  display:flex; align-items:flex-start; gap:var(--sp-5);
  justify-content:space-between; flex-wrap:wrap; margin-bottom:var(--sp-4);
}
.course-head .actions { display:flex; gap:var(--sp-2); flex-shrink:0; }

#modFrameWrap {
  border:1px solid var(--n200); border-radius:var(--r-md);
  background:var(--n0); overflow:hidden;
}

/* Переход к следующему модулю — там, где чтение и заканчивается. Раньше он
   стоял только в шапке: модуль длиной в две тысячи пикселей дочитывали до
   конца и листали обратно наверх, чтобы нажать «дальше». */
.module-foot {
  display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap;
  margin:var(--sp-5) 0 var(--sp-8); padding:var(--sp-4) var(--sp-5);
  background:var(--n0); border:1px solid var(--n200); border-radius:var(--r-md);
  box-shadow:var(--shadow-sm);
}
.module-foot .next-of {
  font-size:13px; color:var(--n500); min-width:0;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
@media (max-width:768px) {
  .module-foot { flex-direction:column; align-items:stretch; }
  .module-foot .next-of { order:-1; white-space:normal; text-align:center; }
  .module-foot .spacer { display:none; }
}

/* Список модулей на узком экране. Ниже 1080 колонки схлопываются в одну, и
   четырнадцать модулей встают НАД текстом: содержимое начиналось на 940-й
   точке, до него нужно было пролистать весь список. Сворачиваем в строку —
   развернуть можно нажатием. На широком экране список стоит сбоку и всегда
   раскрыт, поэтому заголовок-раскрывашка там не показывается. */
.mod-picker > summary { display:none; }
@media (max-width:1080px) {
  .mod-picker { border:1px solid var(--n200); border-radius:var(--r-md);
    background:var(--n0); margin-bottom:var(--sp-4); }
  .mod-picker > summary {
    display:flex; align-items:center; gap:var(--sp-2); cursor:pointer;
    min-height:44px; padding:0 var(--sp-4); font-size:14px; font-weight:600;
    color:var(--n900); list-style:none;
  }
  .mod-picker > summary::-webkit-details-marker { display:none; }
  .mod-picker > summary::after {
    content:""; margin-left:auto; width:8px; height:8px; flex:none;
    border-right:2px solid var(--n500); border-bottom:2px solid var(--n500);
    transform:rotate(45deg) translate(-2px,-2px);
  }
  .mod-picker[open] > summary::after { transform:rotate(-135deg) translate(-2px,-2px); }
  .mod-picker > summary .now { font-weight:400; color:var(--n500); min-width:0;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .mod-picker[open] > nav { padding:0 var(--sp-2) var(--sp-2); }
}
#modFrame { display:block; width:100%; border:0; min-height:60vh; }

/* ------------------------------------------------------------- аттестация */
/* Модуль, закрытый идущей аттестацией. Строку оставляем на месте и видимой —
   человек должен понимать, что курс никуда не делся, просто сейчас закрыт. */
.course-nav nav button.locked { opacity:.45; cursor:not-allowed; }
.course-nav nav button.locked:hover { background:none; }

/* Экран перед началом: условия видно до того, как курс закроется. */
.exam-start {
  border:1px solid var(--n200); border-radius:var(--r-md); background:var(--n0);
  padding:var(--sp-6); box-shadow:var(--shadow-sm); max-width:640px;
}
.exam-start-facts { display:flex; gap:var(--sp-6); flex-wrap:wrap; margin-bottom:var(--sp-4); }
.exam-start-facts > div { display:flex; flex-direction:column; gap:var(--sp-1); }
.exam-start-facts b { font-size:24px; font-weight:600; font-variant-numeric:tabular-nums; }
.exam-start-facts span { font-size:13px; color:var(--n500); }
.exam-start .field-hint { display:block; margin-bottom:var(--sp-5); line-height:1.5; }

/* Полоса с таймером: видна всё время попытки, поэтому липкая. */
.exam-bar {
  position:sticky; top:calc(var(--topbar-h) + var(--sp-4)); z-index:5;
  display:flex; align-items:center; gap:var(--sp-4); flex-wrap:wrap;
  border:1px solid var(--warn-br); background:var(--warn-bg); color:var(--warn);
  border-radius:var(--r-md); padding:var(--sp-3) var(--sp-4); margin-bottom:var(--sp-5);
}
.exam-bar > div:first-child { flex:1; display:flex; flex-direction:column; gap:var(--sp-1); }
.exam-bar .field-hint { color:var(--n500); }
.exam-timer { font-size:20px; font-weight:600; font-variant-numeric:tabular-nums; }
/* Последние две минуты — другим цветом: цифры на краю поля зрения. */
.exam-bar.hot { border-color:var(--danger-br); background:var(--danger-bg); color:var(--danger); }

.exam-q {
  border:1px solid var(--n200); border-radius:var(--r-md); background:var(--n0);
  padding:var(--sp-5); margin-bottom:var(--sp-4);
}
.exam-q.right { border-color:var(--ok-br); background:var(--ok-bg); }
.exam-q.wrong { border-color:var(--danger-br); background:var(--danger-bg); }
.exam-q-text {
  font-weight:600; margin-bottom:var(--sp-3); display:flex; gap:var(--sp-3);
  line-height:1.4;
}
.exam-q-text .n {
  flex:0 0 24px; height:24px; border-radius:var(--r-full);
  background:var(--n50); color:var(--n500); font-family:var(--mono);
  font-size:12px; font-weight:600; display:grid; place-items:center;
}
.exam-opt {
  display:flex; gap:var(--sp-3); align-items:flex-start;
  padding:var(--sp-3); border:1px solid var(--n200); border-radius:var(--r-md);
  margin-bottom:var(--sp-2); cursor:pointer; background:var(--n0);
}
.exam-opt:hover { border-color:var(--n300); }
.exam-opt:has(input:checked) { border-color:var(--brand); background:var(--brand-50); }
.exam-opt input { margin-top:3px; accent-color:var(--brand); }
.exam-opt.is-correct { border-color:var(--ok); background:var(--ok-bg); }
.exam-opt.is-wrong { border-color:var(--danger); background:var(--danger-bg); }
.exam-explain {
  margin-top:var(--sp-3); padding-top:var(--sp-3); border-top:1px solid var(--n200);
  font-size:13px; color:var(--n700);
}
.exam-result {
  display:flex; align-items:center; gap:var(--sp-5);
  border:1px solid var(--n200); border-left:3px solid var(--n300);
  border-radius:var(--r-md); padding:var(--sp-5); margin-bottom:var(--sp-5);
  background:var(--n0);
}
.exam-result.ok { border-left-color:var(--ok); background:var(--ok-bg); }
.exam-result.bad { border-left-color:var(--danger); background:var(--danger-bg); }
.exam-result .score {
  font-size:30px; font-weight:700; letter-spacing:-.03em;
  font-variant-numeric:tabular-nums;
}
.exam-result.ok .score { color:var(--ok); }
.exam-result.bad .score { color:var(--danger); }
.exam-result p { margin:var(--sp-1) 0 0; font-size:13px; color:var(--n700); }

/* --------------------------------------------------- отчёт по сотрудникам */
#reportTable td.cell { text-align:center; cursor:pointer; white-space:nowrap; }
#reportTable td.cell:hover { background:var(--n50); }
#reportTable .mark { font-family:var(--mono); font-size:13px; font-weight:700; display:block; }
#reportTable .mark.all { color:var(--ok); }
#reportTable .mark.own { color:var(--info); }
#reportTable .mark.bad { color:var(--danger); }
#reportTable .mark.no { color:var(--n300); font-weight:400; }
#reportTable .pct { display:block; font-size:12px; color:var(--n500); }
#reportTable td.who .role { display:block; font-size:12px; color:var(--n500); }
#reportTable tr.dep td {
  background:var(--n25); font-size:12px; font-weight:600;
  letter-spacing:.06em; text-transform:uppercase; color:var(--n500);
}

/* ---------------------------------------------------------- рабочий стол */
.attention-card { display:block; }
.attention-card .big {
  font-size:30px; font-weight:700; letter-spacing:-.03em; line-height:1;
  font-variant-numeric:tabular-nums; margin-bottom:var(--sp-2);
}
.attention-card.danger .big { color:var(--danger); }
.attention-card.warn .big { color:var(--warn); }
.attention-card.info .big { color:var(--info); }

.learn-row {
  display:flex; align-items:center; gap:var(--sp-4);
  padding:var(--sp-3) 0; border-bottom:1px solid var(--n100);
  text-decoration:none; color:inherit;
}
.learn-row:last-child { border-bottom:0; }
.learn-row:hover .t { color:var(--brand-700); }
.learn-row .t { flex:1; font-weight:500; font-size:14px; }
.learn-row .p { flex:0 0 120px; }
.learn-row .v {
  flex:0 0 44px; text-align:right; font-variant-numeric:tabular-nums;
  font-size:13px; color:var(--n500);
}
.learn-row .s { flex:0 0 110px; text-align:right; }
@media (max-width:768px) {
  .learn-row { flex-wrap:wrap; gap:var(--sp-2); }
  .learn-row .t { flex:1 1 100%; }
}

#teamTable td .role { display:block; font-size:12px; color:var(--n500); }
#teamTable tr.dep td {
  background:var(--n25); font-size:12px; font-weight:600;
  letter-spacing:.06em; text-transform:uppercase; color:var(--n500);
}

/* --------------------------------------------------- справочник сотрудников */
/* Было семь колонок и жёсткие минимумы у полей — таблица требовала 1518 px и
   уезжала вбок на любом ноутбуке. Стало пять: должность встала под ФИО,
   подразделение — под ролью. Пара «главное сверху, уточнение снизу» читается
   быстрее, чем те же значения, разложенные по четырём столбцам. */
/* Строка сотрудника — это две линии: главное сверху, уточнение снизу. Чтобы
   линии совпадали по всем пяти колонкам, у каждой ячейки один и тот же шаг:
   32 px на линию, 4 px между. И ячейки прижаты к верху — при выравнивании по
   центру короткая колонка съезжала вниз на половину разницы, и поля второй
   линии стояли на разной высоте с соседними. */
#peopleTable td { vertical-align:top; }
#peopleTable .stack,
#peopleTable td .acc-cell,
#peopleTable td .pwd-cell {
  display:grid; grid-auto-rows:32px; gap:var(--sp-1); align-items:center;
}
#peopleTable .stack > * { width:100%; }
#peopleTable td .acc-cell, #peopleTable td .pwd-cell { justify-items:start; }
#peopleTable .c-who { min-width:240px; }
#peopleTable .c-role { min-width:240px; }
/* Имя — главное в строке, должность под ним — уточнение: одинаковый вес делал
   из пары два равнозначных поля, и глаз не знал, за что зацепиться. */
#peopleTable .p-fio { font-weight:600; }
#peopleTable .p-position, #peopleTable .p-department { color:var(--n700); }
#peopleTable .marks { display:flex; flex-wrap:wrap; gap:var(--sp-1); }
#peopleTable td > .chip { display:inline-flex; }
#peopleTable td .btn.ghost.sm, #peopleTable td .btn.danger.sm { flex:0 0 auto; }
/* Галочка стоит на первой линии строки, а не посередине всей ячейки:
   24 px по центру 32-пиксельной линии — это отступ в 4. */
#peopleTable .c-active { text-align:center; }
#peopleTable .c-active input { margin-top:var(--sp-1); }
/* Отключённый сотрудник виден, но не спорит за внимание с работающими.
   Было инлайновым `opacity:.55` в разметке — класс правится в одном месте. */
#peopleTable tr.off td { opacity:.5; }
#peopleTable tr.off:hover td, #peopleTable tr.off:focus-within td { opacity:1; }

/* -------------------------------------------------- данные из чужого сервиса */
/* Откуда цифры и на какой они момент. Показывать вчерашние числа как
   сегодняшние хуже, чем не показывать ничего. */
.source-note {
  font-size:13px; color:var(--n500); padding:var(--sp-2) 0;
  border-bottom:1px solid var(--n100);
}
#ppTable td .role, #recTable td .role { display:block; font-size:12px; color:var(--n500); }

/* Произвольный период рядом с готовыми. Поля дат по умолчанию тянутся на всю
   ширину (`input {width:100%}`) — в строке фильтров им нужна своя. */
.date-range input[type=date] { width:auto; min-width:150px; height:32px; font-size:13px; }
.date-range .dash { color:var(--n500); }

/* Итоговая строка таблицы: отделена и не подсвечивается при наведении —
   это сумма, а не запись, на которую можно нажать. */
table.data tr.sum td {
  background:var(--n25); border-top:1px solid var(--n200); border-bottom:0;
  font-weight:600; color:var(--n900);
}
table.data tbody tr.sum:hover td { background:var(--n25); }

/* Доли: полосой мерятся признаки между собой, поэтому 100% — самый частый из
   них, а не сумма. Доля от всех обращений тут ничего не объясняет. */
.share-row { display:flex; align-items:center; gap:var(--sp-3); padding:var(--sp-2) 0; font-size:13px; }
.share-row + .share-row { border-top:1px solid var(--n50); }
.share-row .t { flex:0 0 240px; color:var(--n700); }
.share-row .bar { flex:1; height:8px; min-width:60px; background:var(--n50); border-radius:999px; overflow:hidden; }
.share-row .bar i { display:block; height:100%; background:var(--brand); border-radius:999px; }
.share-row.manual .bar i { background:var(--warn); }
.share-row .n { flex:0 0 56px; text-align:right; font-weight:600; color:var(--n900); font-variant-numeric:tabular-nums; }
@media (max-width:768px){
  .share-row { flex-wrap:wrap; }
  .share-row .t { flex:1 1 100%; }
}

/* ------------------------------------------ запросы между подразделениями */
.req-head { display:flex; gap:var(--sp-2); flex-wrap:wrap; }
.req-body {
  font-size:14px; line-height:1.55; color:var(--n700);
  background:var(--n25); border-radius:var(--r-md); padding:var(--sp-3) var(--sp-4);
}
/* Факты в две колонки: подпись сверху мелким, значение под ней — так строка
   читается целиком, а не разъезжается двоеточиями по всей ширине. */
.req-facts {
  display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr));
  gap:var(--sp-3) var(--sp-4); margin-top:var(--sp-4);
  border-top:1px solid var(--n100); padding-top:var(--sp-4);
}
.req-facts > div { display:flex; flex-direction:column; gap:var(--sp-1); min-width:0; }
.req-facts span { font-size:12px; color:var(--n500); }
.req-facts b { font-size:13px; color:var(--n900); font-weight:600; }
.req-facts b.danger-text { color:var(--danger); }

.req-actions {
  margin-top:var(--sp-5); border:1px solid var(--n200); border-radius:var(--r-md);
  padding:var(--sp-4); background:var(--n25);
}
.req-due {
  display:flex; align-items:flex-end; gap:var(--sp-3); flex-wrap:wrap;
  margin-top:var(--sp-4); padding-top:var(--sp-4); border-top:1px solid var(--n200);
}
.req-due .f { max-width:200px; }

.req-feed { display:flex; flex-direction:column; gap:var(--sp-2); }
.req-event {
  border-left:2px solid var(--n200); padding:0 0 var(--sp-2) var(--sp-4);
}
.req-event-head { display:flex; gap:var(--sp-3); flex-wrap:wrap; align-items:baseline; }
.req-event-head b { font-size:13px; color:var(--n900); }
.req-event-head span { font-size:12px; color:var(--n500); }
.req-event p {
  margin:var(--sp-1) 0 0; font-size:13px; line-height:1.5; color:var(--n700);
}

/* ------------------------------------------------------ состав раздачи лидов */
.dist-grid {
  display:grid; grid-template-columns:repeat(auto-fill, minmax(240px, 1fr));
  gap:var(--sp-2);
}
.dist-item {
  display:flex; align-items:center; gap:var(--sp-3);
  padding:var(--sp-3); border:1px solid var(--n200); border-radius:var(--r-md);
  background:var(--n50); color:var(--n500); cursor:pointer; font-size:13px;
}
.dist-item:hover { border-color:var(--n300); }
.dist-item.on { background:var(--n0); color:var(--n900); border-color:var(--ok-br); }
.dist-item input { width:16px; height:16px; accent-color:var(--ok); flex:0 0 auto; }
.dist-item:has(input:disabled) { cursor:default; opacity:.75; }

/* ------------------------------------------------------------- паспорт дня */
.pp-filters { display:flex; gap:var(--sp-5); flex-wrap:wrap; margin:16px 0 24px; }
table.data tr.clickable { cursor:pointer; }
/* Строка-кнопка получает видимую рамку фокуса: обводить надо саму строку,
   а не ячейку, иначе с клавиатуры непонятно, что именно откроется. */
table.data tr.clickable:focus-visible {
  outline:2px solid var(--brand);
  outline-offset:-2px;
}
#ppTable td .role { display:block; font-size:12px; color:var(--n500); }

/* тренд за две недели */
.trend { display:flex; align-items:flex-end; gap:var(--sp-2); height:120px; }
.trend-col { flex:1; display:flex; flex-direction:column; align-items:center; gap:var(--sp-2); height:100%; }
.trend-col .bar {
  flex:1; width:100%; max-width:28px; background:var(--n50);
  border-radius:var(--r-sm); display:flex; align-items:flex-end; overflow:hidden;
}
.trend-col .bar i { display:block; width:100%; background:var(--n300); }
.trend-col .bar i.ok { background:var(--ok); }
.trend-col .bar i.warn { background:var(--warn); }
.trend-col .bar i.danger { background:var(--danger); }
.trend-col .lbl { font-size:12px; color:var(--n500); font-family:var(--mono); }

/* почасовой разбор */
details.hour { border:1px solid var(--n200); border-radius:var(--r-md); margin-bottom:var(--sp-2); background:var(--n0); }
details.hour summary {
  padding:var(--sp-3) var(--sp-4); cursor:pointer; display:flex;
  align-items:center; gap:var(--sp-3); font-size:13px;
}
details.hour summary::-webkit-details-marker { display:none; }
details.hour[open] summary { border-bottom:1px solid var(--n100); }
.hour-body { padding:var(--sp-3) var(--sp-4); display:flex; flex-direction:column; gap:var(--sp-1); }
.hour-gap {
  font-size:13px; color:var(--danger); background:var(--danger-bg);
  border-radius:var(--r-sm); padding:var(--sp-2) var(--sp-3);
}
.hour-event { display:flex; gap:var(--sp-3); font-size:13px; align-items:baseline; }
.hour-event .mono { color:var(--n500); flex:0 0 44px; }

/* ============================================================================
   Продажи. Раздел показывает чужую таблицу и ничего в неё не пишет, поэтому
   собственных элементов здесь два: итоговая строка и полоса дней.
   ========================================================================= */

/* Итог таблицы — не такая же строка, как остальные: у неё нет своего человека,
   и сортировка её не двигает. Отделяем чертой и фоном, а не жирностью одной. */
table.data tfoot td {
  border-top:2px solid var(--n200); background:var(--n25);
  padding:var(--sp-3) var(--sp-4); font-variant-numeric:tabular-nums;
}
table.data tfoot tr:hover td { background:var(--n25); }
/* Свёрнутый хвост: кнопка стоит в подвале таблицы, но подвалом-итогом не
   является — ни заливки, ни жирной черты ей не нужно. */
table.data tfoot tr.expander td {
  background:none; border-top:1px solid var(--n100); padding:var(--sp-3) 0;
}
table.data tfoot tr.expander:hover td { background:none; }

/* Полоса дней месяца. Высота столбика — доля от лучшего дня, шкала своя у
   каждого человека: сравнивать здесь надо себя с собой, а не с соседом.
   Выходной приглушён — пустая суббота не должна читаться как провал. */
.sales-days {
  display:flex; align-items:flex-end; gap:2px; overflow-x:auto;
  padding-bottom:var(--sp-1);
}
.sales-day {
  flex:1 0 18px; display:flex; flex-direction:column; align-items:center;
  justify-content:flex-end; gap:var(--sp-1); min-width:18px;
}
.sales-day i { display:block; width:100%; max-width:20px; background:var(--brand);
  border-radius:2px 2px 0 0; }
.sales-day b { font-size:12px; font-weight:400; color:var(--n500);
  font-variant-numeric:tabular-nums; }
.sales-day.off i { background:var(--n100); }
.sales-day.off b { color:var(--n300); }
.sales-day.empty i { background:var(--n100); }

/* Списки сверки: строка на человека, без маркеров — это перечисление, а не
   порядок действий. */
.sales-list { display:flex; flex-direction:column; gap:var(--sp-2); }
.sales-list div { min-width:0; }
/* Должность идёт второй строкой и переносится. Чипом её ставить нельзя: у чипа
   `white-space:nowrap`, и «Менеджер отдела корпоративных продаж» толкал вбок
   всю страницу на 375 px — обёртки со своей прокруткой у списка нет. */
.sales-list .role { display:block; font-size:12px; color:var(--n500); }
