/* Тема системы дашбордов «Сити-Клининг».
   Светлая и тёмная темы обязательны (ТЗ §21). Цвет не единственный носитель
   статуса — рядом всегда текст. Шрифты только локальные/системные: обращения
   к Google Fonts и внешним CDN запрещены.

   ── Палитра ──────────────────────────────────────────────────────────────
   Фирменные цвета заказчика: #322252 (глубокий индиго — в переписке назван
   синим) и #62A837 (зелёный логотипа, снят с файла точно). Тёмный цвет
   надписи логотипа — #2B2A29, он же цвет основного текста: интерфейс и
   логотип написаны одной краской.

   Остальные цвета выведены из этих двух с проверкой контрастности
   (ТЗ §21: «палитра подбирается из логотипа с учётом контрастности»).
   Каждое сочетание проверено на соответствие WCAG AA — 4,5:1 для текста
   и 3:1 для элементов диаграмм:

     светлая тема            тёмная тема
     текст      14,33:1      текст      14,22:1
     приглушён   6,35:1      приглушён   6,99:1
     акцент     14,13:1      акцент      4,65:1
     «в норме»   4,58:1      «в норме»   5,95:1
     «внимание»  5,93:1      «внимание»  7,85:1
     «ошибка»    6,57:1      «ошибка»    6,32:1

   Два решения, которые не видно из значений:

   1. Фирменный зелёный #62A837 на белом даёт всего 2,93:1 — как текст он
      нечитаем. Поэтому в светлой теме он затемнён до #4C832B, а исходный
      остаётся в логотипе и на тёмном фоне, где его контраст достаточен.
   2. Индиго #322252 на тёмном фоне почти неразличим (1,23:1), поэтому там
      он осветлён до #9176C6. Оттенок сохранён — меняется только светлота.

   Зелёный служит и фирменным цветом, и цветом состояния «в норме»: это
   один и тот же смысл, разводить их значило бы завести в интерфейсе два
   зелёных, между которыми пришлось бы различать. */

/* ── Шрифт ────────────────────────────────────────────────────────────────
   Open Sans лежит файлами в `static/fonts/`: ТЗ §21 требует локальные шрифты
   и запрещает обращения к Google Fonts и другим внешним CDN. Кириллица и
   латиница разнесены по `unicode-range` — браузер качает только нужное.
   Системный шрифт остаётся запасным на случай, если файл не загрузился. */
@font-face {
  font-family: "Open Sans"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("../fonts/open-sans-cyrillic-400-normal.57eebb7fb68b.woff2") format("woff2");
  unicode-range: U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116;
}
@font-face {
  font-family: "Open Sans"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("../fonts/open-sans-latin-400-normal.289ab8c3689e.woff2") format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+FEFF,U+FFFD;
}
@font-face {
  font-family: "Open Sans"; font-style: normal; font-weight: 600; font-display: swap;
  src: url("../fonts/open-sans-cyrillic-600-normal.9ee7b4409298.woff2") format("woff2");
  unicode-range: U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116;
}
@font-face {
  font-family: "Open Sans"; font-style: normal; font-weight: 600; font-display: swap;
  src: url("../fonts/open-sans-latin-600-normal.a6d99d784ef0.woff2") format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+FEFF,U+FFFD;
}
@font-face {
  font-family: "Open Sans"; font-style: normal; font-weight: 700; font-display: swap;
  src: url("../fonts/open-sans-cyrillic-700-normal.44b3aa6b405b.woff2") format("woff2");
  unicode-range: U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116;
}
@font-face {
  font-family: "Open Sans"; font-style: normal; font-weight: 700; font-display: swap;
  src: url("../fonts/open-sans-latin-700-normal.a957c7ebe6c1.woff2") format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+FEFF,U+FFFD;
}

:root {
  color-scheme: light;
  /* Знак галочки — маска: цвет задаёт тема, рисунок один */
  --check-mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E");
  --c-bg: #f4f6f3;
  --c-surface: #ffffff;
  --c-surface-2: #edf0ea;
  --c-border: #d5dbd2;
  --c-rule: #bcc5b8;
  /* Тёмная подложка герой-карточки: фирменное индиго */
  --c-ink: #322252;
  --c-text: #2b2a29;
  --c-muted: #5c6159;
  --c-accent: #322252;
  --c-brand: #62a837;
  --c-ok: #4c832b;
  --c-warn: #8a5a00;
  --c-fail: #b42318;
  --c-series-1: #322252;
  --c-series-2: #4c832b;
  --c-series-3: #b5651d;
  /* Цвета групп в диаграммах. Зелёный сюда не входит: в системе он значит
     «в норме», и группа этого цвета читалась бы как оценка. Каждый проверен
     на 3:1 к подложке — порог WCAG для элементов диаграмм. */
  --c-series-4: #2a6f97;
  --c-series-5: #7a3b6f;
  /* Расходящаяся шкала «раньше — позже» (ADR-0024): два оттенка и серая
     середина. Проверены validate_palette.js: различимы при протанопии
     (ΔE 17,7) и обычным зрением (21,7). Смесь с подложкой светлее у слабого
     шага и насыщеннее у сильного; в тёмной теме слабее, чтобы светлый текст
     в ячейке читался. */
  --c-div-cool: #1d6fc2;
  --c-div-warm: #c0661f;
  --heat-strong: 72%;
  --heat-soft: 32%;
  /* Глобальная полоса: тёмная в обеих темах (см. .shell). Отдельные токены,
     потому что --c-ink в тёмной теме светлеет — это подложка герой-карточки. */
  --c-shell: #322252;
  --c-on-shell: #ffffff;
  --c-on-shell-dim: #c3bcd9;
  --c-on-shell-line: rgba(255, 255, 255, .16);
  --radius: 8px;
  /* Одна высота для всех кнопок и полей (решение заказчика 25.09.2026).
     Раньше её задавали отступы и межстрочный интервал: кнопка выходила 41 px,
     поле формы 40, поле фильтра 34, а ссылка-кнопка — как получится. */
  --control-h: 38px;
  --space: 16px;
  --font: "Open Sans", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --c-bg: #111015;
    --c-surface: #1a1922;
    --c-surface-2: #232231;
    --c-border: #33324a;
    --c-rule: #454463;
    --c-ink: #edebf5;
    --c-text: #e9e7f0;
    --c-muted: #a5a2b5;
    --c-accent: #9176c6;
    --c-brand: #62a837;
    --c-ok: #62a837;
    --c-warn: #e0a33a;
    --c-fail: #f2776b;
    --c-series-1: #9176c6;
    --c-series-2: #62a837;
    --c-series-3: #e08a45;
    --c-series-4: #5aa7d4;
    --c-series-5: #c98bb8;
    --c-div-cool: #4a9be0;
    --c-div-warm: #e08a45;
    --heat-strong: 58%;
    --heat-soft: 28%;
    --c-shell: #241a3c;
    --c-on-shell: #e9e7f0;
    --c-on-shell-dim: #a5a2b5;
    --c-on-shell-line: rgba(255, 255, 255, .10);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --c-bg: #111015;
  --c-surface: #1a1922;
  --c-surface-2: #232231;
  --c-border: #33324a;
  --c-rule: #454463;
  --c-ink: #edebf5;
  --c-text: #e9e7f0;
  --c-muted: #a5a2b5;
  --c-accent: #9176c6;
  --c-brand: #62a837;
  --c-ok: #62a837;
  --c-warn: #e0a33a;
  --c-fail: #f2776b;
  --c-series-1: #9176c6;
  --c-series-2: #62a837;
  --c-series-3: #e08a45;
  --c-series-4: #5aa7d4;
  --c-series-5: #c98bb8;
  --c-div-cool: #4a9be0;
  --c-div-warm: #e08a45;
  --heat-strong: 58%;
  --heat-soft: 28%;
  --c-shell: #241a3c;
  --c-on-shell: #e9e7f0;
  --c-on-shell-dim: #a5a2b5;
  --c-on-shell-line: rgba(255, 255, 255, .10);
}

* { box-sizing: border-box; }
/* Скрытое должно скрываться: правило браузера для [hidden] слабее любого
   собственного display, и поле с классом .field--inline оставалось на экране. */
[hidden] { display: none !important; }

body {
  margin: 0;
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.5;
  color: var(--c-text);
  background: var(--c-bg);
}

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

   Всё это завёрнуто в :where() — селектор с нулевым весом. Иначе `a:visited`
   весит больше, чем `.button--primary`, и у посещённой ссылки-кнопки текст
   становился тёмным на тёмно-индиговом фоне: кнопка «Добавить» после первого
   же нажатия переставала читаться. Базовое правило обязано уступать любому
   правилу элемента, а не только тем, что записаны длиннее. */
:where(a) {
  color: inherit;
  text-decoration-color: var(--c-rule);
  text-underline-offset: 2px;
}
:where(a:visited) { color: inherit; }
:where(a:hover), :where(a:focus-visible) { text-decoration-color: currentColor; }

.skip-link {
  position: absolute;
  left: -9999px;
}
.skip-link:focus {
  left: var(--space);
  top: var(--space);
  padding: 8px 12px;
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  z-index: 10;
}

/* ── Глобальная полоса (уровень 1) ────────────────────────────────────────
   Тёмная в обеих темах: полоса отвечает за систему целиком, и её цвет не
   должен зависеть от того, какую тему выбрал пользователь. Так два уровня
   навигации различаются с одного взгляда — тёмное про систему, светлое про
   текущий модуль. Разбор уровней — apps/common/navigation.py. */
.shell {
  display: flex;
  align-items: center;
  gap: 12px;
  height: 56px;
  padding: 0 var(--space);
  background: var(--c-shell);
  color: var(--c-on-shell);
  position: sticky;
  top: 0;
  z-index: 30;
}
.shell__brand { display: flex; align-items: center; text-decoration: none; }
.shell__logo { display: block; height: 26px; width: auto; }
.shell__logo--print { display: none; }
.shell__sep { width: 1px; height: 24px; background: var(--c-on-shell-line); }
.shell__spacer { flex: 1; }
.shell__module { font-weight: 600; font-size: 15px; padding: 0 4px; }

.shell__act {
  font: inherit;
  font-size: 13.5px;
  color: var(--c-on-shell-dim);
  background: transparent;
  border: 0;
  padding: 7px 10px;
  border-radius: var(--radius);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  text-decoration: none;
  white-space: nowrap;
}
.shell__act:hover { background: rgba(255, 255, 255, .10); color: var(--c-on-shell); }
.shell__act--icon { padding: 7px; }
/* Стрелка раскрытия. Девять пикселей — размер, при котором её видно, только
   если знаешь, что она есть: на полосе шапки она читалась как точка. */
.caret { font-size: 13px; line-height: 1; opacity: .9; }
.icon { width: 17px; height: 17px; display: block; fill: none; stroke: currentColor;
        stroke-width: 1.7; stroke-linecap: round; }
.icon--moon { fill: currentColor; stroke: none; }

/* Значок темы: показывается тот, в который переключит нажатие. Обе иконки в
   разметке, выбор делает CSS — иначе при первой отрисовке кнопка была бы пустой. */
.icon--sun { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .icon--moon { display: none; }
  :root:not([data-theme="light"]) .icon--sun { display: block; }
}
:root[data-theme="dark"] .icon--moon { display: none; }
:root[data-theme="dark"] .icon--sun { display: block; }
:root[data-theme="light"] .icon--moon { display: block; }
:root[data-theme="light"] .icon--sun { display: none; }

/* Переключатель модуля и выпадающие меню — на <details>, без скриптов. */
.switch, .menu { position: relative; }
.switch > summary, .menu > summary { list-style: none; cursor: pointer; }
.switch > summary::-webkit-details-marker,
.menu > summary::-webkit-details-marker { display: none; }
.switch > summary {
  display: flex; align-items: center; gap: 6px;
  padding: 7px 10px; border-radius: var(--radius);
  font-weight: 600; font-size: 15px;
}
.switch > summary:hover { background: rgba(255, 255, 255, .10); }
.switch[open] > summary, .menu[open] > .shell__act { background: rgba(255, 255, 255, .14); }

.switch__panel, .menu__panel {
  position: absolute; top: calc(100% + 8px); z-index: 40;
  background: var(--c-surface); color: var(--c-text);
  border: 1px solid var(--c-border); border-radius: var(--radius);
  box-shadow: 0 18px 44px -14px rgba(20, 16, 40, .45);
  padding: 6px;
}
.switch__panel { left: 0; min-width: 300px; }
.menu__panel { right: 0; width: 264px; }
.switch__item {
  display: block; padding: 9px 10px; border-radius: 7px; text-decoration: none;
}
.switch__item:hover { background: var(--c-surface-2); }
.switch__item[aria-current] { background: var(--c-surface-2); }
.switch__item--soon { opacity: .65; cursor: default; }
.switch__name { display: block; font-weight: 600; }
.switch__note { display: block; font-size: 12px; color: var(--c-muted); line-height: 1.35; }
.switch__foot {
  display: block; margin-top: 6px; padding: 9px 10px 5px;
  border-top: 1px solid var(--c-border);
  font-size: 12px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--c-muted); text-decoration: none;
}
.menu__group {
  font-size: 11px; text-transform: uppercase; letter-spacing: .08em;
  color: var(--c-muted); padding: 8px 10px 4px; margin: 0;
}
.menu__link {
  display: block; width: 100%; text-align: left;
  padding: 7px 10px; border-radius: 7px; text-decoration: none; font-size: 14px;
}
.menu__link:hover { background: var(--c-surface-2); }
/* Счётчик несделанного. Не тревожный красный: восемь неразобранных
   уведомлений — обычная рабочая очередь, а не авария. Цвет здесь подсказка,
   смысл несёт само число. */
.badge {
  display: inline-block; min-width: 18px; margin-left: 7px; padding: 0 5px;
  border-radius: 999px; background: var(--c-warn); color: #fff;
  font-family: var(--font-mono); font-size: 11px; line-height: 18px;
  text-align: center; vertical-align: 1px;
}
.menu__link .badge { float: right; margin-left: 10px; }
.menu__link[aria-current] { color: var(--c-accent); font-weight: 600; }
.menu__link--button { font: inherit; font-size: 14px; border: 0; background: transparent;
                      color: inherit; cursor: pointer; }

/* Вкладки внутри раздела — третий уровень: «Файлы» и «Показания одометра»
   внутри «Загрузки данных». Отличаются от строки модуля намеренно: подчёркнутая
   вкладка и приглушённый фон, чтобы уровни не путались между собой. */
