/*
  style.css — визуальный язык «Зачёт» (этап 3 «Веб-скелет», волна 2б).
  Источники канона:
    - docs/zachet/юикс-требования.md, §3 «Адаптивность», §4 «Визуальный язык»
      (палитра §4.1/4.2, подсветка расхождений §4.3, типографика §4.4).
    - Палитра/акцент унаследованы у scripts/wb_cs_flow/index.html (синий
      #3B5BDB, светлый фон #EEF1F5) — чтобы два внутренних инструмента
      одного бизнеса не выглядели продуктами разных компаний.

  ПРАВИЛО (юикс-требования.md §4, критерий приёмки №12): хардкод цвета,
  размера шрифта или отступа МИМО токена --zc-* в шаблонах/компонентах
  запрещён — весь дизайн идёт через custom properties этого файла.

  Брейкпоинты — mobile-first (базовые правила без media query — для S,
  затем min-width для M/L). Причина порядка объяснена в юикс-требования.md
  §3.1: при обратном порядке любое незакрытое переопределение ломает именно
  телефон, то есть главного пользователя (Эльвира).
    S — 320-767px  (базовые правила, без media query)
    M — 768-1199px (min-width: 768px)
    L — >=1200px   (min-width: 1200px)
  Доп. точка 480px — «сверхкомпактный» режим внутри S (подписи нижней
  навигации скрываются).
*/

/* ============================================================ ТОКЕНЫ ===
   ДВЕ ТЕМЫ, ТЁМНАЯ — УМОЛЧАНИЕ (решение владельца 25.08.2026, PRD
   docs/zachet/2026-08-25-tyomnaya-tema-zachet.md, дизайн-бук
   docs/zachet/дизайн-бук-тёмная-тема.md §8.3).
   `:root` несёт ТЁМНЫЕ значения: если атрибут data-theme почему-то не доехал,
   страница остаётся рабочей и тёмной, а не бесцветной.
   `prefers-color-scheme` НЕ участвует в выборе темы вообще — см. §10 бука.
   Наборов три: тёмный (`:root`), светлый (`:root[data-theme="light"]`)
   и бумажный (`@media print`). Набор имён во всех трёх ОДИНАКОВ (правило Д-54).

   СВЕТЛАЯ ТЕМА НЕ РЕДИЗАЙНИТСЯ (PRD, «Вне периметра»): из палитры меняется
   ровно один токен — `--zc-line-strong` (был `#A9B2BF`, давал 2.14 на белом,
   провал SC 1.4.11; стал `#838EA0`, 3.31). Прочие светлые значения численно
   совпадают с тем, что было. Реально заметные отклонения светлой темы —
   только сила теней (5 пунктов, дизайн-бук §15.3), и для двух самых заметных
   мест (кнопка консультанта, подложка нижнего листа) исходная сила тени
   сохранена буквально через отдельные токены `--zc-shadow-consultant-btn`
   и `--zc-scrim-sheet` (см. компоненты ниже). */
:root{
  color-scheme: dark;

  /* поверхности */
  --zc-bg:              #0F1319;
  --zc-surface:         #161B22;
  --zc-surface-2:       #1C222B;
  --zc-surface-3:       #232A35;
  --zc-surface-sunken:  #0A0D12;

  /* слои высоты (elevation) — отдельно от --zc-surface-*, см. дизайн-бук §8.2 */
  --zc-elev-2:          #1C222B;
  --zc-elev-3:          #232A35;
  --zc-elev-4:          #2B3340;
  /* Слой 5 (26.08.2026, дефект «модалка в темноте не поднимается над страницей»).
     Семейство всплывающих ушло на ступень вверх — `.zc-modal`,
     `.zc-filterwin`, шторка «Ещё», панель консультанта и оба поповера подсказки
     теперь лежат на `--zc-elev-4`. Подсказке `.zc-hintwin`, которая по построению
     ложится ПОВЕРХ модалки (§9.6 бука), нужна ступень выше — иначе два окна
     сравниваются по светлоте и «поверх» перестаёт читаться.
     `#333C4A` — следующий шаг лестницы: L 21.0 % -> 24.5 % (+3.5 п.п., коридор
     3–5 п.п. §2). В светлой = `#FFFFFF`, как и остальные `--zc-elev-*`. */
  --zc-elev-5:          #333C4A;

  /* ПЛОСКАЯ ЗАЛИВКА ПОВЕРХ ПОДНЯТОГО СЛОЯ — СОБСТВЕННАЯ РОЛЬ (25.08.2026).
     Дизайн-бук отдал эту работу `--zc-surface-3`, а его значение в тёмной
     совпало со значением `--zc-elev-3` (#232A35). Совпадение сожгло смысл там,
     где заливка ЛЕЖИТ НА слое 3: ховер крестика модалки, крестик шторки
     консультанта, неактивная кнопка «Отправить», плашка кратности короба в
     подсказке артикула — все четыре давали контраст к своей подложке РОВНО
     1.000, то есть исчезали (замер в браузере 25.08.2026). В светлой теме те
     же пары дают 1.186 — смысл был, и в тёмной он обязан сохраниться.
     Значение подобрано так, чтобы повторить светлую: 1.188 к `--zc-elev-3`.
     `--zc-surface-3` при этом НЕ трогается — он остаётся заливкой на фоне
     страницы и на слое 2 (например, число во вкладке `.zc-tabs__num`).

     26.08.2026: #2E3644 -> #37404F. Хозяин заливки уехал с `--zc-elev-3` на
     `--zc-elev-4`, и прежнее значение давало к нему 1.047 — та же болезнь
     «заливка растворилась в слое», от которой этот токен и заводился. Новое
     значение держит к `--zc-elev-4` 1.217 (роль сохранена), `--zc-ink-muted`
     на нём — 4.89. Светлое значение не тронуто. */
  --zc-fill-on-elev:    #37404F;

  /* текст */
  --zc-ink:             #E7EBF2;
  --zc-ink-muted:       #A9B2C1;
  --zc-ink-faint:       #8B95A6;
  --zc-ink-disabled:    #6B7584;
  --zc-ink-invert:      #0D1117;

  /* линии */
  --zc-line:            #333C4A;
  --zc-line-soft:       #262D38;
  --zc-line-strong:     #727C8B;

  /* акцент */
  --zc-accent:        #8098EE;
  --zc-accent-hover:  #93A6F2;
  --zc-accent-active: #6579CC;
  --zc-accent-soft:   #1C2547;
  --zc-focus:         #9BB0FF;

  /* семантика действий / расхождений */
  --zc-success:      #43D18C;  --zc-success-bg:  #0F2A20;  --zc-success-line: #35A375;
  --zc-warning:      #E5A13A;  --zc-warning-bg:  #2C2010;  --zc-warning-line: #B98328;
  --zc-danger:       #F2695F;  --zc-danger-bg:   #3A1C19;  --zc-danger-line:  #C4544A;
  --zc-neutral:      #9AA5B4;  --zc-neutral-bg:  #1D232C;  --zc-neutral-line: #667289;
  --zc-info:         #6EA8FF;  --zc-info-bg:     #12203A;  --zc-info-line:    #4A82D6;

  /* заливка опасного действия (кнопка danger-confirm) — отдельная тройка,
     т.к. текст поверх --zc-danger-line в тёмной даёт 4.25 (провал 4.5) */
  --zc-danger-solid:        #B0463A;
  --zc-danger-solid-hover:  #A83E33;
  --zc-danger-on:           #E7EBF2;

  /* СТРОКИ ТАБЛИЦЫ — ТОЛЬКО НЕПРОЗРАЧНЫЕ ЦВЕТА, ПОЛУПРОЗРАЧНАЯ ПЛЁНКА ЗДЕСЬ
     ЗАПРЕЩЕНА (25.08.2026, возврат дефекта 07.08.2026 — история в блоке
     «СТРОКА ОБЯЗАНА ИМЕТЬ СВОЙ НЕПРОЗРАЧНЫЙ ФОН» ниже по файлу).
     Приклеенная колонка берёт `background: inherit`, то есть ВЫЧИСЛЕННЫЙ фон
     своей строки. Плёнка `rgba(255,255,255,.035)` наследуется плёнкой — и под
     номером документа снова едет чужой текст («2026-000002» поверх соседней
     колонки читалось как «02026-0000102»). Владелец ловил это глазами дважды.

     Значения — ТОЧНЫЕ КОМПОЗИТЫ прежних плёнок поверх `--zc-surface`,
     посчитанные в самом дизайн-буке (§8.2, столбец «Тёмная»):
       rgba(255,255,255,.035) над #161B22 -> #1E232A  (CR 1.09 к строке)
       rgba(255,255,255,.06)  над #161B22 -> #24292F  (CR 1.18 к строке)
     То есть на карточке зебра и наведение выглядят ровно как выглядели, но
     наследуются НЕПРОЗРАЧНЫМИ. Плата за это записана честно: на поверхности,
     отличной от `--zc-surface` (таблица внутри модалки), полоса зебры теперь
     не «осветляет фон», а красит своим цветом — за это и держится правило,
     что зебра включена ТОЛЬКО на широком экране, где таблица лежит в карточке
     (см. `.zc-table tbody tr:nth-child(even)` ниже). */
  --zc-row-zebra:     #1E232A;
  --zc-row-hover:     #24292F;
  --zc-row-selected:  #1C2547;

  /* поля ввода */
  --zc-field-disabled-bg: #1A1F27;
  --zc-autofill-bg:       #1B2031;
  --zc-field-inset:       inset 0 1px 2px rgba(0,0,0,.5);

  /* служебные краски */
  /* ЗАТЕМНЕНИЕ ПОД МОДАЛКОЙ — .72 -> .90 (26.08.2026, замер).
     Прежняя плёнка поверх и без того тёмной страницы почти ничего не делала:
     страница уходила с `#0F1319` на `#080A0E`, а текст `--zc-ink` под ней
     держал 1.66 к своей подложке — даты, суммы и плашки таблицы читались
     сквозь окно, и модалка выглядела наложенной, а не поднятой (в светлой
     теме та же плёнка уводит фон на три ступени, в тёмной — на полступени).
     При .90 текст под плёнкой падает до 1.19, то есть страница гаснет.
     Светлое значение не тронуто. Токен используется РОВНО в одном месте —
     `.zc-modal::backdrop`. */
  --zc-scrim:        rgba(4,6,10,.90);
  /* Матовое размытие подложки — второе средство «поднять, а не затемнить»:
     оно убирает читаемость страницы за окном, не трогая ни одного контраста.
     В светлой теме — `none`, там подложка и так работает. */
  --zc-scrim-blur:   blur(3px);
  --zc-scroll-fade:  rgba(0,0,0,.55);
  --zc-shadow-color: rgba(0,0,0,.65);
  --zc-thumb-bg:     #1C222B;

  /* Тело алерта в тёмной — нейтральным --zc-ink, а не цветом заголовка
     (дизайн-бук §9.5): цветной текст на цветном тинте «вибрирует». В светлой
     тело остаётся цветом заголовка, как сегодня — отсюда `inherit` там же. */
  --zc-alert-body-ink: var(--zc-ink);

  /* два места, где СВЕТЛОЕ значение силы тени сохранено буквально (задача
     аппликатора, п.3): кнопка консультанта (была rgba(20,30,45,.28)) и
     подложка листа «Ещё» (была rgba(20,30,45,.35)) — не через общие
     --zc-shadow-2/--zc-scrim, чтобы не сблизить их с остальными в светлой.
     В тёмной у обоих ролевое значение — дизайн-бук §9.4 отдаёт консультанту
     ровно --zc-shadow-3, а подложка листа держится общей логики --zc-scrim. */
  --zc-shadow-consultant-btn: 0 0 0 1px rgba(255,255,255,.10),
                               0 8px 16px -4px rgba(0,0,0,.55),
                               0 20px 32px -8px rgba(0,0,0,.65);
  /* Подложка листа «Ещё» — та же болезнь и то же лечение, что у `--zc-scrim`
     (26.08.2026): при .72 страница за листом оставалась читаемой. */
  --zc-scrim-sheet:           rgba(4,6,10,.90);

  /* ---- КРАСКИ ПОДНЯТЫХ СЛОЁВ (26.08.2026) --------------------------------
     Всплывающие переехали с `--zc-elev-3` на `--zc-elev-4`/`--zc-elev-5`, и
     тонкие краски, подобранные под `--zc-surface`, на этих ступенях
     перестают работать. Бук проговаривает это прямым текстом (§3: «на
     четвёртом слое минимальный уровень — `--zc-ink-muted`»), но токенов под
     правило не завёл. Здесь они и заводятся — по одному на краску, значение
     в СВЕТЛОЙ равно тому, что стоит там сегодня, поэтому светлая тема не
     меняется ни на пиксель.

       --zc-line-on-elev      волосок-разделитель внутри окна.
                              `--zc-line-soft` #262D38 давал на модалке 1.04 —
                              разделители пунктов были на грани видимости
                              (в светлой те же 1.24). #404A59 даёт 1.42.
       --zc-line-mid-on-elev  разделитель/рамка внутри окна (`--zc-line`
                              #333C4A давал 1.14 — рамка чекбокса в окне
                              фильтра пропадала). #4A5568 даёт 1.69.
       --zc-ink-faint-on-elev тонкая подпись и шеврон: `--zc-ink-faint`
                              #8B95A6 на `elev-4` даёт 4.21 — бук ЗАПРЕЩАЕТ
                              его там (§3). Берётся уровень `--zc-ink-muted`:
                              5.95 на elev-4, 5.21 на elev-5.
       --zc-danger-on-elev    текст ошибки: `--zc-danger` #F2695F на elev-4 —
                              4.22, провал 4.5. #F78B80 даёт 5.43 / 4.75.
       --zc-accent-on-elev    ссылка/акцент на elev-5 (`.zc-hintwin`):
                              `--zc-accent` там 4.05, `--zc-accent-hover`
                              #93A6F2 — 4.76. */
  --zc-line-on-elev:      #404A59;
  --zc-line-mid-on-elev:  #4A5568;
  --zc-ink-faint-on-elev: #A9B2C1;
  --zc-danger-on-elev:    #F78B80;
  --zc-accent-on-elev:    #93A6F2;

  /* ---- СЛУЖЕБНЫЙ АКЦЕНТ (26.08.2026) -------------------------------------
     В светлой теме акцент `#3B5BDB` тёмный и уходит НАЗАД; в тёмной `#8098EE`
     светлый и лезет ВПЕРЁД. От этого на первом экране «Документов» полоса
     «ПЕРИОД ОПЕРАЦИЯ СТАТУС …» весила больше самих документов: 60 акцентных
     элементов на экран, из них 42 — вторичные кнопки и их значки.
     `--zc-accent-quiet` — цвет служебного органа управления В ПОКОЕ. Акцент
     возвращается на наведении и остаётся у главной кнопки и у ссылок на
     объекты (номер документа, название товара). В светлой теме значение равно
     `--zc-accent`, то есть светлая не меняется.
     `--zc-cardlink-ink` — цвет содержимого карточки, которая ЦЕЛИКОМ ссылка
     (`a.zc-card`): заголовок такой карточки в тёмной красился акцентом и
     читался как отдельная ссылка внутри ссылки. */
  --zc-accent-quiet:  #A9B2C1;
  --zc-cardlink-ink:  #E7EBF2;

  /* ---- ВЫКЛЮЧЕННАЯ КНОПКА (26.08.2026) -----------------------------------
     `disabled` у ЗАЛИТОЙ кнопки терял контейнер: оставался серый текст без
     фигуры, и человек не понимал, что здесь было действие. Возвращаем силуэт
     заливкой + рамкой. Контраст текста выключенного органа управления WCAG
     не нормирует, но фигура обязана читаться: #232A35 даёт 1.29 к фону
     страницы и 1.20 к карточке. В светлой оба токена прозрачны — там
     выключенная кнопка выглядит ровно как выглядела. */
  --zc-btn-disabled-bg:   #232A35;
  --zc-btn-disabled-line: #333C4A;

  /* ---- СПАРКЛАЙН (26.08.2026) --------------------------------------------
     Линия рисуется сервером (`routes/dashboard.py::sparkline`) цветом
     `var(--zc-accent)` и толщиной 2. В светлой теме это плотная тёмно-синяя
     линия на белом; в тёмной — светло-лавандовая ниточка, тонущая в карточке.
     Вес даётся двумя средствами: светлее (9.10 против 6.30 к карточке) и
     толще. Светлые значения равны сегодняшним. */
  --zc-spark-ink:    #A8B9FF;
  --zc-spark-width:  2.6;

  /* Тень всплывающего окна. Отдельный токен, а не `--zc-shadow-3`/`-4`:
     уровень тени в светлой теме менять нельзя (там она уже принята), а в
     тёмной окно, переехавшее на слой 4, обязано получить и кромку слоя 4
     (Д-52: светлота + кромка + тень поднимаются вместе). Светлое значение —
     БУКВАЛЬНО сегодняшний светлый `--zc-shadow-3`. */
  --zc-shadow-overlay: 0 0 0 1px rgba(255,255,255,.12),
                       0 12px 24px -6px rgba(0,0,0,.60),
                       0 32px 56px -12px rgba(0,0,0,.70);

  /* высота: светлота + световая кромка + тень (правило Д-52, дизайн-бук §7).

     АЛЬФЫ СВЕТОВОЙ КРОМКИ ПОДНЯТЫ ПО ЗАМЕРУ, А НЕ ПО РАСЧЁТУ (25.08.2026).
     Расчёт §7.2 обещал по кромке 1.12 / 1.17 / 1.24 / 1.30, а A/B-замер в
     Chromium (снимок с тенью против снимка с `box-shadow:none`, самый
     контрастный пиксель у края) дал у карточки 1.088, у верхней панели 1.070
     при пороге 1.10: расчёт считал идеальную кромку, а браузер размывает её
     сглаживанием на скруглении и дробной координатой края. Чёрные слои
     вытянуть это не могут — их потолок в темноте 1.13 при альфе 1.0 (§7.1),
     поэтому подняты именно альфы белого кольца и матового блика. Итоговые
     числа замера записаны в отчёте волны; порядок альф по уровням сохранён
     возрастающим — чем выше слой, тем ярче кромка.

     СВЕТОВАЯ КРОМКА СТОИТ ПЕРВОЙ В СПИСКЕ — И ЭТО НЕСУЩАЯ ДЕТАЛЬ, А НЕ
     ПОРЯДОК СЛОВ. Слои `box-shadow` рисуются в обратном порядке: первый в
     списке лежит ПОВЕРХ остальных. Пока кромка стояла последней, чёрная умбра
     (она смещена вниз и накрывает ту же однопиксельную полосу за краем)
     закрашивала её снизу — у верхней панели, где на экран попадает только
     нижний край, кромки не было видно ВООБЩЕ: подъём альфы с .055 до .08 не
     сдвинул замер ни на сотую (1.0703 -> 1.0703), потому что мерился не белый
     ободок, а чёрная тень под ним. С кромкой впереди тот же край даёт 1.19.
  */
  --zc-shadow-1: 0 0 0 1px rgba(255,255,255,.065),
                 0 1px 2px rgba(0,0,0,.45);
  --zc-shadow-2: 0 0 0 1px rgba(255,255,255,.08),
                 0 2px 4px rgba(0,0,0,.45),
                 0 6px 12px -4px rgba(0,0,0,.55);
  --zc-shadow-3: 0 0 0 1px rgba(255,255,255,.10),
                 0 8px 16px -4px rgba(0,0,0,.55),
                 0 20px 32px -8px rgba(0,0,0,.65);
  --zc-shadow-4: 0 0 0 1px rgba(255,255,255,.12),
                 0 12px 24px -6px rgba(0,0,0,.60),
                 0 32px 56px -12px rgba(0,0,0,.70);
  --zc-hairline-top: inset 0 1px 0 rgba(255,255,255,.09);

  /* типографика — системный стек, без веб-шрифтов (юикс-требования §4.4) */
  --zc-font-ui:   system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --zc-font-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;

  --zc-t-display-size: 34px; --zc-t-display-lh: 40px; --zc-t-display-w: 600;
  --zc-t-h1-size:      22px; --zc-t-h1-lh:      28px; --zc-t-h1-w:      600;
  --zc-t-h2-size:      18px; --zc-t-h2-lh:      24px; --zc-t-h2-w:      600;
  --zc-t-body-size:    16px; --zc-t-body-lh:    24px; --zc-t-body-w:    400;
  --zc-t-small-size:   14px; --zc-t-small-lh:   20px; --zc-t-small-w:   400;
  --zc-t-caption-size: 13px; --zc-t-caption-lh: 18px; --zc-t-caption-w: 400;
  --zc-t-micro-size:   12px; --zc-t-micro-lh:   16px; --zc-t-micro-w:   500;

  /* раскладка — поля страницы/промежуток по точкам (юикс-требования §3.1) */
  --zc-page-pad:  16px;
  --zc-gap:       12px;
  --zc-radius:    10px;
  --zc-radius-lg: 16px;

  --zc-topnav-h:    64px;
  --zc-bottomnav-h: 56px;
  --zc-safe-bottom: env(safe-area-inset-bottom, 0px);

  /* Кнопка ИИ-консультанта (раздел «ИИ-КОНСУЛЬТАНТ» в конце файла) — диаметр
     и просвет до того, что под ней (нижняя панель на S/M, низ экрана на L),
     токенами, а не литералами: те же два числа читает и позиция кнопки, и
     резерв места у содержимого (`#zc-main`) — единый источник истины вместо
     двух копий одних и тех же 56/16/24px (та же причина, по которой рядом
     не задублированы `--zc-bottomnav-h`/`--zc-safe-bottom`, см. комментарий
     у `.zc-consultant-btn`). */
  --zc-consultant-size: 56px;
  --zc-consultant-gap:  16px;
}

@media (min-width: 768px){
  :root{ --zc-page-pad: 24px; --zc-gap: 16px; }
}
@media (min-width: 1200px){
  :root{ --zc-page-pad: 32px; --zc-gap: 24px; --zc-consultant-gap: 24px; }
}

/* ============================================== СВЕТЛАЯ ТЕМА (по выбору) ===
   Ставится cookie `zc_theme=light`, читает сервер, атрибут <html data-theme>
   (дизайн-бук §10). Отдельного :root[data-theme="dark"] НЕТ и не заводится —
   тёмные значения лежат в голом :root намеренно (§10.2 бука): страница без
   атрибута остаётся тёмной и рабочей, а не бесцветной. */
:root[data-theme="light"]{
  color-scheme: light;

  --zc-bg:              #EEF1F5;
  --zc-surface:         #FFFFFF;
  --zc-surface-2:       #F6F8FB;
  --zc-surface-3:       #E8ECF2;
  --zc-surface-sunken:  #FFFFFF;

  --zc-elev-2:          #FFFFFF;
  --zc-elev-3:          #FFFFFF;
  --zc-elev-4:          #FFFFFF;
  --zc-elev-5:          #FFFFFF;
  /* Ровно прежний `--zc-surface-3` — светлая тема не меняется ни на пиксель. */
  --zc-fill-on-elev:    #E8ECF2;

  --zc-ink:             #1B2432;
  --zc-ink-muted:       #5B6472;
  --zc-ink-faint:       #8A93A2;
  --zc-ink-disabled:    #6C7382;
  --zc-ink-invert:      #FFFFFF;

  --zc-line:            #CED5DF;
  --zc-line-soft:       #E2E7EE;
  --zc-line-strong:     #838EA0;   /* было #A9B2BF — 2.14 на белом, провал SC 1.4.11 */
  --zc-alert-body-ink:  inherit;   /* в светлой тело алерта = цвет заголовка, как сегодня */

  --zc-accent:        #3B5BDB;
  --zc-accent-hover:  #314CC0;
  --zc-accent-active: #2A42A6;
  --zc-accent-soft:   #E5EAFB;
  --zc-focus:         #1B4DFF;

  --zc-success:      #0B7A54;  --zc-success-bg:  #E6F4EE;  --zc-success-line: #0B7A54;
  --zc-warning:      #8A5300;  --zc-warning-bg:  #FCF2DC;  --zc-warning-line: #C98A0E;
  --zc-danger:       #A3231B;  --zc-danger-bg:   #FBE9E7;  --zc-danger-line:  #C7362B;
  --zc-neutral:      #5B6472;  --zc-neutral-bg:  #F2F4F7;  --zc-neutral-line: #A9B2BF;
  --zc-info:         #1C4E9C;  --zc-info-bg:     #E7EFFA;  --zc-info-line:    #3B5BDB;

  --zc-danger-solid:        #C7362B;
  --zc-danger-solid-hover:  #A3231B;
  --zc-danger-on:           #FFFFFF;

  /* НЕ `transparent`: прозрачную зебру наследует приклеенная колонка, и
     светлая тема получает тот же дефект просвечивания, что и тёмная. Значение
     равно фону строки (`--zc-surface`), поэтому зебры в светлой по-прежнему
     НЕ ВИДНО — она непрозрачна, а не невидима. */
  --zc-row-zebra:     #FFFFFF;
  --zc-row-hover:     #F6F8FB;
  --zc-row-selected:  #E5EAFB;

  --zc-field-disabled-bg: #FFFFFF;
  --zc-autofill-bg:       #FFFFFF;
  --zc-field-inset:       none;

  --zc-scrim:        rgba(27,36,50,.5);
  --zc-scroll-fade:  rgba(27,36,50,.13);
  --zc-shadow-color: rgba(20,30,45,.24);
  --zc-thumb-bg:     #F6F8FB;

  --zc-shadow-consultant-btn: 0 4px 16px rgba(20,30,45,.28);
  --zc-scrim-sheet:           rgba(20,30,45,.35);

  /* Токены, заведённые 26.08.2026 ради глубины ТЁМНОЙ темы. Каждое светлое
     значение равно тому, что светлая тема рисует сегодня, — она не меняется. */
  --zc-scrim-blur:        none;                /* размытия в светлой не было */
  --zc-line-on-elev:      #E2E7EE;             /* = --zc-line-soft */
  --zc-line-mid-on-elev:  #CED5DF;             /* = --zc-line */
  --zc-ink-faint-on-elev: #8A93A2;             /* = --zc-ink-faint */
  --zc-danger-on-elev:    #A3231B;             /* = --zc-danger */
  --zc-accent-on-elev:    #3B5BDB;             /* = --zc-accent */
  --zc-accent-quiet:      #3B5BDB;             /* = --zc-accent */
  --zc-cardlink-ink:      #3B5BDB;             /* = --zc-accent (цвет ссылки) */
  --zc-btn-disabled-bg:   transparent;         /* силуэта у выключенной не было */
  --zc-btn-disabled-line: transparent;
  --zc-spark-ink:         #3B5BDB;             /* = --zc-accent */
  --zc-spark-width:       2;                   /* ровно то, что рисует сервер */
  --zc-shadow-overlay:    0 8px 24px rgba(27,36,50,.18);  /* = светлый --zc-shadow-3 */

  --zc-shadow-1: none;
  --zc-shadow-2: 0 4px 16px rgba(20,30,45,.22);
  --zc-shadow-3: 0 8px 24px rgba(27,36,50,.18);
  --zc-shadow-4: 0 12px 40px rgba(27,36,50,.28);
  /* НЕ буквальный `none`: карточка/модалка комбинируют --zc-hairline-top с
     реальной тенью через запятую в одном box-shadow (§7.3 бука); "none"
     смешанный с настоящим <shadow> в общем списке — невалидное значение
     CSS целиком (IACVT), и модалка потеряла бы тень в светлой теме. Нулевая
     видимость та же, что у буквального none, а синтаксис остаётся валиден. */
  --zc-hairline-top: inset 0 0 0 0 transparent;
}

/* ===================================================== ПЕЧАТЬ (§11 бука) ===
   Печать всегда светлая, независимо от выбранной темы (Д-56): документы и
   отчёты «Зачёта» печатают, тёмная страница на бумаге — залитый тонером лист. */
