/* kohle-ops — дизайн-система «Stripe-like» (задача 1.5, task-1.5-brief.md).
   Токены — дословно из брифа. Светлая тема — основная; тёмная не делается
   в Фазе 1. Мобильная вёрстка — mobile-first (нижний таб-бар, формы в один
   столбец, кнопки h=44px), десктоп — media (min-width: 900px). */

@font-face {
  font-family: "InterVariable";
  src: url("/static/fonts/InterVariable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root{
  /* итерация 4, аудит DS (P0): --ink-3 был #727F96 — на белом --surface
     это ≈3,9:1, ниже порога AA (4,5:1) для мелкого текста подписей
     (.kpi-label/.hint/.field .hint и т.д., которые все читаются этим
     токеном). #5E6B84 держит ≈4,9:1 при том же характере цвета (холодный
     серо-синий), просто темнее. */
  --bg:#F6F9FC; --surface:#FFFFFF; --ink:#0A2540; --ink-2:#425466; --ink-3:#5E6B84;
  --line:#E6EBF1; --accent:#635BFF; --accent-hover:#5851EA; --accent-soft:#F0EFFF;
  --ok:#0E6245; --ok-soft:#E5F6EE; --warn:#9A5B13; --warn-soft:#FEF4E6;
  --no:#DF1B41; --no-soft:#FDE8EC;
  --shadow-sm:0 1px 3px rgba(10,37,64,.08),0 1px 2px rgba(10,37,64,.04);
  --shadow-md:0 6px 16px rgba(10,37,64,.10);
  --radius:8px; --radius-lg:12px;
  /* расширение сверх буквы брифа: нейтральная пилюля для «неизвестного» статуса
     (Архив / статус не задан) — в брифе перечислены только 4 семантических
     цвета пилюль, для пятого (нейтрального) состояния нужен свой мягкий фон. */
  --neutral-soft:#EEF1F5;
  --tabbar-h:60px;
  /* A-14 (генеральная репетиция 18.08.2026): высота липкой мобильной шапки
     (.topbar) — 14px padding сверху + 26px знак бренда + 14px снизу + 1px
     границы = 55px. Токеном, а не литералом: то же число нужно
     scroll-padding-top ниже, и разъехаться они не должны. */
  --topbar-h:55px;
  --sidebar-w:220px;
  /* Task 5 (R1, аудит 13.08.2026): двухрежимная шкала контролов —
     телефон 44px (Global Constraint, палец), десктоп плотнее (мышь,
     курсор точнее). Значения ниже — мобильные/базовые (файл mobile-first,
     см. шапку); десктопный медиа-блок ниже по файлу переопределяет оба
     токена на 36px/38px.
     ВАЖНО (тот же урок, что у мобильного h1.page-title ниже): не писать
     здесь буквально полный текст правила ширины 900px, объединённый со
     знаком собаки и словом "media", — тесты design_redesign.py находят
     десктопный блок регэкспом по этой строке, и декой-совпадение в
     комментарии РАНЬШЕ настоящего блока обрежет разбор до пустой строки. */
  --control-h: 44px;
  --row-h: 44px;
}

*, *::before, *::after { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: "InterVariable", -apple-system, "Segoe UI", sans-serif;
  font-size: 15px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}

/* цифры — всегда табличные (выравнивание в KPI/таблицах) */
.num, .kpi-value, table td.num, .money {
  font-variant-numeric: tabular-nums;
}

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hover); text-decoration: underline; }

h1, h2, h3 { color: var(--ink); margin: 0 0 12px; }
h1 { font-size: 22px; font-weight: 600; }
h2 { font-size: 17px; font-weight: 600; }

/* R1 (аудит 13.08.2026): на телефоне название экрана уже показывает
   топ-бар (.topbar-title, из {% block topbar_title %}) — H1 контента
   (.page-title, из {% block content %}) дублирует тот же текст сразу под
   шапкой. Прячем H1 только на мобильной ширине; десктоп ниже по файлу
   (там .topbar скрыт) показывает H1 как обычно. Размещено ПОСЛЕ базовых
   правил h1 — тот же порядок каскада, что чинили в Task 2
   (I-13-регрессия у #toast-container): точечная правка позже по файлу не
   должна незаметно оказаться раньше того, что переопределяет.
   ВАЖНО: не писать здесь буквально строку "min-width: 900px" — тесты
   design_redesign.py находят десктопный @media-блок регэкспом по этому
   тексту, и первое совпадение (даже в комментарии) обрежет разбор. */
@media (max-width: 899px) {
  h1.page-title { display: none; }
}

/* ---------------------------------------------------------------------
   Каркас: сайдбар (десктоп) / нижний таб-бар (мобильный)
   --------------------------------------------------------------------- */

.app-shell {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* ⚠️ **Полоса УЕЗЖАЕТ вместе с содержимым, а не липнет сверху** (19.08.2026,
   генеральная репетиция, находка anzeige-browser A-14; решение владельца
   «починить»). Пока стояло `position: sticky`, её 55 px навсегда занимали
   верх экрана и ЛОВИЛИ НАЖАТИЯ: строка списка, уехавшая под полосу, на тап
   не отвечала — тот же на ощупь дефект, на который владелец уже жаловался
   словами «по некоторым кнопкам кликаю и ничего».

   Почему убрать липкость, а не сдвигать содержимое вниз. Сдвиг (`padding-top`
   у `.main`) чинит НАЧАЛЬНОЕ положение, но не саму ловушку: полоса всё равно
   висит поверх прокручиваемого списка. `pointer-events: none` тоже не годится
   — полоса непрозрачная, и нажатия проваливались бы на строки, которых не
   видно, что хуже молчания.

   Что теряется: пока листаешь длинный список, название раздела уходит с
   экрана. Это не потеря — НИЖНИЙ таб-бар остаётся на месте и подсвечивает
   текущий раздел (`.tabbar-item.active`), то есть ответ «где я» никуда не
   девается. Взамен телефон получает 55 px содержимого на каждом экране.

   Интерактивных элементов в полосе нет ни одного (заголовок и знак бренда),
   поэтому её уход ничего не уносит с собой. Замер после правки —
   `test_a14_die_kopfleiste_faengt_keine_tipps_mehr_ab`: настоящий браузер,
   393×852, прокрутка на 1500 px, `elementFromPoint` в верхней полосе экрана
   обязан возвращать содержимое, а не полосу. */
.topbar {
  position: static;
  z-index: 20;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  padding: 14px 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.topbar-title { font-size: 17px; font-weight: 600; }
/* Итерация 5 (15.08.2026): бренд в мобильном баре — знак + слово в строку.
   Знак 26px (бар низкий), gap 8px; height/width заданы в px, а не auto, —
   у картинки известны собственные 256x256, поэтому место под неё занято
   ещё до загрузки и бар не дёргается. */
.topbar-logo {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-weight: 700;
  color: var(--ink);
}
.topbar-logo img { width: 26px; height: 26px; display: block; }

/* Task 6 (R3, аудит 13.08.2026): SVG-спрайт (app/static/icons.svg) вместо
   эмодзи в навигации. .icon — общий базовый класс для <svg class="icon">:
   не сжимается в строке flex (sidebar-link/tabbar-item сами flex-контейнеры),
   выравнивание по базовой линии соседнего текста (у иконки, в отличие от
   символа эмодзи, нет собственных font-size/line-height, которые раньше
   давали вертикальное центрирование само по себе). Цвет — stroke="currentColor"
   на каждом <symbol> спрайта, поэтому активный пункт (.sidebar-link.active/
   .tabbar-item.active меняют свой color) перекрашивает иконку сам, без
   явного override здесь. */
.icon { flex: 0 0 auto; vertical-align: -3px; }

.main {
  flex: 1;
  padding: 16px 16px calc(var(--tabbar-h) + 24px);
  max-width: 1080px;
  width: 100%;
  margin: 0 auto;
}

/* A-14: липкая шапка (position: sticky, 55px) перехватывает нажатие по
   строке, которая оказалась под ней: document.elementFromPoint в центре
   такой строки возвращает <header class="topbar">, и палец попадает в
   шапку, а не в чекбокс «В маршрут» или ссылку клиента.
   Systematisch это лечится только там, где прокрутку делает БРАУЗЕР, —
   у переходов по якорю: `/avto?…#fahrt-12` после записи поездки и
   `/#warnungen` из тоста-сводки ставили целевую строку ровно под шапку.
   scroll-padding-top отодвигает конечную точку любой такой прокрутки на
   высоту шапки + 8px зазора, разом для ВСЕХ якорей проекта (нынешних и
   будущих) — отсюда и токен, а не scroll-margin-top на каждой цели.
   На десктопе шапки нет (медиа-блок ниже прячет .topbar) — там отступ
   снимается, иначе якорь уезжал бы вниз без причины. */
/* 19.08.2026 (A-14): полоса больше не липкая — объезжать якорю нечего, и
   прежние 55 px оставляли бы над целью пустую полосу. Отступ сохранён
   символическим: он спасает от прилипания заголовка вплотную к краю экрана
   и от чужих будущих липких элементов. Токен `--topbar-h` продолжает
   выражать высоту полосы (сторож требует его наличия), просто больше не
   участвует в этом расчёте. */
html { scroll-padding-top: 8px; }

/* Сайдбар — скрыт на мобильном, показывается от 900px */
.sidebar {
  display: none;
}

/* Таб-бар — нижний, только мобильный */
.tabbar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 30;
  display: flex;
  background: var(--surface);
  border-top: 1px solid var(--line);
  box-shadow: var(--shadow-md);
  height: var(--tabbar-h);
}
.tabbar-item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  font-size: 11px;
  color: var(--ink-3);
  /* Task 5 (R1): тач-цель, не масштабируется десктопной шкалой
     --control-h — на десктопе .tabbar целиком скрыт (см. медиа-блок
     ниже), но правило не должно НЕОЖИДАННО завязаться на токен. */
  min-height: 44px;
}
.tabbar-item.active { color: var(--accent); font-weight: 600; }
.tabbar-item:hover { text-decoration: none; color: var(--accent); }

/* A-11 (генеральная репетиция 18.08.2026): бейдж числа предупреждений на
   пункте «Сегодня». Счётчик существовал только в подвале сайдбара, а
   сайдбар на телефоне скрыт — работая с телефона на «Складе» или «Кассе»,
   владелец не знал, что предупреждения есть вообще.
   .tabbar-icon — позиционирующая обёртка вокруг иконки (у самого
   .tabbar-item position не задан, а задавать его значило бы трогать
   раскладку всех шести пунктов); бейдж висит на ней абсолютом и потому не
   меняет высоту пункта и не двигает подпись. */
.tabbar-icon { position: relative; display: inline-flex; }
.tabbar-badge {
  position: absolute; top: -4px; left: 12px;
  min-width: 15px; height: 15px; padding: 0 3px;
  border-radius: 8px; background: var(--warn); color: #fff;
  font-size: 10px; font-weight: 700; line-height: 15px; text-align: center;
}
/* F-12 (генеральная репетиция 18.08.2026): блокирующая ошибка красит значок
   в КРАСНЫЙ. Жёлтый значок означает «посмотри, когда дойдут руки», красный —
   «сейчас не пройдёт продажа». Раньше блокировка не показывалась на телефоне
   вовсе: счётчик считал только предупреждения. */
.tabbar-badge-block { background: var(--no); }

@media (min-width: 900px) {
  .app-shell { flex-direction: row; }
  .topbar { display: none; }
  .tabbar { display: none; }
  /* A-14: шапки на этой ширине нет — прятать под неё нечего. */
  html { scroll-padding-top: 0; }

  .sidebar {
    display: flex;
    flex-direction: column;
    width: var(--sidebar-w);
    min-width: var(--sidebar-w);
    background: var(--surface);
    border-right: 1px solid var(--line);
    padding: 20px 12px;
    position: sticky;
    top: 0;
    height: 100vh;
  }
  /* Итерация 5 (15.08.2026): вместо жирной подписи «Mono Flame» — сам
     локап. 77px — правка по показу владельцу: первая версия (116px) читалась
     тяжёлой плашкой в светлом сайдбаре, «меньше в полтора раза» (116/1,5).
     Ширина сайдбара 220px минус padding = 196px, так что запас есть с
     избытком; выравнивание — по левому краю пунктов навигации. font-weight/
     font-size/color оставлены: они отрисуют alt-текст, если картинка не
     отдалась. aspect-ratio держат атрибуты width/height у <img>.
     Знак в мобильном топбаре (26px) НЕ трогаем — он и так иконочного
     размера, полтора раза от него дали бы нечитаемое пятно. */
  .sidebar-logo {
    font-weight: 700;
    font-size: 16px;
    color: var(--ink);
    padding: 0 8px 20px;
  }
  .sidebar-logo img { width: 77px; height: auto; display: block; }
  .sidebar-nav { display: flex; flex-direction: column; gap: 2px; flex: 1; }
  /* Task 10 (аудит 13.08.2026): 9 пунктов сайдбара плоским списком без
     визуальной иерархии + ~400px пустоты до статус-блока. Три подписи-
     разделителя (base.html: «Операции»/«Учёт»/«Система», группировка по
     имени секции ПРЯМО В ШАБЛОНЕ — nav_sections/NAV_SECTIONS остаётся
     единственным источником пунктов, порядок не менялся). Первая группа —
     без верхнего отступа (:first-child), это же правило само по себе
     ничего не удлиняет — просто читаемая шкала внутри уже занятой высоты. */
  .sidebar-group {
    font-size: 10.5px;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--ink-3);
    padding: 14px 10px 4px;
  }
  .sidebar-group:first-child { padding-top: 0; }
  .sidebar-link {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 10px;
    border-radius: var(--radius);
    color: var(--ink-2);
    font-size: 14px;
    font-weight: 500;
  }
  .sidebar-link:hover { background: var(--bg); text-decoration: none; color: var(--ink); }
  .sidebar-link.active { background: var(--accent-soft); color: var(--accent); }

  .sidebar-status {
    border-top: 1px solid var(--line);
    padding-top: 12px;
    font-size: 12px;
    color: var(--ink-3);
  }
  .sidebar-status .row { display: flex; align-items: center; gap: 8px; padding: 4px 8px; }
  /* Ссылка на инструкцию (25.08.2026) — той же тихой типографики, что строки
     статуса над ней; отдельный класс, чтобы не считаться разделом. */
  .sidebar-help { display: flex; align-items: center; gap: 8px; padding: 6px 8px 10px; font-size: 12px; color: var(--ink-2); text-decoration: none; }
  .sidebar-help:hover { color: var(--ink); text-decoration: underline; }
  /* F-12: строка «Ошибок: N» — красная, как banner-error: у блокирующей
     ошибки и у предупреждения разный смысл, и цвет обязан их различать. */
  .sidebar-status .row-block { color: var(--no); font-weight: 600; }
  .dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
  .dot-ok { background: var(--ok); }
  .dot-no { background: var(--no); }

  .main { max-width: 1240px; margin: 0 auto; padding: 24px 32px; }

  /* -----------------------------------------------------------------
     Task 5 (R1, аудит 13.08.2026): десктопный слой токенов.
     Жалоба пользователя «всё крупное» — на ширине 900px+ (планшет/
     десктоп, курсор мыши точнее пальца) экран до сих пор нёс ту же
     мобильную шкалу (44px контролы, 15px базовый текст), что и телефон
     375px. Здесь плотность растёт: --control-h/--row-h переопределены
     на 36/38px, базовый текст и заголовки уменьшены (частичная
     перезапись свойств — остальные свойства базовых правил не тронуты).

     RULING (префлайт контроллера): `html { font-size: 14px }` в одиночку
     не подействовал бы на текст — `body` несёт СВОЙ font-size (15px) в
     пикселях, не в rem, и вся вёрстка файла — px, а не rem-цепочка от
     html. Переопределяем ОБА селектора напрямую.

     ПОЧЕМУ .btn/table.data-table td/.btn-block/.btn-giant НЕ ЗДЕСЬ: этот
     медиа-блок стоит РАНЬШЕ базовых `.btn { … }` (секция «Кнопки»),
     `table.data-table td { … }` (секция «Таблицы») и `.btn-block`/
     `.btn-giant` (та же секция «Кнопки») по файлу. При равной
     специфичности выигрывает ПОЗДНЕЕ правило в файле НЕЗАВИСИМО от
     медиа-условия — браузерная верификация (Chromium, 1440px) поймала
     это буквально: font-size/padding кнопки и ячейки оставались
     мобильными (15px/0 18px и 14px/8px 10px), а `.btn-giant` — 56px/17px
     (мобильные значения Task 4) вместо задуманных 44px/15px — тот же
     класс бага, что I-13/тосты (Task 2): при равной специфичности
     порядок файла решает, а не более узкое условие. Fix-раунд задачи 5
     (ruling контроллера отменил «оставь» Task 4 — оно и породило баг).
     :root/html,body/h1/h2 работают корректно — их базовые правила стоят
     РАНЬШЕ этого блока (см. верх файла и секцию заголовков), поэтому
     desktop-перезапись здесь их корректно перебивает. `.btn`/
     `table.data-table td`/`.btn-block`/`.btn-giant` вместо
     этого переопределены в собственных медиа-блоках сразу ПОСЛЕ своих
     базовых правил (секции «Кнопки»/«Таблицы» ниже по файлу) — тот же
     приём порядка, что уже используют мобильные override'ы (h1.page-
     title, #toast-container). */
  :root { --control-h: 36px; --row-h: 38px; }
  html, body { font-size: 14px; }
  h1 { font-size: 20px; }
  h2 { font-size: 15px; }
}

/* ---------------------------------------------------------------------
   Карточки / KPI
   --------------------------------------------------------------------- */

.card {
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: 18px;
  margin-bottom: 16px;
  /* M-7 (финальный фикс-раунд Фазы 4, часть C): длинное неразрывное слово
     (URL сайта клиента, токен в заметке — оба приходят из Google Sheet, не
     из формы с валидацией длины) иначе раздвигает `.card`/`.field` вбок:
     `overflow-wrap` — унаследованное свойство, действует на ВСЁ содержимое
     карточки (в т.ч. `.field` на kunde.html), а не только на прямых
     потомков. Таблицы (`.table-scroll`) уже скроллятся сами и это правило
     не задевает — здесь чинится ровно то место, где скроллера нет.
     Замер в Chromium на 390 px карточки клиента (app/templates/kunde.html,
     поля «Сайт»/«Заметки»): scrollWidth 399 (длинный URL) и 561
     (50-символьный токен в заметке) — оба ДО этой правки шире экрана.

     13.08.2026 (аудит R2): правило сужено до точечного утилити-класса —
     полный перенос слова на ВСЁЙ карточке рвал числа и кнопки в любой
     вложенной таблице (заголовки «ЦЕНА» -> «ЦЕ НА», числа «25.000» ->
     «25.00 0», даты, подписи кнопок «Открыть» -> «Откр ыть»), хотя нужен
     он был ровно двум полям kunde.html. Утилити-класс сразу под этим
     блоком переносит длинное неразрывное слово ТОЛЬКО там, где проставлен
     явно (см. kunde.html, поля «Сайт»/«Заметки»); больше нигде в проекте
     полный перенос слова не действует. */
}

/* R2 (аудит 13.08.2026): точечный перенос для неразрывных значений из
   Google Sheet (URL сайта, токен в заметке) — ровно те места, что чинил M-7. */
.wrap-anywhere { overflow-wrap: anywhere; }

/* ⚠️ `minmax(0, 1fr)`, а НЕ `1fr` (18.08.2026, генеральная репетиция,
   находка anzeige-browser A-01 — high, замерена в браузере). `1fr` это на
   самом деле `minmax(auto, 1fr)`: колонка не сжимается ниже min-content
   своего содержимого, а содержимое здесь — длинное число. На 320 px правая
   колонка выходила за край экрана на 47 px, на 375 px — на 16 px, у
   «Результата» срезало знак €. Порог, при котором вёрстка держалась, —
   391 px, то есть её рвал ЛЮБОЙ обычный телефон; с шестизначными суммами
   (контейнер на 425.000,00 €) порог уезжал ещё выше.
   `minmax(0, …)` снимает нижнюю границу — колонка честно делит ширину. */
.kpi-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
  margin-bottom: 16px;
}
@media (min-width: 700px) {
  .kpi-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  /* Task 9 (аудит 13.08.2026, шаг 3): «Финансы» несёт ШЕСТЬ kpi-card —
     базовый repeat(4, 1fr) выше рвёт их 4+2 (последняя пара повисает
     отдельным рядом, не занимая ширину). Отдельный модификатор-класс
     (специфичность 0,2,0 против 0,1,0 у голого .kpi-grid здесь же —
     побеждает независимо от порядка объявления внутри ОДНОГО медиа-блока)
     раскладывает ту же сетку 3×2. */
  .kpi-grid.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.kpi-card {
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: 14px 16px;
}
.kpi-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--ink-3);
  font-weight: 600;
  margin-bottom: 6px;
}
.kpi-value {
  font-size: 24px;
  font-weight: 600;
  color: var(--ink);
  line-height: 1.2;
}
.kpi-value.is-danger { color: var(--no); }
/* Task 9 (п. г отчёта аудита): три места (heute.html «Ёмкость: перебор»/
   «План и факт: ошибка в данных», finanzen.html «Статус: …») несли
   ОДИНАКОВЫЙ инлайн-стиль `style="font-size:16px;"` по отдельности —
   .kpi-value на этих карточках несёт СЛОВА (статус), а не число, и
   базовые 24px читаются слишком тяжело для целого предложения. Общий
   модификатор вместо трижды скопированного инлайн-стиля. */
.kpi-value.is-status { font-size: 16px; }
.kpi-delta {
  display: inline-block;
  margin-top: 6px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
}
.kpi-delta.ok { background: var(--ok-soft); color: var(--ok); }
.kpi-delta.no { background: var(--no-soft); color: var(--no); }
/* Task 9 (аудит 13.08.2026, шаг 3): «KPI-пилюли несут абзацы» — .kpi-delta
   без модификатора ok/no не несёт статуса, только методологическую сноску
   («брутто, с НДС — по дате продажи…», 60+ символов) — круглая пилюля
   (padding 2px 8px, border-radius 999px) под такой текст не рассчитана
   и либо распухает, либо рвётся. .kpi-note — плоская подпись под
   значением: ok/no пилюли (короткий статус — «Есть просрочка (K-09)»,
   «Пока не требуется») остаются пилюлями, как и были. */
.kpi-note { font-size: 12px; color: var(--ink-3); margin-top: 4px; }

/* ---------------------------------------------------------------------
   Таблицы
   --------------------------------------------------------------------- */