.subtabs {
  display: flex; gap: 4px; flex-wrap: wrap;
  margin: 0 0 var(--space);
  border-bottom: 1px solid var(--c-border);
}
.subtabs a {
  padding: 8px 12px; text-decoration: none; font-size: 14px;
  color: var(--c-muted); border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.subtabs a:hover { color: var(--c-text); }
.subtabs a[aria-current] {
  color: var(--c-accent); font-weight: 600; border-bottom-color: var(--c-accent);
}

/* Узкий экран: полоса перестаёт помещаться в одну строку и уезжает вбок
   вместе со всей страницей. Пусть переносится, а строка модуля перестаёт
   липнуть — её отступ считался от высоты полосы в одну строку. */
@media (max-width: 760px) {
  .shell {
    height: auto;
    flex-wrap: wrap;
    padding: 8px var(--space);
    row-gap: 6px;
    position: static;
  }
  .shell__spacer { flex-basis: 100%; height: 0; }
  .modulebar { position: static; }
}

/* ── Строка модуля (уровень 2) ───────────────────────────────────────────── */
.modulebar {
  display: flex; align-items: stretch; gap: 2px;
  padding: 0 var(--space);
  background: var(--c-surface);
  border-bottom: 1px solid var(--c-border);
  position: sticky; top: 56px; z-index: 20;
  overflow-x: auto;
}
.modulebar a {
  display: flex; align-items: center; height: 44px; padding: 0 12px;
  color: var(--c-muted); text-decoration: none; font-size: 14px; white-space: nowrap;
  border-bottom: 2px solid transparent;
}
.modulebar a:hover { color: var(--c-text); }
.modulebar a[aria-current] {
  color: var(--c-accent); font-weight: 600; border-bottom-color: var(--c-accent);
}

.theme-toggle {
  font: inherit;
  padding: 4px 10px;
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  background: transparent;
  color: var(--c-text);
  cursor: pointer;
}
.theme-toggle:hover { border-color: var(--c-accent); }

/* ── Хлебные крошки ───────────────────────────────────────────────────────
   Показываются там, где есть куда возвращаться: на странице раздела путь и
   так виден в строке модуля. Разделитель рисуется псевдоэлементом — тогда он
   не попадает в выделение и не читается скринридером как слово. */
.crumbs {
  max-width: 1200px; margin: 0 auto;
  padding: 10px var(--space) 0;
  font-size: 13px; color: var(--c-muted);
}
.crumbs ol { display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
             margin: 0; padding: 0; list-style: none; }
.crumbs li { display: flex; align-items: center; gap: 6px; min-width: 0; }
.crumbs li + li::before { content: "/"; color: var(--c-rule); }
.crumbs a { color: var(--c-muted); text-decoration: none; }
.crumbs a:hover { color: var(--c-accent); text-decoration: underline; }
.crumbs [aria-current] {
  color: var(--c-text); font-weight: 600;
  max-width: 46ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.app-main { max-width: 1200px; margin: 0 auto; padding: var(--space); }
.app-footer {
  max-width: 1200px; margin: 0 auto; padding: var(--space);
  color: var(--c-muted); font-size: 13px;
}

h1 { font-size: 22px; margin: 0 0 8px; }
h2 { font-size: 17px; margin: 0 0 8px; }
.lead { color: var(--c-muted); margin-top: 0; }

.card {
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  padding: var(--space);
  margin-top: var(--space);
}

.plain-list { margin: 0; padding-left: 18px; }
code { font-family: var(--font-mono); font-size: 13px; }

.banner {
  margin: 0;
  padding: 10px var(--space);
  border-bottom: 1px solid var(--c-border);
  font-size: 14px;
}
.banner--warn { background: color-mix(in srgb, var(--c-warn) 12%, var(--c-surface)); }

@media print {
  .shell__act, .switch, .menu, .modulebar, .crumbs, .skip-link,
  .progress, .subtabs { display: none; }
  body { background: #fff; color: #000; }
  /* Индиго-полоса на бумаге — залитый краской прямоугольник. На печати она
     белая, поэтому логотип меняется на версию для светлого фона. */
  .shell { background: #fff; color: #000; border-bottom: 1px solid #999; position: static; }
  .shell__logo--screen { display: none; }
  .shell__logo--print { display: block; }
}

/* ───────────────── Этап 2: вход, навигация, каталог модулей, журнал ───── */

/* ── Ход импорта ──────────────────────────────────────────────────────────
   Шаги, а не проценты: доля разобранных строк не сказала бы главного — что
   после разбора файл ещё не в системе и остался шаг, который делает человек.
   Состояние шага несёт не только цвет: у каждого свой знак (✓, номер, !, ✕). */
.progress {
  margin: 0 0 var(--space);
  padding: 14px 16px 12px;
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
}
.progress__steps {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0;
  margin: 0; padding: 0; list-style: none;
}
.progress__step {
  display: flex; align-items: center; gap: 8px;
  font-size: 13px; color: var(--c-muted); white-space: nowrap;
}
.progress__step + .progress__step::before {
  content: ""; width: 26px; height: 1px; margin: 0 10px;
  background: var(--c-rule); flex: none;
}
.progress__mark {
  width: 22px; height: 22px; border-radius: 999px; flex: none;
  display: grid; place-items: center;
  font-size: 11px; font-family: var(--font-mono);
  border: 1px solid var(--c-border); background: var(--c-surface-2);
  color: var(--c-muted);
}
.progress__step--done .progress__mark {
  background: var(--c-ok); border-color: var(--c-ok); color: var(--c-surface);
}
.progress__step--done .progress__title { color: var(--c-text); }
.progress__step--running .progress__mark {
  background: var(--c-accent); border-color: var(--c-accent); color: var(--c-surface);
}
.progress__step--waiting .progress__mark {
  background: var(--c-warn); border-color: var(--c-warn); color: var(--c-surface);
}
.progress__step--failed .progress__mark {
  background: var(--c-fail); border-color: var(--c-fail); color: var(--c-surface);
}
.progress__step--running .progress__title,
.progress__step--waiting .progress__title { color: var(--c-text); font-weight: 600; }

.progress__now {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin: 12px 0 0; padding-top: 10px; border-top: 1px solid var(--c-border);
  font-size: 13px;
}
.progress__hint { color: var(--c-muted); }
/* Бегунок: работа идёт прямо сейчас. Замер движения выключает и его — но
   тогда состояние всё равно видно по знаку шага и по подписи. */
.progress__pulse {
  width: 34px; height: 4px; border-radius: 2px; flex: none;
  background: linear-gradient(90deg, var(--c-surface-2) 0 40%,
                              var(--c-accent) 40% 60%, var(--c-surface-2) 60%);
  background-size: 250% 100%;
  animation: progress-run 1.1s linear infinite;
}
@keyframes progress-run { from { background-position: 100% 0; } to { background-position: 0 0; } }
@media (prefers-reduced-motion: reduce) { .progress__pulse { animation: none; } }
@media (max-width: 620px) {
  .progress__step + .progress__step::before { width: 12px; margin: 0 6px; }
  .progress__step { font-size: 12px; }
}

/* --- Ожидание результата (1.35.0) ---
   После «Посмотреть сейчас» — окно поверх размытой страницы: проверка в
   очереди, страница обновится сама. Раньше было всплывающее «Обновите
   страницу», хотя страница и так обновлялась. */
.spinner {
  display: inline-block; flex: none; width: 14px; height: 14px;
  border: 2px solid var(--c-border); border-top-color: var(--c-accent);
  border-radius: 50%; animation: spin .9s linear infinite;
}
.spinner--large { width: 34px; height: 34px; border-width: 3px; }
.status .spinner { width: 11px; height: 11px; margin-right: 6px; vertical-align: -1px; }
@keyframes spin { to { transform: rotate(360deg); } }
.wait-note {
  display: flex; align-items: center; gap: 10px;
  margin: 12px 0 0; font-size: 13px; color: var(--c-muted);
}
.wait-dialog {
  width: min(480px, calc(100vw - 32px)); max-width: none; padding: 24px 24px 20px;
  border: 1px solid var(--c-border); border-radius: 16px;
  background: var(--c-surface); color: var(--c-text);
  box-shadow: 0 24px 60px rgba(0, 0, 0, .28), 0 2px 8px rgba(0, 0, 0, .12);
}
.wait-dialog[open] { animation: wait-in .22s cubic-bezier(.2, .8, .2, 1); }
/* Цвет подложки — не переменной темы: в ::backdrop они доходят не во всех
   браузерах. Полупрозрачный тёмный подходит обеим темам. */
.wait-dialog::backdrop {
  background: rgba(18, 14, 30, .38);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  animation: wait-fade .22s ease-out;
}
@keyframes wait-in { from { opacity: 0; transform: translateY(8px) scale(.98); } }
@keyframes wait-fade { from { opacity: 0; } }
.wait-dialog__head { display: flex; align-items: center; gap: 14px; margin-bottom: 16px; }
.wait-dialog__head h2 { margin: 0; font-size: 19px; line-height: 1.3; }
.wait-dialog__kind { margin: 2px 0 0; font-size: 13px; color: var(--c-muted); }
#wait-text { margin: 0 0 14px; }
.wait-dialog__slow { display: none; }
.wait-dialog.is-slow .wait-dialog__usual { display: none; }
.wait-dialog.is-slow .wait-dialog__slow { display: inline; color: var(--c-warn); }
.wait-dialog__progress {
  height: 6px; border-radius: 3px; background: var(--c-surface-2); overflow: hidden;
}
.wait-dialog__progress > span {
  display: block; width: 0; height: 100%; border-radius: inherit;
  background: var(--c-accent); transition: width 1s linear;
}
/* Дольше двух минут полоса стояла бы у края — «почти готово», хотя конца
   не видно. Тогда она бежит, не обещая, сколько осталось. */
.wait-dialog.is-slow .wait-dialog__progress > span {
  width: 30% !important; transition: none;
  animation: wait-slide 1.6s ease-in-out infinite;
}
@keyframes wait-slide { from { transform: translateX(-100%); } to { transform: translateX(340%); } }
.wait-dialog__time {
  margin: 6px 0 16px; font-size: 12px; color: var(--c-muted);
  font-variant-numeric: tabular-nums;
}
.wait-dialog__hint {
  margin: 0; padding-top: 14px; border-top: 1px solid var(--c-border);
  font-size: 13px; color: var(--c-muted);
}
.wait-dialog .form-actions { width: 100%; margin-top: 16px; }
@media (prefers-reduced-motion: reduce) {
  .spinner { animation-duration: 2.4s; }
  .wait-dialog[open], .wait-dialog::backdrop { animation: none; }
  .wait-dialog__progress > span { transition: none; }
  .wait-dialog.is-slow .wait-dialog__progress > span {
    width: 100% !important; animation: none; opacity: .4;
  }
}

/* --- Формы --- */
.field { display: block; margin-bottom: 14px; }
.field__label { display: block; font-size: 13px; color: var(--c-muted); margin-bottom: 4px; }
.field__required { color: var(--c-fail); font-style: normal; margin-left: 3px; }

/* Перечислять типы полей по одному нельзя: раньше рамку получали только text,
   password и select, а адрес почты, дата, число и многострочное поле
   оставались без оформления — на одном экране получались поля двух видов.
   Исключены только те, у кого своя форма: галочки, переключатели и файл. */
.field input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
.field textarea,
.field select {
  width: 100%;
  padding: 9px 11px;
  font: inherit;
  line-height: 1.4;
  color: var(--c-text);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
}
.field input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
.field select { height: var(--control-h); padding-top: 0; padding-bottom: 0; }
.field textarea { min-height: 72px; resize: vertical; }
/* Числовые поля без стрелок. Стрелки у поля «Год» позволяли увести значение в
   отрицательные числа, а у объёма и суммы они бесполезны: значение набирают
   или вставляют, а не докручивают. Заодно все числовые поля выглядят
   одинаково, чем бы они ни были в разметке. */
.field input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.field input[type="number"]::-webkit-outer-spin-button,
.field input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none; margin: 0;
}
.field input:hover:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
.field textarea:hover, .field select:hover { border-color: var(--c-rule); }
.field input:focus, .field textarea:focus, .field select:focus {
  outline: 2px solid var(--c-accent);
  outline-offset: 1px;
  border-color: var(--c-accent);
}
.field input:disabled, .field textarea:disabled, .field select:disabled {
  background: var(--c-surface-2); color: var(--c-muted); cursor: not-allowed;
}
.field--invalid input, .field--invalid textarea, .field--invalid select {
  border-color: var(--c-fail);
}

/* Галочка стоит перед подписью и в одной с ней строке. Подпись блоком над
   галочкой роняла саму галочку на следующую строку. */
.field--check {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 4px 9px;
  cursor: pointer;
}
.field--check input[type="radio"] {
  width: 16px; height: 16px; margin: 0; accent-color: var(--c-accent); cursor: pointer;
}
/* Галочка одна на всю систему (1.34.1). Список галочек из Django оставался
   браузерным — 13 px и голубым («Зимние месяцы» в настройках логистики)
   рядом с фиолетовыми 16 px, а галочка строки фильтров нарисована сама.
   Теперь каждая галочка рисуется как в строке фильтров: 16 px, скругление
   4 px, отмеченная — фирменного цвета. В режиме высокой контрастности
   браузер рисует свою: своя там не видна. */
input[type="checkbox"] {
  -webkit-appearance: none; appearance: none;
  flex: none; display: inline-grid; place-content: center;
  width: 16px; height: 16px; margin: 0; vertical-align: -3px;
  border: 1.5px solid var(--c-rule); border-radius: 4px;
  background: transparent; color: var(--c-surface); cursor: pointer;
}
input[type="checkbox"]::before {
  content: ""; width: 12px; height: 12px; background: currentColor; visibility: hidden;
  -webkit-mask: var(--check-mark) center / contain no-repeat;
  mask: var(--check-mark) center / contain no-repeat;
}
input[type="checkbox"]:checked { background: var(--c-accent); border-color: var(--c-accent); }
input[type="checkbox"]:checked::before { visibility: visible; }
input[type="checkbox"]:hover:not(:disabled) { border-color: var(--c-accent); }
input[type="checkbox"]:focus-visible { outline: 2px solid var(--c-accent); outline-offset: 2px; }
input[type="checkbox"]:disabled { opacity: .45; cursor: not-allowed; }
@media (forced-colors: active) {
  input[type="checkbox"] { -webkit-appearance: auto; appearance: auto; }
}
.field--check .field__label { grid-column: 2; margin: 0; color: var(--c-text); }
.field--check .field__help, .field--check .field__error { grid-column: 2; margin-top: 0; }

.field--inline { display: inline-block; width: auto; margin-right: 12px; }
/* Галочка в строке фильтров: раскладка галочки поверх «в строку». */
.field--check.field--inline { display: inline-grid; }
.field__error { display: block; margin-top: 4px; color: var(--c-fail); font-size: 13px; }

.button {
  font: inherit;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: var(--control-h);
  padding: 0 16px;
  line-height: 1.2;
  white-space: nowrap;
  vertical-align: middle;
  text-decoration: none;
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  background: var(--c-surface);
  color: var(--c-text);
  cursor: pointer;
}
.button:hover { border-color: var(--c-accent); }
.button--primary {
  background: var(--c-accent);
  border-color: var(--c-accent);
  color: #fff;
}

.form-error {
  padding: 10px 12px;
  margin-bottom: var(--space);
  border: 1px solid var(--c-fail);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--c-fail) 10%, var(--c-surface));
}
.form-error p { margin: 0; }

/* --- Вход --- */
.login { max-width: 420px; margin: 40px auto; }
.login .banner { border: 1px solid var(--c-border); border-radius: var(--radius); }
.login__hint { margin-top: var(--space); font-size: 13px; color: var(--c-muted); }
.banner--notice { background: color-mix(in srgb, var(--c-accent) 8%, var(--c-surface)); }

/* --- Плитки модулей --- */
.tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--space);
  margin-top: var(--space);
}
/* Плитки в ряду одной высоты, а кнопка прижата к низу: описания разной
   длины, и «Открыть» стояла у каждой плитки на своей высоте (замечание
   заказчика 26.09.2026). Кнопка — обычная основная, 38 px, как везде. */
.tile {
  display: flex; flex-direction: column; align-items: flex-start;
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  padding: var(--space);
}
.tile--pending { opacity: 0.75; }
.tile__title { font-size: 16px; margin: 0 0 6px; }
.tile__text { color: var(--c-muted); font-size: 14px; margin: 0 0 12px; }
.tile__link, .tile__status { margin-top: auto; }
.tile__status { font-size: 13px; color: var(--c-muted); }

/* Для тех, кто читает экран не глазами: подпись есть, места не занимает. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* --- Матрица шаблонов ролей --- */
/* Пять карточек со списками отвечали на вопрос «что даёт роль», но не на тот,
   ради которого сюда заходят: «чем одна роль отличается от другой». Матрица
   отвечает взглядом — при условии, что и шапка, и колонка разрешений остаются
   на месте при прокрутке. */
.matrix { min-width: 720px; }
.matrix__role { text-align: center; vertical-align: bottom; }
.matrix__name { display: block; }
.matrix__count {
  display: block; font-weight: 400; font-size: 11px; color: var(--c-muted);
  font-family: var(--font-mono);
}
.matrix__permission { text-align: left; font-weight: 400; }
.matrix__permission code { display: block; font-size: 11px; }
.matrix__cell { text-align: center; font-size: 15px; }
.matrix__yes { color: var(--c-ok); }
.matrix__no { color: var(--c-border); }
/* Группа разрешений — заголовок во всю ширину, как в матрице пользователя.
   Селектор с запасом по весу: правило закреплённой первой колонки тоже метит
   в этот `th` (он первый в строке), а заголовок на всю ширину закреплять
   не за чем — он и так виден целиком. */
.table-scroll tr.matrix__group th {
  position: static;
  text-align: left; padding-top: 14px;
  font-size: 12px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--c-muted); background: var(--c-surface-2);
}
/* Строка, одинаковая у всех ролей, разницы не показывает: внимание ей ни к
   чему. Приглушена, но не спрятана — перечень обязан быть полным. */