@media print{
  :root, :root[data-theme="light"]{
    color-scheme: light;

    --zc-bg: #FFFFFF; --zc-surface: #FFFFFF; --zc-surface-2: #FFFFFF;
    --zc-surface-3: #FFFFFF; --zc-surface-sunken: #FFFFFF;
    --zc-elev-2: #FFFFFF; --zc-elev-3: #FFFFFF; --zc-elev-4: #FFFFFF;
    --zc-elev-5: #FFFFFF;
    --zc-fill-on-elev: #FFFFFF;

    --zc-ink: #000000; --zc-ink-muted: #333333; --zc-ink-faint: #555555;
    --zc-ink-disabled: #767676; --zc-ink-invert: #FFFFFF;

    --zc-line: #949494; --zc-line-soft: #CCCCCC; --zc-line-strong: #666666;
    --zc-alert-body-ink: inherit;

    --zc-accent: #1C4E9C; --zc-accent-hover: #1C4E9C; --zc-accent-active: #1C4E9C;
    --zc-accent-soft: #FFFFFF; --zc-focus: #1C4E9C;

    --zc-success: #0B7A54;  --zc-success-bg: #FFFFFF;  --zc-success-line: #0B7A54;
    --zc-warning: #8A5300;  --zc-warning-bg: #FFFFFF;  --zc-warning-line: #8A5300;
    --zc-danger:  #A3231B;  --zc-danger-bg:  #FFFFFF;  --zc-danger-line:  #A3231B;
    --zc-neutral: #5B6472;  --zc-neutral-bg: #FFFFFF;  --zc-neutral-line: #5B6472;
    --zc-info:    #1C4E9C;  --zc-info-bg:    #FFFFFF;  --zc-info-line:    #1C4E9C;

    --zc-danger-solid: #A3231B; --zc-danger-solid-hover: #A3231B;
    --zc-danger-on: #FFFFFF;

    --zc-row-zebra: #FFFFFF; --zc-row-hover: #FFFFFF;
    --zc-row-selected: #FFFFFF;

    --zc-field-disabled-bg: #FFFFFF; --zc-autofill-bg: #FFFFFF;
    --zc-field-inset: none;

    --zc-scrim: transparent; --zc-scroll-fade: transparent;
    --zc-shadow-color: transparent; --zc-thumb-bg: #FFFFFF;
    --zc-shadow-consultant-btn: none; --zc-scrim-sheet: transparent;

    --zc-shadow-1: none; --zc-shadow-2: none;
    --zc-shadow-3: none; --zc-shadow-4: none;
    --zc-hairline-top: none;

    /* Токены глубины тёмной темы (26.08.2026) — на бумаге все нейтральны:
       краски равны своим печатным аналогам, тени и размытие сняты (Д-56). */
    --zc-scrim-blur: none;
    --zc-line-on-elev: #CCCCCC; --zc-line-mid-on-elev: #949494;
    --zc-ink-faint-on-elev: #555555; --zc-danger-on-elev: #A3231B;
    --zc-accent-on-elev: #1C4E9C; --zc-accent-quiet: #1C4E9C;
    --zc-cardlink-ink: #1C4E9C;
    --zc-btn-disabled-bg: transparent; --zc-btn-disabled-line: transparent;
    --zc-spark-ink: #1C4E9C; --zc-spark-width: 2;
    --zc-shadow-overlay: none;
  }

  /* НА БУМАГУ УХОДИТ ДОКУМЕНТ, А НЕ ИНТЕРФЕЙС (25.08.2026, найдено печатью
     страницы документа в PDF: на лист попадали нижняя навигация «Главная /
     Товары / Склады / …» и плавающая кнопка консультанта). До этой правки
     блок печати переопределял только цвета — то есть красил интерфейсную
     обвязку в белый и печатал её. Скрывается ровно обвязка: две навигации,
     переключатель темы, консультант, кнопки действий и пагинация. Содержимое
     страницы — таблицы, карточки, тексты — не трогается. */
  .zc-skip-link,
  .zc-topnav, .zc-bottomnav, .zc-bottomnav__sheet,
  .zc-theme-form,
  .zc-consultant-btn, .zc-consultant-panel,
  .zc-tabs, .zc-pagehead__right, .zc-actions, .zc-bulkbar, .zc-pager{
    display: none !important;
  }
  /* Место, зарезервированное под нижнюю навигацию, на бумаге не нужно. */
  #zc-main{ padding-bottom: 0 !important; }
}

/* ============================================================= RESET === */
*, *::before, *::after{ box-sizing: border-box; }
html, body{
  margin: 0; padding: 0;
  max-width: 100%;
  overflow-x: hidden;               /* критерий приёмки: НЕТ горизонтальной прокрутки страницы */
}
body{
  background: var(--zc-bg);
  color: var(--zc-ink);
  font-family: var(--zc-font-ui);
  font-size: var(--zc-t-body-size);
  line-height: var(--zc-t-body-lh);
  font-weight: var(--zc-t-body-w);
  -webkit-text-size-adjust: 100%;
  padding-bottom: calc(var(--zc-bottomnav-h) + var(--zc-safe-bottom));
}
@media (min-width: 1200px){
  body{ padding-bottom: 0; }
}
img, svg{ max-width: 100%; height: auto; }
a{ color: var(--zc-accent); text-decoration: none; }
a:hover{ text-decoration: underline; }
/* КАРТОЧКА, КОТОРАЯ ЦЕЛИКОМ ССЫЛКА, НЕ КРАСИТ СВОЁ СОДЕРЖИМОЕ АКЦЕНТОМ
   (26.08.2026). На дашборде так сделаны плитки: `<a class="zc-card">`, внутри
   заголовок `.zc-h2`. В светлой теме акцент тёмный и заголовок читался просто
   как заголовок; в тёмной `#8098EE` светлый, и «Расхождения» начинало спорить
   с данными под ним — заголовок карточки выглядел ссылкой внутри ссылки.
   Цель ссылки тут — вся карточка, а не слово, поэтому слово перестаёт краситься.
   `:where()` обнуляет вес селектора НАМЕРЕННО: правило обязано перебить общее
   `a{}` выше, но проиграть любому явному правилу раздела (на дашборде это
   `.zc-dash__card{color: inherit}`), иначе в СВЕТЛОЙ теме плитки с числами
   стали бы синими. В светлой `--zc-cardlink-ink` = `--zc-accent`.
   Правила на `:hover` здесь СОЗНАТЕЛЬНО НЕТ: `:hover` весит как класс, и
   `a:where(.zc-card):hover` перебило бы `.zc-dash__card{color: inherit}` —
   в СВЕТЛОЙ теме плитки синели бы под курсором. Признак наведения у карточки
   и так есть: подчёркивание (`a:hover` выше) и подсветка рамки. */
a:where(.zc-card){ color: var(--zc-cardlink-ink); }
/* ЗАГОЛОВОК КАРТОЧКИ, КОТОРЫЙ САМ ССЫЛКА, — та же история и то же лечение:
   «Заканчивается товар — 3 позиц.» на дашборде это `<a>` внутри `.zc-h2`.
   В тёмной он светился акцентом 18/600 и спорил со списком под собой; ссылкой
   он остаётся (курсор, подчёркивание на наведении), просто не красится.
   `:where()` — по той же причине, что и у правила выше. */
:where(.zc-card > .zc-h2) > a{ color: var(--zc-cardlink-ink); }
h1, h2, h3, p, ul, ol{ margin: 0 0 var(--zc-gap) 0; }
ul, ol{ padding-left: 1.25em; }
button{ font-family: inherit; }

/* «Перейти к содержимому» — первая цель Tab на КАЖДОЙ странице, то есть
   единственный элемент интерфейса, который человек на клавиатуре встречает
   раньше всего остального. Раньше высота была 40px (24px строки + 8+8
   padding) — ниже минимума 44px (юикс-требования §3.5/§10.1 №2). Минимум
   задан явно через min-height + inline-flex, а не подгонкой padding: padding
   зависит от line-height темы, min-height — нет. */
.zc-skip-link{
  position: absolute; left: -9999px; top: 0;
  display: inline-flex; align-items: center;
  min-height: 44px;
  background: var(--zc-accent); color: var(--zc-ink-invert);
  padding: 8px 12px; border-radius: var(--zc-radius);
  z-index: 100;
}
.zc-skip-link:focus{ left: 8px; top: 8px; }

:focus-visible{ outline: 2px solid var(--zc-focus); outline-offset: 2px; }

/* Подпись только для скринридера. Нужна там, где у образца подписи нет
   ВООБЩЕ: колонка-чекбокс в шапке таблицы, поле поиска с одной лупой,
   стрелка возврата. Убирать элемент через display:none нельзя — тогда его
   не прочитает и скринридер; поэтому классический clip-приём. */
.zc-visually-hidden{
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip: rect(0 0 0 0); clip-path: inset(50%);
  overflow: hidden; white-space: nowrap;
}

/* Базовый тач-таргет для ЛЮБОГО <details>/<summary> в системе — без стилей
   браузер рисует inline-block ~20-24px, ниже минимума (юикс-требования
   §3.5/§10.1 №2). Более специфичные правила (.zc-confirm, .zc-bottomnav__item,
   .gd-filters) переопределяют это по мере надобности — селектор здесь
   намеренно низкой специфичности. */
details > summary{
  min-height: 44px;
  display: flex; align-items: center;
  padding: 8px 4px;
  cursor: pointer;
  list-style: none;
}
details > summary::-webkit-details-marker{ display: none; }

/* =========================================================== LAYOUT ==== */
.zc-container{
  max-width: 1200px;
  margin: 0 auto;
  padding: var(--zc-gap) var(--zc-page-pad);
  width: 100%;
}
/* --container--wide (31.08.2026). Экраны-СПИСКИ (`/docs`, вкладки «Остатки»,
   контрагенты, пользователи, склады, корзина, расхождения — таблица есть
   главным содержимым, а не формой/карточкой) получают этот модификатор
   вторым классом на `<main>` (`{% block main_class %}` в base.html).

   ПОЧЕМУ 1600, А НЕ «БЕЗ ПОТОЛКА». При окне 1440 таблицы (`/goods/transfers`
   1260px, `/docs` 1177px) требуют больше, чем прежние 1200-64=1136px рабочей
   ширины — узкий `.zc-container` их и резал горизонтальной прокруткой. Без
   ЛЮБОГО потолка ширина списка росла бы вместе с монитором до нелепых
   значений на 2560+ — того же нарушения, что и с текстовым столбцом форм,
   только для таблицы. 1600 — потолок: при 1440 он не связывает (окно уже
   ýже), и рабочая ширина получается 1440-64=1376px (попадает в ориентир
   ~1380-1400 из требования); на мониторах шире 1600 контейнер перестаёт расти
   и держит те же поля по краям, что и везде (`var(--zc-page-pad)`).

   Форм и карточки документа не касается: у них свой узкий предел
   (`.zc-field`/`form.zc-card`/`--zc-form-col`), и `main_class` они не
   переопределяют — читать длинный текст в 1600px-колонку никто не просил. */
.zc-container--wide{
  max-width: 1600px;
}

/* ======================================================= TOPNAV (L) ==== */
.zc-topnav{
  display: none;                    /* показывается только на L (см. media ниже) */
  /* --zc-elev-2 = #FFFFFF в светлой (равно прежнему --zc-surface), тёмная
     получает свою ступень + тень (дизайн-бук §9.6, §15.3: новая мягкая тень
     на панели — принятое отклонение светлой). */
  background: var(--zc-elev-2);
  border-bottom: 1px solid var(--zc-line-soft);
  box-shadow: var(--zc-shadow-2);
  /* position+z-index остались от выпадающего списка подразделов (снят
     06.08.2026 — состав раздела теперь в постоянной строке вкладок). Не
     убраны намеренно: шапка обязана лежать выше содержимого страницы,
     иначе тень и границы соседних карточек рисуются поверх неё. */
  position: relative;
  z-index: 20;
}
.zc-topnav__inner{
  max-width: 1200px; margin: 0 auto;
  height: var(--zc-topnav-h);
  padding: 0 var(--zc-page-pad);
  display: flex; align-items: center; gap: 24px;
}
.zc-topnav__brand{
  font-size: var(--zc-t-h2-size); font-weight: var(--zc-t-h2-w);
  color: var(--zc-ink);
  /* Логотип и слово — одна строка, выровненная по центру (07.08.2026).
     inline-flex, а не flex: бренд стоит в flex-строке шапки, и блочный
     вариант растянул бы ссылку на всю свободную ширину, отодвинув меню. */
  display: inline-flex; align-items: center; gap: 8px;
  white-space: nowrap;
}
/* ЛОГОТИП — ФОН ПУСТОГО ЭЛЕМЕНТА, не тег <img> (почему — в base.html рядом
   с разметкой). Размер задан ОБОИМИ измерениями и токеном: пустой элемент
   собственных размеров не имеет, а место под рисунок обязано быть занято
   до того, как файл доехал, иначе подпись дёргается. `contain` — рисунок
   квадратный, вписывается без обрезки на любом размере токена. */
.zc-topnav__logo{
  width: var(--zc-t-h1-size); height: var(--zc-t-h1-size);
  flex: 0 0 auto;
  display: block;
  /* Рисунок — ИНЛАЙНОВЫЙ <svg> внутри этого span (см. base.html): фоновая
     картинка `url(/static/logo.svg)` убрана 25.08.2026 вместе с костылём
     `background:none` в разметке. Причина в самом файле: заливки внутри него
     зашиты литералами, тему он не слышит и на тёмной шапке давал 2.82:1.
     `color` здесь — источник `currentColor` для фигур инлайнового рисунка. */
  color: var(--zc-accent);
}
.zc-topnav__brand:hover{ text-decoration: none; }
.zc-topnav__links{ display: flex; align-items: center; gap: 8px; flex: 1; }
.zc-topnav__link{
  color: var(--zc-ink-muted);
  font-weight: 600;
  font-size: var(--zc-t-small-size);
  padding: 0 4px;
  height: var(--zc-topnav-h);
  display: inline-flex; align-items: center;
  border-top: 2px solid transparent;   /* активный пункт — не только цветом */
}
.zc-topnav__link:hover{ color: var(--zc-ink); text-decoration: none; }
.zc-topnav__link.is-active{
  color: var(--zc-accent);
  border-top-color: var(--zc-accent);
}
.zc-topnav__profile{ display: flex; align-items: center; gap: 12px; }
.zc-topnav__user{ font-size: var(--zc-t-small-size); color: var(--zc-ink-muted); }
/* Имя пользователя — ссылка в /profile (06.08.2026). Геометрия ровно такая
   же, как у пунктов меню слева (`.zc-topnav__link`): высота во всю шапку и
   акцентная полоса сверху у активного. Так «Профиль» читается тем же пунктом
   навигации, что и остальные, а полоса-индикатор попадает на край шапки, а не
   висит скруглённой дугой посреди неё. Минимум 44px тач-таргета выполняется с
   запасом (высота шапки 64px) и задан явно — на случай смены темы. */
.zc-topnav__me{
  display: inline-flex; align-items: center; gap: 8px;
  height: var(--zc-topnav-h); min-height: 44px; padding: 0 8px;
  border-top: 2px solid transparent;      /* активный пункт — не только цветом */
  color: var(--zc-ink-muted);
}
.zc-topnav__me:hover{ background: var(--zc-surface-2); text-decoration: none; }
.zc-topnav__me:hover .zc-topnav__user{ color: var(--zc-ink); }
.zc-topnav__me.is-active{ border-top-color: var(--zc-accent); }
.zc-topnav__me.is-active .zc-topnav__user{ color: var(--zc-accent); }

@media (min-width: 1200px){
  .zc-topnav{ display: block; }
}

/* ==================================================== ПЕРЕКЛЮЧАТЕЛЬ ТЕМЫ ===
   Разметка — base.html (форма POST /theme, солнце/луна) и profile.html.
   Здесь только вид: квадратная кнопка-иконка, тач-таргет ≥44×44 (Д-29),
   `currentColor` для SVG-глифа, кольцо фокуса — outset токеном --zc-focus. */
.zc-theme-form{
  display: inline-flex; align-items: center; margin: 0;
}
.zc-theme-toggle{
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; min-width: 44px; min-height: 44px;
  padding: 0; border: 0; border-radius: 999px;
  background: transparent; color: var(--zc-ink-muted);
  cursor: pointer;
}
.zc-theme-toggle svg{ width: 20px; height: 20px; color: currentColor; }
@media (hover: hover){
  .zc-theme-toggle:hover{ background: var(--zc-surface-2); color: var(--zc-ink); }
}
.zc-theme-toggle:focus-visible{
  outline: 2px solid var(--zc-focus); outline-offset: 2px;
}

/* ============================ ВКЛАДКИ РАЗДЕЛА (ЛЮБАЯ ШИРИНА) ============ */
/* 06.08.2026, правка владельца: «…что у тебя открыт раздел, и выбран сам таб,
   и можно сразу переключаться на другие вкладки, без наведения на „товары“.
   Страницы с развилкой вообще быть не должно».

   ЧТО СНЯТО ЭТИМ БЛОКОМ. `.zc-topnav__group/__caret/__drop/__dropitem`
   (список, выпадавший по `:hover`), `.zc-subnav*` (строка-кнопка «Товары ›
   Номенклатура ▾») и `.zc-submenu*` (окно `popover` со списком подпунктов).
   Две механики на одну задачу и ни одной видимой вкладки — здесь ОДНА строка
   на все ширины, и она видна всегда.

   ПОЛОСА ВО ВСЮ ШИРИНУ, СОДЕРЖИМОЕ — ДО 1200px, как у шапки: строка вкладок
   продолжает её и обязана выглядеть её частью, а не карточкой внутри
   контейнера.

   ГОРИЗОНТАЛЬНАЯ ПРОКРУТКА — ВНУТРИ СТРОКИ И ТОЛЬКО ТАМ. `overflow-x:auto` на
   внутреннем контейнере + `flex:0 0 auto` у вкладок: шесть вкладок «Отчётов»
   на 375px не помещаются никогда, и без этого они бы либо раздули страницу
   вбок (нарушение §10 юикс-требований — жёсткий отказ), либо перенеслись на
   вторую строку и съели пол-экрана. `min-width:0` на контейнере обязателен:
   он flex-ребёнок, а его минимум по умолчанию равен min-content, то есть
   `overflow` без него не сработал бы вовсе и страница поехала бы вбок.

   44px и 16px — те же требования §10, что у любого нажимаемого на телефоне.
   `scrollbar-width:none` (и webkit-аналог) убирает полосу прокрутки: она
   съедала бы 15px высоты у элемента, чья высота ровно 44px по норме. */