table.data-table { width: 100%; border-collapse: collapse; }
table.data-table thead th {
  text-align: left;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .03em;
  color: var(--ink-3);
  font-weight: 600;
  padding: 8px 10px;
  border-bottom: 1px solid var(--line);
  background: transparent;
  /* R2 (аудит 13.08.2026): заголовок «ЦЕНА» рвался посреди слова
     («ЦЕ НА») из-за унаследованного `overflow-wrap: anywhere` с `.card` —
     см. правку M-7 выше. Заголовки короткие и в переносе не нуждаются. */
  white-space: nowrap;
}
table.data-table tbody tr { height: var(--row-h); border-bottom: 1px solid var(--line); }
table.data-table tbody tr:hover { background: #F6F9FC; }
table.data-table td { padding: 8px 10px; font-size: 14px; color: var(--ink); }
/* Task 5 (R1, аудит 13.08.2026): десктопная перезапись — размещена СРАЗУ
   ПОСЛЕ базового правила (не в раннем @media (min-width: 900px) у сайдбара
   выше — см. объяснение там же): при равной специфичности выигрывает
   правило, которое стоит ПОЗЖЕ в файле, независимо от медиа-условия;
   браузерная верификация (Chromium, 1440px) поймала это буквально —
   ячейка оставалась 14px/8px 10px. */
@media (min-width: 900px) {
  table.data-table td { font-size: 13.5px; padding: 6px 10px; }
}
table.data-table td.num { text-align: right; }
table.data-table th.num { text-align: right; }
/* R2 (аудит 13.08.2026): числа («25.000» -> «25.00 0») и даты
   («09.08.2026» -> «09.08.202 6») не должны рваться посреди значения —
   тот же унаследованный `anywhere`, что и в заголовках выше. Ширину,
   если не влезает, забирает `.table-scroll` (overflow-x: auto). */
table.data-table td.num, table.data-table td.dt { white-space: nowrap; }

.table-scroll { overflow-x: auto; }

/* ---------------------------------------------------------------------
   Таблицы на телефоне (задача 3.1, task-3.1-brief.md) — изначально
   карточки-анкеты: thead/tbody/tr/td -> display:block, td::before читал
   data-label как подпись над каждым значением. Шапка не удаляется из DOM
   (скринридер её видит), только визуально скрыта — тот же паттерн, что
   .visually-hidden выше. Ширина «телефон» по брифу — до 640px.

   Task 7 (R6, аудит редизайна 13.08.2026): анкета label/value заменена
   двухстрочными записями. Причина замены — сама анкета: одна td = один
   визуальный блок с ::before-подписью НАД значением, то есть N колонок
   таблицы = N блоков подряд на телефоне. Замер аудита — продажа ≈600px,
   клиент ≈505px, весь список клиентов (/kunden) — 91 008 физ.px
   (= 45 504 CSS px при device-scale-factor 2) на одну ширину экрана.
   Приём: <tr> — flex-ряд; <td> — inline-элемент внутри него; порядок и
   перенос строк держит не разметка, а CSS `order`/`flex-basis` на пяти
   классах ячейки (контракт — .superpowers/sdd/2026-08-13-kohle-ops-
   redesign-p0p1/task-7-brief.md, «Interfaces»):
     .m-title — главная сущность строки (имя клиента, тип операции),
                жирным, растягивается по доступной ширине;
     .m-val   — сумма/кг/статус справа от title, тоже на первой строке;
     .m-sub   — второстепенное (дата, кг, способ оплаты, …): flex-basis
                100% заставляет КАЖДУЮ следующую .m-sub начинать новую
                строку (компактный стек, а не единственная "строка 2" —
                у записи с тремя .m-sub будет три подстроки, у записи с
                одной — одна; smallcaps-подпись над значением ушла, слово
                перед первым непонятно без неё, поэтому соседние .m-sub
                отделены символом «· » через ::before);
     .m-hide  — колонка не несёт нового на телефоне (всегда одно и то же
                значение вроде «без НДС», ключ не назначен, пустой
                комментарий) — скрыта ЦЕЛИКОМ (display:none), а не просто
                без подписи;
     .m-act   — кнопки/формы действий, отдельной строкой внизу записи.
   data-label АТРИБУТЫ ОСТАЮТСЯ на каждом td без исключения (скринридер +
   сторожа tests/test_phase3.py: test_p31_jede_td_hat_data_label,
   test_p31_data_label_stimmt_mit_th_ueberein) — меняется только то, КАК
   браузер их показывает: ::before с содержимым атрибута больше нигде не
   рисуется (content: none ниже, без узкого исключения для колонки
   действий — исключение было нужно только затем, что подпись существовала
   ПО УМОЛЧАНИЮ и её приходилось гасить точечно; теперь по умолчанию нет
   подписи вовсе).
   --------------------------------------------------------------------- */
@media (max-width: 640px) {
  table.data-table thead {
    position: absolute;
    width: 1px; height: 1px; margin: -1px;
    padding: 0; overflow: hidden; clip: rect(0 0 0 0);
    white-space: nowrap; border: 0;
  }
  table.data-table, table.data-table tbody { display: block; width: 100%; }
  table.data-table tbody tr {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 8px;
    height: auto; padding: 10px 12px; border-bottom: 1px solid var(--line);
    border-radius: 0; box-shadow: none; margin: 0;
  }
  table.data-table tbody tr:hover { background: var(--surface); }
  table.data-table td { display: inline; width: auto; padding: 0; border: none; font-size: 14px; }
  table.data-table td.num { text-align: left; }
  table.data-table td::before { content: none; }
  /* Fix round Task 7 (браузерная верификация, Chromium 390px): бриф даёт
     голые `td.m-title`/`td.m-val`/`td.m-sub`/`td.m-hide`/`td.m-act`
     (специфичность 0,1,1 — тип+класс), а правило прямо ДО них,
     `table.data-table td { … font-size: 14px; padding: 0; … }`, весит
     0,1,2 (тип+класс+тип). Тот же класс бага, что уже трижды пойман в
     этом файле на ПОРЯДКЕ объявления (I-13/тосты, .btn/.btn-giant,
     table.data-table td font-size десктопа) — здесь ловушка не в порядке
     (m-hide стоит ПОСЛЕ базового правила по файлу), а в СПЕЦИФИЧНОСТИ:
     при конфликте свойств более специфичное правило побеждает
     НЕЗАВИСИМО от места в файле. Итог без этой правки, подтверждено
     `getComputedStyle` в браузере: `td.m-hide` реально получал
     `display: block` (не `none` — «скрытая» пустая ячейка «—» видна
     каждой строкой), `td.m-sub` — `font-size: 14px` (не 12.5px, та же
     ловушка на `font-size`), `td.m-act` — `padding-top: 0` (не 6px,
     `padding: 0` базового правила бьёт `padding-top: 6px` как один
     лонгхенд против другого). Приём фикса — тот, что уже используют
     `table.data-table td.num`/`td.dt`/`td.is-fakt` в этом же файле:
     квалифицировать селектор префиксом `table.data-table td.`, подняв
     специфичность до 0,1,2 (табличная ничья при равной специфичности
     решалась бы порядком — здесь специфичность встаёт на один уровень
     С базовым правилом, а порядок объявления этих пяти классов ПОСЛЕ
     базового правила решает исход, как и было задумано).

     Fix round 2 (ruling контроллера после отчёта Task 7): буквальный
     приём брифа (`td.m-sub { flex: 1 1 100% }`) не даёт «строку 2» —
     он даёт СВОЮ строку КАЖДОМУ m-sub (flex-basis 100% монопольно
     занимает всю ширину линии, следующий элемент уже не помещается и
     переносится сам). У записи с тремя m-sub (heute «Последние
     операции»: дата/кг/оплата) это было title/val + 3 отдельные
     подстроки + строка действия = 5 строк, ≈154px вместо задуманных
     двух. Механизм переноса вынесен из td.m-sub в отдельный
     flex-break — псевдоэлемент `tbody tr::before` с `flex-basis: 100%`
     (тот же приём, что раньше нёс m-sub, но теперь СВОЙ, невидимый
     элемент строки, а не сама ячейка): он сам занимает всю строку и
     переносит следующий элемент (первый m-sub) на новую строку РОВНО
     ОДИН РАЗ. m-sub теперь `flex: 0 1 auto` (по содержимому, как
     обычный инлайн-текст) — несколько m-sub делят ОДНУ строку и сами
     заворачиваются, только если не влезли по ширине (естественный
     flex-wrap, не форсированный). Порядок: title(1), val(2),
     break(3), sub(4), act(5) — act c `margin-left: auto` прижимается
     вправо на той же строке 2, что и sub; не влезло — заворачивается
     само (flex-wrap с контейнера). */
  table.data-table td.m-title { font-weight: 600; order: 1; flex: 1 1 auto; }
  table.data-table td.m-val   { order: 2; font-weight: 600; white-space: nowrap; }
  /* Невидимый элемент строки (не td, не несёт data-label и не влияет на
     сторожей data-label) — его ЕДИНСТВЕННАЯ роль: flex-basis:100% сам
     занимает всю ширину линии и переносит ВСЁ, что идёт после него
     (order 4+: m-sub/m-act), на новую строку. height:0 — он не должен
     добавлять видимую высоту сам по себе (только gap контейнера вокруг
     него на счету). */
  table.data-table tbody tr::before { content: ""; flex-basis: 100%; order: 3; height: 0; }
  table.data-table td.m-sub   { order: 4; flex: 0 1 auto; color: var(--ink-3); font-size: 12.5px; }
  /* Сепаратор «· » — от ВТОРОГО ВИДИМОГО m-sub, а не от второго В DOM:
     `+` (соседний) не годится сам по себе — между первым m-sub (обычно
     «Дата») и вторым в разметке часто стоит НЕ-sub ячейка (m-title/
     m-val того же столбца), то есть они не соседи в DOM, хотя визуально
     обе оказываются на строке 2. Общий комбинатор `~` матчит «есть более
     ранний m-sub где угодно раньше», независимо от того, что между ними;
     `:not(.m-hide)` с ОБЕИХ сторон — иначе скрытый m-hide-сосед всё
     равно «считается» (соответствие классу в CSS не зависит от
     display:none) и либо навешивает «·» первому ВИДИМОМУ полю (когда
     скрыт единственный элемент ПЕРЕД ним), либо не навешивает его на
     самом деле второе видимое поле (когда между двумя видимыми лежит
     скрытый — тот всё равно совпадает с левой частью `~`, так что этот
     случай уже покрыт; сам риск был только в «первый видимый получает
     хвостовую точку», и :not(.m-hide) слева его снимает). Проверено
     браузером на /kunden (Город/Телефон/Цена/Уголь — все четыре
     условно-пустые). */
  table.data-table td.m-sub:not(.m-hide) ~ td.m-sub:not(.m-hide)::before { content: "· "; color: var(--ink-3); }
  table.data-table td.m-hide  { display: none; }
  table.data-table td.m-act   { order: 5; flex: 0 0 auto; margin-left: auto; }

  /* Кнопки/поля в строке действий у части таблиц (Склад, Касса, Финансы,
     Сегодня, Клиенты) несут .btn-compact/.date-compact — плотная
     ДЕСКТОП-строка (32px, попасть пальцем сложнее). На телефоне — те же
     44px, что у обычных .btn/полей, обычным каскадом (без !important —
     Fix round 1, ревью: класс вместо инлайн-стиля снял саму причину).

     I-12 (финальный фикс-раунд Фазы 3): селектор был привязан к
     `table.data-table td`, а ДВЕ кнопки гашения предупреждений на
     «Сегодня» — «Занести в кассу» (K-05) и «Внести в банк» (K-12) —
     живут в <ul><li>, не в таблице. Замер в Chromium: 116,6×32 и 106×32
     на 390 и 360 px. Это единственные два .btn-compact вне <td> в
     проекте, и именно ими закрывают предупреждения с телефона.
     Привязка теперь к области содержимого (.main), а не к таблице:
     специфичность (0,2,0)/(0,3,1) перебивает базовые 32px независимо от
     порядка правил в файле — плоский `.btn-compact` (0,1,0) проиграл бы
     объявлению ниже по файлу.

     Task 5 (R1): тач-цель на телефоне (десктопная шкала --control-h там
     не в силе — это override ВНУТРИ мобильного @media max-640), поэтому
     44px остаётся литералом, не var(--control-h). */
  .main .btn-compact,
  .main input[type="date"].date-compact {
    height: 44px;
  }
  /* I-13 мобильный override #toast-container ПЕРЕЕХАЛ отсюда ниже, после
     базовых правил #toast-container/.toast (~строка 550+) — см. блок
     "R4 (аудит редизайна 13.08.2026)" там же: он стоял здесь, т.е. РАНЬШЕ
     базового правила в файле, и при равной специфичности проигрывал ему
     в каскаде (computed top:16px на телефоне вместо top:auto). */
}

/* ---------------------------------------------------------------------
   Задача 3.3 (План и факт) — факт-колонка таблицы «План и факт» выделена
   (бриф, шаг 4: "факт выделен"), тот же токен акцента, что .badge-accent.
   --------------------------------------------------------------------- */

table.data-table td.is-fakt { font-weight: 700; color: var(--accent); }

/* Fix round 1 (ревью 3.3, "Взгляд вперёд") — строки предпросмотра будущих
   месяцев в таблице «План и факт» показаны приглушённо: там только план,
   факта не может быть в принципе (app/szenarien.py: ist_zukunft). */
tr.row-zukunft { opacity: .55; }

/* ---------------------------------------------------------------------
   I-6 (финальный фикс-раунд Фазы 3) — км отменённой поездки на /avto.
   Пилюля «Отменена» стояла, а цифра выглядела как живая: строки за
   август читались как 100 + 30 + 120 = 250 км при 130 в месячном отчёте
   на том же экране. По этой строке заполняют деловые км Anlage EKS
   (§ 3 Abs. 7 GrusiGV) — цифра обязана быть видимо погашена.
   --------------------------------------------------------------------- */
.km-storniert { color: var(--ink-3); text-decoration: line-through; }
/* Задача 4.6 (анализ п.25): та же подпись для кг пары «запись + её отмена»
   на «Сегодня»/карточке клиента — перенесено с «Авто» (см. .km-storniert
   выше). */
.kg-storniert { color: var(--ink-3); text-decoration: line-through; }

/* M-1 (финальный фикс-раунд Фазы 3) — две карточки-ссылки «Ёмкость»/«План
   и факт» на «Сегодня» (.kpi-emkost-card/.kpi-planfakt-card) СНЯТЫ
   итерацией 3 точечного прохода редизайна (мокап пользователя 14.08.2026):
   heute.html больше не несёт этих классов — их заменили duo-карты
   «Запасы»/«План / Факт» (см. секцию «Итерация 3» в конце файла), полная
   картина по обеим темам остаётся без изменений на /sklad и /finanzen.
   Правило удалено вместе с классами (тест test_t9_regressions_
   bestehender_kpi_wert_sichtbarkeit, требовавший его непустым, — тоже
   снят, обоснование там же). */

/* ---------------------------------------------------------------------
   Пилюли статусов
   --------------------------------------------------------------------- */

.badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}
.badge-ok { background: var(--ok-soft); color: var(--ok); }
.badge-accent { background: var(--accent-soft); color: var(--accent); }
.badge-no { background: var(--no-soft); color: var(--no); }
.badge-warn { background: var(--warn-soft); color: var(--warn); }
.badge-neutral { background: var(--neutral-soft); color: var(--ink-3); }

/* ---------------------------------------------------------------------
   Кнопки
   --------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: var(--control-h);
  padding: 0 18px;
  border-radius: var(--radius);
  font-size: 15px;
  font-weight: 600;
  border: 1px solid transparent;
  cursor: pointer;
  transition: background .15s ease, box-shadow .15s ease, transform .05s ease;
  /* R2 (аудит 13.08.2026): подпись кнопки («Открыть» -> «Откр ыть») рвалась
     посреди слова тем же унаследованным `anywhere` с `.card`, что и
     заголовки таблиц выше. `.btn-compact` — второй класс на ТОМ ЖЕ
     элементе (не потомок, наследование тут ни при чём): своего
     `white-space` не задаёт, значит объявление ниже по каскаду отсюда
     применяется как есть. */
  white-space: nowrap;
}
/* Task 5 (R1, аудит 13.08.2026): десктопная перезапись — размещена СРАЗУ
   ПОСЛЕ базового правила (не в раннем @media (min-width: 900px) у сайдбара
   выше — см. объяснение там же): при равной специфичности выигрывает
   правило, которое стоит ПОЗЖЕ в файле, независимо от медиа-условия;
   браузерная верификация (Chromium, 1440px) поймала это буквально —
   кнопка оставалась 15px/0 18px. */
@media (min-width: 900px) {
  .btn { font-size: 13.5px; padding: 0 14px; }
}
.btn:hover { text-decoration: none; }
.btn:active { transform: translateY(1px); }

.btn-primary { background: var(--accent); color: #fff; }
.btn-primary:hover { background: var(--accent-hover); color: #fff; box-shadow: var(--shadow-md); }

.btn-secondary { background: var(--surface); color: var(--ink); border-color: var(--line); }
.btn-secondary:hover { background: var(--bg); color: var(--ink); }

.btn-danger { background: var(--surface); color: var(--no); border-color: var(--no-soft); }
.btn-danger:hover { background: var(--no-soft); color: var(--no); }

.btn-block { width: 100%; }
.btn-giant { height: 56px; font-size: 17px; }
/* Task 4 (R1, аудит 13.08.2026), фикс-раунд Task 5: перенос из раннего
   блока — при равной специфичности решает порядок файла (тот же класс
   бага, что I-13/тосты); фикс задачи 4 внутри задачи 5. Ранняя версия
   (`@media (min-width: 900px)` у сайдбара) стояла РАНЬШЕ этих базовых
   правил и молча проигрывала им в каскаде — браузерная верификация
   (Chromium, 1440px) показала `.btn-giant` 56px/17px вместо 44px/15px на
   десктопе. Размещено СРАЗУ ПОСЛЕ базовых правил — тот же приём порядка,
   что уже используют `.btn`/`table.data-table td` (см. секции «Кнопки»/
   «Таблицы» выше и объяснение в раннем медиа-блоке у сайдбара). */
@media (min-width: 900px) {
  /* R1 (аудит 13.08.2026): full-width CTA — мобильный паттерн (кнопка на
     всю ширину экрана 375-430px). На десктопе та же кнопка растянута до
     ~980px (.main max-width 1080px минус паддинги) и читается рекламным
     баннером, а не кнопкой действия — ограничиваем по содержимому. */
  .btn-block, .btn-giant { width: auto; max-width: 320px; }
  /* Task 5 ruling (двухрежимная шкала токенов): .btn-giant остаётся 44px
     литералом и на десктопе — по смыслу это главное CTA-действие экрана,
     крупнее рядовых кнопок (те — var(--control-h): 36px); 44px здесь уже
     «компактнее» мобильных 56px из Task 4, второго уменьшения не нужно. */
  .btn-giant { height: 44px; font-size: 15px; }
}

/* Задача 3.1, Fix round 1 (ревью): раньше это был инлайн-стиль
   `style="height:32px; padding:0 10px; font-size:12px;"`, ДОСЛОВНО
   повторённый на 10 из 11 компактных кнопок действий в строках таблиц
   (Сегодня/Склад/Касса/Клиенты/Финансы) — плотная десктоп-строка данных.
   Инлайн-стиль нельзя перебить в @media без !important (специфичность
   style="" выше любого селектора класса); класс снимает саму причину, а
   не только симптом — мобильный override ниже задаёт 44px обычным
   каскадом. `.date-compact` — тот же приём для одного `<input
   type="date">` (Склад, строка «В пути») рядом с такой кнопкой. */
.btn-compact { height: 32px; padding: 0 10px; font-size: 12px; }
/* `input[type="date"].date-compact` (не просто `.date-compact`) — иначе
   специфичность проигрывает общему `input[type="date"]{width:100%;
   height:44px;...}` (Формы, ниже в каскаде: атрибут+элемент против одного
   класса) и на десктопе дата рисуется НЕ 130×32px, а обычным полем на всю
   ширину. Обнаружено сравнением скриншотов до/после (Fix round 1). */
input[type="date"].date-compact { width: 130px; height: 32px; font-size: 12px; }

.btn-toggle-input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.btn-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: var(--control-h);
  /* M-6 (финальный фикс-раунд Фазы 4, часть C): min-width — пол цели
     нажатия НЕЗАВИСИМО от родителя. `flex: 1` ниже растягивает кнопку
     только внутри `.btn-toggle-group` (verkauf_form.html/sklad.html — там
     несколько кнопок в ряд и без пола хватало ширины слова "💵 Bar"); на
     /kunden (галочка «✓» в столбце «В маршрут», app/templates/kunden.html)
     родитель — обычный `<td>`, не flex-контейнер, `flex: 1` там не
     действует вообще, и ширина кнопки — только padding (14+14) + один
     символ «✓»: замер в Chromium на 390 px — 42,4×44 px, чуть меньше цели
     нажатия. min-width — пол, который держит форму независимо от того,
     растягивает ли её родитель. */
  min-width: 44px;
  padding: 6px 14px;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink-2);
  font-weight: 600;
  cursor: pointer;
  flex: 1;
  text-align: center;
  /* 17.08.2026 (замечание владельца «выровняй текст по центру»): высота выше
     задана `height` — ЖЁСТКО. Пока подпись в одну строку, всё хорошо; но
     «НЕТТО — НДС сверху» на телефоне (кнопка ~154 px) переносится на две, и
     две строки в жёсткие 44 px не помещаются — текст прижимался к краям и
     читался как «съехавший», хотя `text-align`/`justify-content` центр уже
     стояли. Лечится не выравниванием, а высотой: `min-height` держит ту же
     цель нажатия (44 px), а кнопка растёт под вторую строку, и `align-items:
     center` центрирует текст по вертикали ЧЕСТНО. `height: auto` обязателен —
     без него `min-height` перебивается объявленным выше `height`.
     `text-wrap: balance` делит подпись на строки поровну («НЕТТО —» /
     «НДС сверху»), а не «НЕТТО — НДС» / «сверху». */
  height: auto;
  min-height: var(--control-h);
  text-wrap: balance;
}
.btn-toggle-input:checked + .btn-toggle {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
/* итерация 4, аудит DS (P1): disabled — видимая обратная связь для
   knoepfeSperren (app.js дизейблит кнопки сабмита сразу после клика).
   Специфичность `.btn:disabled` (0,2,0) и `.btn-toggle-input:disabled +
   .btn-toggle` (0,3,0) — ОБЕ выше базовых `.btn`/`.btn-toggle` (0,1,0) и
   выше `.btn-primary`/`.btn-secondary`/`.btn-danger` (0,1,0 каждый) —
   позиция в файле не нужна для победы в каскаде, свойства не пересекаются
   с `:checked` (opacity/cursor/transform против background/border/color). */
.btn:disabled, .btn-toggle-input:disabled + .btn-toggle {
  opacity: .55;
  cursor: not-allowed;
  transform: none;
}
.btn-toggle-group { display: flex; gap: 8px; }

/* ---------------------------------------------------------------------
   Формы
   --------------------------------------------------------------------- */

.form-grid { display: grid; grid-template-columns: 1fr; gap: 14px; }
@media (min-width: 700px) {
  .form-grid.cols-2 { grid-template-columns: 1fr 1fr; }
}

.field label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-2);
  margin-bottom: 6px;
}
/* ⚠️ Кнопка-переключатель внутри `.field` — это тоже <label>, и правило
   ВЫШЕ перебивает её собственное `.btn-toggle {display:inline-flex}`:
   `.field label` = (0,2,0) против (0,1,0). В блоке `align-items: center`
   не значит НИЧЕГО — строка прижимается к верхнему краю, а `margin-bottom`
   подписи добавляет пустоту снизу. Замер 17.08.2026 (замечание владельца
   «это по центру?»): текст стоял на 10 px выше центра — сверху 8, снизу 18.
   Специфичность здесь ТА ЖЕ (0,2,0), поэтому важен порядок: правило обязано
   идти ПОСЛЕ `.field label`. Частный случай `.field .seg .btn-toggle` ниже
   лечил ровно это для сегмент-кнопок; здесь — общий случай.
   Сторож — tests/test_browser.py::test_browser_umschalter_text_steht_mittig
   (проверен на красном: без этого правила падает с display=block). */
.field .btn-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 0;
}

.field .hint { font-size: 12px; color: var(--ink-3); margin-top: 4px; }

/* Задача 8 плана редизайна (R5, аудит 13.08.2026): длинные подсказки/
   юридические сноски и docker-руководства сворачиваются под «Подробнее» —
   текст НЕ сокращается (память проекта: §-ссылки reviewer-верифицированы),
   только первая фраза остаётся видимой, остальное — по клику. Фоллбэки
   var(--accent-ink, var(--accent)) / var(--surface-2, var(--bg)): токенов
   --accent-ink/--surface-2 в :root нет (grep подтвердил), берём ближайшие
   существующие — --accent и --bg. */
details.hint-more { border: none; background: none; padding: 0; margin: 4px 0 0; }
details.hint-more summary { font-size: 12px; color: var(--accent-ink, var(--accent)); cursor: pointer; font-weight: 500; }
details.hint-more[open] { padding: 6px 10px; background: var(--surface-2, var(--bg)); border-radius: 6px; }
details.hint-more p { font-size: 12.5px; color: var(--ink-2); margin: 4px 0; }

input[type="text"], input[type="search"], input[type="number"],
input[type="tel"], input[type="date"], input[type="password"],
select, textarea {
  width: 100%;
  height: var(--control-h);
  padding: 0 12px;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  font-size: 15px;
  font-family: inherit;
}
textarea { height: auto; min-height: 88px; padding: 10px 12px; resize: vertical; }
/* итерация 4, аудит DS (P1): disabled-состояние полей — раньше визуально
   неотличимо от обычного (тот же белый фон, тот же тёмный текст), хотя
   JS уже дизейблит кнопки САБМИТА при отправке (knoepfeSperren, app.js) и
   формы дизейблят поля программно в паре мест. Позиция ОБЯЗАНА быть
   здесь, СРАЗУ ПОСЛЕ базового составного правила полей: специфичность
   `input:disabled` (тег+псевдокласс) РАВНА специфичности
   `input[type="text"]` (тег+атрибут) из блока выше — при равной
   специфичности решает порядок в файле (тот же класс ловушки, что у
   .btn/table.data-table td/.btn-giant, см. комментарии Task 5 выше), и
   если бы это правило стояло РАНЬШЕ базового, background/color
   заблокированного поля молча остались бы обычными. */
input:disabled, select:disabled, textarea:disabled {
  background: var(--neutral-soft);
  color: var(--ink-3);
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  /* итерация 4, аудит DS (P0): добавлено внутреннее кольцо (0 0 0 2px
     var(--bg)) между рамкой поля и внешним акцентным кольцом — раньше
     акцентная рамка примыкала к акцентному box-shadow без зазора и
     сливалась в одну сплошную полосу на светлом фоне. */
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent);
}
/* Task 9 (аудит 13.08.2026, шаг 2): числовые поля наследовали ЛЕВОЕ
   выравнивание текстовых инпутов — введённая сумма визуально сливалась
   с плейсхолдером (тот же серый, тот же левый край), не отличить факт
   ввода от подсказки. Числа читаются от младшего разряда справа — тот же
   приём, что уже стоит на table.data-table td.num/th.num. `input.num` —
   для number-подобных текстовых полей без type="number" (парсинг сумм
   строковый, правило №1 конституции — «money» в БД остаётся int, но
   поле формы часто type="text", чтобы принять «1.234,56»). */
input[type="number"], input.num { text-align: right; }
input.input-big { font-size: 22px; text-align: center; font-weight: 600; }

/* итерация 4, аудит DS (P2): десктопный слой полей — та же ловушка
   позиции, что уже решена для .btn/table.data-table td/.btn-giant (Task
   5 выше): наивный `input, select, textarea { font-size: 14px }` внутри
   РАННЕГО @media (min-width: 900px) (секция сайдбара) стоит ФИЗИЧЕСКИ
   РАНЬШЕ базового составного правила полей выше — и проиграл бы каскад.
   Плюс `input, select, textarea` (голые теговые селекторы, специфичность
   0,0,1) слабее самого базового правила (`input[type="text"]` — тег+
   атрибут, 0,1,1) НЕЗАВИСИМО от позиции — override обязан повторить ТОТ
   ЖЕ составной селектор, что и база, чтобы специфичность совпала и
   решил порядок в файле. Лейблы уже 13px (`.field label`, без изменений)
   — здесь поля подтягиваются до сопоставимой десктопной плотности. */
@media (min-width: 900px) {
  input[type="text"], input[type="search"], input[type="number"],
  input[type="tel"], input[type="date"], input[type="password"],
  select, textarea { font-size: 14px; }
}

.form-actions { display: flex; gap: 10px; margin-top: 6px; flex-wrap: wrap; }

/* ---------------------------------------------------------------------
   Баннер ошибки (block) и тост (warn)
   --------------------------------------------------------------------- */

.banner {
  border-radius: var(--radius);
  padding: 12px 14px;
  margin-bottom: 16px;
  font-size: 14px;
  font-weight: 500;
}
.banner-error { background: var(--no-soft); color: var(--no); border: 1px solid rgba(223,27,65,.25); }
.banner-error ul { margin: 4px 0 0; padding-left: 18px; }

/* задача 1.6 (/marshrut): K-17 «адрес не удалось геокодировать» — warn, не
   блокирует построение маршрута, поэтому не banner-error; переиспользует
   существующие токены --warn/--warn-soft (та же пара, что и .toast). */
.banner-warn { background: var(--warn-soft); color: var(--warn); border: 1px solid rgba(154,91,19,.25); }
.banner-warn ul { margin: 4px 0 0; padding-left: 18px; }

/* итерация 4, аудит DS (P1): вариант успеха/состояния — раньше был только
   error/warn, а нейтральное «это не ошибка, это факт» (sklad.html:
   «Снимок … зафиксирован») рисовалось голым `.banner` без единого
   свойства фона/цвета (комментарий на месте применения так и говорил:
   «класса «ок» в системе стилей нет, заводить его ради одного места
   незачем») — сообщение о состоянии визуально ничем не отличалось от
   текста страницы. Токены --ok/--ok-soft уже есть (используются в
   .badge-ok/.kpi-delta.ok) — второй раз их не заводим. */
.banner-ok { background: var(--ok-soft); color: var(--ok); border: 1px solid rgba(14,98,69,.25); }

#toast-container {
  position: fixed;
  top: 16px;
  right: 16px;
  z-index: 999;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: min(360px, calc(100vw - 32px));
}
.toast {
  background: var(--warn-soft);
  color: var(--warn);
  border: 1px solid rgba(154,91,19,.25);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  padding: 10px 14px;
  font-size: 13px;
  font-weight: 500;
  display: flex;
  align-items: flex-start;
  gap: 8px;
  animation: toast-in .15s ease-out;
}
/* I-13 (финальный фикс-раунд Фазы 3): крестик закрытия был 8,2×14 px — без
   размеров и паддинга вовсе. Попасть по нему пальцем невозможно, а тост на
   телефоне закрывает KPI: единственный способ убрать его раньше 6 секунд
   обязан быть достижим. 44px — та же цель нажатия, что у всех кнопок и
   полей проекта (Global Constraints). Task 5 (R1): тач-цель, не
   масштабируется десктопной шкалой --control-h — остаётся литералом на
   любом вьюпорте. */
.toast button {
  background: none; border: none; color: inherit; cursor: pointer;
  font-size: 18px; line-height: 1; padding: 0; margin-left: auto;
  min-width: 44px; min-height: 44px;
  display: flex; align-items: center; justify-content: center;
  flex: 0 0 auto;
}

/* Финальное ревью редизайна 13.08.2026 (Important-1): ссылка тост-сводки
   «⚠ Предупреждений: N — открыть» (initToasts, app.js) без своего правила
   наследовала дефолтный accent-цвет поверх жёлтого warn-фона и не несла
   тач-цели — тот же Global Constraint 44px, что у крестика закрытия
   рядом (.toast button выше). Правило стоит ПОСЛЕ базовых .toast/.toast
   button — та же позиционная методика каскада, что и остальные фиксы
   файла (I-13/R4 выше). */
.toast.warn-summary a { flex: 1; display: flex; align-items: center; min-height: 44px; color: inherit; text-decoration: underline; }
@keyframes toast-in { from { opacity: 0; transform: translateY(-6px);} to { opacity: 1; transform: translateY(0);} }