.matrix__row--same .matrix__permission,
.matrix__row--same .matrix__cell { opacity: .55; }

/* --- Таблицы --- */
.table-scroll { overflow-x: auto; margin-top: var(--space); }
.data-table {
  width: 100%;
  border-collapse: collapse;
  background: var(--c-surface);
  font-size: 14px;
}
.data-table th, .data-table td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--c-border);
  text-align: left;
  vertical-align: top;
}
.data-table thead th {
  position: sticky;
  top: 0;
  background: var(--c-surface);
  border-bottom: 2px solid var(--c-border);
  font-weight: 600;
  white-space: nowrap;
}
/* Первая колонка держится на месте при боковой прокрутке: в широкой таблице
   без неё через экран вправо непонятно, чья это строка. Правило общее для всех
   таблиц: узкая таблица вбок не едет, и закрепление в ней ничего не меняет, а
   на телефоне вбок едет любая.

   Разделитель и тень появляются только когда таблицу действительно сдвинули
   (класс ставит static/js/table-scroll.js): иначе в нетронутой таблице
   получалась бы лишняя вертикальная линия.

   Уголок «шапка × первая колонка» перекрывает оба слоя, поэтому z-index у него
   выше: иначе он проезжает под ячейками. */
.table-scroll th:first-child,
.table-scroll td:first-child {
  position: sticky;
  left: 0;
  background: var(--c-surface);
  z-index: 1;
}
.table-scroll thead th:first-child { z-index: 3; }
.table-scroll.is-scrolled th:first-child::after,
.table-scroll.is-scrolled td:first-child::after {
  content: "";
  position: absolute; top: 0; right: -6px; bottom: -1px; width: 6px;
  background: linear-gradient(90deg, rgba(20, 16, 40, .10), transparent);
  border-left: 1px solid var(--c-border);
}
/* Строка под курсором подсвечивается целиком, вместе с закреплённой ячейкой. */
.table-scroll tbody tr:hover td:first-child { background: var(--c-surface-2); }
.table-scroll tr.row--muted td:first-child { background: var(--c-surface); }

.nowrap { white-space: nowrap; }
.muted { color: var(--c-muted); font-size: 13px; }
.change__pair code { word-break: break-all; }
.pager { display: flex; gap: 16px; align-items: center; margin-top: var(--space); }

/* --- Экраны управления доступом --- */
/* Кнопки шапки — справа, на уровне заголовка. Раньше длинное пояснение
   под заголовком выталкивало кнопку под текст, влево («Уведомления»), а при
   коротком она стояла справа посередине блока. */
.page-head {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px var(--space);
}
/* Текст шапки не уже 360 px: кнопки, которым рядом не хватает места,
   уходят под текст. Без переноса четыре кнопки «Проверок смен» сжимали
   заголовок и пояснение в столбик шириной в слово (1.34.1). */
.page-head > :first-child { flex: 1 1 360px; min-width: 0; }
.page-head > :not(:first-child) { flex: none; max-width: 100%; margin-top: -3px; }
/* Строка действий под пояснением: подпись и группа кнопок */
.action-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px;
  margin: 0 0 var(--space);
}
.action-row__label { color: var(--c-muted); font-size: 13px; }
@media (max-width: 760px) {
  /* На телефоне элементы справа уходят под заголовок и занимают всю ширину:
     иначе «Дашборд месяца» уезжал за край экрана. */
  .page-head > :not(:first-child) { flex: 1 1 100%; margin-top: 0; }
}
.form-card { max-width: 560px; }
/* Поля формы внутри широкой карточки — той же ширины, что в карточке-форме:
   поле «Отчество» на 1150 px читалось как ошибка вёрстки. */
.card form:not(.inline-form) .field, form.card .field { max-width: 560px; }
.field__help { display: block; margin-top: 4px; font-size: 12px; color: var(--c-muted); }
/* Кнопки одной группы — одинаковой ширины, по самой длинной надписи
   (решение заказчика 25.09.2026). Форма вокруг кнопки прозрачна для сетки:
   кнопка «Отключить» в своей <form> встаёт в ряд как соседние. На узком
   экране кнопки идут столбиком во всю ширину. */
.form-actions { margin-top: var(--space); }
.form-actions, .button-group {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  justify-content: start; align-items: center; gap: 10px;
  width: max-content; max-width: 100%;
}
.form-actions > form, .button-group > form { display: contents; }
.form-actions .button, .button-group .button { width: 100%; }
@media (max-width: 640px) {
  .form-actions, .button-group { grid-auto-flow: row; width: 100%; }
}
/* Форма в строке таблицы: поле и кнопка одной высоты и на одной линии.
   Раньше поля здесь были родными браузерными — 21–24 px рядом с кнопкой
   в 38 px («Модули», «Товары», «Пороги аномалий», «Каналы»). */
.inline-form { display: inline-flex; align-items: center; gap: 6px; margin: 0; }
.inline-form input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]),
.inline-form select,
.data-table td > input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]),
.data-table td > select {
  height: var(--control-h); padding: 0 9px; font: inherit; font-size: 14px;
  color: var(--c-text); background: var(--c-surface);
  border: 1px solid var(--c-border); border-radius: var(--radius);
  min-width: 0;
}
.inline-form input:focus, .inline-form select:focus,
.data-table td > input:focus, .data-table td > select:focus { outline: 2px solid var(--c-accent); outline-offset: -1px; }
/* В строке с такой формой всё по середине строки: иначе текст соседних ячеек
   стоял у верхнего края, а поле и кнопка — ниже. */
.data-table tr:has(> td > .inline-form) > td { vertical-align: middle; }
/* Несколько таблиц одна под другой с одинаковыми столбцами — ширины заданы
   <colgroup>, и столбцы всех таблиц стоят на одних местах. */
.data-table--aligned { table-layout: fixed; width: 100%; min-width: 720px; }
/* Таблица «название — значение» (строки из двух ячеек: th и td): столбец
   названий одной ширины во всех блоках страницы — иначе в «Файле», «Периоде»
   и «Сводке» импорта значения начинались каждый раз на новом месте. */
.data-table tbody tr:has(> th:first-child + td:last-child) > th { width: 240px; }

/* --- «Чистая логистика» ---
   Текст оповещения показывается так, как его получит человек в мессенджере:
   с переносами строк и без разметки. */
.alert-text {
  white-space: pre-wrap; font-family: var(--font); font-size: 14px; line-height: 1.5;
  margin: 0; padding: 12px 14px; border-radius: var(--radius);
  background: var(--c-surface-2); border: 1px solid var(--c-border);
}
.checks > div { display: flex; flex-wrap: wrap; gap: 8px 18px; margin-top: 4px; }
/* Отступ от галочки до подписи — как у одиночной галочки формы */
.checks label { display: inline-flex; align-items: center; gap: 9px; cursor: pointer; }
fieldset.field { border: 0; padding: 0; margin: 0 0 var(--space); min-width: 0; }
.inline-check { display: inline-flex; align-items: center; gap: 9px; margin-right: 12px;
                font-size: 13px; }
.big-code { font-family: var(--font-mono); font-size: 22px; letter-spacing: .12em; }
/* Настройки модуля блоками (1.44.0, заказчик 08.10.2026): блок — один вид
   оповещения со всем, что его настраивает. Короткие поля стоят в ряд, галочки,
   списки галочек и подзаголовки — во всю ширину блока. Подпись поля в одну
   строку: иначе поля одного ряда стояли бы на разной высоте. */
.settings-block__head {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: 4px 16px; margin-bottom: 12px;
}
.settings-block__head h2 { margin: 0; }
.settings-block__head a { font-size: 13px; }
.settings-fields {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
  gap: 0 24px; align-items: start; max-width: 1020px;
}
.settings-fields > .field--check, .settings-fields > fieldset.field,
.settings-fields > h3 { grid-column: 1 / -1; }
.settings-fields > h3 { font-size: 14px; margin: 4px 0 10px; }
.settings-fields > h3:not(:first-child) { margin-top: 10px; }
.settings-fields > .field:not(.field--check) > .field__label {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.settings-fields > .field--check { max-width: 640px; }
.settings-block--save .field { max-width: 640px; }
.settings-title { margin: 28px 0 0; font-size: 19px; }
/* Мессенджеры (1.44.0): строка — кто, что приходит лично, действия. Кнопки
   стоят в своей колонке справа и у всех строк на одном месте: раньше
   «Сохранить» шла следом за галочками и съезжала от строки к строке
   (заказчик 08.10.2026). */
.people { margin: 12px 0 0; }
.people + .banner, .people + .form-actions { margin-top: var(--space); }
.person {
  display: grid; grid-template-columns: minmax(170px, 230px) minmax(0, 1fr) auto;
  gap: 10px 24px; align-items: center;
  padding: 12px 0; border-top: 1px solid var(--c-border);
}
.person:last-child { border-bottom: 1px solid var(--c-border); }
.person__who { display: grid; gap: 2px; min-width: 0; }
.person__who span { font-size: 13px; color: var(--c-muted); overflow-wrap: anywhere; }
.person__text { margin: 0; }
.person__kinds {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(230px, 100%), 1fr));
  gap: 8px 18px; margin: 0;
}
.person__kinds label { display: flex; align-items: center; gap: 9px; cursor: pointer; }
@media (max-width: 900px) {
  .person { grid-template-columns: 1fr; }
}
/* Каналы уведомлений (1.44.1): канал — блок с галочками видов по группам,
   названием, уровнем и кнопками. Длинные названия системных поводов не
   помещаются в колонку 230 px, поэтому сетка шире. */
.person__kinds--wide { grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); }
.channel { padding: 14px 0 18px; border-top: 1px solid var(--c-border); margin: 0; }
.channel:first-of-type { margin-top: 12px; }
.channel__head {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 6px 16px; margin-bottom: 10px;
}
.channel__group { border: 0; padding: 0; margin: 0 0 14px; min-width: 0; }
.channel__group legend { padding: 0; margin-bottom: 8px; font-size: 13px; color: var(--c-muted); }
.channel .settings-fields { margin-top: 4px; }
.channel .form-actions { margin-top: 4px; }

/* Загрузка прессов (1.46.0, заказчик 12.10.2026). Строка читается слева
   направо: машина и вывод словами, полоса её дня на фоне дня группы, два рейса
   в долях полного, итог недели. Фон группы стоит у всех строк на одном месте:
   полоса, вышедшая за него вправо, и полоса, не дошедшая до его края, видны
   без чтения чисел. Колонки заданы один раз: шапка и строки не разъезжаются. */
.wl { --wl-cols: 168px minmax(240px, 1fr) 196px 178px 204px; }
.wl-head, .wl-row {
  display: grid; grid-template-columns: var(--wl-cols); gap: 6px 16px; align-items: center;
}
.wl-head {
  padding: 2px 8px 6px; font-family: var(--font-cond); font-weight: 600; font-size: 10.5px;
  letter-spacing: .08em; text-transform: uppercase; color: var(--c-muted);
}
.wl-head--days { margin-top: 14px; }
.wl-axis { position: relative; height: 14px; }
.wl-axis i {
  position: absolute; top: 0; transform: translateX(-50%); font-style: normal;
  font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0; white-space: nowrap;
}
/* Крайние подписи не вылезают в соседние колонки */
.wl-axis i:first-child { transform: none; }
.wl-axis i:last-child { transform: translateX(-100%); }
.wl-section + .wl-section { margin-top: 16px; }
.wl-section .rank-group__cap { margin: 0 0 2px; padding: 0 8px 6px; }
.wl-row { padding: 8px; border-bottom: 1px solid var(--c-border); }
.wl-row:hover, .wl-row:focus { background: var(--c-surface-2); outline: none; }
.wl-row.is-selected { box-shadow: inset 3px 0 0 var(--c-accent); background: var(--c-surface-2); }
.wl-section--normal .wl-name .status, .wl-section--few .wl-name .status { font-weight: 400; }
.wl-name { display: grid; gap: 2px; min-width: 0; }
.wl-name a, .wl-name b { font-family: var(--font-mono); font-size: 13px; font-weight: 600;
                         color: var(--c-text); white-space: nowrap; }
.wl-name a { text-decoration: none; }
.wl-name a:hover { text-decoration: underline; }
.wl-name .status { font-size: 12px; font-weight: 600; }
/* Дорожка: линии часов, поверх — день группы и полоса машины */
.wl-track { position: relative; height: 24px; background: var(--c-surface-2); }
.wl-row:hover .wl-track, .wl-row.is-selected .wl-track { background: var(--c-surface); }
.wl-track--off { opacity: .45; }
.wl-grid { position: absolute; top: 0; bottom: 0; border-left: 1px solid var(--c-border); }
.wl-band { position: absolute; top: 0; bottom: 0;
           background: color-mix(in srgb, var(--c-series-4) 26%, transparent); }
/* Цвет полосы — цвет группы машин, как на остальных дашбордах: без модификатора
   серии это первая серия */
.wl-bar { position: absolute; top: 8px; height: 8px; background: var(--c-series-1); }
.wl-bar--out { background: var(--c-warn) !important; }
.wl-tick { position: absolute; top: 3px; bottom: 3px; width: 2px; margin-left: -1px;
           background: var(--c-text); box-shadow: 0 0 0 1px var(--c-surface); }
.wl-text { display: grid; gap: 1px; min-width: 0; font-size: 12.5px; }
.wl-text b { font-family: var(--font-mono); font-weight: 600; font-variant-numeric: tabular-nums; }
.wl-text span { color: var(--c-muted); font-size: 12px; }
.wl-diff { color: var(--c-muted); font-weight: 400; white-space: nowrap; }
.wl-diff--marked { color: var(--c-warn); font-weight: 600; }
/* Рейсы: дорожка — полный рейс машины, пунктир — порог малого рейса */
.wl-trips { display: grid; gap: 3px; min-width: 0; }
.wl-trip { display: grid; grid-template-columns: 10px 1fr 34px; gap: 6px; align-items: center;
           font-family: var(--font-mono); font-size: 11.5px; font-variant-numeric: tabular-nums; }
.wl-trip__no { color: var(--c-muted); }
.wl-trip__track { position: relative; height: 8px; background: var(--c-surface-2); }
.wl-row:hover .wl-trip__track, .wl-row.is-selected .wl-trip__track { background: var(--c-surface); }
.wl-trip__track::after {
  content: ""; position: absolute; top: -2px; bottom: -2px; left: var(--wl-limit, 60%);
  border-left: 1px dashed var(--c-muted);
}
.wl-trip__bar { display: block; height: 8px; background: var(--c-series-1); }
.wl-trip__bar--small { background: var(--c-warn) !important; }
.wl-trip__tons { text-align: right; }
.wl-trips__full { font-size: 11px; color: var(--c-muted); }
.wl-weights { display: flex; flex-wrap: wrap; gap: 2px 10px; font-family: var(--font-mono);
              font-size: 12.5px; font-variant-numeric: tabular-nums; }
.wl-weights__small { color: var(--c-warn); font-weight: 600; }
.wl-flags { display: flex; flex-wrap: wrap; gap: 3px 10px; font-size: 12px; color: var(--c-warn);
            font-weight: 600; }
.wl-flags__ok { color: var(--c-muted); font-weight: 400; }
.wl-row--off .wl-name b { color: var(--c-muted); font-weight: 400; }
.wl-hint { margin: 0 0 12px; padding: 9px 12px; background: var(--c-surface-2); font-size: 13.5px; }
.wl-more { margin-top: 18px; }
.wl-more h3 { font-family: var(--font-cond); font-weight: 700; font-size: 14px; margin: 0 0 6px; }
.wl-more .table-scroll { margin-top: 0; }
/* Пояснения в таблицах под днями читаются целиком: компактная таблица иначе
   обрезает длинную ячейку многоточием */
.wl-more .data-table td.wl-wrap { white-space: normal; overflow: visible; max-width: none; }
.wl-foot { margin: 12px 2px 0; font-size: 12.5px; color: var(--c-muted); }
.wl-week { cursor: default; }
.swatch.wl-swatch-band { background: color-mix(in srgb, var(--c-series-4) 26%, transparent);
                         box-shadow: inset 0 0 0 1px var(--c-rule); }