.zc-tabs{
  background: var(--zc-surface);
  border-bottom: 1px solid var(--zc-line-soft);
}
.zc-tabs__inner{
  max-width: 1200px; margin: 0 auto;
  padding: 0 var(--zc-page-pad);
  display: flex; align-items: stretch; gap: 4px;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.zc-tabs__inner::-webkit-scrollbar{ display: none; }
.zc-tabs__tab{
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 6px;
  /* ОБЯЗАТЕЛЬНО, И ЭТО НЕ КОСМЕТИКА. Внутри вкладки лежит расшифровка числа
     для скринридера (`.zc-visually-hidden`), а она `position:absolute
     !important`. Без позиционированного предка её containing block — начальный
     (страница целиком), и абсолютный элемент НЕ клипается прокруткой
     `.zc-tabs__inner`: на 375px шестая вкладка уносила его на x=811, и ВСЯ
     СТРАНИЦА получала горизонтальную прокрутку 437px при том, что сама строка
     вкладок скроллилась внутри себя правильно. Замерено в Chromium
     (`window.scrollTo(500,0)` -> `scrollX === 437`), а не выведено из спеки. */
  position: relative;
  min-height: 44px; padding: 0 12px;
  color: var(--zc-ink-muted);
  font-size: var(--zc-t-body-size); font-weight: 600;
  line-height: 1.2; white-space: nowrap;
  /* Признак текущей вкладки — НЕ только цвет (юикс-требования §3.2):
     подчёркивающая полоса снизу, как у образца. Прозрачная у неактивных,
     чтобы переключение не двигало текст на 2px. */
  border-bottom: 2px solid transparent;
}
.zc-tabs__tab:hover{ color: var(--zc-ink); text-decoration: none; }
.zc-tabs__tab.is-current{ color: var(--zc-accent); border-bottom-color: var(--zc-accent); }
/* Число во вкладке — бывший счётчик плитки хаба («8 активных карточек
   товара», «12 проведено за 30 дней»). Пилюля, а не голая цифра: рядом с
   подписью вкладки цифра без фона читается как часть названия. */
.zc-tabs__num{
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 20px; height: 20px; padding: 0 6px;
  border-radius: 999px;
  background: var(--zc-surface-3); color: var(--zc-ink-muted);
  font-size: var(--zc-t-micro-size); font-weight: var(--zc-t-micro-w);
  font-variant-numeric: tabular-nums;
}
.zc-tabs__tab.is-current .zc-tabs__num{ background: var(--zc-accent-soft); color: var(--zc-accent); }
/* Тревожное число (сегодня это ноль отгрузок за 30 дней) — красным ВСЕГДА,
   в том числе на текущей вкладке: сигнал не должен гаснуть от того, что
   человек на неё зашёл. */
.zc-tabs__num.is-alert,
.zc-tabs__tab.is-current .zc-tabs__num.is-alert{
  background: var(--zc-danger-bg); color: var(--zc-danger);
}

/* ============================================ ПРИЗНАК КАБИНЕТА ========= */
/* ЗАМЕНА ПОЛОСЫ КАБИНЕТА (06.08.2026). Полоса `zc-cabinet` занимала строку
   между шапкой и содержимым на КАЖДОМ экране; владелец попросил унести выбор
   в раздел «Профиль». Причина, по которой полоса была видна на всех ширинах,
   при этом никуда не делась: незамеченный чужой кабинет = товар, заведённый
   не туда. Поэтому имя кабинета осталось на виду, но компактно — бейджем
   рядом с именем в шапке (>=1200px) и подписью пятого слота нижней панели
   (<1200px, см. `.zc-bottomnav__label--always`). Оба ведут в /profile.
   Это НЕ элемент управления, а подпись: у бейджа нет своего href и своего
   тач-таргета, он целиком внутри ссылки `.zc-topnav__me`. */
.zc-cabinet-badge{
  display: inline-flex; align-items: center;
  height: 24px; padding: 0 8px;
  border: 1px solid var(--zc-line);
  border-radius: 999px;
  background: var(--zc-surface-2);
  color: var(--zc-ink);
  font-size: var(--zc-t-micro-size); font-weight: var(--zc-t-micro-w);
  line-height: 1;
  white-space: nowrap;
}

/* --- раздел «Профиль» (/profile) — куда переехал выбор кабинета ---------- */
/* Имя текущего кабинета крупно: это ответ на вопрос, ради которого человек
   сюда и зашёл. Пилюли выбора — тот же `.zc-chip`, что и у срезов в строке
   заголовка (44px тач-таргета на S, 32px на L), второго вида пилюли для
   этого заводить не нужно. */
.zc-profile__current{
  font-size: var(--zc-t-h2-size); line-height: var(--zc-t-h2-lh);
  font-weight: var(--zc-t-h2-w);
  color: var(--zc-ink);
}
.zc-profile__cabs{ display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.zc-profile__logout{ margin: 0; }

/* =================================================== BOTTOMNAV (S/M) === */
.zc-bottomnav{
  position: fixed; left: 0; right: 0; bottom: 0;
  height: calc(var(--zc-bottomnav-h) + var(--zc-safe-bottom));
  padding-bottom: var(--zc-safe-bottom);
  /* --zc-elev-2 = #FFFFFF в светлой (равно прежнему --zc-surface) — панель
     не меняется в светлой, тёмная получает свою ступень. */
  background: var(--zc-elev-2);
  border-top: 1px solid var(--zc-line-soft);
  /* 5 слотов: Главная/Товары/Склады/Ещё/Профиль. Пятым слотом стал НЕ
     «создать документ» (акцентная «+» по-прежнему запрещена — ложное
     ожидание, см. ниже), а профиль: верхняя шапка живёт только от 1200px,
     и без этого слота с телефона не было бы ни входа в профиль, ни признака
     текущего кабинета (06.08.2026, см. base.html). */
  display: grid; grid-template-columns: repeat(5, 1fr);
  align-items: stretch;
  z-index: 30;
}
.zc-bottomnav__item{
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px;
  color: var(--zc-ink-muted);
  /* min-width:0 — НЕ косметика. Колонка `1fr` — это `minmax(auto, 1fr)`, то
     есть её минимум равен min-content содержимого. Подписью слота профиля
     служит имя кабинета, а `account_short()` при отсутствии поля `short`
     откатывается на длинное `label` («ИП Ляшев Г.Р. (кабинет 1)») — такая
     подпись раздувала свою колонку и сжимала остальные четыре до 28px при
     обязательных 44px тач-таргета (поймано сьютом test_zachet_web_docs.py,
     чья фикстура кабинета живёт как раз на фолбэке). Ноль снимает этот
     минимум, и лишнее уходит в многоточие (`text-overflow` у подписи ниже),
     а не ломает всю панель. */
  min-width: 0;
  height: var(--zc-bottomnav-h);
  border-top: 2px solid transparent;
  min-height: 44px;
  list-style: none;
  cursor: pointer;
}
.zc-bottomnav__item:hover{ text-decoration: none; }
.zc-bottomnav__item.is-active{ color: var(--zc-accent); border-top-color: var(--zc-accent); }
/* Значок слота живёт в коробке ФИКСИРОВАННОЙ высоты, а не «по строке шрифта».
   Глифы у слотов разные (⌂ ▢ ▣ ◉ ⋯), и метрики у них разные тоже: у «⋯»
   отрисовка сидит заметно ниже, из-за чего и значок, и подпись слота «Ещё»
   стояли на 4-5px ниже соседей — панель выглядела кривой (видно на снимке
   после включения подписей). Одинаковая коробка выравнивает все пять. */
.zc-bottomnav__icon{
  font-size: 20px; line-height: 1;
  height: 22px; flex: 0 0 22px;
  display: flex; align-items: center; justify-content: center;
}
/* ПОДПИСЬ ЕСТЬ У КАЖДОГО СЛОТА И НА ЛЮБОЙ ШИРИНЕ (10.08.2026, пятый пункт
   обхода: «нижняя панель — четыре значка без подписей, текущий раздел не
   назван»). Раньше ниже 480px подписи гасились ради места, а 375px — это и
   есть телефон владельца: он видел ⌂ ▢ ▣ ◉ ⋯ и должен был угадывать. Замер:
   пять слотов по 75px, самая длинная подпись («Главная») — 48px при 12px
   шрифта, то есть места хватало и тогда. Длинное значение (имя кабинета в
   слоте профиля) режется многоточием — правило ниже общее для всех слотов,
   а не только для профиля, как было. */
.zc-bottomnav__label{
  font-size: var(--zc-t-micro-size); font-weight: var(--zc-t-micro-w);
  max-width: 100%; padding: 0 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Текущий раздел назван НЕ ТОЛЬКО цветом и полосой сверху (§3.2): подпись
   активного слота ещё и жирная — на маленьком шрифте это заметнее оттенка. */
.zc-bottomnav__item.is-active .zc-bottomnav__label{ font-weight: 700; }
/* .zc-bottomnav__item--accent / .zc-bottomnav__plus (акцентная «+», 5-й слот)
   удалены вместе с кнопкой в base.html — план прямо запрещает «создать
   документ» на этом этапе (ложное ожидание у Эльвиры, что документ можно
   завести уже сейчас). Вернутся вместе с формой документа в Этапе 4. */

/* «Ещё» — нативный <details>/<summary>, работает БЕЗ единой строки JS */
.zc-bottomnav__more{ display: contents; }
.zc-bottomnav__more > summary{
  list-style: none;
  /* Общее правило `details > summary` вверху файла даёт КАЖДОМУ <summary>
     системы `padding: 8px 4px` (там оно нужно ради тач-таргета). Слоту «Ещё»
     оно лишнее и вредное: высоту слоту задаёт `.zc-bottomnav__item` (56px), а
     чужие поля сдвигали содержимое вниз — значок и подпись «Ещё» стояли на 5px
     ниже соседних четырёх, и панель выглядела кривой (замер после включения
     подписей 10.08.2026: 646 против 641). Тач-таргет от обнуления не страдает,
     он держится на `min-height` слота. */
  padding: 0;
}
.zc-bottomnav__more > summary::-webkit-details-marker{ display: none; }
.zc-bottomnav__sheet{
  position: fixed; left: 0; right: 0; bottom: 0;
  display: none;
  /* Слой 4 (26.08.2026): семейство всплывающих поднято на ступень —
     см. комментарий у `.zc-modal`. В светлой `--zc-elev-4` = `#FFFFFF`,
     ровно как `--zc-elev-3`, лист не меняется. */
  background: var(--zc-elev-4);
  border-top-left-radius: var(--zc-radius-lg);
  border-top-right-radius: var(--zc-radius-lg);
  box-shadow: 0 -4px 24px var(--zc-shadow-color);
  padding: 12px 16px calc(16px + var(--zc-bottomnav-h) + var(--zc-safe-bottom));
  z-index: 40;
}
.zc-bottomnav__more[open] .zc-bottomnav__sheet{ display: flex; flex-direction: column; }
.zc-bottomnav__more[open]::before{
  content: ""; position: fixed; inset: 0;
  /* Значение сохранено буквально в светлой (задача аппликатора, п.3):
     --zc-scrim/--zc-shadow-color дали бы более плотную подложку. */
  background: var(--zc-scrim-sheet);
  -webkit-backdrop-filter: var(--zc-scrim-blur);
  backdrop-filter: var(--zc-scrim-blur);
  z-index: 35;
}
/* ПЛАВАЮЩАЯ КНОПКА КОНСУЛЬТАНТА УХОДИТ ПОД ЛИСТ, ПОКА ЛИСТ ОТКРЫТ
   (26.08.2026, обход дизайн-критика). Страница затемнена, а кнопка оставалась
   единственным неприглушённым объектом на экране и ФИЗИЧЕСКИ накрывала пункт
   «Включить светлую тему» (замер перекрытия — 2632 px²). `z-index: 50` у самой
   кнопки поставлен по делу: в ПОКОЕ она обязана быть выше нижней панели и её
   листа. Пока лист открыт, это требование не действует — активная поверхность
   тут лист, а не кнопка. `.zc-bottomnav` создаёт свой контекст наложения
   (`z-index: 30`), поэтому «под лист» значит «ниже 30», а не «ниже 40». */
body:has(.zc-bottomnav__more[open]) .zc-consultant-btn{ z-index: 29; }
.zc-bottomnav__sheet-link{
  display: block; padding: 14px 8px;
  font-size: var(--zc-t-body-size);
  color: var(--zc-ink);
  border-bottom: 1px solid var(--zc-line-soft);
  min-height: 44px;
  text-align: left; background: none; border-left: 0; border-right: 0; border-top: 0;
  width: 100%;
}
.zc-bottomnav__sheet-link:hover{ text-decoration: none; background: var(--zc-surface-2); }
.zc-bottomnav__sheet-link:last-child{ border-bottom: 0; }
.zc-bottomnav__sheet-form{ margin: 0; }
.zc-bottomnav__sheet-link--btn{ font: inherit; cursor: pointer; }

/* СВЕРХКОМПАКТНОГО РЕЖИМА «ТОЛЬКО ГЛИФЫ» БОЛЬШЕ НЕТ (10.08.2026).
   Здесь стояло `@media (max-width: 479px){ .zc-bottomnav__label{display:none} }`
   с исключением для подписи кабинета (`--always`). Ровно это правило и
   создавало дефект из обхода: на 375px — на телефоне владельца — панель
   превращалась в четыре безымянных значка плюс имя кабинета, и назвать
   текущий раздел было нечем. Подписи теперь стоят на любой ширине (см.
   `.zc-bottomnav__label` выше), а класс `--always` потерял смысл и снят
   из base.html вместе с этим правилом. */
@media (min-width: 1200px){
  .zc-bottomnav{ display: none; }
}

/* ============================================================ FOOTER === */
.zc-footer{
  border-top: 1px solid var(--zc-line-soft);
  color: var(--zc-ink-faint);
  font-size: var(--zc-t-caption-size);
}
.zc-footer__inner{ padding-top: var(--zc-gap); padding-bottom: var(--zc-gap); }

/* ========================================================= TYPOGRAPHY == */
/* margin-bottom 12px, а не var(--zc-gap): на L токен равен 24px, и отбивка
   под заголовком раздела вдвое превышала отбивку внутри заголовочного блока
   (.zc-pagehead/.zc-actions — 12px). Приводим к одному значению: разнобой
   был виден как «заголовок висит отдельно от своего экрана». */
.zc-h1{ font-size: var(--zc-t-h1-size); line-height: var(--zc-t-h1-lh); font-weight: var(--zc-t-h1-w); margin: 0 0 12px; }
.zc-h2{ font-size: var(--zc-t-h2-size); line-height: var(--zc-t-h2-lh); font-weight: var(--zc-t-h2-w); margin: 0 0 8px; }
.zc-display{ font-size: var(--zc-t-display-size); line-height: var(--zc-t-display-lh); font-weight: var(--zc-t-display-w); }
.zc-small{ font-size: var(--zc-t-small-size); line-height: var(--zc-t-small-lh); }
.zc-caption{ font-size: var(--zc-t-caption-size); line-height: var(--zc-t-caption-lh); color: var(--zc-ink-muted); }
.zc-micro{ font-size: var(--zc-t-micro-size); line-height: var(--zc-t-micro-lh); font-weight: var(--zc-t-micro-w); letter-spacing: .04em; text-transform: uppercase; }
.zc-ink-muted{ color: var(--zc-ink-muted); }
.zc-ink-faint{ color: var(--zc-ink-faint); }
.zc-mono{
  font-family: var(--zc-font-mono);
  font-variant-numeric: tabular-nums;
  /* длинные непрерывные строки без пробелов (пути файлов, sku_id с
     подчёркиваниями, id локаций WB:CENTRAL/KOLEDINO) иначе не находят точку
     разрыва и на 375px вылезают за край, обрезанные body{overflow-x:hidden}
     — это НЕ горизонтальная прокрутка страницы (тело её не даёт), но
     реальная визуальная порча текста; overflow-wrap:anywhere разрешает
     разрыв в любом месте токена, когда естественных пробелов не хватает. */
  overflow-wrap: anywhere;
  word-break: break-word;
}

/* ============================================================= CARD ==== */
.zc-card{
  background: var(--zc-surface);
  border: 1px solid var(--zc-line-soft);
  border-radius: var(--zc-radius);
  padding: var(--zc-gap);
  margin-bottom: var(--zc-gap);
  /* Уровень 1 (дизайн-бук §7.2/§9.2): в светлой оба токена — none, карточка
     держится рамкой, как сегодня; в тёмной — световая кромка + тень, «матовость». */
  box-shadow: var(--zc-hairline-top), var(--zc-shadow-1);
}

/* ============================================================ BUTTONS == */
.zc-btn{
  display: inline-flex; align-items: center; justify-content: center;
  gap: 8px;
  min-height: 44px;
  padding: 0 16px;
  /* ПИЛЮЛЯ (05.08.2026, переделка под образец «Складолог», §4.2 каркаса):
     скругление на всю высоту у ВСЕХ кнопок системы. Раньше было
     var(--zc-radius)=10px. Изменение чисто косметическое — ни один сьют
     не проверяет радиус, а «кнопка = пилюля» опознаётся владельцем как
     главный признак образца. */
  border-radius: 999px;
  border: 1px solid transparent;
  font-size: var(--zc-t-body-size);
  font-weight: 600;
  cursor: pointer;
  /* ШИРИНА — ПО СОДЕРЖИМОМУ. Кнопка внутри flex-контейнера иначе тянется
     до высоты/ширины соседей (align-items:stretch), и «залитая пилюля»
     превращается в полосу. */
  flex: 0 0 auto;
  background: var(--zc-accent);
  color: var(--zc-ink-invert);
}
.zc-btn:hover{ background: var(--zc-accent-hover); text-decoration: none; color: var(--zc-ink-invert); }
.zc-btn:active{ background: var(--zc-accent-active); }
.zc-btn--ghost{
  background: transparent; color: var(--zc-accent);
  border-color: var(--zc-line);
}
.zc-btn--ghost:hover{ background: var(--zc-accent-soft); color: var(--zc-accent-hover); }
/* «Кнопка (вторичная, в ряду)» — юикс-требования §4.5, таблица высот: 44px на S/M
   (тач), 36px только на L (курсор). Раньше было плоских 36px везде — это и давало
   промахи на пагинации, «Применить» на карточке склада и т.д. на телефоне. */
.zc-btn--small{ min-height: 44px; padding: 0 12px; font-size: var(--zc-t-small-size); }
@media (min-width: 1200px){
  .zc-btn--small{ min-height: 36px; }
}
/* «Кнопка во всю ширину» — ТОЛЬКО телефон (05.08.2026, переделка под образец).
   У образца кнопок во всю ширину контента нет НИГДЕ: главное действие —
   пилюля по содержимому в левом краю строки действий (каркас §4.2). У нас же
   `zc-btn--block` стоял в разметке разделов и на широком экране рисовал
   синие полосы по 1300px — по две подряд («Добавить контрагента» и
   «Фильтры (0)»), из-за чего приоритет действий не читался вовсе.

   Правило снимает ширину НА УРОВНЕ ОБЩЕГО СЛОЯ, не трогая ни один шаблон
   раздела: на S кнопка остаётся во всю ширину (палец, одна колонка — это
   правильно и проверено сьютом test_zachet_web_mobile.py), с 768px
   становится по содержимому. Следующая волна убирает сам класс из разметки
   разделов; до тех пор это правило чинит все экраны разом. */
.zc-btn--block{ width: 100%; }
@media (min-width: 768px){
  .zc-btn--block{ width: auto; }
}
/* Модификаторы кнопки живут РЯДОМ с базовым .zc-btn и только здесь. До
   03.08.2026 три из них (--secondary/--danger/--danger-confirm) были объявлены
   инлайном в templates/users_extra_head.html: разметка `class="zc-btn
   zc-btn--secondary"` на любом другом экране давала обычную синюю кнопку —
   класс есть, стилей нет. Ровно этот дефект приёмка нашла на плитках отчётов;
   семейство кнопок закрыто здесь, чтобы он не повторился на формах этапа 4. */
.zc-btn--secondary{
  background: var(--zc-surface); color: var(--zc-ink); border: 1px solid var(--zc-line);
}
.zc-btn--secondary:hover{ background: var(--zc-surface-2); color: var(--zc-ink); }
.zc-btn--danger{
  background: var(--zc-surface); color: var(--zc-danger); border: 1px solid var(--zc-danger-line);
}
.zc-btn--danger:hover{ background: var(--zc-danger-bg); color: var(--zc-danger); }
/* --zc-danger-solid/-hover/-on: светлые значения В ТОЧНОСТИ равны прежним
   --zc-danger-line/--zc-danger/--zc-ink-invert — светлая не меняется. В
   тёмной var(--zc-ink-invert) на этой заливке давал бы 4.25 (провал 4.5,
   дизайн-бук §6) — отдельная тройка токенов закрывает дефект. */
.zc-btn--danger-confirm{
  background: var(--zc-danger-solid); color: var(--zc-danger-on); border-color: var(--zc-danger-solid);
}
.zc-btn--danger-confirm:hover{ background: var(--zc-danger-solid-hover); color: var(--zc-danger-on); }

/* Ссылка «← Назад» (zc-caption, под шапкой раздела) — сама по себе это 13/18
   текст без отступов, кликабельная область 15-19px. Класс расширяет тач-таргет
   до 44px по высоте, не трогая типографику (юикс-требования §3.5). */
.zc-back-link{
  display: inline-flex; align-items: center;
  min-height: 44px;
  padding: 4px 0;
}

/* Инлайн-текстовая ссылка с безопасным тач-таргетом — тот же приём, что и
   .zc-back-link (текстовая ссылка, не блочная кнопка), но без семантики
   «назад»: используется там, где ссылка не «шапка раздела», а часть текста
   или ячейки таблицы (баннер дублей контрагента, столбец «Наименование» в
   таблице контрагентов, «Отменить и вернуться к списку» в форме).
   Найдено 03.08.2026 мобильным сьютом: класс стоял в разметке четырёх мест,
   но НИ ОДНОГО правила для него в стилях не было — ссылка «Отменить и
   вернуться к списку» рисовалась 15px вместо честных 44px (юикс-требования
   §10.1, критерий №2). */
.zc-link{
  display: inline-flex; align-items: center;
  min-height: 44px;
  padding: 4px 0;
}

/* ================================================== ИНЛАЙН-ПОДТВЕРЖДЕНИЕ === */
/* Необратимое действие раскрывается на месте, без модалки (юикс-требования
   §5.7: «модалка — не место для формы») — нативный <details>/<summary>, без
   единой строчки JS.
   Переехал сюда из templates/users_extra_head.html 03.08.2026. Раньше здесь
   стоял только КОММЕНТАРИЙ «класс определён в шаблоне раздела» — то есть
   общий файл стилей ссылался на правила, которых в нём нет: ровно та связь,
   из-за которой на этапе 4 удаление документа получило бы <summary> без
   тач-таргета. */
.zc-confirm{
  margin-top: 8px;
  padding: var(--zc-gap);
  border-radius: var(--zc-radius);
  background: var(--zc-surface-2);
  border: 1px solid var(--zc-line-soft);
}
/* <summary> без явных размеров — нативный inline ~24px высотой, вдвое ниже
   минимального тач-таргета (юикс-требования §10.1, критерий №2) — и это на
   необратимых действиях (сброс пароля, деактивация), где промах на телефоне
   дорого стоит. display:flex + min-height дают честные 44px, не трогая
   типографику/окраску блока. */
.zc-confirm > summary{
  display: flex; align-items: center; gap: 6px;
  min-height: 44px;
  padding: 10px 4px;
  cursor: pointer;
  font-weight: 600;
  list-style: none;
}
.zc-confirm > summary::-webkit-details-marker{ display: none; }
.zc-confirm > summary::before{ content: "▸"; font-size: 12px; color: var(--zc-ink-muted); }
.zc-confirm[open] > summary::before{ content: "▾"; }
.zc-confirm__text{ color: var(--zc-ink-muted); margin: 8px 0; }

/* ============================================== ФЛАЖОК В СТРОКЕ (44px) === */
/* «Показать деактивированных / архивные / служебные» — один и тот же по смыслу
   переключатель на четырёх экранах, и до 03.08.2026 он был сделан ЧЕТЫРЬМЯ
   разными способами: .gd-check с честными 44px на /goods/list, голый <label>
   на складах (флажок растягивался на всю ширину правилом input{width:100%}) и
   <label> с инлайновым style="width:auto;min-height:auto" на контрагентах —
   там тач-таргет схлопывался до 24px.

   Общий компонент: <label class="zc-check"><input type="checkbox" …> Текст
   </label>. Высота строки 44px берёт на себя ЛЕЙБЛ (кликабельна вся строка, а
   не 16px квадратика), а сам <input> освобождается от общего правила формы
   input{width:100%; min-height:44px}, которое флажку не подходит. */
.zc-check{
  display: flex; align-items: center; gap: 8px;
  min-height: 44px;
  cursor: pointer;
}
.zc-check input{
  width: auto; min-height: auto;
  flex: 0 0 auto;
  margin: 0;
}

/* ================================================== ИНЛАЙН-ФОРМА В РЯД === */
/* Ряд полей + кнопка, переносится на узком экране. Поля тянутся, кнопка
   прижата к их нижнему краю. Переехал сюда из users_extra_head.html, потому
   что класс с общим префиксом `zc-` использован на двух экранах раздела и
   заведомо понадобится формам этапа 4.
   Близкие по смыслу .gc-inline-form (goods_categories), .zcw-filters-row и
   .zcw-goods-filters (склады) ОСТАВЛЕНЫ инлайн сознательно: каждый из них
   живёт ровно на одном экране и отличается базисом поля. Новую форму писать
   на .zc-inline-form, а не копировать их. */
.zc-inline-form{ display: flex; gap: 8px; flex-wrap: wrap; align-items: flex-end; }
.zc-inline-form .zc-field{ flex: 1 1 200px; margin-bottom: 0; }

/* Мелкая поясняющая строка под полем/блоком. */
.zc-muted-note{ color: var(--zc-ink-muted); font-size: var(--zc-t-caption-size); margin: 4px 0 0; }

/* ============================================================= FORMS === */
/* Минимальный размер шрифта в полях ввода — 16px на ЛЮБОМ размере экрана.
   Требование безусловное (юикс-требования §4.4): меньший шрифт заставляет
   Safari iOS зумить страницу при фокусе и ломает липкую вёрстку. */
input, select, textarea{
  font-family: var(--zc-font-ui);
  font-size: var(--zc-t-body-size);
  line-height: var(--zc-t-body-lh);
  color: var(--zc-ink);
  /* --zc-surface-sunken = #FFFFFF в светлой (равно прежнему --zc-surface) —
     поле не меняется в светлой; в тёмной поле «утоплено» темнее карточки. */
  background: var(--zc-surface-sunken);
  border: 1px solid var(--zc-line);
  border-radius: var(--zc-radius);
  padding: 10px 12px;
  min-height: 44px;
  width: 100%;
  /* Глубина утопленного поля (дизайн-бук §9.3) — в светлой none, невидимо. */
  box-shadow: var(--zc-field-inset);
}
input:focus, select:focus, textarea:focus{
  border-color: var(--zc-line-strong);
  outline: 2px solid var(--zc-focus);
  outline-offset: 1px;
}
/* Автозаполнение Chrome красит поле своим жёлтым и игнорирует `background` —
   перекрывается только `inset`-тенью в 1000px (дизайн-бук §9.3, ловушка §12.8). */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus{
  -webkit-box-shadow: 0 0 0 1000px var(--zc-autofill-bg) inset;
  -webkit-text-fill-color: var(--zc-ink);
  caret-color: var(--zc-ink);
}
/* НАТИВНЫЕ ЭЛЕМЕНТЫ УПРАВЛЕНИЯ КРАСЯТСЯ ТОКЕНОМ (25.08.2026, обход тёмной
   темы). Без `accent-color` браузер рисует отмеченную галочку и выделенную
   строку раскрытого `<select>` СВОИМ светло-голубым — цветом, которого нет ни
   в одной из двух палитр: на тёмной странице он выглядит как чужой элемент.
   Свойство наследуется, поэтому названо один раз на корне, а не перечислением
   контролов; значение — токен, как требует Д-26. */
:root{ accent-color: var(--zc-accent); }

/* Иконка календаря — по умолчанию тёмная и на тёмном поле невидима. */
input[type="date"]::-webkit-calendar-picker-indicator{
  filter: invert(1) brightness(.85);
}
:root[data-theme="light"] input[type="date"]::-webkit-calendar-picker-indicator{
  filter: none;
}
@media print{
  input[type="date"]::-webkit-calendar-picker-indicator{ filter: none; }
}
/* Выключенное поле — токеном, не opacity (Д-55). */
input:disabled, select:disabled, textarea:disabled{
  background: var(--zc-field-disabled-bg);
  color: var(--zc-ink-disabled);
  border-color: var(--zc-line);
}
label{ display: block; font-size: var(--zc-t-caption-size); color: var(--zc-ink-muted); margin-bottom: 4px; }
.zc-field{ margin-bottom: var(--zc-gap); }

/* ================================== ШИРИНА ПОЛЯ ПО СОДЕРЖИМОМУ (§5.2) === */
/*
   ЖАЛОБА ВЛАДЕЛЬЦА 07.08.2026, дословно: «Почему на весь блять экран растянуты
   списки? Ты можешь сделать нормально в соответствии с дизайн скилами?» На
   скриншоте — «Дата операции» шириной 1086 CSS-px под ввод восьми символов
   `дд.мм.гггг` (замерено браузером на 1920). Правило, которое это запрещает,
   в письменном эталоне БЫЛО и до жалобы — `docs/zachet/юикс-требования.md`
   §5.2: «Ширина поля намекает на содержимое: количество — 96 px, номер
   накладной — 160 px, название — вся ширина. Поле на всю ширину под
   трёхзначное число — ложный сигнал». Не было ИСПОЛНЕНИЯ: общее правило
   `input,select,textarea{width:100%}` выше делало все поля одинаковыми.

   ПОЧЕМУ РАЗМЕРАМИ, А НЕ ШИРИНОЙ У КАЖДОГО ПОЛЯ. Ширина, проставленная
   поштучно, живёт до первого нового поля: следующий автор скопирует соседнюю
   разметку без неё и снова получит поле во всю ширину. Здесь размеров
   ЧЕТЫРЕ, они применяются ПО СМЫСЛУ, и три из четырёх подхватываются САМИ —
   по типу поля (`input[type=date]`, `inputmode=numeric`), без единого класса
   в шаблоне. Руками ставится только `--code` и `--doc` там, где тип поля
   смысла не выдаёт (артикул, номер накладной).

   НА ТЕЛЕФОНЕ НИЧЕГО НЕ СУЖАЕТСЯ. Все правила ниже живут в `min-width: 768px`:
   на S поле по-прежнему во всю доступную ширину, и это правильно — палец
   толще курсора, а экрана мало. Ограничение касается только широких экранов.
*/
/* ЧИСЛА НЕ ВЫДУМАНЫ ПОД ЖАЛОБУ — они уже жили в системе. `--zc-form-col` и
   `--zc-form-col-2` равны ширине модалки и «широкой» модалки (`.zc-modal`,
   `.zc-modal--wide`): та же форма не может быть 640px в окне и 1900px на
   отдельной странице. Требование дизайн-бука, правило Д-17. */
:root{
  --zc-form-col:   640px; /* колонка формы в один столбец — как `.zc-modal` */
  --zc-form-col-2: 880px; /* форма с боковой колонкой — как `.zc-modal--wide` */
  --zc-fw-qty:      96px; /* количество, штук в коробе, габариты */
  --zc-fw-date:    176px; /* дд.мм.гггг + нативная иконка календаря */
  --zc-fw-doc:     200px; /* номер накладной, номер поставки, ИНН */
  --zc-fw-code:    280px; /* артикул, код склада, логин */
}
@media (min-width: 768px){
  /* КАК ЭТО УСТРОЕНО, ЧТОБЫ НЕ БЫЛО ВОЙНЫ СПЕЦИФИЧНОСТЕЙ.
     Ограничивается САМ КОНТРОЛ (а не `.zc-field`: подпись под полем обязана
     занимать полную ширину колонки, иначе объяснение в три слова встанет
     столбиком под 96-пиксельным полем количества). Явный размер приходит
     переменной `--zc-field-w`, которую ставит класс на `.zc-field`;
     автоматический размер стоит В ЭТОЙ ЖЕ строке ЗАПАСНЫМ значением.
     Поэтому класс всегда сильнее автоматики — не потому, что «специфичнее»,
     а потому, что автоматика вообще не участвует, когда переменная задана.
     (Первая редакция решала это специфичностью и проиграла: у
     `:has(> input[attr])` она 0-2-1, у одиночного класса 0-2-0, и явное
     `zc-field--qty` молча не срабатывало. Поймано замером в браузере.) */
  .zc-field > input,
  .zc-field > select,
  .zc-field > textarea{
    max-width: var(--zc-field-w, var(--zc-form-col));
  }
  /* Автоматически — по типу поля.
     ЧИСЛОВОЕ ПОЛЕ — НЕ ОБЯЗАТЕЛЬНО КОЛИЧЕСТВО, и 96px ему давать нельзя:
     `inputmode="numeric"` носят и ИНН (до 15 цифр), и телефон. Поэтому
     автоматический размер числового поля — «номер» (200px, хватает и ИНН, и
     номеру накладной), а 96px под количество ставится ЯВНО классом
     `zc-field--qty` там, где поле действительно счётное. */
  .zc-field > input[type="date"]{         max-width: var(--zc-field-w, var(--zc-fw-date)); }
  .zc-field > input[inputmode="numeric"],
  .zc-field > input[inputmode="decimal"],
  .zc-field > input[type="tel"]{          max-width: var(--zc-field-w, var(--zc-fw-doc)); }
  /* Вручную — там, где тип поля смысла не выдаёт. */
  .zc-field--qty{  --zc-field-w: var(--zc-fw-qty); }
  .zc-field--date{ --zc-field-w: var(--zc-fw-date); }
  .zc-field--doc{  --zc-field-w: var(--zc-fw-doc); }
  .zc-field--code{ --zc-field-w: var(--zc-fw-code); }
  /* `--wide` — «во всю колонку формы», а не «во всю страницу». */
  .zc-field--wide{ --zc-field-w: var(--zc-form-col); }
  /* Файл/камера — по содержимому кнопки, растягивать нечего. */
  .zc-field > input[type="file"]{ max-width: var(--zc-form-col); }
  /* Флажок из-под ограничения выведён: у него своя ширина (см. `.zc-check`). */
  .zc-check > input{ max-width: none; }
  /* Подсказка под полем — по правилу длины строки (§4.4, 72 символа). */
  .zc-muted-note, .zc-field > .zc-caption{ max-width: 64ch; }
}
/* Инлайн-форма (поиск товара, фильтры) кладёт поля В РЯД и меряет их своим
   флекс-базисом, поэтому предел ставится на само поле, а не на контрол —
   иначе поле тянулось бы, а рамка внутри стояла бы уже, и справа зияла бы
   дыра. Предел тот же, что у колонки формы: до правки 07.08.2026 строка
   поиска товара занимала 988px на 1920 — ровно то, на что жаловался
   владелец, только в другом разделе. */
@media (min-width: 768px){
  .zc-inline-form .zc-field{ max-width: var(--zc-form-col); }
}

/* КАРТОЧКА-ФОРМА НЕ РАСПОЛЗАЕТСЯ (та же жалоба 07.08.2026). Панель на всю
   ширину с колонкой полей у левого края читается как незаконченная вёрстка:
   глаз идёт по пустому полю справа. Ограничение стоит на `<form>`, который
   САМ является карточкой, — это ровно формы ввода (документ, товар,
   пользователь, вход) и ни один список, ни одна таблица и ни одна панель с
   данными. `--zc-gap * 2` — внутренние поля самой карточки, чтобы колонка
   внутри осталась ровно `--zc-form-col`, а не сжалась на padding. */
@media (min-width: 768px){
  form.zc-card{ max-width: calc(var(--zc-form-col) + var(--zc-gap) * 2); }
  /* Форма с боковой колонкой (плашка фото у нового товара) — это «широкая»
     форма, и предел у неё тот же, что у широкой модалки (дизайн-бук, Д-17):
     иначе сетка полей в две колонки сжалась бы вдвое. */
  form.zc-card--sidecol{
    max-width: calc(var(--zc-form-col-2) + var(--zc-gap) * 2);
  }
}

/* ======================================================= ЧИСЛО НА ЭКРАНЕ == */
/* ОБЁРТКА ЗНАЧЕНИЯ `ui.num()/ui.money()/ui.qty()` (`_ui.html`, раздел 0-тер).
   Сам текст числа собирает Python (`zachet_web_http.fmt_*`): разряды узкой
   неразрывной шпацией, настоящий минус «−», копейки только ненулевые. Здесь —
   то, что текстом не выражается и без чего разряды всё равно не сойдутся:

     * `tabular-nums` — цифры одной ширины. Без него «1» уже «8», и столбец
       из пропорциональных цифр не выстраивается, сколько его ни выравнивай;
     * `white-space: nowrap` — число не рвётся по своим же разделителям;
     * ПРАВЫЙ КРАЙ у ячейки, а не у span'а. Выравнивание — свойство ЯЧЕЙКИ:
       `text-align` внутри inline-элемента не делает ничего, поэтому правило
       адресуется `td`/`th`, внутри которых лежит значение. Оно живёт НИЖЕ, в
       разделе таблицы, и не здесь — см. там же, почему.

   ТОЛЬКО НА ШИРОКОМ ЭКРАНЕ. Ниже 1200px таблица свёрнута в карточки
   («подпись слева — значение справа», см. ниже), значения идут столбиком
   ПОД разными подписями и сопоставлять их по разрядам не с чем: правый край
   там оторвал бы число от собственной подписи. */
.zc-num{
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ============================================================= BADGES == */
/* Подсветка расхождений — юикс-требования §4.3. ЧЕТЫРЕ независимых носителя
   признака на каждое состояние: оттенок, глиф, знак числа, стиль левой
   границы — цвет НИКОГДА единственный носитель смысла (критично для
   дейтеранопии/протанопии и ч/б печати). */
/* ЛЕВЫЙ КРАЙ ПРЯМОЙ, ПРАВЫЙ — ПИЛЮЛЯ (10.08.2026, обход владельца).
   Было `border-radius: 999px` при `border-left: 4-5px` и НУЛЕВЫХ остальных
   границах. В этом сочетании браузер рисует боковую границу не полосой, а
   ДУГОЙ, сводя её ширину к нулю у соседних сторон: на экране получается
   полумесяц, торчащий из контура плашки, — то, что владелец при обходе
   назвал «плашки откусаны слева». Грубее всего у `--shortage`, где стиль
   `double` 5px: вместо двух прямых рисуются две дуги (снимок
   `docs/zachet/скриншоты/плашка-ДО.png`).
   Скругление снято РОВНО с той стороны, где стоит цветная метка; справа
   пилюля осталась. Все четыре носителя смысла из §4.3 целы (оттенок, глиф,
   знак числа, СТИЛЬ левой границы) — стиль границы после правки читается
   лучше, а не хуже: solid/dashed/double/dotted различимы на прямой полосе и
   неразличимы на дуге в 4px. */
.zc-badge{
  display: inline-flex; align-items: center; gap: 4px;
  font-family: var(--zc-font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--zc-t-small-size);
  padding: 2px 8px;
  border-radius: 0 999px 999px 0;
  border-left: 4px solid;
}
.zc-badge--ok{       color: var(--zc-success); background: var(--zc-success-bg); border-left-style: solid;   border-left-color: var(--zc-success-line); }
.zc-badge--surplus{  color: var(--zc-warning); background: var(--zc-warning-bg); border-left-style: dashed;  border-left-color: var(--zc-warning-line); }
.zc-badge--shortage{ color: var(--zc-danger);  background: var(--zc-danger-bg);  border-left-style: double;  border-left-width: 5px; border-left-color: var(--zc-danger-line); }
.zc-badge--unknown{  color: var(--zc-neutral); background: var(--zc-neutral-bg); border-left-style: dotted; border-left-color: var(--zc-neutral-line); }
/* Активность записи (пользователь/контрагент/склад — везде одна пара
   состояний). Переехали из templates/users_extra_head.html 03.08.2026:
   модификатор семейства обязан лежать там же, где базовый .zc-badge. */
.zc-badge--active{
  color: var(--zc-success); background: var(--zc-success-bg);
  border-left-style: solid; border-left-color: var(--zc-success-line);
}
.zc-badge--inactive{
  color: var(--zc-neutral); background: var(--zc-neutral-bg);
  border-left-style: dotted; border-left-color: var(--zc-neutral-line);
}

/* ============================================================= ALERTS === */
/* Баннер результата/ошибки над содержимым страницы. Четыре тона —
   info/success/warning/error, каждый несёт признак не только цветом, но и
   глифом в разметке (юикс-требования §4.3).
   Переехал сюда 03.08.2026 из templates/users_extra_head.html; заодно
   поглотил .zc-login__error из login.html — это была ПОБУКВЕННАЯ копия
   .zc-alert + .zc-alert--error под другим именем. Две копии одного баннера в
   двух шаблонах — то же самое, что плитки отчётов без стилей, только
   наоборот: стили есть, но каждый экран носит свои. */
/* Левый край прямой — по той же причине, что у `.zc-badge` выше: цветная
   метка в 4px и скругление на том же углу дают дугу вместо полосы. У баннера
   радиус 10px, а не 999px, поэтому дефект мягче, но он тот же самый, и чинить
   его надо там же, иначе следующая приёмка найдёт «откусанный» баннер после
   исправленных плашек. */
.zc-alert{
  display: flex; gap: 10px; align-items: flex-start;
  border-left: 4px solid;
  border-radius: 0 var(--zc-radius) var(--zc-radius) 0;
  padding: 12px var(--zc-gap);
  margin-bottom: var(--zc-gap);
  font-size: var(--zc-t-small-size);
}
.zc-alert__glyph{ font-size: 18px; line-height: var(--zc-t-body-lh); }
/* В светлой тело наследует цвет заголовка (--zc-alert-body-ink: inherit) —
   не меняется. В тёмной тело становится нейтральным --zc-ink: цветной текст
   на цветном тинте вибрирует (дизайн-бук §9.5); заголовок и значок остаются
   цветными через .zc-alert--*{color}. */
.zc-alert__body{ min-width: 0; color: var(--zc-alert-body-ink); }
.zc-alert__title{
  font-size: var(--zc-t-h2-size); font-weight: 600; margin: 0 0 4px;
}
.zc-alert--info{
  color: var(--zc-info); background: var(--zc-info-bg); border-color: var(--zc-info-line);
}
.zc-alert--success{
  color: var(--zc-success); background: var(--zc-success-bg); border-color: var(--zc-success-line);
}
.zc-alert--warning{
  color: var(--zc-warning); background: var(--zc-warning-bg); border-color: var(--zc-warning-line);
}
.zc-alert--error{
  color: var(--zc-danger); background: var(--zc-danger-bg); border-color: var(--zc-danger-line);
}
/* Заголовок — цветом варианта явно: он лежит ВНУТРИ .zc-alert__body, и
   `color` варианта, унаследованный сквозь body, был бы перекрыт
   --zc-alert-body-ink выше. Значок — прямой ребёнок .zc-alert и наследует
   цвет варианта напрямую, без переопределения здесь. */
.zc-alert--info .zc-alert__title{ color: var(--zc-info); }
.zc-alert--success .zc-alert__title{ color: var(--zc-success); }
.zc-alert--warning .zc-alert__title{ color: var(--zc-warning); }
.zc-alert--error .zc-alert__title{ color: var(--zc-danger); }

/* ============================================================== TILES === */
/* Сетка крупных ссылок-плиток: разделы на дашборде, отчёты на /reports.
   Ровно тот компонент, на котором сломалась приёмка 03.08.2026: разметку с
   классами .zc-dash__tile скопировали в reports_placeholder.html, а правила
   остались инлайном в <style> дашборда — и пять плиток отчётов вышли высотой
   19px вплотную друг к другу вместо 88px с промежутком. Класс переименован из
   .zc-dash__tile в .zc-tile: `__dash` в имени говорил «принадлежит дашборду»
   и провоцировал ровно ту копипасту, которая случилась.
   2 колонки на S, 3 на M, 5 на L. */
.zc-tiles{
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--zc-gap);
}
.zc-tile{
  display: flex; align-items: center; justify-content: center;
  min-height: 88px; text-align: center; padding: 8px;
  background: var(--zc-surface); border: 1px solid var(--zc-line-soft);
  border-radius: var(--zc-radius);
  font-weight: 600; color: var(--zc-ink);
}
.zc-tile:hover{ text-decoration: none; background: var(--zc-surface-2); box-shadow: var(--zc-shadow-2); }
@media (min-width: 768px){
  .zc-tiles{ grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 1200px){
  .zc-tiles{ grid-template-columns: repeat(5, 1fr); }
  .zc-tile{ min-height: 104px; }
}

/* ============================================== CARD-LIST (механизм A) == */
/* «Строка становится карточкой» — справочники/списки на S/M (юикс-требования
   §3.3). Роли: заголовок / ключ / значение. Используется будущими разделами
   (Номенклатура, Склады, Контрагенты, Пользователи) — готово заранее. */
.zc-rowcard{
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
  padding: 14px 4px;
  border-bottom: 1px solid var(--zc-line-soft);
  min-height: 64px;
}
.zc-rowcard:last-child{ border-bottom: 0; }
.zc-rowcard__main{ min-width: 0; }
.zc-rowcard__title{
  font-size: var(--zc-t-h2-size); font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.zc-rowcard__key{
  font-size: var(--zc-t-caption-size); color: var(--zc-ink-muted);
  font-family: var(--zc-font-mono);
}
.zc-rowcard__value{
  font-size: var(--zc-t-h2-size);
  font-family: var(--zc-font-mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ============================================== ФОТО ТОВАРА (миниатюра) = */
/* Номенклатура WB здесь — семь артикулов подряд, отличающихся одной цифрой
   (`WB_краскопульт_макита_2/3/6`) — фото снижает риск перепутать позицию при
   приёмке. Размер контейнера ФИКСИРОВАН заранее (aspect-ratio, не зависит от
   реального размера файла) — страница не должна прыгать, пока картинки
   догружаются на плохой связи (юикс-требования §3, §9.1 «скелетоны загрузки»
   не делаем, но геометрия резервируется всегда).

   Соотношение сторон 3:4 подобрано под ОБА пресета WB, которые реально
   используются (tm 75x100, c246x328 246x328) — один aspect-ratio годится и
   миниатюре списка, и картинке карточки.

   «Нет фото» и «фото не загрузилось» ВИЗУАЛЬНО ОДИНАКОВЫ и НАМЕРЕННО: глиф
   лежит в разметке всегда, фото (если ссылка вообще есть) кладётся ПОВЕРХ
   него отдельным слоем с `background-image` и абсолютным позиционированием.
   Успешная загрузка перекрывает глиф картинкой. Ссылка на WB может протухнуть
   (смена CDN, потеря карточки) — это тоже разновидность «фото нет», а не
   повод показывать сломанную иконку браузера или пустое место.

   ПОЧЕМУ ФОН, А НЕ <img> (06.08.2026): тег требовал `onerror="this.remove()"`,
   чтобы вместо не загрузившейся картинки проступал глиф, — и это был
   ЕДИНСТВЕННЫЙ обработчик события во всём отдаваемом HTML при каноне «ни
   строки скриптов». Фон, который не загрузился, не рисует ничего, и глиф
   проступает сам, без единой строки скрипта. Место под миниатюру резервирует
   САМА эта плашка (фиксированная ширина + aspect-ratio), а не атрибуты
   width/height тега, — поэтому контент не прыгает и без <img>. За резерв
   отвечает мутация «6 плашка фото резервирует место» в
   test_zachet_web_mobile.py: снятый отсюда aspect-ratio обязан краснеть.

   Иллюстраций тут нет (юикс-требования §9.1) — только глиф + подпись
   (у крупного варианта). */
.zc-thumb{
  position: relative;
  flex: 0 0 auto;
  width: 40px; aspect-ratio: 3 / 4;
  border-radius: var(--zc-radius);
  background: var(--zc-thumb-bg);
  border: 1px solid var(--zc-line-soft);
  overflow: hidden;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px;
  color: var(--zc-ink-faint);
}
.zc-thumb svg{ width: 16px; height: 16px; flex: 0 0 auto; }
.zc-thumb img{
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  /* Подложка + кромка, НЕ filter:invert (Д-57) — фото товара WB не инвертируется. */
  background: var(--zc-thumb-bg);
}
.zc-thumb__label{
  display: none;
  font-size: var(--zc-t-micro-size); color: var(--zc-ink-faint); text-align: center;
  padding: 0 4px;
}
.zc-thumb--lg{ width: 128px; }
.zc-thumb--lg svg{ width: 30px; height: 30px; }
.zc-thumb--lg .zc-thumb__label{ display: block; }

/* ==================================================== TABLE (карточки по умолчанию) = */
/* ПЕРЕВЁРНУТО 03.08.2026 (разбор трёх однотипных дефектов за один день:
   карточка товара, четыре заглушки документов, карточка контрагента — во
   всех трёх таблица на телефоне листалась вбок, потому что автор шаблона не
   вспомнил добавить явный класс карточного режима). Тот же принцип, что у
   guard'а маршрутов (routes/__init__.py, zachet_web_guard): новое ЗАКРЫТО по
   умолчанию, разрешение — явное. Здесь роль «маршрута» у любой таблицы:
   безопасное поведение (карточки на S/M) — умолчание для ЛЮБОГО <table>,
   даже вообще без класса .zc-table; горизонтальный скролл — исключение,
   которое теперь нужно включать явно (см. .zc-table--scroll ниже), а не
   помнить включить безопасность.

   Правила ниже разбиты на две части:
   1) .zc-table — чисто косметика (границы, отступы, фон шапки), которая
      не влияет на «скроллится вбок / стала карточкой» и одинакова что для
      обычной таблицы, что для карточной раскладки.
   2) @media (max-width: 1199px) — структурная подмена: ЛЮБАЯ таблица (тег
      <table>, класс не нужен) превращается в список карточек, шапка (thead)
      прячется, её роль берёт на себя атрибут data-th на каждой <td>. */
.zc-table-wrap{ overflow-x: auto; max-width: 100%; }
.zc-table{ border-collapse: collapse; width: 100%; }
.zc-table th, .zc-table td{
  padding: 8px 12px;
  text-align: left;
  border-bottom: 1px solid var(--zc-line-soft);
  font-size: var(--zc-t-small-size);
}
.zc-table th{
  background: var(--zc-surface-2);
  font-weight: 600; color: var(--zc-ink-muted);
}
.zc-table td.zc-mono, .zc-table th.zc-mono{ font-variant-numeric: tabular-nums; }
/* ЧИСЛОВАЯ КОЛОНКА — ПРАВЫЙ КРАЙ (11.08.2026). Ячейка со значением
   `ui.num()/ui.money()/ui.qty()` и её заголовок `<th class="zc-num">`
   выравниваются вправо: только так разряды соседних строк встают друг под
   друга, ради чего числа и печатаются моноширинными (см. `.zc-num` выше).

   ⚠ ПРАВИЛО ОБЯЗАНО СТОЯТЬ ИМЕННО ЗДЕСЬ, ПОСЛЕ `.zc-table th, .zc-table td{
   text-align: left }`, И НЕСТИ ПРЕФИКС `.zc-table`. Первая редакция стояла
   выше по файлу и без префикса — вес селекторов совпал (0,1,1), победило
   правило, стоящее ниже, и колонка осталась левой. Найдено замером правых
   краёв ячеек в браузере (`fixture.py`: 747/704/776/687 вместо одного
   значения), а не чтением кода: по коду правило выглядело рабочим.

   `:has()` вместо отдельного класса на `td` — потому что раздел не обязан
   помнить про два класса вместо одного, а `:has()` в этом файле уже несущая
   механика (панель массовых действий, гашение пустых ячеек). */
@media (min-width: 1200px){
  .zc-table td:has(> .zc-num), .zc-table th.zc-num{ text-align: right; }
}

/* ПЛОТНОСТЬ СТРОКИ — образец, каркас §4.4: «~55-57px, не 40 (не excel), не 72
   (не карточки)». `height` на <tr> в таблице работает как МИНИМУМ: строка с
   переносом текста вырастет, но не сожмётся ниже. Только на L — на S/M строка
   и так карточка, и фиксировать ей высоту нельзя.
   Шапка при этом НАМЕРЕННО остаётся низкой (~40px): она входит в бюджет
   «от заголовка до первой строки данных», который мы ужимаем до 140-160px. */
@media (min-width: 1200px){
  .zc-table tbody tr{ height: 57px; }
  .zc-table th{ padding-top: 10px; padding-bottom: 10px; }
}

/* ПЛОТНЫЙ РЕЖИМ ДЛЯ МАТРИЧНЫХ ТАБЛИЦ «МЕТРИКИ × ДНИ» (08.09.2026).
   Модификатор поверх правила плотности выше, НЕ замена: то правило рассчитано
   на список — одна строка = один объект (карточка, документ, контрагент), —
   и 57px там сознательный компромисс читаемости (образец §4.4). Здесь другая
   форма данных: строка — одна метрика в сводной сетке «метрики × дни»
   (эталон владельца — его Google-таблица «По артикулам»), и ценность именно
   в том, сколько строк и дней видно ОДНОВРЕМЕННО, а не в воздухе вокруг
   одной строки. `height` на <tr> работает как МИНИМУМ (см. правило выше) —
   без отдельного класса матрица наследует эти 57px независимо от паддингов
   ячеек, паддинг тут ни при чём, снижать нужно именно высоту строки.
   Специфичность оверрайда сознательно поднята через `table.zc-table` (не
   просто класс-модификатор в одиночку), чтобы порядок правил в файле не
   решал, кто победит — тот же приём, что у «ЧИСЛОВАЯ КОЛОНКА» выше.
   Подключается явно: <table class="zc-table zc-table--matrix">. */
@media (min-width: 1200px){
  table.zc-table.zc-table--matrix tbody tr{ height: 30px; }
  table.zc-table.zc-table--matrix th,
  table.zc-table.zc-table--matrix td{ padding: 3px 5px; }
}

/* ================= ВЫСОТА КАРТОЧКИ НА ТЕЛЕФОНЕ (10.08.2026) ================
   ЖАЛОБА ВЛАДЕЛЬЦА, ГЛАВНАЯ ИЗ ОБХОДА: «списки чудовищно длинные». Замер до
   правки (Chromium, 375×667, копия боевой базы, 13 товаров, стенд 8993):
     карточка товара ......................... 779px
     страница «Номенклатура» ................. 11 413px  (17 экранов)
     карточка документа ...................... 1 022px
   Из чего складывались 779px: ОДИННАДЦАТЬ ячеек, и почти каждая — 69px:
   подпись колонки (18px) + отбивка 4px + значение (24px) + вертикальные поля
   8+8. То есть две трети высоты уходило на то, что подпись стоит НАД
   значением, а не рядом с ним, и на воздух вокруг.

   ЧТО СДЕЛАНО, ТРЕМЯ ПРАВИЛАМИ (каждое меряется отдельно):
     1. Подпись — В КОЛОНКУ СЛЕВА, значение справа от неё. Ячейка из двух
        строк становится одной: 69px -> 32px.
     2. Пустое значение (`.zc-dash`, макрос `ui.dash()`) НЕ ЗАНИМАЕТ СТРОКУ.
        На «Номенклатуре» таких ячеек 52 из 143 — «Категория —»,
        «Штрихкод —», «Цена —», «Стоимость —» в каждой карточке. Это не
        сокрытие данных: `.zc-dash` — системный знак «показывать нечего», а
        «обязательное и незаполненное» несёт ДРУГОЙ знак — `.zc-na` («Н/Д»),
        и он остаётся на месте.
     3. Поля карточки и ячейки ужаты (10/12 -> 6/12 у строки, 8 -> 5 у
        ячейки) и между ячейками появилась тонкая линия: без воздуха карточка
        читается ХУЖЕ, если пары «подпись — значение» ничем не разделены.

   ПОЧЕМУ ПОДПИСЬ ПОЗИЦИОНИРУЕТСЯ АБСОЛЮТНО, А НЕ ЧЕРЕЗ flex/grid НА `td`.
   Флекс- или грид-контейнер разбивает содержимое ячейки на ОТДЕЛЬНЫЕ
   элементы: ячейка «Склад» («Поставщик Гуанчжоу → ФФ Складовик») собрана из
   нескольких тегов, и они разъехались бы по колонкам сетки, а не встали
   значением в одну. Абсолютная подпись + левое поле у ячейки работают с
   ЛЮБЫМ содержимым — с одним текстовым узлом, с пятью тегами, с картинкой, —
   и это ОБЩИЙ слой: он обязан пережить разметку, которую напишут завтра.
   ========================================================================= */
@media (max-width: 1199px){
  thead{ display: none; }
  table, tbody, tr, td{ display: block; width: 100%; }
  tr{
    border: 1px solid var(--zc-line-soft);
    border-radius: var(--zc-radius);
    padding: 6px 12px;
    margin-bottom: var(--zc-gap);
  }
  tr:last-child{ margin-bottom: 0; }
  td{
    border-bottom: 0;
    padding: 5px 0;
    white-space: normal;
    position: static;
    background: none;
  }
  /* Разделитель между парами «подпись — значение». Заменяет воздух, который
     отсюда убран: без него ячейки сливаются в сплошной столбик. */
  tr > td + td{ border-top: 1px solid var(--zc-line-soft); }
  /* Подпись колонки в карточном режиме — не декоративная мелочь, а
     единственный заголовок, который у ячейки остался (thead выше скрыт).
     Если data-th на <td> ЕСТЬ — подпись выводится как обычно. Если ЗАБЫТА —
     дефект не должен молча слиться с соседними ячейками (как забытая подпись
     схлопывалась бы в пустоту при обычном attr()); вместо неё — заметная
     плашка тревожного цвета: дефект виден на первом же просмотре страницы на
     телефоне, а не только при чтении HTML построчно. */
  td[data-th]{
    position: relative;
    padding-left: 116px;      /* колонка подписи 108px + зазор 8px */
    min-height: 34px;         /* 24px строки значения + поля 5+5 */
  }
  td[data-th]::before{
    content: attr(data-th);
    position: absolute; left: 0; top: 5px;
    width: 108px;
    font-size: var(--zc-t-caption-size);
    line-height: var(--zc-t-caption-lh);
    color: var(--zc-ink-muted);
    /* Длинная подпись («Позиций / Кол-во», «Последнее действие») переносится
       внутри своей колонки, а не толкает значение: колонка фиксированной
       ширины, перенос — по словам. */
    overflow-wrap: anywhere;
  }
  /* ПУСТОЕ ЗНАЧЕНИЕ НЕ ЗАНИМАЕТ СТРОКУ. Условие узкое НАМЕРЕННО: гасится
     только ячейка, ВСЁ содержимое которой — системный прочерк `ui.dash()`.
     Ячейка с прочерком И чем-то ещё, ячейка с «Н/Д» (`.zc-na` — обязательное
     не заполнено) и ячейка с пустой строкой остаются видимыми: пропажа
     первых двух была бы потерей факта, а не уборкой шума. */
  td:has(> .zc-dash:only-child){ display: none; }
  /* ПУСТАЯ ПОДПИСЬ НЕ ЗАНИМАЕТ КОЛОНКУ. `data-th=""` — законный случай:
     ячейка, у которой подписи нет по смыслу (плашка фото в «Номенклатуре»,
     строка пустого состояния под шапкой). Резервировать под неё 108px
     означало бы держать пустую полосу слева от картинки — ровно это и было
     видно на снимке `S375_goods_top.png` после первой правки. */
  td[data-th=""]{ padding-left: 0; }
  /* ЯЧЕЙКА С КНОПКАМИ БЕРЁТ ВСЮ ШИРИНУ, подпись встаёт НАД ними. Кнопка —
     это 44px тач-таргета плюс слово, то есть 130-160px; две таких в колонке
     значения (219px после подписи) не помещаются и встают друг под друга.
     Замер: ячейка «Действия» карточки товара — 114px, карточки документа —
     112px, при том что тот же ряд кнопок в полную ширину карточки занимает
     44px. Признак ячейки — общий класс кнопки `.zc-btn`, а не имя колонки:
     раздел волен назвать её как угодно, кнопку он рисует одну на систему
     (правило 1 `_ui.html`). */
  td[data-th]:has(.zc-btn){ padding-left: 0; }
  td[data-th]:has(.zc-btn)::before{
    position: static;
    display: block; width: auto;
    margin-bottom: 2px;
  }
  /* ЯЧЕЙКА С ПЛАШКОЙ ФОТО — по тому же правилу и по той же причине. Плюс
     своя: раздел вправе погасить подпись такой ячейки (`td.gd-cell-photo::
     before{content:none}` в «Номенклатуре» — над картинкой слово «Фото»
     лишнее), и тогда колонка подписи осталась бы ПУСТОЙ ПОЛОСОЙ слева от
     миниатюры — ровно это и было видно на снимке `S375_goods_top.png`.
     Здесь подпись, если она есть, встаёт над плашкой, а если её погасили —
     место под неё не резервируется вовсе. */
  td[data-th]:has(> .zc-thumb){ padding-left: 0; }
  td[data-th]:has(> .zc-thumb)::before{
    position: static;
    display: block; width: auto;
    margin-bottom: 2px;
  }
  td:not([data-th])::before{
    content: "⚠ нет подписи столбца (забыт data-th)";
    display: block;
    font-size: var(--zc-t-caption-size);
    font-weight: 600;
    color: var(--zc-danger);
    background: var(--zc-danger-bg);
    padding: 2px 6px;
    border-radius: 4px;
    margin-bottom: 4px;
  }
  /* Ссылка внутри свёрнутой в карточку строки (например «Товар» в журнале
     движений) — тот же тач-таргет 44px, что у остального интерфейса на S/M.
     Найдено 03.08.2026: без этого правила ссылка на sku_id рисовалась
     42×16px, вдвое ниже требуемого. */
  td a{
    display: inline-flex; align-items: center;
    min-height: 44px; min-width: 44px;
    /* ССЫЛКА НЕ ШИРЕ СВОЕЙ ЯЧЕЙКИ (10.08.2026). `inline-flex` — атомарный
       строчный бокс: по умолчанию он ровно по max-content и НЕ ужимается,
       поэтому длинный артикул без пробелов (`shevee_регистратор_блек_бокс`,
       232px) вылезал за правый край экрана, как только колонка подписи
       забрала себе 116px слева. Замер: right=377 при ширине окна 375 на
       «Номенклатуре», «Списаниях» и «Расхождениях». Предел ширины ужимает
       бокс, а разрыв в любом месте даёт тексту куда перенестись — иначе
       предел ничего не решает, содержимое просто вытечет. */
    max-width: 100%;
    overflow-wrap: anywhere;
  }
}

/* ============================================ TABLE SCROLL (явно, только L) = */
/* Горизонтальная прокрутка — законное исключение ТОЛЬКО для таблиц с большим
   числом колонок, которые на широком экране читать построчно (карточками)
   хуже, чем скроллом (отчёты этапа 7, ABC-анализ, длинный журнал). Требование
   безусловно: «горизонтальная прокрутка таблицы допустима только на
   компьютере» (юикс-требования §3.3) — поэтому весь блок целиком внутри
   min-width:1200px и НИЧЕГО не меняет на S/M: там, как у любой другой
   таблицы, работает карточный блок выше. Включается явно:
   <table class="zc-table zc-table--scroll"> внутри <div class="zc-table-wrap">. */
@media (min-width: 1200px){
  .zc-table--scroll{ min-width: 480px; }
  .zc-table--scroll th, .zc-table--scroll td{ white-space: nowrap; }
  .zc-table--scroll th{ position: sticky; top: 0; }

  /* СТРОКА ОБЯЗАНА ИМЕТЬ СВОЙ НЕПРОЗРАЧНЫЙ ФОН — иначе приклеенная ячейка
     ПРОСВЕЧИВАЕТ (найдено и воспроизведено замером 07.08.2026).

     Как выглядел дефект. Ниже у первой колонки стоит `background: inherit`,
     и это правильное намерение — «повторяй фон своей строки». Но `inherit`
     берёт ВЫЧИСЛЕННЫЙ фон родителя, а фон `<tr>` в обычном состоянии никем
     не назван, то есть `transparent`. Приклеенная ячейка едет ПОВЕРХ соседних
     колонок с прозрачной заливкой — и под номером документа проезжал текст
     колонки «Склад»: «2026-000001» поверх «ФФ BeeFull», нечитаемая каша.
     Замер живым браузером (Chromium, 1440px, копия боевой базы): «Документы»
     — 14 колонок, таблица просит 2599px при контейнере 1134px; из 26
     приклеенных ячеек 13 были прозрачными (шапка + все строки), пиксели
     первой ячейки менялись на 99.6% от одной лишь прокрутки вбок.
     На НАВЕДЁННОЙ строке дефекта не было: там `tr` фон получал, и `inherit`
     срабатывал — ровно поэтому дефект и жил долго, глазами он ловится только
     без мыши над строкой.

     ПОЧЕМУ ФОН НАЗВАН СТРОКЕ, А НЕ САМОЙ ЯЧЕЙКЕ. Соблазн — написать
     `td:first-child{ background: var(--zc-surface) }`. Это чинит прозрачность
     и заводит дефект хуже: строка бывает не только белой. Наведённая
     (`--zc-surface-2`), отмеченная галочкой (`--zc-accent-soft`, «Выделенная
     строка» ниже), тревожная в отчётах (`.rp-row--warn`, `--zc-warning-bg`) —
     и на каждой такой строке приклеенная колонка встала бы БЕЛОЙ ПОЛОСОЙ
     поперёк цветной строки. Заливка называется РОВНО В ОДНОМ МЕСТЕ — на
     `<tr>`, — а ячейка её наследует. Новое состояние строки, заведённое
     завтра, приклеенная колонка подхватит сама.

     `:where()` ОБНУЛЯЕТ ВЕС СЕЛЕКТОРА (0,0,2 — только теги `tbody`/`tr`)
     СОЗНАТЕЛЬНО: это фон ПО УМОЛЧАНИЮ, и его обязано перебивать что угодно с
     классом. Без `:where()` вес был бы (0,1,2), и правило перекрасило бы в
     белый тревожную строку отчёта `.rp-row--warn` (0,1,0) — то есть починка
     прозрачности сломала бы цвет строки. Наведение и отметка галочкой
     перебивают в обоих случаях, а вот `.rp-row--warn` — только так.

     `z-index` ПЕРВОЙ КОЛОНКЕ НУЖЕН ИМЕННО В ШАПКЕ. В теле таблицы приклеенная
     ячейка и так рисуется поверх обычных соседей. Но в шапке `sticky` стоит у
     КАЖДОЙ `th` (строка выше, вертикальная липкость), и при равном z-index
     побеждает порядок в разметке: непрозрачная вторая `th` («Склад»)
     закрашивала первую («Номер») целиком — в замере шапка приклеенной колонки
     была не полупрозрачной, а невидимой. */
  :where(.zc-table--scroll) thead tr{ background: var(--zc-surface-2); }
  /* Тень липкой шапки — ОТДЕЛЬНОЙ строкой, а не четвёртым свойством в строке
     выше: строка выше дословно записана шаблоном мутации №2 в
     `test_zachet_web_sticky.py`, и любая дописка в неё превращает мутацию в
     «шаблон найден 0 раз» — тест перестаёт проверять прозрачность шапки,
     оставаясь на вид зелёным. Вес (0,1,2) намеренно выше, чем у фона: тень
     не наследуется приклеенной ячейкой и ничего не перекрашивает. */
  .zc-table--scroll thead tr{ box-shadow: var(--zc-shadow-2); }
  :where(.zc-table--scroll) tbody tr{ background: var(--zc-surface); }
  .zc-table--scroll td:first-child, .zc-table--scroll th:first-child{
    position: sticky; left: 0; z-index: 1;
    background: inherit;
    /* ВИДИМЫЙ КРАЙ ПРИКЛЕЕННОЙ КОЛОНКИ (10.08.2026, второй пункт обхода).
       Дефект, который владелец описал как «рядом с приклеенным заголовком
       „Номер“ торчит обрывок „зо“, а плашка „нет плана“ срезана слева».
       Замер показал, что прозрачности больше нет (её починили 07.08.2026) и
       колонка честно перекрывает соседей: обрывок — это ХВОСТ уехавшего под
       неё столбца, а обрубленный кружок — половина плашки, наполовину
       заехавшая под колонку. Механика верна; неверно то, что край, за
       которым содержимое исчезает, НИЧЕМ НЕ ОБОЗНАЧЕН — и полслова у самой
       границы читается как порча отрисовки, а не как «дальше прокрутка».
       ПОЧЕМУ НЕ `border-right` И НЕ `box-shadow` — ОБА ПРОВЕРЕНЫ ЗАМЕРОМ,
       ОБА НЕ РАБОТАЮТ. `box-shadow` у ячейки глушит `border-collapse:
       collapse` (замер 07.08.2026, блок ниже). `border-right: 2px solid`
       поставлен и снят 10.08.2026: при `collapse` границы принадлежат СЕТКЕ
       таблицы, а не ячейке, и линия уезжала вместе с прокруткой — на снимке
       `docs_1440_scrolled` её у приклеенной колонки нет вовсе.
       Работает ПСЕВДОЭЛЕМЕНТ: он рисуется от самой ячейки, а `position:
       sticky` уже задаёт для него систему координат, поэтому едет вместе с
       колонкой. `inset 1px 0 0` даёт жёсткую линию ровно по краю, градиент —
       мягкое затухание поверх уехавшего содержимого, то есть привычный
       признак «дальше прокрутка». `right: -10px` кладёт его ЗА пределы
       ячейки, поверх соседей: z-index 1 приклеенной колонки это позволяет. */
  }
  .zc-table--scroll td:first-child::after,
  .zc-table--scroll th:first-child::after{
    content: "";
    position: absolute; top: 0; bottom: 0; right: -10px;
    width: 10px;
    box-shadow: inset 1px 0 0 var(--zc-line);
    background: linear-gradient(to right, var(--zc-scroll-fade), transparent);
    pointer-events: none;
  }
  /* ТЕНИ У ПРИКЛЕЕННОЙ КОЛОНКИ ЗДЕСЬ НЕТ — И ЭТО НЕ ЗАБЫВЧИВОСТЬ.
     Первая редакция этой починки добавила зеркало тени колонки действий
     (`box-shadow: 10px 0 10px -10px …`, «признак: правее есть ещё колонки»).
     Замер пикселей показал, что тень НЕ РИСУЕТСЯ ВООБЩЕ: у таблицы
     `border-collapse: collapse`, а в этом режиме браузер не рисует тень
     ячейки. Правило осталось бы мёртвым кодом с комментарием про то, чего на
     экране нет. Если признак приклейки понадобится — он делается тем, что при
     `collapse` работает (граница ячейки), и заводится замером, а не на веру.
     Тем же замером выяснилось, что уже стоящие тени у колонок действий
     (`.gd-cell-act`, `.zcd-cell-act`, `.cpf-cell-act`) не рисуются по той же
     причине — это отдельная находка и отдельная задача. */
}

/* ==================================================== PLACEHOLDER PAGE = */
.zc-placeholder{ padding: 8px 0; }
.zc-placeholder__title{ font-size: var(--zc-t-h1-size); font-weight: var(--zc-t-h1-w); margin: 0 0 8px; }
.zc-placeholder__text{ color: var(--zc-ink-muted); margin: 0 0 var(--zc-gap); }
.zc-placeholder__error{ color: var(--zc-danger); }
.zc-placeholder__routes{ margin: 0; padding-left: 1.1em; }
.zc-placeholder__routes li{ margin-bottom: 4px; font-size: var(--zc-t-small-size); }


/* =============================================================================
   ============  СЛОЙ «ЭКРАН-СПИСОК» — общий каркас разделов  =================
   =============================================================================
   Заведён 05.08.2026, переделка интерфейса под образец «Складолог».
   Канон: docs/zachet/юикс-эталон-каркас.md, часть 4 («Общие правила образца —
   применить ко ВСЕМ экранам сразу»).

   ЗАЧЕМ ЭТОТ СЛОЙ. До него каждый раздел собирал шапку экрана сам: где-то
   «← Дашборд» отдельной строкой над H1, где-то возврата не было вовсе; главное
   действие рисовалось кнопкой во всю ширину контента, под ней второй такой же
   полосой — «Фильтры (0)»; фильтры жили отдельной карточкой с собственной
   кнопкой «Применить». От заголовка до первой строки данных уходило 450-620px:
   на ноутбуке владелец не видел ни одной строки таблицы без прокрутки.

   Здесь лежат ГОТОВЫЕ КЛАССЫ, из которых раздел собирает экран. Разметку по
   ним пишет не человек, а макросы `templates/_ui.html` — они и есть публичный
   интерфейс этого слоя. Прямое использование классов допустимо, но макрос
   гарантирует порядок элементов и атрибуты доступности.

   БЮДЖЕТ ВЫСОТЫ (цель задания: 140-160px от верха H1 до первой строки данных):
       строка заголовка ниже H1 ... 36px  (H1 центрирован в ряду 44px)
       промежуток ................. 12px
       строка действий ............ 44px
       промежуток ................. 12px
       шапка таблицы .............. 40px
                                   -----
                                    144px
   Любое новое правило в этом слое, добавляющее вертикаль, обязано сверяться
   с этой таблицей — она проверяется замером в браузере, а не на глаз.

   БЕЗ JS — ЭТО ЖЁСТКОЕ ОГРАНИЧЕНИЕ СИСТЕМЫ (в «Зачёте» нет ни одной строки
   script, сьют test_zachet_web_mobile.py проверяет работу с выключенным JS).
   Поэтому интерактив здесь сделан платформенными средствами:
     * раскрытие/закрытие окон — атрибуты `popover` / `popovertarget`
       (нативный top layer: Escape, клик мимо окна и фокус-ловушка бесплатно);
     * подсветка выделенной строки и показ панели массовых действий —
       селектор `:has()` по состоянию чекбоксов;
     * сортировка, фильтры и пагинация — обычные ссылки GET.
   ============================================================================= */

/* --------------------------------------------------- 1. СТРОКА ЗАГОЛОВКА --- */
/* `← H1  [чип] [? подсказка] ..................................... [Поиск]`
   Одна строка. Возврат — стрелка СЛЕВА ОТ ЗАГОЛОВКА, в одной строке с ним
   (каркас §1.2 «Возврат назад»): отдельная строка «← Дашборд» над H1 съедала
   вертикаль и создавала второе правило возврата там, где нужно одно. */
.zc-pagehead{
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 8px var(--zc-gap);
  min-height: 44px;
  /* ФИКСИРОВАННЫЕ 12px, а НЕ var(--zc-gap). Токен на L равен 24px, и две
     таких отбивки (под заголовком и под строкой действий) съедали 48px из
     бюджета в 144 — замер показывал 176px вместо 152px. Отбивка между
     строками одного заголовочного блока — не тот отступ, который должен
     расти вместе с полями страницы. */
  margin: 0 0 12px;
}
.zc-pagehead__back{
  display: inline-flex; align-items: center; justify-content: center;
  /* Тач-таргет 44px обязателен (юикс-требования §3.5), но занимать 44px
     ШИРИНЫ слева от заголовка стрелке незачем — отрицательный внешний отступ
     возвращает оптическое выравнивание H1 по левому краю контента. */
  min-width: 44px; min-height: 44px;
  margin-left: -10px; margin-right: -6px;
  font-size: 22px; line-height: 1;
  color: var(--zc-ink); text-decoration: none;
}
.zc-pagehead__back:hover{ color: var(--zc-accent); text-decoration: none; }
.zc-pagehead__title{
  font-size: var(--zc-t-h1-size); line-height: var(--zc-t-h1-lh);
  font-weight: var(--zc-t-h1-w);
  margin: 0;                       /* .zc-h1 несёт margin-bottom — здесь его нет */
  min-width: 0;
}
.zc-pagehead__count{
  /* НЕ `--zc-ink-faint`: в светлой теме он даёт на фоне страницы 2.73 при
     норме 4.5 для обычного текста, а это счётчик списка («Документы 128»),
     а не декорация. `--zc-ink-muted` — 6.42 в светлой, 8.09 в тёмной. */
  color: var(--zc-ink-muted); font-weight: 400;
  font-size: var(--zc-t-body-size);
}
/* Ряд чипов-срезов. На широком экране — обычный flex-ряд рядом с заголовком
   (ведёт себя как прежняя россыпь чипов). На телефоне — своя строка, которая
   прокручивается ВНУТРИ СЕБЯ: три чипа «Номенклатуры» на 375px переносом
   давали два ряда и лишние 52px до первой строки данных. Приём и его
   обоснование — те же, что у `.zc-tabs__inner` и `.zc-actions`. */
.zc-pagehead__chips{
  display: flex; align-items: center; gap: 8px;
  min-width: 0;
}
@media (max-width: 767px){
  .zc-pagehead__chips{
    width: 100%;
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .zc-pagehead__chips::-webkit-scrollbar{ display: none; }
  .zc-pagehead__chips > .zc-chip{ flex: 0 0 auto; }
}

/* Всё, что правее заголовка, прижимается к правому краю строки. */
.zc-pagehead__right{
  margin-left: auto;
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}

/* Чип-срез рядом с H1 — «1 Незаполненные» (каркас §4.5). Белая пилюля с
   акцентной рамкой; включённый срез — залит. Это ССЫЛКА, а не форма. */
.zc-chip{
  display: inline-flex; align-items: center; gap: 6px;
  /* 44px — безусловный минимум тач-таргета на телефоне (юикс-требования §3.5,
     критерий приёмки №2, проверяется test_zachet_web_mobile.py). На L, где
     курсор, чип ужимается до 32px: иначе строка заголовка растёт до 56px и
     ломает бюджет высоты. Порядок правил mobile-first — сначала 44, потом
     переопределение для широкого экрана. */
  min-height: 44px; padding: 0 12px;
  border: 1px solid var(--zc-accent); border-radius: 999px;
  background: var(--zc-surface); color: var(--zc-accent);
  font-size: var(--zc-t-small-size); font-weight: 600;
  white-space: nowrap;
  box-shadow: var(--zc-shadow-1);
}
.zc-chip:hover{ background: var(--zc-accent-soft); text-decoration: none; }
.zc-chip.is-active{ background: var(--zc-accent); color: var(--zc-ink-invert); }
/* ЧИСЛО В ЧИПЕ — ПИЛЮЛЯ СПРАВА ОТ ПОДПИСИ, ровно как во вкладке раздела
   (`.zc-tabs__num` выше) и на кнопке фильтра (`.zc-btn__count`). До
   11.08.2026 это была голая цифра ПЕРЕД подписью («10 Без категории»), и три
   таких чипа подряд читались как список чисел, а не как список срезов. Общая
   геометрия с `.zc-tabs__num` не совпадение: это ОДНО И ТО ЖЕ число одного и
   того же среза, просто напечатанное в двух местах экрана. */
.zc-chip__num{
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 20px; height: 20px; padding: 0 6px;
  border-radius: 999px;
  background: var(--zc-accent-soft); color: var(--zc-accent);
  font-size: var(--zc-t-micro-size); font-weight: var(--zc-t-micro-w);
  font-variant-numeric: tabular-nums;
}
.zc-chip__label{ min-width: 0; }
.zc-chip__glyph{ font-size: 14px; line-height: 1; }
/* Включённый срез залит акцентом — пилюля числа на нём обязана вывернуться,
   иначе акцент по акценту. */
.zc-chip.is-active .zc-chip__num{
  background: var(--zc-surface); color: var(--zc-accent);
}
@media (min-width: 1200px){
  .zc-chip{ min-height: 32px; }
}
/* Тревожный срез (расхождения, недостачи) — тот же чип в красном. */
.zc-chip--danger{ border-color: var(--zc-danger-line); color: var(--zc-danger); }
.zc-chip--danger:hover{ background: var(--zc-danger-bg); }
.zc-chip--danger.is-active{ background: var(--zc-danger); color: var(--zc-ink-invert); }
.zc-chip--danger .zc-chip__num{ background: var(--zc-danger-bg); color: var(--zc-danger); }
.zc-chip--danger.is-active .zc-chip__num{ background: var(--zc-surface); color: var(--zc-danger); }

/* Подсказка «? Как …» (каркас §4.9) — серый текст со значком-кружком. */
.zc-hint{
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 44px;
  color: var(--zc-ink-muted); font-size: var(--zc-t-small-size);
}
.zc-hint:hover{ color: var(--zc-ink); text-decoration: underline; }
.zc-hint::before{
  content: "?";
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; flex: 0 0 18px;
  border: 1px solid currentColor; border-radius: 50%;
  font-size: 12px; font-weight: 600; line-height: 1;
}

/* Поиск — НЕ фильтр (каркас §4.5): живёт в правом краю строки заголовка,
   компактным полем с лупой. Обычная GET-форма, submit по Enter. */
.zc-search{ display: flex; align-items: center; position: relative; }
.zc-search input[type="search"], .zc-search input[type="text"]{
  width: 220px; padding-right: 38px;
  border-radius: 999px;
  /* 44px на S/M (палец), 40px только на L — см. пояснение к .zc-chip. */
  min-height: 44px;
}
@media (min-width: 1200px){
  .zc-search input[type="search"], .zc-search input[type="text"]{ min-height: 40px; }
}
/* ЛУПА — ЗНАК, А НЕ ЭМОДЗИ (11.08.2026). Стояла `🔍`: цветная, рисуется
   эмодзи-шрифтом системы, вид разный в разных ОС — и это на девяти экранах
   сразу, потому что поиск живёт в общей строке заголовка. Заменена на `⌕`
   (U+2315) из реестра `ui.GLYPHS`: наследует цвет и размер текста.
   РАЗМЕР ПОДНЯТ 15 -> 18px: у `⌕` тоньше штрих и меньше площадь, чем у
   эмодзи, и на прежних 15px знак пропадал в поле (замерено на снимке
   `L1440_goods.png`). Цвет остаётся `ink-faint` — лупа подсказка, а не
   действие: сама она некликабельна (`pointer-events: none`), поиск
   отправляется Enter'ом. */
.zc-search__glyph{
  position: absolute; right: 12px;
  color: var(--zc-ink-muted); font-size: 18px; line-height: 1;
  pointer-events: none;
}
@media (max-width: 767px){
  /* На телефоне поиск уходит на свою строку во всю ширину (как у образца:
     `order: -1; width: 100%`), иначе он выдавливает заголовок. */
  .zc-pagehead__right{ margin-left: 0; width: 100%; }
  .zc-search{ width: 100%; }
  .zc-search input[type="search"], .zc-search input[type="text"]{ width: 100%; }
}

/* --------------------------------------------------- 2. СТРОКА ДЕЙСТВИЙ ---- */
/* `[＋ Главное]  ВТОРИЧНОЕ ......... ⧩ ФИЛЬТР  ⧩ ФИЛЬТР  ЭКСПОРТ`
   Одна строка, ~56px вместе с нижним промежутком. Главное действие — слева.
   Всё, что после `.zc-actions__gap`, уезжает вправо. */
.zc-actions{
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 8px;
  min-height: 44px;
  margin: 0 0 12px;   /* см. пояснение к .zc-pagehead: не var(--zc-gap) */
}
.zc-actions__gap{ margin-left: auto; }
/* Форма внутри строки действий (экспорт POST-ом, submit фильтра) не должна
   ломать ряд собственным блочным поведением. */
.zc-actions form{ margin: 0; display: contents; }

/* СТРОКА ДЕЙСТВИЙ НА ТЕЛЕФОНЕ — ОДНА СТРОКА, ПРОКРУТКА ВНУТРИ СЕБЯ
   (10.08.2026, третий пункт обхода: «до первой строки данных ~1300px органов
   управления»). Замер до правки на 375px: «Номенклатура» — восемь кнопок,
   которые переносом вставали в ЧЕТЫРЕ ряда и занимали 200px; «Документы» —
   девять кнопок, те же 200px. Ужать их нечем: 44px высоты — безусловный
   минимум тач-таргета (Д-29), а ширина кнопки задана её словом.
   Механика ровно та же и по тем же причинам, что у строки вкладок
   `.zc-tabs__inner` выше (она эту развилку уже проходила): `nowrap` +
   `overflow-x` на самой строке, `min-width: 0`, чтобы флекс-минимум не
   отменил прокрутку, и `scrollbar-width: none`, чтобы полоса прокрутки не
   съела 15px у ряда высотой ровно 44px. Страница вбок при этом не едет —
   прокручивается контейнер, и это проверяется теми же тремя проверками §10,
   что и вкладки.
   ГЛАВНОЕ ДЕЙСТВИЕ ОСТАЁТСЯ ВИДНЫМ БЕЗ ПРОКРУТКИ: залитая кнопка стоит в
   строке первой (правило 1 макроса `_ui.html`), то есть в левом краю ряда.
   `.zc-actions__gap` (`margin-left:auto`) при `nowrap` схлопывается в ноль —
   свободного места в ряду нет, — и фильтры просто продолжают ряд. Это и
   требовалось: на телефоне «прижать вправо» нечего.
   Только S: на планшете (768-1199) ширины хватает на два ряда, и прятать
   действия за прокрутку там незачем. */
@media (max-width: 767px){
  .zc-actions{
    flex-wrap: nowrap;
    min-width: 0;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .zc-actions::-webkit-scrollbar{ display: none; }
  .zc-actions > *{ flex: 0 0 auto; }
}

/* ============================ КРАЙ ПРОКРУЧИВАЕМОЙ ПОЛОСЫ ================= */
/* Полоса, которая прокручивается внутри себя, ОБРЫВАЕТ содержимое у своего
   правого края — и обрывок слова у самой границы читается как порча
   отрисовки, а не как «дальше есть ещё». Это тот же дефект, за который
   владелец зацепился у приклеенной колонки («торчит обрывок „зо“»), только
   в другом месте: на снимке `S375_goods_top.png` строка действий обрывалась
   на «КАТЕ…».

   Затухание — `mask-image`, а не наложенный градиент: маска не добавляет ни
   одного узла в разметку (значит не ломает `:has()`-механику панели массовых
   действий и не считается «элементом за правым краем»), не участвует в
   раскладке и не перехватывает нажатия. Когда содержимое короче полосы,
   затухание приходится на пустое место и невидимо, поэтому включать его по
   условию «есть переполнение» (чего без скриптов и не сделать) не нужно.

   `.zc-tabs__inner` в списке НЕ случайно: механика прокрутки у трёх полос
   одна, и оставить одну из них без признака края значило бы завести второй
   способ решать ту же задачу (Д-4). */
.zc-tabs__inner,
.zc-pagehead__chips,
.zc-actions{
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 20px), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 20px), transparent);
}
@media (min-width: 768px){
  /* На широком экране строка действий и чипы не прокручиваются — маска там
     только съела бы правый край последней кнопки. */
  .zc-pagehead__chips, .zc-actions{ -webkit-mask-image: none; mask-image: none; }
}

/* Иерархия кнопок (каркас §4.3). Базовый `.zc-btn` = ГЛАВНОЕ действие:
   залитая акцентная пилюля, одна на экран. Ниже — остальные четыре уровня. */

/* Значимое вторичное: белая пилюля с акцентной рамкой. */
.zc-btn--outline{
  background: var(--zc-surface); color: var(--zc-accent);
  border-color: var(--zc-accent);
}
.zc-btn--outline:hover{ background: var(--zc-accent-soft); color: var(--zc-accent-hover); }

/* Обычное вторичное: ТЕКСТ КАПСОМ без рамки и фона.
   ЦВЕТ В ПОКОЕ — СЛУЖЕБНЫЙ, НЕ АКЦЕНТНЫЙ (26.08.2026).
   Замер первого экрана «Документов»: 60 элементов, окрашенных акцентом, из них
   42 — эти кнопки и их значки (шесть фильтров, экспорт, «Открыть»/«Изменить» в
   каждой строке). Типографика та же: 14/600 капсом против 14/400 у самих
   данных — полоса «ПЕРИОД ОПЕРАЦИЯ СТАТУС …» весила почти как заголовок
   страницы и заметно больше документов, ради которых экран открыт. В светлой
   теме это гасил сам акцент (`#3B5BDB` тёмный, уходит назад), в тёмной
   `#8098EE` светлый и лезет вперёд — дефект СОЗДАН тёмной темой.
   Акцент никуда не делся: он возвращается на наведении (правило ниже) и
   остаётся у главной залитой кнопки и у ссылок на объекты (номер документа,
   название товара). В светлой `--zc-accent-quiet` = `--zc-accent`. */
.zc-btn--text{
  background: transparent; color: var(--zc-accent-quiet); border-color: transparent;
  padding: 0 12px;
  font-size: var(--zc-t-small-size); font-weight: 600;
  letter-spacing: .04em; text-transform: uppercase;
}
.zc-btn--text:hover{ background: var(--zc-accent-soft); color: var(--zc-accent-hover); }

/* Недоступное: тот же текст капсом, но серый и некликабельный. Отдельный
   класс нужен ссылкам — у <a> нет атрибута disabled, а «выглядит серым, но
   ведёт куда-то» хуже, чем ничего. */
.zc-btn--muted,
.zc-btn--text[disabled], .zc-btn[disabled]{
  background: transparent; color: var(--zc-ink-faint); border-color: transparent;
  cursor: default; pointer-events: none;
}
/* ⚠ НО У ЗАЛИТОЙ КНОПКИ СИЛУЭТ ОСТАЁТСЯ (26.08.2026, обход дизайн-критика).
   Правило выше писалось под кнопки-ССЫЛКИ в списках (`kind='muted'`,
   `--text`): там рядом есть соседний контекст, и «серый текст капсом» читается
   как выключенное действие. У ГЛАВНОЙ, залитой кнопки соседей нет — на витрине
   `disabled` превращал её в серую надпись без контейнера, и человек уже не
   понимал, что здесь вообще было действие. Ровно этот дефект однажды чинили
   точечно у кнопки «Спросить» консультанта (см. её правило ниже по файлу);
   теперь он закрыт системно, а точечное правило осталось как есть — у него
   своя заливка `--zc-fill-on-elev`, потому что кнопка лежит на поднятом слое.

   ЧЕМ ОТЛИЧАЕТСЯ «ЗАЛИТАЯ ВЫКЛЮЧЕННАЯ» ОТ «ТЕКСТОВОЙ ВЫКЛЮЧЕННОЙ» — разметка
   уже отвечает, придумывать признак не нужно (`_ui.html`, макрос `btn`):
     * `ui.btn('Сохранить', disabled=True)` -> `zc-btn zc-btn--muted` + атрибут
       `disabled` — это ГЛАВНАЯ кнопка в выключенном состоянии;
     * `ui.btn(..., kind='muted')`          -> `zc-btn zc-btn--text zc-btn--muted`
       — это ТЕКСТОВАЯ кнопка, контейнера у неё нет и не было.
   То есть разделитель — наличие `--text`, а не наличие `disabled` (плоская
   выключенная кнопка атрибут несёт, но `--muted` ей макрос дописывает сам).
   `--outline`/`--secondary`/`--danger` исключены — у них силуэт несёт
   собственная рамка, и заливка её бы затёрла.

   Контраст ТЕКСТА выключенного органа управления WCAG 1.4.3 не нормирует
   (исключение для inactive components), а вот фигура обязана читаться: замер
   `--zc-btn-disabled-bg` #232A35 — 1.29 к фону страницы и 1.20 к карточке.
   В светлой теме оба токена прозрачны — там кнопка выглядит ровно как
   выглядела до правки.

   ИСКЛЮЧЕНИЯ ЗАВЁРНУТЫ В `:where()` — ЭТО НЕСУЩАЯ ДЕТАЛЬ, А НЕ УКРАШЕНИЕ.
   `:not()` тянет за собой вес своего аргумента, и пять исключений подряд дали
   бы селектору (0,6,0) — выше, чем у точечного правила
   `.zc-btn.zc-consultant__send[disabled]` (0,3,0) ниже по файлу. Кнопка
   «Спросить» получила бы общую заливку вместо своей, а её заливка не общая по
   делу: панель консультанта лежит на ПОДНЯТОМ слое, и там заливка обязана быть
   светлее подложки (`--zc-fill-on-elev`), а не темнее. `:where()` обнуляет вес
   исключений: правило остаётся (0,1,0)/(0,2,0) — ровно как у общего правила
   выше, которое оно перебивает порядком в файле, — и точечные правила
   компонентов по-прежнему сильнее. */
.zc-btn--muted:where(:not(.zc-btn--text):not(.zc-btn--outline):not(.zc-btn--secondary):not(.zc-btn--danger):not(.zc-btn--danger-text)),
.zc-btn[disabled]:where(:not(.zc-btn--text):not(.zc-btn--outline):not(.zc-btn--secondary):not(.zc-btn--danger):not(.zc-btn--danger-text)){
  background: var(--zc-btn-disabled-bg);
  border-color: var(--zc-btn-disabled-line);
}
/* ⚠ ИСКЛЮЧЕНИЕ: ПРИГЛУШЁННАЯ КНОПКА, КОТОРАЯ ОБЪЯСНЯЕТ, ПОЧЕМУ НЕЛЬЗЯ
   (11.08.2026, найдено обходом и подтверждено нажатием в браузере).

   `pointer-events: none` выше стоит по делу: «приглушённая» кнопка не должна
   выполнять действие. Но у неё есть ВТОРАЯ роль, и она прямо противоположна —
   открыть окно с объяснением, почему действие недоступно. На `/warehouses`
   так сделаны СЕМЬ корзин из семи: `kind='muted'` + `popovertarget='n-…'`,
   окно «Почему это место нельзя убрать». Правило выше гасило им нажатие
   целиком: `elementFromPoint` в центре кнопки возвращал не её, а контейнер
   `.zcw-acts`, окно не открывалось НИКОГДА, и объяснение было недостижимо ни
   мышью, ни пальцем. Человек видел серую корзину, жал — не происходило
   ничего.

   Признак «кнопка не делает, а объясняет» уже есть в разметке и придумывать
   его не нужно: это наличие `popovertarget`. Кнопка БЕЗ него по-прежнему
   мертва — контракт `ui.btn(kind='muted')` не меняется. */
.zc-btn--muted[popovertarget]{ pointer-events: auto; cursor: pointer; }

/* Опасное в списке — текст капсом красным. Залитая красная кнопка (подтверждение
   необратимого) — это уже существующая `.zc-btn--danger-confirm`. */
.zc-btn--danger-text{
  background: transparent; color: var(--zc-danger); border-color: transparent;
  padding: 0 12px;
  font-size: var(--zc-t-small-size); font-weight: 600;
  letter-spacing: .04em; text-transform: uppercase;
}
.zc-btn--danger-text:hover{ background: var(--zc-danger-bg); color: var(--zc-danger); }

/* Значок внутри кнопки (＋, воронка) — всегда ВМЕСТЕ с текстом (каркас §4.7). */
.zc-btn__glyph{ font-size: 16px; line-height: 1; }

/* Счётчик выбранного НА САМОЙ кнопке фильтра (каркас §4.5: «счётчик выбранного
   — на кнопке»), а не отдельной строкой «Фильтры (0)» под ней. Ноль не
   печатается вовсе — макрос его не выводит. */
.zc-btn__count{
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 20px; height: 20px; padding: 0 6px;
  border-radius: 999px;
  background: var(--zc-accent); color: var(--zc-ink-invert);
  font-size: var(--zc-t-micro-size); font-weight: 600;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}
.zc-btn--text .zc-btn__count{ background: var(--zc-accent); }
.zc-btn:not(.zc-btn--text):not(.zc-btn--outline) .zc-btn__count{
  background: var(--zc-ink-invert); color: var(--zc-accent);
}

/* ------------------------------------------- 3. ОКНА: ФИЛЬТР И МОДАЛКА ----- */
/* ОДИН механизм на оба: нативный `popover`. Кнопка-открыватель — это
   `<button popovertarget="ID">`, само окно — `<div id="ID" popover>`.
   Что даёт платформа без единой строки JS:
     * top layer — окно поверх всего, независимо от z-index и overflow предков
       (у прежнего `<details>` вложенность в карточку с overflow резала окно);
     * Escape закрывает;
     * клик мимо окна закрывает (popover=auto);
     * фокус уходит внутрь окна и возвращается на кнопку при закрытии;
     * `::backdrop` — затемнение фона.
   Где вводимые данные терять нельзя (форма создания/редактирования) — окно
   объявляется `popover="manual"`: Escape и клик мимо ОТКЛЮЧАЮТСЯ платформой,
   закрыть можно только крестиком или «Отмена», а те ведут в окно
   подтверждения. Это наш ответ на требование «подтверждение при потере
   введённого» в системе без JS: отличить «форма грязная» от «форма чистая»
   без скрипта нельзя, поэтому решение принимается ЗАРАНЕЕ, на уровне типа
   окна, а не в момент закрытия. */
/* САМ элемент `[popover]` — ЭТО КАРТОЧКА ОКНА, а не подложка во весь экран.
   Разница принципиальная и стоила одной переделки: если растянуть popover на
   весь вьюпорт и рисовать карточку внутри, то «клик мимо окна» попадает В
   САМ popover, и платформенное light-dismiss не срабатывает — окно закрылось
   бы только Escape'ом и крестиком. Затемнение фона даёт `::backdrop`, который
   к площади самого элемента отношения не имеет. */
.zc-modal{
  position: fixed;
  inset: auto;
  top: 6vh; left: 50%;
  /* именно `translate`, а не `transform`: transform занят анимацией
     появления на .zc-modal__card, и два правила не должны драться */
  translate: -50% 0;
  margin: 0; padding: 0;
  width: min(640px, calc(100vw - 32px));
  max-width: none;
  max-height: 86vh; overflow: auto;
  border: 0; border-radius: var(--zc-radius-lg);
  /* СЛОЙ 4, А НЕ 3 (26.08.2026, дефект «модалка не поднимается над страницей»).
     Замер до правки: поверхность модалки против затемнённой страницы — 1.372
     в тёмной против 3.469 в светлой. Ступень поверхности — единственное
     средство, которое в темноте реально поднимает (Д-52, §7.1: потолок чёрной
     тени 1.13), поэтому окно уезжает на верхнюю ступень лестницы.
     `--zc-elev-4` в светлой = `#FFFFFF`, ровно как `--zc-elev-3`, — светлая
     модалка не меняется ни на пиксель.
     Тень — `--zc-shadow-overlay`, а не `--zc-shadow-4`: в тёмной это стек
     уровня 4 (кромка .12), в светлой БУКВАЛЬНО прежний светлый `--zc-shadow-3`. */
  background: var(--zc-elev-4); color: var(--zc-ink);
  box-shadow: var(--zc-hairline-top), var(--zc-shadow-overlay);
  /* Явный display:none — страховка для браузера БЕЗ поддержки popover:
     там атрибут игнорируется, и без этой строки содержимое всех окон
     страницы вывалилось бы в поток простынёй. Открыть окно такой браузер всё
     равно не сможет — поэтому раздел обязан сохранять маршрут отдельной
     страницы для того же действия (см. шапку макроса `modal` в _ui.html). */
  display: none;
}
.zc-modal:popover-open{ display: block; }
/* Подложка: плотность + матовое размытие (26.08.2026). Затемнение одно в
   темноте почти ничего не даёт (страница уходит на полступени), поэтому
   работают три средства сразу: ступень поверхности у самого окна, плотная
   плёнка и размытие того, что за ней. Оба значения — токены, и оба в светлой
   теме равны сегодняшним (`rgba(27,36,50,.5)` и `none`). */
.zc-modal::backdrop{
  background: var(--zc-scrim);
  -webkit-backdrop-filter: var(--zc-scrim-blur);
  backdrop-filter: var(--zc-scrim-blur);
}
/* КРАСКИ ВНУТРИ ПОДНЯТОГО ОКНА. Слой 4 светлее слоя, под который подбирались
   тонкие краски, — на нём `--zc-line-soft` даёт 1.04 (разделитель пунктов
   исчезает), `--zc-ink-faint` 4.21 (бук запрещает его на elev-4, §3),
   `--zc-danger` 4.22 (провал 4.5). Переопределяем ТОКЕНАМИ, а не значениями:
   в светлой теме правая часть равна тому, что стоит там сегодня, и ни одно
   правило ниже по дереву не меняется. */
.zc-modal,
.zc-bottomnav__sheet,
.zc-consultant-panel,
.zcd-sku-pop,
.zcd-pick-pop{
  --zc-line-soft: var(--zc-line-on-elev);
  --zc-line:      var(--zc-line-mid-on-elev);
  --zc-ink-faint: var(--zc-ink-faint-on-elev);
  --zc-danger:    var(--zc-danger-on-elev);
}
.zc-modal__card{
  /* Появление «увеличением из центра», как у образца. Уважает системную
     настройку «уменьшить движение» (см. блок ниже). */
  display: block;
  animation: zc-modal-in .18s ease-out;
}
.zc-modal--narrow{ width: min(480px, calc(100vw - 32px)); }
.zc-modal--wide  { width: min(880px, calc(100vw - 32px)); }
@keyframes zc-modal-in{
  from{ opacity: 0; transform: scale(.96); }
  to  { opacity: 1; transform: scale(1); }
}
@media (prefers-reduced-motion: reduce){
  .zc-modal__card{ animation: none; }
}
.zc-modal__head{
  display: flex; align-items: center; gap: var(--zc-gap);
  padding: var(--zc-gap) var(--zc-gap) 8px;
}
.zc-modal__title{
  font-size: var(--zc-t-h2-size); line-height: var(--zc-t-h2-lh);
  font-weight: var(--zc-t-h2-w); margin: 0; flex: 1 1 auto; min-width: 0;
}
.zc-modal__close{
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; min-height: 44px; margin: -8px -8px -8px 0;
  border: 0; border-radius: 999px; background: transparent;
  color: var(--zc-ink); font-size: 20px; line-height: 1; cursor: pointer;
}
.zc-modal__close:hover{ background: var(--zc-fill-on-elev); }
.zc-modal__body{ padding: 0 var(--zc-gap) var(--zc-gap); }
.zc-modal__note{ color: var(--zc-ink-muted); font-size: var(--zc-t-small-size); margin: 0 0 var(--zc-gap); }
/* Подвал: кнопки ПРИЖАТЫ ВПРАВО, отмена — левее главной (каркас §4.8). */
.zc-modal__foot{
  display: flex; align-items: center; justify-content: flex-end;
  flex-wrap: wrap; gap: 8px;
  padding: var(--zc-gap);
  border-top: 1px solid var(--zc-line-soft);
}
/* Вложенный блок на сером фоне внутри модалки. */
.zc-modal__group{
  background: var(--zc-surface-2);
  border-radius: var(--zc-radius);
  padding: var(--zc-gap);
  margin-bottom: var(--zc-gap);
}
/* Поля модалки в две колонки — с M и шире; на телефоне всегда одна. */
.zc-modal__grid{ display: grid; grid-template-columns: 1fr; gap: 0 var(--zc-gap); }
@media (min-width: 768px){
  .zc-modal__grid{ grid-template-columns: 1fr 1fr; }
  .zc-modal__grid .zc-field--wide{ grid-column: 1 / -1; }
}
/* Обязательное поле и ошибка валидации (каркас §4.8). */
.zc-req{ color: var(--zc-danger); }
.zc-field--error input, .zc-field--error select, .zc-field--error textarea{
  border-color: var(--zc-danger-line);
}
.zc-field__error{
  display: flex; align-items: center; gap: 6px;
  color: var(--zc-danger); font-size: var(--zc-t-caption-size); margin: 4px 0 0;
}
.zc-field__error::before{
  content: "!";
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; flex: 0 0 16px;
  border-radius: 50%; background: var(--zc-danger); color: var(--zc-ink-invert);
  font-size: 11px; font-weight: 700; line-height: 1;
}

/* Окно фильтра — та же механика, окно поменьше и прижато к верху. */
.zc-filterwin{ width: min(420px, calc(100vw - 32px)); top: 10vh; }
.zc-filterwin__list{ display: flex; flex-direction: column; }
/* Вариант фильтра — ССЫЛКА, применяется СРАЗУ (каркас §4.5: кнопки
   «Применить» нет). Отмеченный вариант помечен галочкой. */
.zc-filterwin__opt{
  display: flex; align-items: center; gap: 8px;
  min-height: 44px; padding: 0 8px;
  border-radius: var(--zc-radius);
  color: var(--zc-ink); font-size: var(--zc-t-body-size);
}
.zc-filterwin__opt:hover{ background: var(--zc-surface-2); text-decoration: none; }
.zc-filterwin__opt.is-on{ color: var(--zc-accent); font-weight: 600; }
.zc-filterwin__opt::before{
  content: "";
  width: 18px; height: 18px; flex: 0 0 18px;
  border: 1px solid var(--zc-line); border-radius: 4px;
}
.zc-filterwin__opt.is-on::before{
  content: "\2713";
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--zc-accent); border-color: var(--zc-accent);
  color: var(--zc-ink-invert); font-size: 13px; line-height: 1;
}

/* ------------------------------------- 4. ПАНЕЛЬ МАССОВЫХ ДЕЙСТВИЙ --------- */
/* Появляется МЕЖДУ строкой действий и шапкой таблицы, как только отмечена
   хоть одна строка (каркас §1.2). Без JS это делает `:has()` по состоянию
   чекбоксов внутри общей обёртки `.zc-listing`.

   ВАЖНО про место: панель не «выталкивает» таблицу рывком — она занимает ту
   же высоту, что и была бы, потому что появляется в потоке ВМЕСТО пустоты
   ровно в момент выделения, и до выделения её нет вовсе (display:none), то
   есть в бюджет 140-160px «от заголовка до первой строки» она не входит. */
.zc-listing{ display: block; }
.zc-bulkbar{
  display: none;
  align-items: center; flex-wrap: wrap; gap: 8px;
  min-height: 44px; margin-bottom: 8px;
  padding: 0 8px;
  background: var(--zc-accent-soft); border-radius: var(--zc-radius);
  box-shadow: var(--zc-shadow-2);
}
.zc-listing:has(tbody input[type="checkbox"]:checked) .zc-bulkbar{ display: flex; }
.zc-bulkbar__count{
  font-size: var(--zc-t-small-size); font-weight: 600; color: var(--zc-accent);
  font-variant-numeric: tabular-nums;
}
/* Счётчик «N строк выбрано.» под таблицей слева — тоже только при выделении. */
.zc-listing__selected{ display: none; font-size: var(--zc-t-small-size); color: var(--zc-ink-muted); }
.zc-listing:has(tbody input[type="checkbox"]:checked) .zc-listing__selected{ display: block; }

/* ----------------------------------------------- 5. ТАБЛИЦА: ДОБАВКИ ------- */
/* Косметика и плотность самой таблицы — выше, в блоке TABLE. Здесь только то,
   что появилось вместе с образцом: колонка-чекбокс, сортировка кликом по
   заголовку, подсветка выделенной строки. */

/* Первая колонка — чекбокс, всегда (каркас §4.4). */
.zc-table__check{ width: 44px; }
.zc-table__check input[type="checkbox"]{
  width: 18px; height: 18px; min-height: 0; margin: 0; padding: 0;
}
/* Тач-таргет чекбокса на телефоне обеспечивает подпись-обёртка. */
.zc-table__check label{
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; min-height: 44px; margin: 0;
}
/* САМ КВАДРАТИК НА ТЕЛЕФОНЕ — 24px, А НЕ 18 (10.08.2026, обход владельца:
   «чекбоксы строк 18×18 при норме 44×44, двенадцать штук на экране»).
   Нажимаемая область и до правки была честные 44×44 — её держит `<label>`
   выше, и это ровно то, что требует Д-29 («тач-таргет», а не «размер
   квадратика»). Но 18px под пальцем НЕ ВИДНО куда целиться, и обход это
   поймал глазами, а не измерением: цель есть, а признака цели нет. Поэтому
   растёт рисунок, а 44px обёртки остаются нетронутыми — растянуть до 44 сам
   `input` нельзя, нативный флажок такого размера рисуется пустой коробкой в
   пол-строки и читается как поле ввода, а не как галочка.
   На широком экране (курсор, плотность строки 57px) остаются 18px. */
@media (max-width: 1199px){
  .zc-table__check input[type="checkbox"]{ width: 24px; height: 24px; }
}
@media (max-width: 1199px){
  /* В карточном режиме каждая <td> печатает подпись колонки из `data-th`.
     Для колонки-чекбокса это дало бы строку «Выбор» над квадратиком —
     подпись у элемента, который и так очевиден, и лишние 20px в КАЖДОЙ
     карточке. Гасим подпись, но не сам чекбокс: выделять строки на телефоне
     нужно ровно так же. Доступность держится на `.zc-visually-hidden`
     внутри <label> (макрос `ui.td_check` подставляет её всегда). */
  td.zc-table__check{ padding: 0; }
  td.zc-table__check::before{ content: none; }
  td.zc-table__check label{ justify-content: flex-start; }
}

/* Сортировка кликом по названию колонки; стрелка — ТОЛЬКО у активной. */
.zc-th-sort{
  display: inline-flex; align-items: center; gap: 4px;
  color: inherit; font: inherit;
}
.zc-th-sort:hover{ color: var(--zc-ink); text-decoration: none; }
.zc-th-sort__arrow{ font-size: 11px; color: var(--zc-ink); }

/* ЗЕБРА ПЛОТНЫХ ТАБЛИЦ (дизайн-бук §9.1) — второй носитель разделения строк
   в тёмной теме: одного волоска `--zc-line-soft` на 30+ строк мало. В светлой
   токен равен фону строки, поэтому визуально зебры там нет и не появляется.

   ДВА ОГРАНИЧЕНИЯ, БЕЗ КОТОРЫХ ЗЕБРА ЛОМАЕТ СОСЕДЕЙ (найдено замером
   25.08.2026, оба воспроизводились на живом стенде).

   1. ВЕС СЕЛЕКТОРА ПОНИЖЕН ДО (0,0,2) через `:where()`. Первая редакция
      `.zc-table tbody tr:nth-child(even)` весила (0,2,2) и перебивала
      `.rp-row--warn` (0,1,0) — тревожная строка отчёта на каждой чётной
      позиции теряла свой цвет (тест `test_zachet_web_sticky.py` ловил это
      как «тревожная: строка ... , ячейка ...»). Ровно та же ловушка уже
      разобрана у фона прокручиваемой таблицы выше по файлу.
      (0,0,2) выбрано НЕ произвольно: это вес правила
      `:where(.zc-table--scroll) tbody tr` — зебра обязана его ПЕРЕБИВАТЬ
      (стоит ниже по файлу, при равном весе побеждает порядок), иначе в
      широких таблицах зебры не будет вовсе. Любое правило С КЛАССОМ
      (наведение, отметка галочкой, `.rp-row--warn`) перебивает зебру.

   2. ТОЛЬКО ШИРОКИЙ ЭКРАН. На S/M строка таблицы — отдельная карточка без
      собственного фона, лежащая прямо на `--zc-bg`. Непрозрачная зебра там
      покрасила бы каждую вторую карточку в цвет поверхности — в светлой теме
      это белые полосы на сером фоне, то есть редизайн светлой темы, который
      PRD прямо запрещает. Зебра нужна плотной таблице, а плотная таблица
      живёт только на L. */
@media (min-width: 1200px){
  :where(.zc-table) tbody tr:where(:nth-child(even)){ background: var(--zc-row-zebra); }
}

/* Выделенная строка — светло-голубая заливка на всю ширину (каркас §4.4).
   Держится на `:has()`: чекбокс отмечен → строка подсвечена, без JS.
   --zc-row-selected численно равен --zc-accent-soft в обеих темах — имя
   отдельное, потому что роль отдельная (дизайн-бук §8.2). */
.zc-table tbody tr:has(input[type="checkbox"]:checked){
  background: var(--zc-row-selected);
}
@media (min-width: 1200px){
  /* --zc-row-hover: в светлой равен прежнему --zc-surface-2 (не меняется),
     в тёмной — своя полупрозрачная заливка (surface-2 отдана липкой шапке). */
  .zc-table tbody tr:hover{ background: var(--zc-row-hover); }
  .zc-table tbody tr:has(input[type="checkbox"]:checked):hover{ background: var(--zc-row-selected); }
}
/* Таблица лежит в белой карточке со скруглением; фон страницы светло-серый. */
.zc-table-card{
  background: var(--zc-surface);
  border: 1px solid var(--zc-line-soft);
  border-radius: var(--zc-radius);
  overflow: hidden;
  box-shadow: var(--zc-hairline-top), var(--zc-shadow-1);
}
.zc-table-card > .zc-table-wrap{ margin: 0; }
@media (max-width: 1199px){
  /* На S/M строки — карточки сами по себе; вторая рамка вокруг них лишняя. */
  .zc-table-card{ background: transparent; border: 0; border-radius: 0; overflow: visible; box-shadow: none; }
}

/* Прочерк «значение пусто» и красное «Н/Д» для незаполненного обязательного
   (каркас §4.4). Носителей два — цвет И текст, как требует §4.3. */
.zc-dash{ color: var(--zc-ink-faint); }
.zc-na{ color: var(--zc-danger); font-weight: 600; }

/* --------------------------------------------------- 6. ПАГИНАЦИЯ ---------- */
/* Под таблицей, прижата вправо (каркас §1.2). Слева от неё — счётчик
   выделенного, поэтому обёртка — flex с space-between. */
.zc-listing__foot{
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 8px;
  margin-top: var(--zc-gap);
}
.zc-pager{
  display: flex; align-items: center; gap: 4px; flex-wrap: wrap;
  margin-left: auto;
}
.zc-pager__label{ font-size: var(--zc-t-small-size); color: var(--zc-ink-muted); margin-right: 8px; }
.zc-pager__size{ width: auto; min-height: 36px; padding: 4px 8px; border-radius: 999px; }
.zc-pager__btn{
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 36px; min-height: 36px; padding: 0 8px;
  border: 1px solid var(--zc-line); border-radius: 999px;
  background: var(--zc-surface); color: var(--zc-accent);
  font-size: var(--zc-t-small-size);
}
.zc-pager__btn:hover{ background: var(--zc-accent-soft); text-decoration: none; }
.zc-pager__btn.is-off{ color: var(--zc-ink-faint); border-color: var(--zc-line-soft); pointer-events: none; }
.zc-pager__cur{
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 36px; min-height: 36px; padding: 0 8px;
  border-radius: var(--zc-radius);
  background: var(--zc-accent); color: var(--zc-ink-invert);
  font-size: var(--zc-t-small-size); font-weight: 600;
  font-variant-numeric: tabular-nums;
}
@media (max-width: 767px){
  /* Тач-таргеты пагинации на телефоне — 44px, как всё остальное. */
  .zc-pager__btn, .zc-pager__cur, .zc-pager__size{ min-width: 44px; min-height: 44px; }
}

/* --------------------------------------------------- 7. ПУСТОЕ СОСТОЯНИЕ --- */
/* Формула образца (каркас §4.6): жирный заголовок «Нет <объектов>» + ОДНА
   строка-инструкция «чтобы появились — нужно …». Прижато влево, без картинок
   и без центровки. Таблица и пагинация при этом исчезают целиком, а строка
   заголовка и строка действий ОСТАЮТСЯ — главное действие всегда под рукой.

   Текст — не «ничего не найдено», а путь к данным. Наши честные формулировки
   («сумма неизвестна, а не ноль рублей»; «учёт по кабинету ещё не начат»)
   ЛУЧШЕ образца и переезжают сюда как есть — под них третья, необязательная
   строка `.zc-empty__note`. */
.zc-empty{ padding: var(--zc-gap) 0 var(--zc-page-pad); max-width: 68ch; }
.zc-empty__title{
  font-size: var(--zc-t-h2-size); line-height: var(--zc-t-h2-lh);
  font-weight: 600; color: var(--zc-ink); margin: 0 0 4px;
}
.zc-empty__lead{ color: var(--zc-ink-muted); margin: 0; }
/* `--zc-ink-muted`, не `--zc-ink-faint`: это строка, которую человек обязан
   прочитать («сумма неизвестна, а не ноль рублей»), а faint даёт в светлой
   теме 2.73 на фоне страницы при норме 4.5. */
.zc-empty__note{ color: var(--zc-ink-muted); font-size: var(--zc-t-caption-size); margin: 8px 0 0; }
.zc-empty__actions{ display: flex; gap: 8px; flex-wrap: wrap; margin-top: var(--zc-gap); }

/* --- 8. КАРТОЧКА-ПЕРЕХОД (хаб) — СНЯТА 06.08.2026 -------------------------- */
/* Хабов с плитками в системе не осталось: пункт раздела ведёт сразу в главный
   подраздел, состав раздела виден строкой вкладок (`.zc-tabs` выше). Стили
   `.zc-hubcards`/`.zc-hubcard*` удалены вместе с макросами `_ui.html` и обоими
   шаблонами хаба — чтобы плитки нельзя было воскресить «просто добавив класс». */

/* ------------------------------- 9. МАРКЕР РАСКРЫТИЯ У СТАРЫХ ФИЛЬТРОВ ----- */
/* ВРЕМЕННОЕ правило — снять, когда все разделы переедут на `zc_filter_btn`.

   Разбор дефекта «в „Списаниях“ и „Перемещениях“ блок „Фильтры“ не
   раскрывается» (05.08.2026). Механика `<details>` исправна: настоящий клик
   настоящим Chromium раскрывает блок во ВСЕХ пяти отчётах, на копии боевых
   данных и на синтетике, на 1280px и на 375px — воспроизвести отказ не
   удалось. Что удалось найти — причину, по которой отказ ВЫГЛЯДИТ так:

   у `<summary>` в этой системе НЕТ НИ ОДНОГО признака раскрываемости.
   Правило `details > summary { list-style: none }` (строка ~146) гасит
   встроенный треугольник, а взамен не даёт ничего: свёрнутые «Фильтры» —
   просто белая полоса с жирным словом, неотличимая от заголовка секции.
   Раскрытие тоже не всегда заметно: в «Пополнениях» и «Остатке» под полосой
   разворачивается высокий блок с наборами «Источник»/«Режим», а в
   «Списаниях» и «Перемещениях» на пустом кабинете — четыре поля, дублирующие
   пресеты периода, которые уже видны выше строкой чипов. Клик «ничего не
   меняет» ровно там, где владелец это и сообщил.

   Поэтому чиним ПРИЗНАК, а не обработчик: треугольник ▸/▾, курсор, наведение
   и рамка-состояние. Селекторы адресуют классы, живущие в CSS разделов
   (reports.css/zc-docs.css и инлайн-стили) — намеренно: это общий механизм,
   один и тот же на пяти экранах, и чинить его пятью копиями нельзя. */
.rp-filters > summary,
.gd-filters > summary,
.zcd-filters > summary,
.rc-filters > summary{
  gap: 8px;
}
.rp-filters > summary::before,
.gd-filters > summary::before,
.zcd-filters > summary::before,
.rc-filters > summary::before{
  content: "\25B8";
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; flex: 0 0 16px;
  font-size: 12px; color: var(--zc-ink-muted);
  transition: transform .12s ease-out;
}
.rp-filters[open] > summary::before,
.gd-filters[open] > summary::before,
.zcd-filters[open] > summary::before,
.rc-filters[open] > summary::before{ content: "\25BE"; }
.rp-filters > summary:hover,
.gd-filters > summary:hover,
.zcd-filters > summary:hover,
.rc-filters > summary:hover{ background: var(--zc-surface-2); color: var(--zc-accent); }
@media (prefers-reduced-motion: reduce){
  .rp-filters > summary::before,
  .gd-filters > summary::before,
  .zcd-filters > summary::before,
  .rc-filters > summary::before{ transition: none; }
}

/* ================================================= ВЫБОР ФАЙЛА ========== */
/* Макрос `ui.file_field()` (`_ui.html`, раздел 10). Разбор, почему подписи
   `Choose File` / `No file chosen` нельзя перевести настройкой, — там же.
   Здесь механика: НАСТОЯЩИЙ `<input type="file">` лежит прозрачным слоем
   поверх рамки и принимает клик (родное поведение, ни строки скрипта), а
   видимые подписи — наши. */
.zc-file__box{
  position: relative;
  display: flex; align-items: center; gap: 12px;
  min-height: 44px;                 /* тач-таргет, юикс-требования §3.5 */
  padding: 6px 12px;
  border: 1px dashed var(--zc-line);
  border-radius: var(--zc-radius);
  background: var(--zc-surface);
  max-width: var(--zc-form-col);
}
.zc-file__input{
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  opacity: 0;                       /* видимости нет, поведение родное */
  cursor: pointer;
}
/* Фокус с клавиатуры получает сам <input> — он невидим, поэтому кольцо
   рисуется на рамке. Без этого поле было бы недостижимо глазами при обходе
   табом (юикс-требования §3.4). */
.zc-file__input:focus-visible ~ .zc-file__btn{ text-decoration: underline; }
.zc-file:has(.zc-file__input:focus-visible) .zc-file__box{
  outline: 2px solid var(--zc-accent);
  outline-offset: 2px;
}
.zc-file__box:hover{ border-color: var(--zc-accent); }
.zc-file__btn{
  flex: 0 0 auto;
  padding: 6px 14px;
  border: 1px solid var(--zc-accent); border-radius: 999px;
  color: var(--zc-accent);
  font-size: var(--zc-t-small-size); font-weight: 600;
  white-space: nowrap;
}
.zc-file__note{
  color: var(--zc-ink-muted);
  font-size: var(--zc-t-caption-size);
  min-width: 0; overflow-wrap: anywhere;
}
/* ПРИЗНАК «ФАЙЛ ВЫБРАН» ЕСТЬ ТОЛЬКО У ОБЯЗАТЕЛЬНОГО ПОЛЯ. У необязательного
   пустое значение валидно, и `:valid` горит всегда — подпись «файл выбран»
   стояла бы там с самого открытия формы, то есть врала бы. Поэтому правило
   привязано к `.zc-file--req`, который макрос ставит вместе с `required`. */
.zc-file--req .zc-file__input:invalid ~ .zc-file__note .zc-file__state::before{
  content: "файл не выбран";
}
.zc-file--req .zc-file__input:valid ~ .zc-file__note .zc-file__state::before{
  content: "файл выбран";
  color: var(--zc-success);
}
/* ПОЛЕ С `state=true` — ТО ЖЕ МЕСТО, НО ТЕКСТ НАСТОЯЩИЙ (14.08.2026). CSS
   здесь ничего не утверждает: он не может знать, выбран ли файл у поля без
   `required`. Текст ставит либо сервер (`file_state`, после отправки), либо
   браузер (`static/receipt_form.js`, сразу после выбора) — и только второй
   означает «файл прямо сейчас лежит в поле», поэтому зелёным красится ровно
   он, по классу `is-set`, а не по `:valid`, который у необязательного поля
   горит всегда и на этом месте уже однажды соврал. */
.zc-file--state .zc-file__state{ color: var(--zc-ink-muted); }
.zc-file--state .zc-file__state.is-set{ color: var(--zc-success); font-weight: 600; }

/* ==================================================== ИИ-КОНСУЛЬТАНТ === */
/* Кнопка справа снизу + шторка-диалог справа во всю высоту (12.08.2026,
   просьба владельца + уточнение в тот же день «по образцу Timeweb Cloud»:
   не окошко в углу — шторка на весь правый край, содержимое страницы под
   ней остаётся видимым, открытие/закрытие БЕЗ перехода/перезагрузки.
   Разметка — base.html (гейт `not hide_nav and csrf_token is defined`),
   поведение — static/consultant.js. Цвета — ТОЛЬКО токены --zc-*, палитра
   образца (тёмная) не копируется — юикс-требования §4.

   БЕЗ JS ШТОРКА НЕДОСТИЖИМА НАМЕРЕННО: `visibility:hidden` не снимается
   ничем, кроме класса `.is-open`, которого без скрипта взяться неоткуда —
   кнопка при этом остаётся обычной рабочей ссылкой на /consultant. */

/* --- плавающая кнопка ----------------------------------------------------
   Позиция считается от нижней панели/safe-area теми же переменными, что и
   сама `.zc-bottomnav` (`--zc-bottomnav-h`, `--zc-safe-bottom`) — числа НЕ
   задублированы, а взяты из общего источника истины (юикс-требования §3.1).
   На S/M кнопка поднята НАД нижней панелью на `--zc-consultant-gap` (16px —
   отступ поверх её высоты и safe-area), на L (>=1200px, у нижней панели
   `display:none`, `--zc-consultant-gap` переопределён в 24px) прижата к низу
   как обычный плавающий элемент. z-index=50: выше нижней панели (30) и
   шторки «Ещё» (40), но ниже консультанта (55/60), который поверх ВСЕГО.

   ИКОНКА (обход 12.08.2026, дефект 1 — образец Timeweb Cloud): пузырь
   диалога `<svg>`, не «?» — вопросительный знак читался как «справка/FAQ»,
   а не «спросить ИИ». Сама разметка — base.html (`.zc-consultant-btn__icon`,
   `currentColor` наследует `color` отсюда, свой цвет иконке не назначаем). */
.zc-consultant-btn{
  position: fixed;
  right: var(--zc-page-pad);
  bottom: calc(var(--zc-bottomnav-h) + var(--zc-safe-bottom) + var(--zc-consultant-gap));
  z-index: 50;
  width: var(--zc-consultant-size); height: var(--zc-consultant-size);
  min-width: 44px; min-height: 44px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 999px;
  background: var(--zc-accent);
  color: var(--zc-ink-invert);
  /* Светлое значение сохранено буквально (задача аппликатора, п.3) —
     --zc-shadow-2 дало бы более мягкую тень, чем сейчас. */
  box-shadow: var(--zc-shadow-consultant-btn);
}
.zc-consultant-btn:hover{ background: var(--zc-accent-hover); text-decoration: none; color: var(--zc-ink-invert); }
.zc-consultant-btn:active{ background: var(--zc-accent-active); }
.zc-consultant-btn__icon{ display: block; }
@media (min-width: 1200px){
  .zc-consultant-btn{ bottom: var(--zc-consultant-gap); }
}

/* --- резерв места у СОДЕРЖИМОГО страницы — В «ЗАЧЁТЕ» ЭТО ОБХОД 12.08.2026,
   ЗДЕСЬ ОН СНЯТ (07.09.2026). Причина резерва там: плавающая кнопка
   ИИ-консультанта (`.zc-consultant-btn`, `#zc-main` резервировало под неё
   `var(--zc-gap) + var(--zc-consultant-gap) + var(--zc-consultant-size)` =
   104px на L). У «Инсайтера» этой кнопки НЕТ ВООБЩЕ — `templates/base.html`
   не рендерит её разметку (`grep -rn consultant templates/` пуст, кроме
   упоминаний в комментариях девяти вкладок, которые как раз и жаловались на
   этот резерв). Резервировать место под элемент, которого нет на странице, —
   не защита, а немотивированные 104px, съеденные у каждого экрана: это и
   раздувало `sku_card.html` до 1016-1020px на 1440×900 вместо 900 (замер
   07.09.2026, см. `tab_shelves_in.html`/`tab_niche.html`). Оставлен только
   исходный отступ-«воздух», который был здесь и без кнопки. */
#zc-main{
  padding-bottom: var(--zc-gap);
}

/* =========================================== INSIGHTER: ВЫСОТА В ОДИН ЛИСТ ===
   Правило `ui-no-walls-of-text.md`, «Экран — один лист»: `scrollHeight` всей
   страницы <= 900 на 1440×900, а прокрутка — только у таблиц/списков внутри
   вкладки. Раньше это чинилось ДЕВЯТЬ РАЗ по-разному (`max-height: 400px` в
   tab_niche/tab_shelves_in, `430px` в tab_positions, `58vh` в
   tab_shelves_out/tab_zones, `46vh` в tab_reviews, `250px` в tab_rnp) — числа
   подобраны на глаз под то, что осталось от экрана ПОСЛЕ вычета 104px
   резерва под несуществующую кнопку и переноса строки вкладок на два ряда
   (следующий блок ниже), и рассинхронизировались бы при любой правке шапки.

   ЗДЕСЬ — ОДИН расчёт вместо девяти: на L (>=1200px) `<body>` и `#zc-main` —
   flex-колонки на всю высоту вьюпорта, `.ins-tabbody` (обёртка
   `{% block tab_body %}` в `sku_card.html`) — flex-колонка внутри `#zc-main`
   с `flex:1; min-height:0`, и последний информационный блок вкладки
   (`.zc-table-wrap` для простых вкладок; `.zc-table-card` — обёртка над ним —
   для вкладок с карточкой) получает `flex:1; min-height:0; overflow-y:auto`.
   Высота «шапки над таблицей» (сайт-шапка + карточка sku + строка вкладок +
   свои фильтры/чипы/график вкладки) вычитается АВТОМАТИЧЕСКИ потоком флекса —
   раздутие любого блока НАД таблицей само сжимает доступную высоту таблицы,
   а не переполняет экран. Список артикулов (`sku_list.html`) — тот же
   механизм на `#zc-main > .zc-table-card` напрямую, `.ins-tabbody` там нет.

   Вкладки, у которых прокручиваемый блок обёрнут в СВОЙ контейнер
   (`.ins-rnp`, `.ins-pos`, `.ins-rev` — навигация/тулбар/легенда над
   таблицей, тулбар не должен сжиматься) продолжают цепочку flex ещё на один
   уровень в СВОЁМ файле (`tab_rnp.html`/`tab_positions.html`/
   `tab_reviews.html`) — а не отдельной константой здесь: их разметка тому
   автору и принадлежит («зона одного автора», см. заголовки файлов).

   `min-height:0` на каждом звене ОБЯЗАТЕЛЕН — без него flex-item не
   сжимается ниже содержимого и вся цепочка перестаёт работать (проверено:
   без него `.zc-table-wrap` рос вместе с таблицей и `overflow-y:auto` не
   включался никогда). На S/M (<1200px, есть нижняя панель) правило не
   действует — там штатная прокрутка страницы, как и раньше.

   `height:100vh` У BODY, А НЕ `min-height` (замер 07.09.2026, живой Chromium):
   с `min-height` `<body>` как корневой flex-контейнер без заданной ВЫСОТЫ
   считает свой размер от содержимого — flex-контейнеру нужна ОПРЕДЕЛЁННАЯ
   высота, чтобы было что распределять `flex-grow`'ом; без неё «лишнего»
   места нет по определению, и `flex:1` ничего не сжимает. Итог был не
   гипотетическим: `.ins-shin-wrap` (206 строк конкурентов без явного лимита)
   раздула `<body>` до 12153px вместо 900 — `min-height:0` на всей цепочке
   был на месте, а сжимать оказалось попросту НЕЧЕГО. `height:100vh` даёт
   body фиксированный размер, и `flex:1 + min-height:0` на потомках наконец
   выключают лишнее в `overflow-y:auto`, а не рвут страницу вниз. */
@media (min-width:1200px){
  body{ display:flex; flex-direction:column; height:100vh; }
  #zc-main{ flex:1 1 auto; display:flex; flex-direction:column; min-height:0; }

  /* `overflow-y:auto` здесь — СТРАХОВКА, не основной механизм: у настроенных
     вкладок (ниже) внутренний `.zc-table-wrap` сам забирает ровно всю
     доступную высоту через `flex:1`, и до края `.ins-tabbody` дело не
     доходит. Для вкладки БЕЗ таблицы (`tab_ai.html` — пустое состояние) или
     с содержимым, которое не размечено под этот флекс (`tab_stock.html` —
     фиксированные блоки: график + две колонки с уже своим `max-height` у
     складов), это последний рубеж: если контент всё-таки выше отведённого
     места, скроллится сама вкладка целиком, а НЕ вся страница. */
  .ins-tabbody{ flex:1 1 auto; min-height:0; overflow-y:auto; display:flex; flex-direction:column; }

  /* `max-height:100%` — НЕ `flex:1`. Разница видна на разреженных данных
     (замер 07.09.2026, `tab_positions.html`: 3 ключа без позиций, все
     значения «—»): `flex:1` НАСИЛЬНО растягивал карточку на весь остаток
     экрана — три строки и голый пустой прямоугольник ниже, тот самый
     «пустой серый блок», который правило `ui-no-walls-of-text.md` просит
     не показывать. `max-height:100%` — потолок, а не цель: короткая таблица
     остаётся своего роста, длинная (206 строк в `tab_shelves_in.html`)
     упирается в потолок и прокручивается внутри себя — обе вкладки читают
     ОДИН и тот же расчёт высоты, просто не растягиваются туда, где им
     нечего показать. Проценты у `max-height` разрешаются относительно
     родителя, ТОЛЬКО если у него самого определённая (не auto) высота —
     она у `.ins-tabbody` есть, потому что цепочка `flex` доходит до
     фиксированного `height:100vh` на `<body>` (см. выше).

     Простая вкладка: `.zc-table-wrap` — прямой потомок `.ins-tabbody`
     (tab_niche, tab_shelves_in). */
  .ins-tabbody > .zc-table-wrap{ flex:0 1 auto; min-height:0; max-height:100%; overflow-y:auto; }

  /* Вкладка с карточкой: `.zc-table-card` — прямой потомок `.ins-tabbody`
     (tab_shelves_out, tab_zones) ИЛИ список артикулов (`#zc-main` напрямую,
     тегов `.ins-tabbody` там нет). Вложенная карточка «Пропали» внутри
     `<details>` у tab_shelves_out НЕ прямой потомок — селектор её не
     задевает, второстепенный список остаётся как есть, без флекса. */
  .ins-tabbody > .zc-table-card,
  #zc-main > .zc-table-card{
    flex:0 1 auto; min-height:0; max-height:100%; display:flex; flex-direction:column;
  }
  .ins-tabbody > .zc-table-card > .zc-table-wrap,
  #zc-main > .zc-table-card > .zc-table-wrap{
    flex:0 1 auto; min-height:0; overflow-y:auto;
  }
}

/* --- шторка ---------------------------------------------------------------
   Полная высота (top:0; bottom:0), прижата к правому краю — «во всю высоту
   экрана», как у образца. Ширина — `min(440px, calc(100vw - 32px))`: на
   десктопе это честные 380-440px из ТЗ, на телефоне 32px суммарного отступа
   оставляют слева узкую полосу страницы — «почти во весь экран», а не
   ЛИТЕРАЛЬНО весь (содержимое позади доказуемо не исчезло).

   ЗАКРЫТА ПО УМОЛЧАНИЮ `visibility:hidden` + `translateX(100%)` — так есть
   что анимировать (`transform`/`opacity`) БЕЗ рывка, и `visibility` (не
   `display`) убирает панель из таб-порядка ровно так же надёжно, но с
   версией задержки на закрытии (см. `transition` у `visibility` ниже),
   чтобы не обрывать анимацию исчезновения на середине.

   ⚠ ДЕФЕКТ (замер живым браузером в `test_zachet_web_goods.py` §17.5/§17.7,
   найден 12.08.2026 ПОСЛЕ обхода дефектов 1-3, разбор отдельным заходом):
   `translateX(100%)` уводит закрытую панель вправо ЗА пределы окна —
   `right` её прямоугольника был 1880 при 1440px и 718 при 375px (панель
   шириной 440/343px паркуется целиком за краем). Пока `display` оставался
   `flex`, `getBoundingClientRect()` у панели и ВСЕХ её потомков (крестик,
   поле ввода и т.д.) продолжал отдавать ненулевую ширину — ровно то, что
   ловит §17.5/17.7 (сканирует `body *` на `right > innerWidth`, «законно»
   только внутри контейнера со СВОЕЙ горизонтальной прокруткой, чем эта
   панель не является).
   `document.documentElement.scrollWidth` при этом РАВЕН `clientWidth` (нет
   ни разъезда страницы, ни видимой прокрутки) — `position:fixed` не
   расширяет scrollable overflow root, если ни один предок панели не ставит
   `transform`/`filter`/`contain`, меняющий containing block с viewport на
   себя (здесь предков с такими свойствами нет). Иными словами: экран не
   едет вбок и глазами дефект не виден НИКОГДА (поэтому не попал ни в один
   из скриншотов обхода) — но геометрия элемента формально «висит» за
   краем окна, а сьюта в этом проекте намеренно проверяет именно геометрию
   элементов, а не только `scrollWidth` (см. её же докстрока `FIT_JS`/Д-31:
   `overflow-x:hidden` на body тоже прятал бы `scrollWidth`, но не решал
   задачу).

   ЛЕЧЕНИЕ — `display:none` в закрытом состоянии (щит, который предложил
   Глеб): при `display:none` `getBoundingClientRect().width` панели и ЛЮБОГО
   её потомка равен 0, `FIT_JS` их даже не досматривает (`if (!r.width)
   return;`, её же код). Раскладка/анимация не потеряны — `display`
   переключает СКРИПТ (`static/consultant.js`), СИНХРОННО ДО начала
   transition (см. `openPanel`/`closePanel` там): `display:flex` выставляется
   инлайн-стилем на кадр РАНЬШЕ, чем класс `.is-open` меняет
   `transform`/`opacity` (иначе браузер не увидит «исходное» состояние для
   интерполяции — переход от `display:none` пропал бы совсем), а обратно в
   `display:none` уходит ПОСЛЕ анимации закрытия — та же пауза .2s, что и у
   `visibility` ниже, чтобы не обрывать её на середине. При
   `prefers-reduced-motion` скрипт прячет панель СРАЗУ, без искусственной
   паузы (анимации всё равно нет — ждать нечего).

   `prefers-reduced-motion` — статья, а не декорация: она обязана уважаться
   в любом анимированном элементе интерфейса (юикс-требования §3, общее
   требование доступности), не только там, где это проверено сьютом. */
.zc-consultant-panel{
  position: fixed;
  top: 0; right: 0; bottom: 0;
  width: min(440px, calc(100vw - 32px));
  z-index: 60;
  display: none;                    /* переключает static/consultant.js, см. выше */
  flex-direction: column;
  background: var(--zc-elev-4);   /* слой 4, 26.08.2026 — см. `.zc-modal` */
  border-left: 1px solid var(--zc-line);
  box-shadow: -8px 0 24px var(--zc-shadow-color);
  visibility: hidden;
  opacity: 0;
  transform: translateX(100%);
  transition: transform .2s ease, opacity .2s ease, visibility 0s linear .2s;
}
.zc-consultant-panel.is-open{
  display: flex;                    /* декларативный конечный вид — совпадает с тем, что уже выставил JS инлайн-стилем к этому моменту */
  visibility: visible;
  opacity: 1;
  transform: translateX(0);
  transition: transform .2s ease, opacity .2s ease, visibility 0s linear 0s;
}
@media (prefers-reduced-motion: reduce){
  .zc-consultant-panel{ transition: none; }
  .zc-consultant-panel.is-open{ transition: none; }
}

/* --- шапка шторки: аватар + название + роль + крестик --------------------
   Аватар — тот же приём, что у `.zc-topnav__logo` (base.html, рядом с
   разметкой): ИНЛАЙНОВЫЙ <svg> на `currentColor`, а не фоновая картинка.
   Файлом рисунок здесь был до 25.08.2026 и давал короб к кружку аватара
   2.64:1 вместо 5.44 — заливки внутри `logo.svg` зашиты литералами и темы
   не слышат. Кружок под рисунком — `--zc-accent-soft` (токен). */
.zc-consultant__head{
  display: flex; align-items: center; gap: 12px;
  padding: var(--zc-gap);
  border-bottom: 1px solid var(--zc-line-soft);
  flex: 0 0 auto;
}
.zc-consultant__avatar{
  flex: 0 0 auto;
  width: 40px; height: 40px;
  border-radius: 999px;
  background: var(--zc-accent-soft);
  display: flex; align-items: center; justify-content: center;
}
.zc-consultant__avatar-mark{
  display: block;
  width: 20px; height: 20px;
  color: var(--zc-accent);
}
.zc-consultant__head-text{ flex: 1 1 auto; min-width: 0; }
.zc-consultant__title{
  margin: 0;
  font-size: var(--zc-t-h2-size); line-height: var(--zc-t-h2-lh); font-weight: var(--zc-t-h2-w);
  color: var(--zc-ink);
  /* НЕ однострочный обрез (был `overflow:hidden;text-overflow:ellipsis;
     white-space:nowrap`) — находка design-critique 12.08.2026, обход
     дефектов 1-3: на 390px (ширина шторки `100vw-32px`, из ТЗ) заголовок
     «Консультант «Зачёта»» обрезался до «Консультант «Зачё…» — висящая
     непарная кавычка читается как поломка рендера, а не как задуманное
     сокращение (см. f2_mobile_open_header_zoom.png в отчёте обхода). Ширина
     шторки везде хватает под перенос МАКСИМУМ на вторую строку — просто
     разрешаем перенос вместо обрезки, на десктопе (панель 440px) строка и
     так помещается в одну, поведение не меняется. */
  overflow-wrap: anywhere;
}
.zc-consultant__role{
  margin: 0;
  font-size: var(--zc-t-caption-size); color: var(--zc-ink-muted);
}
.zc-consultant__close{
  flex: 0 0 auto;
  width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 999px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--zc-ink-muted);
  font-size: 18px; line-height: 1;
  cursor: pointer;
}
.zc-consultant__close:hover{ background: var(--zc-fill-on-elev); color: var(--zc-ink); }

/* --- лента сообщений -------------------------------------------------------
   Пузыри: помощник слева (поверхность-2), человек справа (акцент) — то же
   различие «свой/чужой», что уже есть в системе (`.zc-table tbody
   tr:has(input:checked)` и т.п.), здесь выражено выравниванием И цветом
   одновременно (не только цветом — юикс-требования §4.3). Время — отдельной
   строкой ПОД пузырём, как в образце. */
.zc-consultant__feed{
  flex: 1 1 auto;
  overflow-y: auto;
  padding: var(--zc-gap);
  display: flex; flex-direction: column; gap: 10px;
}
.zc-consultant__msg{
  max-width: 86%;
  display: flex; flex-direction: column; gap: 2px;
}
.zc-consultant__msg-text{
  padding: 10px 12px;
  border-radius: var(--zc-radius);
  font-size: var(--zc-t-small-size); line-height: var(--zc-t-small-lh);
  /* переносы строк ответа — стилями, не разметкой (текст вставляется через
     textContent, см. consultant.js) */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.zc-consultant__msg-time{
  font-size: var(--zc-t-micro-size);
  color: var(--zc-ink-faint);
  padding: 0 4px;
}
.zc-consultant__msg--bot{ align-self: flex-start; }
.zc-consultant__msg--bot .zc-consultant__msg-text{
  background: var(--zc-surface-2); color: var(--zc-ink);
  border-bottom-left-radius: 4px;
}
.zc-consultant__msg--user{ align-self: flex-end; }
.zc-consultant__msg--user .zc-consultant__msg-time{ align-self: flex-end; }
.zc-consultant__msg--user .zc-consultant__msg-text{
  background: var(--zc-accent); color: var(--zc-ink-invert);
  border-bottom-right-radius: 4px;
}
.zc-consultant__msg--pending .zc-consultant__msg-text{ color: var(--zc-ink-muted); font-style: italic; }
.zc-consultant__msg--error .zc-consultant__msg-text{ background: var(--zc-danger-bg); color: var(--zc-danger); }

/* индикатор ожидания ответа (15-23с измерено вживую — пустой пузырь недопустим,
   см. base.html/consultant.js) — три мигающие точки внутри пузыря помощника */
.zc-consultant__dots{ display: inline-flex; gap: 3px; margin-left: 6px; vertical-align: middle; }
.zc-consultant__dots span{
  width: 5px; height: 5px; border-radius: 999px;
  background: var(--zc-ink-faint);
  animation: zc-consultant-blink 1.2s infinite ease-in-out;
}
.zc-consultant__dots span:nth-child(2){ animation-delay: .2s; }
.zc-consultant__dots span:nth-child(3){ animation-delay: .4s; }
@keyframes zc-consultant-blink{ 0%, 80%, 100%{ opacity: .25; } 40%{ opacity: 1; } }
@media (prefers-reduced-motion: reduce){
  .zc-consultant__dots span{ animation: none; opacity: .6; }
}

/* --- поле ввода, закреплено внизу шторки ----------------------------------
   `textarea`, а не `input`: Shift+Enter — новая строка (обычный `input` её
   не умеет). Общее правило `textarea{width:100%}` (раздел FORMS выше)
   переопределяется намеренно: внутри flex-строки со кнопкой рядом `width:
   100%` разъехался бы за пределы контейнера (ширина считается ДО раздела
   места между flex-элементами) — нужны и `width:auto`, и `min-width:0`. */
.zc-consultant__form{
  flex: 0 0 auto;
  border-top: 1px solid var(--zc-line-soft);
  padding: var(--zc-gap);
  padding-bottom: calc(var(--zc-gap) + var(--zc-safe-bottom));
  display: flex; gap: 8px; align-items: flex-end;
}
.zc-consultant__input{
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
  resize: none;
  min-height: 44px; max-height: 120px;
}
.zc-consultant__send{ flex: 0 0 auto; }

/* ⚠ ДЕФЕКТ 3 (обход 12.08.2026, `shot_pending_t10.png`): системное правило
   `.zc-btn[disabled]` (раздел BUTTONS выше, ~строка 1674) гасит заливку до
   прозрачной — верно для кнопок-ССЫЛОК В СПИСКАХ/КАРТОЧКАХ (`ui.btn(kind=
   'muted')`, документировано там же: «тот же текст капсом, но серый»), там
   рядом есть соседний контекст (подпись поля, строка таблицы), кнопка не
   единственный ориентир. Здесь — не тот случай: «Спросить» стоит ВПЛОТНУЮ
   к текстовому полю одна, без соседей, и потеряв заливку на время ответа
   (15-23с, см. consultant.js) выглядит НЕ приглушённой кнопкой, а обычным
   текстом/подписью — юикс-требования §5.12 «значок/кнопка не единственный
   носитель действия» тут работает в обратную сторону: пропал последний
   визуальный признак «это кнопка».

   Правим ТОЛЬКО эту кнопку точечным селектором (три класса — специфичность
   выше `.zc-btn[disabled]`, порядок в файле тоже в её пользу, но не
   полагаемся на один порядок). Системное правило `.zc-btn[disabled]` для
   ОСТАЛЬНОЙ системы не трогаем — вне периметра этой задачи, и оно размечено
   в `_ui.html` намеренно.

   Приглушённая ЗАЛИВКА вместо акцентной, а не текст: тот же силуэт пилюли
   (форма/радиус/паддинг не переопределены), серый фон (`--zc-surface-3`,
   тот же токен, что у нейтральной цифры вкладки `.zc-tabs__num`) — «кнопка,
   сейчас недоступна», а не мёртвая ссылка. Текст на приглушённом фоне ниже
   4.5:1 — для НЕактивных элементов управления это не требование WCAG 1.4.3
   (текст неактивного компонента интерфейса в исключении), форма кнопки
   несёт смысл независимо от контраста текста. */
.zc-btn.zc-consultant__send[disabled]{
  background: var(--zc-fill-on-elev);
  color: var(--zc-ink-faint);
  border-color: transparent;
  cursor: default;
}

/* ============================================================================
   ПРИЁМКА — ОДНА СТРАНИЦА (13.08.2026, П-22, PRD «Приёмка от поставщика —
   одна страница вместо лестницы экранов»). Разметка: templates/doc_new.html,
   макрос `receipt_line_row` + ветка `{% if not doc and op_code ==
   'ПРИЁМКА_ОТ_ПОСТАВЩИКА' %}`; поведение: static/receipt_form.js.

   Таблица позиций несёт класс `.zc-table` и `data-th` у каждой ячейки — на
   S/M она автоматически становится карточками ПО ОБЩЕМУ правилу раздела
   «TABLE (карточки по умолчанию)» выше (умолчание закрыто для ЛЮБОЙ
   таблицы, дополнительных медиа-запросов на переключение в карточный режим
   здесь заводить не нужно). Ниже — ТОЛЬКО то, чего в общем правиле таблиц
   нет: подсказка артикула, панель «завести товар», вид неактивного поля
   коробов и компактная ширина числовых колонок на L.
   ========================================================================= */

/* ФОРМА-КАРТОЧКА С ТАБЛИЦЕЙ ВНУТРИ — СНИМАЕТ ОБЩИЙ ПРЕДЕЛ ШИРИНЫ
   `form.zc-card` (раздел «КАРТОЧКА-ФОРМА НЕ РАСПОЛЗАЕТСЯ» выше, 640+2*gap).
   НАЙДЕНО ЖИВЫМ БРАУЗЕРОМ, 1440px, не чтением кода: та колонка сшита под
   узкую форму полей (дата/склад/поставщик) — она и задумана узкой ровно там,
   где это единственный столбец полей. Но в этой форме внутри той же
   карточки ещё и таблица позиций (7 колонок), и она утыкалась в правый край
   640-пиксельной карточки на всю ширину монитора — видно было 4 колонки из
   7, дальше горизонтальный скролл ВНУТРИ `.zc-table-wrap`, о существовании
   которого на экране ничто не намекало.
   Поля шапки при этом не расползаются сами — у каждого свой предел ширины
   ПО ТИПУ ПОЛЯ (`--zc-fw-date`/`--zc-fw-code`/…, раздел «ШИРИНА ПОЛЯ ПО
   СОДЕРЖИМОМУ»), он стоит на самом `<input>`, а не на карточке, и работает
   независимо от того, чем ограничена сама карточка. Общий предел ослаблен
   ТОЛЬКО у ЭТОЙ формы — класс, не переопределение `form.zc-card` целиком:
   у обычных однополосных форм системы (товар, пользователь, контрагент)
   узкая карточка остаётся тем самым «не расползается», ради чего правило
   и заводили 07.08.2026. Верхняя граница всё равно есть — общий контейнер
   страницы (`max-width: 1200px`, раздел LAYOUT), просто он шире 640px. */
@media (min-width: 768px){
  form.zc-card--data-form{ max-width: none; }
}

/* Контейнер поля артикула — точка привязки для панели «завести товар»,
   которая встаёт ПОД полем, а не рядом (на S/M рядом ей и так некуда: поле
   уже на всю ширину карточки). */
.zcd-ln-sku-wrap{ position: relative; }

/* ============================================================================
   СПИСОК ПОДСКАЗОК ПО АРТИКУЛУ (16.08.2026, жалоба владельца по снимку:
   «ужасно выглядит дроп лист»).

   ПОЧЕМУ ЗДЕСЬ ВООБЩЕ ПОЯВИЛСЯ CSS. До этой правки подсказки рисовал
   БРАУЗЕР — поле несло `list="zcd-sku-suggest"` и `<datalist>`. Такой список
   не стилизуется ничем: ни шрифт, ни цвет, ни отступы, ни высота строки, ни
   подсветка совпадения разметке не отдаются, а при тёмной теме СИСТЕМЫ он
   выходит чёрным блоком крупным моноширинным шрифтом посреди светлого
   интерфейса. Правило Д-4 («один способ на задачу») здесь не нарушено:
   второго списка подсказок в системе не заводится — нативный убран, свой
   пришёл на его место. Соседние поля-справочники (`pickcreate`: контрагент,
   склад, поставщик) `<datalist>` сохраняют осознанно: там десятки коротких
   имён без второй строки и без кратности, и оформлять их нечем и незачем.

   РАЗМЕТКА — `templates/doc_new.html` (`.zcd-sku-pop`, один блок на всю
   таблицу), поведение — `static/receipt_form.js` (раздел «СВОЙ ВЫПАДАЮЩИЙ
   СПИСОК»). Координаты и ширину ставит скрипт инлайном по прямоугольнику
   поля; здесь — только вид.
   ========================================================================= */

/* `position: fixed` — потому что узел живёт в конце `<body>`, вне таблицы с
   `overflow-x: auto` и вне карточки формы: любой предок со скроллом обрезал
   бы список. z-index 55 выбран по существующей лестнице файла, а не «побольше
   на всякий случай»: выше нижней панели (30) и кнопки консультанта (50),
   НИЖЕ шторки консультанта (60) — шторка это модальное окно, и всплывающий
   список формы не имеет права оказаться поверх него. */
.zcd-sku-pop{
  position: fixed;
  z-index: 55;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--zc-elev-4);   /* слой 4, 26.08.2026 — см. `.zc-modal` */
  border: 1px solid var(--zc-line-strong);
  border-radius: var(--zc-radius);
  box-shadow: var(--zc-shadow-3);
  overflow: hidden;
}
/* `display:flex` выше побеждает `hidden` — возвращаем атрибуту его силу
   явно, иначе список висел бы на экране всегда. */
.zcd-sku-pop[hidden]{ display: none; }
/* Появление — ТОЛЬКО прозрачностью и только там, где движение не запрещено
   системной настройкой. Прозрачность не двигает раскладку: список не
   «выезжает» и не сдвигает ничего под собой. */
@media (prefers-reduced-motion: no-preference){
  .zcd-sku-pop{ animation: zcd-sku-pop-in 90ms ease-out; }
}
@keyframes zcd-sku-pop-in{ from{ opacity: 0; } to{ opacity: 1; } }

/* Прокрутка живёт ВНУТРИ списка (высоту ставит скрипт), а `overscroll-behavior`
   не даёт докрутке списка утащить за собой страницу — на телефоне это самый
   частый способ «случайно уехать вверх» посреди выбора. */
.zcd-sku-pop__list{
  overflow-y: auto;
  overscroll-behavior: contain;
  min-height: 0;
}

/* СТРОКА ПОДСКАЗКИ. 44px — не «примерно палец», а нижняя граница тач-таргета
   (Д-29); при двух строках текста высота и так больше, но у товара без
   названия строка одна, и без `min-height` она вышла бы 40px. */
.zcd-sku-opt{
  display: block;
  padding: 10px 12px;
  min-height: 44px;
  border-left: 3px solid transparent;
  border-bottom: 1px solid var(--zc-line-soft);
  cursor: pointer;
  /* убирает задержку 300ms на тапе и запрещает двойной тап-зум по строке */
  touch-action: manipulation;
}
.zcd-sku-opt:last-child{ border-bottom: 0; }

/* НАВЕДЕНИЕ — ТОЛЬКО ТАМ, ГДЕ УКАЗАТЕЛЬ ЕСТЬ. На телефоне `:hover`
   «залипает» после тапа: строка остаётся подсвеченной, хотя палец уже убран,
   и следующий выбор выглядит сделанным заранее. */
@media (hover: hover){
  .zcd-sku-opt:hover{ background: var(--zc-surface-2); }
}
/* Ответ на само нажатие — пальцем видно, что попал. */
.zcd-sku-opt:active{ background: var(--zc-accent-soft); }

/* ПОДСВЕЧЕННАЯ КЛАВИАТУРОЙ СТРОКА. Признаков ТРИ, и ни один не цвет-в-одиночку
   (Д-6): заливка, сплошная полоса слева и более жирное начертание названия.
   Плюс рамка 2px — это и есть видимый фокус: сам фокус остаётся в поле ввода
   (шаблон `aria-activedescendant`), поэтому «кольцо» обязано быть нарисовано
   здесь, иначе при обходе стрелками на экране не видно ничего. */
.zcd-sku-opt.is-active{
  background: var(--zc-accent-soft);
  border-left-color: var(--zc-accent);
  outline: 2px solid var(--zc-focus);
  outline-offset: -2px;
}
.zcd-sku-opt.is-active .zcd-sku-opt__code{ font-weight: 600; }

/* ОБЕ СТРОКИ ПОДСКАЗКИ — ОДНОГО КЕГЛЯ (`--zc-t-body-size`), И ЭТО И ЕСТЬ
   ПРАВКА ЖАЛОБЫ. Нативный список печатал `value` (артикул) крупно, а `label`
   (название) — мелким вторым рядом: название на снимке владельца буквально
   терялось. Здесь строки различают ШРИФТ и НАЧЕРТАНИЕ, а не размер:
   артикул — моноширинный (юикс-требования §4.4: артикулы, номера, коды),
   название — обычный. Мельче основного кегля в строке только плашка
   кратности: она метаданные о товаре, а не его имя. */

/* ПЕРВАЯ СТРОКА: артикул слева, кратность короба справа.

   `flex-wrap: nowrap` — НАЙДЕНО СНИМКОМ, а не чтением. С переносом плашка
   кратности уезжала на СВОЮ строку между артикулом и названием (видно на
   `shevee_регистратор_дбс1` и `shevee_самокат_город_2026`): решение о
   переносе строки flex принимает по ЖЕЛАЕМОЙ ширине детей, до сжатия, а
   желаемая ширина длинного артикула больше списка. Без переноса тот же
   артикул сжимается и переносится ВНУТРИ себя (`overflow-wrap: anywhere`
   ниже), а плашка остаётся справа на первой строке — там, где и задумана. */
.zcd-sku-opt__head{
  display: flex;
  flex-wrap: nowrap;
  align-items: baseline;
  gap: 8px;
}
/* НА ТЕЛЕФОНЕ ПЛАШКА ВСЁ-ТАКИ ПЕРЕНОСИТСЯ, И ЭТО НЕ ОТКАТ ПРАВКИ ВЫШЕ.
   Найдено снимком 390px: ширины на артикул + плашку в один ряд там нет, и
   запрет переноса ломал уже сам АРТИКУЛ — `shevee_регистратор_дбс1`
   разрывался на «…дбс» и одинокую «1» следующей строкой. Из двух зол выбрано
   меньшее: целый артикул одной строкой, плашка кратности под ним. Граница
   768px — та же, что отделяет телефон от планшета во всём файле. */
@media (max-width: 767px){
  .zcd-sku-opt__head{ flex-wrap: wrap; gap: 2px 8px; }
}
/* `min-inline-size: 0` обязателен: без него flex-ребёнок не сжимается, и
   длинный артикул выталкивает плашку кратности за край списка.
   `overflow-wrap: anywhere` — переносит, а не растягивает (Д-31/Д-34). */
.zcd-sku-opt__code{
  flex: 1 1 auto;
  min-inline-size: 0;
  font-size: var(--zc-t-body-size);
  line-height: var(--zc-t-body-lh);
  font-weight: 500;
  color: var(--zc-ink);
  overflow-wrap: anywhere;
}
/* ВТОРАЯ СТРОКА: название. Того же кегля и того же цвета, что артикул, —
   приглушённого «второго ряда» здесь нет и быть не должно. */
.zcd-sku-opt__name{
  display: block;
  margin-top: 2px;
  font-size: var(--zc-t-body-size);
  line-height: var(--zc-t-body-lh);
  color: var(--zc-ink);
  overflow-wrap: anywhere;
}
/* КРАТНОСТЬ КОРОБА — ПЛАШКОЙ. Она отвечает на вопрос «этот товар или
   похожий», поэтому стоит в самой подсказке, а не «потом в таблице». Фон
   `--zc-fill-on-elev`, не цветной: это факт о товаре, а не состояние и не
   тревога. Токен именно «заливка на поднятом слое» — подсказка артикула
   лежит на `--zc-elev-3`, и общий `--zc-surface-3` слился бы с ней в темноте
   (см. объяснение у самого токена). */
.zcd-sku-opt__box{
  flex: 0 0 auto;
  padding: 1px 8px;
  border-radius: var(--zc-radius);
  background: var(--zc-fill-on-elev);
  color: var(--zc-ink-muted);
  font-size: var(--zc-t-caption-size);
  line-height: var(--zc-t-caption-lh);
  white-space: nowrap;
}

/* СОВПАВШИЙ КУСОК ЗАПРОСА. Носителей два — заливка И начертание: на
   подсвеченной строке фон уже акцентный, и подсветка одним лишь цветом на ней
   растворилась бы (Д-6). Жёлтый здесь не «предупреждение», а общепринятое
   выделение результата поиска, и другого жёлтого в этом списке нет. */
.zcd-sku-hit{
  background: var(--zc-warning-bg);
  color: inherit;
  font-weight: 600;
  border-radius: 3px;
  padding: 0 1px;
}

/* ДВЕ СЛУЖЕБНЫЕ СТРОКИ СПИСКА: «идёт поиск» сверху и отсечка длинного списка
   снизу. Обе — подписи, а не варианты выбора, поэтому они лежат ВНЕ
   `role="listbox"` (иначе скринридер считал бы их пунктами) и набраны мелким
   кеглем на другой поверхности. */
.zcd-sku-pop__status,
.zcd-sku-pop__foot{
  flex: 0 0 auto;
  margin: 0;
  padding: 8px 12px;
  background: var(--zc-surface-2);
  color: var(--zc-ink-muted);
  font-size: var(--zc-t-caption-size);
  line-height: var(--zc-t-caption-lh);
}
.zcd-sku-pop__status{ border-bottom: 1px solid var(--zc-line-soft); }
.zcd-sku-pop__foot{ border-top: 1px solid var(--zc-line-soft); }
.zcd-sku-pop__status[hidden],
.zcd-sku-pop__foot[hidden]{ display: none; }

/* ПАНЕЛЬ «ТАКОГО АРТИКУЛА НЕТ — ЗАВЕСТИ». Тот же приём, что у `pickcreate`
   выше по системе (создание контрагента/склада прямо из формы документа):
   явная рамка тревожного, но не ошибочного цвета — товара ещё нет, это не
   отказ, а предложение действия. `zc-surface-2`, а не `zc-warning-bg`:
   отсутствие в номенклатуре — обычный, ожидаемый случай при вводе новой
   поставки, а не что-то, что требует тревожного цвета (он занят у системы
   плашкой «накладная обязательна» строкой выше в этой же форме). */
.zcd-ln-create{
  margin-top: 8px;
  padding: 8px 10px;
  border: 1px solid var(--zc-line);
  border-radius: var(--zc-radius);
  background: var(--zc-surface-2);
}
.zcd-ln-create-row{ display: flex; gap: 8px; flex-wrap: wrap; align-items: flex-end; }
.zcd-ln-create-row input{ flex: 1 1 160px; }
.zcd-ln-create-row .zc-btn{ flex: 0 0 auto; }
.zcd-ln-create-err{ color: var(--zc-danger); }

/* ПРАВИЛА `.zcd-ln-boxes[readonly]` ЗДЕСЬ БОЛЬШЕ НЕТ (16.08.2026, новая модель
   владельца). Оно красило поле «Коробов» неактивным у товара без кратности в
   карточке — то есть оформляло собой тот самый тупик, который владелец
   потребовал убрать: «не могу ни кратность указать, ни количество коробов,
   только количество штук». Поле открыто всегда и ничем от соседних не
   отличается, поэтому и красить нечего; `readonly` на нём не ставит теперь ни
   разметка, ни скрипт (разбор — `receipt_line_row` в doc_new.html и раздел
   «КРАТНОСТЬ КОРОБА» в static/receipt_form.js).

   ОТКУДА КРАТНОСТЬ ВЗЯЛАСЬ В КОЛОНКЕ. Она считается по строке (штуки ÷
   короба, округление до целого) и печатается рядом с пометкой «из карточки»
   в единственном случае — когда считать ещё не из чего и на экране стоит
   ОЖИДАНИЕ из номенклатуры, а не факт приёмки. Пометка обязана быть тише
   самого числа, но оставаться читаемой: `--zc-ink-muted` (#5B6472 на белом —
   контраст 6.6:1, выше требуемых 4.5:1), а не `--zc-ink-faint`. */
/* `display` У СКРЫТОГО ЭЛЕМЕНТА — ЛОВУШКА, ПОЙМАННАЯ СНИМКОМ (16.08.2026).
   Атрибут `hidden` работает правилом браузера `[hidden]{display:none}`, а у
   него специфичность ниже любого правила по КЛАССУ. Пометка «из карточки»
   поэтому висела в ячейке ВСЕГДА — и рядом с посчитанной кратностью («167 из
   карточки»), и в пустых строках («— из карточки»), то есть врала ровно там,
   где заведена ради честности. Поймано просмотром скриншота 1440px, не
   чтением кода: разметка и скрипт `hidden` ставили верно. */
.zcd-ln-mult-src[hidden]{ display: none; }
.zcd-ln-mult-src{
  display: inline-block;
  margin-left: 6px;
  font-family: var(--zc-font-ui);
  font-size: var(--zc-t-caption-size);
  line-height: var(--zc-t-caption-lh);
  color: var(--zc-ink-muted);
  white-space: nowrap;
}
/* На телефоне ячейка становится строкой карточки и печатает свою подпись из
   `data-th` — пометка уезжает во вторую строку и там висит одна. Прижимаем её
   к числу тем же приёмом, что и всё в карточном режиме: без переноса. */
@media (max-width: 1199px){
  .zcd-ln-mult-src{ margin-left: 4px; }
}

/* КОМПАКТНЫЕ ЧИСЛОВЫЕ КОЛОНКИ — ТОЛЬКО НА L, ТОЛЬКО ЭТА ТАБЛИЦА. Общее
   правило ширины поля по содержимому (раздел «ШИРИНА ПОЛЯ ПО СОДЕРЖИМОМУ»
   выше) само не применяется здесь: оно бьёт по `.zc-field > input`, а поля
   строки лежат прямо в `<td>`, без обёртки `.zc-field` (обёртка на каждую
   ячейку добавила бы вложенность ради вложенности — таблица уже даёт и
   подпись колонки, и структуру). Три узких поля счётных величин получают
   те же 96px, что у `--zc-fw-qty`, цену — чуть шире под дробное число с
   разделителем. На S/M ничего не сужается: `<td>` там уже блочная карточка
   на всю ширину, и ужатое поле означало бы палец мимо цели. */
@media (min-width: 1200px){
  /* ШИРИНЫ ПОДНЯТЫ 15.08.2026 (требование владельца: «не влезают цены
     нормально», замер живым браузером на строке 5000 × 1 234 567,89).
     Было 96px у счётных и 140/100px у цены — и при этом в поле «Штук»
     требовалось 65px против 63px доступных, а в цене 121px против 114px:
     обрезалось на два и на семь пикселей, то есть у последнего знака.
     Место под это взято не у соседей, а у колонки «Название», которую
     сервер теперь убирает, когда она дублирует артикул (разбор — в
     `routes/doc_new.py::show_line_title`): ширина освободилась, и вот куда
     она ушла. `min-width` у счётных полей появился по той же причине, что
     он давно есть у цены, — потолок без пола сжимается под соседей. */
  #zcd-lines-table .zcd-ln-boxes,
  #zcd-lines-table .zcd-ln-qty{ max-width: 128px; min-width: 92px; }
  /* У ЦЕНЫ ЕСТЬ И НИЖНЯЯ ГРАНИЦА (14.08.2026, дизайн-критика). Стоял только
     потолок, и колонка сжималась под соседей: как только в строке появлялось
     длинное НАЗВАНИЕ товара, поле цены ужималось настолько, что обрезало и
     плейсхолдер, и уже введённое число («199,50» показывалось как «199,5(»).
     100px — ширина, в которую помещается и подсказка «₽ за шт.», и сумма с
     копейками; проверено замером в живом браузере на заполненной форме. */
  #zcd-lines-table .zcd-ln-cost{ max-width: 190px; min-width: 150px; }
  #zcd-lines-table .zcd-ln-sku{ min-width: 220px; }
}

/* `<TFOOT>` САМ НЕ СТАНОВИТСЯ КАРТОЧКОЙ — НАЙДЕНО ЖИВЫМ БРАУЗЕРОМ (13.08.2026,
   скриншот 390px этой же формы, не чтением кода). Общее правило карточного
   режима (раздел «TABLE» выше) переводит в `display:block` `table, tbody,
   tr, td` — но НЕ сам `tfoot`. UA-умолчание `tfoot` — `table-footer-group`,
   и внутри уже блочной `<table>` это рассогласование заставляет Chromium
   строить анонимную табличную обёртку вокруг подвала; на скриншоте она
   красится штриховкой вместо обычного фона — визуально выглядит как
   сломанная вёрстка итога документа.

   ПРАВКА СКОПИРОВАНА ТОЛЬКО НА ЭТУ ТАБЛИЦУ (`#zcd-lines-table`), не в общее
   правило: тот же дефект есть и у старой таблицы позиций документа, и у
   `doc_item.html` (оба уже носят `<tfoot>` и точно такой же локальный
   `extra_head`-оверрайд для `tfoot tr`/`tfoot th`, но не для самого
   `tfoot`), но обе вне периметра задачи «Приёмка — одна страница» — правка
   разметки чужого экрана без задачи на него. Найденное вынесено в отчёт
   отдельным пунктом, не исправлено молча (см. `rules/prd-first.md`). */
@media (max-width: 1199px){
  #zcd-lines-table tfoot{ display: block; }
}

/* ЗАВЕДОМО ПУСТАЯ ЯЧЕЙКА НЕ ЗАНИМАЕТ МЕСТО НА ТЕЛЕФОНЕ (14.08.2026,
   дизайн-критика по снимку 390px). В карточном режиме каждая `td` печатает
   свою подпись из `data-th` ОТДЕЛЬНОЙ СТРОКОЙ — поэтому «Название —»,
   «Кратность короба —» и «Сумма —» до выбора артикула съедали примерно треть
   высоты карточки позиции, не сообщая ничего (Д-51: система не говорит
   больше, чем знает). Признак ставит разметка (`receipt_line_row`) и снимает
   `static/receipt_form.js::setCell` вместе с подстановкой значения.

   ТОЛЬКО В КАРТОЧНОМ РЕЖИМЕ. На L это настоящая таблица: спрятанная ячейка
   сдвинула бы всю строку по колонкам, и прочерк там — законная запись
   «значения пока нет» на своём месте под своим заголовком. */
@media (max-width: 1199px){
  #zcd-lines-table tbody td.zcd-ln-empty{ display: none; }
}

/* ПОДПИСЬ ЖИВЁТ У СВОЕЙ КНОПКИ (14.08.2026, дизайн-критика). «Строк может
   быть сколько угодно» относится к «+ ещё артикул», но стояла в 43px под ней
   и в 6px над «Провести» — по расстоянию читалась как пояснение к проведению
   (Д-13: одно внятное «что делать»). Кнопка и её подпись собраны в общий
   блок, отбивка между ними снята, а весь воздух вынесен ПЕРЕД «Провести». */
/* ПОДПИСЬ ПРО ЧЕРНОВИК (16.08.2026, PRD «Закупка и приёмка», «Черновики»).
   Кнопки «Сохранить черновик» на форме нет по решению владельца — значит про
   само сохранение человек может узнать только отсюда, и строка обязана быть
   ЗАМЕТНОЙ, но не кричащей: обычный текст мелким кеглем, без плашки, без
   цвета тревоги. Плашка на этом экране означает событие (отказ, «товар поехал
   в путь»), а тут — постоянное свойство формы.

   ДВЕ ЧАСТИ В ОДНОЙ СТРОКЕ: слева — что происходит всегда, справа — состояние
   («Сохранён: RCP-…», «Сохранено 14:32»), которое дописывает браузер. На узком
   экране они стакаются сами (`flex-wrap`), а не режутся многоточием: обе
   половины — предложения, а не подписи.

   СОСТОЯНИЕ ПОЛУЖИРНОЕ, но БЕЗ зелёного: цвет успеха на строке, которая
   зажигается каждые несколько секунд, превратился бы в мигающую гирлянду.
   Цвет здесь и не носитель смысла (Д-6) — смысл несёт сам текст. */
.zcd-draft{
  display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline;
  margin: 0 0 var(--zc-gap);
  font-size: var(--zc-t-small-size); line-height: 1.45;
  color: var(--zc-ink-muted);
}
.zcd-draft__text{ flex: 0 1 auto; min-width: 0; }
.zcd-draft__state{ font-weight: 600; color: var(--zc-ink); }
/* Пустое состояние не занимает места и не читается вслух как пустой абзац. */
.zcd-draft__state:empty{ display: none; }
/* Блок удаления стоит в конце формы и отбивается от кнопки «Провести»:
   вплотную он читался бы как её продолжение. */
#zcd-draft-del-box{ margin-top: var(--zc-gap); }

/* `.zcd-draft-mark` СНЯТ 15.08.2026 вместе с разметкой, которую он оформлял
   (пометка «Черновик» + «завёл(а) …» внутри ячейки «Номер»). Черновик и его
   автор показываются теперь настоящими колонками «Статус» и «Кто добавил»,
   переставленными в начало таблицы, — разбор у `routes/docs.py::DOC_COLUMNS`.
   Правило удалено, а не оставлено «на всякий случай»: мёртвый селектор в
   таблице стилей читается как работающий и переживает следующую правку. */

.zcd-add-block{ margin: var(--zc-gap) 0 28px; }
.zcd-add-block .zcd-add-btn-line{ margin: 0; }
.zcd-add-block .zc-muted-note{ margin-top: 4px; }

/* ЗАГОЛОВКИ РАЗДЕЛОВ ФОРМЫ ОТБИТЫ ОДИНАКОВО (14.08.2026, дизайн-критика).
   У `.zc-h2` верхнего отступа нет вовсе (`margin: 0 0 8px`), и расстояние над
   заголовком получалось случайным — тем, что оставил сосед сверху: над
   «Накладной» стояло поле шапки и давало 38px, над «Позициями» — подпись
   мелким кеглем и давала 10px. Два заголовка одного уровня выглядели разными
   по значимости, а «Позиции» читались продолжением предыдущего абзаца.
   Отступ задаётся ЯВНО и только в этой форме.

   ИСКЛЮЧЕНИЯ ДЛЯ «ПЕРВОГО ЗАГОЛОВКА» ЗДЕСЬ НЕТ, И ЭТО ПРОВЕРЕНО, А НЕ
   предположено: первая редакция несла `.zc-h2:first-child{ margin-top: 0 }`
   «на случай, если заголовок окажется первым в карточке» — правило
   совпало с НУЛЁМ элементов (замер в живом браузере:
   `querySelectorAll(...).length === 0`),
   потому что первый ребёнок формы — скрытое поле `csrf_token`. Мёртвая строка,
   выглядящая осмысленной, дороже отсутствующей: следующий читатель поверит,
   что случай учтён. Понадобится — заводить вместе с разметкой, которая его
   порождает. */
.zc-card--data-form > .zc-h2{ margin-top: 28px; }

/* ============================================================================
   ПРИЁМКА НА СКЛАДЕ — ТА ЖЕ ОДНА СТРАНИЦА, ДРУГИЕ КОЛОНКИ (14.08.2026, PRD
   «Закупка и приёмка — два документа со сверкой»). Разметка:
   templates/doc_new.html, макрос `receipt_take_row`.

   Здесь только то, чего нет в общих правилах: отметка фулфилмента в ячейке,
   подпись расхождения под полем количества и ширина числовых колонок на L.
   Карточный режим на S/M, подписи `data-th`, тач-таргеты и размер шрифта
   полей приходят из общих разделов выше — второй раз их не переписываем.
   ========================================================================= */

/* ОТМЕТКА ФУЛФИЛМЕНТА. `.zc-check` уже даёт строку 44px и кликабельную
   подпись (раздел «ФЛАЖОК» выше) — здесь снимается только нижний отступ,
   который в ячейке таблицы читался бы как лишний воздух между строками. */
.zcd-ln-ffok{ margin: 0; }
/* На L колонка узкая: слово «Принял» рядом с квадратиком помещается, перенос
   посреди пары «квадратик + слово» выглядел бы разрывом одного органа. */
.zcd-ln-ffok > span{ white-space: nowrap; }

/* РАСХОЖДЕНИЕ ПОД ПОЛЕМ КОЛИЧЕСТВА. Печатается только когда СЧИТАЕТ СЕРВЕР
   (перерисовка после отправки) — при первом показе поле пусто и говорить
   нечего. Отступ маленький: подпись принадлежит полю над собой, а не строке. */
.zcd-ln-delta{ margin: 4px 0 0; }

/* ПРАВИЛА ШИРИНЫ ПОЛЕЙ ЗДЕСЬ НЕ ПОВТОРЯЮТСЯ. Поля коробов и штук у приёмки
   несут те же классы, что у закупки (`.zcd-ln-boxes`/`.zcd-ln-qty`), и уже
   получают свои 96px из блока «КОМПАКТНЫЕ ЧИСЛОВЫЕ КОЛОНКИ» выше — таблица
   там названа тем же `#zcd-lines-table`. Второй набор тех же чисел разошёлся
   бы с первым на первой же правке. */

/* ============================ ПОДСКАЗКА У ПОЛЯ (знак вопроса) === §12 === */
/*
   Требование владельца 15.08.2026: вместо абзаца текста под каждым полем —
   маленький знак вопроса рядом с подписью, раскрывающий объяснение.
   Разметка и разбор — макросы `hint_mark`/`hint_win`/`field_head` в _ui.html.

   ЧЕТЫРЕ ВЕЩИ, КОТОРЫЕ ЗДЕСЬ НЕЛЬЗЯ УПРОЩАТЬ:
   1. ЗОНА НАЖАТИЯ 44×44 (Д-29). Сам значок — кружок 20px, но КНОПКА ровно
      44×44: на телефоне палец толще курсора, и сьюта `test_zachet_web_mobile`
      меряет именно getBoundingClientRect() кнопки, а не её содержимого.
   2. ОТРИЦАТЕЛЬНЫЕ ПОЛЯ по вертикали — чтобы эти 44px не раздували строку
      подписи на 28px у КАЖДОГО поля формы (шесть полей — плюс экран
      прокрутки). Зона нажатия при этом остаётся полной: она лишь заходит на
      пустое место сверху и снизу от подписи.
   3. ФОКУС ВИДЕН. Общее правило `:focus-visible` в шапке файла даёт обводку
      снаружи; у кнопки с отрицательными полями она вылезала бы на соседей,
      поэтому здесь обводка рисуется по самому кружку.
   4. НА ТЕЛЕФОНЕ ОКНО — НИЖНИЙ ЛИСТ, а не карточка по центру: подсказка
      объясняет поле, и накрывать это поле собой она не должна. Снизу экрана
      она оставляет видимой ту часть формы, где человек и находится.
*/
.zc-field__head{
  display: flex; align-items: center; gap: 2px;
  margin-bottom: 4px; min-width: 0;
}
.zc-field__head > label{ margin-bottom: 0; }
.zc-hintmark{
  flex: 0 0 auto;
  width: 44px; height: 44px;
  margin: -10px 0 -10px -12px;      /* см. п.2 выше */
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; border: 0; background: none; cursor: pointer;
  color: var(--zc-ink-muted);
}
.zc-hintmark__glyph{
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border-radius: 50%;
  border: 1px solid var(--zc-line);
  font-size: 12px; font-weight: 700; line-height: 1;
}
.zc-hintmark:hover .zc-hintmark__glyph,
.zc-hintmark:focus-visible .zc-hintmark__glyph{
  color: var(--zc-accent); border-color: var(--zc-accent);
  background: var(--zc-accent-soft);
}
.zc-hintmark:focus-visible{ outline: none; }
.zc-hintmark:focus-visible .zc-hintmark__glyph{
  outline: 2px solid var(--zc-focus); outline-offset: 2px;
}
/* Окно подсказки: узкая карточка, а не полноразмерное модальное. */
/* Слой 4 (дизайн-бук §9.6): подсказка может лечь поверх модалки, поэтому
   ступень выше, чем у .zc-modal (elev-3). В светлой elev-4=#FFFFFF, равно
   прежнему фону — визуально не меняется. */
.zc-hintwin{
  width: min(420px, calc(100vw - 32px));
  /* СЛОЙ 5, А НЕ 4 (26.08.2026). Модалка уехала на `--zc-elev-4` (см. её
     правило), и подсказка, которая по построению ложится ПОВЕРХ модалки,
     обязана уехать на ступень выше — иначе два окна сравниваются по светлоте
     и «поверх» перестаёт читаться. В светлой `--zc-elev-5` = `#FFFFFF`, как и
     `--zc-elev-4`, — светлая подсказка не меняется.
     Акцент на этой ступени даёт 4.05 (провал 4.5), поэтому внутри подсказки
     он подтягивается до `--zc-accent-hover` — 4.76. */
  background: var(--zc-elev-5);
  box-shadow: var(--zc-shadow-4);
  --zc-accent: var(--zc-accent-on-elev);
}
.zc-hintwin__body{ font-size: var(--zc-t-body-size); }
.zc-hintwin__body p{ margin: 0 0 8px; }
.zc-hintwin__body p:last-child{ margin-bottom: 0; }
@media (max-width: 767px){
  .zc-hintwin{
    top: auto; bottom: 0; left: 0;
    translate: none;
    width: 100%; max-width: none;
    max-height: 62vh;
    border-radius: var(--zc-radius-lg) var(--zc-radius-lg) 0 0;
  }
}

/* ============================================================================
   СПИСОК ПОЛЕЙ-СПРАВОЧНИКОВ И ОШИБКИ ПРОВЕРКИ ДО ОТПРАВКИ (16.08.2026)

   Два требования владельца, оба дословные: «если нет совпадений, то в дропдауне
   будет предлагаться кнопка "Создать"» и «не давай нажимать кнопку, если какие
   поля не заполнены, не перезагружай страницу».

   Оформление списка НЕ ДУБЛИРУЕТ правила списка артикулов, а переиспользует их
   через общий набор селекторов там, где вид совпадает буквально (рамка, тень,
   позиционирование, прокрутка). Своё здесь — только то, чем поле-справочник
   отличается от номенклатуры: у него одна строка вместо двух и последняя
   строка списка — кнопка действия.
   ========================================================================= */

/* Корпус списка — тот же, что у артикулов (см. `.zcd-sku-pop` выше): та же
   лестница z-index, та же тень, то же `position: fixed` по прямоугольнику поля.
   Правило написано отдельным селектором, а не добавлено к `.zcd-sku-pop`
   запятой, чтобы правка одного списка не уезжала молча в другой. */
.zcd-pick-pop{
  position: fixed;
  z-index: 55;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--zc-elev-4);   /* слой 4, 26.08.2026 — см. `.zc-modal` */
  border: 1px solid var(--zc-line-strong);
  border-radius: var(--zc-radius);
  box-shadow: var(--zc-shadow-3);
  overflow: hidden;
}
.zcd-pick-pop[hidden]{ display: none; }
@media (prefers-reduced-motion: no-preference){
  .zcd-pick-pop{ animation: zcd-sku-pop-in 90ms ease-out; }
}
.zcd-pick-pop__list{
  overflow-y: auto;
  overscroll-behavior: contain;
  min-height: 0;
}
.zcd-pick-pop__status{
  flex: 0 0 auto;
  margin: 0;
  padding: 8px 12px;
  background: var(--zc-surface-2);
  color: var(--zc-ink-muted);
  font-size: var(--zc-t-caption-size);
  line-height: var(--zc-t-caption-lh);
  border-bottom: 1px solid var(--zc-line-soft);
}
.zcd-pick-pop__status[hidden]{ display: none; }