/* R4 (аудит редизайна 13.08.2026): каскадная регрессия I-13. Этот блок
   стоял ВНУТРИ @media таблиц (~339-347) — т.е. РАНЬШЕ базового правила
   #toast-container выше. При равной специфичности (оба — селектор по
   #id) побеждает ПОЗДНЕЕ объявление в файле, а не более узкое медиа-
   условие: базовое top:16px перебивало override top:auto, и на телефоне
   тосты стояли СВЕРХУ, закрывая ~40% первого экрана (замер аудита).
   13.08.2026: блок обязан стоять ПОЗЖЕ базового — при равной
   специфичности порядок файла решает; регрессия найдена аудитом
   (computed top:16px на 390px).

   Смысл самого правила (I-13, финальный фикс-раунд Фазы 3) не менялся:
   на 390 px четыре тоста занимали y = 16…298, x = 16…374 — перекрыты
   все KPI «Сегодня». Решение: стопка уезжает ВНИЗ (над таб-баром),
   высота ограничена, лишнее прокручивается внутри самой стопки. */
@media (max-width: 640px) {
  #toast-container {
    top: auto;
    bottom: calc(var(--tabbar-h) + 12px);
    left: 12px;
    right: 12px;
    max-width: none;
    max-height: 40vh;
    overflow-y: auto;
  }
}

/* ---------------------------------------------------------------------
   Клиенты — список поиска/фильтров, карточка клиента
   --------------------------------------------------------------------- */

.filter-bar { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 16px; }
.filter-bar > * { flex: 1 1 160px; min-width: 140px; }
/* Task 9: период-фильтры (Финансы/Касса/Авто — все три используют этот же
   class="filter-bar", grep подтвердил) несли тот же общий width:100% на
   поле даты, что и bezahlt-form выше — дата растягивалась на всю ширину
   своего .field при уже достаточных 44px высоты. */
.filter-bar input[type="date"] { max-width: 170px; }

.kunde-header { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; flex-wrap: wrap; }
.kunde-header .meta { color: var(--ink-3); font-size: 13px; margin-top: 4px; }

.log-item { padding: 10px 0; border-bottom: 1px solid var(--line); }
.log-item:last-child { border-bottom: none; }
.log-item .ts { font-size: 12px; color: var(--ink-3); }

.kunde-picker-search { margin-bottom: 10px; }
.kunde-picker-list { max-height: 260px; overflow-y: auto; border: 1px solid var(--line); border-radius: var(--radius); }
.kunde-picker-item {
  display: flex; justify-content: space-between; align-items: center;
  padding: 10px 12px; min-height: 44px; cursor: pointer; border-bottom: 1px solid var(--line);
}
.kunde-picker-item:last-child { border-bottom: none; }
.kunde-picker-item:hover { background: var(--bg); }
.kunde-picker-item.selected { background: var(--accent-soft); }
.kunde-picker-item .stadt { color: var(--ink-3); font-size: 12px; }

.empty-state { color: var(--ink-3); font-size: 14px; padding: 20px 0; text-align: center; }

.placeholder-card { text-align: center; padding: 60px 20px; }
.placeholder-card .emoji { font-size: 36px; margin-bottom: 12px; }

/* I-4 (финальный фикс-раунд): «Оплата получена» спрашивает дату и способ.
   Тач-цель — Global Constraint (палец, а не мышь); на телефоне строка
   переносится, на десктопе остаётся в одну. Высота — var(--control-h):
   44px база (телефон), 36px десктопная плотность (Task 5). Финальное
   ревью 13.08.2026 (Important-2): на стыке Task 5×Task 9 эти три
   объявления остались литералом 44px — не подхватили десктопный токен. */
.bezahlt-form { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
/* Поле суммы частичной оплаты (17.08.2026): узкое — рядом с датой и
   способом оплаты в одной строке; на телефоне ряд и так переносится
   (flex-wrap выше). */
.bezahlt-form .bez-betrag { width: 104px; }
.bezahlt-form input[type="date"],
.bezahlt-form select { height: var(--control-h); min-width: 130px; }
.bezahlt-form .btn { height: var(--control-h); padding: 0 12px; font-size: 13px; }

/* Task 9 (аудит редизайна 13.08.2026): строка дебиторки/расходов с формой
   «Оплата получена»/«Оплачен» (bezahlt_form/kosten_bezahlt_form,
   _makros.html — оба макроса несут ОДИН И ТОТ ЖЕ class="bezahlt-form")
   разъезжалась на всю ширину ячейки (330–960px при высоте строки
   150–200px): общий `input[type="date"]/select { width: 100% }` (секция
   «Формы» ниже по файлу) не отменяется правилом ВЫШЕ — оно задаёт только
   height/min-width, свойство width каждое правило каскада несёт
   независимо от остальных в том же объявлении. Позиционный сторож (тот
   же класс бага I-13/.btn/table.data-table td выше по файлу): override
   стоит ПОСЛЕ базового `.bezahlt-form { … }` — при равной специфичности
   выигрывает более позднее объявление, а не более узкий селектор. */
.bezahlt-form input[type="date"] { width: auto; max-width: 150px; }
.bezahlt-form select { width: auto; max-width: 190px; }
.bezahlt-form { flex-wrap: nowrap; }
@media (max-width: 640px) {
  /* На телефоне строка таблицы уже карточка-анкета (см. секцию «Таблицы
     на телефоне» выше) — ширины хватает, а поля обязаны остаться тач-
     целью 44px, не сжиматься до 150/190px рядом друг с другом. */
  .bezahlt-form { flex-wrap: wrap; }
}

/* Подпись только для скринридера: у полей внутри .bezahlt-form видимых
   label нет (место в строке таблицы), но безымянное поле — это поле, смысл
   которого знает только автор. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------------------------------------------------------------------
   Страница «Ещё» (мобильный таб-бар -> оставшиеся разделы) — Task 10
   (аудит 13.08.2026): раньше переиспользовала .sidebar-link, стилизован
   ТОЛЬКО внутри @media (min-width: 900px) выше — на телефоне, для
   которого страница и существует, класс не давал никакого оформления
   вовсе, и шаблон компенсировал это инлайн-рамкой на КАЖДУЮ строку
   (гиперссылка-«коробка»). Settings-список: тёмный текст (не цвет
   ссылки), разделитель между строками вместо рамки-коробки на каждой,
   chevron справа — как в типовом мобильном меню настроек.
   --------------------------------------------------------------------- */
.eshche-list { list-style: none; margin: 0; padding: 0; max-width: 480px; }
.eshche-row { border-bottom: 1px solid var(--line); }
.eshche-row:last-child { border-bottom: none; }
.eshche-item {
  color: var(--ink);
  display: flex;
  gap: 10px;
  align-items: center;
  padding: 14px 10px;
}
.eshche-item:hover { text-decoration: none; background: var(--bg); }
.eshche-item .eshche-chevron { margin-left: auto; color: var(--ink-3); }

/* ---------------------------------------------------------------------
   Модалка подтверждения (12.08.2026) — вместо window.confirm.
   Кнопки наследуют .btn (44px) — цель нажатия та же, что везде.
   --------------------------------------------------------------------- */
/* Обычный <div>, а не <dialog> (перенос с ветки прода 17.08.2026; там —
   12.08.2026 по жалобе с телефона: на браузере старше марта 2022 кнопки с
   подтверждением были мертвы). Затемнение — сам overlay, а не ::backdrop,
   которого у <div> нет. z-index выше таб-бара (30) и ниже тостов (999):
   тост поверх модалки допустим, таб-бар поверх неё — нет. */
.modal {
  position: fixed;
  inset: 0;
  /* inset понимают не все старые движки — дублируем по сторонам */
  top: 0; right: 0; bottom: 0; left: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(10, 37, 64, .45);
  color: var(--ink);
}
/* [hidden] обязан побеждать display:flex — иначе модалка висела бы поверх
   всей системы и глотала нажатия, а выглядело бы это как «сайт не
   реагирует». Тот же класс ошибки, что и чинится этой правкой. */
.modal[hidden] { display: none; }
.modal-card {
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  padding: 20px;
  /* min() — на телефоне 390px карточка не должна упираться в края */
  width: min(380px, calc(100vw - 32px));
}
.modal-text { margin: 0 0 18px; font-size: 15px; overflow-wrap: anywhere; }
/* Причина отмены в окне подтверждения (17.08.2026, решение владельца
   «пусть будут шаблоны»). Блок скрыт [hidden], когда у формы нет поля
   `grund` — тогда окно спрашивает просто «да/нет», как раньше. */
/* Панель выбора дня на экране «Маршрут» (17.08.2026). Обычная строка над
   карточками: подпись, поле даты, кнопка и — если день не сегодняшний —
   плашка-напоминание, каким числом лягут продажи из чек-листа. */
.route-datum-bar {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 0 0 14px;
}
.route-datum-bar label { font-size: 13px; font-weight: 600; color: var(--ink-2); }
.route-datum-bar input[type="date"] { height: var(--control-h); width: 160px; }
.route-datum-bar .btn { height: var(--control-h); }

/* Поле даты маршрута в нижней панели «Клиентов» (17.08.2026): узкое,
   рядом с кнопкой «Построить маршрут»; панель и так flex с gap. */
.route-datum input[type="date"] { height: var(--control-h); width: 148px; }
@media (max-width: 640px) { .route-datum input[type="date"] { width: 130px; } }

/* Строка «Итого» в таблице продаж (18.08.2026, VF-03): в Anlage EÜR идёт
   нетто, в UStVA — база и налог отдельно; складывать колонку глазами нельзя
   (в таблице видны и отменённые продажи). Жирная, с верхней линией — читается
   как итог, а не как ещё одна продажа. */
tfoot tr.summe td {
  font-weight: 700;
  border-top: 2px solid var(--line);
  background: var(--surface-2, transparent);
}

.modal-grund { display: flex; flex-direction: column; gap: 6px; margin: 0 0 16px; }
.modal-grund[hidden] { display: none; }
.modal-grund label { font-size: 13px; font-weight: 600; color: var(--ink-2); }
.modal-grund select, .modal-grund .modal-grund-frei { height: var(--control-h); width: 100%; }
.modal-grund .modal-grund-frei[hidden] { display: none; }
.modal-actions { display: flex; gap: 8px; justify-content: flex-end; }
.modal-actions .btn { min-width: 96px; }

/* ===========================================================================
   Итерация 3 точечного прохода редизайна (мокап пользователя 14.08.2026):
   раздел «Сегодня» (page-head/KPI/duo/warnrow/таблица) + глобальные
   токен-правки, применяемые ко ВСЕЙ системе (правило контроллера п.1 —
   «не только heute»). Секция целиком стоит ПОСЛЕ всех существующих правил
   файла — та же дисциплина каскада, что и у всех предыдущих точечных
   override'ов этого файла (I-13/тосты, Task 4/5/7/9): при равной
   специфичности выигрывает ПОЗДНЕЕ объявление независимо от медиа-условия,
   поэтому override обязан физически стоять здесь, а не рядом со «своей»
   базовой секцией выше по файлу.
   =========================================================================== */

/* Токены мокапа — фон и линия чуть светлее/холоднее прежних (базовый :root
   несёт остальные токены без изменений, здесь переопределены только два). */
:root { --bg: #F7F8FA; --line: #E5E7EB; }

/* Плоские карточки: тень -> тонкая рамка, радиус — 10px вместо
   --radius-lg (12px) везде, где раньше была тень. Действует ГЛОБАЛЬНО
   (все .card/.kpi-card системы, не только «Сегодня»). */
.card, .kpi-card { box-shadow: none; border: 1px solid var(--line); border-radius: 10px; }
.card { padding: 18px 20px; margin-bottom: 14px; }
.kpi-card { padding: 16px 18px; }
.kpi-grid { gap: 14px; margin-bottom: 14px; }
.kpi-label { font-size: 13px; margin-bottom: 6px; }
/* clamp — базовый размер чуть меньше прежних 24px и слегка растёт с
   шириной вьюпорта (13px + 1.1vw), но не выше 27px; nowrap — то же
   назначение, что у остальных числовых ячеек файла (число не рвётся
   посреди значения на узкой карточке). */
.kpi-value { font-size: clamp(22px, 13px + 1.1vw, 27px); line-height: 1.15; white-space: nowrap; }
.kpi-note { margin-top: 4px; font-size: 12.5px; }

/* ---------------------------------------------------------------------
   page-head — заголовок экрана + дата + быстрые действия (заменяет
   голый <h1 class="page-title"> и полноширинный CTA-блок «Сегодня»,
   которые жили в самом низу экрана).
   --------------------------------------------------------------------- */
.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 16px; }
.page-head h1 { margin: 0; font-size: 28px; font-weight: 600; }
.page-date { color: var(--ink-2); font-size: 15px; margin: 2px 0 0; }
.page-head .acts { display: flex; gap: 8px; flex-wrap: wrap; }
.page-head .btn { height: 40px; font-size: 15px; padding: 0 20px; border-radius: 8px; }
@media (max-width: 640px) {
  .page-head .acts { flex: 1 1 100%; }
  .page-head .btn { flex: 1; height: 44px; }
}

/* ---------------------------------------------------------------------
   duo — пара плоских карточек «Запасы»/«План и факт» (заменяет прежние
   .kpi-emkost-card/.kpi-planfakt-card, см. обоснование в секции M-1 выше
   по файлу) + общие элементы их содержимого (rowline/bar/qlink).
   --------------------------------------------------------------------- */
.duo { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 14px; }
@media (max-width: 640px) {
  .duo { grid-template-columns: 1fr; }
}
.duo > .card { margin: 0; display: flex; flex-direction: column; }
.qlink { font-size: 13px; color: var(--ink-2); margin-top: auto; padding-top: 10px; text-align: right; display: block; }
.qlink:hover { color: var(--ink); text-decoration: underline; }

.rowline { display: flex; justify-content: space-between; gap: 12px; font-size: 13.5px; color: var(--ink-2); padding: 4px 0; }
.rowline .v { font-weight: 600; color: var(--ink); }
.bar { height: 8px; border-radius: 999px; background: var(--neutral-soft); overflow: hidden; margin: 8px 0 4px; }
.bar i { display: block; height: 100%; background: var(--ink-3); border-radius: 999px; }

/* ---------------------------------------------------------------------
   warnrow — карточка «Предупреждения» heute.html больше не список <li>
   внутри одной <div class="card">, а отдельная плашка НА КАЖДОЕ
   предупреждение (id="warnungen" остаётся на общем контейнере — тост-
   сводка app.js по-прежнему ссылается на /#warnungen).
   --------------------------------------------------------------------- */
.warnrow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; background: var(--warn-soft); border: 1px solid rgba(154,91,19,.25); border-radius: 10px; padding: 14px 16px; margin-bottom: 14px; font-size: 14px; color: var(--warn); }
.warnrow b { font-weight: 650; }
.warnrow .warn-inline { vertical-align: -3px; margin-right: 6px; } /* 14g: иконка в строке с текстом */
.warnrow .btn { margin-left: auto; }
/* Расширение сверх буквы мокапа: мокап рисовал ГОЛУЮ <button> действием
   warnrow (K-12/K-33/K-34 — ровно такие, `.btn` попадает под правило
   выше), но два действия каталога (K-05 «Занести в кассу», K-09 —
   makros.bezahlt_form) — целые <form>, а не голая кнопка (нужен скрытый
   form_token/next, у K-09 ещё дата+способ). Кнопка ВНУТРИ <form> — уже не
   прямой flex-потомок .warnrow, и `margin-left: auto` на ней саму строку
   вправо не подвинет — толкать нужно сам <form>. */
.warnrow form { margin-left: auto; }

/* Десктопная плотность таблиц, слой 2 (Task 5 уже даёт 13.5px/6px 10px
   сразу после базового table.data-table td — этот блок стоит ПОЗЖЕ и
   уточняет её дальше по мокапу: 34px строка, 13px/5px 8px ячейка). */
@media (min-width: 900px) {
  table.data-table tbody tr { height: 34px; }
  table.data-table td { padding: 5px 8px; font-size: 13px; }
}

/* «Последние операции» на пустой БД — короткая подпись вместо таблицы
   (тот же полёт, что .empty-state, но без центрирования/паддинга блока —
   компактная строка внутри уже существующей карточки). */
.ops-empty { font-size: 13px; color: var(--ink-3); padding: 4px 0 2px; }

/* Кнопка РАЗРУШАЮЩЕГО действия строки таблицы (td.m-act .btn-ghost-no) —
   «привидение»: без фона/рамки, только текст токеном --no (то же
   смысловое место, что раньше занимала полноценная btn-secondary
   «Отменить» на «Сегодня»).

   Fix-раунд ревью контроллера (14.08.2026): первая версия этого правила
   стояла на ГОЛОМ `.btn-compact` — специфичность (0,2,1) красила КАЖДОЕ
   компактное действие в любой td.m-act (heute/kunden/kasse/finanzen/
   sklad/avto/bestellungen/route), не только «Отменить». Мокап красил
   ТОЛЬКО отмену (единственное разрушающее действие в этих таблицах);
   «Открыть» (kunden.html), «Продажа» (bestellungen.html/heute.html:
   «На сегодня»), «Оприходовать» (sklad.html), «Зачесть»/«Разделить»
   (finanzen.html: авансы) читались бы как опасность/удаление наравне с
   отменой — их семантика другая (навигация/создание записи), и красный
   цвет здесь вводит в заблуждение.

   Правило сужено до отдельного модификатора `.btn-ghost-no`, который
   несут ТОЛЬКО кнопки «Отменить» (все формы `action="/storno/...}"` в
   таблицах — avto/bestellungen/finanzen×5/heute/kasse/kunde×2/sklad,
   грепом подтверждено 12 мест). Остальные компактные действия в
   `td.m-act` вернулись к обычному виду (btn-primary/btn-secondary
   compact) — CSS-правки этой задачи их больше не трогают вовсе. */
td.m-act .btn-ghost-no { background: none; border: none; height: auto; min-height: 0; padding: 2px 4px; font-size: 13px; font-weight: 500; color: var(--no); }
td.m-act .btn-ghost-no:hover { color: var(--no); background: none; text-decoration: underline; }
td.m-act .btn-ghost-no:active { transform: none; color: var(--no); }

/* Мобильный слой мокапа: несколько точечных уточнений плотности + два
   Global Constraints телефона — safe-area (iOS home indicator) и
   расширенная тач-цель компактной кнопки строки (36px видимых + 4px
   сверху/снизу ::after = 44px эффективной цели, тот же принцип, что уже
   несёт I-13 для крестика тоста, только через псевдоэлемент, а не
   min-height). Четвёртый occurrence "@media (max-width: 640px)" в файле —
   намеренно НОВЫЙ блок, а не правка одного из трёх существующих: те уже
   разобраны позиционными сторожами test_design_redesign.py по своему
   содержимому (top:16px/#toast-container, .btn-compact/44px,
   .bezahlt-form) — вставка ВНУТРЬ сдвинула бы эти срезы непредсказуемо. */
@media (max-width: 640px) {
  /* B-07 (финальная приёмка 19.08.2026): кегль был СТУПЕНЬКАМИ — 24px до
     400px и 20px ниже, — и обе ступени мерились под шестизначное число. На
     крупных телефонах (401–640px, где кегль ещё 24px) «1.000.000,00 €»
     выходило за скруглённый край карточки на 26px, а на 320–375px за него
     вылезали и вполне рабочие величины: «425.000,00 €» и даже «100.000 кг»
     остатка склада (четыре контейнера — 96 т, для оптовой торговли углём это
     обычный запас, а не выдумка).
     `clamp()` вместо ступени: верх диапазона — прежний кегль мокапа (24px на
     640px, 20px на 400px), ниже он плавно уменьшается вместе с шириной
     экрана. `vw` здесь стоит вместо ширины карточки: KPI на телефоне идут
     ДВУМЯ колонками (`.kpi-grid`), то есть карточка — это половина экрана
     минус постоянные отступы, и от ширины окна она зависит прямо
     пропорционально. Контейнерных запросов ради одной плитки не заводим.
     `nowrap` остаётся (число не переносят посреди разряда), поэтому уступает
     именно кегль.
     ⚠️ Честно про остаток: семизначное «1.000.000,00 €» на экранах уже 375px
     всё ещё выходит за край на 2–13px — при двух колонках оно туда не влезает
     ни при каком читаемом кегле. Порог в миллион евро за месяц для
     Kleinunternehmer § 19 (лимит 100.000 €) недостижим; все ДОСТИЖИМЫЕ
     величины после правки помещаются на всех проверенных ширинах
     (320/360/375/393/430/640, замер Playwright). */
  .kpi-value { font-size: clamp(20px, 3.75vw, 24px); }
  /* A-01, вторая половина: даже с `minmax(0, …)` шестизначная сумма шире
     половины узкого экрана. `nowrap` у .kpi-value стоит намеренно (число не
     переносят посреди разряда), поэтому сжимается КЕГЛЬ — и только там, где
     иначе резало бы знак валюты. */
  @media (max-width: 400px) {
    .kpi-value { font-size: clamp(15px, 4.6vw, 20px); }
  }
  .kpi-grid { gap: 12px; }
  .duo { gap: 12px; }
  .card { padding: 16px; }
  .main { padding-bottom: calc(var(--tabbar-h) + 24px + env(safe-area-inset-bottom)); }
  .tabbar { height: calc(var(--tabbar-h) + env(safe-area-inset-bottom)); padding-bottom: env(safe-area-inset-bottom); }
  /* Позиционный сторож: этот блок обязан стоять ПОСЛЕ существующего
     мобильного override `.main .btn-compact, .main input[type="date"]
     .date-compact { height: 44px; }` (секция «Таблицы на телефоне» выше)
     — при равной специфичности для .btn-compact выигрывает более позднее
     объявление, то есть 36px здесь. `.date-compact` этим блоком не
     тронут — то правило продолжает действовать на него одно. */
  .main .btn-compact { height: 36px; font-size: 13px; font-weight: 600; padding: 0 12px; border-radius: 8px; position: relative; }
  .main .btn-compact::after { content: ""; position: absolute; left: 0; right: 0; top: -4px; bottom: -4px; }
  /* Fix-раунд ревью (14.08.2026, Important-2): ghost-кнопка «Отменить»
     (td.m-act .btn-ghost-no, специфичность 0,2,1) перебивала ДВА правила
     выше (.main .btn-compact — 0,2,0) своим базовым `height: auto;
     min-height: 0; padding: 2px 4px` — специфичность решает НЕЗАВИСИМО от
     порядка в файле, и на телефоне тач-цель «Отменить» реально оставалась
     ≈30px (замер ревью), ниже 36px+::after остальных .btn-compact. Тот же
     приём позиционного каскада, что .main .btn-compact выше: ПОВТОРЕНИЕ
     базового селектора ghost-кнопки (та же специфичность 0,2,1) здесь,
     позже в файле, — побеждает базовое правило независимо от медиа-условия. */
  td.m-act .btn-ghost-no { min-height: 36px; padding: 2px 12px; position: relative; }
  td.m-act .btn-ghost-no::after { content: ""; position: absolute; left: 0; right: 0; top: -4px; bottom: -4px; }
  /* B-08 (финальная приёмка 19.08.2026): между РАЗРУШАЮЩЕЙ кнопкой строки и
     её безобидной соседкой было 6–8px («Продажа» ↔ «Отменить» на «Заказах»,
     «Снять отметку оплаты» ↔ «Отменить» на «Финансах»). Зоны нажатия не
     пересекались, но 6px — это меньше половины пальца: промах в дороге
     приводит к вопросу об отмене, который на автомате подтверждают. Шов
     между обратимым и безвозвратным доводится до 12px тем же приёмом, что у
     колонки действий «Настроек» (.sp-aktionen, gap 18px). */
  td.m-act .btn-ghost-no, td.m-act .btn-danger { margin-left: 6px; }
  /* Тихое «Отменить» (`.btn-quiet`) стоит в тех же рядах — на «Складе» и на
     «Финансах» его отделяли от «Снять отметку оплаты» те же 8px. Ряд
     `.inrow` держит зазор 8px, ячейка действий — 6px: 4px добавки хватает
     обоим, чтобы шов стал 12px. */
  td.m-act .btn-quiet, .inrow .btn-quiet { margin-left: 4px; }
  /* B-08, вторая половина: «Удалить» точки отправки — единственная
     компактная кнопка проекта БЕЗ накладки тач-цели: 38px видимых и 38px
     нажимаемых, тогда как у соседей по экрану эффективные 44px. Накладка
     ±3px доводит её до тех же 44px, не трогая вид. */
  .einst-card .sp-loeschen .btn { position: relative; }
  .einst-card .sp-loeschen .btn::after { content: ""; position: absolute; left: 0; right: 0; top: -3px; bottom: -3px; }
}

/* ===========================================================================
   Итерация 4 точечного прохода редизайна — часть A (аудит дизайн-системы
   пользователя, полировка): :focus-visible, планшетный диапазон тач-целей.
   Секция стоит ПОСЛЕ всех существующих правил файла — та же дисциплина
   каскада, что у секции «Итерация 3» выше.
   =========================================================================== */

/* итерация 4, аудит DS (P0): единое кольцо фокуса для клавиатурной
   навигации — раньше специально стилизован был только фокус самих полей
   ввода (input:focus и т.д. выше); кнопки/ссылки-навигация получали
   только браузерный дефолт (в Chromium/Firefox он есть, но еле заметен на
   --accent/--surface фонах проекта). `:focus-visible` (не `:focus`) —
   кольцо появляется при табуляции с клавиатуры, но не при обычном клике
   мышью, как того требует сам спецификатор псевдокласса. */
/* Ревью Итерации 14 (16.08.2026): `.btn-quiet` (тихое текстовое действие
   таблицы «Закупки», /sklad) — НЕ из семейства `.btn`, поэтому
   «Отменить»/«Реквизиты счёта» остались бы без кольца при табуляции.
   Селектор добавлен В ЭТОТ ЖЕ список, а не копией правила рядом: кольцо у
   проекта одно (сторож списка — test_a2_focus_visible_existiert_fuer_die_
   ganze_liste, tests/test_design_iteration4.py). */
.btn:focus-visible,
.btn-quiet:focus-visible,
.btn-toggle:focus-visible,
a.sidebar-link:focus-visible,
a.tabbar-item:focus-visible,
a.eshche-item:focus-visible,
a.qlink:focus-visible,
summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* итерация 4, аудит DS (P1): планшетный разрыв 641–899px. Task 5 (R1)
   ввела двухрежимную шкалу токенов — 44px мобильный, 36/38px десктопный
   (--control-h/--row-h, @media min-width:900px) — и мобильный override
   `.main .btn-compact`/`.date-compact` (44px, «Таблицы на телефоне» выше)
   и его ПОЗДНЕЙШИЙ мобильный оверрайд-раунд (36px+::after, секция
   «Мобильный слой мокапа» выше) — ОБА заперты в @media (max-width:640px).
   Между 641 и 899px не действовал НИ ОДИН override: `.btn-compact`
   получал голый базовый `height: 32px` (секция «Кнопки») — плотность мыши
   на диапазоне, где реально бывает и палец (планшет). Отдельный диапазон
   (не «перенос» существующего блока — чтобы не задеть уже разобранные
   позиционными сторожами срезы max-width:640 выше) держит компакт-кнопки
   и ghost-«Отменить» на полных 44px здесь, БЕЗ ::after-трюка (в отличие
   от ≤640, тут достаточно места, чтобы дать честные 44px, а не визуальные
   36 + невидимую накладку). */
@media (min-width: 641px) and (max-width: 899px) {
  .main .btn-compact,
  .main input[type="date"].date-compact { height: 44px; }
  td.m-act .btn-ghost-no { min-height: 44px; padding: 2px 12px; }
}

