/* ============================================================================
   Eska Design System
   Источник стиля: https://1eska.ru (Aspro Allcorp2, кастом)
   Прототип и обоснование решений: research/design-system/README.md
   ============================================================================ */

/* Порядок каскадных слоёв. Задаётся ЗДЕСЬ, потому что ds.css подключается первым
   (base.html, login.html) — кто первым объявил, тот и определяет порядок.

   Зачем вообще: Tailwind v4 держит все утилиты в `@layer utilities`, а неслоёвый
   (unlayered) CSS по спецификации каскада сильнее ЛЮБОГО слоя — независимо от
   порядка <link> и специфичности. Пока этот файл был unlayered, каждое его
   правило перебивало утилиты: `class="alert ... hidden"` показывался всегда
   (пустая красная плашка на /services/forum-search), `class="btn ... w-full"`
   молча игнорировал ширину. Регрессия появилась с переходом на локальную
   сборку Tailwind v4 (issue 014); см. docs/_Общее/issue/todo/019_css_cascade_layers/.

   Три вещи, которые здесь легко сломать:
   1. `properties` обязан стоять первым. Иначе слой впервые встретится уже в
      tailwind.css и допишется ПОСЛЕ utilities — тогда `*{--tw-*:initial}`
      перебьёт значения от утилит, и разом умрут все transform/shadow/gradient.
   2. Имя слоя для этого файла — именно `components`: Tailwind сам объявляет его
      между `base` и `utilities`. Со своим именем (например `ds`) слой при потере
      этой строки уехал бы в конец и вернул исходный баг.
   3. `components` не должен оказаться раньше `base`: preflight задаёт
      `h1..h6{font-size:inherit}`, `a{color:inherit}`, `*{margin:0}` теми же
      селекторами, что секция «База» ниже, а между слоями специфичность не
      считается — вся типографика и цвет ссылок умрут. */
@layer properties, theme, base, components, utilities;

/* ---------- 1. Шрифты (self-host, только latin+cyrillic — см. README) ---- */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/inter-400-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/inter-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/inter-500-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/inter-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/inter-600-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/inter-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/montserrat-600-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/montserrat-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/montserrat-700-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/montserrat-700-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* Unbounded (вариативный, отдаём одним файлом на вес 700-900) — заголовочный
   шрифт для инстансов с собственным брендом (напр. dodo-goretskih.css,
   issue 015). Не подключается по умолчанию — используется, только если
   тема инстанса переопределит --font-display на 'Unbounded'. */