/* СТРОКА СПИСКА. 44px — нижняя граница тач-таргета (Д-29), и у справочника
   она работает всегда: строка здесь одна, без второго ряда. */
.zcd-pick-opt{
  display: block;
  padding: 11px 12px;
  min-height: 44px;
  border-left: 3px solid transparent;
  border-bottom: 1px solid var(--zc-line-soft);
  cursor: pointer;
  touch-action: manipulation;
}
.zcd-pick-opt:last-child{ border-bottom: 0; }
@media (hover: hover){
  .zcd-pick-opt:hover{ background: var(--zc-surface-2); }
}
.zcd-pick-opt:active{ background: var(--zc-accent-soft); }
/* Подсвеченная клавиатурой строка: три признака, ни один не цвет-в-одиночку
   (Д-6) — заливка, полоса слева и видимое кольцо фокуса. Сам фокус остаётся в
   поле ввода (`aria-activedescendant`), поэтому кольцо рисуется здесь. */
.zcd-pick-opt.is-active{
  background: var(--zc-accent-soft);
  border-left-color: var(--zc-accent);
  outline: 2px solid var(--zc-focus);
  outline-offset: -2px;
}
.zcd-pick-opt__name{
  display: block;
  font-size: var(--zc-t-body-size);
  line-height: var(--zc-t-body-lh);
  color: var(--zc-ink);
  overflow-wrap: anywhere;
}
/* Уточнение (вид контрагента, ИНН) — вторым рядом мелким кеглем: оно помогает
   различить тёзок, но именем не является. */