/* ===========================================================================
   Итерация 4 точечного прохода редизайна — часть B («Новая продажа»
   модалкой, мокап пользователя 15.08.2026). Разметка/CSS — дословно из
   мокапа (единственная сознательная замена: #fff -> var(--surface),
   токен-дисциплина). Классы .fblock/.fsec/.fgrid/.picked/.seg — ОБЩИЕ
   (не только внутри модалки): та же разметка used И на /verkauf (простая
   карточка .card-narrow), И в модалке heute.html (.mask/.mwin/.mhead/
   .mbody/.mcta) — см. app/templates/_verkauf_form_inhalt.html.
   =========================================================================== */

/* /verkauf — компактная карточка ≤860px по центру вместо полноширинной
   формы (страница остаётся отдельным no-JS/direct-access фоллбэком —
   мокап модалки её не отменяет, ruling контроллера). */
.card-narrow { max-width: 860px; margin: 0 auto; }

/* итерация 4: `[open]` — НЕОБХОДИМАЯ адаптация мокапа под <dialog>
   (мокап рисовал .mask как обычный toggled-<div>, ruling контроллера для
   ЭТОЙ ветки — <dialog>, как у confirm-модалки в base.html; прод-ветка с
   div-модалкой адаптирует иначе, см. задание). Без `[open]` `display:
   flex` — правило АВТОРСКОГО происхождения и по правилам каскада ВСЕГДА
   перебивает UA-правило `dialog:not([open]){display:none}` независимо от
   специфичности (происхождение решает раньше специфичности) — то есть
   модалка была бы видна ПОСТОЯННО, даже до открытия. `.modal`
   (существующий confirm) избегает этого тем, что вообще не трогает
   `display` — здесь класс сам управляет flex-раскладкой .mwin, поэтому
   display обязан остаться под контролем showModal()/[open]. */
/* Браузерная верификация (Chromium 390px): Chromium несёт СОБСТВЕННЫЙ
   UA-стиль для `dialog:modal` — помимо display:none/block по [open]
   (уже разобран выше), там же лежат `max-width: calc(100% - 6em)` и
   `margin: auto`. `inset: 0` перебивает UA-позиционирование (сам
   `position`/`inset` мы объявляем явно), но `max-width`/`margin` автор
   здесь НЕ трогал вовсе — а раз конфликтующего АВТОРСКОГО объявления нет,
   UA-правило для ЭТИХ конкретных свойств остаётся в силе independent от
   специфичности (проигрывает URL всегда, но только когда СОРЕВНУЕТСЯ).
   Итог без явного сброса: `<dialog>` вставал ~354px по центру viewport
   390px (`max-width: calc(100% - 6em)`), а .mwin с `width: 100%` считал
   проценты от ЭТОЙ урезанной ширины — 348px вместо полных 390.
   width/height/max-width/max-height/margin явно сброшены, чтобы `inset:
   0` управлял размером/позицией ЦЕЛИКОМ, как у обычного fixed-элемента. */
.mask[open] { position: fixed; inset: 0; width: auto; height: auto; max-width: none; max-height: none; margin: 0; border: none; background: rgba(10,37,64,.45); z-index: 100; display: flex; align-items: flex-start; justify-content: center; padding: 16px 24px; }
.mwin { background: var(--surface); border: 1px solid var(--line); border-radius: 12px; width: 780px; max-width: calc(100vw - 48px); max-height: calc(100vh - 32px); display: flex; flex-direction: column; overflow: hidden; box-shadow: 0 4px 16px rgba(10,37,64,.08); }
.mhead { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 24px; border-bottom: 1px solid var(--line); flex: 0 0 auto; }
.mhead h2 { margin: 0; font-size: 20px; font-weight: 600; }
.mclose { width: 36px; height: 36px; border: none; background: none; border-radius: 8px; font-size: 22px; line-height: 1; color: var(--ink-2); cursor: pointer; display: flex; align-items: center; justify-content: center; flex: 0 0 auto; }
.mclose:hover { background: var(--bg); color: var(--ink); }
.mbody { padding: 14px 24px; overflow-y: auto; display: flex; flex-direction: column; min-height: 0; }
.fblock { margin-bottom: 10px; flex: 0 0 auto; }
.fblock.fclient { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.fblock.fclient .kunde-picker-list { flex: 1 1 auto; min-height: 88px; }
.picked, .fclient .fsec, .fclient .kunde-picker-search { flex: 0 0 auto; }
.fsec { font-size: 13px; font-weight: 600; color: var(--ink-2); margin-bottom: 8px; }
.fgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
@media (max-width: 480px) { .fgrid { grid-template-columns: 1fr; } }
/* Итерация 6 (15.08.2026): «Заказы» (/bestellungen) хочет ТУ ЖЕ плотность
   полей, что несёт модалка продажи — селектор РАСШИРЕН до .fcard вместо
   третьей копии тех же деклараций (см. секцию «Итерация 6» в конце файла). */
.mbody input, .mbody select, .fcard input, .fcard select { height: 42px; border-radius: 8px; }
.mbody .hint { font-size: 12px; margin-top: 2px; }
.kunde-picker-search { margin-bottom: 8px; }
.mbody .kunde-picker-list { max-height: 134px; border-radius: 8px; }
.mbody .kunde-picker-item { min-height: 44px; padding: 9px 14px; }
.picked { display: flex; gap: 10px; align-items: baseline; margin-top: 4px; font-size: 13.5px; }
.picked .plabel { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-3); font-weight: 600; }
.picked b { color: var(--ink); }
.seg { display: flex; }
.seg .btn-toggle { height: 42px; flex: 1; border-radius: 0; margin-left: -1px; padding: 0 10px; font-size: 14px; }
/* Итерация 14c: внутри `.field` (Склад — «Способ оплаты») правило
   `.field label {display:block; margin-bottom:6px; font-size:13px}` (0,1,1)
   било `.btn-toggle {display:inline-flex}` (0,1,0) — текст прижимался к
   верху кнопки, снизу лишний отступ. Здесь (0,2,1) центрирование и
   нулевой margin-bottom возвращены; в `.fblock` (продажа) проблемы не было. */
.field .seg .btn-toggle { display: inline-flex; align-items: center; justify-content: center; margin-bottom: 0; font-size: 14px; }
.seg label:first-of-type { border-radius: 8px 0 0 8px; margin-left: 0; }
.seg label:last-of-type { border-radius: 0 8px 8px 0; }
.mcta { position: sticky; bottom: -14px; background: var(--surface); margin: 0 -24px -14px; padding: 8px 24px 14px; border-top: 1px solid var(--line); }
.mcta .btn { height: 44px; font-size: 15px; font-weight: 600; padding: 0 24px; width: auto; }
@media (max-width: 640px) {
  /* итерация 4, браузерная верификация (Chromium 390px): `[open]` здесь
     ОБЯЗАТЕЛЕН, не просто `.mask` — базовое правило выше специфичнее
     (`.mask[open]`, 0,2,0) голого `.mask` (0,1,0) и побеждает НЕЗАВИСИМО
     от порядка/медиа-условия. Без этого `[open]` замер показал `.mwin`
     300×… вместо full-width — `padding: 16px 24px` десктопного
     `.mask[open]` продолжал действовать на телефоне, сжимая .mwin через
     max-width: calc(100vw - 48px) базового правила, а этот блок
     проигрывал молча. */
  .mask[open] { padding: 0; align-items: stretch; }
  .mwin { width: 100%; max-width: none; max-height: none; border-radius: 0; border: none; }
  .mhead { padding: 14px 16px; }
  .mclose { width: 44px; height: 44px; }
  .mbody { padding: 16px; padding-bottom: calc(24px + env(safe-area-inset-bottom)); display: block; }
  .fblock.fclient { display: block; flex: none; min-height: auto; }
  .fblock.fclient .kunde-picker-list { flex: none; min-height: 0; }
  .mcta { margin: 4px -16px calc(-24px - env(safe-area-inset-bottom)); padding: 12px 16px calc(16px + env(safe-area-inset-bottom)); bottom: calc(-24px - env(safe-area-inset-bottom)); }
  .mbody input, .mbody select, .seg .btn-toggle, .fcard input, .fcard select { height: 44px; }
  .mcta .btn { width: 100%; }
}

/* ===========================================================================
   Итерация 6 точечного прохода редизайна — «Заказы» (/bestellungen) по
   мокапу пользователя 15.08.2026. Карточка формы переиспользует ТЕ ЖЕ
   .fblock/.fsec/.fgrid/.picked, что «Новая продажа» (итерация 4 выше,
   app/templates/_verkauf_form_inhalt.html) — новых версий этих классов
   здесь нет, и плотность самих ПОЛЕЙ (высота 42/44px, radius 8px) не
   продублирована третьей копией, а добавлена в существующие селекторы
   модалки (`.mbody input, .mbody select` и мобильный вариант — секция
   «Итерация 4» выше, оба теперь несут `, .fcard input, .fcard select`).
   Два по-настоящему НОВЫХ класса:
     .wrap  — узкая колонка страницы (1040px, по центру) — той же формы
              обёртка, что .card-narrow у /verkauf, но на уровне СТРАНИЦЫ
              (обе карточки), не одной карточки;
     .fcard — карточка формы (padding 20px 24px, h2 16px «Новый заказ» —
              своя плотность, отличная от базового .card 18px 20px/h2 17px,
              секция «Итерация 3» выше);
     .fcta  — кнопка сабмита (44px, авто-ширина на десктопе, на всю ширину
              на телефоне — тот же язык, что .mcta .btn модалки, но БЕЗ
              sticky-подвала: это обычная страница, не модалка).
   Список клиентов (`.kunde-picker-list`/`.kunde-picker-item`) намеренно НЕ
   вынесен в общий с `.mbody` селектор — цифры мокапа расходятся (/bestellungen:
   180px десктоп / 134px телефон; модалка: 134px ВСЕГДА, без разбивки по
   ширине) — общее правило дало бы неверное число одной из двух сторон.

   Мобильный override ниже (134 px ширина списка / 100% ширина кнопки)
   ОБЯЗАН физически стоять ПОСЛЕ десктопных/безусловных правил этой секции
   — тот же класс каскадной ловушки, что везде в файле (сравни override
   маски модалки в секции «Итерация 4» выше): у безусловного правила
   списка (без media, значение 180) и мобильного (значение 134) ОДИНАКОВАЯ
   специфичность (0,2,0) — на телефоне подходят ОБА правила, и при равной
   специфичности выигрывает ПОЗДНЕЕ объявление НЕЗАВИСИМО от медиа-условия.
   Если бы мобильный override стоял РАНЬШЕ безусловного правила (как было
   в первой редакции правки — дописан в СУЩЕСТВУЮЩИЙ @media секции
   «Итерация 4», который стоит раньше по файлу), значение 180 молча
   победило бы и на телефоне тоже. Обратная сторона: этот блок — уже
   ВТОРОЙ @media с точкой останова 640 px в файле, и тест (test_design_
   iteration4.py, регресс на override маски модалки) ищет свой блок ПО
   СОДЕРЖИМОМУ конкретной декларации, а не по «последний такой блок в
   файле» — правка того теста входит в этот же коммит. */

.wrap { max-width: 1040px; margin: 0 auto; }

/* ⚠️ НА ДЕСКТОПЕ ограничения нет (19.08.2026, замечание владельца по
   скриншотам). Обёртку `.wrap` носят три экрана — «Заказы», «Авто»,
   «Настройки», — и на широком мониторе они получались заметно уже соседей:
   1040 px против 1240 px у «Клиентов», «Кассы», «Финансов», «Склада».
   Владелец: «надо, чтобы ширина была как у «Клиентов» — тогда везде будет
   одинаково, только на десктопе».

   Ограничение снимается ТОЛЬКО на десктопе, а само правило остаётся: на
   узких экранах оно и так ничего не решает (там ширина меньше 1040 px), но
   его отсутствие развязало бы руки планшетам между 1040 и 1240 px, где
   мокапы не проверялись ни разу.

   Блок стоит ЗДЕСЬ, сразу за правилом, а не в общем десктопном разделе выше
   по файлу: тот объявлен РАНЬШЕ, и при равной силе селектора победило бы
   правило, объявленное позже, — то есть базовые 1040 px. Специфичность у
   обоих одинаковая (0,1,0), спор решает порядок.

   `.wrap.kunde-edit` (1100 px) этим НЕ затрагивается и не должен: у него сила
   селектора выше (0,2,0). Так и задумано — страница правки клиента остаётся
   узкой, как и другие полноэкранные формы («Новая продажа», карточка
   клиента): у формы своя мера ширины, читать длинную строку ввода через весь
   монитор неудобно. Владелец говорил про три экрана со списками и
   таблицами. */
@media (min-width: 900px) {
  .wrap { max-width: none; }
}

.fcard { padding: 20px 24px; }
.fcard h2 { font-size: 16px; }
.fcard .kunde-picker-list { max-height: 180px; border-radius: 8px; }
.fcard .kunde-picker-item { min-height: 44px; padding: 9px 14px; }

.fcta { margin-top: 18px; }
.fcta .btn { height: 44px; font-size: 15px; font-weight: 600; padding: 0 24px; width: auto; }

@media (max-width: 640px) {
  .fcard .kunde-picker-list { max-height: 134px; }
  .fcta .btn { width: 100%; }
}

/* ===========================================================================
   Итерация 7 точечного прохода редизайна — «Клиенты» (/kunden) по мокапам
   kunden-desktop/kunden-mobil пользователя (16.08.2026). Секция стоит ПОСЛЕ
   всех существующих правил файла — та же дисциплина каскада, что у секций
   «Итерация 3/4/6» выше: при равной специфичности выигрывает ПОЗДНЕЕ
   объявление независимо от медиа-условия.

   Главная перемена — таблица клиентов больше не растёт вместе с базой (179
   строк сегодня, DOM не пагинируется): на десктопе она получает
   СОБСТВЕННЫЙ внутренний скроллер (.tcard/.tscroll, max-height: calc(100vh
   - 250px)) со sticky-шапкой и sticky-панелью «В маршруте: N». На телефоне
   внутренний скролл снят (мобильный override .tcard, max-height: none — страница скроллится
   как обычно, список и так короткая лента карточек), а панель «В маршруте»
   зафиксирована над таб-баром (position: fixed).

   ВТОРАЯ мобильная раскладка в файле. Таблица клиентов получает СВОЙ класс
   table.kunden — не участвует в общем контракте table.data-table (thead
   visually-hidden/m-title/m-val/m-sub/m-hide/m-act, секция «Таблицы на
   телефоне» выше): обоснование — комментарий над TABELLEN_OHNE_DATA_TABLE
   в tests/test_hygiene.py (смешение классов дало бы ДВЕ конфликтующие
   мобильные раскладки одновременно на одной таблице).

   a11y-адаптация мокапа (ruling контроллера): мокап красит буквально
   .kunden thead{display:none} на телефоне — это снимает шапку из дерева
   доступности (скринридер её не прочтёт вовсе). Вместо буквального
   display:none — тот же visually-hidden приём, что уже несёт
   table.data-table thead (секция «Таблицы на телефоне» выше, тот же набор
   свойств, что и утилити-класс .visually-hidden): шапка остаётся в DOM для
   скринридера, из визуального потока убрана 1px-клипом. data-label на
   каждой td — тот же контракт, что и везде (сторожа test_p31_* в
   tests/test_phase3.py разбирают любой table, не завязаны на класс).

   Тоггл «В маршрут»: мокап рисует статичный класс .rt.on — приём заменён
   на уже установленный в файле паттерн чекбокс+label с сиблинг-селектором
   :checked (см. .btn-toggle-input:checked + .btn-toggle выше) — тот же
   механизм, что и раньше (checkbox остаётся настоящим полем формы POST
   /marshrut), просто новые имена классов (.rt-input/.rt) и собственный
   внешний вид квадрата. Галочка — SVG из общего спрайта (icons.svg#i-check,
   тот же приём, что i-route/i-map/i-chevron), видна только в
   :checked-состоянии.

   «Открыть»: своя desktop-колонка (col.c-open, 76px) остаётся отдельным td
   и на телефоне (8 исходных колонок = 8 td на любой ширине — сторожа
   data-label этого не касаются). На телефоне текст-ссылка не борется за то
   же место, что и чекбокс (.rt уже занимает верхний правый угол записи, под
   который td.name резервирует 76px отступа мокапа) — «Открыть» уходит
   отдельной строкой внизу записи, прижатой вправо; коллизий с именем любой
   длины это не создаёт (в отличие от размещения рядом с чекбоксом «встык»,
   которое при длинных именах наезжало бы на текст).

   .main:has(.stickybar) — единственный на /kunden способ добавить нижний
   зазор для fixed-панели без правки base.html (там main.main общий для
   всех страниц, page-специфичного класса нет): выбор через :has()
   (Chromium — единственный верифицируемый браузер проекта, поддержка есть)
   точнее, чем расширять .main глобально лишним отступом на всех остальных
   экранах, где никакой sticky/fixed-панели нет.
   =========================================================================== */