.swatch.wl-swatch-out, .swatch.wl-swatch-small { background: var(--c-warn); }
.workload-board #machine { scroll-margin-top: 110px; }
@media (max-width: 1080px) {
  /* Узкий экран: машина и итог недели в первой строке, полоса во всю ширину,
     время и рейсы под ней */
  .wl-head { display: none; }
  .wl-row { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .wl-row > .wl-track { grid-column: 1 / -1; order: 2; }
  .wl-row > .wl-name { order: 0; }
  .wl-row > .wl-text:last-child, .wl-row > .wl-flags { order: 1; text-align: right; justify-content: flex-end; }
  .wl-row > .wl-text { order: 3; }
  .wl-row > .wl-trips, .wl-row > .wl-weights { order: 4; }
}

/* Время работы: машины × дни. Ячейки узкие и одинаковые — глаз сравнивает
   колонку дня сверху вниз и строку машины слева направо. */
.worktime th, .worktime td { padding: 6px 7px; }
/* Таблица по содержимому, как «Выполнение по часам»: в начале месяца два дня
   растягивались на всю ширину панели (заказчик 02.10.2026). Длинный период
   шире панели и по-прежнему прокручивается вбок. */
.data-table.worktime { width: auto; }
/* По центру — с весом «таблица + класс»: общее правило .data-table td (выравнивание
   влево) иначе перебивало, и числа карты стояли у левого края ячейки (1.37.0). */
.data-table .worktime__day { text-align: center; min-width: 52px; }
.worktime__day--weekend { color: var(--c-muted); }
.worktime__weekday { display: block; font-weight: 400; font-size: 11px; color: var(--c-muted); }
.worktime__plate { white-space: nowrap; }
.worktime__group, .worktime__hours { display: block; font-size: 11px; font-weight: 400;
                                     white-space: nowrap; }
.data-table .worktime__cell { text-align: center; font-family: var(--font-mono);
                              font-variant-numeric: tabular-nums; font-size: 12px;
                              line-height: 1.3; }
.worktime__cell--empty { vertical-align: middle; }
.worktime__time { display: block; }
.worktime__sum { font-size: 12px; font-variant-numeric: tabular-nums; }

/* Выполнение по часам: машина × часы проверок. Процент КП — крупно, под ним
   контейнеры и объём, внизу — отклонение прогноза от срока (1.37.0). */
/* Пять часов на 1700 px читались бы как пустыня: таблица — по содержимому */
.data-table.hourly { width: auto; }
.hourly th, .hourly td { padding: 6px 8px; }
.hourly .hourly__hour { text-align: center; min-width: 112px; }
.hourly .hourly__cell { text-align: center; font-variant-numeric: tabular-nums; line-height: 1.3;
                        white-space: nowrap; }
.hourly__kp { display: block; font-family: var(--font-mono); font-size: 15px; font-weight: 600; }
.hourly__sub { display: block; font-size: 11px; color: var(--c-muted); }
.hourly__shift { display: block; font-family: var(--font-mono); font-size: 11px; font-weight: 600; }
.hourly__cell--future { vertical-align: middle; text-align: center; }
.heat td.hourly__cell { border-left: 2px solid var(--c-surface); }
.heat td.hourly__cell:focus { outline: 2px solid var(--c-accent); outline-offset: -2px; }
.hourly .hourly__total { border-left: 2px solid var(--c-rule); }
/* «Маленькая» — только по шрифту и полям: высота та же, что у всех. */
.button--small { padding: 0 12px; font-size: 13px; }

.messages { margin-bottom: var(--space); }
.message {
  margin: 0 0 8px;
  padding: 9px 12px;
  border-radius: var(--radius);
  border: 1px solid var(--c-border);
  background: var(--c-surface);
}
.message--success { border-color: var(--c-ok); }
.message--error { border-color: var(--c-fail); }

/* Статус передаётся значком и текстом, не только цветом (ТЗ §21) */
.status { white-space: nowrap; font-size: 13px; }
.status--ok { color: var(--c-ok); }
.status--warn { color: var(--c-warn); }
.status--fail { color: var(--c-fail); }
.status--muted { color: var(--c-muted); }

/* Цвет на каждый статус. У машины в списке аномалий бывает три-четыре отметки
   сразу, и одинаково-жёлтые они не различаются. Цвет — подсказка, а не
   носитель смысла: название статуса стоит рядом всегда (ТЗ §21).
   Оттенки взяты из уже проверенной палитры, новых красок не заводим. */
.status--code-normal { color: var(--c-ok); }
.status--code-below-group { color: var(--c-ok); }
.status--code-above-group { color: var(--c-warn); }
.status--code-negative-total { color: var(--c-fail); }
.status--code-error { color: var(--c-fail); }
.status--code-period-drift { color: var(--c-series-5); }
.status--code-fuel-without-mileage { color: var(--c-series-4); }
/* Охра диаграмм на белом даёт 4,34:1 — для полосы достаточно, для текста нет.
   У статуса свой оттенок потемнее: 4,99:1. */
.status--code-mileage-without-fuel { color: #a85c17; }
:root[data-theme="dark"] .status--code-mileage-without-fuel { color: var(--c-series-3); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .status--code-mileage-without-fuel {
    color: var(--c-series-3);
  }
}
.status--code-unlinked-card { color: var(--c-series-1); }
.status--code-unknown-vehicle { color: var(--c-series-5); }
.status--code-unknown-product { color: #a85c17; }
:root[data-theme="dark"] .status--code-unknown-product { color: var(--c-series-3); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .status--code-unknown-product { color: var(--c-series-3); }
}
.status--code-no-fuel-data { color: var(--c-muted); }
.status--code-no-weight { color: var(--c-muted); }
.status--code-idle { color: var(--c-muted); }
.status--code-partial-period { color: var(--c-muted); }
.status--code-manual { color: var(--c-series-4); }
.status--code-recalculating { color: var(--c-accent); }

/* Отметка на цветном фоне читается как ярлык, а не как раскрашенный текст. */
.chip.status { border: 1px solid currentColor; background: transparent; }
.row--muted td { opacity: 0.65; }
.critical {
  display: inline-block;
  width: 16px; height: 16px;
  line-height: 16px; text-align: center;
  border-radius: 50%;
  background: var(--c-warn);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
}

/* --- Экраны импорта --- */
.data-table--compact th, .data-table--compact td {
  padding: 4px 8px;
  font-size: 13px;
  white-space: nowrap;
  max-width: 22ch;
  overflow: hidden;
  text-overflow: ellipsis;
}
.row--separator td, .row--separator th {
  background: color-mix(in srgb, var(--c-border) 35%, transparent);
  color: var(--c-muted);
  font-style: italic;
}
.mapping-group {
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  padding: 12px var(--space);
  margin: var(--space) 0;
}
.mapping-group legend { padding: 0 6px; color: var(--c-muted); font-size: 13px; }

/* --- Списки операций --- */
.row--return td { background: color-mix(in srgb, var(--c-warn) 8%, transparent); }
.advanced { margin: 8px 0; }
.advanced summary { cursor: pointer; color: var(--c-muted); font-size: 13px; }
/* Фильтры всех экранов — одна строка плашек, см. «Панель фильтров» ниже
   по файлу (ADR-0030). */

/* ═══════════ Этап 8: отчёт «Топливо» (ТЗ §20, §21) ═══════════
   Подход эталонного HTML сохранён: строгий аналитический интерфейс, компактные
   KPI, ясные таблицы, сдержанные диаграммы. Шрифт — Open Sans файлами в
   `static/fonts/`: ТЗ §21 требует локальные шрифты и запрещает Google Fonts
   и внешние CDN.
   Числа набираются моноширинным с табличными цифрами: колонки выравниваются
   по разрядам, и порядок величины виден без чтения. */

/* Цвета этих переменных заданы выше, в общей палитре: держать один токен
   в двух местах — верный способ развести светлую и тёмную темы. */
:root {
  /* Раньше здесь стоял Roboto Condensed — его нет на большинстве машин, и
     подписи набирались чем придётся. Набор в проекте один, узкого начертания
     у него нет: роль подписи берут на себя размер, разрядка и капитель. */
  --font-cond: var(--font);
}

.report { display: flex; flex-direction: column; gap: 18px; }
/* Дашборд шире обычной страницы. Колонка в 1200 px на экране 1920 оставляла
   по краям 700 px пустоты, а карта «машины × дни» и таблица автомобилей в неё
   не помещались и ехали вбок. Прежнее правило `.report .app-main` не
   срабатывало никогда: .app-main — снаружи .report, а не внутри. */
.app-main:has(> .report), .app-main--wide { max-width: 1760px; }
body:has(.app-main > .report) .crumbs, body:has(.app-main--wide) .crumbs,
body:has(.app-main > .report) .app-footer, body:has(.app-main--wide) .app-footer { max-width: 1760px; }

/* --- Шапка отчёта --- */
.report-head {
  display: flex; flex-wrap: wrap; align-items: flex-end;
  justify-content: space-between; gap: 16px;
  padding-bottom: 14px; border-bottom: 2px solid var(--c-ink);
}
.eyebrow {
  font-family: var(--font-cond); font-weight: 600; font-size: 11.5px;
  letter-spacing: .13em; text-transform: uppercase; color: var(--c-muted);
  margin: 0 0 4px;
}
.report-head h1 { font-size: clamp(24px, 3.4vw, 34px); line-height: 1.05; margin: 0; }
.period-note { font-family: var(--font-mono); font-size: 12px; color: var(--c-muted); margin: 6px 0 0; }

/* --- KPI (ТЗ §20.2) --- */
.kpis {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
  gap: 1px; background: var(--c-border); border: 1px solid var(--c-border);
}
.kpi { background: var(--c-surface); padding: 13px 14px; display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.kpi__label {
  font-family: var(--font-cond); font-weight: 600; font-size: 11px;
  letter-spacing: .1em; text-transform: uppercase; color: var(--c-muted);
}
.kpi__value {
  font-family: var(--font-mono); font-size: 23px; line-height: 1.15;
  font-variant-numeric: tabular-nums; letter-spacing: -.02em;
}
.kpi__value small { font-size: 12px; color: var(--c-muted); letter-spacing: 0; }
.kpi__sub { font-family: var(--font-mono); font-size: 11.5px; color: var(--c-muted); font-variant-numeric: tabular-nums; }
.kpi--hero { background: var(--c-ink); }
.kpi--hero .kpi__label { color: color-mix(in srgb, var(--c-surface) 62%, transparent); }
.kpi--hero .kpi__value, .kpi--hero .kpi__value small, .kpi--hero .kpi__sub { color: var(--c-surface); }
.kpi__value .no-data, .kpi--absent .kpi__value { font-size: 15px; color: var(--c-muted); font-family: var(--font); }

/* --- Панели --- */
.panel {
  background: var(--c-surface); border: 1px solid var(--c-border);
  padding: 16px 18px 18px;
}
.panel > h2 { font-family: var(--font-cond); font-weight: 700; font-size: 17px; margin: 0; }
.panel > .sub { font-size: 12.5px; color: var(--c-muted); margin: 4px 0 12px; max-width: 76ch; }
/* minmax(0, 1fr), а не 1fr: колонка сетки по умолчанию не уже своего
   содержимого, и широкая строка внутри растягивала панель за край страницы —
   вбок ехала вся страница, а не таблица. */
.panel-two { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 18px; }
@media (max-width: 900px) { .panel-two { grid-template-columns: 1fr; } }
/* Колонка сетки не уже своей таблицы, если не разрешить ей сжиматься: на
   телефоне таблица «Товары периода» раздвигала страницу до 580 px, и браузер
   уменьшал всю страницу. Таблица едет вбок внутри своего блока. */
.panel-two > * { min-width: 0; }

/* --- Ранжированные бары (ТЗ §20.1, п.5) --- */
.rank-group + .rank-group { margin-top: 14px; }
.rank-group__cap {
  display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap;
  padding-bottom: 6px; margin-bottom: 6px; border-bottom: 1px solid var(--c-rule);
}
.rank-group__title { font-family: var(--font-cond); font-weight: 700; font-size: 14px; }
.rank-group__count { font-family: var(--font-mono); font-size: 11.5px; color: var(--c-muted); }
.rank-group__avg { font-family: var(--font-mono); font-size: 11.5px; color: var(--c-muted); margin-left: auto; }

/* Колонки рейтинга заданы один раз: по ним же считается положение пунктира
   среднего, и разъехаться они не должны. --rank-fixed — сумма неизменных
   колонок с промежутками, --rank-track — отступ до начала полос. */
.rank { --rank-fixed: 254px; --rank-track: 180px; }
.rank--tonkm { --rank-fixed: 338px; }
.rank-row, .rank-head {
  display: grid; grid-template-columns: 170px 1fr 64px;
  align-items: center; gap: 10px; padding: 2px 0;
}
.rank--tonkm .rank-row, .rank--tonkm .rank-head {
  grid-template-columns: 170px 1fr 64px 74px;
}
.rank-head { padding-bottom: 5px; }
.rank-head__main, .rank-head__sec {
  font-family: var(--font-cond); font-weight: 600; font-size: 10.5px;
  letter-spacing: .09em; text-transform: uppercase; text-align: right;
}
.rank-head__main { color: var(--c-text); }
.rank-head__sec { color: var(--c-muted); }

/* Полосы и пунктир среднего лежат в общем слое: линия тянется через все
   строки группы, поэтому она — сосед строк, а не часть каждой из них. */
.rank-rows { position: relative; padding-bottom: 18px; }
.rank-avg, .rank-avg__cap {
  position: absolute;
  left: calc(var(--rank-track) + (100% - var(--rank-fixed)) * var(--avg, 0));
  pointer-events: none;
}
.rank-avg { top: 0; bottom: 16px; border-left: 2px dashed var(--c-text); opacity: .5; }
.rank-avg__cap {
  bottom: 0; transform: translateX(-50%); white-space: nowrap;
  font-family: var(--font-cond); font-weight: 600; font-size: 10.5px;
  letter-spacing: .06em; text-transform: uppercase; color: var(--c-muted);
}
.rank-row__name {
  font-size: 12.5px; color: var(--c-muted); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.rank-row__name a { color: inherit; text-decoration: none; }
.rank-row__name a:hover { color: var(--c-text); text-decoration: underline; }
.rank-row__track { height: 14px; background: var(--c-surface-2); }
/* display:block обязателен: столбик — вложенный span, а к строчному элементу
   ширина и высота не применяются, и диаграмма осталась бы пустой. */
.rank-row__bar { display: block; height: 14px; background: var(--c-series-1); }
/* Цвет полосы — группа: тот же ключ, что и у точек на соседней диаграмме,
   поэтому цвет на странице читается одинаково везде. «Дороже» и «экономичнее»
   видны по положению относительно пунктира и названы в таблице и подсказке. */
.rank-row__bar--series-2 { background: var(--c-series-3); }
.rank-row__bar--series-3 { background: var(--c-series-4); }
.rank-row__bar--series-4 { background: var(--c-series-5); }
.rank-row__bar--series-0 { background: var(--c-muted); }
.rank-row__value {
  font-family: var(--font-mono); font-size: 12.5px; text-align: right;
  font-variant-numeric: tabular-nums;
}
.rank-row__value--sec { font-size: 11.5px; color: var(--c-muted); }
@media (max-width: 620px) {
  .rank { --rank-fixed: 169px; --rank-track: 111px; }
  .rank--tonkm { --rank-fixed: 227px; }
  .rank-row, .rank-head { grid-template-columns: 104px 1fr 58px; gap: 7px; }
  .rank--tonkm .rank-row, .rank--tonkm .rank-head {
    grid-template-columns: 104px 1fr 58px 58px;
  }
}

/* --- Дивергентные бары (ТЗ §20.1, п.7) --- */
.div-row { display: grid; grid-template-columns: 170px 1fr 74px; align-items: center; gap: 10px; padding: 2px 0; }
.div-track { position: relative; height: 14px; background: var(--c-surface-2); }
.div-mid { display: block; position: absolute; left: 50%; top: -3px; bottom: -3px; border-left: 1px solid var(--c-rule); }
.div-bar { display: block; position: absolute; height: 14px; top: 0; }
.div-bar--up { background: var(--c-fail); }
.div-bar--down { background: var(--c-ok); }
/* Машина, у которой заправка и пробег разъехались по месяцам: её отклонение
   бывает кратным, и красить его в «стало дороже» значило бы утверждать то,
   чего данные не показывают. */
.div-bar--drift {
  background: repeating-linear-gradient(45deg, var(--c-rule) 0 3px, transparent 3px 6px);
  box-shadow: inset 0 0 0 1px var(--c-rule);
}
/* Уголок на конце: полоса обрезана шкалой, а не «столько и есть». */
.div-bar--clamped::after {
  content: ""; position: absolute; top: 2px; width: 0; height: 0;
  border-top: 5px solid transparent; border-bottom: 5px solid transparent;
}
.div-bar--up.div-bar--clamped::after { right: -7px; border-left: 6px solid var(--c-fail); }
.div-bar--down.div-bar--clamped::after { left: -7px; border-right: 6px solid var(--c-ok); }
.div-bar--drift.div-bar--clamped::after { border-left-color: var(--c-rule); border-right-color: var(--c-rule); }
@media (max-width: 620px) { .div-row { grid-template-columns: 104px 1fr 58px; gap: 7px; } }

/* --- Диаграммы SVG --- */
/* Диаграмма занимает ширину панели, но не растягивается сверх своего
   естественного размера: иначе подписи внутри SVG увеличились бы вместе с
   ней и стали больше окружающего текста. */
.chart { width: 100%; max-width: 700px; height: auto; overflow: visible; }
.chart text { font-family: var(--font); font-size: 10px; fill: var(--c-muted); }
.chart .tick { font-family: var(--font-mono); font-size: 10px; }
.chart .axis-caption {
  font-family: var(--font-cond); font-weight: 600; font-size: 10.5px;
  letter-spacing: .07em; text-transform: uppercase; fill: var(--c-muted);
}
/* Подпись машины читается поверх сетки и соседних точек: буквы обведены
   цветом подложки, обводка рисуется под заливкой. */
.chart .point-label {
  font-family: var(--font-cond); font-weight: 600; font-size: 10.5px;
  fill: var(--c-text); stroke: var(--c-surface); stroke-width: 3.5;
  stroke-linejoin: round; paint-order: stroke;
}
.chart .axis { stroke: var(--c-rule); stroke-width: 1; }
.chart .gridline { stroke: var(--c-border); stroke-width: 1; stroke-dasharray: 2 3; }
.chart .point { fill: var(--c-series-1); fill-opacity: .92; }
/* Ореол цветом подложки: точка отделяется от сетки и от соседних точек, не
   меняя при этом собственного цвета — обводка по фигуре съедала бы её размер. */
.chart .point-halo { fill: var(--c-surface); }
/* Невидимый запас вокруг точки: попасть курсором в девять пикселей трудно. */
.point-hit__area { fill: transparent; }
.point-hit:hover .point, .point-hit:focus .point { stroke: var(--c-text); stroke-width: 2; }
.point-hit:focus { outline: none; }
.chart .point--series-2 { fill: var(--c-series-3); }
.chart .point--series-3 { fill: var(--c-series-4); }
.chart .point--series-4 { fill: var(--c-series-5); }
.chart .point--series-0 { fill: var(--c-muted); }
/* Замечание передаётся формой (ромб) и обводкой, а не только цветом (ТЗ §21) */
.chart .point--warn { stroke: var(--c-text); stroke-width: 1.4; }
.chart .line { fill: none; stroke: var(--c-series-1); stroke-width: 2; }
.chart .area { fill: var(--c-series-1); fill-opacity: .1; }
.chart .marker { fill: var(--c-series-1); }

/* --- Всплывающая статистика --- */
/* Появляется у курсора и не мешает наводиться дальше: событий не ловит. */
.tooltip {
  position: fixed; z-index: 60; pointer-events: none;
  max-width: 300px; padding: 10px 12px;
  background: var(--c-shell); color: var(--c-on-shell);
  border-radius: var(--radius);
  box-shadow: 0 12px 34px -12px rgba(20, 16, 40, .6);
  font-size: 12.5px;
  transition: opacity .1s;
}
.tooltip--hidden { opacity: 0; }
.tooltip__title {
  font-weight: 600; font-size: 13px; margin-bottom: 6px;
  color: var(--c-on-shell);
}
.tooltip__row {
  display: flex; justify-content: space-between; gap: 14px;
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  color: var(--c-on-shell-dim);
}
.tooltip__row b { font-weight: 500; color: var(--c-on-shell); }
.tooltip__note { margin-top: 6px; color: var(--c-on-shell-dim); }
@media (prefers-reduced-motion: reduce) { .tooltip { transition: none; } }

/* Носитель подсказки в разметке: содержимое лежит рядом со строкой и на
   экране не показывается — его забирает скрипт. */
[data-tip] { cursor: default; }
[data-tip-body] { display: none; }

/* --- Легенда --- */
.legend { display: flex; flex-wrap: wrap; gap: 14px; margin: 0 0 10px; font-size: 12px; color: var(--c-muted); }
.legend span { display: inline-flex; align-items: center; gap: 6px; }
.swatch { width: 11px; height: 11px; flex: none; }
.swatch--1, .swatch--series-1 { background: var(--c-series-1); }
.swatch--2 { background: var(--c-series-2); }
.swatch--3, .swatch--series-2 { background: var(--c-series-3); }
.swatch--series-3 { background: var(--c-series-4); }
.swatch--series-4 { background: var(--c-series-5); }
.swatch--series-0 { background: var(--c-muted); }
/* Образец ромба — та же примета, что и у точки с замечаниями. */
.swatch--diamond {
  background: var(--c-surface-2); border: 1.4px solid var(--c-text);
  transform: rotate(45deg); width: 9px; height: 9px;
}
.swatch--drift {
  background: repeating-linear-gradient(45deg, var(--c-rule) 0 3px, transparent 3px 6px);
  box-shadow: inset 0 0 0 1px var(--c-rule);
}

/* --- Таблица отчёта (ТЗ §20.3) --- */
.report-table { min-width: 1180px; font-size: 12.5px; }
.report-table th {
  font-family: var(--font-cond); font-weight: 600; font-size: 11px;
  letter-spacing: .06em; text-transform: uppercase; color: var(--c-muted);
  text-align: right; white-space: nowrap; cursor: pointer; user-select: none;
}
.report-table th:hover { color: var(--c-text); }
.report-table th[aria-sort] { color: var(--c-text); }
.report-table th.col-text, .report-table td.col-text { text-align: left; }
.report-table td {
  text-align: right; white-space: nowrap;
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
}
.report-table td.col-text { font-family: var(--font); }
.report-table tbody tr:hover td { background: var(--c-surface-2); }
.report-table tfoot td {
  font-weight: 600; border-top: 2px solid var(--c-rule);
  background: var(--c-surface-2);
}
.no-data { color: var(--c-muted); cursor: help; }
.sort-mark { font-size: 9px; opacity: .7; }

.chip {
  display: inline-flex; align-items: center; gap: 5px; font-size: 11px;
  padding: 1px 8px; border-radius: 999px; border: 1px solid var(--c-border);
  white-space: nowrap; font-family: var(--font);
}
.chip__dot { width: 7px; height: 7px; border-radius: 999px; flex: none; background: currentColor; }
.chip + .chip { margin-left: 4px; }

/* --- Готовые отборы над таблицей: группы автомобилей ---
   Ссылки, а не кнопки скрипта: отбор серверный (views._group_presets).
   Вид — как у готовых отборов в строке фильтров (.fseg): выбранный стоит
   белой «таблеткой» и отмечен aria-current — не только цветом. */
.presets {
  display: inline-flex; flex-wrap: wrap; gap: 2px; padding: 3px; margin: 12px 0 2px;
  background: var(--c-surface-2); border-radius: 18px;
}
.preset {
  display: inline-flex; align-items: center; gap: 6px; height: 30px;
  padding: 0 12px; border-radius: 999px;
  color: var(--c-muted); font-size: 13.5px; text-decoration: none; white-space: nowrap;
}
.preset:hover { color: var(--c-text); }
.preset:focus-visible { outline: 2px solid var(--c-accent); outline-offset: 2px; }
.preset__count {
  min-width: 20px; padding: 1px 6px; border-radius: 999px; text-align: center;
  font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums;
  background: var(--c-surface); color: var(--c-muted);
}
.preset[aria-current] {
  background: var(--c-surface); color: var(--c-text); font-weight: 600;
  box-shadow: 0 1px 3px rgba(20, 16, 40, .12);
}
.preset[aria-current] .preset__count {
  background: color-mix(in srgb, var(--c-accent) 11%, var(--c-surface)); color: var(--c-accent);
}
/* Шапка (56 px), строка модуля (45 px) и строка фильтров (~52 px) липкие:
   без отступа блок после перехода по якорю уезжал бы под них. */
#vehicles, .report .panel[id] { scroll-margin-top: 170px; }

/* --- Панель управления таблицей --- */
.table-tools { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin: 10px 0; }
.table-tools .fsearch { flex: 0 1 280px; }
.column-picker { position: relative; }
.column-picker__list {
  position: absolute; z-index: 20; right: 0; margin-top: 4px; padding: 10px 12px;
  background: var(--c-surface); border: 1px solid var(--c-border);
  border-radius: var(--radius); box-shadow: 0 10px 30px -12px rgba(0,0,0,.4);
  max-height: 320px; overflow-y: auto; min-width: 220px;
}
.column-picker__list label { display: block; font-size: 13px; padding: 3px 0; }

/* --- Готовность периода (ТЗ §18) --- */
.readiness { border-left: 3px solid var(--c-ok); }
.readiness--warn { border-left-color: var(--c-warn); }
.readiness--fail { border-left-color: var(--c-fail); }
.readiness__states { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 4px; padding: 0; list-style: none; }
.readiness__state { font-size: 12px; padding: 2px 9px; border: 1px solid var(--c-border); border-radius: 999px; }
.checklist { margin: 10px 0 0; padding: 0; list-style: none; }
.checklist li { display: grid; grid-template-columns: 92px 1fr; gap: 10px; padding: 6px 0; border-top: 1px solid var(--c-border); }
.checklist__mark { font-family: var(--font-mono); font-size: 11px; }
.checklist__hint { display: block; color: var(--c-muted); font-size: 12.5px; margin-top: 2px; }

/* --- Методика (ТЗ §20.1, п.13) --- */
.method { display: grid; grid-template-columns: auto 1fr; gap: 8px 18px; margin: 8px 0 0; font-size: 12.5px; }
.method dt { font-family: var(--font-mono); white-space: nowrap; }
.method dd { margin: 0; color: var(--c-muted); }
@media (max-width: 700px) { .method { grid-template-columns: 1fr; gap: 2px 0; } .method dd { margin-bottom: 8px; } }

/* --- Форма фильтров --- */
/* Поля выравниваются по верху: подпись под одним из них (у «Сравнить с»)
   иначе сдвигала бы соседей вверх, и ряд переставал читаться как ряд. */

/* --- Инструкции по источникам данных --- */
.guides > .sub { margin: 0 0 10px; }
.guide { border-top: 1px solid var(--c-border); }
.guide:last-child { border-bottom: 1px solid var(--c-border); }
.guide > summary {
  cursor: pointer; padding: 10px 2px;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px;
}
.guide > summary:hover { color: var(--c-accent); }
.guide__source { font-size: 13px; color: var(--c-muted); }
.guide__body { padding: 0 2px 14px; max-width: 76ch; }
.guide__steps { margin: 8px 0; padding-left: 20px; }
.guide__steps li { margin-bottom: 6px; }
.guide__columns { margin: 10px 0 6px; font-size: 14px; }
.guide__draft { margin: 6px 0 0; font-size: 13.5px; color: var(--c-warn); }



/* --- Меню выгрузки --- */
/* Точка отсчёта для раскрытого списка: без неё он считает от страницы. */
.export-menu { display: inline-block; margin: 0; position: relative; }
.export-menu > summary { display: inline-flex; list-style: none; cursor: pointer; }
/* Меню выгрузки — <details class="advanced">, и правило «Ещё» делало кнопку
   мельче и бледнее соседней «Печать». */
.export-menu > summary.button { font: inherit; color: var(--c-text); }
.export-menu > summary::-webkit-details-marker { display: none; }
.export-menu__list {
  /* Прижат к правому краю кнопки: слева от неё места всегда больше, чем
     справа, и раскрытый список уезжал за край страницы. */
  position: absolute; right: 0; z-index: 20; margin-top: 6px; padding: 12px;
  background: var(--c-surface); border: 1px solid var(--c-border);
  border-radius: var(--radius); box-shadow: 0 10px 30px -12px rgba(0,0,0,.4);
  display: flex; flex-direction: column; gap: 6px; max-width: 280px;
}
.export-menu__list p { margin: 0 0 4px; }

/* --- Печатный вид (ТЗ §20.7) --- */
@media print {
  @page { size: A4 landscape; margin: 10mm; }
  .shell, .app-footer, .modulebar, .crumbs, .fbar, .table-tools,
  .column-picker, .skip-link, .no-print, .export-menu { display: none !important; }
  .report { gap: 10px; }
  .panel, .kpis, .kpi { border-color: #999; box-shadow: none; }
  .kpi--hero { background: #fff; }
  .kpi--hero .kpi__label, .kpi--hero .kpi__value, .kpi--hero .kpi__sub { color: #000; }
  .panel, .kpis { break-inside: avoid; }
  a[href]::after { content: ""; }

  /* На бумагу идёт отчёт, а не экран целиком. Готовность данных и методика
     расчёта — подсказки для работы с отчётом: первая говорит, чего не хватает
     и где это исправить, вторая объясняет формулы. На распечатке ни то, ни
     другое не действие, а полторы страницы текста перед числами. */
  [aria-labelledby="readiness-title"],
  [aria-labelledby="method-title"] { display: none; }
  details > summary { display: none; }
  .filter-summary { display: none; }

  /* Диаграммы на полосах нарисованы фоном, а браузер при печати фоны не
     печатает — на бумаге от рейтинга и от изменений оставались одни подписи.
     Здесь фон печатать обязательно: полоса и есть содержимое. */
  .rank-row__bar, .rank-row__track, .diverging__bar, .diverging__track,
  .swatch, .status, .kpi--hero {
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  /* Диаграммы шире половины листа не нужны: рядом встают две. */
  .chart { max-width: 100%; }
  .panel-two { grid-template-columns: 1fr 1fr; gap: 8px; }

  /* Таблица машин: на бумаге нет бокового скролла, поэтому она обязана
     поместиться по ширине листа целиком. Закреплённая колонка на печати
     только мешает — она рассчитана на сдвиг, которого здесь не бывает. */
  .table-scroll { overflow: visible; }
  .report-table { min-width: 0; width: 100%; font-size: 8.5px; table-layout: fixed; }
  /* Перенос внутри ячейки обязателен. На экране ячейки не переносятся —
     таблица едет вбок, и это правильно; на бумаге ехать некуда, и nowrap
     оставлял таблицу шире листа даже при table-layout: fixed. */
  /* `thead th` названо полностью: у экранного правила `.data-table thead th`
     вес выше, и без этого заголовки колонок оставались в одну строку —
     таблица не помещалась на лист из-за них одних. */
  .report-table th, .report-table thead th,
  .report-table td, .report-table td * {
    padding: 2px 3px; white-space: normal; overflow-wrap: anywhere;
  }
  .report-table td * { padding: 0; }
  /* Статусы в колонке — обычным текстом. Овал вокруг слова на бумаге
     разрывается по строкам, и от каждой отметки остаётся по обрубку рамки. */
  .report-table .chip {
    border: 0; border-radius: 0; background: none; display: inline;
  }
  .report-table .chip + .chip::before { content: ", "; }
  .report-table .chip__dot { display: none; }
  /* При table-layout: fixed ширину задаёт первая строка, и без указаний все
     колонки равны. Названию, номеру и статусам нужно больше места, числам —
     меньше: иначе восемнадцатая колонка не умещалась, и таблица вылезала за
     лист на её остаток. */
  .report-table thead th:first-child { width: 9%; }
  .report-table thead th:nth-child(2) { width: 7%; }
  .report-table thead th:last-child { width: 10%; }
  .report-table th:first-child, .report-table td:first-child {
    position: static; box-shadow: none; background: transparent;
  }
  /* Заголовок повторяется на каждой странице — иначе со второй непонятно,
     какая колонка что значит. */
  .report-table thead { display: table-header-group; }
  .report-table tr { break-inside: avoid; }
}

/* --- «Чистая логистика»: дашборд «Время работы» (решение заказчика 25.09.2026) --- */
/* Диаграмма растягивается вместе с подписями (viewBox), поэтому шире
   1130 px не становится: на дашборде шириной 1760 px текст вырос бы вдвое. */
.chart--wide { max-width: 1130px; }
.chart-scroll { overflow-x: auto; }
.chart--gantt { max-width: 1130px; min-width: 760px; }
.chart-title { font-family: var(--font-cond); font-weight: 600; font-size: 14px; margin: 14px 0 6px; }
.panel-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; flex-wrap: wrap; }
/* Выбор дня, машины, кнопки — справа на уровне заголовка блока: середина
   элемента на середине строки заголовка (строка 26 px, элемент 38 px). */
.panel-head > :first-child { flex: 1 1 320px; min-width: 0; }
.panel-head > :not(:first-child) { flex: none; margin-top: -6px; }
@media (max-width: 760px) { .panel-head > :not(:first-child) { flex: 1 1 100%; margin-top: 0; } }
.panel-head h2 { font-family: var(--font-cond); font-weight: 700; font-size: 17px; margin: 0; }
.panel-head .sub { font-size: 12.5px; color: var(--c-muted); margin: 4px 0 12px; max-width: 76ch; }

/* Рабочий день парка: полоса от начала до конца — одна серия, один цвет. */
.chart .wt-band { fill: var(--c-series-1); fill-opacity: .16; }
.chart .wt-tail { fill: none; stroke: var(--c-series-1); stroke-width: 1.5; stroke-dasharray: 4 3; opacity: .7; }
.chart .wt-deadline { fill: none; stroke: var(--c-text); stroke-width: 1.5; stroke-dasharray: 2 3; }
.chart .wt-caption { font-family: var(--font-mono); font-size: 10.5px; fill: var(--c-text); }
.chart .wt-caption--tail, .chart .wt-caption--deadline { fill: var(--c-muted); }
.col-hit__area { fill: transparent; }
.col-hit:hover .col-hit__area, .col-hit:focus .col-hit__area { fill: var(--c-surface-2); fill-opacity: .7; }
.col-hit:focus { outline: none; }

/* Полосы дня: смена цветом группы, разгрузка — засечка цветом текста. */
.row-hit__area { fill: transparent; }
.row-hit:hover .row-hit__area, .row-hit:focus .row-hit__area { fill: var(--c-surface-2); }
.row-hit:focus { outline: none; }
.chart .wt-plate { font-family: var(--font-mono); font-size: 11px; fill: var(--c-text); }
.chart .wt-bar { fill: var(--c-series-1); }
.chart .wt-bar--series-2 { fill: var(--c-series-3); }
.chart .wt-bar--series-3 { fill: var(--c-series-4); }
.chart .wt-bar--series-4 { fill: var(--c-series-5); }
.chart .wt-bar--series-0 { fill: var(--c-muted); }
/* Засечка с окантовкой цветом подложки: видна и на тёмной полосе, и вне её. */
.chart .wt-unload-halo { stroke: var(--c-surface); stroke-width: 4; }
.chart .wt-unload { stroke: var(--c-text); stroke-width: 2; }
.chart .wt-own-deadline { stroke: var(--c-text); stroke-width: 1.5; stroke-dasharray: 2 2; }
.chart .wt-deadline-v { stroke: var(--c-text); stroke-width: 1.5; stroke-dasharray: 4 3; }
.chart .wt-end { font-family: var(--font-mono); font-size: 10.5px; fill: var(--c-muted); }
.chart .wt-end--late { fill: var(--c-text); font-weight: 600; }
.swatch--tick { width: 2px; height: 12px; background: var(--c-text); }
.swatch--deadline { width: 2px; height: 12px; background: repeating-linear-gradient(180deg, var(--c-text) 0 3px, transparent 3px 5px); }
.swatch--fit { height: 2px; background: repeating-linear-gradient(90deg, var(--c-text) 0 4px, transparent 4px 7px); }

/* Машины по дням: расходящаяся шкала, число в ячейке — всегда. */
.heat--cool-2 { background: color-mix(in srgb, var(--c-div-cool) var(--heat-strong), var(--c-surface)); }
.heat--cool-1 { background: color-mix(in srgb, var(--c-div-cool) var(--heat-soft), var(--c-surface)); }
.heat--mid { background: color-mix(in srgb, var(--c-rule) 45%, var(--c-surface)); }
.heat--warm-1 { background: color-mix(in srgb, var(--c-div-warm) var(--heat-soft), var(--c-surface)); }
.heat--warm-2 { background: color-mix(in srgb, var(--c-div-warm) var(--heat-strong), var(--c-surface)); }
.heat--none { background: var(--c-surface-2); }
.heat--live { background: repeating-linear-gradient(45deg, var(--c-surface-2) 0 4px, var(--c-surface) 4px 8px); }
.swatch.heat--mid, .swatch.heat--none, .swatch.heat--live { box-shadow: inset 0 0 0 1px var(--c-rule); }
.heat td.worktime__cell { border-left: 2px solid var(--c-surface); }
.heat td.worktime__cell:focus { outline: 2px solid var(--c-accent); outline-offset: -2px; }
.worktime__shift { display: block; font-size: 11px; font-weight: 600; white-space: nowrap; }

/* Рейтинг машин и приёмка: полоса на общей шкале, числа справа. */
.wt-rank__row, .wt-accept__row {
  display: grid; grid-template-columns: 96px minmax(60px, 1fr) 48px 48px 84px;
  align-items: center; gap: 8px; padding: 2px 0;
}
.wt-accept__row { grid-template-columns: 150px minmax(60px, 1fr) 48px 44px; }
.wt-rank__head { font-family: var(--font-cond); font-size: 10.5px; letter-spacing: .06em;
                 text-transform: uppercase; color: var(--c-muted); padding-bottom: 4px; }
.wt-rank__head span:nth-child(n+3) { text-align: right; }
.wt-rank__name { font-family: var(--font-mono); font-size: 12px; }
.wt-rank__value { font-family: var(--font-mono); font-size: 12px; text-align: right;
                  font-variant-numeric: tabular-nums; white-space: nowrap; }
.wt-rank__row:hover, .wt-rank__row:focus, .wt-div__row:hover, .wt-div__row:focus { background: var(--c-surface-2); outline: none; }
.wt-accept__title { font-size: 12.5px; white-space: nowrap; }

/* Связь времени смены с КП и объёмом */
.wt-rho {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 1px; background: var(--c-border); border: 1px solid var(--c-border); margin-bottom: 6px;
}
.wt-rho__item { background: var(--c-surface); padding: 10px 12px; display: flex; flex-direction: column; gap: 2px; }
.wt-rho__value { font-family: var(--font-mono); font-size: 19px; font-variant-numeric: tabular-nums; }
.wt-rho__line { display: flex; justify-content: space-between; gap: 10px; font-size: 12.5px; }
.wt-rho__line b { font-family: var(--font-mono); font-weight: 500; white-space: nowrap; }
.chart .point--small { fill-opacity: .75; }
.chart .wt-fit { stroke: var(--c-text); stroke-width: 2; stroke-dasharray: 6 4; }
.chart .wt-fit--series-1 { stroke: var(--c-series-1); }
.chart .wt-fit--series-2 { stroke: var(--c-series-3); }
.chart .wt-fit--series-3 { stroke: var(--c-series-4); }
.chart .wt-fit--series-4 { stroke: var(--c-series-5); }
.chart .wt-fit--series-0 { stroke: var(--c-muted); }

/* Водители: отклонение от ожидаемого, влево — быстрее, вправо — медленнее. */
.wt-div__row { display: grid; grid-template-columns: minmax(120px, 220px) minmax(80px, 1fr) 190px;
               align-items: center; gap: 10px; padding: 2px 0; }
.wt-div__name { font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wt-div__value { font-family: var(--font-mono); font-size: 12px; white-space: nowrap; }
.div-bar.wt-div__bar--faster { background: color-mix(in srgb, var(--c-div-cool) var(--heat-strong), var(--c-surface)); }
.div-bar.wt-div__bar--slower { background: color-mix(in srgb, var(--c-div-warm) var(--heat-strong), var(--c-surface)); }
@media (max-width: 620px) {
  .wt-rank__row { grid-template-columns: 84px minmax(40px, 1fr) 40px 44px; }
  .wt-rank__row > :last-child { display: none; }
  .wt-accept__row { grid-template-columns: 110px minmax(40px, 1fr) 40px 40px; }
  /* Имя и отклонение — строкой над полосой: «медленнее на 1 ч 20 мин» рядом
     с полосой не помещается, а полоса во всю ширину держит середину на месте. */
  .wt-div__row { grid-template-columns: minmax(0, 1fr) auto;
                 grid-template-areas: "name value" "track track"; gap: 3px 8px; padding: 4px 0; }
  .wt-div__name { grid-area: name; }
  .wt-div__value { grid-area: value; font-size: 11.5px; }
  .wt-div__row .div-track { grid-area: track; }
}

/* Возврат КП в контроль: основание и кнопка в одну строку, высота как у всех. */
.exclusion-return { display: flex; gap: 8px; align-items: center; min-width: 280px; }
.exclusion-return input[type="text"] {
  flex: 1; min-width: 0; height: var(--control-h); padding: 0 10px;
  font: inherit; font-size: 13px; color: var(--c-text); background: var(--c-surface);
  border: 1px solid var(--c-border); border-radius: var(--radius);
}

/* --- «Чистая логистика»: дашборд «Вес» (решение заказчика 26.09.2026) --- */
/* Тонны всего парка — одна величина, не группа: нейтральный тёмно-серый,
   чтобы столбцы не читались как «прессы» (цвет группы, ADR-0021). */
.chart .wb-bar { fill: color-mix(in srgb, var(--c-text) 55%, var(--c-surface)); }
.bar-hit__area { fill: transparent; }
.bar-hit:hover .bar-hit__area, .bar-hit:focus .bar-hit__area { fill: var(--c-surface-2); }
.bar-hit:hover .wb-bar, .bar-hit:focus .wb-bar { fill: var(--c-text); }
.bar-hit:focus { outline: none; }
/* Последний рейс машины за день — кольцо: примета формой, не цветом. */
.chart .wb-ring { fill: none; stroke: var(--c-text); stroke-width: 1.5; }
.swatch--ring { width: 11px; height: 11px; border-radius: 50%; background: transparent;
                box-shadow: inset 0 0 0 1.5px var(--c-text); }

/* --- Экипажи на одной машине (решение заказчика 26.09.2026) --- */
/* Медиана экипажа — крупное кольцо цветом экипажа поверх его смен. */
.chart .crew-median { fill: var(--c-surface); stroke-width: 3; }
.chart .crew-median.point--series-1 { stroke: var(--c-series-1); }
.chart .crew-median.point--series-2 { stroke: var(--c-series-3); }
.chart .crew-median.point--series-3 { stroke: var(--c-series-4); }
.chart .crew-median.point--series-4 { stroke: var(--c-series-5); }
.chart .crew-median.point--series-0 { stroke: var(--c-muted); }
.crews .swatch { display: inline-block; vertical-align: middle; margin-right: 4px; }
.crews__best { font-size: 11px; font-weight: 600; color: var(--c-text);
               border: 1px solid var(--c-rule); border-radius: 999px; padding: 0 6px; }

/* Машины по дням: каждая разгрузка отдельной плашкой, столбиком в ячейке дня. */
.loads__cell { padding: 3px 4px; vertical-align: top; }
.loads__trip { display: block; margin: 1px 0; padding: 1px 4px; border-radius: 3px;
               font-family: var(--font-mono); font-size: 11.5px; white-space: nowrap; }

/* ══ Панель фильтров (1.32.0, ADR-0030) ══════════════════════════════════
   Одна строка плашек вместо формы с подписью над каждым полем и кнопкой
   «Показать». Строка закреплена под шапкой и строкой модуля: на длинном
   дашборде период и выбранные фильтры видны всё время. Выбранный фильтр
   подсвечен цветом акцента и снимается крестиком. Разбор — apps/common/
   filterbar.py, поведение — static/js/filterbar.js. */
.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.ico {
  width: 16px; height: 16px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
.ico--muted { color: var(--c-muted); }
.ico--caret { width: 14px; height: 14px; color: var(--c-muted); transition: transform .15s; }
details[open] > summary .ico--caret { transform: rotate(180deg); }

.fbar {
  --fbar-accent-soft: color-mix(in srgb, var(--c-accent) 11%, var(--c-surface));
  --fbar-accent-line: color-mix(in srgb, var(--c-accent) 42%, var(--c-surface));
  position: sticky; top: 101px; z-index: 25;
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  padding: 7px;
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: 12px;
  transition: box-shadow .15s;
}
/* Без строки модуля (общесистемный экран без выбранного модуля) выше
   закреплена только тёмная полоса. */
body:not(:has(.modulebar)) .fbar { top: 56px; }
.fbar.is-stuck { box-shadow: 0 8px 22px rgba(20, 16, 40, .12); }
/* Пока грузится выбранный срез — полоска под строкой. */
.fbar.is-busy::after, .fctl.is-busy::after {
  content: ""; position: absolute; left: 12px; right: 12px; bottom: -1px; height: 2px;
  border-radius: 2px; background: var(--c-accent);
  animation: fbar-busy 1.1s ease-in-out infinite; transform-origin: left;
}
@keyframes fbar-busy {
  0% { transform: scaleX(0); opacity: 1; }
  70% { transform: scaleX(1); opacity: 1; }
  100% { transform: scaleX(1); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .fbar.is-busy::after, .fctl.is-busy::after { animation: none; opacity: .6; }
  .ico--caret { transition: none; }
}

/* ── Плашка ── */
.fpill {
  display: inline-flex; align-items: center; gap: 6px;
  height: var(--control-h); padding: 0 11px;
  border: 1px solid var(--c-border); border-radius: 999px;
  background: var(--c-surface); color: var(--c-text);
  font: inherit; font-size: 14px; line-height: 1; white-space: nowrap;
  cursor: pointer; text-decoration: none; list-style: none;
  transition: background .12s, border-color .12s;
}
.fpill::-webkit-details-marker, .fperiod__main::-webkit-details-marker { display: none; }
.fpill:hover { background: var(--c-surface-2); border-color: var(--c-rule); }
.fpill:focus-visible, .fperiod__main:focus-visible, .fperiod__step:focus-visible,
.fopt:focus-visible, .fchip:focus-visible, .fmonth:focus-visible, .fseg__item:focus-visible {
  outline: 2px solid var(--c-accent); outline-offset: 2px;
}
.fpill__k { color: var(--c-muted); }
.fpill__v { font-weight: 600; max-width: 20ch; overflow: hidden; text-overflow: ellipsis; }
.fpill--more { color: var(--c-muted); }
.fpill--more:hover { color: var(--c-text); }

.fctl { position: relative; display: inline-flex; }
.fctl.is-active .fpill, .fpill--toggle.is-active {
  background: var(--fbar-accent-soft); border-color: var(--fbar-accent-line);
}
.fctl.is-active .fpill__k, .fctl.is-active .ico--caret { color: var(--c-accent); }
.fctl.is-active .fpill__v { color: var(--c-ink); }
.fctl.is-active .fpill { padding-right: 38px; }
.fctl.is-active .fpill .ico--caret { display: none; }
.fclear {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: 999px;
  color: var(--c-accent); text-decoration: none;
}
.fclear:hover { background: color-mix(in srgb, var(--c-accent) 18%, transparent); }
.fclear .ico { width: 14px; height: 14px; }

/* Галочка-переключатель */
.fpill__box {
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; border-radius: 4px;
  border: 1.5px solid var(--c-rule); color: transparent;
}
.fpill__box .ico { width: 12px; height: 12px; stroke-width: 3; }
.fpill--toggle.is-active .fpill__box {
  background: var(--c-accent); border-color: var(--c-accent); color: var(--c-surface);
}

/* ── Период: стрелки и название ── */
.fperiod {
  display: inline-flex; align-items: stretch; height: var(--control-h);
  border: 1px solid var(--c-border); border-radius: 999px; background: var(--c-surface);
}
.fperiod__step {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; color: var(--c-text); text-decoration: none;
}
.fperiod__step:first-child { border-radius: 999px 0 0 999px; }
.fperiod__step:last-child { border-radius: 0 999px 999px 0; }
.fperiod__step:hover { background: var(--c-surface-2); }
.fperiod__step.is-off { color: var(--c-border); cursor: default; }
.fperiod__step.is-off:hover { background: none; }
.fperiod .fpop { display: flex; }
.fperiod__main {
  display: inline-flex; align-items: center; gap: 8px; height: 100%;
  padding: 0 12px; list-style: none; cursor: pointer; white-space: nowrap;
  border-left: 1px solid var(--c-border); border-right: 1px solid var(--c-border);
  font-weight: 600; font-size: 14px;
}
.fperiod__main:hover { background: var(--c-surface-2); }
.fperiod__title { max-width: 30ch; overflow: hidden; text-overflow: ellipsis; }

/* ── Раскрывающееся окно ── */
.fpop { position: relative; }
.fpop > summary { list-style: none; }
.fpop__panel {
  position: absolute; top: calc(100% + 8px); left: 0; z-index: 5;
  min-width: 250px; max-width: min(380px, calc(100vw - 24px));
  padding: 8px;
  background: var(--c-surface); color: var(--c-text);
  border: 1px solid var(--c-border); border-radius: 12px;
  box-shadow: 0 14px 36px rgba(20, 16, 40, .18), 0 2px 6px rgba(20, 16, 40, .08);
  font-weight: 400;
}
.fperiod .fpop__panel { top: calc(100% + 9px); left: -35px; }
.fpop__panel--right { left: auto; right: 0; }
.fperiod .fpop__panel--right { left: auto; right: -35px; }
.fpop__panel--period { width: 330px; padding: 10px; }
.fpop__panel--more { width: 340px; padding: 6px 12px 12px; }
.fpop__panel--multi { width: 360px; }
.fpop__label { margin: 10px 0 6px; font-size: 12px; color: var(--c-muted); }
.fpop__search {
  width: 100%; height: 34px; margin-bottom: 6px; padding: 0 10px;
  font: inherit; font-size: 14px; color: var(--c-text);
  background: var(--c-surface-2); border: 1px solid var(--c-border); border-radius: 8px;
}
.fpop__search:focus { outline: 2px solid var(--c-accent); outline-offset: -1px; }
.fpop__foot {
  display: flex; align-items: center; justify-content: flex-end; gap: 12px;
  margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--c-border);
}
.fpop__link { font-size: 13px; color: var(--c-muted); margin-right: auto; }

/* ── Варианты списком ── */
.fopts { list-style: none; margin: 0; padding: 0; max-height: 320px; overflow-y: auto; }
.fopts--compact { max-height: none; }
.fopt {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-radius: 8px;
  color: var(--c-text); text-decoration: none; font-size: 14px; cursor: pointer;
}
.fopt:hover { background: var(--c-surface-2); }
.fopt[aria-current] { color: var(--c-accent); font-weight: 600; }
.fopt__title { flex: 1; min-width: 0; }
.fopt__note { color: var(--c-muted); font-size: 12px; font-weight: 400; white-space: nowrap; }
.fopt__check { width: 16px; height: 16px; visibility: hidden; color: var(--c-accent); }
.fopt[aria-current] .fopt__check { visibility: visible; }
.fopt__dot { width: 10px; height: 10px; border-radius: 999px; }
.fopts--check .fopt__note { font-family: var(--font-mono); }
.fopts__empty { margin: 6px 10px; font-size: 13px; color: var(--c-muted); }

/* ── Период: вкладки, сетка месяцев, формы ── */
.ftabs {
  display: flex; gap: 2px; padding: 3px; margin-bottom: 10px;
  background: var(--c-surface-2); border-radius: 10px;
}
.ftabs__tab {
  flex: 1; height: 30px; padding: 0 6px;
  font: inherit; font-size: 13px; color: var(--c-muted);
  background: transparent; border: 0; border-radius: 7px; cursor: pointer;
}
.ftabs__tab:hover { color: var(--c-text); }
.ftabs__tab[aria-selected="true"] {
  background: var(--c-surface); color: var(--c-text); font-weight: 600;
  box-shadow: 0 1px 3px rgba(20, 16, 40, .12);
}
.fpanel__title { margin: 4px 0 8px; font-size: 13px; color: var(--c-muted); font-weight: 600; }
.fjs .fpanel__title { display: none; }
.fjs .fpanel:not([data-shown]), .fjs .fyear:not([data-shown]) { display: none; }
.fjs .ftabs ~ .fpanel { margin-top: 0; }
.fpanel + .fpanel { margin-top: 14px; }
.fyear + .fyear { margin-top: 10px; }
.fyear__head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 6px; font-size: 14px;
}
.fyear__head b { font-weight: 600; }
.fyear__step {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border: 0; border-radius: 8px;
  background: transparent; color: var(--c-text); cursor: pointer;
}
.fyear__step:hover:not(:disabled) { background: var(--c-surface-2); }
.fyear__step:disabled { color: var(--c-border); cursor: default; }
html:not(.fjs) .fyear__step { visibility: hidden; }
.fmonths { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; }
.fmonth {
  display: flex; align-items: center; justify-content: center; height: 36px;
  border-radius: 8px; font-size: 14px; color: var(--c-text); text-decoration: none;
}
a.fmonth:hover { background: var(--c-surface-2); }
.fmonth.is-now { box-shadow: inset 0 0 0 1px var(--c-border); }
.fmonth.is-on { background: var(--c-accent); color: var(--c-surface); font-weight: 600; box-shadow: none; }
.fmonth.is-off { color: var(--c-muted); opacity: .45; cursor: default; }
.fform { display: grid; gap: 10px; margin-top: 8px; }
.fform__pair { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.fform__field { display: grid; gap: 4px; font-size: 12px; color: var(--c-muted); }
.fform__field input {
  height: var(--control-h); padding: 0 9px; font: inherit; font-size: 14px; color: var(--c-text);
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--radius);
  min-width: 0; width: 100%;
}
.fform .button { justify-self: end; }
.fquick {
  display: flex; flex-wrap: wrap; gap: 6px;
  margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--c-border);
}
.fquick--top { margin: 0 0 4px; padding: 0 0 10px; border-top: 0; border-bottom: 1px solid var(--c-border); }
.fchips { display: flex; flex-wrap: wrap; gap: 6px; }
.fchip {
  display: inline-flex; align-items: center; height: 28px; padding: 0 11px;
  border-radius: 999px; font-size: 13px; text-decoration: none;
  color: var(--c-text); background: var(--c-surface-2); border: 1px solid transparent;
}
.fchip:hover { border-color: var(--c-rule); }
.fchip.is-on { background: var(--fbar-accent-soft, var(--c-surface-2)); border-color: var(--c-accent); color: var(--c-accent); font-weight: 600; }

/* ── Поиск в строке ── */
.fsearch { position: relative; display: inline-flex; align-items: center; flex: 0 1 260px; min-width: 180px; }
.fsearch__ico { position: absolute; left: 12px; color: var(--c-muted); pointer-events: none; }
.fsearch input {
  width: 100%; height: var(--control-h); padding: 0 36px 0 36px;
  font: inherit; font-size: 14px; color: var(--c-text);
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: 999px;
}
.fsearch input::-webkit-search-cancel-button { display: none; }
.fsearch input:focus { outline: 2px solid var(--c-accent); outline-offset: 1px; }
.fsearch.is-active input { background: var(--fbar-accent-soft); border-color: var(--fbar-accent-line); }

/* ── Готовые отборы рядом ── */
.fseg {
  display: inline-flex; gap: 2px; padding: 3px; height: var(--control-h);
  background: var(--c-surface-2); border-radius: 999px;
}
.fseg__item {
  display: inline-flex; align-items: center; gap: 6px; padding: 0 12px;
  border-radius: 999px; font-size: 13.5px; color: var(--c-muted); text-decoration: none;
  white-space: nowrap;
}
.fseg__item:hover { color: var(--c-text); }
.fseg__item[aria-current] {
  background: var(--c-surface); color: var(--c-text); font-weight: 600;
  box-shadow: 0 1px 3px rgba(20, 16, 40, .12);
}
.fseg__n {
  min-width: 20px; padding: 1px 6px; border-radius: 999px; text-align: center;
  font-size: 12px; font-weight: 600; background: var(--c-surface); color: var(--c-muted);
}
.fseg__item[aria-current] .fseg__n { background: var(--fbar-accent-soft); color: var(--c-accent); }

/* ── Сбросить и пояснения ── */
.fbar__reset {
  display: inline-flex; align-items: center; gap: 4px; margin-left: auto;
  height: var(--control-h); padding: 0 8px; border-radius: 999px;
  font-size: 13.5px; color: var(--c-muted); text-decoration: none;
}
.fbar__reset:hover { color: var(--c-text); background: var(--c-surface-2); }
.fbar__reset .ico { width: 14px; height: 14px; }
.fbar-notes { margin-top: -8px; }
.fbar-note { margin: 0 4px; font-size: 12.5px; color: var(--c-muted); }
.fbar-note--warn { color: var(--c-warn); }
.fbar-note + .fbar-note { margin-top: 2px; }
/* Вне .report (списки) строка отделяется от содержимого сама. */
.app-main > .fbar { margin: 4px 0 var(--space); }
.app-main > .fbar-notes { margin: calc(-1 * var(--space) + 6px) 0 var(--space); }

/* ── Телефон ──
   Шапка и строка модуля здесь не закреплены, строка фильтров — у края
   экрана. Плашки едут вбок одной строкой, а не складываются в три: закреплённая
   строка в три яруса закрыла бы полэкрана. Окно выбора — снизу во всю ширину,
   под ним затемнение: щелчок по нему закрывает окно. */
@media (max-width: 760px) {
  .fbar {
    top: 0; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    margin-left: calc(-1 * var(--space)); margin-right: calc(-1 * var(--space));
    padding: 8px var(--space); border-radius: 0; border-left: 0; border-right: 0;
  }
  body:not(:has(.modulebar)) .fbar { top: 0; }
  .fbar::-webkit-scrollbar { display: none; }
  .fbar > * { flex: none; }
  .fsearch { flex: none; width: 220px; }
  .fpop[open]::before {
    content: ""; position: fixed; inset: 0; z-index: 4;
    background: rgba(15, 12, 30, .45);
  }
  .fpop__panel, .fperiod .fpop__panel, .fpop__panel--right, .fperiod .fpop__panel--right {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto;
    width: auto; max-width: none; max-height: 78vh; overflow-y: auto;
    padding: 16px var(--space) calc(18px + env(safe-area-inset-bottom));
    border-radius: 16px 16px 0 0; border-bottom: 0;
    box-shadow: 0 -12px 36px rgba(0, 0, 0, .28);
  }
  .fopts { max-height: none; }
  .fopt { padding: 11px 10px; }
  .fmonth { height: 42px; }
  .app-main > .fbar { margin: 0 calc(-1 * var(--space)) var(--space); }
}
/* Выбор внутри блока (экипажи, малый вес) — та же плашка, что в строке. */
.crews__pick { margin: 4px 0 12px; }
.panel-head .fctl { flex: none; }

/* ── Выбор дня внутри блока (filterbar.day_picker) ──
   Тот же вид, что у периода: стрелки к соседним дням с данными и календарь
   месяца. Дни без данных видны, но не выбираются. */
.panel-tools { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px; }
.panel-tools .button { width: auto; }
.fpop__panel--day { width: 300px; padding: 10px; }
.fdays { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.fdays__wd { padding: 2px 0 4px; text-align: center; font-size: 11.5px; color: var(--c-muted); }
/* Выходные — приглушённо, как в шапке карты: тёплый цвет здесь значит «позже срока». */
.fdays__wd--end { color: var(--c-rule); }
.fdaycell {
  display: flex; align-items: center; justify-content: center; height: 34px;
  border-radius: 8px; font-size: 14px; font-variant-numeric: tabular-nums;
  color: var(--c-text); text-decoration: none;
}
a.fdaycell:hover { background: var(--c-surface-2); }
.fdaycell.is-weekend { color: var(--c-muted); }
.fdaycell.is-now { box-shadow: inset 0 0 0 1px var(--c-border); }
.fdaycell.is-on { background: var(--c-accent); color: var(--c-surface); font-weight: 600; box-shadow: none; }
.fdaycell.is-off { color: var(--c-muted); opacity: .4; cursor: default; }
.fdaycell.is-off.is-on { opacity: 1; background: var(--c-surface-2); color: var(--c-muted); }
.fdaycell:focus-visible { outline: 2px solid var(--c-accent); outline-offset: 1px; }
.fpop__hint { margin: 8px 2px 0; font-size: 12px; color: var(--c-muted); }
@media (max-width: 760px) { .fdaycell { height: 42px; } .panel-tools { justify-content: flex-start; } }

/* --- Загрузка и прессование (решение заказчика 01.10.2026, ADR-0035) --- */
/* Выбранная машина — строка таблицы под ней же на графике. */
.data-table tr.row--selected td { background: var(--c-surface-2); font-weight: 600; }
/* Обычный уровень машины — сплошная, порог недогруза — пунктир, обычные
   т/м³ — точечный луч из нуля: точки ниже него легче на м³. */
.chart .refline { stroke: var(--c-text); stroke-width: 1; opacity: .55; }
.chart .refline--limit { stroke-dasharray: 5 4; }
.chart .refline--ray { stroke-dasharray: 1.5 3; stroke-width: 1.4; opacity: .7; }
.swatch--refline { width: 14px; height: 0; border-top: 1.5px dashed var(--c-text);
                   background: transparent; }
.swatch--ray { width: 14px; height: 0; border-top: 1.5px dotted var(--c-text);
               background: transparent; }
/* Галочка «Последний рейс дня» — отдельной плашкой с пояснением, крупнее
   фильтров: от неё зависят все числа блока (замечание заказчика 01.10.2026). */
.press-last {
  --fbar-accent-soft: color-mix(in srgb, var(--c-accent) 11%, var(--c-surface));
  --fbar-accent-line: color-mix(in srgb, var(--c-accent) 42%, var(--c-surface));
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px 16px;
  margin: 14px 0 16px; padding: 12px 16px; border-radius: 8px;
  border: 1px solid var(--fbar-accent-line); border-left: 4px solid var(--c-accent);
  background: var(--fbar-accent-soft);
}
.press-last .fpill--toggle {
  height: 40px; padding: 0 16px; gap: 9px; font-size: 14.5px; font-weight: 600;
  background: var(--c-surface);
}
.press-last .fpill--toggle.is-active { background: var(--c-surface); border-color: var(--c-accent); }
.press-last .fpill__box { width: 19px; height: 19px; border-width: 2px; border-color: var(--c-accent); }
.press-last__note { font-size: 13px; color: var(--c-text); max-width: 72ch; }
/* Прессы на шкале м³ за рейс: имя, полоса, м³, т/м³, недогрузы. */
.rank--press { --rank-fixed: 444px; }
.rank--press .rank-row, .rank--press .rank-head {
  grid-template-columns: 170px 1fr 64px 86px 84px;
}
.rank--press .rank-head__main, .rank--press .rank-head__sec { white-space: nowrap; }
.rank-row.is-selected .rank-row__name { color: var(--c-text); font-weight: 700; }
/* Машины с малым числом рейсов — под разделителем, полоса приглушена: с
   остальными их не сравнивают (заказчик 01.10.2026). */
.rank-divider {
  display: flex; align-items: center; gap: 10px; margin: 4px 0 8px;
  font-family: var(--font-cond); font-weight: 600; font-size: 10.5px;
  letter-spacing: .06em; text-transform: uppercase; color: var(--c-muted);
}
.rank-divider::before, .rank-divider::after {
  content: ""; flex: 1 1 24px; border-top: 1.5px dashed var(--c-rule);
}
.rank-divider::before { flex: 0 0 24px; }
.rank-rows--few { padding-bottom: 0; }
.rank-row--few .rank-row__bar { opacity: .4; }
/* На узком экране подписи короче, иначе полосе не остаётся места. */
.rank-narrow { display: none; }
@media (max-width: 620px) {
  .rank--press { --rank-fixed: 252px; --rank-track: 99px; }
  .rank--press .rank-row, .rank--press .rank-head {
    grid-template-columns: 92px 1fr 46px 52px 34px;
  }
  .rank--press .rank-wide { display: none; }
  .rank--press .rank-narrow { display: inline; }
}

/* ═══ Сравнение прессов (ADR-0044) ═══
   Факты, а не оценки: отличие от группы выше и ниже подсвечено одним и тем же
   нейтральным тоном, направление показывает знак ▲ ▼. Зелёный и красный здесь
   сказали бы «хорошо» и «плохо», а этого из цифр не видно (заказчик 16.10.2026). */
.cmp-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
/* Знак ▲ ▼ стоит в своём поле справа, у всех числовых ячеек одинаковом: иначе
   число со знаком сдвигается влево и столбец теряет ровный правый край */
.cmp-table th, .cmp-table td { white-space: nowrap; vertical-align: middle; padding: 8px 7px; }
.cmp-table th.num, .cmp-table td.num {
  text-align: right; font-variant-numeric: tabular-nums; padding-right: 19px; position: relative;
}
.cmp-groups th { text-align: center; font-size: 12px; color: var(--c-muted); border-bottom: 1px solid var(--c-border); }
.cmp-groups th[rowspan] { text-align: left; vertical-align: bottom; color: var(--c-text); font-size: 14px; }
.cmp-groups th.num[rowspan] { text-align: right; }
.cmp-table thead th { position: static; }
.cmp-group-row th, .cmp-group-row td { background: var(--c-surface-2); font-weight: 600; }
.cmp-district th {
  background: var(--c-surface); font-family: var(--font-cond); font-size: 13px;
  letter-spacing: .04em; text-transform: uppercase; color: var(--c-muted); padding-top: 14px;
}
.cmp-few th, .cmp-few td { color: var(--c-muted); }
.cmp-group-row .cmp-muted { color: var(--c-muted); font-weight: 400; }
/* С 16.10.2026 выше и ниже группы — двумя тонами, как в согласованном наброске
   (оранжевый — выше, синий — ниже), с серой серединой: это направление, а не
   «хорошо / плохо», поэтому не зелёный и не красный; знак ▲ ▼ остаётся */
.cmp-cell--up { background: color-mix(in srgb, var(--c-series-3) 20%, var(--c-surface)); }
.cmp-cell--down { background: color-mix(in srgb, var(--c-series-4) 18%, var(--c-surface)); }
.swatch.cmp-swatch--up { display: inline-block; background: color-mix(in srgb, var(--c-series-3) 20%, var(--c-surface)); box-shadow: inset 0 0 0 1px var(--c-rule); }
.swatch.cmp-swatch--down { display: inline-block; background: color-mix(in srgb, var(--c-series-4) 18%, var(--c-surface)); box-shadow: inset 0 0 0 1px var(--c-rule); }
.cmp-mark { position: absolute; right: 5px; top: 50%; transform: translateY(-50%); font-size: 10px; color: var(--c-muted); }
.cmp-pace .cmp-mark { top: 10px; transform: none; }
.cmp-usual { display: block; font-size: 11.5px; color: var(--c-muted); font-weight: 400; }
.cmp-help { margin-top: 12px; font-size: 13px; }
.cmp-help summary { cursor: pointer; color: var(--c-accent); }
.cmp-help ul { margin: 8px 0 0; padding-left: 20px; max-width: 96ch; }
.cmp-help li { margin-bottom: 6px; }
.cmp-neighbours { max-width: 560px; }

/* ═══ Загрузка за неделю: из чего сложился день (ADR-0045) ═══
   Полоса в часах от нуля: сбор по маршруту, сверх расчёта (или пунктиром —
   насколько быстрее), рейсы до полигона; пунктирная линия — день группы.
   Колонки заданы один раз: шапка и строки не разъезжаются. */
.wd { --wd-cols: 190px minmax(260px, 1fr) 210px 250px; }
.wd-head, .wd-row { display: grid; grid-template-columns: var(--wd-cols); gap: 6px 16px; align-items: center; }
/* Машина по дням: подпись дня короткая, а время, разница и день группы длиннее */
.wd-head--days, .wd-row--day { grid-template-columns: 96px minmax(240px, 1fr) 330px 230px; }
.wd-head {
  padding: 2px 8px 6px; font-family: var(--font-cond); font-weight: 600; font-size: 10.5px;
  letter-spacing: .08em; text-transform: uppercase; color: var(--c-muted);
}
.wd-axis { position: relative; height: 14px; }
.wd-axis i {
  position: absolute; top: 0; transform: translateX(-50%); font-style: normal;
  font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0; white-space: nowrap;
  text-transform: none;
}
.wd-axis i:first-child { transform: none; }
.wd-axis i:last-child { transform: translateX(-100%); }
.wd-district {
  margin: 14px 0 0; padding: 0 8px 4px; font-family: var(--font-cond); font-size: 12px; font-weight: 700;
  letter-spacing: .05em; text-transform: uppercase; color: var(--c-muted);
}
.wd-row { padding: 7px 8px; border-bottom: 1px solid var(--c-border); }
.wd-row:hover, .wd-row:focus { background: var(--c-surface-2); outline: none; }
.wd-row.is-selected { box-shadow: inset 3px 0 0 var(--c-accent); background: var(--c-surface-2); }
.wd-row--few { opacity: .7; }
.wd-name { display: grid; gap: 2px; min-width: 0; }
.wd-name a, .wd-name b { font-family: var(--font-mono); font-size: 13px; font-weight: 600;
                         color: var(--c-text); white-space: nowrap; text-decoration: none; }
.wd-name a:hover { text-decoration: underline; }
.wd-name .status { font-size: 12px; font-weight: 600; }
.wd-track { position: relative; height: 22px; background: var(--c-surface-2); }
.wd-row:hover .wd-track, .wd-row.is-selected .wd-track { background: var(--c-surface); }
.wd-track--off { opacity: .45; }
.wd-grid { position: absolute; top: 0; bottom: 0; border-left: 1px solid var(--c-border); }
/* Куски полосы стоят встык; между ними тонкий просвет цвета фона */
.wd-part { position: absolute; top: 4px; height: 14px; box-shadow: inset -2px 0 0 var(--c-surface-2); }
.wd-part--plan { background: var(--c-series-4); }
.wd-part--collect { background: color-mix(in srgb, var(--c-series-4) 55%, var(--c-surface)); }
.wd-part--over { background: var(--c-series-3); }
.wd-part--haul { background: color-mix(in srgb, var(--c-muted) 45%, var(--c-surface)); box-shadow: none; }
.wd-part--fast { background: transparent; border: 1.5px dashed var(--c-series-4); box-sizing: border-box; box-shadow: none; }
.wd-group { position: absolute; top: -2px; bottom: -2px; border-left: 2px dashed var(--c-text); opacity: .55; }
.swatch.wd-swatch { display: inline-block; }
.swatch.wd-swatch--plan { background: var(--c-series-4); }
.swatch.wd-swatch--over { background: var(--c-series-3); }
.swatch.wd-swatch--haul { background: color-mix(in srgb, var(--c-muted) 45%, var(--c-surface)); }
.swatch.wd-swatch--fast { border: 1.5px dashed var(--c-series-4); box-sizing: border-box; }
.wd-swatch--group { display: inline-block; width: 0; height: 12px; border-left: 2px dashed var(--c-text); opacity: .55; vertical-align: middle; margin: 0 4px; }
.wd-sub { margin: 18px 0 6px; font-size: 15px; }
@media (max-width: 1080px) {
  /* Узкий экран: машина и итог недели в первой строке, полоса во всю ширину,
     длительность дня под ней */
  .wd-head { display: none; }
  .wd-row { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .wd-row > .wd-track { grid-column: 1 / -1; order: 2; }
  .wd-row > .wd-name { order: 0; }
  .wd-row > .wl-text:last-child { order: 1; text-align: right; }
  .wd-row > .wl-text { order: 3; }
}

/* Таблица «сравнение с группой»: шапка закреплена (заказчик 16.10.2026).
   На широком экране (от 1440 px) таблица — около 1200 px — помещается без
   боковой прокрутки: обёртка не прокручивается, и шапка держится за верх экрана
   под панелями сайта (56 + 45 px). На узком обёртке нужна боковая прокрутка, а с
   ней шапка за экран держаться не может: тогда таблица прокручивается внутри
   своего блока высотой с экран, и шапка держится за верх блока. Высота первой
   строки шапки задана точно: вторая строка стоит сразу под ней. */
.cmp-scroll { max-height: calc(100vh - 120px); overflow: auto; }
/* Раздельные границы: при слитых закреплённая шапка оставляет щель в 1–2 px, и
   в неё видны строки, проезжающие под шапкой */
.cmp-scroll .cmp-table { border-collapse: separate; border-spacing: 0; }
.cmp-scroll .cmp-table thead th {
  position: sticky; top: 0; z-index: 3; background: var(--c-surface);
  box-shadow: inset 0 -1px 0 var(--c-border);
}
.cmp-scroll .cmp-groups th { height: 30px; padding-top: 6px; padding-bottom: 6px; line-height: 18px; box-sizing: border-box; }
.cmp-scroll .cmp-table thead tr:nth-child(2) th { top: 30px; }
/* «Машина» — уголок шапки и первой колонки; первая ячейка второй строки шапки
   («КП») вбок не закрепляется, иначе ложилась бы поверх «Машины» */
.cmp-scroll .cmp-table thead tr:first-child th:first-child { left: 0; z-index: 5; }
.cmp-scroll .cmp-table thead tr:nth-child(2) th:first-child { left: auto; }
@media (min-width: 1440px) {
  .cmp-scroll { max-height: none; overflow: visible; }
  .cmp-scroll .cmp-table thead tr:first-child th { top: 101px; }
  .cmp-scroll .cmp-table thead tr:nth-child(2) th { top: 131px; }
}

/* ═══════════════ Табель (1.55.0) ═══════════════
   Месяц «люди × дни», как бумажный табель: дни 1–15, итог «до 15», дни с 16.
   Колонки дней узкие, чтобы 31 день и итоги помещались на 1920 px без боковой
   прокрутки. Смена — буква «Р»; ходка от третьей — «+1» над ней; две машины
   за день — пунктирная рамка и знак в подсказке, не только цвет. */
.page-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.page-tools form { margin: 0; }
.timesheet .panel { margin-top: 18px; }
.timesheet .kpis { margin-top: 18px; }
.ts-grid { width: auto; }
.ts-grid th, .ts-grid td { padding: 5px 6px; }
.ts-grid__name { white-space: nowrap; max-width: 330px; overflow: hidden; text-overflow: ellipsis; }
.ts-grid__plate { font-size: 12.5px; color: var(--c-muted); }
.data-table .ts-grid__day { text-align: center; min-width: 30px; padding-left: 2px; padding-right: 2px;
  font-variant-numeric: tabular-nums; }
.ts-grid__day--weekend { color: var(--c-muted); }
.ts-grid__weekday { display: block; font-weight: 400; font-size: 10.5px; color: var(--c-muted); }
.data-table .ts-grid__cell { text-align: center; padding: 0; font-weight: 600; font-size: 13px; }
.ts-grid__cell a { display: block; padding: 5px 2px; color: var(--c-text); text-decoration: none; }
.ts-grid__cell a:hover, .ts-grid__cell:focus-within a { background: var(--c-surface-2); }
.ts-grid__cell--two a { outline: 1.5px dashed var(--c-warn); outline-offset: -3px; }
.data-table .ts-grid__sum { text-align: center; font-family: var(--font-mono); font-size: 12.5px;
  font-variant-numeric: tabular-nums; background: var(--c-surface-2); }
.ts-grid__total { font-weight: 700; }
.ts-grid__group th { text-align: left; font-family: var(--font-cond); font-size: 13px; letter-spacing: .04em;
  color: var(--c-muted); background: var(--c-surface); padding-top: 12px; }
.ts-extra { font-size: 9.5px; font-weight: 700; color: var(--c-series-3); margin-left: 1px; }
.ts-mark { display: inline-block; min-width: 22px; padding: 1px 3px; text-align: center; font-weight: 600;
  color: var(--c-text); border: 1px solid var(--c-border); }
.ts-mark--two { outline: 1.5px dashed var(--c-warn); outline-offset: -3px; }
.ts-chip { display: inline-block; margin: 2px 6px 2px 0; padding: 1px 7px; border: 1px solid var(--c-border);
  border-radius: 999px; font-size: 12.5px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ts-chip--extra { border-color: var(--c-series-3); color: var(--c-text);
  background: color-mix(in srgb, var(--c-series-3) 12%, var(--c-surface)); }
.ts-issues { display: grid; grid-template-columns: repeat(auto-fit, minmax(360px, 1fr)); gap: 18px; }
.ts-issues h3 { font-size: 14px; margin: 0 0 6px; }
.ts-issues .data-table { width: 100%; }
@media (max-width: 640px) {
  .ts-issues { grid-template-columns: 1fr; }
  .ts-grid__name { max-width: 150px; }
}

/* Табель, отметки диспетчера (1.56.0). Отметка видна не только цветом: у
   отмеченного дня уголок, у «не смены» (отпуск, больничный) приглушённый текст
   и код буквами. */
.ts-grid__cell--edited a { position: relative; }
.ts-grid__cell--edited a::after, .ts-mark--edited::after {
  content: ""; position: absolute; top: 2px; right: 2px; width: 5px; height: 5px;
  border-radius: 50%; background: var(--c-series-4); }
.ts-mark--edited { position: relative; }
.ts-grid__cell--off a, .ts-mark--off { color: var(--c-muted); font-weight: 500; }
.data-table .ts-grid__cell { font-size: 12.5px; white-space: nowrap; }
.ts-grid__cell--empty a { display: block; min-height: 26px; }
.ts-date input { height: var(--control-h); }
.ts-banner { margin-top: 18px; }
.ts-day th, .ts-day td { vertical-align: top; }
.ts-day__who { min-width: 220px; }
.ts-day__plate { display: block; font-size: 12px; font-weight: 400; color: var(--c-muted); }
.ts-day__warn { display: block; font-weight: 400; white-space: normal; }
.ts-day__disp { max-width: 520px; }
.ts-day__input select { min-width: 150px; }
.ts-day__num input { width: 70px; }
.ts-day__comment input { width: 100%; min-width: 220px; }
.ts-day__who-edit { display: block; font-size: 11.5px; color: var(--c-muted); margin-top: 3px; }
.ts-day .field__error { display: block; }
.ts-add__fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 0 16px; }
.ts-save { position: sticky; bottom: 0; padding: 12px 0; background: var(--c-bg); z-index: 2; }
.ts-period { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 0 16px;
  align-items: end; max-width: 980px; }

/* Табель, решения по disp (1.57.0): у задания «Кто ехал», у разгрузки решение
   под «Решения по ходкам». Повтор, который не считается, перечёркнут и без
   номера: видно не только цветом. */
.ts-task, .ts-trip { display: grid; grid-template-columns: 215px minmax(0, 1fr); align-items: center;
  gap: 4px 10px; margin-bottom: 6px; }
.ts-task__head { line-height: 1.35; }
.ts-task__who { display: inline-flex; align-items: center; gap: 6px; margin: 0; font-size: 12.5px;
  color: var(--c-muted); }
.ts-task__who > span { width: 58px; flex: none; }
.ts-task__who select { height: var(--control-h); width: 260px; max-width: 100%; }
.ts-trips-line { margin-top: 2px; }
.ts-chip--skip { color: var(--c-muted); text-decoration: line-through; }
.ts-trips { margin-top: 6px; }
.ts-trips summary { cursor: pointer; font-size: 12.5px; color: var(--c-accent); }
.ts-trip { margin: 6px 0 0; }
.ts-trip .ts-chip { justify-self: start; }
.ts-check { display: flex; align-items: center; gap: 6px; margin: 8px 0 0; font-size: 12.5px;
  color: var(--c-muted); font-weight: 400; }
.ts-day__disp { min-width: 520px; max-width: 560px; }
@media (max-width: 640px) {
  .ts-day__disp { min-width: 0; }
  .ts-task, .ts-trip { grid-template-columns: 1fr; }
}
.ts-decisions__title { font-size: 14px; margin: 22px 0 6px; }
/* Случаев с решениями стало больше (1.57.0): три колонки на 1920 px вместо
   четырёх, иначе ФИО переносятся по слову, а таблица вылезает за край. */
.ts-issues { grid-template-columns: repeat(auto-fit, minmax(480px, 1fr)); }
.ts-issues .data-table { table-layout: auto; }
@media (max-width: 640px) {
  .ts-issues { grid-template-columns: 1fr; }
}