.zcd-pick-opt__hint{
  display: block;
  margin-top: 2px;
  color: var(--zc-ink-muted);
  font-size: var(--zc-t-caption-size);
  line-height: var(--zc-t-caption-lh);
  overflow-wrap: anywhere;
}

/* КНОПКА «СОЗДАТЬ» — ПОСЛЕДНЯЯ СТРОКА СПИСКА, А НЕ ОТДЕЛЬНАЯ ПАНЕЛЬ. Владелец
   попросил её именно «в дропдауне». Она заметно отличается от строк выбора
   (акцентная подложка и жирнее начертание): выбрать существующее и завести
   новое — разные по последствиям действия, и путать их нельзя. */
.zcd-pick-pop__create{
  flex: 0 0 auto;
  display: block;
  width: 100%;
  min-height: 44px;
  padding: 11px 12px;
  border: 0;
  border-top: 1px solid var(--zc-line-strong);
  background: var(--zc-accent-soft);
  color: var(--zc-ink);
  font: inherit;
  font-size: var(--zc-t-body-size);
  line-height: var(--zc-t-body-lh);
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  touch-action: manipulation;
}
.zcd-pick-pop__create[hidden]{ display: none; }
@media (hover: hover){
  .zcd-pick-pop__create:hover{ filter: brightness(.97); }
}
.zcd-pick-pop__create.is-active{
  outline: 2px solid var(--zc-focus);
  outline-offset: -2px;
}
/* Д-55: выключенное состояние — токеном, не opacity (гасит текст в фон на
   тёмной поверхности). */