.filters { display: grid; grid-template-columns: 1fr; gap: 10px; }
.frow { display: grid; grid-template-columns: 1fr 1fr 1fr auto; gap: 10px; align-items: end; }
@media (max-width: 900px) {
  .frow { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 640px) {
  .frow { grid-template-columns: 1fr; }
}
.filters label { font-size: 12.5px; margin-bottom: 4px; }
.filters input, .filters select { height: 38px; border-radius: 8px; }
.filters .btn { height: 38px; padding: 0 16px; border-radius: 8px; }

.main { min-width: 0; }

/* 17.08.2026 (просьба владельца «сократи список на два клиента, чтобы
   «Построить маршрут» была выше»): вычитаемое 250 -> 346px = ровно две
   строки таблицы (.kunden tbody tr { height: 48px } × 2 = 96). min-height
   тем же шагом 320 -> 224, иначе на низких окнах он бы отменил правку. */
.tcard { padding: 0; overflow: hidden; position: relative; display: flex; flex-direction: column; max-height: calc(100vh - 346px); min-height: 224px; }
.tscroll { overflow-y: auto; overflow-x: auto; flex: 1 1 auto; min-width: 0; position: relative; }
/* 14e: position:relative — тот же урок, что .tcard в Итерации 8: absolute-потомки
   (visually-hidden label у поля «Фактически принято» в table.zk) иначе считают
   координаты от html и раздувают documentElement.scrollWidth (390 -> 600px на
   телефоне: страница уезжала вбок). Замер: html.scrollWidth 600 -> 390. */
table.kunden { width: 100%; min-width: 1100px; border-collapse: collapse; table-layout: fixed; }
.kunden col.c-name { width: 26%; }
.kunden col.c-city { width: 16%; }
.kunden col.c-ph { width: 130px; }
.kunden col.c-price { width: 64px; }
.kunden col.c-fun { width: 110px; }
.kunden col.c-coal { width: 170px; }
.kunden col.c-rt { width: 78px; }
.kunden col.c-open { width: 76px; }
.kunden td.name, .kunden td.city { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.kunden td .badge { white-space: nowrap; }
.kunden tbody tr { height: 48px; }
.kunden th {
  position: sticky; top: 0; z-index: 2; background: var(--surface);
  box-shadow: inset 0 -1px 0 var(--line); font-size: 11px; text-transform: uppercase;
  letter-spacing: .05em; color: var(--ink-3); font-weight: 600; text-align: left;
  padding: 10px 12px; white-space: nowrap;
}
.kunden td { padding: 8px 12px; border-bottom: 1px solid var(--line); font-size: 13.5px; vertical-align: middle; }
.kunden tr:last-child td { border-bottom: none; }
.kunden tbody tr:hover { background: var(--bg); }
.kunden .name a { font-weight: 600; color: var(--ink); text-decoration: none; }
.kunden .name a:hover { color: var(--accent); }
.kunden .sub { color: var(--ink-2); }
.kunden .num { text-align: right; white-space: nowrap; }
.kunden td.ph { white-space: nowrap; }

/* .rt/.rt-input — квадрат-тоггл: checkbox спрятан (тот же приём, что
   .btn-toggle-input), label .rt рисует квадрат, :checked красит его
   акцентом (мокап: .rt.on) и показывает SVG-галочку. */
.rt-input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.rt {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border: 1px solid var(--line); border-radius: 6px;
  background: var(--surface); color: var(--ink-2); cursor: pointer;
}
.rt svg { opacity: 0; }
.rt-input:checked + .rt { background: var(--accent); border-color: var(--accent); color: #fff; }
.rt-input:checked + .rt svg { opacity: 1; }

.open-link { font-size: 13px; color: var(--ink-2); text-decoration: none; padding: 4px 6px; }
.open-link:hover { color: var(--ink); text-decoration: underline; }

.stickybar {
  position: sticky; bottom: 0; z-index: 3; background: var(--surface);
  border-top: 1px solid var(--line); padding: 10px 16px; display: flex;
  align-items: center; justify-content: flex-end; gap: 12px; flex: 0 0 auto;
}
.stickybar .cnt { font-size: 13px; color: var(--ink-2); margin-right: auto; }
.stickybar .btn { height: 40px; font-size: 14px; font-weight: 600; padding: 0 20px; border-radius: 8px; }

@media (max-width: 640px) {
  /* Финал итераций 1-7 (16.08.2026): min-height: 0 поверх базовых 320px
     (.tcard выше, рассчитаны на десктопный внутренний скроллер) — на
     телефоне внутренний скролл уже снят (max-height: none тут же), и без
     этой строки карточка при 1-2 клиентах всё равно держала пустые 320px
     высоты вместо того, чтобы идти по контенту. */
  .tcard { max-height: none; min-height: 0; }
  /* a11y-ruling выше: visually-hidden вместо мокапного display:none —
     дословно тот же набор свойств, что table.data-table thead. */
  .kunden thead {
    position: absolute; width: 1px; height: 1px; margin: -1px;
    padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  table.kunden { min-width: 0; }
  table.kunden, .kunden tbody, .kunden tr, .kunden td { display: block; }
  .kunden colgroup { display: none; }
  .kunden tbody tr { height: auto; }
  .kunden tr { padding: 10px 14px; border-bottom: 1px solid var(--line); position: relative; }
  .kunden td { padding: 0; border: none; font-size: 13px; }
  .kunden td.name {
    font-size: 14.5px; margin-bottom: 2px; padding-right: 76px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: none;
  }
  /* Уточнение владельца (16.08.2026): «Воронка» не трогаем — статус
     остаётся видимым на телефоне (как и раньше, m-val прежней раскладки),
     не уходит в скрытые/десктоп-only колонки. Показан бейджем/прочерком
     на своей строке, тем же кодом _status_badge, что и на десктопе;
     «Уголь» — рядом тем же приёмом (inline-block, тот же ряд бейджей). */
  .kunden td.status, .kunden td.kohle { display: inline-block; margin: 4px 6px 0 0; }
  /* Город/Телефон/Цена — одна строка-«мета» через inline-поток; «·» —
     разделитель ПЕРЕД вторым/третьим значением (оба всегда рендерятся, в
     т.ч. прочерком «—» — тот же приём, что на десктопе, не скрываем
     условно). */
  .kunden td.meta { display: inline; color: var(--ink-2); }
  .kunden td.ph::before { content: "· "; }
  .kunden td.price::before { content: "· "; }
  .kunden td.route { position: absolute; top: 10px; right: 14px; display: flex; align-items: center; }
  .rt { width: 32px; height: 32px; position: relative; }
  /* Тач-цель >= 44 эффективных (ruling задания) — тот же ::after-приём, что
     .main .btn-compact::after/td.m-act .btn-ghost-no::after выше: 32px
     видимых + 6px со всех сторон = 44px эффективной цели. */
  .rt::after { content: ""; position: absolute; top: -6px; right: -6px; bottom: -6px; left: -6px; }
  .kunden td.open { display: block; text-align: right; margin-top: 6px; }
  /* A-05 (генеральная репетиция 18.08.2026): на 393px счётчик «В маршруте:
     12» уезжал ЗА ЛЕВЫЙ КРАЙ экрана (замер getBoundingClientRect: left −51
     при ширине элемента 68px — видно 18px из 68). Причина — три элемента в
     одну строку без переноса: кнопка держит `flex: 1`, поле даты своё, а
     счётчик (обычный span, flex-shrink по умолчанию 1) сжимался, и
     `justify-content: flex-end` выталкивал остаток влево за край. Читалось
     только «те:» — хвост подписи «Дата маршрута:». Маршрут строился по
     невидимому набору точек: лишний или пропущенный клиент обнаруживался
     только на «Маршруте».
     Лечится переносом: счётчик занимает ОТДЕЛЬНУЮ первую строку
     (flex-basis: 100%), дата и кнопка — вторую. `flex: 1 0 100%` заодно
     запрещает сжатие, поэтому исход не зависит от длины подписи (а она
     растёт: «В маршруте: 12 (+7 вне фильтра)»). `margin-right: auto`
     базового правила на своей строке уже ничего не толкает, но снят явно —
     чтобы правило читалось без оглядки на десктопное. */
  .stickybar {
    position: fixed; left: 0; right: 0;
    bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
    border-top: 1px solid var(--line); padding: 10px 16px;
    flex-wrap: wrap; row-gap: 8px;
  }
  .stickybar .cnt { flex: 1 0 100%; margin-right: 0; }
  .stickybar .btn { flex: 1; height: 44px; }
  /* Панель стала на строку выше (счётчик ~18px + row-gap 8px), значит и
     зазор под неё вырос ровно на столько же: 76px -> 102px. */
  .main:has(.stickybar) { padding-bottom: calc(var(--tabbar-h) + 102px + env(safe-area-inset-bottom)); }
  /* Финал итераций 1-7 (16.08.2026): базовые .filters input/select/.btn
     (38px, секция «Итерация 7» выше) — ниже порога тач-цели 44px (тот же
     стандарт, что .rt::after выше); мобильный override поднимает все три
     разом до честных 44px. */
  .filters input, .filters select, .filters .btn { height: 44px; }
}

/* ===========================================================================
   Итерация 8 точечного прохода редизайна — «Клиенты» (/kunden) по
   замечаниям пользователя со скриншотов ПРОДА (16.08.2026). Секция стоит
   ПОСЛЕ Итерации 7 — та же дисциплина каскада файла (позднее объявление
   выигрывает при равной специфичности).

   1) Колонки Имя/Город сжаты (32%->26%, 19%->16%), Телефон 120->130px —
      данные сдвигаются к левому краю, «остаток» уходит правым колонкам
      (п.2 ниже). Правки col.* внесены ВЫШЕ, в исходном блоке Итерации 7
      (та же дисциплина, что рестайл в один присест — не плодить второй
      блок col.* правил с непонятным порядком выигрыша).

   2) Бейджи Воронка/Уголь накладывались на соседей: колонки были уже
      САМОГО ДЛИННОГО бейджа. Замер в браузере (Chromium, 1440px, реальные
      подписи _status_badge/_kohle_badge): «Активный» ~80px, «Потерян»
      ~71px -> Воронка нуждается в ~104px (80 + 2×12px паддинга td) —
      задание предлагало ≥96px, замер показал МАЛО, взято 110px запасом.
      «Предположительно» ~139px -> Уголь нуждается в ~163px (задание
      предлагало ≥150px — тоже недостаточно по факту), взято 170px. Обе
      правки — в блоке col.* Итерации 7 выше (110px/170px). min-width
      таблицы поднят 960->1100px: это НИЖНИЙ дизайн-порог, на котором
      сумма процентных (26%+16%=42% ширины) и фиксированных колонок как
      раз сходится (628px фикс / 0.58 = ~1083px) — без подъёма min-width
      браузер сжал бы фиксированные колонки НИЖЕ их спеки на узких окнах
      ДО срабатывания горизонтального скролла .tscroll, и бейджи снова
      начали бы наезжать. На типичных 1440px .tscroll шире 1100px —
      колонки получают даже больше расчётного (table-layout: fixed
      пропорционально растягивает все колонки без явного % на избыток
      ширины), запас есть.

   3) «В маршрут»: чекбокс был прижат к левому краю колонки (label .rt —
      inline-flex, т.е. inline-уровня элемент — родительский td без
      text-align оставлял его слева). th/td получают text-align: center
      — простого центрирования родителя достаточно, менять .rt не нужно.

   4) Диагностика «нижний край карточки»/«лишний скролл страницы»
      (Playwright, 1440×900, 179 клиентов прод-копии БД): ОДНА причина на
      оба симптома. .tcard (flex-колонка, overflow: hidden, max-height:
      calc(100vh - 250px)) сама по себе была вычислена и склеена
      КОРРЕКТНО (clientHeight/scrollHeight совпадали, 648px), и .main/
      body тоже отчитывались о правильной высоте (~951px) — но
      document.documentElement.scrollHeight отдавал 8762px (реальная
      прокрутка страницы мышью подтвердила: реально пустое пространство
      до этой отметки, ровно симптом «карточка кончается на ~230px,
      страница тянется» со скриншота пользователя). Бинарный перебор
      причины (add_style_tag с !important поверх живой страницы):
      скрытие ЦЕЛОЙ таблицы убирало утечку (900px), урезание ::before
      dot/`position: static` на sticky-элементах — НЕ убирало, а
      `.tcard { position: relative }` УБИРАЛО (951px, как и должно).
      Диагноз: .tcard — «scroll container» (overflow: hidden — тоже
      триггер, не только auto/scroll) БЕЗ собственного positioning
      context; вложенные position: sticky (.kunden th, .stickybar)
      регистрируют constraining rect для ДОКУМЕНТА (что попадает в
      documentElement.scrollHeight/реальную прокрутку колесом), а не
      только для ближайшего скролл-контейнера (.tscroll, куда должны).
      position: relative на .tcard (правка внесена ВЫШЕ, в исходном
      правиле .tcard Итерации 7) даёт браузеру однозначный positioning
      context — утечка пропадает, «обрезанная последняя строка» (то же
      искажение отрисовки на границе бага) тоже: после фикса прокрутка
      .tscroll до конца показывает последнюю строку целиком вплотную над
      stickybar (0px нахлёста, проверено скриптом).

   5) Счётчик «В маршруте: N» — по ruling'у Итерации 7 остаётся
      СЕРВЕРНЫМ источником истины (значение в DOM при загрузке), но
      получает progressive-enhancement живое обновление в app.js
      (initKundenMarschrutZaehler): на change любого .rt-input видимая
      часть числа пересчитывается по факту отмеченных чекбоксов в DOM,
      суффикс «(+N вне фильтра)» — НЕ трогается (он про клиентов, скрытых
      текущим фильтром, JS о них не знает и не обязан). Без JS — прежнее
      поведение (число меняется после сабмита).

   6) Мобиль (390px): «Berlin · +49 30 ... · — —» — непредсказуемый
      inline-перенос заменён на ДЕТЕРМИНИРОВАННЫЙ разрыв после
      Телефона: `td.ph::after { content: "\A"; white-space: pre }`
      (генерируемый перевод строки — в отличие от plain inline-переноса,
      ширина контента больше не решает, где рвётся строка; варианты
      flex-basis:100% и display:block ОТВЕРГНУТЫ — см. блок «ДВА
      ПОДВОДНЫХ КАМНЯ» ниже, оба давали лишние разрывы). Цена не входит в компактную карточку вовсе (не только
      пустая — ЛЮБАЯ, ruling задания: строки 2/3 мокапа её не
      перечисляют) — заодно снимает артефакт «· —» для пустых сумм.
      Пустой статус/уголь (частый случай — Воронка пуста почти всегда,
      пока клиента не провели по циклу продаж) на мобиле не рисует голый
      «—»: HTML-фолбэк остаётся (нужен на десктопе, где «—» в СВОЕЙ
      колонке уместен) — обёрнут в span.dash, скрываемый ТОЛЬКО в этом
      мобильном блоке. */

/* Центрирование чекбокса — ДЕСКТОП (п.4 задания, 1440px): .rt —
   inline-flex (inline-уровня элемент), text-align: center на родителе
   достаточен, .rt трогать не нужно. Вне @media — на мобиле td.route
   получает position: absolute (см. мобильный блок Итерации 7 ниже) и
   text-align на неё уже не влияет визуально, но правило безвредно и там. */
.kunden th.center, .kunden td.route { text-align: center; }

@media (max-width: 640px) {
  /* Ряд 1 (Имя) и ряд 2 (Город · Телефон) уже были детерминированы
     БЕЗ flex Итерацией 7: td.name — display: block (базовое правило
     table.kunden td), td.city/td.ph — display: inline (.meta, секция
     Итерации 7 выше) — первый inline-элемент ПОСЛЕ блочного name
     обязательно начинает новую строку, второй (ph) продолжает ЕЁ ЖЕ.
     Единственная причина хаотичного переноса раньше — Цена (тоже
     .meta, могла утянуть за собой Воронка/Уголь, если ширины хватало)
     — убрана ниже (display: none), а не переведена в display: block. */
  .kunden td.price { display: none; }

  /* ДВА ПОДВОДНЫХ КАМНЯ, найденных браузерной проверкой (16.08.2026) —
     оба про «первый элемент строки 3 обязан начинать НОВУЮ строку, но
     делить ЕЁ с соседями (Уголь + Открыть)», в отличие от строк 1/2,
     где начинающий элемент (Имя) один на своей строке:

     1) flex-basis: 100% на td.status (первая попытка, .kunden tr на
        flex-wrap) — занимает ВСЮ гипотетическую ширину строки САМ ПО
        СЕБЕ, следующий сосед (Уголь) не помещается НА ТОЙ ЖЕ строке и
        уезжает на СВОЮ отдельную — 5 строк вместо 3.
     2) display: block на td.status (вторая попытка) — переносит
        строку БЕЗ проблемы (1), но блочный бокс изолирует себя от
        соседей С ОБЕИХ СТОРОН: следующий inline-block (Уголь)
        стартует уже СВОЕЙ, отдельной строкой сразу ПОСЛЕ блочного
        бокса Воронки, а не делит её с ней — Воронка (когда не пустая)
        оказывается одна на строке, Уголь+Открыть — на следующей: 4
        строки вместо 3 всякий раз, когда Воронка непуста.

     Рабочий приём — перенос строки БЕЗ элемента-виновника: генерируемый
     перевод строки (content: "\A"; white-space: pre) в ::after САМОГО
     ПОСЛЕДНЕГО элемента строки 2 (Телефон). "\A" — символ перевода
     строки в CSS-строке; white-space: pre на этом ::after не даёт
     браузеру схлопнуть его в пробел (как сделал бы normal/nowrap).
     td.ph остаётся inline (не блочный) — перенос ЕСТЬ, побочных
     эффектов НЕТ: td.status/td.kohle остаются inline-block КАК В
     ИТЕРАЦИИ 7 (правило .kunden td.status, .kunden td.kohle {
     display: inline-block } выше уже это делает, здесь трогать не
     нужно) и делят строку 3 друг с другом, как и раньше. */
  .kunden td.ph::after { content: "\A"; white-space: pre; }
  .kunden td.status .dash, .kunden td.kohle .dash { display: none; }
  /* «Открыть» — float: right вместо собственной блочной строки
     (Итерация 7): float вынимает элемент из потока и прижимает к
     правому краю на высоте точки вставки в разметке — сразу после
     Уголь, то есть на ТОЙ ЖЕ строке (короткий бейдж + короткая ссылка
     гарантированно помещаются в ширину карточки). */
  .kunden td.open { display: block; float: right; text-align: right; margin: 4px 0 0 0; }
}

/* ===========================================================================
   Итерация 9 точечного прохода редизайна — «Новый клиент» модалкой по
   мокапу пользователя (kunde-neu-desktop/-mobil, 16.08.2026). Разметка —
   app/templates/_kunde_form_inhalt.html (шарит .fblock/.fgrid/.field с
   формой продажи, итерация 4/6) + app/templates/kunden.html (dialog#
   kunde-dialog) + app/templates/kunde_form.html (страницы /kunden/neu,
   /kunden/{id}/bearbeiten — компактная карточка .card-narrow-wide).
   =========================================================================== */

/* Страница /kunden/neu и /kunden/{id}/bearbeiten — компактная карточка
   ≤920px по центру (тот же приём, что .card-narrow у /verkauf, но своя
   ширина: 920, не 860 — та же цифра, что несёт .mwin-wide у модалки
   ниже, ruling контроллера держит обе поверхности формы клиента одной
   шириной). Плотность полей самой страницы НЕ меняется (мокап описывает
   только модальное окно — страница остаётся прежним .field, см. ниже). */
.card-narrow-wide { max-width: 920px; margin: 0 auto; }

/* Модалка «Новый клиент» шире модалки продажи (мокап: 920px против 780px
   базового .mwin) — модификатор ВТОРЫМ классом на том же .mwin;
   verkauf-модалка его не несёт и остаётся на базовых 780. Обязан стоять
   В ФАЙЛЕ ПОСЛЕ базового `.mwin { width: 780px }` (секция «Итерация 4»
   выше) — при равной специфичности (0,1,0 у обоих) решает порядок.
   Мобильный сброс — ID-скоуп `#kunde-dialog .mwin` ниже, а НЕ второе
   объявление `.mwin-wide` — специфичность с ID (1,1,0) бьёт оба класса
   по 0,1,0 НЕЗАВИСИМО от порядка в файле, тот же приём устойчивее
   классической ловушки «кто позже, тот и выиграл», которая преследует
   этот файл в других местах (не нужно следить, чтобы override физически
   стоял после ЭТОГО правила). */
.mwin-wide { width: 920px; }

/* <form> модалки оборачивает И .mbody (прокручиваемое тело), И .mfoot
   (неподвижный подвал) — в отличие от verkauf-модалки, где сама форма
   ЕСТЬ .mbody, а подвал — sticky .mcta ВНУТРИ неё. Здесь мокап рисует
   .mbody/.mfoot СОСЕДЯМИ под .mwin: кнопка «Сохранить» обязана физически
   лежать ВНУТРИ <form> (app.js:knoepfeSperren ищет submit-кнопки по
   ПОТОМКАМ формы — кнопка снаружи со связью через атрибут form="…" для
   этого поиска невидима, защита от двойного клика молча не сработала
   бы), поэтому <form> оборачивает ОБА блока, а .mform держит
   flex-колонку с overflow:hidden, чтобы прокручивался именно вложенный
   .mbody (min-height:0 — классическая необходимость flexbox: без неё
   flex-элемент не сжимается меньше высоты своего контента, и вложенный
   overflow-y:auto не включается). */
.mform { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }

.mfoot { display: flex; gap: 10px; padding: 12px 24px; border-top: 1px solid var(--line); flex: 0 0 auto; }
.mfoot .btn { height: 42px; }

/* Плотность полей и кастомная стрелка select — дословно из мокапа,
   ТОЛЬКО внутри модалки (ID-скоуп #kunde-dialog). Компактная страница
   /kunden/neu намеренно не несёт этих чисел — мокап рисует только
   модальное окно, страница остаётся прежней плотностью .field (та же
   граница, что уже провела итерация 4 между /verkauf и модалкой
   продажи: там дополнительная плотность тоже осела только в .mbody).
   h42/44 инпутов даёт ОБЩЕЕ правило `.mbody input, .mbody select`
   (секция «Итерация 4») — .mbody здесь тот же класс (просто теперь на
   вложенном <div>, не на самой <form>), дублировать не нужно. */
#kunde-dialog input, #kunde-dialog select, #kunde-dialog textarea { font-size: 14.5px; }
#kunde-dialog .field label { font-size: 13.5px; }
#kunde-dialog .field .hint { font-size: 12.5px; color: var(--ink-3); }
#kunde-dialog .mbody textarea { min-height: 84px; }
/* select — нативная стрелка браузера убрана (`appearance: none`, три
   префикса ради Safari/Firefox), вместо неё — фоновый SVG-шеврон (тот же
   геометрический язык, что и `#i-chevron` спрайта icons.svg: viewBox
   24×24, stroke-width 2, скруглённые концы), цвет — `--ink-3` (#5E6B84)
   буквальным литералом внутри data-URI (custom-property внутри SVG
   background-image браузер не разворачивает — та же причина, по которой
   везде в проекте графика спрайта несёт `currentColor`, а не токен, но
   здесь currentColor унаследовал бы цвет ТЕКСТА select, а не ink-3).

   Итерация 19: kunde-edit-mockup.html (страница /kunden/{id}/bearbeiten)
   несёт ТОТ ЖЕ boilerplate-сброс `appearance:none` на select в шапке
   своего <style> (общий для всех мокапов генератора), но без
   компенсирующей стрелки в разметке — без неё `<select>` рисовался бы
   ПУСТЫМ полем без индикатора «это список» (баг, не буква мокапа —
   тот же класс техдобавки, что уже объяснён у .select-arrow ниже).
   Селектор РАСШИРЕН до `.kunde-edit select` (тот же SVG/те же цифры,
   третьей копии не заводим — приём секции «Итерация 6»); #kunde-dialog
   (модалка) не тронут. */
#kunde-dialog select, .kunde-edit select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235E6B84' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 16px 16px;
  padding-right: 36px;
}

@media (max-width: 640px) {
  /* ID-скоуп (1,1,0) бьёт И базовое `.mwin{width:780px}` (0,1,0), И
     безусловное `.mwin-wide{width:920px}` (0,1,0) выше НЕЗАВИСИМО от
     порядка в файле — модалка идёт на всю ширину телефона, как обычная
     verkauf-модалка (та же max-width:none/border-radius:0/border:none
     уже даёт ОБЩЕЕ мобильное правило `.mwin` итерации 4, вторым классом
     элемент несёт оба селектора, дублировать их здесь не нужно). */
  #kunde-dialog .mwin { width: 100%; }
  /* .fgrid делит класс со всеми модалками (итерация 4/6), их собственный
     мобильный перелом стоит на 480px (секция «Формы», правило .fgrid) —
     мокап именно ЭТОЙ модалки хочет один перелом с самим .mask[open]
     (640px). ID-скоуп оставляет verkauf-модалку/«Заказы» на их прежних
     480px нетронутыми. */
  #kunde-dialog .fgrid { grid-template-columns: 1fr; }
  .mfoot { flex-direction: column; }
  .mfoot .btn-primary { height: 44px; width: 100%; }
  /* «Отмена» — текстовая ссылка-по-виду (border/фон сняты, цвет ink-2),
     тот же приём, что td.m-act .btn-ghost-no (секция «Кнопка
     разрушающего действия» выше) — своя копия вместо общего класса,
     потому что общий .btn-ghost-no заточен под таблицу (min-height:0,
     padding 2px 4px) и здесь дал бы слишком маленькую тач-цель. */
  .mfoot .btn-secondary { height: 44px; border: none; background: none; color: var(--ink-2); }
}

/* ===========================================================================
   Итерация 10 точечного прохода редизайна — «Маршрут» (/marshrut, route.html)
   и «Авто» (/avto, avto.html) по мокапам пользователя (route-desktop/-mobil,
   auto-desktop/-mobil, 16.08.2026). Функциональность 1:1 — переставлена и
   переоформлена только разметка; поля форм/hidden/названия сохранены дословно
   (сторожа — tests/test_design_iteration10.py).

   Переиспользовано БЕЗ единой новой строки CSS (мокап буквально совпал с уже
   существующими классами прежних итераций):
     .wrap            — узкая колонка страницы (Итерация 6, /bestellungen);
     .fcard/.fgrid/.fcta — карточка формы «Новая поездка» (Итерация 6);
     .warnrow          — плашка K-27 «Определить координаты» (Итерация 3,
                          heute.html) — кнопка внутри неё уже «по контенту»
                          (margin-left:auto на .btn/form), не btn-block/giant;
     .tscroll          — горизонтальный скролл .rttable/.mt (Итерация 7,
                          /kunden: overflow-x:auto уже в правиле).

   .rt — ИМЯ мокапа для новой таблицы «Порядок объезда» — намеренно НЕ
   использовано: класс `.rt` уже занят квадратом-тоглом «В маршрут»
   (Итерация 7, table.kunden, см. секцию выше — bare-селектор `.rt { width:
   28px; height: 28px; … }` перекрасил бы саму таблицу в квадрат 28×28).
   Новая таблица — `table.rttable`.
   =========================================================================== */

/* --- Карточные под-заметки (sub-note): раньше `<p class="hint">` под h2
   карточки не нёс НИКАКОГО собственного CSS-правила (bare `.hint {}` в
   файле нет вовсе — есть только `.field .hint`/`.mbody .hint`/scoped) и
   рендерился обычным абзацем браузера. Мокап называет эту роль отдельно
   (`.sub-note`) — даёт реальное мелкое серое оформление вместо случайного
   дефолта. Четыре места: «Порядок объезда» и «Чек-лист» (route.html),
   «Месячный отчёт» и «Поездки» (avto.html). */
.sub-note { font-size: 13px; color: var(--ink-2); margin: 2px 0 12px; }

/* --- KPI-ряд /marshrut: 2 карточки, свои размеры (мокап), не общая сетка
   4/3-в-ряд остальных экранов — модификатор с двумя классами (0,2,0) бьёт
   и голый `.kpi-grid`/`.kpi-card`/`.kpi-label`/`.kpi-value` (0,1,0), и их
   `@media`-перезаписи (та же специфичность, но медиа-условие само по себе
   порядок не решает) НЕЗАВИСИМО от позиции в файле. */
.kpi-grid.route-kpi { grid-template-columns: 1fr 1fr; }
.route-kpi .kpi-card { padding: 12px 16px; }
.route-kpi .kpi-label { font-size: 12px; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-2); font-weight: 600; }
.route-kpi .kpi-value { font-size: 22px; font-weight: 650; }

/* --- .acts — ряд действий route.html («Открыть в Google Maps» / «В
   путевой лист»), тот же визуальный язык, что `.page-head .acts`/
   `.page-head .btn` (Итерация 3), но БЕЗ обёртки `.page-head` (кнопки
   стоят отдельным рядом под карточкой «Порядок объезда», не рядом с h1)
   — отдельное правило вместо переиспользования `.page-head .acts` без
   заголовка. */
.acts { display: flex; gap: 8px; flex-wrap: wrap; margin: 16px 0; }
.acts .btn { height: 40px; font-size: 15px; padding: 0 20px; border-radius: 8px; }
@media (max-width: 640px) {
  .acts { flex-wrap: wrap; }
  .acts .btn { flex: 1 1 auto; height: 44px; }
}

/* --- table.rttable — «Порядок объезда» (route.html): fixed-раскладка,
   колонки — colgroup ниже, СВОЙ мобильный язык (остаётся таблицей в
   .tscroll, горизонтальный скролл — НЕ m-контракт table.data-table). */
table.rttable { table-layout: fixed; min-width: 640px; width: 100%; border-collapse: collapse; }
table.rttable col.c-n { width: 34px; }
table.rttable col.c-name { width: 26%; }
table.rttable col.c-city { width: 22%; }
table.rttable th {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--ink-3); font-weight: 600; text-align: left;
  padding: 6px 8px; border-bottom: 1px solid var(--line);
}
table.rttable td {
  font-size: 13.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  padding: 0 8px;
}
table.rttable tbody tr { height: 44px; border-bottom: 1px solid var(--line); }
table.rttable tbody tr:last-child { border-bottom: none; }
table.rttable td.n { color: var(--ink-3); }

/* --- table.mt — «Месячный отчёт» (avto.html): та же дисциплина, что
   table.rttable выше (fixed, свой мобильный язык — таблица в .tscroll,
   не m-контракт) — первая колонка («Месяц») слева, остальные (числа)
   справа. */
table.mt { table-layout: fixed; min-width: 560px; width: 100%; border-collapse: collapse; }
/* Итерация 16 (мокап auto-*, 17.08.2026): th без переносов («СЛУЖЕБНЫЕ, КМ»
   ломался на две строки при 8px), паддинги 10px, трекинг .05em; строки без
   разделительных линий (мокап: только высота 40px) — таблица одна на
   странице, правила меняются на месте, а не скоупом. */
table.mt th {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--ink-3); font-weight: 600; text-align: right;
  padding: 6px 10px; border-bottom: 1px solid var(--line); white-space: nowrap;
}
table.mt th:first-child { text-align: left; }
table.mt td {
  font-size: 13.5px; text-align: right; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; padding: 0 10px;
}
table.mt td:first-child { text-align: left; }
table.mt tbody tr { height: 40px; }

/* --- .chk — чек-лист после развоза (route.html): GRID-строки, НЕ таблица
   (ruling мокапа Iter10) — `.hrow` (заголовок) и каждая `.crow` (запись)
   несут ОДИНАКОВЫЙ `grid-template-columns`, классический приём выравнивания
   «строка = свой грид» без общего табличного контейнера. Форма продажи
   внутри `.crow` — `display: contents`, чтобы её видимые поля (`.qty2`,
   `.pay`, `.act`) встали в те же дорожки грида, что и заголовок (hidden-
   поля формы невидимы в любом `display`, contents их не портит).

   Итерация 12 (замечание 2, скрин ~1000-1100px, диагностика Playwright
   16.08.2026): `.chk{min-width:760px}` + минимумы колонок
   `180/110/88/88/130` (750px по минимумам + 50px gap = 760, отсюда и была
   взята сама 760) НЕ помещались в карту при ширине контента ~700-750px
   (768/1024 — реальный замер: cardRight-actRight отрицательный, actFitsInCard
   false, `.tscroll` scrollWidth 760 > clientWidth 694/698) — кнопка
   «Продано» обрезалась, `.tscroll` честно скроллил, но выглядело как обрез.
   Фикс: минимумы снижены (`140/100/76/76/110`, сумма 502 + 50 gap = 552),
   последняя колонка — `auto` (кнопка сама по себе не шире своего текста,
   не тянется на фиксированные 100px) и `.chk{min-width:0}` — грид сам
   держит нижнюю границу через `minmax()`, отдельный контейнерный min-width
   был лишним поверх и именно он не давал гриду сжаться до ~552-660px на
   768/1024. Замер ПОСЛЕ (768/1024/1280/1440): actFitsInCard true,
   tscrollOverflows false на всех четырёх. */
.chk { display: flex; flex-direction: column; min-width: 0; }
/* B-02 (финальная приёмка 19.08.2026): колонка статуса была ЖЁСТКИЕ 100px, а
   её содержимое — от 144px («Платит потом: 14 дн.») до 304px («Заказ 1.200 кг
   на 23.08.2026 — везём раньше?»). Значок не обрезался и не переносился
   (`.badge{white-space:nowrap}`), а просто вылезал ВПРАВО и ложился поверх
   полей «кг», «€/кг» и «Оплата», которые идут позже по DOM: «Платит потом: 14»
   — единица «дн.» скрыта, а ссылка «везём раньше?» переставала нажиматься на
   58 % своей ширины (клик попадал в поле цены). Это задевает КАЖДЫЙ ряд, у
   которого есть хоть какой-то значок, то есть любого клиента с отсрочкой и
   любого с заказом.
   Лечение — две половины: колонка получила право растянуться на свободном
   месте (`minmax(100px,0.8fr)`; на узком экране остаётся прежние 100px), а
   значки внутри неё — право переноситься (см. `.crow .st .badge` ниже). */
.hrow, .crow {
  display: grid;
  grid-template-columns: minmax(140px,1.5fr) minmax(100px,0.8fr) 76px 76px minmax(110px,1fr) auto;
  gap: 10px;
  align-items: center;
}
.hrow {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--ink-3); font-weight: 600;
  padding: 0 0 8px; border-bottom: 1px solid var(--line);
}
.crow { min-height: 52px; padding: 8px 0; border-bottom: 1px solid var(--line); }
.crow:last-child { border-bottom: none; }
.crow form { display: contents; }
.kname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kname a { font-size: 13.5px; }
/* B-02: `min-width: 0` — чтобы колонка могла сжаться до своего трека, а не
   раздувалась содержимым; `overflow: hidden` — последний рубеж на случай
   значка, который перенести нельзя (одно длинное слово). */
.st { display: flex; flex-direction: column; gap: 4px; align-items: flex-start; min-width: 0; overflow: hidden; }
/* B-02/B-01: значок статуса ПЕРЕНОСИТСЯ. Общий `.badge{white-space:nowrap}`
   верен для таблиц (там пилюля коротка и обрыв строки посреди статуса читался
   бы как две записи), но в чек-листе маршрута значок несёт целое предложение
   с датой — и именно `nowrap` делал его шире собственной колонки. */
.crow .st .badge { white-space: normal; overflow-wrap: anywhere; max-width: 100%; }
.qty2 { display: contents; }
.qty2 input { height: 38px; }
.pay { height: 38px; }
.act.btn { height: 38px; width: auto; }

@media (max-width: 640px) {
  /* Итерация 11 (диагностика 16.08.2026, прод-скриншот iPhone 14 Pro
     393px): базовый `.chk{min-width:760px}` выше НЕ сбрасывался здесь —
     контейнер оставался 760px внутри 393px `.tscroll`, тот честно скроллил
     его горизонтально (overflow-x:auto), и без ручной прокрутки была видна
     произвольная средняя часть строки: обрезанные слева имя/бейдж,
     инпуты/селект без видимого края (рамка была, просто уехала за кадр) и
     кнопка «Продано» ПОЛНОСТЬЮ вне вьюпорта (её right уходил к 793px при
     ширине экрана 393px). `.crow{grid-template-columns:1fr}` ниже уже был
     верный (реально ОДНА колонка) — ломало не число колонок, а ширина
     самого контейнера, отсюда `min-width: 0`.

     Итерация 12 (замечание 1, скрин 393px, диагностика 16.08.2026): базовое
     правило `.chk` теперь САМО `min-width: 0` (см. выше, фикс замечания 2) —
     отдельного мобильного переопределения этого свойства больше не нужно,
     строка убрана (было бы дублирование текста, не поведения). Ниже —
     новый фикс: `.crow{grid-template-columns:1fr}` (ОДНА колонка) держал
     `.pay` (селект оплаты) и `.act.btn` (кнопка «Продано») на РАЗНЫХ
     полноширинных строках — замер ДО: pay.top 830 / act.top 882,
     sameRow=false. Меняем на ДВЕ колонки (`1fr auto`) и явно растягиваем
     kname/st/qty2 на обе (`grid-column: 1 / -1`) — auto-placement грида
     после трёх full-span элементов сам кладёт `.pay` (без явной колонки —
     первая свободная, col 1) и `.act.btn` (`grid-column: 2`) в ОДНУ и ту
     же следующую строку (курсор sparse-packing идёт слева направо в
     пределах строки, не переносится, пока есть место) — без переноса DOM
     и без grid-template-areas. Замер ПОСЛЕ: pay/act — одинаковый top. */
  .hrow { display: none; }
  /* B-01 (финальная приёмка 19.08.2026, high): было `1fr auto` — второй трек
     брал СТОЛЬКО, сколько просил значок, и на строке со ссылкой «Заказ 600 кг
     на 25.08.2026 — везём раньше?» (295px из 327px карточки) первой колонке
     оставалось 26px. В неё попадают И имя клиента, И `select` «Оплата»
     следующего ряда: имя схлопывалось до «H…» (два соседних «Hasır …»
     становились неразличимы), а у выбора оплаты оставалась одна стрелка —
     прочитать, стоит там «Наличными» или «Переводом», было нельзя. А от этого
     выбора зависит, попадут ли деньги в кассовую книгу сегодня же: чек-лист
     шлёт `bar_sofort=1`, и «Наличными» означает «деньги уже у меня» (§ 146 AO
     требует правдивой ежедневной кассовой книги).
     Минимум в 140px первой колонке — это ширина, при которой слово
     «Наличными» в `select` читается целиком (замер: 137px). Значку остаётся
     остаток строки, и он переносится (см. `.crow .st .badge` выше). */
  .crow { grid-template-columns: minmax(140px,1fr) auto; gap: 8px; }
  /* Итерация 13 (скрин пользователя 16.08): бейдж статуса — В ОДНОЙ СТРОКЕ
     с именем клиента, справа (раньше kname и st были full-span и стояли
     друг под другом). Тот же приём, что у pay/act ниже: kname — col 1,
     st — col 2 (justify-self:end), auto-placement кладёт их в один ряд;
     qty2 остаётся full-span. */
  .kname { grid-column: 1; min-width: 0; }
  .st { grid-column: 2; justify-self: end; }
  .qty2 { grid-column: 1 / -1; }
  .qty2 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
  .qty2 input { height: 44px; }
  .pay { height: 44px; }
  .act.btn { grid-column: 2; justify-self: end; width: auto; height: 44px; }
}