@font-face {
  font-family: 'Unbounded';
  font-style: normal;
  font-weight: 700 900;
  font-display: swap;
  src: url('/static/fonts/unbounded-800-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Unbounded';
  font-style: normal;
  font-weight: 700 900;
  font-display: swap;
  src: url('/static/fonts/unbounded-800-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ---------- 2. Токены ---------------------------------------------------- */
:root {
  /* --- Бренд (взято с 1eska.ru) --- */
  --brand-50:  #e6fbf4;
  --brand-100: #c2f5e4;
  --brand-200: #8aebcd;
  --brand-300: #4bddb2;
  --brand-400: #17d29b;
  --brand-500: #00ca89;   /* основной акцент 1eska */
  --brand-600: #26a47b;   /* hover / нажатие */
  --brand-700: #1c7f60;
  --brand-900: #0a2a2a;   /* глубокий тёмно-зелёный, фон сайдбара/hero */

  /* --- Нейтральные (шкала Tailwind gray — её же использует 1eska.ru) --- */
  --gray-50:  #f9fafb;
  --gray-100: #f3f4f6;
  --gray-200: #e5e7eb;
  --gray-300: #d1d5db;
  --gray-400: #9ca3af;
  --gray-500: #6b7280;
  --gray-600: #4b5563;
  --gray-700: #374151;
  --gray-800: #1f2937;
  --gray-900: #111827;

  /* --- Статусные --- */
  --ok:    #16a34a;  --ok-bg:    #ddf8e5;  --ok-fg:   #15803d;
  --warn:  #f6a142;  --warn-bg:  #fff4e3;  --warn-fg: #b45309;
  --err:   #dc2626;  --err-bg:   #fee2e2;  --err-fg:  #dc2626;
  --info:  #2563eb;  --info-bg:  #e3f4ff;  --info-fg: #2563eb;
  --muted-accent: #7c3aed; --muted-accent-bg: #f9e8ff; --muted-accent-fg: #7c3aed;
  /* Тёмные «чернила» статуса на мягком фоне и самые светлые подложки (текст на --*-bg, плашки в редакторах). */
  --ok-ink: #166534;   --ok-tint: #f0fdf4;
  --warn-ink: #92400e; --warn-tint: #fffbeb;
  --err-ink: #991b1b;  --err-tint: #fef2f2;
  --info-ink: #1d4ed8;
  /* Светлые линии статуса: нижняя граница плашки, рамка выделенного блока. */
  --err-line: #fca5a5; --warn-line: #fcd34d; --info-line: #bfdbfe;

  /* --- Семантика: светлая тема --- */
  --bg:            var(--gray-50);
  --bg-elevated:   #ffffff;
  --bg-sunken:     var(--gray-100);
  --bg-hover:      var(--gray-50);
  --surface-line:  var(--gray-200);
  --line-strong:   var(--gray-300);

  --fg:            var(--gray-900);
  --fg-secondary:  var(--gray-600);
  --fg-muted:      var(--gray-500);
  --fg-faint:      var(--gray-400);
  --fg-on-accent:  #ffffff;

  --accent:        var(--brand-500);
  --accent-hover:  var(--brand-600);
  --accent-soft:   var(--brand-50);
  --accent-fg:     var(--brand-700);

  --sidebar-bg:    var(--brand-900);
  --sidebar-fg:    #cfe6df;
  --sidebar-fg-active: #ffffff;
  /* По умолчанию сайдбар сплошной (все три точки градиента равны
     --sidebar-bg) — вертикальный градиент включается per-instance темой
     (issue 015), переопределив --sidebar-grad-mid/--sidebar-grad-end/
     --sidebar-glow, а не правкой .sidebar здесь. См. themes/dodo.css. */
  --sidebar-grad-mid: var(--sidebar-bg);
  --sidebar-grad-end: var(--sidebar-bg);
  --sidebar-glow: transparent;
  /* Подсветка активного пункта сайдбара и «чернила» на акцентной плашке.
     Были зашиты 1eska-зелёным прямо в правилах — из-за этого тема заказчика
     проступала зелёным (issue 015, брендирование инстансов). */
  --nav-active-bg: rgba(0, 202, 137, .16);
  --on-accent-ink: #06231c;
  /* Промежуточные остановки градиента hero/акцентной статистики. */
  --grad-mid:  #10403a;
  --grad-late: #14524a;
  /* Полупрозрачные слои: подложка кода и рамка в тексте, подсказка графика, пояснение на тёмном и на акценте. */
  --tint-weak: color-mix(in srgb, var(--fg) 6%, transparent);
  --tint-line: color-mix(in srgb, var(--fg) 10%, transparent);
  --scrim: color-mix(in srgb, #000 80%, transparent);
  --on-dark-muted: color-mix(in srgb, var(--fg-on-accent) 55%, transparent);
  --on-dark-sk: color-mix(in srgb, var(--fg-on-accent) 22%, transparent);
  --on-accent-ink-soft: color-mix(in srgb, var(--on-accent-ink) 55%, transparent);
  --on-accent-ink-mid: color-mix(in srgb, var(--on-accent-ink) 65%, transparent);
  --warn-wash: color-mix(in srgb, var(--warn) 14%, transparent);
  /* Световое пятно в углу hero — тоже брендовое. */
  --hero-glow: rgba(0, 202, 137, .35);

  /* --- Палитра графиков ---
     Проверено scripts/validate_palette.js (dataviz) против поверхности #ffffff:
     lightness band, chroma floor, CVD-разделение, normal-vision floor, контраст — ALL CHECKS PASS.
     Порядок фиксированный: серия N всегда берёт --c-N, цвет привязан к сущности,
     а не к позиции в отфильтрованном списке. 7-я серия → «Прочее» или small multiples. */
  --c-1: #0f9b74;  /* мятный — бренд, всегда «основная» серия */
  --c-2: #2563eb;
  --c-3: #c2740e;
  --c-4: #9333ea;
  --c-5: #be185d;
  --c-6: #0891b2;
  /* Последовательная шкала (одна тональность, светлая→тёмная) — тепловые карты, интенсивность */
  --seq-1: #3fcba3; --seq-2: #12b489; --seq-3: #0d9068; --seq-4: #0a6b4e; --seq-5: #064c37;
  /* Расходящаяся: два полюса + нейтральная середина (план/факт, отклонение) */
  --div-neg: #be185d; --div-mid: var(--gray-300); --div-pos: #0f9b74;
  /* Сетка и оси — рецессивные */
  --chart-grid: var(--surface-line);
  --chart-axis: var(--fg-faint);

  /* --- Типографика --- */
  --font-display: 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-ui:      'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-mono:    ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace;

  --fs-xs:  11px;  --fs-sm: 13px;  --fs-md: 14px;
  --fs-lg:  16px;  --fs-xl: 20px;  --fs-2xl: 26px;  --fs-3xl: 34px;
  /* Высота строки (Контур, «Текстовые стили»): заголовок 1.2, обычный текст и подписи 1.4, длинный текст (инструкции, статьи) 1.6 */
  --lh-tight: 1.2;  --lh: 1.4;  --lh-long: 1.6;

  /* --- Скругления (1eska: 8/12/14/16, pill) --- */
  --r-xs: 4px;  --r-sm: 8px;  --r-md: 12px;  --r-lg: 16px;  --r-xl: 22px;  --r-pill: 999px;

  /* --- Тени (мягкие, как на 1eska.ru) --- */
  --sh-sm: 0 1px 2px rgba(10, 42, 42, .05);
  --sh-md: 0 4px 14px rgba(10, 42, 42, .06);
  --sh-lg: 0 12px 32px -8px rgba(10, 42, 42, .16);
  --sh-accent: 0 6px 16px rgba(0, 202, 137, .28);

  /* --- Отступы (шаг 4px) --- */
  --s-1: 4px; --s-2: 8px; --s-3: 12px; --s-4: 16px;
  --s-5: 20px; --s-6: 24px; --s-8: 32px; --s-10: 40px; --s-12: 48px;

  --shell-sidebar: 244px;
  --shell-sidebar-collapsed: 72px;
  --shell-topbar: 60px;
  --transition: 140ms cubic-bezier(.4, 0, .2, 1);
}

/* ---------- 3. База ------------------------------------------------------ */
*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--font-ui);
  font-size: var(--fs-md);
  line-height: 1.5;
  color: var(--fg);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, .display { font-family: var(--font-display); font-weight: 700; margin: 0; letter-spacing: -.01em; }
h1 { font-size: var(--fs-3xl); }
h2 { font-size: var(--fs-2xl); }
h3 { font-size: var(--fs-xl); }
h4 { font-size: var(--fs-lg); }
p  { margin: 0 0 var(--s-3); }
a  { text-decoration: none; }
/* Цвет — всем ссылкам, КРОМЕ ссылок-кнопок: у `.btn--*` свой цвет, и он должен
   выигрывать. Само правило намеренно неслоёвое (как и было), поэтому оно
   перебивает и `.btn--primary` из `@layer components`, и утилиту `.text-white`
   из `@layer utilities` — см. пояснение про слои в шапке файла. Из-за этого
   текст `<a class="btn btn--primary">` красился акцентным цветом поверх
   акцентного фона и становился нечитаемым (04.09.2026, кнопка «Перейти в
   систему» на странице приглашения).

   Тот же класс на `<button>` всё это время рисовался верно — ссылки были
   единственным исключением, так что правка не вводит новый вид, а убирает
   расхождение. Проверено: все `<a class="btn …">` в шаблонах имеют цветовой
   модификатор (primary/secondary/ghost), без цвета не останется ни одна.

   `:where(:not(.btn))`, а НЕ `:not(.btn)` — из-за специфичности. `:not()`
   добавляет специфичность своего аргумента, и селектор становится 0-1-1,
   перебивая `.nav-item--active` (0-1-0): активный пункт бокового меню терял
   белый цвет и красился тёмно-зелёным по тёмному фону (07.09.2026, регрессия
   от правки 04.09.2026 — до неё было просто `a`, то есть 0-0-1). `:where()`
   фильтрует так же, но специфичности не добавляет: остаётся 0-0-1, как было
   изначально, и ссылки-кнопки при этом по-прежнему исключены. */
a:where(:not(.btn)) { color: var(--accent-fg); }
a:hover { text-decoration: underline; }

/* Имена совпадают с утилитами Tailwind — и это осознанно: правила нужны здесь,
   потому что утилита из `@layer utilities` слабее неслоёвого селектора `h3`
   выше, и без них `<h3 class="text-sm">` вырос бы с 13px до 20px. Те же
   значения продублированы токенами --text-sm/--text-xs в tailwind-src.css,
   чтобы утилита давала тот же размер на обычных элементах (issue 019). */
.text-sm    { font-size: var(--fs-sm); }
.text-xs    { font-size: var(--fs-xs); }
.muted      { color: var(--fg-muted); }
.secondary  { color: var(--fg-secondary); }
/* Цвет текста по смыслу (ГАЙД-СТИЛЬ-И-ЦВЕТ, issue 076): токены статуса вместо text-red-600 и т. п. */
.text-ok    { color: var(--ok-fg); }
.text-warn  { color: var(--warn-fg); }
.text-err   { color: var(--err-fg); }
.text-info  { color: var(--info-fg); }
.text-ai    { color: var(--muted-accent-fg); }
/* Линии по умолчанию — светлые, а не чёрные (ГАЙД-СТИЛЬ-И-ЦВЕТ): `border-t`, `divide-y` без цвета брали currentColor.
   Слой base: любой явный цвет (утилиты Tailwind, .border-ok и т. п.) перебивает это правило. */
@layer base {
  .border, .border-t, .border-b, .border-l, .border-r, .border-x, .border-y { border-color: var(--surface-line); }
  .divide-y > * + *, .divide-x > * + * { border-color: var(--surface-line); }
}
/* Мягкие фоны и рамки по смыслу (issue 076): вместо bg-red-50 / border-red-200 и т. п. */
.bg-ok-soft   { background-color: var(--ok-bg); }
.bg-warn-soft { background-color: var(--warn-bg); }
.bg-err-soft  { background-color: var(--err-bg); }
.bg-info-soft { background-color: var(--info-bg); }
.bg-ai-soft   { background-color: var(--muted-accent-bg); }
.border-ok    { border-color: color-mix(in srgb, var(--ok) 35%, transparent); }
.border-warn  { border-color: color-mix(in srgb, var(--warn) 45%, transparent); }
.border-err   { border-color: color-mix(in srgb, var(--err) 35%, transparent); }
.border-info  { border-color: color-mix(in srgb, var(--info) 35%, transparent); }
.border-ai    { border-color: color-mix(in srgb, var(--muted-accent-fg) 35%, transparent); }
/* Насыщенные: выбранное состояние, индикатор, полоса прогресса, подчёркивание активной вкладки (вместо bg-blue-600 / border-blue-500) */
.bg-ok-strong { background-color: var(--ok); }
.bg-warn-strong { background-color: var(--warn); }
.bg-err-strong { background-color: var(--err); }
.bg-info-strong { background-color: var(--info); }
.bg-ai-strong { background-color: var(--muted-accent-fg); }
.border-ok-strong { border-color: var(--ok); }
.border-warn-strong { border-color: var(--warn); }
.border-err-strong { border-color: var(--err); }
.border-info-strong { border-color: var(--info); }
.border-ai-strong { border-color: var(--muted-accent-fg); }
/* Наведение: вместо hover:bg-blue-50 / hover:text-blue-700 / hover:border-blue-300 */
.hover\:bg-ok-soft:hover { background-color: var(--ok-bg); }
.hover\:bg-warn-soft:hover { background-color: var(--warn-bg); }
.hover\:bg-err-soft:hover { background-color: var(--err-bg); }
.hover\:bg-info-soft:hover { background-color: var(--info-bg); }
.hover\:bg-sunken:hover { background-color: var(--bg-sunken); }
.hover\:bg-ai-soft:hover { background-color: var(--muted-accent-bg); }
.hover\:text-ok:hover { color: var(--ok-fg); }
.hover\:text-warn:hover { color: var(--warn-fg); }
.hover\:text-err:hover { color: var(--err-fg); }
.hover\:text-info:hover { color: var(--info-fg); }
.hover\:text-ai:hover { color: var(--muted-accent-fg); }
.hover\:border-ok:hover { border-color: color-mix(in srgb, var(--ok) 35%, transparent); }
.hover\:border-warn:hover { border-color: color-mix(in srgb, var(--warn) 35%, transparent); }
.hover\:border-err:hover { border-color: color-mix(in srgb, var(--err) 35%, transparent); }
.hover\:border-info:hover { border-color: color-mix(in srgb, var(--info) 35%, transparent); }
.hover\:border-ai:hover { border-color: color-mix(in srgb, var(--muted-accent-fg) 35%, transparent); }
.num        { font-variant-numeric: tabular-nums; }
.mono       { font-family: var(--font-mono); font-size: var(--fs-sm); }

/* ---------- 4. Оболочка (shell) ------------------------------------------ */
.shell { grid-template-columns: var(--shell-sidebar) 1fr; min-height: 100vh;
  transition: grid-template-columns var(--transition);
  /* колонка сайдбара остаётся тёмной на всю высоту документа, а не только первого экрана */
  background: linear-gradient(to right, var(--sidebar-bg) 0 var(--shell-sidebar), var(--bg) var(--shell-sidebar));
}
body.sidebar-collapsed .shell {
  grid-template-columns: var(--shell-sidebar-collapsed) 1fr;
  background: linear-gradient(to right, var(--sidebar-bg) 0 var(--shell-sidebar-collapsed), var(--bg) var(--shell-sidebar-collapsed));
}

.sidebar {
  background: linear-gradient(180deg, var(--sidebar-bg) 0%, var(--sidebar-grad-mid) 55%, var(--sidebar-grad-end) 100%);
  color: var(--sidebar-fg); flex-direction: column;
  position: sticky; top: 0; height: 100vh; overflow-x: hidden; overflow-y: auto;
  z-index: 50;
}
/* Свечение снизу — тот же приём, что у .hero, но в нижнем углу.
   z-index:-1 топит его под обычными (не спозиционированными) детьми
   сайдбара внутри его же стекового контекста (сайдбар — sticky + z-index).
   --sidebar-glow: transparent по умолчанию — см. --sidebar-grad-mid выше. */
.sidebar::after {
  content: ''; position: absolute; left: -60px; bottom: -60px; width: 220px; height: 220px;
  border-radius: 50%; background: radial-gradient(circle, var(--sidebar-glow), transparent 70%);
  pointer-events: none; z-index: -1;
}
.sidebar__brand-row { align-items: center; justify-content: space-between; gap: var(--s-2);
  padding: var(--s-5) var(--s-3) var(--s-4) var(--s-5);
}
.sidebar__actions { align-items: center; gap: var(--s-1); flex-shrink: 0; }
.sidebar__brand { align-items: center; gap: var(--s-3); min-width: 0;
  font-family: var(--font-display); font-weight: 700; font-size: var(--fs-lg);
  color: #fff;
}
.sidebar__brand:hover { text-decoration: none; }
.sidebar__logo {
  width: 30px; height: 30px; border-radius: var(--r-sm);
  background: var(--brand-500); color: var(--brand-900); place-items: center; font-size: var(--fs-sm); font-weight: 700;
  flex-shrink: 0;
}
/* Логотип инстанса как картинка (issue 015) — свой фон уже в самой картинке,
   тут только размер/форма, без цветовой заливки текстовой марки. */
.sidebar__logo--img {
  background: none; object-fit: cover;
}
.sidebar__brand--wordmark { gap: 0; }
.sidebar__wordmark {
  width: 166px; height: 44px; object-fit: contain;
  object-position: left center; flex-shrink: 1; min-width: 0;
}
.sidebar__group { padding: var(--s-3) var(--s-3) 0; }
.sidebar__label {
  font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .08em;
  color: rgba(255,255,255,.34); padding: var(--s-2) var(--s-3); font-weight: 600;
}
.nav-item { align-items: center; gap: var(--s-3);
  padding: 9px var(--s-3); border-radius: var(--r-sm);
  color: var(--sidebar-fg); font-size: var(--fs-sm); font-weight: 500;
  transition: background var(--transition), color var(--transition);
}
.nav-item:hover { background: rgba(255,255,255,.07); color: #fff; text-decoration: none; }
.nav-item--active { background: var(--nav-active-bg); color: #fff; box-shadow: inset 2px 0 0 var(--brand-500); }
.nav-item svg { width: 17px; height: 17px; flex-shrink: 0; opacity: .85; }
/* Счётчик непрочитанного в пункте меню. Без объявления display намеренно: правило
   неслоёвое, и `display` здесь перебил бы утилиту `hidden`, которой скрипт прячет
   пустой бейдж (issue 019, тот же капкан). Элемент и так блокифицируется как
   flex-элемент .nav-item, поэтому padding и min-width работают. */
.nav-item__badge {
  margin-left: auto; background: var(--brand-500); color: var(--on-accent-ink);
  font-size: 11px; font-weight: 700; line-height: 1; padding: 3px 7px;
  border-radius: var(--r-pill); min-width: 20px; text-align: center;
}
.sidebar__footer { margin-top: auto; padding: var(--s-4); border-top: 1px solid rgba(255,255,255,.08); }

/* Подвал сайдбара: профиль + выход. Разметку отдаёт _macros/shell.html. */
.sidebar__footer .row { gap: var(--s-2); align-items: center; }
.sidebar__profile { align-items: center; gap: var(--s-3); flex: 1; min-width: 0; color: inherit; }
.sidebar__profile:hover { text-decoration: none; }
.sidebar__avatar { background: var(--brand-500); color: var(--brand-900); }
.sidebar__user-info { min-width: 0; flex: 1; }
.sidebar__user-name { font-size: var(--fs-sm); color: #fff; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sidebar__user-sub { font-size: var(--fs-xs); color: rgba(255,255,255,.45); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sidebar__logout-form { margin: 0; }
.sidebar__logout { color: rgba(255,255,255,.6); flex-shrink: 0; }
.sidebar__logout:hover { color: #fff; background: rgba(255,255,255,.07); }
/* Пункт меню, которого ещё нет: виден, но не кликается. */
.nav-item--soon { opacity: .48; cursor: default; }
.nav-item--soon:hover { background: none; color: var(--sidebar-fg); }

.sidebar__collapse-btn { color: rgba(255,255,255,.6); flex-shrink: 0; }
.sidebar__collapse-btn:hover { color: #fff; background: rgba(255,255,255,.07); }
.sidebar__collapse-btn svg { transition: transform var(--transition); }
body.sidebar-collapsed .sidebar__collapse-btn svg { transform: rotate(180deg); }
.sidebar__search-btn { color: rgba(255,255,255,.72); flex-shrink: 0; }
.sidebar__search-btn:hover { color: #fff; background: rgba(255,255,255,.07); }
.sidebar__search {
  padding: 0 var(--s-3) var(--s-2);
}
.sidebar__search-field {
  align-items: center; gap: var(--s-2); padding: 0 var(--s-3); height: 36px;
  border: 1px solid rgba(255,255,255,.2); border-radius: var(--r-sm); background: rgba(255,255,255,.08);
}
.sidebar__search-field:focus-within { border-color: var(--brand-500); background: rgba(255,255,255,.12); }
.sidebar__search-field svg { width: 16px; height: 16px; flex-shrink: 0; color: rgba(255,255,255,.65); }
.sidebar__search-field input {
  width: 100%; min-width: 0; border: 0; outline: 0; background: transparent; color: #fff; font: inherit;
}
.sidebar__search-field input::placeholder { color: rgba(255,255,255,.5); }
.sidebar__search-field button { color: rgba(255,255,255,.65); padding: 3px; border-radius: var(--r-xs); }
.sidebar__search-field button:hover { color: #fff; background: rgba(255,255,255,.1); }
.sidebar__search-empty { padding: var(--s-2) var(--s-3) 0; color: rgba(255,255,255,.55); font-size: var(--fs-xs); }

/* Свёрнутый сайдбар (десктоп) — только иконки, ширина см. --shell-sidebar-collapsed */
body.sidebar-collapsed .sidebar__brand span,
body.sidebar-collapsed .sidebar__label,
body.sidebar-collapsed .nav-item span,
body.sidebar-collapsed .sidebar__user-info {
  display: none;
}
body.sidebar-collapsed .sidebar__brand-row {
  flex-direction: column; align-items: center; gap: var(--s-2);
  padding: var(--s-4) var(--s-2);
}
body.sidebar-collapsed .sidebar__actions { flex-direction: column; }
body.sidebar-collapsed .nav-item { justify-content: center; padding-inline: 0; }
body.sidebar-collapsed .sidebar__footer .row { flex-direction: column; gap: var(--s-2); }

.main { flex-direction: column; min-width: 0; }

.topbar {
  height: var(--shell-topbar); align-items: center; gap: var(--s-4);
  padding: 0 var(--s-6);
  background: var(--bg-elevated);
  border-bottom: 1px solid var(--surface-line);
  position: sticky; top: 0; z-index: 20;
}
.topbar__title { font-family: var(--font-display); font-weight: 600; font-size: var(--fs-lg); }
.topbar__spacer { flex: 1; }
/* «Назад» стрелкой слева от названия (макрос _macros/back.html): визуально тесно к названию, без своей рамки */
.topbar__back { flex-shrink: 0; margin-inline: calc(var(--s-2) * -1) calc(var(--s-3) * -1); color: var(--fg-secondary); }
.topbar__back:hover { color: var(--fg); }
.sites-context { align-items: center; gap: var(--s-2); flex-shrink: 0; margin-left: auto; }
.sites-context + .topbar__spacer { flex: 0; }
.sites-context .select { width: 210px; min-height: 34px; padding-block: 5px; font-size: var(--fs-sm); }

/* Общий колокольчик уведомлений (issue 041) — виден на любой странице любого
   сервиса, не только там, где topbar_actions что-то определил. */
.notif-bell { position: relative; }
.notif-bell__badge {
  position: absolute; top: 2px; right: 2px;
  min-width: 16px; height: 16px; padding: 0 4px;
  border-radius: 999px; background: var(--err-fg); color: #fff;
  font-size: 10px; line-height: 16px; text-align: center;
  font-variant-numeric: tabular-nums;
}
.notif-bell__panel {
  position: absolute; top: calc(100% + 8px); right: 0;
  width: 360px; max-height: 420px; overflow-y: auto;
  z-index: 30; padding: var(--s-3);
}
.notif-bell__header {
  align-items: center; justify-content: space-between;
  margin-bottom: var(--s-2); padding-bottom: var(--s-2);
  border-bottom: 1px solid var(--surface-line);
}
.notif-bell__header button { color: var(--accent-fg); }
.notif-bell__item {
  width: 100%; text-align: left; padding: var(--s-2);
  border-radius: var(--r-sm); cursor: pointer; background: none; border: 0;
}
.notif-bell__item:hover { background: var(--bg-hover); }
.notif-bell__item--unread { background: var(--accent-soft); }
.notif-bell__item-title { font-size: 13px; color: var(--fg); }
.notif-bell__item-meta { font-size: 11px; color: var(--fg-secondary); margin-top: 2px; }
.notif-bell__empty { padding: var(--s-4); text-align: center; }

/* Верхнеуровневые вкладки страницы в топбаре (issue 028) — усиление
   переполнения через app/static/js/topbar-tabs.js. min-width:0, чтобы
   flex-контейнер топбара мог сжимать эту группу (иначе overflow не
   наступит, потому что flex-item не сжимается ниже intrinsic-ширины). */
.topbar > .tabs {
  border-bottom: 0; margin: 0; min-width: 0; flex-shrink: 1;
  overflow: hidden; flex-wrap: nowrap;
}
.topbar > .tabs .tab { padding: 8px var(--s-3); font-size: var(--fs-sm); }
.topbar__pagetabs-overflow { position: relative; flex-shrink: 0; }
.topbar__pagetabs-more { flex-shrink: 0; }
.topbar__pagetabs-panel {
  top: calc(100% + 4px); right: 0; left: auto; min-width: 200px;
  flex-direction: column; padding: 6px; gap: 2px;
}
/* Телефон: панель по ширине экрана, а не по ширине кнопки «Ещё» — иначе
   длинные пункты уезжали за левый край. Обёртка не позиционируется,
   панель считается от липкого .topbar. */
@media (max-width: 640px) {
  .topbar__pagetabs-overflow { position: static; }
  .topbar__pagetabs-panel {
    left: var(--s-3); right: var(--s-3); min-width: 0; max-width: none;
  }
}
.topbar__pagetabs-panel .tab {
  text-align: left; border-bottom: 0; border-radius: var(--r-sm);
  padding: 8px 10px; margin: 0;
}
.topbar__pagetabs-panel .tab:hover { background: var(--bg-sunken); }
.topbar__pagetabs-panel .tab[aria-selected="true"] { background: var(--accent-soft); color: var(--accent-fg); }

.content { padding: var(--s-6); max-width: 1560px; width: 100%; }
.page-head { margin-bottom: var(--s-6); }
.page-head p { color: var(--fg-secondary); margin: var(--s-2) 0 0; }
.sidebar-backdrop {
  position: fixed; inset: 0; background: rgba(0,0,0,.35); z-index: 45;
  opacity: 0; pointer-events: none; transition: opacity var(--transition);
}
.sidebar-backdrop.is-open { opacity: 1; pointer-events: auto; }

/* ---------- 5. Карточки -------------------------------------------------- */
.card {
  background: var(--bg-elevated);
  border-style: solid;
  border-radius: var(--r-lg);
  box-shadow: var(--sh-sm);
}
/* Толщина и цвет рамки — в слое: разметка навешивает на карточки акцентные
   `border-l-4 border-l-red-500` (issue 019, шаг 2) и они должны перебивать
   дефолт. Остальные свойства карточки остаются unlayered, иначе неслоёвая
   базовая типографика начинает перебивать `.card__head h3`, и заголовки
   внутри карточек вырастают с 14px до 20px (проверено пробой). */
@layer components {
  .card { border-width: 1px; border-color: var(--surface-line); }
}
.card--pad   { padding: var(--s-5); }
/* Всплывающий слой над страницей (выпадающий список, панель фильтра): рамка плотнее, тень выше, чем у карточки страницы. */
.popover { background: var(--bg-elevated); border: 1px solid var(--line-strong); border-radius: var(--r-md); box-shadow: var(--sh-md); }
.popover--pad { padding: var(--s-4); }
.card--hover { transition: box-shadow var(--transition), transform var(--transition), border-color var(--transition); }
.card--hover:hover { box-shadow: var(--sh-md); transform: translateY(-2px); border-color: var(--brand-200); }
.card__head { align-items: center; gap: var(--s-3);
  padding: var(--s-4) var(--s-5); border-bottom: 1px solid var(--surface-line);
}
.card__title { font-family: var(--font-display); font-weight: 600; font-size: var(--fs-md); }
.card__body { padding: var(--s-5); }

/* ---------- 6. Кнопки ---------------------------------------------------- */
.btn { align-items: center; justify-content: center; gap: var(--s-2);
  font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-sm);
  padding: 9px 16px; border-radius: var(--r-sm);
  border: 1px solid transparent; cursor: pointer; white-space: nowrap;
  transition: background var(--transition), color var(--transition), box-shadow var(--transition), border-color var(--transition);
}
.btn svg { width: 16px; height: 16px; }
.btn:hover { text-decoration: none; }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Нестандартный кликабельный элемент (role="button" / data-kbd, kbd-click.js): то же кольцо фокуса, что у кнопки */
[role="button"]:focus-visible, [data-kbd]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.btn:disabled, .btn[disabled] { opacity: .5; cursor: not-allowed; box-shadow: none; }

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

.btn--secondary { background: var(--bg-elevated); color: var(--fg); border-color: var(--line-strong); }
.btn--secondary:hover:not(:disabled) { background: var(--bg-hover); border-color: var(--fg-faint); }

.btn--ghost { background: transparent; color: var(--fg-secondary); }
.btn--ghost:hover:not(:disabled) { background: var(--bg-sunken); color: var(--fg); }

.btn--danger { background: var(--err); color: #fff; }
.btn--danger:hover:not(:disabled) { filter: brightness(.92); }

.btn--sm { padding: 5px 11px; font-size: var(--fs-xs); border-radius: 7px; }
.btn--lg { padding: 12px 24px; font-size: var(--fs-md); border-radius: var(--r-md); }
.btn--icon { padding: 8px; width: 34px; height: 34px; }
.btn--icon-sm { padding: 6px; width: 28px; height: 28px; border-radius: 7px; }
.btn--icon-sm svg { width: 15px; height: 15px; }

/* ---------- 7. Бейджи / чипы --------------------------------------------- */
.badge { align-items: center; gap: 5px;
  padding: 3px 9px; border-radius: var(--r-pill);
  font-size: var(--fs-xs); font-weight: 600; line-height: 1.5;
  background: var(--bg-sunken); color: var(--fg-secondary);
}
.badge--ok    { background: var(--ok-bg);    color: var(--ok-fg); }
.badge--warn  { background: var(--warn-bg);  color: var(--warn-fg); }
.badge--err   { background: var(--err-bg);   color: var(--err-fg); }
.badge--info  { background: var(--info-bg);  color: var(--info-fg); }
.badge--accent{ background: var(--accent-soft); color: var(--accent-fg); }
.badge--ai    { background: var(--muted-accent-bg); color: var(--muted-accent-fg); }
.badge__dot   { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

/* ---------- 7а. Панель тегов (макрос _macros/tags.html, гайд ГАЙД-ТЕГИ-И-ЭТАПЫ) ---------- */
.tags { position: relative; align-items: center; gap: 6px; min-width: 0; max-width: 100%; }
.tags__list { flex-wrap: wrap; gap: 6px; min-width: 0; }
.tags--collapse .tags__list { flex-wrap: nowrap; overflow: hidden; }
/* В автоматической раскладке таблицы колонка тянется по содержимому, и сворачивать нечего — даём тегам предел ширины. */
.table td .tags--collapse { max-width: 24rem; }
/* Колонка из одних меток не должна раздувать таблицу: ширину берёт у соседей, лишнее уходит в «+N» (td.td-tags-fit). */
.table td.td-tags-fit { width: 18rem; }
.table td.td-tags-fit .tags--collapse { width: 0; min-width: 100%; max-width: none; }
.tag { white-space: nowrap; text-decoration: none; }
a.tag:hover { filter: brightness(.96); }
.tag[hidden], .tags__more[hidden], .tags__panel[hidden] { display: none; }
.tags__more { border: 0; cursor: pointer; flex: none; }
.tags__more[aria-expanded="true"] { background: var(--accent-soft); color: var(--accent-fg); }
.tags__panel { position: absolute; top: calc(100% + 6px); left: 0; z-index: 30; flex-wrap: wrap; gap: 6px;
  min-width: 200px; max-width: min(420px, 90vw); padding: 10px;
  background: var(--bg-elevated); border: 1px solid var(--line-strong); border-radius: var(--r-md); box-shadow: var(--shadow-md, 0 6px 20px rgba(0,0,0,.12)); }
.tags__empty { color: var(--fg-muted, var(--fg-secondary)); }
.tags--compact .badge { padding: 1px 7px; }
.design-catalog { max-width: 920px; }
.design-catalog h2 { margin: 0 0 4px; font-size: var(--fs-xl); }
.design-catalog > p { margin: 0; }
.design-catalog section > p { margin: 0 0 var(--s-3); }
.dc-label { margin: var(--s-4) 0 var(--s-2); font-size: var(--fs-xs); font-weight: 600; color: var(--fg-secondary); text-transform: uppercase; letter-spacing: .04em; }
.dc-code { margin: var(--s-4) 0 0; padding: var(--s-3) var(--s-4); background: var(--bg-sunken); border-radius: var(--r-md); font-size: var(--fs-sm); overflow-x: auto; }
.dc-row { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-3); }
.dc-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--s-3); margin-top: var(--s-2); }
.dc-gap { margin-top: var(--s-3); }
.tags-demo-narrow { width: 320px; max-width: 100%; }

/* ---------- 7б. Этапы и лента событий (макрос _macros/stages.html) ---------- */
.stages { margin: 0; padding: 0; list-style: none; }
.stage { position: relative; }
.stage__dot { width: 24px; height: 24px; border-radius: 50%; flex: none; align-items: center; justify-content: center;
  font-size: var(--fs-xs); font-weight: 700; background: var(--bg-sunken); color: var(--fg-secondary); border: 1px solid var(--line-strong); }
.stage__dot svg { width: 13px; height: 13px; }
.stage--done .stage__dot { background: var(--ok-bg); color: var(--ok-fg); border-color: transparent; }
.stage--current .stage__dot { background: var(--brand-600); color: #fff; border-color: transparent; }
.stage--err .stage__dot { background: var(--err-bg); color: var(--err-fg); border-color: transparent; }
.stage--todo .stage__title { color: var(--fg-muted); }
.stage__title { font-weight: 600; }
.stage__sub, .stage__date { font-size: var(--fs-xs); color: var(--fg-muted); }
.stage__text { margin-top: 2px; color: var(--fg-secondary); white-space: pre-line; }
.stage__tags { margin-top: 6px; }
.stages--h { gap: 0; overflow-x: auto; padding-bottom: 4px; }
/* Горизонтальный степпер: кружок сверху, текст под ним, линия идёт между кружками. Название — первой строкой у всех этапов,
   поэтому оно стоит на одной высоте, даже если у этапа нет даты. */
.stages--h .stage { flex: 1 0 96px; flex-direction: column; gap: 8px; align-items: stretch; padding-right: 8px; }
.stages--h .stage:not(:last-child)::after { content: ""; position: absolute; top: 11px; left: 32px; right: 8px; height: 2px; background: var(--line-strong); }
.stages--h .stage--done:not(:last-child)::after { background: var(--ok-fg); opacity: .5; }
.stages--h .stage__body { min-width: 0; display: flex; flex-direction: column; }
.stages--h .stage__title { order: 1; font-size: var(--fs-sm); line-height: 1.3; }
.stages--h .stage__sub { order: 2; }
.stages--h .stage__date { order: 3; }
.stages--h .stage__text, .stages--h .stage__tags { order: 4; }
.stages--v .stage { gap: 12px; padding-bottom: 14px; }
.stages--v .stage:not(:last-child)::before { content: ""; position: absolute; left: 11.5px; top: 28px; bottom: 2px; width: 1px; background: var(--line-strong); }
.stages--v .stage__dot { width: 24px; height: 24px; margin-top: 1px; }
.stages--v .stage:not([class*="stage--"]) .stage__dot { width: 10px; height: 10px; margin: 6px 7px; background: var(--line-strong); border: 0; }
.stages__rest > summary { cursor: pointer; list-style: none; margin: 0 0 12px 36px; }
.stages__rest > summary::-webkit-details-marker { display: none; }
.stages__rest[open] > summary { display: none; }

/* ---------- 7в. Модальное окно и тосты (макрос _macros/modal.html, toast.js) ---------- */
html.modal-open { overflow: hidden; }
.modal { margin: auto; padding: 0; border: 0; background: transparent; color: inherit; max-width: none; max-height: none; width: min(calc(100% - 32px), 32rem); }
.modal--sm { width: min(calc(100% - 32px), 24rem); }
.modal--lg { width: min(calc(100% - 32px), 48rem); }
.modal--xl { width: min(calc(100% - 32px), 72rem); }
.modal::backdrop { background: rgb(15 23 42 / .45); }
.modal__box { flex-direction: column; max-height: 90vh; background: var(--bg-elevated); border: 1px solid var(--line-strong);
  border-radius: var(--r-lg); box-shadow: 0 20px 50px rgb(0 0 0 / .25); overflow: hidden; }
.modal__head { align-items: flex-start; justify-content: space-between; gap: 12px; padding: 18px 20px 12px; }
.modal__title { margin: 0; font-size: var(--fs-lg); font-weight: 600; }
.modal__sub { margin: 4px 0 0; font-size: var(--fs-sm); color: var(--fg-muted); }
.modal__body { overflow-y: auto; padding: 0 20px 20px; min-height: 0; }
.modal__foot { position: sticky; bottom: -20px; justify-content: flex-end; gap: 8px; margin: 16px -20px -20px; padding: 14px 20px;
  background: var(--bg-elevated); border-top: 1px solid var(--line); }
@media (max-width: 640px) {
  .modal { width: 100%; margin: auto 0 0; }
  .modal__box { border-radius: var(--r-lg) var(--r-lg) 0 0; max-height: 92vh; }
  .modal__foot { flex-wrap: wrap; }
  .modal__foot .btn { flex: 1 1 auto; }
}
/* Панель справа (drawer): тот же <dialog class="modal">, прижат к правому краю (макрос _macros/drawer.html) */
.drawer { margin: 0 0 0 auto; width: min(100%, 30rem); height: 100dvh; }
.drawer::backdrop { background: rgb(15 23 42 / .45); }
.drawer[open] { animation: drawer-in .18s ease-out; }
@keyframes drawer-in { from { transform: translateX(24px); opacity: 0; } to { transform: none; opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .drawer[open] { animation: none; } }
.drawer__box { flex-direction: column; height: 100%; background: var(--bg-elevated); border-left: 1px solid var(--line-strong); box-shadow: 0 20px 50px rgb(0 0 0 / .25); }
.drawer__head { align-items: flex-start; justify-content: space-between; gap: 12px; padding: 18px 20px 12px; border-bottom: 1px solid var(--line); }
.drawer__title { margin: 0; font-size: var(--fs-lg); font-weight: 600; }
.drawer__sub { margin: 2px 0 0; font-size: var(--fs-sm); color: var(--fg-muted); }
.drawer__body { flex: 1; min-height: 0; overflow-y: auto; padding: 16px 20px; }
.drawer__foot { align-items: center; gap: 8px; padding: 12px 20px; border-top: 1px solid var(--line); background: var(--bg-elevated); }
/* Плитка выбора (кнопка-вариант с названием и пояснением; визард «Подключить базу»). Не радио: клик сразу ведёт на следующий шаг */
.choice { text-align: left; width: 100%; margin-bottom: 8px; padding: 12px 16px; border: 1px solid var(--line-strong); border-radius: var(--r-md); background: var(--bg-elevated); color: inherit; font: inherit; cursor: pointer; }
.choice:hover { background: var(--bg-sunken); border-color: var(--fg-faint); }
.choice:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.choice__title { font-weight: 600; font-size: var(--fs-sm); }
.choice__text { margin-top: 4px; font-size: var(--fs-xs); color: var(--fg-secondary); }
.choice .badge { margin-top: 8px; }
.toasts { position: fixed; right: 16px; bottom: 16px; z-index: 60; flex-direction: column; gap: 8px; width: min(calc(100vw - 32px), 24rem); pointer-events: none; }
.toasts[popover] { inset: auto 16px 16px auto; margin: 0; padding: 0; border: 0; background: transparent; overflow: visible; }
.toast { align-items: center; gap: 10px; padding: 10px 12px; border-radius: var(--r-md); font-size: var(--fs-sm);
  background: var(--fg); color: var(--bg-elevated); box-shadow: 0 8px 24px rgb(0 0 0 / .2); pointer-events: auto; }
.toast--ok { background: var(--ok-bg); color: var(--ok-fg); border: 1px solid var(--ok-fg); }
.toast--err { background: var(--err-bg); color: var(--err-fg); border: 1px solid var(--err-fg); }
.toast__text { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.toast__act, .toast__x { border: 0; background: transparent; color: inherit; cursor: pointer; font: inherit; }
.toast__act { font-weight: 600; text-decoration: underline; }
.toast__x { font-size: 18px; line-height: 1; opacity: .7; }
@media (max-width: 640px) { .toasts, .toasts[popover] { right: 16px; left: 16px; width: auto; } }

/* ---------- 7г. Рабочий список: панель, чипы, сортировка, страницы, выбор строк, состояния (макрос _macros/list.html, datatable.js) ---------- */
.list-toolbar { align-items: center; gap: var(--s-3); margin-bottom: var(--s-4); flex-wrap: wrap; }
.list-toolbar .segmented { max-width: 100%; overflow-x: auto; }
.list-toolbar__fields { flex-wrap: wrap; align-items: center; gap: var(--s-2); }
.list-toolbar__fields .input { width: 240px; max-width: 100%; }
.list-toolbar__fields .select { width: 200px; max-width: 100%; }
.list-toolbar__action { margin-left: auto; }
.list-toolbar [data-more-toggle] { position: relative; width: 40px; height: 40px; }
.list-toolbar [data-more-count] { position: absolute; top: -6px; right: -6px; min-width: 18px; padding: 0 5px; font-size: var(--fs-xs); }
.list-toolbar__chips { flex-wrap: wrap; align-items: center; gap: 6px; flex-basis: 100%; }
.filter-chip { align-items: center; gap: 6px; padding: 3px 6px 3px 10px; border-radius: var(--r-pill); font-size: var(--fs-xs); font-weight: 600;
  background: var(--accent-soft); color: var(--accent-fg); text-decoration: none; }
.filter-chip:hover { filter: brightness(.95); text-decoration: none; }
button.filter-chip { border: 0; cursor: pointer; font-family: inherit; }   /* чип-кнопка: живой режим без перезагрузки, как <a> со ссылкой */
.segmented svg { width: 18px; height: 18px; }   /* переключатель с иконками вместо подписей */
/* Сводка «что показано» с действиями (_macros/summary.html): переключатель в углу, быстрые поля под линией */
.summary--actions { position: relative; }
.summary--actions > :not(.summary__actions):not(.summary__extra) { padding-right: 112px; }
.summary__actions { position: absolute; top: var(--s-3); right: var(--s-3); }
.summary__extra { margin-top: var(--s-3); padding-top: var(--s-3); border-top: 1px solid var(--line); }
.summary__quick { align-items: center; gap: var(--s-2); flex-wrap: wrap; }
.filter-chip__x { place-items: center; width: 16px; height: 16px; border-radius: 50%; font-size: 13px; line-height: 1; }
.filter-chip:hover .filter-chip__x { background: rgb(0 0 0 / .1); }
th[aria-sort] .sort { display: inline-flex; align-items: center; gap: 6px; color: inherit; text-decoration: none; cursor: pointer;
  background: none; border: 0; padding: 0; font: inherit; text-transform: inherit; letter-spacing: inherit; }
th[aria-sort] .sort::after { content: ""; width: 8px; height: 5px; background: currentColor; opacity: .25;
  clip-path: polygon(0 0, 100% 0, 50% 100%); }
th[aria-sort="ascending"] .sort::after { clip-path: polygon(50% 0, 100% 100%, 0 100%); opacity: 1; }
th[aria-sort="descending"] .sort::after { opacity: 1; }
th[aria-sort="ascending"], th[aria-sort="descending"] { color: var(--fg); }
.sort:hover { color: var(--fg); text-decoration: none; }
.sort:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.table .col-select { width: 40px; padding-right: 0; }
/* Чекбокс выбора строки (issue 067 п. 2, «Таблицы»): виден при наведении и фокусе; после первого выбора — у всех строк
   (режим массовых действий, класс has-selection ставит datatable.js); «выбрать все» в шапке и устройства без наведения — всегда. */
[data-datatable] tbody .col-select input[data-select-row] { opacity: 0; transition: opacity .1s; }
[data-datatable] tbody tr:hover .col-select input[data-select-row],
[data-datatable] tbody tr:focus-within .col-select input[data-select-row],
[data-datatable].has-selection tbody .col-select input[data-select-row],
[data-datatable] tbody .col-select input[data-select-row]:checked { opacity: 1; }
@media (hover: none) { [data-datatable] tbody .col-select input[data-select-row] { opacity: 1; } }
.table tbody tr[aria-selected="true"] { background: var(--accent-soft); }
/* Охранное поле --bulkbar-guard: плашка никогда не ближе 5 px к краю окна (и к чёлке/полосе жестов iOS) —
   ГАЙД-ФИЛЬТРЫ-И-ТАБЛИЦА п. 14. Отступ снизу и ширина считаются от него, а не «на глаз». */
.bulkbar { --bulkbar-guard: 5px; position: fixed; left: 50%; transform: translateX(-50%); z-index: 40;
  bottom: max(24px, calc(env(safe-area-inset-bottom, 0px) + var(--bulkbar-guard)));
  align-items: center; flex-wrap: wrap; gap: var(--s-3); width: max-content;
  max-width: min(90vw, 960px, calc(100vw - 2 * var(--bulkbar-guard))); padding: 8px 14px;
  border: 1px solid var(--line-strong); border-radius: var(--r-md); background: var(--bg-elevated); box-shadow: var(--sh-md); font-size: var(--fs-sm); }
/* Плавающая кнопка («пришлёпка»): вызывает свёрнутую панель — чат ассистента, подсказки. Круг в углу окна, поверх содержимого, ниже уведомлений. */
.fab { position: fixed; z-index: 44; right: max(20px, calc(env(safe-area-inset-right, 0px) + 12px)); bottom: max(20px, calc(env(safe-area-inset-bottom, 0px) + 12px));
  width: 56px; height: 56px; padding: 0; align-items: center; justify-content: center; border: 0; border-radius: 50%; background: var(--accent); color: #fff;
  box-shadow: var(--sh-md), 0 0 0 6px var(--accent-soft); cursor: pointer; }
.fab svg { width: 26px; height: 26px; }
.fab:hover { filter: brightness(1.08); }
.fab:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.bulkbar__count { font-weight: 600; }
.bulkbar__actions { gap: var(--s-2); margin-left: auto; }
@media (max-width: 640px) { .bulkbar { bottom: max(12px, calc(env(safe-area-inset-bottom, 0px) + var(--bulkbar-guard))); width: calc(100vw - 24px); max-width: calc(100vw - 2 * var(--bulkbar-guard)); } }
/* Меню «⋯» у строки и «Ещё» в панели: <details class="row-menu" data-menu>, панель позиционирует menu.js (fixed, не режется прокруткой таблицы) */
.row-menu { position: relative; }
.row-menu > summary { list-style: none; cursor: pointer; }
.row-menu > summary::-webkit-details-marker { display: none; }
/* Меню внешней системы («1С ▾» в topbar страницы): жёлтый тон, как у отметки 1С; ls.row_menu(..., cls='row-menu--ext') */
.row-menu--ext > summary { background: var(--warn-bg); color: var(--warn-fg); border: 1px solid color-mix(in srgb, var(--warn-fg) 22%, transparent); font-weight: 600; }
.row-menu--ext > summary:hover { background: color-mix(in srgb, var(--warn-bg) 80%, var(--warn-fg)); }
tbody tr .row-menu:not([open]) > summary { opacity: .35; }
tbody tr:hover .row-menu > summary, tbody tr:focus-within .row-menu > summary, tbody tr .row-menu[open] > summary { opacity: 1; }
@media (hover: none) { tbody tr .row-menu:not([open]) > summary { opacity: 1; } }
.menu-panel { position: fixed; z-index: 70; min-width: 190px; max-width: 440px; padding: 4px; margin: 0;
  background: var(--bg-elevated); border: 1px solid var(--line-strong); border-radius: var(--r-md); box-shadow: var(--sh-md); font-size: var(--fs-sm); }
.menu-panel__item { padding: 7px 10px; border: 0; border-radius: var(--r-sm); background: transparent; color: var(--fg); font: inherit; text-align: left; text-decoration: none; cursor: pointer; }
.menu-panel__item:hover, .menu-panel__item:focus-visible { background: var(--bg-sunken); text-decoration: none; }
.menu-panel__item--danger { color: var(--err-fg); }
.menu-panel--wide { width: 420px; max-width: calc(100vw - 16px); }
.menu-panel__hint { margin-top: 1px; font-size: var(--fs-xs); color: var(--fg-muted); }
.menu-panel__item:disabled { opacity: .45; cursor: default; }
.menu-panel__item:disabled:hover { background: transparent; }
.menu-panel__sep { height: 1px; margin: 4px 0; background: var(--surface-line); }
.pager { align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--s-3); padding: 10px 14px;
  border-top: 1px solid var(--surface-line); font-size: var(--fs-sm); color: var(--fg-secondary);
  position: sticky; bottom: 0; z-index: 2; background: var(--bg-elevated); }   /* пейджер на виду, не надо долистывать до конца */
.pager__pages { gap: 4px; }
.pager__pages a, .pager__pages span { min-width: 30px; height: 30px; place-items: center; padding: 0 8px; border-radius: var(--r-sm); color: var(--fg-secondary); text-decoration: none; }
.pager__pages a:hover { background: var(--bg-hover); text-decoration: none; }
.pager__pages [aria-current="page"] { background: var(--accent-soft); color: var(--accent-fg); font-weight: 700; }
.pager__pages [aria-disabled="true"] { opacity: .4; }
.state { place-items: center; text-align: center; gap: 8px; padding: 32px 16px; color: var(--fg-secondary); }
.state h4 { margin: 0; color: var(--fg); }
.state p { margin: 0; font-size: var(--fs-sm); }
.state--row td { padding: 0; }
.state__icon { width: 64px; height: 64px; flex: none; }
/* Статус автосохранения формы (макрос save_status.html, save-status.js) */
.save-status { align-items: center; gap: 8px; min-height: 24px; font-size: var(--fs-sm); color: var(--fg-muted); }
.save-status--ok { color: var(--ok-fg); }
.save-status--err { color: var(--err-fg); }
.save-status__retry { border: 0; background: transparent; color: inherit; font: inherit; text-decoration: underline; cursor: pointer; padding: 0; }
.save-status .spinner { width: 14px; height: 14px; }
.table td .badge { white-space: nowrap; }
/* Подпись, видимая только скринридеру: макросы list.html подписывают поля так, а не все продукты грузят Tailwind. */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
.cell-sub { display: block; font-size: var(--fs-xs); color: var(--fg-muted); font-weight: 400; }
@media (max-width: 640px) {
  .list-toolbar__fields { width: 100%; }
  .list-toolbar__fields .input, .list-toolbar__fields .select { width: 100%; }
  .list-toolbar__action { margin-left: 0; width: 100%; }
  .list-toolbar__action .btn { width: 100%; }
}

/* ---------- 8. Формы ----------------------------------------------------- */
.field { flex-direction: column; gap: 6px; }
.label { font-size: var(--fs-xs); font-weight: 600; color: var(--fg-secondary); text-transform: uppercase; letter-spacing: .04em; }
.input, .select, .textarea {
  font-family: var(--font-ui); font-size: var(--fs-sm); color: var(--fg);
  background: var(--bg-elevated);
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  padding: 8px 11px;
  transition: border-color var(--transition), box-shadow var(--transition);
}
/* width:100% только внутри .field — сознательного layout-контейнера.
   Без .field элемент сохраняет свой обычный intrinsic/explicit размер,
   поэтому класс можно безопасно навешивать на старую разметку рядом с
   любым существующим w-32/min-w-[...] без изменения раскладки. */
.field .input, .field .select, .field .textarea { width: 100%; }
.input::placeholder, .textarea::placeholder { color: var(--fg-faint); }
.input:focus, .select:focus, .textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.select {
  appearance: none; padding-right: 30px; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%236b7280'%3E%3Cpath d='M5.5 7.5 10 12l4.5-4.5z'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 8px center; background-size: 16px;
}
/* Все поля и списки в строке — одной высоты (M = 40 px, как кнопка), иначе ряд «прыгает» (гайд «Формы и поля», п. 21). */
.input, .select { min-height: var(--control-h, 40px); box-sizing: border-box; }
.select--sm { min-height: 0; padding: 5px 24px 5px 10px; font-size: var(--fs-xs); border-radius: 7px;
  background-size: 14px; background-position: right 6px center; }
.textarea { resize: vertical; min-height: 84px; line-height: 1.55; }
/* Касание (гайд «Мобильный интерфейс», «Телефон: размеры, касание»): только для тач-экранов, мышь не затрагивается.
   Поле 16 px — iOS не приближает страницу при фокусе; цель нажатия не ниже 40 px (Контур, «Адаптивность»). */
@media (pointer: coarse) {
  .input, .select, .textarea, .select--sm { font-size: 16px; }
  /* Любое текстовое поле, в том числе самописное и из JS: специфичность выше одного класса, чтобы text-sm/свои классы не вернули 14 px */
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="hidden"]):not([type="submit"]):not([type="button"]), select:not([data-fs]):not([data-fs2]), textarea:not([data-fs]):not([data-fs2]) { font-size: 16px; }
  .select--sm { min-height: 40px; }
  .btn--sm { min-height: 40px; padding: 9px 14px; font-size: var(--fs-sm); }
  .btn--icon, .btn--icon-sm { min-width: 40px; min-height: 40px; width: auto; height: auto; padding: 8px; }
}
.input-group { position: relative; }
.input-group .input { padding-left: 34px; }
.input-group svg { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); width: 16px; height: 16px; color: var(--fg-faint); }

/* Форма: секции, сетка, подсказка, ошибка поля, панель действий (макрос _macros/form.html, form.js; гайд ГАЙД-ФОРМЫ-И-ПОЛЯ) */
.form-section { margin: 0 0 20px; }
.form-section__head { margin: 0 0 12px; }
.form-section__title { margin: 0; font-size: var(--fs-md, 16px); font-weight: 600; }
.form-section__desc { margin: 2px 0 0; font-size: var(--fs-sm); color: var(--fg-muted); }
.form-grid { grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }
/* Ряд из нескольких полей в одну строку: по верхнему краю, без нижнего отступа у полей (его даёт сетка), подпись в одну строку, подсказка под полем не двигает соседей. */
.field-row { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 16px; align-items: start; }
.field-row > .field { margin-bottom: 0; }
.field-row .label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.form-grid > .field--wide { grid-column: 1 / -1; }
@media (max-width: 700px) { .form-grid { grid-template-columns: 1fr; } }
.label--req::after { content: " *"; color: var(--err-fg); }
.label__opt { font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--fg-muted); }
.field__hint { margin: 0; font-size: var(--fs-xs); color: var(--fg-muted); }
.field__err { margin: 0; font-size: var(--fs-xs); color: var(--err-fg); }
.field--err .input, .field--err .select, .field--err .textarea { border-color: var(--err-fg); }
.field--err .input:focus, .field--err .select:focus, .field--err .textarea:focus { box-shadow: 0 0 0 3px var(--err-bg); }
.form-actions { align-items: center; justify-content: flex-end; gap: 8px; margin-top: 8px; padding-top: 16px; border-top: 1px solid var(--line); }
.form-actions__start { margin-right: auto; }
[data-form-error] { margin-bottom: var(--s-4); }
.form-actions--sticky { position: sticky; bottom: 0; background: var(--bg); padding-bottom: 12px; }

/* Пары «название — значение» (макрос _macros/kv.html, Kv.html() в kv.js; гайд ГАЙД-КАРТОЧКА-ОБЪЕКТА) */
.kv { margin: 0; gap: 10px 24px; }
.kv--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.kv--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.kv--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 1000px) { .kv--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.kv__item { margin: 0; min-width: 0; grid-template-columns: minmax(120px, .7fr) minmax(0, 1.3fr); gap: 2px 16px; align-items: baseline; }
.kv--stack .kv__item { grid-template-columns: 1fr; gap: 2px; }
.kv dt { margin: 0; font-size: var(--fs-sm); color: var(--fg-muted); }
.kv dd { margin: 0; min-width: 0; overflow-wrap: anywhere; font-size: var(--fs-sm); color: var(--fg); }
.kv--stack dt { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .04em; font-weight: 600; }
.kv__empty { color: var(--fg-faint); }
.kv__hint { display: block; margin-top: 2px; font-size: var(--fs-xs); color: var(--fg-muted); }
.kv__copy { margin-left: 6px; vertical-align: middle; }

/* Поле с секретом: токен, пароль, код, одноразовая ссылка (макрос _macros/secret.html, secret.js; гайд ГАЙД-СЕКРЕТЫ-И-ТОКЕНЫ) */
.secret { align-items: center; gap: 8px; padding: 8px 8px 8px 12px; background: var(--bg-sunken, #f3f4f6); border: 1px solid var(--line-strong); border-radius: var(--r-sm); }
.secret__value { flex: 1; min-width: 0; overflow-wrap: anywhere; font-family: var(--font-mono); font-size: var(--fs-sm); letter-spacing: .04em; user-select: none; }
.secret--open .secret__value { letter-spacing: 0; user-select: all; }
.secret__actions { gap: 4px; flex: none; }
.secret__note { margin: 8px 0 0; }
.secret__meta { margin: 6px 0 0; font-size: var(--fs-xs); color: var(--fg-muted); }
.secret-actions { gap: 8px; flex-wrap: wrap; margin-top: 12px; }

/* Аккордеон: сворачиваемые блоки на нативном <details> (макрос _macros/accordion.html, accordion.js; гайд ГАЙД-АККОРДЕОН) */
.acc { gap: 8px; }
.acc__bar { gap: 8px; justify-content: flex-end; margin-bottom: 4px; }
.acc__item { background: var(--bg-elevated); border: 1px solid var(--line-strong); border-radius: var(--r-md, 10px); }
.acc__head { align-items: center; gap: 10px; padding: 12px 14px; cursor: pointer; list-style: none; min-height: 44px; border-radius: inherit; }
.acc__head::-webkit-details-marker { display: none; }
.acc__head:hover { background: var(--bg-sunken); }
.acc__head:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.acc__title { flex: 1; min-width: 0; font-weight: 600; font-size: var(--fs-sm); }
.acc__meta { font-size: var(--fs-xs); color: var(--fg-muted); }
.acc__chev { flex: none; color: var(--fg-muted); transition: transform .15s; }
.acc__item[open] > .acc__head .acc__chev { transform: rotate(180deg); }
.acc__body { padding: 4px 14px 14px; font-size: var(--fs-sm); color: var(--fg-secondary); }
.acc--flat { gap: 0; }
.acc--flat .acc__item { border: 0; border-bottom: 1px solid var(--line); border-radius: 0; background: transparent; }

/* Правка значения по месту (edit-in-place.js; гайд ГАЙД-КАРТОЧКА-ОБЪЕКТА, п. 14) */
.eip { align-items: baseline; gap: 6px; min-width: 0; }
.eip__value { min-width: 0; overflow-wrap: anywhere; }
.eip__empty { color: var(--fg-faint); }
.eip__edit { opacity: 0; transition: opacity .12s; }
.eip:hover .eip__edit, .eip:focus-within .eip__edit, .eip__edit:focus-visible { opacity: 1; }
@media (hover: none) { .eip__edit { opacity: 1; } }
.eip__form { gap: 6px; width: 100%; }
.eip__row { align-items: center; gap: 6px; }
.eip__row .input, .eip__row .textarea { flex: 1; min-width: 0; }
.eip__row .textarea { resize: none; overflow: hidden; }   /* растёт по мере ввода (edit-in-place.js) */
.eip__row .spinner { flex-shrink: 0; align-self: center; }
.eip__err { margin: 0; font-size: var(--fs-xs); color: var(--err-fg); }
.eip__hint { margin: 0; font-size: var(--fs-xs); color: var(--fg-muted); }
.eip--busy { opacity: .6; pointer-events: none; }

/* Блок кода с кнопкой «Скопировать» (code-block.js, макрос _macros/code.html; гайд ГАЙД-БЛОК-КОДА) */
.codeblock { position: relative; }
.codeblock__pre { margin: 0; padding: 10px 112px 10px 12px; background: #0f172a; color: #e2e8f0; border-radius: var(--r-md); font-family: var(--font-mono, ui-monospace, monospace); font-size: var(--fs-xs); white-space: pre-wrap; overflow-wrap: anywhere; overflow-x: auto; }
.codeblock__copy { position: absolute; top: 6px; right: 6px; }

/* Список и карточка (master–detail; master-detail.js, макрос _macros/master_detail.html; гайд ГАЙД-СПИСОК-И-КАРТОЧКА).
   Класс .list-card, не .md: .md уже занят под рендер markdown-текста (секция
   «Markdown» ниже) — то же имя значило бы, что любой блок с текстом продукта/
   паспорта получает заодно grid-колонки и min-height 480px master-detail'а
   (найдено 08.10.2026 — карточка продукта с пустой второй колонкой на вкладке
   «Внутренние заметки»). */
.list-card { grid-template-columns: minmax(280px, 360px) minmax(0, 1fr); border: 1px solid var(--line-strong); border-radius: var(--r-md); background: var(--bg-elevated); overflow: hidden; min-height: 480px; }
.list-card__list { border-right: 1px solid var(--line); flex-direction: column; min-width: 0; }
.list-card__items { overflow: auto; max-height: 70vh; }
.list-card__tools { padding: 10px; border-bottom: 1px solid var(--line); gap: 8px; }
.list-card__item { gap: 2px; padding: 10px 14px; border: 0; border-bottom: 1px solid var(--line); cursor: pointer; text-align: left; width: 100%; background: transparent; font: inherit; color: inherit; }
.list-card__item:hover { background: var(--bg-sunken); }
.list-card__item[aria-current="true"] { background: var(--accent-soft); box-shadow: inset 0 0 0 1px var(--accent); }
/* Вариант «чат» (--chat): список 320–360 px со своей прокруткой; поиск и фильтры внутри колонки; справа переписка. Чаты DODO. */
.list-card--chat { grid-template-columns: minmax(320px, 360px) minmax(0, 1fr); height: var(--chat-h, calc(100vh - 160px)); min-height: 560px; } /* --chat-h выставляет скрипт страницы по высоте окна */
.list-card--chat .list-card__list { min-height: 0; }
.list-card--chat .list-card__detail { min-height: 0; }
/* Карточка чата тянется на всю высоту: лента скроллится сама, поле ввода всегда внизу. */
.chat-card { flex-direction: column; height: 100%; min-height: 0; }
.chat-card .chat-thread { flex: 1; flex-direction: column; min-height: 0; }
.chat-card .chat-thread__log { flex: 1; min-height: 0; max-height: none; }
.chat-card .chat-thread__compose { flex: none; }
.list-card--chat .list-card__list { border-right: 1px solid var(--line-strong); }
.list-card--chat .list-card__items { flex: 1; min-height: 0; max-height: none; }
.list-card--chat .list-card__item { border-bottom: 1px solid var(--surface-line); padding: 10px 14px; }
.list-card--chat .list-card__item[aria-current="true"] { background: var(--accent-soft); box-shadow: inset 0 0 0 1px var(--accent); }
.list-card--chat .list-card__detail { padding: 0 var(--s-4) var(--s-3); }
.list-card--chat .list-card__tools { flex-direction: column; align-items: stretch; }
.list-card--chat .list-toolbar { margin: 0; gap: var(--s-2); }
.list-card--chat .list-toolbar__fields { order: -1; width: 100%; }
.list-card--chat .list-toolbar__fields .input { width: 100%; }
.list-card--chat .segmented a { padding: 5px 8px; font-size: var(--fs-xs); white-space: nowrap; }
.chat-list-sort { justify-content: space-between; align-items: center; gap: var(--s-2); min-height: 28px; }
.chat-list-sort .text-sm { color: var(--fg-muted); min-width: 0; }
.chat-row__top { align-items: center; justify-content: space-between; gap: var(--s-2); }
.chat-row__title { font-weight: 600; font-size: var(--fs-sm); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-row__meta { align-items: center; gap: var(--s-2); flex: none; color: var(--fg-muted); font-size: var(--fs-xs); }
.chat-row__preview { font-size: var(--fs-xs); color: var(--fg-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-row--unread .chat-row__preview { color: var(--fg); font-weight: 600; }
.chat-row--unread .chat-row__meta { color: var(--accent); font-weight: 600; }
.chat-row__preview--empty { font-style: italic; }
.chat-row__at { font-weight: 700; color: var(--accent); }
.list-card__empty-box { justify-items: center; gap: var(--s-2); text-align: center; max-width: 260px; }
.list-card__empty-box strong { color: var(--fg); font-size: var(--fs-base, 1rem); }
.list-card__empty-icon { color: var(--fg-muted); opacity: .6; }
.list-card__empty-icon svg { width: 40px; height: 40px; }
.list-card__item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.list-card__name { font-weight: 600; font-size: var(--fs-sm); gap: 8px; align-items: center; justify-content: space-between; }
.list-card__title { min-width: 0; overflow-wrap: anywhere; }
.list-card__sub { font-size: var(--fs-xs); color: var(--fg-muted); }
.list-card__item--lead { grid-template-columns: auto minmax(0, 1fr); column-gap: 10px; align-items: center; }
.list-card__text { gap: 2px; min-width: 0; }
.list-card__detail { padding: 18px 22px; min-width: 0; }
.list-card__back { margin-bottom: 10px; }
.list-card__empty { place-items: center; min-height: 300px; color: var(--fg-muted); font-size: var(--fs-sm); }
@media (max-width: 900px) {
  .list-card { grid-template-columns: minmax(0, 1fr); }
  .list-card[data-view="detail"] .list-card__list, .list-card[data-view="list"] .list-card__detail { display: none; }
  .list-card__list { border-right: 0; }
  .list-card__items { max-height: none; }
}

/* Командная палитра Cmd/Ctrl+K (command-palette.js; гайд ГАЙД-ПАЛИТРА-КОМАНД) */
.cp-dlg { margin: 12vh auto auto; width: min(640px, calc(100% - 32px)); }
.cp { flex-direction: column; max-height: 70vh; background: var(--bg-elevated); border: 1px solid var(--line-strong); border-radius: 14px; box-shadow: 0 24px 60px rgb(0 0 0 / .3); overflow: hidden; }
.cp__in { align-items: center; gap: 10px; padding: 12px 16px; border-bottom: 1px solid var(--line); }
.cp__in svg { color: var(--fg-muted); flex: none; }
.cp__in input { flex: 1; min-width: 0; border: 0; outline: 0; background: transparent; font: inherit; font-size: var(--fs-md); color: var(--fg); }
.cp__list { overflow: auto; padding: 6px; }
.cp__grp { padding: 8px 10px 4px; font-size: var(--fs-xs); font-weight: 600; text-transform: uppercase; letter-spacing: .06em; color: var(--fg-muted); }
.cp__item { align-items: center; gap: 10px; width: 100%; padding: 9px 10px; border-radius: 8px; color: inherit; text-decoration: none; cursor: pointer; }
.cp__item[aria-selected="true"] { background: var(--accent-soft); }
.cp__t { flex: 1; min-width: 0; font-size: var(--fs-sm); }
.cp__t mark { background: transparent; color: var(--accent-fg); font-weight: 700; }
.cp__sub { display: block; font-size: var(--fs-xs); color: var(--fg-muted); }
.cp__kbd { font-size: 11px; color: var(--fg-muted); border: 1px solid var(--line-strong); border-radius: 5px; padding: 1px 6px; }
.cp__foot { gap: 14px; padding: 8px 16px; border-top: 1px solid var(--line); font-size: var(--fs-xs); color: var(--fg-muted); }
.cp__empty { padding: 28px; text-align: center; color: var(--fg-muted); font-size: var(--fs-sm); }
.cp-open { color: var(--fg-muted); }
.cp__x { display: none; flex: none; }
@media (max-width: 640px) {
  .cp-dlg { margin: 0; width: 100%; max-width: 100%; height: 100%; max-height: 100%; }
  .cp { max-height: 100%; height: 100%; border-radius: 0; border: 0; padding-top: env(safe-area-inset-top); box-sizing: border-box; }
  .cp__in { padding-inline: 12px; }
  /* 16px — иначе iOS Safari увеличивает страницу при фокусе, и окно съезжает (как у .input выше) */
  .cp__in input { font-size: 16px; }
  .cp__kbd, .cp__foot { display: none; }
  .cp__x { display: inline-flex; }
  .cp__list { padding-bottom: env(safe-area-inset-bottom); }
}
@media (max-width: 700px) {
  .kv--2, .kv--3, .kv--4 { grid-template-columns: 1fr; }
  .kv__item { grid-template-columns: 1fr; }
}

.filters { flex-wrap: wrap; align-items: flex-end; gap: var(--s-3); }
.filters .field { min-width: 150px; }

/* Прокручиваемый список с рамкой (чекбоксы выбора, узкие перечни) — граница
   всегда через var(--surface-line)/var(--r-lg), НЕ голые border/divide-y:
   Tailwind v4 красит border-color в currentColor без явного цвета, и в этом
   проекте нет preflight-сброса на серый (issue 006, «Доступные сайты Метрики»). */
.list-box { border: 1px solid var(--surface-line); border-radius: var(--r-lg); max-height: 320px; overflow-y: auto; }
.list-row:not(:last-child) { border-bottom: 1px solid var(--surface-line); }

/* Список с поиском вместо браузерного <select> (issue 027) — усиление
   через app/static/js/searchable-select.js, только pointer:fine. Исходный
   <select> остаётся в DOM (для форм/существующего JS), визуально скрыт. */
.searchable-select { position: relative; }
/* !important — `.field .select { width: 100% }` (специфичность 0-2-0) иначе
   перебивает скрытие 0-1-0 у нативного select внутри .field: он остаётся
   растянутым на всю ширину обёртки, absolute-позиционирование выносит эту
   ширину в scrollable overflow страницы — невидимый select ломает горизонтальный
   скролл всей страницы (issue 027, найдено на «Отчётах» кассы). */
.searchable-select__native {
  position: absolute; left: 0; top: 0; opacity: 0; pointer-events: none;
  width: 1px !important; height: 1px !important; overflow: hidden;
}
/* Тот же шеврон, что у нативного .select — иначе поле не читается как
   "выбор из списка" и выглядит обычным текстовым вводом (issue 027). */
.searchable-select__input {
  width: 100%; appearance: none; cursor: pointer; padding-right: 30px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%236b7280'%3E%3Cpath d='M5.5 7.5 10 12l4.5-4.5z'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 8px center; background-size: 16px;
}
.searchable-select__panel {
  position: absolute; top: calc(100% + 4px); left: 0; z-index: 50;
  width: 100%; min-width: 180px; max-height: 260px; overflow-y: auto; overscroll-behavior: contain;   /* прокрутка списка не двигает страницу */
  padding: 4px;
}
.searchable-select__option {
  padding: 7px 10px; border-radius: var(--r-sm); cursor: pointer;
  font-size: var(--fs-sm); color: var(--fg);
}
.searchable-select__option:hover, .searchable-select__option.is-active {
  background: var(--accent-soft); color: var(--accent-fg);
}
.searchable-select__hint { display: block; font-size: var(--fs-xs); color: var(--fg-faint); }   /* ИНН, номер счёта под названием (поиск на сервере, issue 087) */
.searchable-select__option.is-active .searchable-select__hint, .searchable-select__option:hover .searchable-select__hint { color: inherit; opacity: .75; }
.searchable-select__empty {
  padding: 7px 10px; font-size: var(--fs-sm); color: var(--fg-faint);
}
.searchable-select__empty .spinner { width: 14px; height: 14px; vertical-align: -2px; }
.searchable-select__retry { border: 0; background: transparent; padding: 0; font: inherit; color: var(--accent-fg); text-decoration: underline; cursor: pointer; }

/* Сегментированный переключатель — вместо голых радио/табов */
.segmented { background: var(--bg-sunken); border-radius: var(--r-sm); padding: 3px; gap: 2px; }
.segmented button, .segmented a {
  border: 0; background: transparent; cursor: pointer; text-decoration: none; white-space: nowrap;
  font-family: var(--font-ui); font-size: var(--fs-sm); font-weight: 500; color: var(--fg-secondary);
  padding: 6px 13px; border-radius: 6px; transition: all var(--transition);
}
.segmented button[aria-selected="true"], .segmented a[aria-selected="true"] { background: var(--bg-elevated); color: var(--fg); box-shadow: var(--sh-sm); font-weight: 600; }
.segmented a:hover { color: var(--fg); text-decoration: none; }

/* Выпадающая панель (например, настройки сервиса из шестерёнки в топбаре) */
.dropdown { position: relative; }
.dropdown__panel {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 50;
  width: 320px;
}

/* Пикер периода (кнопка + панель с тремя годами месяцев, drag-выделение — паттерн 1С) */
.period-picker { position: relative; gap: var(--s-2); align-items: center; }
.period-picker__btn { align-items: center; gap: var(--s-2);
  min-width: 220px; padding: 9px var(--s-4);
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  background: var(--bg-elevated); color: var(--fg);
  font-family: var(--font-ui); font-size: var(--fs-sm); font-weight: 500;
  cursor: pointer; transition: border-color var(--transition), background var(--transition);
}
.period-picker__btn:hover { background: var(--bg-sunken); }
.period-picker__btn[aria-invalid="true"] { border-color: var(--err-fg); }
.period-picker__btn svg { width: 16px; height: 16px; flex-shrink: 0; color: var(--fg-muted); }
.period-picker__btn .period-picker__chevron { margin-left: auto; }
.period-picker__granularity { min-width: 128px; }

/* Период страниц сайтов (site-period.js): кнопки периода, «Свой период» и подпись с точными датами */
.site-period { display: flex; flex-direction: column; align-items: flex-end; gap: var(--s-1); min-width: 0; max-width: 100%; }
.site-period__row { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--s-2); }
.site-period__picker { position: relative; }
.site-period__picker > .btn::after {
  content: ""; display: inline-block; margin-left: var(--s-2); vertical-align: middle;
  border: 4px solid transparent; border-top-color: currentColor; border-bottom-width: 0;
}
.site-period__note { margin: 0; font-size: var(--fs-xs); text-align: right; }
.site-period__panel { position: absolute; right: 0; top: calc(100% + 6px); z-index: 50; width: 420px; max-width: calc(100vw - 32px); box-sizing: border-box; padding: var(--s-4); }
.site-period__panel[hidden] { display: none; }
.site-period__group { margin: 0 0 var(--s-2); font-size: var(--fs-xs); letter-spacing: .04em; text-transform: uppercase; color: var(--fg-muted); }
.site-period__presets { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-2); margin-bottom: var(--s-4); }
.site-period__preset {
  padding: 8px var(--s-3); border: 1px solid var(--surface-line); border-radius: var(--r-sm);
  background: var(--bg-elevated); color: var(--fg-secondary); font-family: var(--font-ui); font-size: var(--fs-sm);
  text-align: left; cursor: pointer; transition: background var(--transition), color var(--transition);
}
.site-period__preset:hover:not(:disabled) { background: var(--bg-sunken); color: var(--fg); }
.site-period__preset:disabled { opacity: .45; cursor: not-allowed; }
.site-period__custom { display: flex; align-items: center; gap: var(--s-2); }
.site-period__custom .input { flex: 1 1 0; min-width: 0; }
.site-period__custom .btn { flex: none; }
.site-period__error { margin: var(--s-2) 0 0; font-size: var(--fs-xs); color: var(--err-fg); }
@media (max-width: 640px) {
  .site-period { align-items: stretch; width: 100%; max-width: 100%; }
  .site-period__row { justify-content: flex-start; }
  .site-period .segmented { max-width: 100%; overflow-x: auto; scrollbar-width: none; }
  .site-period__note { text-align: left; }
  .site-period__custom { flex-wrap: wrap; }
  .site-period__custom .btn { width: 100%; }
  .site-period__panel { position: fixed; inset: auto var(--s-3) var(--s-3); width: auto; max-width: none; max-height: calc(100vh - 24px); overflow-y: auto; }
}

/* Отчёты кассы: основной сценарий остаётся в одной строке. Период закреплён
   слева; поиск точки и действия — отдельной группой у правого края. Редкие
   ограничения («Юрлицо», «Статус» и фильтр вкладки) открываются ниже по
   кнопке «Фильтры», поэтому не ломают шапку на обычном ноутбуке. */
@media (min-width: 1100px) {
  .dodo-report-filters__primary {
    grid-template-columns: max-content minmax(24px, 1fr) minmax(260px, 480px) 150px 42px;
    align-items: end;
  }
  .dodo-report-filters__period { grid-column: 1; }
  .dodo-report-filters__unit { grid-column: 3; }
  .dodo-report-filters__toggle { grid-column: 4; grid-row: 1; width: 150px; }
  .dodo-report-filters__refresh { grid-column: 5; grid-row: 1; }
  .dodo-report-filters__period .period-picker { width: max-content; }
  .dodo-report-filters__period .period-picker__btn { width: max-content; min-width: 0; }
  .dodo-report-filters__period .period-picker__granularity { width: 128px; min-width: 0; }
}

.dodo-report-filters__primary { gap: var(--s-3); }
.dodo-report-filters__refresh { width:42px; height:42px; padding:9px; }
.dodo-report-filters__toggle { height:42px; }
/* На любом не-мобильном экране дата занимает ровно ширину содержимого,
   не переносится и не тянет за собой пустой участок до стрелки. */
@media (min-width: 641px) {
  .dodo-report-filters__period .period-picker { width: max-content; }
  .dodo-report-filters__period .period-picker__btn { width: max-content; min-width: 0; }
  .dodo-report-filters__period .period-picker__btn span { white-space: nowrap; }
  /* Не хватает места — «По дням» уходит под кнопку периода, а не сжимает дату до «30.09.2026 — 17.1…» */
  .dodo-report-filters__period .period-picker { flex-wrap: wrap; max-width: 100%; }
  .dodo-report-filters__period .period-picker__btn { flex: none; }
  .dodo-report-filters__period .period-picker__granularity { width: 128px; min-width: 0; }
}
.dodo-report-filters__advanced {
  grid-template-columns: minmax(180px, 280px) 132px minmax(200px, 260px) minmax(210px, 280px);
  gap: var(--s-3); margin-top: var(--s-4); padding-top: var(--s-4);
  border-top: 1px solid var(--surface-line); align-items:end;
}

@media (max-width: 1099px) {
  .dodo-report-filters__primary { grid-template-columns:minmax(0, 1fr) 42px; align-items:end; }
  .dodo-report-filters__period, .dodo-report-filters__unit { grid-column:1; }
  .dodo-report-filters__refresh { grid-column:2; grid-row:1; }
  .dodo-report-filters__toggle { grid-column:1; width:max-content; }
  .dodo-report-filters__advanced { grid-template-columns:repeat(2, minmax(0, 1fr)); }
}

.period-panel {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 50;
  width: auto; min-width: 620px; padding: var(--s-4);
  user-select: none;
}
/* Кнопка у правого края контейнера (см. period_picker(align='right')) —
   панель открывается влево от кнопки, а не вправо за пределы экрана. */
.period-panel--right { left: auto; right: 0; }

.period-panel__nav { align-items: center; justify-content: space-between;
  gap: var(--s-3); margin-bottom: var(--s-3);
}
.period-panel__nav-btn { place-items: center; width: 30px; height: 30px;
  border-radius: var(--r-pill); border: none; background: transparent;
  color: var(--fg-muted); font-size: 16px; cursor: pointer;
  transition: background var(--transition), color var(--transition);
}
.period-panel__nav-btn:hover { background: var(--bg-sunken); color: var(--fg); }

.period-years { gap: var(--s-5); flex: 1; justify-content: center; }
.period-year-col { flex-direction: column; align-items: center; gap: var(--s-2); }
.period-year-label { font-family: var(--font-display); font-weight: 700; font-size: var(--fs-sm); color: var(--fg); }
.period-year-grid { grid-template-columns: repeat(3, 1fr); gap: 4px; }

.period-month-btn {
  min-width: 52px; padding: 7px 10px; border-radius: var(--r-sm);
  border: 1px solid transparent; background: transparent;
  font-family: var(--font-ui); font-size: var(--fs-sm); color: var(--fg-secondary);
  cursor: pointer; transition: background var(--transition), color var(--transition), border-color var(--transition);
}
.period-month-btn:hover { background: var(--bg-sunken); color: var(--fg); }
.period-month-btn.is-current { color: var(--accent-fg); font-weight: 600; border-color: var(--accent-soft); }
.period-month-btn.is-in-range { background: var(--accent-soft); color: var(--accent-fg); font-weight: 600; }
.period-month-btn.is-dragging { background: var(--accent); color: var(--on-accent-ink); font-weight: 600; }

.period-panel__presets { grid-template-columns: repeat(4, 1fr); gap: var(--s-2);
  padding: var(--s-3) 0; border-top: 1px solid var(--surface-line);
}
.period-panel__preset-btn {
  padding: 7px 0; border-radius: var(--r-sm); border: 1px solid var(--surface-line);
  background: var(--bg-elevated); color: var(--fg-secondary);
  font-family: var(--font-ui); font-size: var(--fs-xs); cursor: pointer;
  transition: background var(--transition), color var(--transition);
}
.period-panel__preset-btn:hover { background: var(--bg-sunken); color: var(--fg); }

.period-panel__custom { align-items: center; gap: var(--s-2);
  padding-top: var(--s-3); border-top: 1px solid var(--surface-line);
}
.period-panel__custom .input { flex: 1; }

/* Навигатор по месяцам (◀ Месяц Год ▶ + компактный пикер на клик по подписи) —
   для документов «один в месяц» (авансовый отчёт и т.п.), не диапазонов
   отчётов (для тех — period-picker выше). Переиспользует .period-panel__nav-btn/
   .period-year-grid/.period-month-btn для единообразия визуального языка.
   Тот же вид и для дня: макрос month_nav(id, step='day') (◀ 28 октября 2026, среда ▶),
   новых классов нет; на тач-экране стрелки 40 px (правило ниже). */
.month-nav { position: relative; align-items: center; gap: var(--s-2); }
.month-nav__btn { place-items: center; width: 30px; height: 30px;
  border-radius: var(--r-pill); border: none; background: transparent;
  color: var(--fg-muted); font-size: 16px; cursor: pointer;
  transition: background var(--transition), color var(--transition);
}
.month-nav__btn:hover:not(:disabled) { background: var(--bg-sunken); color: var(--fg); }
.month-nav__btn:disabled { color: var(--fg-faint); cursor: not-allowed; }
/* Касание: стрелки 40 px (гайд «Мобильный интерфейс»). Правило после основного — иначе оно же его не перебьёт (одна специфичность). */
@media (pointer: coarse) { .month-nav__btn { width: 40px; height: 40px; } }
.month-nav__label { padding: 6px var(--s-3); border-radius: var(--r-sm);
  border: none; background: transparent; color: var(--fg);
  font-family: var(--font-ui); font-weight: 600; font-size: var(--fs-sm);
  cursor: pointer; transition: background var(--transition);
}
.month-nav__label:hover { background: var(--bg-sunken); }
/* Шаг «день»: настоящий input type=date под подписью только якорь для календаря браузера, сам не виден. */
.month-nav__date { position: absolute; left: 0; bottom: 0; width: 100%; height: 0; padding: 0; border: 0; opacity: 0; pointer-events: none; }
.month-nav__picker {
  position: absolute; top: calc(100% + 6px); left: 50%; transform: translateX(-50%);
  z-index: 50; width: 200px; padding: var(--s-3); user-select: none;
}
.month-nav__picker .period-year-grid { grid-template-columns: repeat(3, 1fr); }
.month-nav__picker .period-month-btn.is-disabled { color: var(--fg-faint); cursor: not-allowed; }
.month-nav__picker .period-month-btn.is-disabled:hover { background: transparent; }

/* ---------- 9. Метрики (KPI) --------------------------------------------- */
.stat-grid { gap: var(--s-4); grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.stat {
  background: var(--bg-elevated); border: 1px solid var(--surface-line);
  border-radius: var(--r-lg); padding: var(--s-4) var(--s-5); flex-direction: column; gap: 6px;
}
.stat__label { font-size: var(--fs-xs); font-weight: 600; color: var(--fg-muted); text-transform: uppercase; letter-spacing: .05em; }
.stat__value { font-family: var(--font-display); font-size: var(--fs-2xl); font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.stat__foot  { font-size: var(--fs-xs); color: var(--fg-muted); align-items: center; gap: 5px; }
.delta       { font-weight: 600; font-variant-numeric: tabular-nums; }
.delta--up   { color: var(--ok); }
.delta--down { color: var(--err); }
.stat--accent { background: linear-gradient(135deg, var(--brand-900), var(--grad-mid)); border-color: transparent; color: #fff; }
.stat--accent .stat__label, .stat--accent .stat__foot { color: rgba(255,255,255,.6); }
.stat--accent .stat__value { color: var(--brand-400); }
/* плитка-фильтр: role="button" tabindex="0", клик включает отбор в списке (Анализ обновлений) */
.stat[role="button"] { cursor: pointer; }
.stat[role="button"]:hover { border-color: var(--accent); }

/* ---------- 10. Таблицы -------------------------------------------------- */
/* :where() обнуляет вклад ".table"/"tbody" в специфичность — правила ниже
   действуют как ДЕФОЛТ и всегда проигрывают любому Tailwind-утилити-классу
   на том же элементе (text-right/text-center/py-2 и т.п., которых в старой
   разметке много). Без :where() ".table th{text-align:left}" (0,1,1) перебивал
   бы одиночный класс ".text-right" (0,1,0) и молча разворачивал бы выравнивание
   у числовых колонок в уже существующих таблицах. */
.table-wrap { overflow-x: auto; border-radius: var(--r-lg); }
/* Шапка длинной таблицы (Контур, «Залипающие элементы»): обёртка с overflow сама становится областью прокрутки, и
   `sticky` у th не доходил до окна (135 из 146 таблиц), а без обёртки шапка пряталась под верхней панелью.
   Теперь длинная таблица прокручивается внутри обёртки, шапка липнет к её верху. Запас — над таблицей фильтры
   и заголовок; короткие таблицы высоту не меняют. Карточный вид телефона и таблицы в окнах — без ограничения. */
.table-wrap:has(table.table) { max-height: var(--wrap-max, max(20rem, calc(100dvh - 15rem))); overflow-y: auto; }   /* --wrap-max — sticky-table-head.js: окно минус настоящее расстояние до обёртки */
.table-wrap .table th { top: var(--th-offset, 0px); }   /* --th-offset — sticky-table-head.js: ряд шапки под предыдущим */
.table th { top: calc(var(--shell-topbar, 60px) + var(--th-offset, 0px)); }   /* таблица без обёртки липнет под верхней панелью, а не за ней */
/* Обёртка с overflow (`overflow-x-auto`, `overflow-auto`) сама область прокрутки: `top` у sticky считается от её края, а не от окна.
   60px от верхней панели сдвигали шапку на 60px вниз поверх первой строки даже без прокрутки (случай «Сайты → Результат»). */
.overflow-x-auto .table th, .overflow-auto .table th, .overflow-y-auto .table th { top: var(--th-offset, 0px); }
.modal__body .table-wrap:has(table.table), .drawer .table-wrap:has(table.table) { max-height: none; overflow-y: visible; }
@media (max-width: 640px) {
  .table-wrap:has(.responsive-table--cards) { max-height: none; overflow-y: visible; }
}
/* В слое `components`, потому что эти правила — намеренно слабый ДЕФОЛТ (см.
   комментарий выше и :where(), обнуляющий специфичность): разметка таблиц
   рассчитывает, что `text-right` на числовой колонке, `py-2` на th и т.п.
   перебивают дизайн-систему. Одного :where() для этого больше не хватает —
   неслоёвый CSS сильнее любого слоя, включая utilities (issue 019, шаг 2). */
@layer components {
  :where(.table) { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--fs-sm); }
  :where(.table) th {
    text-align: left; font-size: var(--fs-xs); font-weight: 600;
    text-transform: uppercase; letter-spacing: .05em; color: var(--fg-muted);
    background: var(--bg-sunken); padding: 10px var(--s-4);
    border-bottom: 1px solid var(--surface-line); white-space: nowrap;
    position: sticky; top: 0; z-index: 1;
  }
  :where(.table) td { padding: 11px var(--s-4); border-bottom: 1px solid var(--surface-line); vertical-align: middle; }
  :where(.table) tbody tr { transition: background var(--transition); }
  :where(.table) tbody tr:hover { background: var(--bg-hover); }
  :where(.table) tbody tr:last-child td { border-bottom: 0; }
}
.table .num, .table .t-right { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.table .t-name { white-space: nowrap; font-weight: 600; }
.table .t-people { width: 14rem; min-width: 12rem; max-width: 14rem; }   /* ячейка со списком людей: чипы «Фамилия И.» (tags, collapse), а не строка в 8 линий */
.table .table-cell-link { color: var(--accent-fg); cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.table .table-cell-link:focus-visible { outline: 2px solid var(--accent); outline-offset: -3px; }
.table--compact td { padding: 7px var(--s-4); }

/* Закреплённые левые колонки при горизонтальном скролле (отчёт «Пиццерии»
   кассы dodo и подобные широкие отчёты — Дата/Точка не должны уезжать за
   экран). `left` каждой ячейки — инлайн-стиль от JS (app/static/js/
   sticky-table-columns.js, ширина колонок не фиксирована вёрсткой). Ячейка
   шапки залипает и сверху (обычный `.table th`), и слева одновременно —
   так и задумано, это и есть «заморозка угла», как в Excel. */
.table--sticky-cols .is-sticky { position: sticky; z-index: 2; background: var(--bg-elevated); }
.table--sticky-cols thead .is-sticky { z-index: 3; background: var(--bg-sunken); }
.table--sticky-cols tbody tr:hover .is-sticky { background: var(--bg-hover); }
.table--sticky-cols .is-sticky-edge { box-shadow: 2px 0 4px -2px rgb(0 0 0 / .15); }

/* Группы колонок под составной (двухуровневой) шапкой — разделить визуально,
   какой блок цифр к какой группе относится (отчёт «Пиццерии» кассы dodo:
   Остаток на начало / Терминал / Наличные / … — план/факт/расхожд. по
   каждой статье подряд, без разделения легко перепутать колонку). Найдено
   пользователем 08.09.2026 — макет 4 вариантов, выбран №6: чередующаяся
   мягкая заливка группы В ТЕЛЕ (не в шапке — там взгляд уже не смотрит,
   когда сверяешь цифры в строке) + тонкая линия на стыке групп, видна
   на всей высоте таблицы, включая шапку. Класс на th/td group-й группы —
   `.is-group-alt`, на ПЕРВУЮ ячейку каждой группы (независимо от чётности)
   — `.is-group-start`. Переиспользуемый паттерн, не только для «Пиццерий»
   — для любой таблицы с составной шапкой. */
.table--grouped .is-group-alt { background: var(--bg-sunken); }
/* color-mix, не хардкод hex — подстраивается под тему инстанса (dodo и
   будущие клиенты меняют --bg-sunken/--gray-300, хардкод остался бы
   зелёным/серым мимо палитры, как уже было с сайдбаром — issue 015). */
.table--grouped tbody tr:hover .is-group-alt { background: color-mix(in srgb, var(--bg-sunken), var(--gray-300) 35%); }
/* --surface-line наполовину прозрачная — сам токен всё ещё заметен на
   заливке (is-group-alt), там он визуально ЧЕТЧЕ, чем на белом фоне между
   строками. Полупрозрачность выравнивает контраст в обоих местах.
   Итерация 08.09.2026 — сплошной --surface-line всё ещё «бил по глазам». */
.table--grouped .is-group-start { border-left: 1px solid color-mix(in srgb, var(--surface-line), transparent 55%); }

/* Строка итогов (<tfoot>) — жирным, на подложке шапки, отделена сверху
   заметной линией. Найдено пользователем 08.09.2026: «во всех таблицах
   додо надо добавить нижнюю строчку жирным, которая итог будет считать».
   Наполнение — за JS каждой страницы (app/static/js/table-totals.js),
   суммируются разные поля в разных таблицах, здесь только внешний вид. */
.table tfoot td { font-weight: 700; background: var(--bg-sunken); border-top: 2px solid var(--line-strong); }
.table tfoot tr:last-child td { border-bottom: 0; }
/* Итоги, которые нужны на виду при любой прокрутке (отчёты BI): подвал прилипает к низу обёртки, как шапка к верху (п. 14 гайда фильтров и таблиц). */
.table--sticky-foot tfoot td { position: sticky; bottom: 0; z-index: 1; }

/* Раскрываемые группы внутри таблицы. Разметку и поведение даёт
   `grouped-table.js`; эти классы намеренно не привязаны к отчёту кассы,
   чтобы одинаково использовать компонент в других списках. */
.table .grouped-table__header td { background: var(--bg-sunken); font-weight: 600; }
.table .grouped-table__header:hover { background: var(--bg-sunken); }
.table .grouped-table__header { cursor: pointer; }
.table .grouped-table__header:hover td { background: var(--bg-hover); }
.table .grouped-table__header:focus-visible { outline: 2px solid var(--accent); outline-offset: -3px; }
.grouped-table__chevron { inline-size: 1em; text-align: center; }
/* Дочерние строки начинаются правее заголовка группы. Это не просто
   декоративный отступ: в длинной сводной таблице он быстро показывает
   уровень «точка → статья», как в иерархических отчётах 1С. */
.table .grouped-table__row td:first-child {
  position: relative;
  padding-left: calc(var(--s-4) + var(--s-6));
}
.table .grouped-table__row td:first-child::before {
  content: ''; position: absolute; left: calc(var(--s-4) + 8px);
  top: 50%; width: var(--s-3); border-top: 1px solid var(--surface-line);
}
/* Второй уровень свода АО: пиццерия → период → статья. */
.table .expense-summary__period td { background: color-mix(in srgb, var(--bg-sunken), transparent 35%); font-weight: 600; }
.table .expense-summary__period { cursor: pointer; }
.table .expense-summary__period td:first-child { padding-left: calc(var(--s-4) + var(--s-6)); }
.table .expense-summary__category td:first-child { padding-left: calc(var(--s-4) + var(--s-6) + var(--s-6)); }
.table .expense-summary__category td:first-child::before { left: calc(var(--s-4) + var(--s-6) + 8px); }

/* Выбор нескольких точек в отчётах кассы. Системный синий checkbox выбивался
   из нейтрально-оранжевой палитры сервиса; свой компактный контрол остаётся
   таким же нативным input для клавиатуры и скринридера.
   Вне списков — класс `.checkbox` на самом input (флаг в таблице настройки, форма): `<input type="checkbox" class="checkbox">`. */
.search-picker__panel input[type="checkbox"],
.checklist input[type="checkbox"],
input.checkbox {
  appearance: none; width: 18px; height: 18px; margin: 0;
  border: 1.5px solid var(--line-strong); border-radius: 5px;
  background: var(--bg-elevated); cursor: pointer; flex: 0 0 auto;
  place-content: center;
}
.search-picker__panel input[type="checkbox"]::after,
.checklist input[type="checkbox"]::after,
input.checkbox::after {
  content: ''; width: 8px; height: 4px; border: solid #fff;
  border-width: 0 0 2px 2px; transform: rotate(-45deg) translate(1px, -1px);
  opacity: 0;
}
.search-picker__panel input[type="checkbox"]:checked,
.checklist input[type="checkbox"]:checked,
input.checkbox:checked { background: var(--brand-600); border-color: var(--brand-600); }
.search-picker__panel input[type="checkbox"]:checked::after,
.checklist input[type="checkbox"]:checked::after,
input.checkbox:checked::after { opacity: 1; }
.search-picker__panel input[type="checkbox"]:focus-visible,
.checklist input[type="checkbox"]:focus-visible,
input.checkbox:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
input.checkbox:disabled { opacity: .5; cursor: not-allowed; }

/* Выбор нескольких значений из списка — макрос _macros/checklist.html, поведение checklist.js.
   display — в секции 14. Подробнее: docs/Гайды/ГАЙД-ВЫБОР-ИЗ-СПИСКА.md */
.checklist { border: 1px solid var(--line-strong); border-radius: var(--r-md); background: var(--bg-elevated); overflow: hidden; }
.checklist__bar { align-items: center; gap: var(--s-2); flex-wrap: wrap; padding: var(--s-3); border-bottom: 1px solid var(--line-strong); background: var(--bg-sunken); }
.checklist__search { flex: 1 1 180px; min-width: 0; }
.checklist__count { color: var(--fg-muted); font-size: var(--fs-sm); margin-left: auto; }
.checklist__count--set { color: var(--accent-fg); font-weight: 600; }
.checklist__list { max-height: 340px; overflow-y: auto; padding: var(--s-2) 0; }
.checklist__item { align-items: center; gap: var(--s-3); padding: var(--s-2) var(--s-4); min-width: 0; cursor: pointer; overflow-wrap: anywhere; }
.checklist__item:hover { background: var(--bg-sunken); }
.checklist__none { margin: 0; padding: var(--s-3) var(--s-4); color: var(--fg-muted); }

/* Список дел объекта («Что сделать» в карточке кампании): нейтральная карточка. Когда горит — красная рамка
   ЦЕЛИКОМ и красный счётчик. Цветная полоска сбоку запрещена гайдом «Карточка объекта», п. 12. display — в секции 14. */
.todo { background: var(--bg-elevated); border: 1px solid var(--surface-line); border-radius: var(--r-md); padding: var(--s-4) var(--s-5); }
.todo--hot { border-color: var(--err); }
.todo__head { align-items: center; gap: var(--s-2); font-size: var(--fs-sm); font-weight: 700; margin-bottom: var(--s-1); }
.todo__item { align-items: center; justify-content: space-between; gap: var(--s-3); flex-wrap: wrap; padding: var(--s-3) 0; border-top: 1px solid var(--surface-line); }
.todo__head + .todo__item { border-top: 0; }
.todo__text { flex: 1 1 280px; min-width: 0; }
.todo__act { align-items: center; gap: var(--s-2); flex-wrap: wrap; }
.todo__act small { color: var(--fg-muted); font-size: var(--fs-xs); }
/* Выпадающая панель из <details class="dropdown"> (цели кампании и т. п.): панель поверх страницы, а не в потоке */
details.dropdown > summary { list-style: none; cursor: pointer; }
details.dropdown > summary::-webkit-details-marker { display: none; }
details.dropdown > .dropdown__panel { left: 0; right: auto; width: 360px; max-width: 80vw; padding: var(--s-3) var(--s-4); }

/* ---------- 10b. Графики ------------------------------------------------- */
.chart { position: relative; }
.chart svg { display: block; width: 100%; overflow: visible; }
.chart .grid line { stroke: var(--chart-grid); stroke-width: 1; }
.chart .axis text { fill: var(--fg-muted); font-size: 11px; font-family: var(--font-ui); }
.chart .axis line, .chart .axis path { stroke: var(--chart-axis); }
/* Текст носит текстовые токены, а не цвет серии */
.chart .value-label { fill: var(--fg-secondary); font-size: 11px; font-variant-numeric: tabular-nums; }
.chart .series-line { fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* 2px «шов» цветом поверхности между соседними заливками */
.chart .bar { stroke: var(--bg-elevated); stroke-width: 2; }
.chart .dot { stroke: var(--bg-elevated); stroke-width: 2; }

.hbar { grid-template-columns: 170px 1fr 64px; align-items: center; gap: var(--s-3); padding: 5px 0; }
.hbar__label { font-size: var(--fs-sm); color: var(--fg-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hbar__track { height: 9px; background: var(--bg-sunken); border-radius: var(--r-pill); overflow: hidden; }
.hbar__fill  { height: 100%; border-radius: var(--r-pill); transition: width 400ms cubic-bezier(.4,0,.2,1); }
.hbar__value { font-size: var(--fs-sm); color: var(--fg); text-align: right; font-weight: 600; }
.hbar:hover .hbar__label { color: var(--fg); }

.legend { flex-wrap: wrap; gap: var(--s-4); font-size: var(--fs-xs); color: var(--fg-secondary); }
.legend__item { align-items: center; gap: 6px; }
.legend__swatch { width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0; }

.chart-tip {
  position: absolute; pointer-events: none; opacity: 0; transform: translate(-50%, -120%);
  background: var(--bg-elevated); border: 1px solid var(--surface-line);
  border-radius: var(--r-sm); box-shadow: var(--sh-lg);
  padding: 8px 10px; font-size: var(--fs-xs); white-space: nowrap; z-index: 30;
  transition: opacity var(--transition);
}
.chart-tip[data-open="true"] { opacity: 1; }
.chart-tip b { font-family: var(--font-display); font-size: var(--fs-sm); display: block; margin-bottom: 4px; }
.chart-tip .row { gap: 6px; justify-content: space-between; }
.chart-tip .num { font-weight: 600; font-variant-numeric: tabular-nums; }

/* ---------- 11. Пустое состояние / загрузка ------------------------------ */
.empty { text-align: center; padding: var(--s-12) var(--s-6); color: var(--fg-muted); }
.empty svg { width: 42px; height: 42px; color: var(--fg-faint); margin-bottom: var(--s-3); }
.empty h4 { color: var(--fg); margin-bottom: var(--s-2); }

.skeleton { background: linear-gradient(90deg, var(--bg-sunken) 25%, var(--surface-line) 50%, var(--bg-sunken) 75%);
  background-size: 200% 100%; animation: sk 1.4s infinite; border-radius: 6px; }

.spinner { width: 18px; height: 18px; border: 2px solid var(--surface-line); border-top-color: var(--accent);
  border-radius: 50%; animation: spin .7s linear infinite; }

/* "Устаревший отчёт" (window.createReportRunner.markStale, base.html) —
   фильтры поменялись, а на экране всё ещё старые результаты. Тяжёлые отчёты
   (1С/OData) сознательно не обновляются автоматически при смене фильтра,
   поэтому вместо тихого расхождения — затемнение + плавающая плашка. */
.report-zone { position: relative; }
.report-body { transition: filter 200ms ease, opacity 200ms ease; }
.report-stale .report-body {
  filter: blur(3px) saturate(.7);
  opacity: .55;
  pointer-events: none;
  user-select: none;
}
.stale-pill {
  position: absolute;
  top: 8px; left: 50%;
  transform: translate(-50%, -12px) scale(.96);
  z-index: 5;
  display: flex; align-items: center; gap: 12px;
  padding: 10px 10px 10px 16px;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--bg-elevated) 78%, transparent);
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  border: 1px solid color-mix(in srgb, var(--warn) 35%, var(--surface-line));
  box-shadow: 0 8px 24px -6px rgba(180,83,9,.16), 0 2px 8px rgba(0,0,0,.06);
  font-size: var(--fs-sm); font-weight: 600; color: var(--fg);
  white-space: nowrap;
  opacity: 0; visibility: hidden;
  transition: opacity 220ms ease, transform 220ms cubic-bezier(.2,.8,.2,1), visibility 220ms;
}
.report-stale .stale-pill {
  opacity: 1; visibility: visible;
  transform: translate(-50%, 0) scale(1);
}
.stale-pill__icon {
  display: grid; place-items: center;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--warn); color: #fff; flex-shrink: 0;
}
.stale-pill__icon svg { width: 13px; height: 13px; }
.stale-pill .btn { border-radius: var(--r-pill); }

/* ---------- 12. Прочее --------------------------------------------------- */
.alert { gap: var(--s-3); padding: var(--s-4); border-radius: var(--r-md); font-size: var(--fs-sm); border: 1px solid transparent; }
.alert svg { width: 18px; height: 18px; flex-shrink: 0; margin-top: 1px; }
.alert--info { background: var(--info-bg); color: var(--info-fg); border-color: rgba(37,99,235,.2); }
.alert--ok   { background: var(--ok-bg);   color: var(--ok-fg); }
.alert--warn { background: var(--warn-bg); color: var(--warn-fg); }
.alert--err  { background: var(--err-bg);  color: var(--err-fg); }

/* overflow-x:auto — страницы с 6+ вкладками (напр. update_analysis.html)
   иначе переносили текст внутри вкладки на 2 строки вместо горизонтального
   скролла (flex-shrink сжимал кнопку раньше, чем текст мог себе позволить
   не завернуться). flex-shrink:0 на .tab держит вкладки в один ряд. */
.tabs { gap: var(--s-1); border-bottom: 1px solid var(--surface-line); overflow-x: auto; overflow-y: hidden; }
.tab {
  padding: 10px var(--s-4); font-size: var(--fs-sm); font-weight: 600; color: var(--fg-muted);
  cursor: pointer; background: none; white-space: nowrap; flex-shrink: 0;
  border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px;
  font-family: var(--font-ui); transition: all var(--transition);
}
.tab:hover { color: var(--fg); }
/* Счётчик во вкладке: один вид везде. Отступ от подписи и ширина по цифре — чтобы «5» не липло к слову. */
.tab__count { display: inline-block; box-sizing: border-box; min-width: 20px; margin-left: 6px; padding: 1px 6px; border-radius: var(--r-pill);
  background: var(--bg-sunken); color: var(--fg-muted); font-size: var(--fs-xs); font-weight: 600; line-height: 1.5; text-align: center;
  font-variant-numeric: tabular-nums; vertical-align: 1px; }
.tab__count:empty { display: none; }
.tab[aria-selected="true"] .tab__count, .tab[aria-current="page"] .tab__count { background: var(--accent-soft); color: var(--accent-fg); }
.tab__count--err { background: var(--err-bg); color: var(--err-fg); }
.tab__count--warn { background: var(--warn-bg); color: var(--warn-fg); }
.tab[aria-selected="true"], .tab[aria-current="page"] { color: var(--accent-fg); border-bottom-color: var(--accent); }

/* Переключатель разделов «плашкой» (issue 076): серая рейка, активный раздел — белая плашка. Активность задаёт
   aria-selected, а не набор классов; делегируется makeTabSwitcher (base.html). Для вкладок-ссылок страницы — .tabs/.tab. */
.segtabs { display: flex; gap: var(--s-1); padding: 6px; background: var(--bg-sunken); border-radius: var(--r-lg); flex-wrap: wrap; }
.segtab {
  flex: 1; display: flex; align-items: center; justify-content: center; gap: var(--s-2);
  padding: 10px var(--s-3); border: 0; border-radius: var(--r-md); background: none; cursor: pointer;
  font-family: var(--font-ui); font-size: var(--fs-sm); font-weight: 500; color: var(--fg-muted);
  transition: background var(--transition), color var(--transition);
}
.segtab:hover { color: var(--fg); }
.segtab[aria-selected="true"] { background: var(--bg-elevated); color: var(--fg); box-shadow: var(--sh-sm); }

.avatar { width: 30px; height: 30px; border-radius: 50%; background: var(--accent-soft); color: var(--accent-fg); place-items: center; font-size: var(--fs-xs); font-weight: 700; flex-shrink: 0; overflow: hidden; }
.avatar img { width: 100%; height: 100%; object-fit: cover; }

.doc-sec + .doc-sec { margin-top: var(--s-4); padding-top: var(--s-4); border-top: 1px solid var(--surface-line); }
.doc-sec__title { margin: 0 0 4px; font-size: var(--fs-md); font-weight: 600; color: var(--fg); }
.doc-sec__hint { margin: 0 0 6px; font-size: var(--fs-xs); color: var(--fg-muted); }
.error-page { max-width: 560px; margin: var(--s-8, 48px) auto; }
.error-page__code { font-size: 44px; font-weight: 700; line-height: 1; color: var(--fg-faint); }
.error-page__title { margin: var(--s-3) 0 var(--s-2); font-size: var(--fs-xl, 20px); }
.error-page__actions { gap: var(--s-2); flex-wrap: wrap; margin-top: var(--s-4); }
.error-page__id { margin-top: var(--s-4); font-size: var(--fs-xs); }
/* Прогресс долгой операции: подпись шага, остаток, «Отмена» (макрос progress.html, progress.js) */
.progress-op__head { justify-content: space-between; gap: var(--s-3); margin-bottom: 6px; font-size: var(--fs-sm); color: var(--fg-secondary); }
.progress-op__foot { margin-top: var(--s-2); }
.progress__bar { transition: width .3s; }
.progress { height: 6px; background: var(--bg-sunken); border-radius: var(--r-pill); overflow: hidden; }
.progress__bar { height: 100%; background: var(--accent); border-radius: var(--r-pill); }

/* ---------- 7д. Поля (issue 067, очередь 3; макросы _macros/fields.html, fields.js) ---------- */
.input.is-invalid, .select.is-invalid { border-color: var(--err-fg); }
.is-flash { animation: field-flash .4s; }
@keyframes field-flash { 50% { box-shadow: 0 0 0 3px var(--accent-soft); border-color: var(--accent); } }
.money { align-items: center; gap: 6px; }
.money .input { text-align: right; font-variant-numeric: tabular-nums; }
.money__cur { color: var(--fg-muted); }
.field-pw { position: relative; }
.field-pw .input { width: 100%; padding-right: 76px; }
.field-pw__toggle { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); border: 0; background: transparent; color: var(--fg-muted); font: inherit; font-size: var(--fs-xs); cursor: pointer; }
.pw-bar { height: 4px; margin-top: 6px; border-radius: 2px; background: var(--bg-sunken); overflow: hidden; }
.pw-bar::after { content: ""; display: block; height: 100%; width: 0; transition: width .2s; }
.pw-bar--short::after { width: 33%; background: var(--err); }
.pw-bar--weak::after { width: 66%; background: var(--warn-fg, #d97706); }
.pw-bar--ok::after { width: 100%; background: var(--ok-fg); }
.counter { margin-top: 4px; font-size: var(--fs-xs); color: var(--fg-muted); text-align: right; visibility: hidden; }
.counter.is-on, .counter.is-over { visibility: visible; }
.counter.is-over { color: var(--err-fg); }
.textarea[data-max] { max-height: calc(15 * 1.55em + 18px); overflow-y: auto; }
.tokens { flex-wrap: wrap; gap: 4px; padding: 5px; border: 1px solid var(--line-strong); border-radius: var(--r-sm); background: var(--bg-elevated); }
.tokens:focus-within { border-color: var(--fg); box-shadow: 0 0 0 2px var(--accent-soft); }
.tokens__input { flex: 1; min-width: 120px; border: 0; outline: 0; background: transparent; font: inherit; font-size: var(--fs-sm); color: var(--fg); padding: 3px; }
.tokens__input.is-invalid { color: var(--err-fg); }
.token { display: inline-flex; align-items: center; gap: 6px; padding: 3px 8px; border-radius: 6px; background: var(--bg-sunken); font-size: var(--fs-sm); }
.token.is-selected { outline: 2px solid var(--accent); }
.token__x { border: 0; background: transparent; color: var(--fg-muted); cursor: pointer; font-size: 15px; line-height: 1; padding: 0; }
.dropzone { flex-direction: column; align-items: center; justify-content: center; gap: 4px; padding: 18px; text-align: center; cursor: pointer; font-size: var(--fs-sm); color: var(--fg-secondary);
  border: 1.5px dashed var(--line-strong); border-radius: var(--r-md); transition: background var(--transition), border-color var(--transition); }
.dropzone:hover { background: var(--bg-sunken); }
.dropzone:focus-within { border-color: var(--fg); }
.dropzone.is-over { border: 3px solid var(--accent); background: var(--accent-soft); }
.dropzone.is-done { border-style: solid; border-color: var(--ok-fg); color: var(--ok-fg); }
.dropzone.is-error { border-color: var(--err-fg); color: var(--err-fg); }
.dropzone input[type="file"] { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.dropzone__hint { font-size: var(--fs-xs); color: var(--fg-muted); }
.filebtn input[type="file"] { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.filebtn:focus-within .btn { outline: 2px solid var(--accent); outline-offset: 2px; }
.filebtn.is-error .btn { border-color: var(--err-fg); }
.file-chip { align-items: center; gap: var(--s-2); max-width: 100%; padding: 4px 10px; border: 1px solid var(--surface-line); border-radius: var(--r-md); background: var(--bg-sunken); font-size: var(--fs-sm); }
.file-chip__name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.file-chip__meta { color: var(--fg-muted); white-space: nowrap; }

/* ---- Чат и холст (issue 076; гайды ГАЙД-ЛЕЙАУТ п. 11, ГАЙД-ФОРМЫ-И-ПОЛЯ п. 19 и 24): лента в колонке чтения, поле-«капсула», рабочая область с панелью документа ---- */
.chat__log { flex-direction: column; gap: var(--s-4); width: 100%; max-width: 46rem; margin: 0 auto; }
.chat__me { align-self: flex-end; max-width: 80%; padding: 10px 14px; border-radius: var(--r-lg); background: var(--bg-sunken); line-height: 1.55; white-space: pre-wrap; overflow-wrap: anywhere; }
.chat__ai { line-height: 1.65; white-space: pre-wrap; overflow-wrap: anywhere; }
.chat__who { align-items: center; gap: var(--s-2); margin-bottom: 4px; font-size: var(--fs-xs); font-weight: 600; color: var(--fg-secondary); }
.chat__logo { width: 22px; height: 22px; align-items: center; justify-content: center; border-radius: 7px; background: var(--accent); color: #fff; font-size: 12px; }
.chat__chips { flex-wrap: wrap; gap: var(--s-2); }
.chat__note { color: var(--fg-muted); font-size: var(--fs-sm); }
.chat__ask { padding: 12px 14px; border: 1px solid var(--surface-line); border-radius: var(--r-lg); background: var(--bg-elevated); }
.chat__ask-title { align-items: baseline; gap: var(--s-2); margin-bottom: var(--s-2); font-weight: 600; }
.chat__ask-n { margin-left: auto; font-size: var(--fs-xs); font-weight: 400; color: var(--fg-muted); white-space: nowrap; }
.chat__opt { align-items: flex-start; gap: 10px; width: 100%; margin-top: 6px; padding: 8px 10px; border: 1px solid var(--surface-line); border-radius: var(--r-md); background: transparent; font: inherit; color: inherit; text-align: left; cursor: pointer; }
.chat__opt::before { content: ""; flex: none; width: 16px; height: 16px; margin-top: 2px; border: 1.5px solid var(--line-strong); border-radius: 50%; }
.chat__opt:hover { border-color: var(--line-strong); background: var(--bg-sunken); }
.chat__opt[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-soft); }
.chat__opt[aria-pressed="true"]::before { border-color: var(--accent); background: radial-gradient(var(--accent) 45%, transparent 50%); }
.chat__own { width: 100%; margin-top: 6px; }
.chat__card { align-items: center; gap: var(--s-3); width: 100%; padding: 10px 12px; border: 1px solid var(--surface-line); border-radius: var(--r-lg); background: var(--bg-elevated); font: inherit; color: inherit; text-align: left; cursor: pointer; }
.chat__card:hover { border-color: var(--line-strong); }
.chat__card-text { flex: 1; min-width: 0; }
.chat__card-title { font-weight: 600; }
.chat__card-sub { font-size: var(--fs-xs); color: var(--fg-muted); }
/* Реплика записанного диалога (read-only): запись сессии, транскрипт звонка; живой чат — .chat__* выше. Макрос _macros/turn.html, turn.js; гайд ГАЙД-ЛЕЙАУТ п. 11а. display — в секции 14. Без боковой полоски (ГАЙД-КАРТОЧКА-ОБЪЕКТА п. 12): автор — подпись и заливка. */
.turn { gap: var(--s-1); min-width: 0; padding: var(--s-3) var(--s-4); border: 1px solid var(--surface-line); border-radius: var(--r-md); background: var(--bg-elevated); }
.turn--user { background: var(--bg-sunken); }
.turn__meta { flex-wrap: wrap; align-items: center; gap: var(--s-2); font-size: var(--fs-xs); color: var(--fg-muted); }
.turn__who { font-weight: 600; color: var(--fg); }
.turn__text { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.55; }
.turn__text--clamp { -webkit-line-clamp: 5; -webkit-box-orient: vertical; overflow: hidden; }
.turn__more { justify-self: start; }
.turn-quote { gap: var(--s-1); justify-items: start; margin: 0; padding: var(--s-2) var(--s-3); border-radius: var(--r-sm); background: var(--bg-sunken); color: var(--fg-secondary); font-size: var(--fs-sm); overflow-wrap: anywhere; }
.composer { width: 100%; max-width: 46rem; margin: 0 auto; padding: 10px 12px 8px; border: 1px solid var(--line-strong); border-radius: 18px; background: var(--bg-elevated); box-shadow: var(--sh-sm); }
.composer:focus-within { border-color: var(--fg-secondary); }
.composer__input { width: 100%; max-height: 12rem; min-height: 26px; padding: 0; border: 0; outline: 0; resize: none; background: transparent; color: var(--fg); font: inherit; line-height: 1.5; }
.composer__bar { align-items: center; gap: var(--s-2); margin-top: var(--s-2); }
.composer__send { width: 32px; height: 32px; flex: none; align-items: center; justify-content: center; border: 0; border-radius: 50%; background: var(--fg); color: var(--bg-elevated); font-size: 15px; cursor: pointer; }
.composer__send:disabled { background: var(--surface-line); color: var(--fg-muted); cursor: default; }
.composer__hint { max-width: 46rem; margin: 6px auto 0; font-size: var(--fs-xs); color: var(--fg-muted); text-align: center; }
/* Лента переписки людей (чаты DODO, issue 025): макрос _macros/chat_thread.html, chat-thread.js; реплики — .turn, поле — .composer, аватар — .avatar. display — в секции 14. Гайд ГАЙД-ЛЕЙАУТ п. 11б. */
.chat-thread { min-width: 0; }
.chat-thread__log { flex-direction: column; gap: var(--s-2); min-height: 240px; max-height: 55vh; overflow-y: auto; padding: var(--s-1) 0; }
.chat-thread__day { justify-content: center; margin: var(--s-2) 0 var(--s-1); }
.chat-thread__day span { padding: 1px var(--s-3); font-size: var(--fs-xs); color: var(--fg-muted); background: var(--bg-sunken); border-radius: 999px; }
.chat-thread__new { align-items: center; gap: var(--s-2); font-size: var(--fs-xs); color: var(--accent-fg); }
.chat-thread__new::before, .chat-thread__new::after { content: ""; flex: 1; border-top: 1px solid currentColor; opacity: .4; }
.chat-thread__turn { position: relative; padding: var(--s-2) var(--s-3) var(--s-2) 46px; }
.chat-thread__turn--user { background: var(--accent-soft); border-color: transparent; }
.chat-thread__turn--gone { padding-left: var(--s-3); color: var(--fg-muted); font-style: italic; }
.chat-thread__turn--focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.chat-thread__av { position: absolute; left: var(--s-3); top: var(--s-2); width: 28px; height: 28px; }
.chat-thread__at { color: var(--accent-fg); background: var(--accent-soft); border-radius: 4px; padding: 0 3px; }
.chat-thread__quote { display: block; max-width: 100%; margin: 2px 0; padding: 2px var(--s-2); border: 1px solid var(--surface-line); background: var(--bg-sunken); border-radius: var(--r-sm); font: inherit; font-size: var(--fs-xs); color: var(--fg-muted); text-align: left; cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-thread__link { align-items: center; gap: var(--s-2); max-width: 100%; margin: 1px 0; padding: 1px var(--s-2); border: 1px solid var(--surface-line); border-radius: 999px; background: var(--bg-elevated); color: var(--fg); font-size: var(--fs-sm); text-decoration: none; vertical-align: middle; }
.chat-thread__link:hover { border-color: var(--accent); text-decoration: none; }
.chat-thread__link--drive { border-color: var(--accent); }
.chat-thread__link-kind { font-weight: 600; white-space: nowrap; }
.chat-thread__link-host { color: var(--fg-muted); font-size: var(--fs-xs); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-thread__files { flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-1); }
.chat-thread__file { align-items: center; gap: var(--s-2); max-width: 100%; padding: var(--s-1) var(--s-2); border: 1px solid var(--surface-line); border-radius: var(--r-sm); background: var(--bg-elevated); color: var(--fg); font-size: var(--fs-sm); text-decoration: none; }
.chat-thread__file:hover { border-color: var(--accent); text-decoration: none; }
.chat-thread__file-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-thread__file-size { color: var(--fg-muted); font-size: var(--fs-xs); white-space: nowrap; }
.chat-thread__thumb { width: 56px; height: 56px; object-fit: cover; border-radius: 4px; }
.chat-thread__actions { position: absolute; top: 2px; right: var(--s-2); gap: 2px; padding: 1px; border: 1px solid var(--surface-line); border-radius: var(--r-sm); background: var(--bg-elevated); opacity: 0; }
.chat-thread__turn:hover .chat-thread__actions, .chat-thread__turn:focus-within .chat-thread__actions { opacity: 1; }
.chat-thread__menu { position: absolute; right: 0; top: 100%; z-index: 6; padding: var(--s-1); }
.chat-thread__edit { gap: var(--s-2); }
.chat-thread__edit-bar { gap: var(--s-2); }
.chat-thread__compose { position: relative; margin-top: var(--s-3); }
.chat-thread__compose .composer { max-width: none; }
.chat-thread__compose .composer__input { min-height: 0; resize: none; overflow: hidden; }
.chat-thread__compose .composer__hint { max-width: none; text-align: left; }
.chat-thread__reply { align-items: center; gap: var(--s-2); margin-bottom: var(--s-1); padding: var(--s-1) var(--s-2); border: 1px solid var(--surface-line); background: var(--bg-sunken); border-radius: var(--r-sm); font-size: var(--fs-xs); color: var(--fg-muted); }
.chat-thread__reply-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-thread__tray { flex-wrap: wrap; gap: var(--s-1); margin-bottom: var(--s-1); }
.chat-thread__chip { align-items: center; gap: var(--s-1); padding: 1px var(--s-2); border: 1px solid var(--surface-line); border-radius: 999px; background: var(--bg-elevated); font-size: var(--fs-xs); }
.chat-thread__chip--busy { color: var(--fg-muted); }
.chat-thread__chip-x { border: 0; background: transparent; color: var(--fg-muted); cursor: pointer; font: inherit; }
.chat-thread__mention { position: absolute; left: 0; bottom: 100%; z-index: 5; min-width: 260px; max-height: 220px; overflow-y: auto; margin-bottom: 4px; padding: var(--s-1); }
.chat-thread__person { justify-content: space-between; gap: var(--s-3); width: 100%; }
.chat-thread__person[aria-selected="true"] { background: var(--accent-soft); }
.chat-thread__person small { color: var(--fg-muted); }
.chat-thread mark, .list-card__sub mark { background: var(--warn-bg, #fff3bf); color: inherit; border-radius: 3px; padding: 0 1px; }
.chat-thread mark.chat-thread__mark--current { outline: 2px solid var(--accent); }
@media (hover: none) { .chat-thread__actions { opacity: 1; position: static; margin-top: var(--s-1); border: 0; } }
.canvas { position: relative; height: calc(100dvh - var(--shell-topbar, 60px) - 150px); min-height: 440px; border: 1px solid var(--surface-line); border-radius: var(--r-lg); background: var(--bg-elevated); overflow: hidden; }
.canvas__main { flex: 1; flex-direction: column; min-width: 0; min-height: 0; }
.canvas__scroll { flex: 1; overflow: auto; padding: var(--s-6) var(--s-5) var(--s-2); }
.canvas__dock { padding: var(--s-2) var(--s-5) var(--s-4); }
.canvas__panel { flex: none; flex-direction: column; width: min(46%, 42rem); min-height: 0; border-left: 1px solid var(--surface-line); background: var(--bg-elevated); }
.canvas__head { align-items: center; gap: var(--s-3); padding: 10px var(--s-4); border-bottom: 1px solid var(--surface-line); }
.canvas__title { flex: 1; min-width: 0; font-family: var(--font-display); font-weight: 600; font-size: var(--fs-md); }
.canvas__body { flex: 1; min-height: 0; overflow: auto; padding: var(--s-4) var(--s-5); }
.canvas__foot { align-items: center; gap: var(--s-3); padding: 10px var(--s-4); border-top: 1px solid var(--surface-line); }
.brief-sec { margin-bottom: var(--s-4); }
.brief-sec__value { padding: 2px 6px; margin: 0 -6px; border-radius: var(--r-sm); line-height: 1.55; white-space: pre-wrap; }
.brief-sec--new .brief-sec__value { background: var(--accent-soft); }
@media (max-width: 900px) {
  .canvas { height: calc(100dvh - var(--shell-topbar, 60px) - 120px); }
  .canvas__panel { position: absolute; inset: 0; width: auto; z-index: 5; }
  .canvas__scroll, .canvas__dock { padding-inline: var(--s-3); }
}
.switch { align-items: center; gap: 10px; cursor: pointer; font-size: var(--fs-sm); }
.switch input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.switch__track { position: relative; flex: none; width: 36px; height: 20px; border-radius: var(--r-pill); background: var(--line-strong); transition: background var(--transition); }
.switch__track::after { content: ""; position: absolute; left: 2px; top: 2px; width: 16px; height: 16px; border-radius: 50%; background: #fff; transition: left var(--transition); }
.switch input:checked + .switch__track { background: var(--ok-fg); }
.switch input:checked + .switch__track::after { left: 18px; }
.switch input:focus-visible + .switch__track { outline: 2px solid var(--accent); outline-offset: 2px; }
.segment { background: var(--bg-sunken); border-radius: var(--r-sm); padding: 3px; gap: 2px; }
.segment label { position: relative; padding: 5px 12px; border-radius: 6px; font-size: var(--fs-sm); color: var(--fg-secondary); cursor: pointer; }
.segment input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.segment label:has(input:checked) { background: var(--bg-elevated); color: var(--fg); font-weight: 600; box-shadow: var(--sh-sm); }
.segment label:has(input:focus-visible) { outline: 2px solid var(--accent); }
.btn--s { min-height: 32px; }
.btn--m { min-height: 40px; }
.btn--l { min-height: 48px; font-size: var(--fs-md); }
.btn.is-busy { position: relative; color: transparent; background: var(--bg-sunken); border-color: var(--line-strong); pointer-events: none; }
.btn.is-busy::after { content: ""; position: absolute; left: 50%; top: 50%; width: 16px; height: 16px; margin: -8px 0 0 -8px; border: 2px solid var(--line-strong); border-top-color: var(--fg-secondary); border-radius: 50%; animation: spin .7s linear infinite; }

.divider { height: 1px; background: var(--surface-line); border: 0; margin: var(--s-5) 0; }

/* утилиты компоновки */
/* Действия в строке таблицы (2+ кнопки/ссылки в последней ячейке) — всегда
   оборачивать в `.row`, не склеивать текстом с `ml-N` между элементами.
   Сырая конкатенация переносится по-разному в зависимости от того, сколько
   действий в конкретной строке — высота и выравнивание строк расходятся
   (найдено 2026-09-29, `transcription.html`, колонка действий «История
   заданий»: «Открыть Скачать» на одной строке, «×» падал на вторую только у
   строк с несколькими действиями). `.row` — `display:flex` без `flex-wrap`
   (значит `nowrap` по умолчанию) + единый `gap`, поэтому действия всегда в
   одну строку с одинаковым отступом независимо от их числа в конкретной
   строке. Не заводить отдельный `.row-actions` — `.row` уже делает то же
   самое. */
.row { align-items: center; gap: var(--s-3); }
.row--between { justify-content: space-between; }
.col { flex-direction: column; gap: var(--s-3); }
.stack-4 > * + * { margin-top: var(--s-4); }
.stack-6 > * + * { margin-top: var(--s-6); }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: var(--s-4); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); gap: var(--s-4); }

/* ---------- 12b. Документы (план/факт/расхождение) ----------------------- */
/* Паттерн для денежных документов со строками план/факт/расхождение +
   опциональный комментарий (issue 018, «Смена» dodo — первый потребитель,
   design review 2026-08-23, research/design-system/README.md §3.1). Второй
   потребитель — «Приказы» (issue 018, фаза 3), поэтому вынесено сюда, а не
   оставлено инлайн-стилями внутри шаблона одного сервиса.

   Карточка-обёртка — обычный `.card`, статус — обычный `.badge`, компоновка
   заголовка документа — `.row`/`.row--between`: новых классов под то, что
   уже есть в дизайн-системе, не заводим. */
.doc-section { margin-bottom: var(--s-6); }
.doc-section__label {
  font-family: var(--font-display); font-size: var(--fs-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: .07em; color: var(--fg-faint);
  padding: 0 2px var(--s-3);
}
.card--muted { background: var(--bg-sunken); }

/* Заголовок колонок — один раз на карточку, а не на каждой строке (план/
   факт по 4 раза подряд читается как повторяющийся шум, не как таблица).
   Та же сетка колонок у .doc-cols и .doc-row__top — иначе числа "плывут"
   относительно заголовка и друг друга, как только состав ячеек в очередной
   строке меняется (пусто/инпут/бейдж). */
.doc-cols {
  grid-template-columns: 1fr 84px 116px 72px; align-items: center; gap: var(--s-4);
  padding: var(--s-3) var(--s-5); background: var(--bg-sunken); border-bottom: 1px solid var(--surface-line);
}
.doc-cols span { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .06em; color: var(--fg-faint); font-weight: 700; text-align: right; }
.doc-cols span:first-child { text-align: left; }
.card--muted .doc-cols { background: var(--bg-hover); }

.doc-row { padding: var(--s-3) var(--s-5); border-bottom: 1px solid var(--surface-line); }
.doc-row:last-child { border-bottom: 0; }
.doc-row__top { grid-template-columns: 1fr 84px 116px 72px; align-items: center; gap: var(--s-4); }
.doc-row__label { min-width: 0; font-weight: 600; font-size: var(--fs-sm); }
.doc-row__label small { display: block; font-weight: 400; color: var(--fg-muted); font-size: var(--fs-xs); margin-top: 2px; }
/* Комментарий — справа от названия статьи, в той же первой колонке (там,
   где план/факт/расхождение всё равно пустые для этой строки), а не
   отдельной строкой ниже — так карточка не растёт по высоте без нужды. */
.doc-row__label--with-comment { align-items: center; gap: var(--s-4); }
.doc-row__label-text { flex-shrink: 0; font-weight: 600; }

.doc-val { text-align: right; }
.doc-val__num { font-weight: 600; font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
.doc-val--muted .doc-val__num { color: var(--fg-faint); font-weight: 400; }
.doc-val--empty .doc-val__num { color: var(--fg-faint); }

/* "Тихая" ячейка-инпут — рамка появляется только на hover/focus, а не
   всегда: иначе на строке из 4 колонок инпут в рамке рядом с обычным
   текстом и цветным бейджем расхождения читается как три конкурирующих
   друг с другом элемента вместо одной строки таблицы. */
.doc-fact-input, .doc-comment {
  border: 1px solid transparent; border-radius: var(--r-sm); background: var(--bg-sunken);
  font-family: var(--font-ui); color: var(--fg); padding: 6px 9px;
}
.doc-fact-input { width: 100%; text-align: right; font-size: var(--fs-sm); font-weight: 600; }
.doc-comment { flex: 1; min-width: 0; font-size: var(--fs-xs); color: var(--fg-secondary); }
.doc-fact-input::placeholder, .doc-comment::placeholder { color: var(--fg-faint); font-weight: 400; }
.doc-fact-input:hover, .doc-comment:hover { border-color: var(--line-strong); }
.doc-fact-input:focus, .doc-comment:focus {
  outline: none; background: var(--bg-elevated); border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft);
}

.doc-diff { text-align: right; font-weight: 700; font-size: var(--fs-sm); justify-self: end; font-variant-numeric: tabular-nums; }
.doc-diff--neg { color: var(--err-fg); }
.doc-diff--pos { color: var(--ok-fg); }
.doc-diff--na { color: var(--fg-faint); font-weight: 400; }

.doc-future-note { align-items: center; gap: var(--s-2); font-size: var(--fs-xs); color: var(--fg-faint); padding: var(--s-3) var(--s-5) var(--s-4); }

/* Итоговое поле, блокирующее закрытие документа без заполнения (ТЗ dodo,
   «сумма доступная для инкассации») — акцентная рамка, а не просто ещё
   один .card, чтобы взгляд сразу находил обязательное поле в самом низу
   длинного документа. */
.doc-required { border: 1px solid var(--accent); border-radius: var(--r-md); background: var(--accent-soft); padding: var(--s-4) var(--s-5); }
.doc-required__label { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .05em; color: var(--accent-fg); font-weight: 700; margin-bottom: var(--s-2); }
.doc-required input {
  width: 100%; padding: 9px 12px; border: 1.5px solid var(--accent); border-radius: var(--r-sm);
  font-size: var(--fs-md); font-weight: 700; text-align: right; background: var(--bg-elevated); font-family: var(--font-ui);
}

/* ---------- 13. Главная — hero и карточки сервисов ----------------------- */
.hero {
  background: linear-gradient(135deg, var(--brand-900) 0%, var(--grad-mid) 60%, var(--grad-late) 100%);
  border-radius: var(--r-xl); padding: 34px 38px; color: #fff;
  position: relative; overflow: hidden; margin-bottom: var(--s-6);
}
.hero::after {
  content: ''; position: absolute; right: -70px; top: -70px; width: 300px; height: 300px;
  border-radius: 50%; background: radial-gradient(circle, var(--hero-glow), transparent 68%);
}
.hero h1 { font-size: 32px; margin-bottom: 10px; position: relative; }
.hero p  { color: rgba(255,255,255,.72); max-width: 620px; position: relative; margin: 0; }
.hero__stats { gap: 34px; margin-top: 26px; position: relative; flex-wrap: wrap; }
.hero__stat b { display: block; font-family: var(--font-display); font-size: 26px; color: var(--brand-400); }
.hero__stat span { font-size: 12px; color: rgba(255,255,255,.55); }

.svc { flex-direction: column; padding: 20px; height: 100%; }
.svc__icon {
  width: 42px; height: 42px; border-radius: var(--r-md);
  background: var(--accent-soft); color: var(--accent-fg); place-items: center; margin-bottom: 14px;
}
.svc__icon svg { width: 21px; height: 21px; }
.svc__name { font-family: var(--font-display); font-weight: 600; font-size: 15px; margin-bottom: 6px; }
.svc__desc { font-size: 13px; color: var(--fg-secondary); line-height: 1.5; flex: 1; margin-bottom: 14px; }
.svc__foot { align-items: center; justify-content: space-between; gap: 8px; }
.svc--off { opacity: .55; }
.svc-link { color: inherit; }
.svc-link:hover { text-decoration: none; }
.group-title { font-family: var(--font-display); font-size: 13px; font-weight: 700;
               text-transform: uppercase; letter-spacing: .07em; color: var(--fg-muted); margin: var(--s-8) 0 var(--s-4); }
.group-title:first-child { margin-top: 0; }
.services-group { margin-top: var(--s-8); }

@media (max-width: 900px) {
  .shell, body.sidebar-collapsed .shell { grid-template-columns: 1fr; background: var(--bg); }
  .sidebar {
    position: fixed; inset: 0 auto 0 0; width: var(--shell-sidebar);
    transform: translateX(-100%); transition: transform var(--transition);
  }
  .sidebar.is-open { transform: translateX(0); box-shadow: var(--sh-lg); }
  .content { padding: var(--s-4); }
}

/* Телефон — не просто уменьшенный десктоп. На ширине до 640px формы и
   документы переходят в одноколоночный рабочий режим: сотрудник должен
   заполнить смену одной рукой, а не прокручивать страницу по горизонтали.
   Таблицы-реестры остаются со скроллом (там важна полнота данных), а строки
   документа «план / факт / расхождение» становятся компактными карточками. */
@media (max-width: 640px) {
  .content { padding: var(--s-3); }
  .topbar { padding: 0 var(--s-3); gap: var(--s-2); }
  .topbar__title { font-size: var(--fs-md); white-space: nowrap; }
  .topbar > .tabs .tab { padding-inline: var(--s-2); }

  .card--pad { padding: var(--s-4); }
  .grid-2, .grid-3 { grid-template-columns: minmax(0, 1fr); }
  .row.row--between { flex-wrap: wrap; }

  .period-picker { width: 100%; flex-direction: column; align-items: stretch; }
  .period-picker__btn { width: 100%; min-width: 0; }
  .period-picker__granularity { width: 100%; min-width: 0; }
  .period-panel, .period-panel--right {
    position: fixed; inset: auto var(--s-3) var(--s-3); right: var(--s-3);
    width: auto; min-width: 0; max-height: calc(100vh - 24px); overflow-y: auto;
  }
  .period-years { justify-content: flex-start; overflow-x: auto; padding-bottom: var(--s-2); }
  .period-year-col { flex: 0 0 176px; }
  .period-panel__presets { grid-template-columns: repeat(2, 1fr); }

  .notif-bell__panel { width: calc(100vw - 24px); max-width: 360px; }
  .stale-pill { width: calc(100% - 24px); white-space: normal; justify-content: center; }

  .doc-row { padding: var(--s-3) var(--s-4); }
  .doc-row__top { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-3); }
  .doc-row__label { grid-column: 1 / -1; }
  .doc-row__label--with-comment { align-items: flex-start; flex-direction: column; gap: var(--s-2); }
  .doc-comment { width: 100%; }
  /* Заголовок и число в мобильной ячейке должны иметь один правый край:
     иначе «Факт» слева, а правовыравненный инпут справа выглядят как разные
     колонки. */
  .doc-val, .doc-diff, .doc-fact-cell { text-align: right; justify-self: stretch; }
  .doc-val::before, .doc-diff::before, .doc-fact-cell::before {
    margin-bottom: 2px; color: var(--fg-muted);
    font-size: 10px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  }
  .doc-row__top > :nth-child(2)::before { content: 'План'; }
  .doc-row__top > :nth-child(3)::before { content: 'Факт'; }
  .doc-row__top > :nth-child(4)::before { content: 'Расхождение'; }

  /* Общая строка фильтров: поля и действие становятся вертикальным сценарием. */
  .responsive-form-row { flex-direction: column; align-items: stretch !important; }
  .responsive-form-row > .field,
  .responsive-form-row > .row { width: 100%; min-width: 0 !important; margin-left: 0 !important; }
  .responsive-form-row > .btn { width: 100%; }
  .responsive-form-row > .row .btn--primary { width: 100%; }

  /* Результаты поиска остаются рядом с полем и не попадают под клавиатуру iOS. */
  .search-picker__panel {
    position: absolute !important; inset: calc(100% + 4px) auto auto 0 !important;
    width: 100% !important; min-width: 0 !important; max-height: min(230px, 35vh);
  }

  .responsive-section-header { align-items: flex-start; gap: var(--s-2); }
  .responsive-section-header > .row { width: 100%; justify-content: space-between; flex-wrap: wrap; }
  .responsive-collection-form { flex-direction: column; align-items: stretch; }
  .responsive-collection-form > * { width: 100%; }

  /* Ведомость на div/grid: те же карточные правила, что у таблиц, но без table. */
  .responsive-data-grid__row { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; gap: var(--s-3) !important; }
  .responsive-data-grid__primary { grid-column: 1 / -1; }
  .responsive-data-grid__cell::before {
    margin-bottom: 2px; color: var(--fg-muted);
    font-size: 10px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  }
  .responsive-data-grid__cell::before { content: attr(data-mobile-label); }
  .responsive-data-grid__input { flex-direction: column; align-items: stretch; gap: 4px !important; }

  /* Администрирование → пользователи. Это не числовой реестр: на телефоне
     важнее быстро понять, кто это и какие у него права, чем удерживать пять
     десктопных колонок в одной строке. */
  .responsive-page-head { flex-direction: column; align-items: stretch; }
  .responsive-page-actions { width: 100%; flex-direction: column; }
  .responsive-page-actions > button { width: 100%; }
  .responsive-table-wrap { overflow: visible; }
  .responsive-table--cards tbody { gap: var(--s-2); padding: var(--s-2); }
  .responsive-table--cards tbody tr {
    grid-template-columns: minmax(0, 1fr); gap: 0;
    padding: var(--s-4); border: 1px solid var(--surface-line);
    border-radius: var(--r-md); background: var(--bg-elevated);
  }
  .responsive-table--cards tbody td {
    padding: 0 !important; border: 0 !important; white-space: normal !important;
    text-align: left !important;
  }
  .responsive-table--cards tbody td + td { margin-top: var(--s-3); }
  .responsive-table__identity > :last-child { min-width: 0; }
  .responsive-table__secondary { overflow-wrap: anywhere; }
  .responsive-table--cards tbody td[data-mobile-label]::before {
    content: attr(data-mobile-label); margin-bottom: 2px;
    color: var(--fg-muted); font-size: 10px; font-weight: 700;
    letter-spacing: .05em; text-transform: uppercase;
  }
  .responsive-table__actions { justify-content: flex-start; }
  #invite-dodo-role, #invite-dodo-unit { grid-column: 1 / -1; }
  .mobile-modal .flex.justify-end { flex-wrap: wrap; }
  .mobile-modal { padding: var(--s-3); }
  .mobile-modal > .card {
    max-height: calc(100dvh - 24px); margin: 0; overflow-y: auto;
  }
}


/* ---------- 13b. Отрендеренный markdown ---------------------------------- */
/* Результат renderMarkdownSafe() (app/static/js/markdown-safe.js) без этих
   правил выглядит плоским текстом: Tailwind preflight сбрасывает маркеры
   списков, размеры заголовков и отступы абзацев, а своего стиля под вывод
   markdown в дизайн-системе до этого не было — каждый экран, который рендерил
   markdown, показывал «простыню» одинакового серого текста.
   Свойства `display` здесь намеренно не задаются — значит, секция 14 не нужна. */
.md { color: var(--fg); font-size: var(--fs-sm); line-height: 1.6; }
.md > :first-child { margin-top: 0; }
.md > :last-child  { margin-bottom: 0; }
.md p { margin: 0 0 var(--s-3); }
.md h1, .md h2, .md h3, .md h4 {
  margin: var(--s-5) 0 var(--s-2); font-weight: 600; line-height: 1.3; color: var(--fg);
}
.md h1 { font-size: var(--fs-lg); }
.md h2 { font-size: var(--fs-md); }
.md h3, .md h4 { font-size: var(--fs-sm); }
.md ul, .md ol { margin: 0 0 var(--s-3); padding-left: var(--s-6); }
.md ul { list-style: disc; }
.md ol { list-style: decimal; }
.md li { margin: 0 0 var(--s-1); }
.md li::marker { color: var(--fg-muted); }
.md strong { font-weight: 600; color: var(--fg); }
.md em { font-style: italic; }
.md a { color: var(--accent-fg); text-decoration: underline; text-underline-offset: 2px; }
.md code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .92em;
  background: var(--bg-sunken); border-radius: var(--r-sm); padding: 1px 4px;
}
.md pre { background: var(--bg-sunken); border-radius: var(--r-md); padding: var(--s-3); overflow-x: auto; margin: 0 0 var(--s-3); }
.md pre code { background: none; padding: 0; }
.md blockquote {
  margin: 0 0 var(--s-3); padding: var(--s-2) var(--s-4);
  background: var(--bg-sunken); border-radius: var(--r-md); color: var(--fg-secondary);
}
.md hr { border: 0; border-top: 1px solid var(--surface-line); margin: var(--s-5) 0; }
.md table { border-collapse: collapse; margin: 0 0 var(--s-3); }
.md th, .md td { border: 1px solid var(--surface-line); padding: var(--s-2) var(--s-3); text-align: left; }
.md th { background: var(--bg-sunken); font-weight: 600; }


/* Анимации — снаружи слоя: имена глобальны, в каскаде свойств не участвуют. */
@keyframes sk { from { background-position: 200% 0; } to { background-position: -200% 0; } }
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------- 7е. Подсказка «ⓘ» и «Когда выполнить» (issue 068; hint.js, schedule.js) ---------- */
.ds-hint { width: 16px; height: 16px; margin-left: 4px; padding: 0; border: 1px solid var(--line-strong); border-radius: 50%; background: var(--bg-sunken); color: var(--fg-muted);
  font: 700 11px/16px var(--font-ui); text-align: center; vertical-align: middle; cursor: pointer; }
.ds-hint:hover, .ds-hint[aria-expanded="true"] { background: var(--accent-soft); color: var(--accent-fg); }
.ds-hint:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ds-hint__panel { position: fixed; z-index: 60; padding: 10px 12px; border: 1px solid var(--line-strong); border-radius: var(--r-md); background: var(--bg-elevated);
  box-shadow: var(--sh-md); color: var(--fg-secondary); font-size: var(--fs-sm); font-weight: 400; line-height: 1.45; text-align: left; white-space: normal; }
.badge--action { cursor: pointer; border: 0; font: inherit; }
.ds-sched__when { flex-wrap: wrap; }
.ds-sched__item { gap: 8px; align-items: center; font-size: var(--fs-sm); color: var(--fg-secondary); }
.ds-sched__item .input { width: auto; }
.ds-sched__panel { gap: 16px; align-items: center; margin-top: var(--s-3); }
.ds-sched__now { margin: var(--s-3) 0 0; }
.ds-days { gap: 6px; flex-wrap: wrap; }
.ds-days__chip { min-width: 40px; min-height: 36px; padding: 0 10px; border: 1px solid var(--line-strong); border-radius: var(--r-sm); background: var(--bg-elevated);
  color: var(--fg-secondary); font: 500 var(--fs-sm) var(--font-ui); cursor: pointer; }
.ds-days__chip[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-fg); font-weight: 600; }
.ds-days__chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Тултип по клику (hint.js): крестик и ссылка внутри панели */
.ds-hint__panel--click { padding-right: 32px; max-width: 320px; }
.ds-hint__x { position: absolute; top: 4px; right: 4px; width: 24px; height: 24px; padding: 0; border: 0; border-radius: var(--r-sm);
  background: none; color: var(--fg-muted); font-size: 18px; line-height: 24px; cursor: pointer; }
.ds-hint__x:hover { background: var(--bg-sunken); color: var(--fg); }
.ds-hint__x:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.ds-hint__link { margin-top: 6px; font-weight: 500; }
/* Подпись-подсказка (hn.term / Hint.term): сама подпись открывает панель; точечное подчёркивание — единственный знак.
   span, не button: переносится по строкам и выделяется. Шрифт, регистр и цвет — от окружения (подпись метрики, шапка колонки, плашка). */
.ds-term { cursor: help; text-decoration: underline dotted var(--fg-faint); text-decoration-thickness: 1px; text-underline-offset: 3px; }
.ds-term:hover, .ds-term[aria-expanded="true"] { text-decoration-color: var(--accent); text-decoration-style: solid; }
.ds-term:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }

/* ---------- 7ж. Глобальный лоадер (issue 067 п. 4.1; global-loader.js) ---------- */
.gloader { position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 90; overflow: hidden; pointer-events: none;
  opacity: 0; transition: opacity .25s; }
.gloader.is-on { opacity: 1; }
.gloader i { position: absolute; top: 0; bottom: 0; left: 0; width: 30%; background: var(--accent); border-radius: 0 3px 3px 0; }
.gloader.is-on i { animation: gloader-run 1.1s ease-in-out infinite; }
.gloader.is-done i { animation: none; width: 100%; transition: width .2s ease-out; }
@keyframes gloader-run { from { transform: translateX(-100%); } to { transform: translateX(340%); } }
@media (prefers-reduced-motion: reduce) { .gloader.is-on i { animation: none; width: 100%; opacity: .6; } }
/* Блок, который обновляется: GlobalLoader.block(el, promise) */
.is-refreshing { opacity: .5; transition: opacity .15s; }

/* ---------- 14. display-объявления компонентов — в слое `components` ----- */
/* Здесь и только здесь дизайн-система задаёт `display`. Причина: Tailwind v4
   держит `.hidden` (и `flex`, `lg:block`, `sm:table-cell`) в `@layer utilities`,
   а неслоёвый CSS сильнее любого слоя — из-за этого `class="alert ... hidden"`
   показывался всегда (issue 019). Перенос ТОЛЬКО display оставляет остальным
   правилам файла прежнюю силу, поэтому вёрстка выглядит как до миграции, а
   утилиты display наконец работают.
   Добавляете новый компонент с display — объявляйте его здесь, иначе
   `hidden` на нём снова перестанет действовать (сторож: tests/test_css_layers_guard.py). */
@layer components {
  /* Подпись плитки с функциональной точкой: число нейтральное, смысл (ok/warn/err/info/ai) — в точке у подписи (issue 076). */
  .stat-dot::before { content: ""; display: inline-block; width: 8px; height: 8px; margin-right: 6px; border-radius: 50%; background: var(--fg-faint); vertical-align: 0; }
  .stat-dot--ok::before { background: var(--ok); }
  .stat-dot--warn::before { background: var(--warn); }
  .stat-dot--err::before { background: var(--err); }
  .stat-dot--info::before { background: var(--info); }
  .stat-dot--ai::before { background: var(--muted-accent); }
  /* Отчёт ещё не построен: подсказка следующего шага вместо пустого места (ставит base.html, пока .report-body скрыт). */
  .report-empty { display: none; }
  .report-zone:not(:has(> .report-body:not(.hidden))) > .report-empty { display: block; }
  .auth-shell { min-height: 100vh; display: flex; align-items: center; justify-content: center;
                background: linear-gradient(135deg, var(--brand-900) 0%, var(--grad-mid) 55%, var(--grad-late) 100%); }
  .caret { display: inline-block; width: 6px; height: 6px; margin: 0 3px 1px 1px; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: rotate(-45deg); transition: transform .15s; }
  .caret--open { transform: rotate(45deg); }
  .ds-sched__when { display: inline-flex; }
  .ds-sched__panel { display: flex; flex-wrap: wrap; }
  .ds-sched__panel[hidden], .ds-sched__now[hidden] { display: none; }
  .ds-sched__item { display: inline-flex; }
  .ds-days { display: flex; }
  .shell { display: grid; }
  .sidebar { display: flex; }
  .sidebar__brand-row { display: flex; }
  .sidebar__actions { display: flex; }
  .sidebar__profile { display: flex; }
  .todo__head { display: flex; }
  .todo__item { display: flex; }
  .todo__act { display: flex; }
  .sidebar__brand { display: flex; }
  .sidebar__logo { display: grid; }
  .sidebar__wordmark { display: block; }
  .sidebar__brand--wordmark .sidebar__logo { display: none; }
  body.sidebar-collapsed .sidebar__wordmark { display: none; }
  body.sidebar-collapsed .sidebar__brand--wordmark .sidebar__logo { display: grid; }
  .sidebar__search-field { display: flex; }
  .nav-item { display: flex; }
  /* Это правило должно идти после `.nav-item { display: flex }`: оба
     селектора одной специфичности и находятся в одном каскадном слое. */
  .nav-item--search-hidden, .sidebar__group--search-hidden, body.sidebar-collapsed .sidebar__search { display: none; }
  .sidebar__search-field input::-webkit-search-cancel-button { display: none; }
  .main { display: flex; }
  .topbar { display: flex; }
  .sites-context { display: flex; }
  .card__head { display: flex; }
  .btn { display: inline-flex; }
  /* После `.btn`: оба селектора одной специфичности в одном слое, иначе кнопка «Меню» видна и на десктопе. */
  .nav-toggle { display: none; }
  /* Цвет текста кнопки по смыслу (issue 076): `.btn--secondary`/`--ghost` задают color в этом же слое,
     а утилиты `.text-err` и т. п. лежат слоем ниже и проигрывали — «Отозвать» становилась чёрной. */
  .btn.text-err, .btn.text-err:hover:not(:disabled) { color: var(--err-fg); }
  .btn.text-ok, .btn.text-ok:hover:not(:disabled)   { color: var(--ok-fg); }
  .btn.text-warn, .btn.text-warn:hover:not(:disabled) { color: var(--warn-fg); }
  .btn.text-info, .btn.text-info:hover:not(:disabled) { color: var(--info-fg); }
  .badge { display: inline-flex; }
  .tags { display: flex; }
  .tags__list { display: flex; }
  .tags__panel { display: flex; }
  .stage { display: flex; }
  .stage__dot { display: inline-flex; }
  .stages--h { display: flex; }
  .modal__box { display: flex; }
  .modal__head { display: flex; }
  .modal__foot { display: flex; }
  .toasts { display: flex; }
  .toast { display: flex; }
  .list-toolbar { display: flex; }
  .list-toolbar__fields { display: flex; }
  .list-toolbar__chips { display: flex; }
  .filter-chip { display: inline-flex; }
  .filter-chip__x { display: inline-grid; }
  .fab { display: flex; }
  .fab[hidden] { display: none; }
  .summary__quick { display: flex; }
  .summary__quick[hidden] { display: none; }
  .bulkbar { display: flex; }
  .bulkbar__actions { display: flex; }
  .menu-panel { display: flex; flex-direction: column; }
  .menu-panel__item { display: block; }
  .menu-panel__hint { display: block; }
  .pager { display: flex; }
  .pager__pages { display: flex; }
  .pager__pages a, .pager__pages span { display: inline-grid; }
  .state { display: grid; }
  .money { display: flex; }
  .tokens { display: flex; }
  .dropzone { display: flex; }
  .filebtn { display: inline-flex; }
  .file-chip { display: inline-flex; }
  .chat__log { display: flex; }
  .chat__who { display: flex; }
  .chat__logo { display: inline-flex; }
  .chat__chips { display: flex; }
  .chat__card { display: flex; }
  .chat__ask-title { display: flex; }
  .chat__opt { display: flex; }
  .turn { display: grid; }
  .turn__meta { display: flex; }
  .turn__text--clamp { display: -webkit-box; }
  .turn-quote { display: grid; }
  .composer__input { display: block; }
  .composer__bar { display: flex; }
  .composer__send { display: inline-flex; }
  .chat-thread__log, .chat-thread__turn, .chat-thread__new, .chat-thread__day, .chat-thread__edit, .chat-thread__edit-bar { display: flex; }
  .chat-thread__turn, .chat-thread__edit { flex-direction: column; }
  .chat-thread__av { display: grid; }
  .chat-thread__link, .chat-thread__file, .chat-thread__chip, .chat-thread__files, .chat-thread__tray, .chat-thread__reply, .chat-thread__actions { display: inline-flex; }
  .chat-thread__files, .chat-thread__tray, .chat-thread__reply { display: flex; }
  .chat-thread__quote { display: block; }
  .chat-thread__menu { display: flex; flex-direction: column; }
  .chat-thread__menu[hidden], .chat-thread__reply[hidden], .chat-thread__tray[hidden] { display: none; }
  .chat-thread__mention { display: block; }
  .chat-thread__mention[hidden] { display: none; }
  .chat-thread__person { display: flex; }
  .canvas { display: flex; }
  .canvas__main { display: flex; }
  .canvas__panel { display: flex; }
  .canvas__panel[hidden] { display: none; }
  .canvas__head { display: flex; }
  .canvas__foot { display: flex; }
  .switch { display: inline-flex; }
  .segment { display: inline-flex; }
  .segment label { display: block; }
  .error-page__actions { display: flex; }
  .progress-op__head { display: flex; }
  .progress-op__foot { display: flex; }
  .save-status { display: inline-flex; }
  .save-status:empty { display: none; }
  .bulkbar[hidden], .list-toolbar__chips[hidden] { display: none; }
  .field { display: flex; }
  .form-grid { display: grid; }
  .field-row { display: grid; }
  .form-actions { display: flex; }
  .kv { display: grid; }
  .kv__item { display: grid; }
  .secret { display: flex; }
  .secret__actions { display: flex; }
  .secret-actions { display: flex; }
  .acc { display: grid; }
  .acc__bar { display: flex; }
  .acc__head { display: flex; }
  .eip { display: flex; }
  .eip__form { display: grid; }
  .eip__row { display: flex; }
  .eip__err[hidden] { display: none; }
  .list-card { display: grid; }
  .list-card__list { display: flex; }
  .list-card__item { display: grid; }
  .list-card__text { display: grid; }
  .list-card__tools { display: grid; }
  .list-card__name { display: flex; }
  .list-card__empty { display: grid; }
  .list-card__empty-box { display: grid; }
  .chat-card, .chat-card .chat-thread { display: flex; }
  .chat-card[hidden] { display: none; }
  .chat-row__top, .chat-row__meta, .chat-list-sort { display: flex; }
  .chat-row__preview { display: block; }
  .list-card__back { display: none; }
  .list-card__empty[hidden] { display: none; }
  .choice { display: block; }
  .label { display: block; }
  .drawer__box { display: flex; }
  .drawer__head { display: flex; }
  .drawer__foot { display: flex; }
  .cp { display: flex; }
  .cp__in { display: flex; }
  .cp__item { display: flex; }
  .cp__foot { display: flex; }
  .field__err[hidden], [data-form-error][hidden] { display: none; }
  .filters { display: flex; }
  .segmented { display: inline-flex; }
  .dropdown { display: inline-flex; }
  .dodo-report-filters__primary { display: grid; }
  .dodo-report-filters__advanced { display: grid; }
  .search-picker__panel input[type="checkbox"], .checklist input[type="checkbox"], input.checkbox { display: inline-grid; }
  .checklist__bar { display: flex; }
  .checklist__item:not([hidden]) { display: flex; }
  .period-picker { display: inline-flex; }
  .period-picker__btn { display: flex; }
  .period-panel__nav { display: flex; }
  .period-panel__nav-btn { display: grid; }
  .period-years { display: flex; }
  .period-year-col { display: flex; }
  .period-year-grid { display: grid; }
  .period-panel__presets { display: grid; }
  .period-panel__custom { display: flex; }
  .stat-grid { display: grid; }
  .stat { display: flex; }
  .stat__foot { display: flex; }
  .hbar { display: grid; }
  .legend { display: flex; }
  .legend__item { display: inline-flex; }
  .alert { display: flex; }
  .tabs { display: flex; }
  .topbar__pagetabs-panel { display: flex; }
  .avatar { display: grid; }
  .row { display: flex; }
  .col { display: flex; }
  .grid-2 { display: grid; }
  .grid-3 { display: grid; }
  .hero__stats { display: flex; }
  .svc { display: flex; }
  .svc__icon { display: grid; }
  .svc__foot { display: flex; }
  .doc-cols { display: grid; }
  .doc-row__top { display: grid; }
  .doc-row__label--with-comment { display: flex; }
  .notif-bell__header { display: flex; }
  .notif-bell__item { display: block; }
  @media (max-width: 900px) {
    .nav-toggle { display: inline-flex; }
    .sidebar__collapse-btn { display: none; }
    .list-card__back { display: inline-flex; }
    .list-card__empty { display: none; }
  }
  @media (max-width: 640px) {
    .doc-cols { display: none; }
    .doc-val::before, .doc-diff::before, .doc-fact-cell::before,
    .responsive-data-grid__cell::before,
    .responsive-table--cards tbody td[data-mobile-label]::before { display: block; }
    .responsive-data-grid__head { display: none !important; }
    .responsive-table--cards thead { display: none; }
    .responsive-table--cards tbody { display: grid; }
    .responsive-table--cards tbody tr { display: grid; }
    .responsive-table--cards tbody td { display: block; }
  }
}

/* Подсказка по наведению (issue 015): нативный title="" ненадёжен — заметная
   задержка перед показом, срывается кликом/выделением текста, не ловится
   скриншотами, недоступен с клавиатуры/тача. Здесь — сразу по :hover/:focus,
   без задержки; opacity/visibility, не display, поэтому вне слоя components. */
.hint-tip { position: relative; cursor: help; border-bottom: 1px dotted #9aa6b8; }
.table th.hint-tip { position: sticky; }   /* иначе relative из .hint-tip снимает залипание шапки у этих столбцов (/services/sites/testing, 18.10.2026) */
.hint-tip::after {
  content: attr(data-tip);
  position: absolute; left: 0; top: 100%; margin-top: 6px;
  background: #1f2937; color: #fff; padding: 8px 10px; border-radius: 6px;
  font-size: 12px; font-weight: 400; line-height: 1.45; white-space: normal;
  width: 320px; max-width: 80vw; z-index: 50;
  box-shadow: 0 4px 14px rgba(0, 0, 0, .18);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .12s ease;
}
.hint-tip:hover::after, .hint-tip:focus-visible::after { opacity: 1; visibility: visible; }

/* Календарь у даты (fx.date picker=True, fields.js). Цвет — токен --accent: в PRAI зелёный, в теме DODO оранжевый.
   Раскраска запретных дней (is-blocked) и легенда появляются, только если страница задала Fields.dateRule. */
.datepick { position: absolute; z-index: 70; top: calc(100% + 4px); left: 0; width: 300px; max-width: calc(100vw - 32px); padding: var(--s-3); }
.datepick[hidden], .datepick__legend[hidden] { display: none; }
.datepick__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--s-2); }
.datepick__head b { font-size: var(--fs-md); text-transform: capitalize; }
.datepick__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; text-align: center; }
.datepick__dow { padding: 4px 0; font-size: var(--fs-xs); color: var(--fg-muted); }
.datepick__day { height: 36px; border: 0; border-radius: var(--r-sm); background: transparent; color: var(--fg); font: inherit; font-size: var(--fs-sm); cursor: pointer; }
.datepick__day:hover:not(:disabled) { background: var(--bg-sunken); }
.datepick__day:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.datepick__day.is-today { box-shadow: inset 0 0 0 1px var(--line-strong); }
.datepick__day.is-selected, .datepick__day.is-selected:hover { background: var(--accent); color: #fff; font-weight: 600; }
.datepick__day.is-blocked { background: var(--bg-sunken); color: var(--fg-faint); cursor: not-allowed; }
.datepick__legend { display: flex; flex-wrap: wrap; gap: var(--s-3); margin-top: var(--s-3); font-size: var(--fs-xs); color: var(--fg-muted); }
.datepick__swatch { display: inline-block; width: 12px; height: 12px; margin-right: 4px; border-radius: 3px; background: var(--bg-sunken); box-shadow: inset 0 0 0 1px var(--line-strong); vertical-align: -2px; }