.zcd-pick-pop__create:disabled{ color: var(--zc-ink-disabled); cursor: default; }

/* ============================================================================
   ОШИБКА У САМОГО ПОЛЯ (проверка до отправки). Сводка сверху отвечает «сколько
   всего и что», подпись здесь — «что не так вот с этим полем»; одно другого не
   заменяет. Носителей два, не один цвет: рамка поля И строка текста под ним.
   ========================================================================= */
.zcd-invalid{
  border-color: var(--zc-danger) !important;
  outline-color: var(--zc-danger);
}
.zcd-field-err{
  margin: 4px 0 0;
  color: var(--zc-danger);
  font-size: var(--zc-t-caption-size);
  line-height: var(--zc-t-caption-lh);
}
/* Список пунктов сводки — обычный маркированный, с отступом под маркер. */
.zcd-err-list{ margin: 4px 0 0; padding-left: 1.1em; }
.zcd-err-list a{ color: inherit; }

/* ============================================================================
   СПАРКЛАЙН ДАШБОРДА — ВЕС ЛИНИИ В ТЁМНОЙ ТЕМЕ (26.08.2026)
   ============================================================================
   Разметка спарклайна приходит с сервера (`routes/dashboard.py::sparkline`)
   инлайн-стилем: `stroke: var(--zc-accent); stroke-width: 2`. В светлой теме
   это плотная тёмно-синяя линия на белом; в тёмной — светло-лавандовая
   ниточка, которая тонет в карточке (снимок `05-dashboard-dark.png`). Вес
   даётся двумя средствами сразу: цветом (`--zc-spark-ink` #A8B9FF — 9.10 к
   карточке против 6.30 у акцента) и толщиной.

   ЦВЕТ — ПЕРЕОПРЕДЕЛЕНИЕМ ПЕРЕМЕННОЙ, А НЕ СВОЙСТВА: инлайн-стиль на элементе
   сильнее любого правила из файла, но `var(--zc-accent)` внутри него читает
   ЗНАЧЕНИЕ переменной в этой точке дерева — его и подменяем. Хардкода мимо
   токена тут нет (Д-26): подставляется другой токен.

   ТОЛЩИНА — единственное место, где нужен `!important`: `stroke-width: 2`
   стоит в том же инлайн-стиле литералом, и без него правило не побеждает.
   Светлое значение токена равно двойке, которую рисует сервер, — светлая тема
   не меняется ни на пиксель.

   Правила живут ЗДЕСЬ, а не в `templates/dashboard.html` (владельце класса
   `.zc-dash__spark`) вынужденно: сегодня шаблон правит другой агент, и два
   агента в один файл не заходят. Первым же заходом в dashboard.html эти пять
   строк переезжают туда. */
.zc-dash__spark{ --zc-accent: var(--zc-spark-ink); }
.zc-dash__spark svg *{ stroke-width: var(--zc-spark-width) !important; }