/* --- .period — строка периода (avto.html «Период»): один `<label>`, два
   `input[type=date]` (150px, aria-label различает «с»/«по» — второй визуальной
   подписи мокап не рисует), «Показать» справа (`margin-left: auto`). */
.period { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.period label { font-size: 13px; font-weight: 600; color: var(--ink-2); }
.period input[type="date"] { width: 150px; height: 38px; }
.period .sep { color: var(--ink-3); }
.period .btn { height: 38px; margin-left: auto; }
@media (max-width: 640px) {
  .period input[type="date"] { flex: 1 1 auto; width: auto; height: 44px; }
  .period .btn { flex: 1 1 100%; height: 44px; margin-left: 0; }
}

/* --- .select-arrow — кастомная стрелка select (тот же приём/тот же SVG,
   что #kunde-dialog select итерации 9: appearance:none + фоновый шеврон
   --ink-3 буквальным литералом в data-URI), но своим переиспользуемым
   классом вместо ID-скоупа — итерация 9 (#kunde-dialog) не трогаем. */
.select-arrow {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235E6B84' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 16px 16px;
  padding-right: 36px;
}

/* --- avto.html «Новая поездка»: .fcta даёт 44px безусловно (мокап «Заказы»
   Итерации 6) — здесь мокап хочет 42 на десктопе / 44 на телефоне, другая
   пара, поэтому переопределение СКОУПЛЕНО модификатором `.avto-wrap`
   (второй класс на том же `.wrap`, что и /bestellungen — оба используют
   голый `.wrap{max-width:1040px}`, но `.fcta .btn` у них теперь расходится
   на десктопе, общий `.wrap .fcta .btn` задел бы и /bestellungen). */
.avto-wrap .fcta .btn { height: 42px; }
@media (max-width: 640px) {
  .avto-wrap .fcta .btn { height: 44px; }
}

/* ===========================================================================
   Итерация 14 точечного прохода редизайна — «Склад» (/sklad) по мокапам
   пользователя lager-desktop/-mobil (16.08.2026). Мокап рисован по
   v7-составу (без 5A: без «Ключ»/«Нетто» в таблице закупок, без реквизитов
   счёта, без «Изъятия»/«Снимок §22») — эти dev-элементы ОСТАЮТСЯ (функция
   1:1) и оформлены в ТОМ ЖЕ языке классов ниже, но мокап их не диктует
   буквально (ruling брифа).

   Переиспользовано БЕЗ единой новой строки CSS: .warnrow/.kpi-grid/.kpi-card/
   .kpi-label/.kpi-value/.kpi-note/.kpi-delta (Итерация 3), .fgrid/.field
   (Итерация 4/6, ЕДИНЫЙ breakpoint 480px — регресс-сторож test_e_fgrid_
   basis_480_bleibt_unveraendert, tests/test_design_iteration9.py — здесь
   НЕ трогаем, 640px-коллапс ниже даёт СВОЙ скоуп `.sklad-card`), .seg
   (Итерация 4), .fcta (Итерация 6), details.hint-more (Task 8),
   .select-arrow (Итерация 9), .tscroll/table.rttable-подобный приём
   (Итерация 7/10 — новая таблица тут `table.zk`), .ops-empty (Итерация 6/10).

   Новые классы:
     .stats            — плоская 4-колоночная полоса «Ёмкости» (замена
                          kpi-grid ИЗ КАРТОЧЕК на полосу с вертикальными
                          разделителями) — ВНУТРИ каждой колонки классы
                          .kpi-label/.kpi-value/.kpi-note/.kpi-delta
                          ПЕРЕИСПОЛЬЗОВАНЫ буквально (не .sl/.sv/.sn мокапа):
                          H-7/_emkost_karte/T9-сторожа (tests/test_hygiene.py,
                          tests/test_design_redesign.py) пришиты к ИМЕННО
                          этим именам классов — переименование их сломало бы
                          защиту без надобности, а нужный вид (11.5 caps/
                          18-650/12 ink-3) даёт масштабирование через
                          descendant-селектор `.stats .kpi-label` и т. д.,
                          визуально идентичное .sl/.sv/.sn мокапа.
     .cap-note/.wline   — подвал карточки «Ёмкость» (disclosure-абзацы):
                          border-top отделяет от .stats, .wline красит
                          предупреждение о несовпадении с «Финансами» warn-
                          цветом (текст ОБОИХ абзацев не тронут — правило
                          проекта «текст подсказки не теряем молча»).
     .sklad-card        — скоуп для форм СКЛАДА (плотность полей/fcta/
                          640px-коллапс .fgrid), СВОЙ класс вместо реюза
                          .fcard/.avto-wrap: .fcard уже несёт .fgrid ВНУТРИ
                          СЕБЯ на /bestellungen (та же пара классов) — общий
                          `.fcard .fgrid{grid-template-columns:1fr}` @640
                          задел бы и её раскладку в диапазоне 480-640px,
                          которого там сегодня нет.
     table.zk/.inrow    — таблица «Закупки»: fixed-раскладка (colgroup),
                          СВОЙ мобильный язык (остаётся таблицей в .tscroll,
                          НЕ m-title/m-val/m-sub/m-hide-контракт table.data-
                          table — ruling мокапа «таблица на мобиле остаётся
                          таблицей со скроллом»). Колонки «Приход»/«действия»
                          — РАЗНЫЕ <td>: инпуты лежат в форме `#lief-<id>`
                          внутри «Приход», кнопка «Оприходовать» — снаружи
                          этой формы, но с атрибутом `form="lief-<id>"`
                          (валидный HTML5-приём ассоциации без вложенности —
                          `<form>` не может физически охватывать ДВЕ ячейки
                          таблицы). data-confirm остаётся на самой форме —
                          `event.submitter` (app.js) корректно находит кнопку
                          независимо от того, что она вне формы физически.
     .zk-scrollhint     — подсказка «Таблица прокручивается вбок →» ПОД
                          .tscroll, видна только ≤640px (на десктопе
                          горизонтальный скролл и так виден полосой).
     .btn-quiet         — тихое текстовое действие («Отменить» в таблице
                          закупок; «Реквизиты счёта» summary) — сознательно
                          БЕЗ .btn-ghost-no (красный): в узкой колонке
                          «действия» (206px) рядом с «Оприходовать» кнопка
                          «Отменить» на каждой строке красным была бы
                          навязчивой — мокап рисует её тихой (см. коммент
                          у test_i3_alle_storno_knoepfe_tragen_btn_ghost_no,
                          tests/test_design_redesign.py, порог там снижен и
                          «sklad» снят из перечня НЕ случайно).
     .ibtn/.ibtn-cell   — кнопка «Записать корректировку» в ПРАВОЙ ячейке
                          строки (не под полями, как раньше) — .ibtn-cell
                          выравнивает её по нижнему краю соседнего поля
                          «Дата корректировки» на десктопе, full-width 44px
                          на телефоне (мокап).
   =========================================================================== */

/* --- «Ёмкость»: плоская полоса вместо 4 карточек. Классы .kpi-label/
   .kpi-value/.kpi-note/.kpi-delta ВНУТРИ .stats — те же самые (см. выше),
   масштаб задаётся descendant-селектором. */
.stats { display: grid; grid-template-columns: repeat(4, 1fr); }
.stats > div { padding: 2px 16px; border-left: 1px solid var(--line); }
.stats > div:first-child { border-left: none; }
.stats .kpi-label { font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-3); font-weight: 600; }
.stats .kpi-value { font-size: 18px; font-weight: 650; margin-top: 2px; }
.stats .kpi-note { font-size: 12px; color: var(--ink-3); margin-top: 2px; }
.stats .kpi-delta { margin-top: 2px; }
@media (max-width: 900px) {
  .stats { grid-template-columns: 1fr 1fr; row-gap: 12px; }
  .stats > div:nth-child(odd) { border-left: none; }
}

/* --- Подвал карточки «Ёмкость»: disclosure-абзацы «В базе N…» + warn-строка
   несовпадения с «Финансами» + details «Подробнее» (текст НЕ тронут). */
.cap-note { border-top: 1px solid var(--line); margin-top: 12px; padding-top: 10px; }
.cap-note p { font-size: 13px; color: var(--ink-2); margin: 6px 0; }
.cap-note p:first-child { margin-top: 0; }
.cap-note .wline { color: var(--warn); font-weight: 600; }

/* --- Формы «Склада»: своя плотность полей (42px десктоп / 44px телефон —
   var(--control-h) сама по себе даёт 36px на десктопе ≥900px, «Формы» этой
   страницы хотят ту же плотность, что «Заказы»/«Поездки» (.fcard/.avto-
   wrap), а не табличную) + 640px-коллапс .fgrid, СКОУПЛЕННЫЙ на этот класс
   (см. объяснение вверху секции — почему не .fcard). */
.sklad-card input, .sklad-card select { height: 42px; border-radius: 8px; }
.sklad-card .fcta .btn { height: 42px; }
@media (max-width: 640px) {
  .sklad-card .fgrid { grid-template-columns: 1fr; }
  .sklad-card input, .sklad-card select { height: 44px; }
  .sklad-card .fcta .btn { width: 100%; height: 44px; }
}

/* --- «Записать корректировку» в правой ячейке строки (мокап), не под
   полями. */
.ibtn-cell { display: flex; flex-direction: column; justify-content: flex-end; }
.ibtn { height: 42px; width: auto; }
@media (max-width: 640px) {
  .ibtn { width: 100%; height: 44px; }
}

/* --- table.zk — «Закупки»: fixed, colgroup, СВОЙ мобильный язык (таблица
   в .tscroll со скроллом, НЕ m-title/m-val-контракт data-table). 9 колонок
   мокапа (Дата 80/Поставщик 13%/Кг 64/€за кг 70/НДС 70/Брутто 88/Статус 84/
   Приход 212/действия 206, min-width 1000) + 2 dev-колонки 5A («Ключ»/
   «Нетто», по 70/76px, мокап их не рисует) — min-width поднят до 1180px,
   чтобы новые колонки не сжимали остальные ниже читаемого. */
/* Ревью Итерации 14 (16.08.2026), находка 1 — ЗАМЕР вместо оценки (та же
   дисциплина, что п.2 Итерации 7 для бейджей /kunden: «задание предлагало
   >=96px, замер показал МАЛО»). Первая редакция взяла ширины из мокапа (он
   рисован БЕЗ двух 5A-колонок и без реальных подписей) — Chromium 1440px,
   прод-подобная БД (контейнер 19.000 кг × 1,32 €, ключи ig_erwerb/13b_2_1/
   NULL) показал, что `table-layout: fixed` + `text-overflow: ellipsis`
   МОЛЧА резали данные (td.scrollWidth > td.clientWidth):
     Дата     80px  -> нужно 87  («12.08.2026» обрезалась НА КАЖДОЙ строке)
     Нетто    76px  -> нужно 97  («25.080,00 €» -> «25.080,0…»)
     Брутто   88px  -> нужно 97  (то же)
     Ключ     70px  -> нужно 97  («не назначен»; «внутрисоюзное приобретение»
                                  218, «reverse charge § 13b Abs. 2 Nr. 1» 225)
     Статус   84px  -> нужно 149 (бейдж «Ожидает оплаты» ВЫЛЕЗАЛ за ячейку —
                                  ровно тот дефект, что чинил п.2 Итерации 7)
   Деньги и даты в бухгалтерской таблице обрезаться молча не могут (правило
   проекта «данные не теряем молча»), поэтому колонки с ОГРАНИЧЕННОЙ длиной
   содержимого расширены так, чтобы ellipsis не срабатывал НИКОГДА, а две
   свободнотекстовые (Поставщик/Ключ) дополнительно несут `title` в шаблоне
   (тот же приём, что `.kunden td.name title=` Итерации 7).

   Арифметика min-width (dev-ветка, 11 колонок): фиксированные колонки
   92+64+76+104+104+104+104+152+212+206 = 1218px при Поставщике 13% ->
   1218 / 0.87 = 1400px (Поставщик получает 182px). Таблица стала шире — она
   и была задумана прокручиваемой (.tscroll + .zk-scrollhint), а обрезанный
   евро — нет. Повторный замер после правки: ellipsis остался ТОЛЬКО на
   «Поставщике» и на двух самых длинных подписях ключа («внутрисоюзное
   приобретение» 218, «reverse charge § 13b Abs. 2 Nr. 1» 225 — растягивать
   колонку под них значило бы отдать им шестую часть таблицы), и обе колонки
   несут `title=` (на этой ветке — только «Поставщик», колонки «Ключ» нет). */
/* Итерация 14d (скрин пользователя, 16.08.2026): замерные ширины (1161px)
   давали ГОРИЗОНТАЛЬНЫЙ СКРОЛЛ на обычном десктопе (контент карточки ~1000px)
   и прятали «Отменить» за краем — мокап такого не рисует. Возврат к ширинам
   мокапа (80|13%|64|70|70|88|84|212|206, min-width 900 — контент карточки на 1280px = 954px, см. замер) + `table-layout:
   auto`: колонка, чьё содержимое шире мокапной ширины (длинный евро, бейдж
   «Ожидает оплаты»), РАСТЁТ сама вместо обрезки — данные не теряются, а
   таблица не шире карточки, пока содержимое обычное. Ellipsis — только у
   свободнотекстового «Поставщика» (max-width + title в шаблоне). */
table.zk { table-layout: auto; min-width: 1060px; /* dev: +«Ключ»/«Нетто» */ width: 100%; border-collapse: collapse; }
table.zk col.c-datum { width: 72px; }
table.zk col.c-lieferant { width: auto; } /* 14d: не 13% — процентная колонка в auto-раскладке раздувала таблицу до (сумма фикс.)/0.87 */
table.zk col.c-kg { width: 56px; }
table.zk col.c-preis { width: 64px; }
table.zk col.c-schluessel { width: 90px; }
table.zk col.c-netto { width: 84px; }
table.zk col.c-ust { width: 68px; }
table.zk col.c-brutto { width: 84px; }
table.zk col.c-status { width: 84px; }
table.zk col.c-prihod { width: 200px; }
table.zk col.c-akt { width: 200px; }
table.zk th {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--ink-3); font-weight: 600; text-align: left;
  padding: 6px 6px; border-bottom: 1px solid var(--line);
}
table.zk th.num { text-align: right; }
table.zk td {
  font-size: 13.5px; padding: 6px 6px; /* 14d: 8→6 — таблица без скролла на 954px */
}
table.zk td:not(.m-act) { white-space: nowrap; }
table.zk td.lieferant, table.zk td.schluessel { max-width: 96px; overflow: hidden; text-overflow: ellipsis; }
table.zk td.num { text-align: right; }
table.zk td.m-act { padding: 8px 6px; }
table.zk .badge { font-size: 11px; padding: 2px 6px; }
.inrow .btn { padding: 0 10px; }
table.zk tbody tr { height: 52px; border-bottom: 1px solid var(--line); }
table.zk tbody tr:last-child { border-bottom: none; }

/* --- .inrow — плотная строка «Приход» (дата+кг)/«действия» (кнопки),
   h36 (мокап). */
.inrow { display: flex; gap: 8px; align-items: center; min-height: 36px; flex-wrap: nowrap; } /* 14d: строкой, как в мокапе */
.inrow input.date-compact { width: 106px; }
.inrow input { font-size: 13px; padding: 0 6px; } /* 14d: как в мокапе (.zk input 13px) — дата не режется в 106px */
.inrow input[name="fakt_kg"] { width: 68px; }  /* выравнивание — общий input.num */
/* Ревью Итерации 14, находка 2 (каскадная ловушка + тач-цель). Плоские
   `.inrow .btn { height: 36px }` и инлайновые `style="height:36px"` на
   полях «Прихода» стояли ПОЗЖЕ всех мобильных override'ов файла и
   перебивали их (инлайн — вообще всё):
     390px  — «Оприходовать» 36+::after = 44 эффективных (ок), но поля даты
              и кг были 36 вместо 44 (`.main input[type="date"].date-compact
              { height: 44px }`, «Таблицы на телефоне», и var(--control-h));
     768px  — ВСЁ по 36: планшетный блок 641–899 («.main .btn-compact,
              .main input[type=date].date-compact { height: 44px }»,
              Итерация 4 часть A) проигрывал по порядку в файле.
   Замеры Chromium (390/768/1024/1440) — в ревью-скрипте iter14rev-measure4.
   Лечение: плотность 36px объявлена ТОЛЬКО для десктопа (>=900px, где
   действует десктопная шкала --control-h), мобильные и планшетные правила
   проекта снова работают как написаны. */
@media (min-width: 900px) {
  .inrow .btn { height: 36px; }
  /* `.inrow input.date-compact` (0,2,1), а не `.inrow input` (0,1,1):
     базовое `input[type="date"].date-compact { height: 32px }` выше по
     файлу — при меньшей специфичности ряд «Прихода» разъезжался бы
     32/36 (замер десктопа 1024/1440 до правки). */
  .inrow input.date-compact { height: 36px; }
}

/* --- .zk-scrollhint — подсказка про горизонтальный скролл (была видна только
   на телефоне). 17.08.2026, решение пользователя: подсказку «Таблица
   прокручивается вбок →» НЕ показывать нигде — разметка снята с /sklad и
   /avto (сторож test_design_iteration16.py: фразы нет ни в одном шаблоне);
   правило оставлено как позиционный якорь мобильного блока ниже (сторожа
   test_design_iteration14.py разбирают этот блок по нему). */
.zk-scrollhint { display: none; font-size: 12px; color: var(--ink-3); margin-top: 6px; }
.ops-note { font-size: 12.5px; color: var(--ink-3); margin: 8px 0 0; }
@media (max-width: 640px) {
  .zk-scrollhint { display: block; }
  /* 14f (мокап lager-mobil, ≤640): кнопка warnrow — своей строкой слева,
     без прижима вправо (`margin-left:auto` базы выше по файлу). */
  .warnrow .btn { margin-left: 0; }
  /* 14e: на телефоне поля строки «Прихода» — 44px (правило проекта), кнопка
     «Оприходовать» оставалась 36 — ряд был неровным; выравниваем по 44. */
  .inrow .btn { height: 44px; }
}

/* --- .btn-quiet — тихое текстовое действие (см. объяснение вверху секции).

   Ревью Итерации 14, находка 2 (продолжение): «Отменить» ушло с
   `td.m-act .btn-ghost-no` на `.btn-quiet` и вместе с классом потеряло ДВА
   правила тач-цели, которые тот нёс, — `min-height: 36px` + `::after`
   (-4px сверху/снизу = 44 эффективных) на <=640 и честные `min-height:
   44px` на 641–899 (обе — фикс-раунд ревью 14.08, Important-2: тогда
   намерили ~30px и это считалось находкой). Замер 390/768: 36px плоских,
   без ::after. Здесь тот же приём повторён для .btn-quiet — «тихо» это про
   ЦВЕТ и отсутствие рамки, а не про размер пальца. */
.btn-quiet {
  background: none; border: none; padding: 0 4px; height: 36px;
  color: var(--ink-2); font-size: 13.5px; font-weight: 500; cursor: pointer;
  position: relative;
}
.btn-quiet::after { content: ""; position: absolute; left: 0; right: 0; top: -4px; bottom: -4px; }
.btn-quiet:hover { color: var(--ink); text-decoration: underline; }
@media (min-width: 641px) and (max-width: 899px) {
  .btn-quiet { height: 44px; }
}

/* ===========================================================================
   Итерация 15 точечного прохода редизайна — «Касса» (/kasse) по мокапам
   пользователя kasse-desktop/-mobil (Claude Design, 16.08.2026; оба
   мокапа несут ОДИН и тот же CSS — раскладку переключает брейкпоинт 640).
   Скоуп — только классы этой страницы (.kasse-kpi на kpi-grid, .kasse-card
   на карточках): сквозные компоненты (.hint вне .field, summary
   «Подробнее», .card h2, .btn-giant, select) в остальных разделах НЕ
   трогаются — это отдельный пункт сверки 16.08 (docs/superpowers/plans/
   2026-08-16-kohle-ops-mobil-gap-check.md, раздел A), старт по команде.

   Переиспользовано: .period (Итерация 10, /avto), .fblock/.fgrid/.field
   (Итерация 4/6), .fcta (Итерация 6), .select-arrow (Итерация 9/14),
   .ops-empty (Итерация 6/10), .kpi-* (Итерация 3). Числа — из мокапа
   (kasse-mobil.html <style>): kpi-grid 1fr 1fr / gap 14 (телефон — ОДНА
   колонка, gap 12); kpi-label 12px uppercase .05em --ink-2 mb 4;
   kpi-value 24px/650 (телефон 22); kpi-note 12.5 --ink-3; .card 18px 22px
   (телефон 16), .card.slim 12px 22px (телефон 12px 16px), h2 16px mb 12;
   .period: plabel 13.5/600, поля 150×38, «Показать» 38 margin-left 8,
   подписи «С»/«По» скрыты — на телефоне всё в столбик, plabel 16px, .pcap
   видны, поля и кнопка 100%×44; формы: .field label 13.5/600, поля 42
   (44 телефон), .fblock mb 14, .fcta mt 12, кнопка 42/44 15px 600 0 22px
   (100% на телефоне); .hint 12.5 --ink-3 mt 8 lh 1.4; summary 13px --ink-2
   без UA-маркера, свой глиф «▸». */

/* --- KPI-пара «Кассы»: вариант B мокапов (тот же, что .route-kpi
   Итерации 10, плюс 24px значение и одна колонка на телефоне). Дубль
   .route-kpi по правилам осознанный — сведение обоих в один класс = задача
   P2-4 (консолидация CSS), не этой итерации. */
.kpi-grid.kasse-kpi { grid-template-columns: 1fr 1fr; gap: 14px; }
.kasse-kpi .kpi-label { font-size: 12px; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-2); font-weight: 600; margin-bottom: 4px; }
.kasse-kpi .kpi-value { font-size: 24px; font-weight: 650; }
.kasse-kpi .kpi-note { font-size: 12.5px; color: var(--ink-3); margin-top: 4px; }

/* --- Карточки «Кассы»: паддинг мокапа, h2 16px, .slim для строки периода. */
.kasse-card { padding: 18px 22px; }
.kasse-card.slim { padding: 12px 22px; }
.kasse-card h2 { font-size: 16px; margin: 0 0 12px; }
.kasse-card h2.tight { margin-bottom: 4px; }

/* --- Строка периода: поверх базового .period (Итерация 10, /avto —
   там один label и aria-label у второй даты) мокап «Кассы» рисует подпись
   .plabel + два интервала .pf с подписями .pcap («С»/«По»), которые на
   десктопе скрыты (тире между полями), а на телефоне раскрываются над
   полями. Специфичность (0,3,x) — чтобы перебить и `.period label`
   (0,1,1: 13px --ink-2), и мобильные `.period input[type=date]`/`.period
   .btn` Итерации 10 (0,2,x). */
.kasse-card .period { gap: 10px; }
.kasse-card .period .plabel { font-size: 13.5px; font-weight: 600; }
.kasse-card .period .pf { display: flex; align-items: center; }
.kasse-card .period .pcap { display: none; font-size: 13px; font-weight: 600; color: var(--ink); margin-bottom: 6px; }
.kasse-card .period .dash { color: var(--ink-3); }
.kasse-card .period .btn { height: 38px; padding: 0 16px; margin-left: 8px; }

/* --- Формы «Кассы»: плотность мокапа поверх общих .fblock/.fcta (те несут
   verkauf-значения 10/18 — здесь 14/12), поля 42px (десктопная шкала
   --control-h дала бы 36). Голого `.hint{}` в app.css нет (комментарий
   секции Итерации 10 выше) — подсказка под кнопкой оформлена ЗДЕСЬ, в
   скоупе страницы. */
.kasse-card .field label { font-size: 13.5px; }
.kasse-card .field input, .kasse-card .field select { height: 42px; border-radius: 8px; }
.kasse-card .fblock { margin-bottom: 14px; }
.kasse-card .fblock-last { margin-bottom: 0; }
.kasse-card .fcta { margin-top: 12px; }
.kasse-card .fcta .btn { height: 42px; font-size: 15px; font-weight: 600; padding: 0 22px; width: auto; }
.kasse-card .hint { font-size: 12.5px; color: var(--ink-3); margin: 8px 0 0; line-height: 1.4; }
.kasse-card details.hint-more { margin-top: 4px; }
.kasse-card details.hint-more summary { font-size: 13px; color: var(--ink-2); font-weight: 400; list-style: none; cursor: pointer; }
.kasse-card details.hint-more summary::-webkit-details-marker { display: none; }
.kasse-card details.hint-more summary::before { content: "\25B8 "; }
.kasse-card details.hint-more[open] summary::before { content: "\25BE "; }
.kasse-card details.hint-more summary:hover { color: var(--ink); }
.kasse-card details.hint-more p { font-size: 13px; color: var(--ink-2); margin: 6px 0 0; }

@media (max-width: 640px) {
  .kpi-grid.kasse-kpi { grid-template-columns: 1fr; gap: 12px; }
  .kasse-kpi .kpi-value { font-size: 22px; }
  .kasse-card { padding: 16px; }
  .kasse-card.slim { padding: 12px 16px; }
  .kasse-card .period { display: block; }
  .kasse-card .period .plabel { display: block; font-size: 16px; margin-bottom: 10px; }
  .kasse-card .period .dash { display: none; }
  .kasse-card .period .pf { display: block; margin-bottom: 12px; }
  .kasse-card .period .pcap { display: block; }
  .kasse-card .period input[type="date"] { width: 100%; height: 44px; }
  .kasse-card .period .btn { margin-left: 0; width: 100%; height: 44px; }
  .kasse-card .fgrid { grid-template-columns: 1fr; }
  .kasse-card .field input, .kasse-card .field select { height: 44px; }
  .kasse-card .fcta .btn { width: 100%; height: 44px; }
}

/* ===========================================================================
   Итерация 16 точечного прохода редизайна — «Авто» (/avto) дожата по мокапам
   пользователя auto-desktop/-mobil (Claude Design, 16.08.2026; десктопный
   файл — более поздняя редакция того же CSS: .sub-note 13px, кнопка периода
   margin-left 8px; его .zk-scrollhint по решению пользователя 17.08 не
   переносится — подсказки «прокручивается вбок» нигде нет). Итерация 10
   портировала структуру, здесь — остатки сверки 16.08 (docs/superpowers/
   plans/2026-08-16-kohle-ops-mobil-gap-check.md, «Авто»): карточка периода
   без h2 с подписями «С»/«По», hint оценки, паддинг карточки формы на
   телефоне, ритм рядов/CTA, h2 16px, .sub-note, summary «Подробнее», шаг
   карточек. Скоуп — `.avto-wrap` (у страницы уже есть, Итерация 10);
   разделяемые классы (.fcard/.fcta/.fgrid/.sub-note/.period/details) в
   других разделах не задеты. Та же дисциплина, что «Касса» (Итерация 15):
   специфичность (0,2,x)+, чтобы перебить и базу, и её @media-перезаписи. */

/* --- Карточки: паддинг мокапа (18px 20px / телефон 16 — `.fcard{20px 24px}`
   Итерации 6 стоит в файле ПОСЛЕ мобильного `.card{padding:16px}` и на 390
   побеждал), h2 16px mb 10, шаг между карточками — базовый .card mb 14. */
.avto-wrap .fcard { padding: 18px 20px; }
.avto-wrap .card h2 { font-size: 16px; margin: 0 0 10px; }
.avto-wrap .fblock { margin-bottom: 14px; }
.avto-wrap .fblock-last { margin-bottom: 0; }
.avto-wrap .field label { font-size: 13.5px; }
.avto-wrap .fcta { margin-top: 14px; }
.avto-wrap .fcta .btn { padding: 0 22px; }
/* hint оценки по прямой (между рядами «Куда» и «Одометр»): мокап
   `.hint{12.5px;--ink-3;lh 1.4}` + inline `margin:-4px 0 10px`. */
.avto-wrap .km-hint { font-size: 12.5px; color: var(--ink-3); line-height: 1.4; margin: -4px 0 10px; }
/* .sub-note (десктопная редакция мокапа: 13px --ink-3) + inline
   `margin:-4px 0 8px` у обоих мест страницы. */
.avto-wrap .sub-note { font-size: 13px; color: var(--ink-3); line-height: 1.4; margin: -4px 0 8px; }
.avto-wrap details.hint-more { margin-top: 6px; }
.avto-wrap details.hint-more summary { font-size: 13px; color: var(--ink-2); font-weight: 400; list-style: none; cursor: pointer; }
.avto-wrap details.hint-more summary::-webkit-details-marker { display: none; }
.avto-wrap details.hint-more summary::before { content: "\25B8 "; }
.avto-wrap details.hint-more[open] summary::before { content: "\25BE "; }
.avto-wrap details.hint-more summary:hover { color: var(--ink); }
.avto-wrap details.hint-more p { font-size: 13px; color: var(--ink-2); margin: 6px 0 0; }

/* --- Строка периода: та же разметка/CSS, что «Касса» (Итерация 15) — поверх
   базового .period Итерации 10 (там `.period label` 13px --ink-2 и кнопка
   margin-left:auto). */
.avto-wrap .period { gap: 10px; }
.avto-wrap .period .plabel { font-size: 13.5px; font-weight: 600; }
.avto-wrap .period .pf { display: flex; align-items: center; }
.avto-wrap .period .pcap { display: none; font-size: 13px; font-weight: 600; color: var(--ink); margin-bottom: 6px; }
.avto-wrap .period .dash { color: var(--ink-3); }
.avto-wrap .period .btn { height: 38px; padding: 0 16px; margin-left: 8px; }

@media (max-width: 640px) {
  .avto-wrap .fcard { padding: 16px; }
  /* мокап ломает .fgrid в одну колонку на 640 (общий .fgrid — на 480). */
  .avto-wrap .fgrid { grid-template-columns: 1fr; }
  .avto-wrap .period { display: block; }
  .avto-wrap .period .plabel { display: block; font-size: 16px; margin-bottom: 10px; }
  .avto-wrap .period .dash { display: none; }
  .avto-wrap .period .pf { display: block; margin-bottom: 12px; }
  .avto-wrap .period .pcap { display: block; }
  .avto-wrap .period input[type="date"] { width: 100%; height: 44px; }
  .avto-wrap .period .btn { margin-left: 0; width: 100%; height: 44px; }
}

/* ===========================================================================
   Итерация 17 точечного прохода редизайна — «Финансы» (/finanzen) по
   мокапу пользователя finanzen-desktop/-mobil (Claude Design, сохранён
   16.08.2026; один файл — раскладку переключает брейкпоинт 900 (KPI) и
   640 (всё остальное), та же дисциплина, что «Касса»/«Авто» (Итерации
   15/16).

   Нумерация: в брифе контроллера страница названа «итерация 15» — на этой
   ветке номер 15 уже занят «Кассой», 16 — «Авто» (заголовки секций выше,
   tests/test_design_iteration15.py — это КАССА, не «Финансы»). Здесь и в
   имени файла сторожей взят следующий свободный номер по факту слияний
   этой ветки (17) — чтобы не переписывать чужие тесты и не путать нумерацию.

   Переиспользовано: .period/.pf/.pcap (Итерация 10/15/16 — здесь третий
   .pf добавляет фильтр категории, которого у /kasse и /avto нет),
   .kpi-grid.cols-3 (Task 9 — уже нёс ровно 6 kpi-card «Финансов»),
   .fblock/.fgrid/.field/.fcta (Итерация 4/6/15/16), .select-arrow
   (Итерация 9/14), .ops-empty (Итерация 6/10), details.hint-more
   (R5/15/16), .badge/.badge-neutral (пилюли статусов), .btn-quiet
   (Итерация 14, /sklad «Закупки»), .tscroll (Итерация 7/10/14). Скоуп
   нового CSS — `.finanzen-card` (второй класс на каждой `.card`, тот же
   приём, что `.kasse-card`): сквозные компоненты в остальных разделах не
   задеты.

   Новые классы мокапа без готового аналога — заведены здесь: .only-d/
   .only-m (утилити «на десктопе один текст / на телефоне короче», НЕ
   скоуплены — понадобятся другим экранам) заведены, но в разметке этой
   странице НЕ применены к самим kpi-label/kpi-note: десяток существующих
   регрессионных сторожей (test_phase2.py/test_final_fix.py, `_kpi_wert()`
   — «ЛЕЙБЛ</div>» встык к следующему `<div class="kpi-value…">», и
   test_design_redesign.py: `test_t9_finanzen_lange_kpi_delta_pillen_
   wurden_zu_kpi_note`/`test_t9_finanzen_ergebnis_karte_zweite_notiz_in_
   details` — требуют текст лейбла/сноски БУКВАЛЬНО, без обёртки `<span>`
   между открывающим `<div>` и текстом. Мокапное укорачивание текста на
   телефоне — чисто косметическая деталь; ослаблять больше десятка чужих
   регрессионных проверок ради неё не стали (сознательное узкое отступление
   от буквы мокапа, см. коммит/отчёт итерации). .mrow/.mwarn/.mnote/.tdiv/
   .bigval/.subnote/.formdiv/.fcta-note — скоуплены `.finanzen-card` (роли
   специфичны для карточек «Финансов»). table.pf — БУКВАЛЬНО из мокапа
   (fixed-раскладка, colgroup, ellipsis) только у «Плана и факта» —
   ЕДИНСТВЕННОЙ таблицы, которую мокап рисует с примером строк (8 узких
   числовых колонок фиксированной мокапом ширины никогда не переполняются
   реальными данными: месяц/№/три кг сценария/факт кг/факт выручка/
   клиентов). Таблицы «Дебиторка»/«Расходы»/«Личные деньги»/«Продажи» мокап
   рисует ТОЛЬКО пустыми (.ops-empty) — типографику table.pf им даёт
   модификатор `table.pf-liste` (тот же шрифт/паддинг/44px-строка), а
   раскладку — `table-layout: auto` + `.num` на числовых колонках (урок
   Итерации 14d/table.zk: fixed+ellipsis-всюду молча резал реальные суммы/
   даты «Закупок» — auto + ellipsis ТОЛЬКО на свободном тексте с `title=`,
   тот же приём, что «Поставщик» там же). Стартовые ширины colgroup —
   заданы инлайн на `<col>` (тот же приём, что «План и факт» в самом
   мокапе), подобраны по самой длинной подписи каждой колонки (категория/
   тип — до 53 символов, «Личное изъятие — учёт для Jobcenter
   (Privatentnahme)») и уточнены браузерным прогоном Итерации 17 (см.
   коммит).

   Регрессионные сторожа СТАРШИХ фаз (H-17/H-18 в test_hygiene.py,
   test_p33_… и test_h9_… в test_phase3.py/test_hygiene.py) пинают
   БУКВАЛЬНЫЕ атрибуты трёх ячеек таблицы «План и факт» — `class="num
   is-fakt m-val" data-label="Факт, кг"`, `class="num money is-fakt m-sub
   {{ 'm-hide' if not zeile.fakt }}" data-label="Факт, выручка"`, `class="num
   m-sub {{ 'm-hide' if not zeile.fakt }}" data-label="Клиентов, факт"`, и
   класс `row-zukunft` на приглушённой строке (НЕ переименован в `future`
   мокапа — семантически то же приглушение, `tr.row-zukunft{opacity:.55}`
   ниже по файлу, класс не удалён). `m-val`/`m-sub`/`m-hide` не несут
   собственного CSS вне `table.data-table` (эта таблица — `table.pf`) —
   классы оставлены ИНЕРТНЫМИ ради тех тестов, визуально ничего не меняют.
   `data-label="…"` оставлены на ВСЕХ ячейках всех пяти таблиц (тот же
   приём, что `table.mt`/`table.zk` — не data-table, но data-label несут),
   ради project-wide сторожа `test_alle_dmy_zellen_in_tabellen_haben_dt`
   (КАЖДАЯ `<td>` с `| dmy` обязана нести `class="dt"` — соблюдено на всех
   пяти таблицах этой страницы).

   Решение пользователя 17.08.2026 (см. секцию «.zk-scrollhint» выше по
   файлу и Итерацию 16b): подсказку «Таблица прокручивается вбок →» мокап
   ещё рисует (сохранён 16.08, ДО решения) — здесь её нет нигде, та же
   причина, что у /avto и /sklad (сторож test_design_iteration16.py:
   test_i16_keine_scrollhinweis_phrase_in_keinem_template — по ВСЕМ
   шаблонам, включая этот, без исключений). */

.only-m { display: none; }
/* Мобильный флип .only-m/.only-d — ВНУТРИ единственного мобильного блока
   секции, в самом низу (НЕ отдельным блоком здесь): позиционный сторож
   test_design_iteration17.py ищет ПЕРВОЕ вхождение брейкпоинта 640 в
   секции и читает от него до конца файла секции — второй, более ранний
   блок дал бы ложное совпадение. */

/* --- Карточки «Финансов»: паддинг/h2 мокапа (тот же паддинг, что «Касса»,
   h2 margin — тот же, что «Авто»). */
.finanzen-card { padding: 18px 22px; }
.finanzen-card.slim { padding: 12px 22px; }
.finanzen-card h2 { font-size: 16px; margin: 0 0 10px; }

/* --- Строка периода: .period базовый (Итерация 10) + ТРЕТИЙ `.pf` для
   фильтра категории (которого у /kasse и /avto нет) — тот же приём
   скрытых на десктопе `.pcap`/видимых на телефоне, что «Касса»/«Авто». */
.finanzen-card .period { gap: 10px; }
.finanzen-card .period .plabel { font-size: 13.5px; font-weight: 600; }
.finanzen-card .period .pf { display: flex; align-items: center; }
.finanzen-card .period .pcap { display: none; font-size: 13px; font-weight: 600; color: var(--ink); margin-bottom: 6px; }
.finanzen-card .period .dash { color: var(--ink-3); }
.finanzen-card .period .btn { height: 38px; padding: 0 16px; margin-left: 8px; }
.finanzen-card .period select { height: 38px; min-width: 180px; }

/* --- Формы «Финансов» (Новый расход / Новая запись): плотность мокапа
   поверх общих .fblock/.fcta/.field (тот же приём и те же числа, что
   «Касса»/«Авто»). */
.finanzen-card .fsec { font-size: 12px; text-transform: uppercase; letter-spacing: .05em; margin: 0 0 12px; }
.finanzen-card .fblock { margin-bottom: 14px; }
.finanzen-card .field label { font-size: 13.5px; }
.finanzen-card .field input, .finanzen-card .field select { height: 42px; border-radius: 8px; }
.finanzen-card .field .hint { font-size: 12.5px; color: var(--ink-3); margin-top: 6px; line-height: 1.4; }
.finanzen-card .fcta { margin-top: 12px; }
.finanzen-card .fcta .btn { height: 42px; font-size: 15px; font-weight: 600; padding: 0 22px; width: auto; }
.finanzen-card .fcta-note { font-size: 12.5px; color: var(--ink-3); margin-top: 12px; line-height: 1.4; font-weight: 400; }
.finanzen-card details.hint-more { margin-top: 4px; }
.finanzen-card details.hint-more summary { font-size: 13px; color: var(--ink-2); font-weight: 400; list-style: none; cursor: pointer; }
.finanzen-card details.hint-more summary::-webkit-details-marker { display: none; }
.finanzen-card details.hint-more summary::before { content: "\25B8 "; }
.finanzen-card details.hint-more[open] summary::before { content: "\25BE "; }
.finanzen-card details.hint-more summary:hover { color: var(--ink); }
.finanzen-card details.hint-more p { font-size: 13px; color: var(--ink-2); margin: 6px 0 0; }

/* --- Карточные под-заметки мокапа: .mrow (строка-подсказка под h2, пара
   с hint-more), .mwarn (предупреждающий цвет ВНУТРИ .mrow — не отдельный
   баннер), .mnote (статус/итог без hint-more рядом), .tdiv (тонкий
   разделитель ПЕРЕД таблицей «Плана и факта», отступ мокапа). */
.finanzen-card .mrow { font-size: 12.5px; color: var(--ink-3); line-height: 1.45; margin-bottom: 4px; }
.finanzen-card .mrow .mwarn { color: var(--warn); }
.finanzen-card .mnote { font-size: 13px; color: var(--ink-2); margin: 8px 0 0; }
.finanzen-card .tdiv { border-top: 1px solid var(--line); margin: 12px -22px 0; padding: 0 22px; }
.finanzen-card .formdiv { border-top: 1px solid var(--line); margin: 16px -22px 14px; padding: 0; }

/* --- table.pf — БУКВАЛЬНО мокапа: «План и факт», единственная таблица
   страницы с примером данных (см. заголовок секции). is-fakt/row-zukunft
   — унаследованные правила старших фаз (не в этой секции). */
/* min-width 900, не 860 мокапа: первая колонка («Месяц») расширена до
   190px (было 150px мокапа) — браузерный замер Итерации 17 показал clip
   «08.2026 месяц идёт» в 150px, см. колгруппу в finanzen.html. */
.finanzen-card table.pf { width: 100%; border-collapse: collapse; table-layout: fixed; min-width: 900px; margin-top: 12px; }
.finanzen-card table.pf th { font-size: 11.5px; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-3); text-align: right; font-weight: 600; padding: 6px 8px; border-bottom: 1px solid var(--line); white-space: nowrap; }
.finanzen-card table.pf th:first-child { text-align: left; }
.finanzen-card table.pf td { padding: 0 8px; border-bottom: 1px solid var(--line); font-size: 13.5px; text-align: right; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-variant-numeric: tabular-nums; }
.finanzen-card table.pf td:first-child { text-align: left; }
.finanzen-card table.pf tbody tr { height: 44px; }
.finanzen-card table.pf tbody tr:last-child td { border-bottom: none; }
/* Мокапная пилюля «месяц идёт» ЧУТЬ мельче/плотнее общей `.badge` (11.5px/
   2px 8px против общих 12px/3px 10px) — переопределение СКОУПЛЕНО этой
   таблицей, остальные badge-neutral (статусы Отменён/Активен на других
   таблицах страницы, «Активен»/«Отменена» на /kasse и т.д.) не задеты. */
.finanzen-card table.pf .badge-neutral { font-size: 11.5px; padding: 2px 8px; margin-left: 8px; vertical-align: 1px; color: var(--ink-2); }

/* --- table.pf-liste — та же типографика table.pf (шрифт/паддинг/строка
   44px/uppercase-шапка), но `table-layout: auto` + `.num` для числовых
   колонок (см. объяснение в шапке секции) — 4 таблицы страницы, которые
   мокап рисует только пустыми: «Дебиторка»/«Расходы»/«Личные деньги»/
   «Продажи». `.trunc` — узкий эквивалент `table.zk td.lieferant`
   (свободный текст, ellipsis + `title=` в шаблоне, НЕ на суммах/датах).

   Итерация 19 (17.08.2026): «Открытые заказы» kunde.html переиспользует
   ЭТУ же типографику (мокап карточки клиента таблицу не рисует —
   инструкция контроллера «в том же языке, что table.pf-liste из
   Финансов» — см. app/templates/kunde.html) — селектор РАСШИРЕН до
   `.kunde-karte`, третьей копии деклараций не заводим (тот же приём, что
   `.mbody input, .fcard input` в секции «Итерация 6»).

   Раздел «Долг» (25.08.2026, личный заём владельца): тот же жанр — выписка
   строк с датой, суммой и статусом, — и тот же довод: он обязан выглядеть
   так же, как «Личные деньги и займы» на «Финансах», рядом с которыми и
   живёт по смыслу. Селектор расширен до `.dolg-card`, деклараций снова не
   копируем. */
.finanzen-card table.pf-liste, .kunde-karte table.pf-liste, .dolg-card table.pf-liste { table-layout: auto; width: 100%; border-collapse: collapse; margin-top: 12px; }
.finanzen-card table.pf-liste th, .kunde-karte table.pf-liste th, .dolg-card table.pf-liste th { font-size: 11.5px; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-3); text-align: left; font-weight: 600; padding: 6px 6px; border-bottom: 1px solid var(--line); white-space: nowrap; }
.finanzen-card table.pf-liste th.num, .kunde-karte table.pf-liste th.num, .dolg-card table.pf-liste th.num { text-align: right; }
.finanzen-card table.pf-liste td, .kunde-karte table.pf-liste td, .dolg-card table.pf-liste td { padding: 0 6px; border-bottom: 1px solid var(--line); font-size: 13.5px; text-align: left; white-space: nowrap; vertical-align: middle; }
.finanzen-card table.pf-liste td.num, .kunde-karte table.pf-liste td.num, .dolg-card table.pf-liste td.num { text-align: right; font-variant-numeric: tabular-nums; }
.finanzen-card table.pf-liste td.trunc, .kunde-karte table.pf-liste td.trunc, .dolg-card table.pf-liste td.trunc { max-width: 160px; overflow: hidden; text-overflow: ellipsis; }
.finanzen-card table.pf-liste tbody tr, .kunde-karte table.pf-liste tbody tr, .dolg-card table.pf-liste tbody tr { height: 44px; }
.finanzen-card table.pf-liste tbody tr:last-child td, .kunde-karte table.pf-liste tbody tr:last-child td, .dolg-card table.pf-liste tbody tr:last-child td { border-bottom: none; }
/* Строка «Оплачен» в таблице расходов — компактно, одной строкой (те же
   размеры, что приход на «Складе»: дата 106, селект по содержимому, 13px);
   base .bezahlt-form даёт min-width 130 на оба поля и flex-wrap. */
.finanzen-card .bezahlt-form { flex-wrap: nowrap; gap: 6px; }
.finanzen-card .bezahlt-form input[type="date"] { width: 106px; min-width: 0; font-size: 13px; padding: 0 6px; }
.finanzen-card .bezahlt-form select { min-width: 0; width: 132px; font-size: 13px; padding-right: 26px; }
.finanzen-card .bezahlt-form .btn { padding: 0 10px; font-size: 13px; }
.finanzen-card table.pf-liste .badge { font-size: 11px; padding: 2px 6px; }

/* --- Кредиторка: `.bigval` (итог без подписи-обёртки kpi-*, мокап рисует
   его отдельно от kpi-grid) + `.subnote` (роль — как у sub-note /avto, но
   своё имя мокапа: своя карточка, не разделяемый компонент). */
.finanzen-card .bigval { font-size: 24px; font-weight: 650; margin: 2px 0 4px; }
.finanzen-card .subnote { font-size: 13px; color: var(--ink-3); line-height: 1.45; }

@media (max-width: 640px) {
  .only-m { display: inline; }
  .only-d { display: none; }
  .finanzen-card.slim { padding: 12px 16px; }
  .finanzen-card .period { display: block; }
  .finanzen-card .period .plabel { display: block; font-size: 16px; margin-bottom: 10px; }
  .finanzen-card .period .dash { display: none; }
  .finanzen-card .period .pf { display: block; margin-bottom: 12px; }
  .finanzen-card .period .pcap { display: block; }
  .finanzen-card .period input[type="date"] { width: 100%; height: 44px; }
  .finanzen-card .period select { width: 100%; height: 44px; }
  .finanzen-card .period .btn { margin-left: 0; width: 100%; height: 44px; }
  .finanzen-card .fgrid { grid-template-columns: 1fr; }
  .finanzen-card .field input, .finanzen-card .field select { height: 44px; }
  .finanzen-card .fcta .btn { width: 100%; height: 44px; }
  .finanzen-card .tdiv, .finanzen-card .formdiv { margin-left: -16px; margin-right: -16px; }
  .finanzen-card .tdiv { padding: 0 16px; }
}

/* ===========================================================================
   Итерация 18 точечного прохода редизайна (17.08.2026) — «Настройки»
   (/einstellungen, einstellungen.html) по мокапу пользователя
   einstellungen-desktop/-mobil (Claude Design, сохранён 17.08.2026 как
   C:/tmp/kohle-redesign/einstellungen-mockup.html; один файл — раскладку
   переключает брейкпоинт 640px). Функциональность НЕ меняется — переставлена
   и переоформлена только разметка/CSS (сторож name/id-set — tests/
   test_design_iteration18.py; action/method/hidden форм — те же).

   Переиспользовано: .wrap (Итерация 6, /bestellungen — та же 1040px
   колонка), .fblock/.fgrid/.field/.fcta (Итерация 4/6/15/16/17),
   details.hint-more (R5 и старшие фазы), .tscroll (Итерация 7/10/14/17),
   .ops-empty (Итерация 6/10), .num/input.num (утилити tabular-nums/
   право-выравнивание — уже глобальные, задача 9). Скоуп плотности —
   `.einst-card` (второй класс на каждой `.card`, тот же приём, что
   `.finanzen-card`/`.kasse-card`/`.avto-wrap`).

   Новые классы мокапа без готового аналога — заведены здесь БАРЕ
   (глобально, НЕ скоуплены `.einst-card` — тот же приём, что table.zk/
   table.mt/table.rttable выше: имя достаточно уникально, другой странице
   не понадобится раньше своей редактуры, см. test_hygiene.py:
   TABELLEN_EIGENE_KLASSE["einstellungen.html"] = {"hist"}): table.hist
   («История изменений» — fixed, colgroup, свой мобильный язык — .tscroll,
   горизонтальный скролл, НЕ m-title/m-val-контракт), details.acc
   («Пользователи и пароли» — аккордеон с вращающимся глифом «▸»),
   .sync-* («Синхронизация» — подпись/значение/техническая моно-строка/
   сноска вокруг существующей формы POST /einstellungen/sync).

   Отличие ОТ старших карточек: здесь details.hint-more — ИНЛАЙН («▸
   Подробнее» дословно в тексте `<summary>`, встык к предложению подсказки;
   мокап рисует его без отдельной строки и без CSS-глифа ::before — в
   отличие от «Кассы»/«Финансов»/«Авто», где summary — блок ПОСЛЕ .hint, а
   «▸»/«▾» рисует ::before). `.einst-card details.hint-more` получает
   display: inline — единственная страница с этим вариантом, поэтому
   скоуплена отдельно, а не переопределяет общий (блочный) details.hint-more
   других разделов.

   Технические добавки СВЕРХ буквы мокапа (мокап их не пишет, но без них
   баг): `::-webkit-details-marker{display:none}` — и на .hint-more
   summary, и на .acc summary — без сброса `list-style:none` не гасит
   нативный треугольник в Chrome/Safari (тот же сброс уже стоит у ВСЕХ
   старших .hint-more — .kasse-card/.finanzen-card/.avto-wrap выше);
   без него была бы двойная стрелка. `.zk-scrollhint` НЕ заведён и не
   отрисован — решение пользователя 17.08.2026 действует по всем шаблонам
   без исключений (см. секцию «.zk-scrollhint» выше и test_design_
   iteration16.py: test_i16_keine_scrollhinweis_phrase_in_keinem_template);
   сам мокап тоже не рисует ни одного div.zk-scrollhint в теле — инертное
   правило `.zk-scrollhint{display:none}` в его <style> это то же
   наследие общего шаблона мокапов, что h1.page-title:28px, `.card{padding:
   18px 22px}`-боилерплейт и белый инпут-фон буквальным hex-цветом — их не
   применила ни одна из старших итераций (finanzen-mockup.html несёт те
   же строки, задел Итерации 17 их тоже не тронул) — отсутствие подсказки
   на «Настройках» СОВПАДАЕТ с буквой мокапа, не отступление от неё. */

.einst-card { padding: 18px 22px; }
.einst-card h2 { font-size: 16px; margin: 0 0 14px; }
.einst-card .fblock { margin-bottom: 18px; }
.einst-card .field label { font-size: 13.5px; }
.einst-card .field input { height: 42px; }
.einst-card .hint { font-size: 12.5px; color: var(--ink-3); margin-top: 6px; line-height: 1.4; }
.einst-card .fcta { margin-top: 14px; }
.einst-card .fcta .btn { height: 42px; font-size: 15px; font-weight: 600; padding: 0 22px; width: auto; }
.einst-card details.hint-more { display: inline; }
.einst-card details.hint-more summary { font-size: 12.5px; color: var(--ink-2); cursor: pointer; list-style: none; display: inline; }
.einst-card details.hint-more summary::-webkit-details-marker { display: none; }
.einst-card details.hint-more summary:hover { color: var(--ink); }

/* --- Порт на дев-ветку (17.08.2026, схема v12/Фаза 5A): раздел «Налоговый
   режим» (einstellungen.html) несёт `<select>`/`.btn-toggle-group` внутри
   `.field` — прод (v7) их в этой карточке не имеет вовсе, готового правила
   нет. Та же плотность 42/44px, что у текстовых полей карточки выше — тот
   же приём, что `.kasse-card .field select`/`.finanzen-card .field select`
   (высота у type=text/select/.btn-toggle без скоупа общая — var(--control-h),
   44px — здесь плотнее, вровень с соседними полями). */
.einst-card .field select { height: 42px; border-radius: 8px; }
/* 17.08.2026 (замечание владельца «выровняй текст по центру»): у кнопок
   режима цены здесь `min-height`, а не `height`. Подпись «БРУТТО — НДС
   внутри» в кнопку шириной ~154 px (телефон) не влезает в одну строку и
   переносится; при ЖЁСТКОЙ высоте две строки не помещались в 42/44 px —
   текст упирался в края и читался как съехавший, хотя центрирование
   (`text-align`/`justify-content`/`align-items`) стояло с самого начала.
   Лечится высотой, а не выравниванием: цель нажатия та же (42/44 px — пол),
   кнопка растёт под вторую строку, и текст центрируется по вертикали
   честно. Специфичность (0,3,0) выше общего `.btn-toggle` (0,1,0) — именно
   это правило и перебивало `min-height`, добавленный там. */
.einst-card .field .btn-toggle { height: auto; min-height: 42px; }

/* --- table.hist — «История изменений»: fixed, colgroup 140|auto|110|110
   (Когда/Что/Было/Стало), «Что» — ellipsis + title= (свободный текст,
   длина подписи поля не гарантирована), «Когда»/«Было»/«Стало» —
   tabular-nums (переиспользован общий утилити-класс .num). */
table.hist { width: 100%; border-collapse: collapse; table-layout: fixed; min-width: 640px; }
.hist th { font-size: 11.5px; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-3); text-align: left; font-weight: 600; padding: 6px 8px; border-bottom: 1px solid var(--line); white-space: nowrap; }
.hist th.num { text-align: right; }
.hist td { padding: 0 8px; border-bottom: 1px solid var(--line); font-size: 13.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.hist td.num { text-align: right; }
.hist tbody tr { height: 42px; }
.hist tbody tr:last-child td { border-bottom: none; }
.hist td.dt { color: var(--ink-2); }

/* --- «Синхронизация»: подпись/значение/техническая строка/сноска вокруг
   формы POST /einstellungen/sync (форма сохранена, hidden form_token). */
.sync-when { font-size: 12px; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-2); font-weight: 600; margin-bottom: 2px; }
.sync-val { font-size: 18px; font-weight: 650; margin-bottom: 6px; }
.sync-tech { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; color: var(--ink-3); margin-bottom: 12px; line-height: 1.5; word-break: break-word; }
.sync-note { font-size: 12.5px; color: var(--ink-3); margin-top: 12px; line-height: 1.4; }
.sync-btn { height: 40px; font-size: 14.5px; font-weight: 600; padding: 0 18px; }

/* --- «Пользователи и пароли»: аккордеон с вращающимся «▸» (details.acc) —
   содержимое (docker-команда, рунбук) внутри не тронуто. */
details.acc summary { font-size: 16px; font-weight: 600; cursor: pointer; list-style: none; display: flex; align-items: center; gap: 8px; }
details.acc summary::-webkit-details-marker { display: none; }
details.acc summary::before { content: "\25B8"; font-size: 13px; color: var(--ink-2); transition: transform .15s; }
details.acc[open] summary::before { transform: rotate(90deg); }

@media (max-width: 640px) {
  .einst-card .fgrid { grid-template-columns: 1fr; gap: 0; }
  .einst-card .fgrid .field { margin-bottom: 18px; }
  .einst-card .fgrid .field:last-child { margin-bottom: 0; }
  .einst-card .field input { height: 44px; }
  .einst-card .field select { height: 44px; }
  /* см. врезку у .einst-card .field .btn-toggle выше: на телефоне подпись
     переносится на две строки, поэтому пол высоты, а не жёсткая высота. */
  .einst-card .field .btn-toggle { height: auto; min-height: 44px; }
  .einst-card .fcta .btn { width: 100%; height: 44px; }
  .sync-btn { width: 100%; height: 44px; }
}

/* ===========================================================================
   Итерация 19 точечного прохода редизайна (17.08.2026) — карточка клиента
   (/kunden/{id}, app/templates/kunde.html) и страница редактирования
   (/kunden/{id}/bearbeiten, app/templates/kunde_form.html) по мокапам
   пользователя (Claude Design, сохранены 17.08.2026 как C:/tmp/kohle-
   redesign/kunde-karte-mockup.html и kunde-edit-mockup.html — один файл на
   экран, мобайл = медиа-правила ≤640 того же файла).

   Оба мокапа несут ОБЩИЙ boilerplate-заголовок генератора (`:root{--bg;
   --line}`, `html,body{overflow-x:hidden}`, `.main{min-width:0}`,
   `.card{padding:18px 22px}`, `h1.page-title{font-size:28px}`, литеральный
   input-фон/рамка буквальным hex-цветом) — тот же самый, что уже несли
   einstellungen-/finanzen-mockup.html (секция «Итерация 18» выше явно
   называет три из этих строк боилерплейтом и подтверждает: ни одна
   старшая итерация их не применила). Правило то же здесь: где мокап
   ЭТОГО экрана поверх
   боилерплейта не добавляет своего значения — боилерплейт молча
   игнорируется (карточка клиента остаётся на глобальных `.card{padding:
   18px 20px}`/`h1.page-title` 22px<900px-скрыт), а где добавляет
   (kunde-edit-mockup.html несёт ВТОРОЕ `.card{padding:22px 24px}` ПОСЛЕ
   боилерплейта, `.wrap{max-width:1100px}` ≠ стандартных 1040) —
   применяется буквально, скоуплено `.kunde-edit`.

   .kunde-karte — второй класс на корневой обёртке kunde.html (тот же
   приём, что .avto-wrap/.kasse-card/.finanzen-card/.einst-card старших
   итераций). .kunde-edit — второй класс на `.wrap` СТРАНИЦЫ /kunden/{id}/
   bearbeiten (kunde_form.html, ветка modus=='bearbeiten' ТОЛЬКО — ветка
   modus=='neu', она же no-JS фоллбэк модалки «Новый клиент» kunden.html,
   этим мокапом не рисуется и не тронута, разница явно прокинута через
   Jinja-условие в самом kunde_form.html, не через CSS-скоуп).
   =========================================================================== */

/* --- kunde.html: .page-head делит класс с heute.html (Итерация 3, 2 кнопки)
   и kunden.html (Итерация 7, 1 кнопка) — там `align-items:flex-end`
   (короткая шапка), здесь мокап хочет `flex-start` (шапка клиента — h1 +
   адрес/телефон + статус-строка, три visual-строки против одной короткой
   кнопки справа) — отличие СКОУПЛЕНО `.kunde-karte .page-head`, общий
   `.page-head` не тронут (heute.html/kunden.html проверены браузером —
   не сдвинулись). */
.kunde-karte .page-head { align-items: flex-start; }

.kunde-karte .khead-meta { font-size: 13.5px; color: var(--ink-2); margin-top: 4px; }
.kunde-karte .khead-status { margin-top: 8px; display: flex; gap: 8px; align-items: center; font-size: 13px; color: var(--ink-3); }

/* --- «Данные»: .dgrid/.dt2 — двухколоночный список подпись/значение
   (мокап), заменяет прежний `.form-grid.cols-2`. `min-width:0` на `.dt2`
   ОБЯЗАТЕЛЕН — без него ellipsis `.v` не срабатывает (grid-элемент по
   умолчанию не сжимается меньше min-content своего текста, длинный URL
   в Сайт/Карта раздвинул бы `.card` вбок — тот же класс бага, что уже
   объясняет комментарий `.wrap-anywhere` выше по файлу, здесь решён
   ellipsis-контейнером вместо переноса). */
.kunde-karte .dgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 7px 24px; }
.kunde-karte .dt2 { min-width: 0; }
.kunde-karte .dt2 .l { font-size: 12.5px; color: var(--ink-2); margin-bottom: 1px; font-weight: 600; }
.kunde-karte .dt2 .v { font-size: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.kunde-karte .dt2.full { grid-column: 1 / -1; }
.kunde-karte .dt2.full .v { white-space: normal; }

.kunde-karte .badge-mini { font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 999px; background: var(--neutral-soft); color: var(--ink-2); vertical-align: 1px; margin-left: 6px; }

/* Ссылки внутри карточки клиента (Сайт/Карта) — приглушённый ink-2 вместо
   акцентного brand-цвета (буква мокапа); других голых `<a>` на странице
   нет («Продать»/«Продажа»/«Отменить» — `.btn`/`.btn-quiet`, специфичнее
   и не задеты). */
.kunde-karte a { color: var(--ink-2); }
.kunde-karte a:hover { color: var(--ink); }

/* --- «Заметки»: .notes-form — форма комментария (POST /kunden/{id}/
   kommentar, name="text" не переименован — функциональность 1:1),
   .notes-empty — пустое состояние (мокап, замена прежнего .empty-state,
   тот же приём, что avto.html уже проделал для .ops-empty). */
.kunde-karte .notes-form label { display: block; font-size: 13.5px; font-weight: 600; margin-bottom: 6px; }
.kunde-karte .notes-form textarea { width: 100%; min-height: 76px; resize: vertical; padding: 10px 12px; }
.kunde-karte .notes-form .btn { height: 40px; font-size: 14.5px; font-weight: 600; padding: 0 20px; margin-top: 10px; }
.kunde-karte .notes-empty { font-size: 12.5px; color: var(--ink-3); margin-top: 10px; }

/* --- «Опасная зона»: .danger-acts/.danger-note — замена прежних
   `.form-actions`/`<p class="hint">`. `.btn-danger` уже совпадает с
   мокапом (белый фон, красная рамка/текст) — глобальное правило секции
   «Кнопки» проверено, override не нужен. */
.kunde-karte .danger-acts { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; }
.kunde-karte .danger-acts .btn { height: 38px; font-size: 14px; padding: 0 16px; }
.kunde-karte .danger-note { font-size: 12.5px; color: var(--ink-3); line-height: 1.4; }

@media (max-width: 640px) {
  /* Шапка клиента несёт 3 действия («Продать»/«+ Заказ»/«Редактировать»)
     — heute.html/kunden.html (общий `.page-head .btn{flex:1;height:44px}`)
     не поднимаются выше 2. `calc(50% - 4px)` даёт «2 по 44» на первой
     строке (сумма двух базисов + gap 8px = 100%, идентично прежнему
     `flex:1` при РОВНО двух кнопках — heute.html визуально не меняется),
     третья кнопка не помещается на первую строку и переносится
     `flex-wrap:wrap` (общий `.page-head .acts`) — `:nth-child(3)` растягивает
     её на всю вторую строку. Скоуплено `.kunde-karte` — heute.html (2
     кнопки)/kunden.html (1 кнопка) проверены браузером, не задеты. */
  .kunde-karte .page-head .btn { flex: 1 1 calc(50% - 4px); padding: 0 12px; }
  .kunde-karte .page-head .acts .btn:nth-child(3) { flex: 1 1 100%; }
  .kunde-karte .dgrid { grid-template-columns: 1fr; gap: 7px; }
  .kunde-karte .khead-meta .sep { display: none; }
  .kunde-karte .khead-meta .adr, .kunde-karte .khead-meta .tel { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .kunde-karte .notes-form textarea { font-size: 16px; }
  .kunde-karte .notes-form .btn { width: 100%; height: 44px; }
  /* Технически сверх буквы мокапа (мокап не пишет мобильный override для
     .danger-acts вовсе — на 640px кнопки «В архив»/«Удалить» остались бы
     на буквальных 38px из десктопного правила): браузерная проверка
     (Playwright, 390px) намерила 38px — ниже 44px тач-цели, обязательной
     на телефоне ВЕЗДЕ по проекту (Task 5 R1, «Global Constraint, палец»;
     тот же класс техдобавки, что уже даёт .select-arrow компенсацию
     appearance:none — без неё баг, не отступление от мокапа). Опасная
     зона — не повод для МЕНЬШЕЙ тач-цели: случайный недо-тап по «Удалить»
     потребовал бы повторного прицеливания рядом с другими элементами. */
  .kunde-karte .danger-acts .btn { height: 44px; }
  /* B-08 (финальная приёмка 19.08.2026): «В архив» и «Удалить безвозвратно»
     разделяли 8px — меньше половины пальца. Тач-цели уже по 44px, но шов
     между обратимым и безвозвратным обязан быть виден и промахом. 12px —
     та же величина, что у действий строк таблиц (td.m-act) этой же правки. */
  .kunde-karte .danger-acts { gap: 12px; }
}

/* --- kunde_form.html, ветка modus=='bearbeiten' (/kunden/{id}/bearbeiten):
   .kunde-edit — второй класс на `.wrap` (страничная обёртка, как
   /bestellungen/einstellungen), несущий свою ширину 1100px (мокап,
   ≠ стандартных 1040 — единственная страница с этим числом) и свой
   `.card` 22/24 (мокап несёт ВТОРОЕ, более специфичное объявление
   `.card{padding:22px 24px}` ПОСЛЕ общего боилерплейта 18/22 — та же
   логика чтения каскада мокапа, что решает, какое из двух правил
   реальное). Мобильный `.card{padding:16px}` УЖЕ даёт глобальное
   правило (секция «Кнопки»/каркас выше) — но т.к. правило ниже
   объявлено БЕЗ media-условия (специфичность 0,2,0 > глобальных 0,1,0
   НЕЗАВИСИМО от порядка в файле), его нужно явно вернуть на телефоне —
   иначе мобильная карточка редактирования молча осталась бы на 22/24. */
/* `.wrap.kunde-edit` (составной селектор, не голый `.kunde-edit`) — тот
   же самый элемент несёт ОБА класса (kunde_form.html: `class="wrap
   kunde-edit"`), специфичность 0,2,0 бьёт базовый `.wrap{max-width:
   1040px}` (0,1,0) НЕЗАВИСИМО от порядка в файле — тот же приём, что
   уже отвёл `.mwin-wide` от классической ловушки «кто позже, тот и
   выиграл» (см. комментарий у `.mwin-wide` выше). */
.wrap.kunde-edit { max-width: 1100px; }
.kunde-edit .card { padding: 22px 24px; }
.kunde-edit .card h2 { font-size: 16px; }

/* Поля формы — та же плотность, что модалка «Новый клиент» (#kunde-dialog,
   Итерация 9), но СВОИМИ цифрами мокапа (76px/84px textarea, 13.5px
   лейбл — местами совпадает с модалкой, местами нет, отдельная
   декларация вместо расширения #kunde-dialog: две поверхности одной
   формы стилизуются по СВОИМ, а не общим мокапам, см. предисловие
   секции). Плотность НЕ действует на /kunden/neu (та же `.field` разметка,
   другая ветка Jinja без класса `.kunde-edit`) и не на модалку
   (`#kunde-dialog`, свой ID-скоуп) — обе проверены браузером. */
.kunde-edit .fblock { margin-bottom: 16px; }
.kunde-edit .field label { font-size: 13.5px; }
.kunde-edit .field input, .kunde-edit .field select { height: 42px; text-overflow: ellipsis; }
.kunde-edit .field textarea { min-height: 84px; }
.kunde-edit .field .hint { font-size: 12.5px; margin-top: 6px; line-height: 1.4; }

.kunde-edit .fcta { display: flex; gap: 10px; margin-top: 16px; }
.kunde-edit .fcta .btn { height: 42px; font-size: 15px; font-weight: 600; padding: 0 22px; }

@media (max-width: 640px) {
  .kunde-edit .card { padding: 16px; }
  /* `.fgrid` (общий с формой продажи/«Заказов», Итерация 4/6) ломается
     на 480px — мокап этой страницы хочет один перелом с `.card` на
     640px (тот же приём, что уже развёл #kunde-dialog .fgrid от общего
     480px, секция «Итерация 9»). `gap:0` + `margin-bottom` на `.field` —
     мокап держит межпольный интервал на самих полях, не на gap сетки
     (последнее поле — без нижнего отступа, иначе задвоился бы с
     `.fblock` снаружи). */
  .kunde-edit .fgrid { grid-template-columns: 1fr; gap: 0; }
  .kunde-edit .fgrid .field { margin-bottom: 16px; }
  .kunde-edit .fgrid .field:last-child { margin-bottom: 0; }
  .kunde-edit .field input, .kunde-edit .field select { height: 44px; font-size: 16px; }
  .kunde-edit .field textarea { font-size: 16px; }
  .kunde-edit .fcta { flex-direction: column; gap: 2px; }
  .kunde-edit .fcta .btn { width: 100%; height: 44px; }
  /* «Отмена» — текстовая ссылка-по-виду на телефоне (мокап): рамка/фон
     сняты, обычный вес шрифта, цвет ink-2 — тот же приём, что .mfoot
     .btn-secondary модалки (Итерация 9) и td.m-act .btn-ghost-no
     (таблицы), своя копия вместо общего класса — у общего
     `.btn-ghost-no` паддинг под ячейку таблицы, здесь нужна полноширинная
     кнопка. */
  .kunde-edit .fcta .btn-secondary { border: none; background: none; font-weight: 500; color: var(--ink-2); }
}

/* ---------------------------------------------------------------------
   Экран входа (/login) — отдельная страница, каркас base.html не
   используется.
   --------------------------------------------------------------------- */
.login-body {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: 16px;
}
.login-card {
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  padding: 28px 22px;
  width: 100%;
  max-width: 360px;
}
.login-logo {
  font-weight: 700;
  font-size: 20px;
  text-align: center;
  margin-bottom: 20px;
}
/* Итерация 5 (15.08.2026): на экране входа — сам локап, 128px (карточка
   360px, места больше, чем в сайдбаре с его 77px). margin: 0 auto —
   text-align центрирует строчные элементы, а у блочного <img> своя
   центровка. Типографика выше сохранена: она отрисует alt-текст, если
   картинка не отдалась. */
.login-logo img { width: 128px; height: auto; display: block; margin: 0 auto; }
.login-card .btn { margin-top: 8px; }

/* Кнопка «Выйти» под статусом синка в сайдбаре (десктоп). На телефоне
   сайдбар скрыт целиком (display:none) — там та же кнопка живёт в
   разделе «Ещё» (eshche.html), стили ей не нужны: обычный .card. */
.sidebar-logout { padding: 12px 14px 16px; }


/* ===========================================================================
   Точки отправки (19.08.2026, решение владельца «уголь выезжает не только из
   дома»). Ни одного НОВОГО визуального языка секция не заводит — только
   распространяет уже существующие правила на два места, где их не хватало.
   =========================================================================== */

/* Строка выбора точки на «Маршруте» — та же полоса, что и выбор дня
   (.route-datum-bar выше), поэтому класс переиспользуется как есть.
   Собственного здесь одно: выпадающий список обязан получить ту же высоту
   44px, что дата и кнопка рядом, и не растягиваться на всю строку на
   десктопе (адреса длинные — max-width вместо width). */
.route-start-bar { margin-top: -6px; }
/* ⚠️ Исключение из подтяжки выше. МЕЖДУ полосой дня и полосой точки может
   встать форма «Убрать план на этот день» (.route-plan-bar) — кнопка тихая,
   но разрушительная. Замер 19.08.2026 (десктоп 1280): её зона нажатия
   (`::after` у .btn-quiet, -4 px сверху и снизу) не доставала до «Запомнить
   на этот день» 4 px — в машине палец промахивается мимо «запомнить» прямо
   в «убрать план». Подтяжка -6 px задумана для случая «две полосы подряд»;
   когда между ними стоит эта форма, её быть не должно. */
.route-plan-bar + .route-start-bar { margin-top: 8px; }
.route-start-bar select {
  height: var(--control-h);
  max-width: 100%;
  flex: 1 1 240px;
  min-width: 0;
}
@media (max-width: 640px) {
  /* На телефоне список уезжает на свою строку целиком: адрес точки длиннее
     кнопки, и ужимать его в остаток строки нельзя. */
  .route-start-bar select { flex: 1 1 100%; }
}

/* Разделитель между точками в карточке «Настроек» — дословно то же правило,
   что у «Финансов» (.finanzen-card .formdiv): там оно отделяет форму от
   списка, здесь — одну точку от другой. Отдельным селектором, а не общим:
   отрицательные поля подобраны под паддинг карточки, а он у .einst-card
   свой. */
.einst-card .formdiv { border-top: 1px solid var(--line); margin: 16px -22px 14px; padding: 0; }
@media (max-width: 640px) {
  .einst-card .formdiv { margin-left: -16px; margin-right: -16px; }
}

/* Три действия точки в карточке «Настроек» — КАЖДОЕ СВОЕЙ СТРОКОЙ
   (19.08.2026, замер в браузере). Было: все три — одинаковые тихие
   текстовые кнопки, причём «Сделать обычной» и «Удалить» стояли в 4 px друг
   от друга (правый край первой 158,2 px, левый край второй 162,4 px), а их
   44-пиксельные зоны нажатия ещё и налезали сверху на «Сохранить
   изменения». Промах пальцем в машине давал либо вопрос об удалении, либо —
   что хуже — молчаливую смену обычной точки для ВСЕХ будущих маршрутов.
   Колонка с зазором 18 px оставляет ~10 px чистого воздуха между зонами
   даже с учётом `::after` у `.btn-quiet` (-4 px сверху и снизу). */
.einst-card .sp-aktionen { display: flex; flex-direction: column; gap: 18px; }
.einst-card .sp-aktionen form { margin: 0; }
/* «Удалить» — тот же язык, что «Удалить клиента» (.btn.btn-danger,
   kunde.html): белый фон, красная рамка, красный текст. Между обратимым и
   безвозвратным должен быть шов, который ВИДНО, а не только другой цвет. */
.einst-card .sp-loeschen { margin-top: 4px; }
.einst-card .sp-loeschen .btn { height: 38px; font-size: 14px; padding: 0 16px; }

/* Галочка «сразу сделать обычной» — квадрат-тоггл `.rt`/`.rt-input` (язык
   «Клиентов», колонка «В маршрут»), а не голый `<input type="checkbox">`:
   замер 19.08.2026 — 13 px в ширину, ЕДИНСТВЕННЫЙ такой во всём
   приложении, пальцем в машине в него не попасть.
   ⚠️ `display`/`margin-bottom`/размер задаются здесь заново: общее
   `.field label` (0,2,0) перебивает собственное `.rt { display:inline-flex }`
   (0,1,0) — ровно та же ловушка каскада, что описана у `.btn-toggle` выше.
   32 px вместо 28: у «Клиентов» квадрат сидит в ячейке с паддингом, которая
   и добирает цель нажатия, а здесь он стоит в строке сам по себе. */
.einst-card .sp-standard {
  display: grid; grid-template-columns: auto 1fr;
  align-items: center; column-gap: 10px;
}
.einst-card .sp-standard label { margin-bottom: 0; cursor: pointer; }
.einst-card .sp-standard label.rt { display: inline-flex; width: 32px; height: 32px; }
.einst-card .sp-standard .hint { grid-column: 1 / -1; margin-top: 8px; }
/* `.tm-hinweis` — тот же тоггл у флажка «только напомнить, без просрочки» в
   форме «Добавить срок» бухгалтерского календаря (22.08.2026): те же правила,
   по той же причине (13 px голого чекбокса пальцем не взять). Правила
   повторены, а не склеены селектором через запятую: сторож
   test_txt08_das_haekchen_… ищет правило `.sp-standard label.rt` дословно. */
.einst-card .tm-hinweis {
  display: grid; grid-template-columns: auto 1fr;
  align-items: center; column-gap: 10px;
}
.einst-card .tm-hinweis label { margin-bottom: 0; cursor: pointer; }
.einst-card .tm-hinweis label.rt { display: inline-flex; width: 32px; height: 32px; }
.einst-card .tm-hinweis .hint { grid-column: 1 / -1; margin-top: 8px; }

/* ===========================================================================
   Бухгалтерский календарь на «Настройках» (22.08.2026, решение владельца «все
   уведомления по бухгалтерскому календарю — в Telegram»). Карточка говорит
   языком соседних (.einst-card/.formdiv/.fsec/.badge/.btn-quiet/.btn-danger,
   details.hint-more/details.acc) — здесь только раскладка СТРОКИ срока,
   которой у соседей нет: шапка «дата · через N дн. · бейдж», название, две
   кнопки в ряд. Скоуп — .einst-card, как у всего раздела «Настроек».
   =========================================================================== */
/* Шапка строки: дата, подпись срока и бейджи в одну строку, на узком экране
   переносятся (flex-wrap) — семнадцать сроков подряд не должны занимать по
   четыре строки каждый. */
.einst-card .tm-kopf { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; margin-bottom: 4px; }
.einst-card .tm-datum { font-weight: 600; }
.einst-card .tm-frist { font-size: 13px; color: var(--ink-3); }
/* «сегодня» — жёлтым, «просрочен» — красным: те же цвета, что у бейджей
   .badge-warn/.badge-no, чтобы глаз ловил состояние без чтения. */
.einst-card .tm-heute { color: var(--warn); font-weight: 600; }
.einst-card .tm-ueberfaellig { color: var(--no); font-weight: 600; }
.einst-card .tm-titel { font-size: 14.5px; line-height: 1.4; margin-bottom: 6px; }
/* Заметка и источник — как их написал reviewer: с переносами строк (pre-line),
   иначе пометка «⚠ дата из заметок…» слипается с разбором в один абзац. */
.einst-card .tm-notiz, .einst-card .tm-quelle { white-space: pre-line; margin: 6px 0 0; }
/* Две кнопки в ряд с зазором 22 px: «Сделано» — тихая, «Убрать» — красная с
   рамкой (шов между обратимым и безвозвратным должен быть ВИДЕН — тот же
   довод, что у .sp-loeschen выше). Зазор больше зоны `::after` у .btn-quiet
   (±4 px), чтобы промах пальцем по «Сделано» не попадал в «Убрать». */
.einst-card .tm-aktionen { display: flex; align-items: center; gap: 22px; margin-top: 10px; }
.einst-card .tm-aktionen form { margin: 0; }
.einst-card .tm-aktionen .btn { height: 36px; font-size: 14px; padding: 0 14px; }
/* Поле заметки — той же высоты строки и с тем же скруглением, что инпуты
   карточки; общее правило textarea (см. «Формы») даёт остальное. */
.einst-card .field textarea { min-height: 84px; border-radius: 8px; }
@media (max-width: 640px) {
  /* На телефоне — полные 44 px тач-цели у «Убрать» (B-08 генеральной
     репетиции: разрушающая кнопка ниже 44 px и ближе 12 px к соседке —
     промах пальцем в дороге; сторож — test_b08_zerstoerende_knoepfe_…,
     замер в браузере на 393 px). Десктопные 36 px сюда не протекают: у
     этого правила та же специфичность и оно стоит позже. */
  .einst-card .tm-aktionen { gap: 18px; }
  .einst-card .tm-aktionen .btn { height: 44px; }
}

/* ============================================================================
   Раздел «Долг» (25.08.2026) — ЛИЧНЫЙ заём владельца, НЕ бухгалтерия.
   ============================================================================
   Собственных визуальных решений раздел не заводит СОЗНАТЕЛЬНО: объявления
   ниже — те же, что у .kasse-card/.finanzen-card, только под своим скоупом
   .dolg-card. Экран обязан выглядеть частью системы; отделять его должна
   навигация (группа «Личное» в сайдбаре, отдельный пункт на «Ещё»), а не
   другой облик — иначе «непохожий» экран начинает читаться как чужой
   инструмент, а он свой, просто про другие деньги. */
.dolg-card .hint { font-size: 12.5px; color: var(--ink-3); margin: 8px 0 0; line-height: 1.4; }
.dolg-card .mrow { font-size: 12.5px; color: var(--ink-3); line-height: 1.45; margin-bottom: 4px; }
.dolg-card h2 { margin-bottom: 10px; }
/* Две карточки — та же пара, что у «Кассы» (.kasse-kpi): две равные колонки
   на десктопе, одна на телефоне, чтобы подпись «Осталось вернуть» не
   переносилась. Комиссий здесь больше нет (решение владельца 25.08.2026). */
.kpi-grid.dolg-kpi { grid-template-columns: 1fr 1fr; }
@media (max-width: 640px) {
  .kpi-grid.dolg-kpi { grid-template-columns: 1fr; }
}
/* Правая (числовая) колонка вплотную к левой текстовой — заголовки
   «КУРС ПРИМЕЧАНИЕ» слипались. */
.dolg-card table.pf-liste th.num + th:not(.num),
.dolg-card table.pf-liste td.num + td:not(.num) { padding-left: 16px; }

/* ---- Сорта угля (26.08.2026): подпись сорта плашкой в таблицах («Сегодня»,
   «Клиенты», «Маршрут», «Склад»). Тот же вид, что у .kunde-karte .badge-mini
   (источник расхода в карточке клиента) — правило стало общим, скоуп карточки
   остаётся ниже как был. */
.badge-mini { font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 999px; background: var(--neutral-soft); color: var(--ink-2); vertical-align: 1px; margin-left: 6px; white-space: nowrap; }
