/*
 * @mozaika/tokens — design tokens compiled from the MOZAIKA design-system spec.
 *
 * Source of truth: ../spec/*.dc.html (copied verbatim from the shared ds-spec
 * worktree). Where a page and IMPLEMENTATION.md disagree, the page wins.
 * Every token below cites its source page + section heading (never a line
 * number — the source tree is shared and actively edited, so line numbers
 * rot). Values not found literally in the spec are NOT invented here — see
 * ../docs/OPEN-QUESTIONS.md.
 *
 * Dark theme convention: an explicit [data-theme] choice always wins over the
 * system preference, in either direction.
 */

@import "@fontsource-variable/manrope";
@import "@fontsource-variable/jetbrains-mono";

:root {
  /* ---------------------------------------------------------------------
   * Fonts — Foundations.dc.html, "03 Типографика"
   * --------------------------------------------------------------------- */
  --mz-font-sans: 'Manrope Variable', 'Manrope', ui-sans-serif, system-ui, sans-serif;
  --mz-font-mono: 'JetBrains Mono Variable', 'JetBrains Mono', ui-monospace, monospace;

  /* ---------------------------------------------------------------------
   * Color roles — Foundations.dc.html, "02 Закон цвета"
   * Owner decision: --mz-agent (violet) is the primary/CTA color.
   * --mz-human (blue) is reserved for human-initiated actions only
   * (signature, primary form submit, selected checkbox, focus ring).
   * The old package aliased --mz-primary to --mz-human — do not repeat that.
   * --------------------------------------------------------------------- */
  --mz-agent: #6f3ff5;
  /* ОТКАТ ПОДЪЁМА 1a00c0f, РЕШЕНИЕ ВЛАДЕЛЬЦА 2026-09-05: облик важнее порога.
     Подъём до #9872ff (3,00:1 на --mz-surface-0 при пороге 3:1 WCAG 1.4.11)
     дал видимую обводку вместо тонкого разделителя — владелец отверг
     результат («обводка это ужасно выглядит»). Возвращено значение архива
     #ded2ff — 1,26:1 на --mz-surface-0, оговорка в
     scripts/contrast-waivers.json. */
  --mz-agent-line: #ded2ff;
  --mz-agent-soft: #f0ecff;
  --mz-human: #2456d8;
  /* ОТКАТ ПОДЪЁМА 1a00c0f, РЕШЕНИЕ ВЛАДЕЛЬЦА 2026-09-05: облик важнее порога.
     Подъём до #5b87f1 (3,00:1 на --mz-surface-0 при пороге 3:1 WCAG 1.4.11)
     дал видимую обводку вместо тонкого разделителя — владелец отверг
     результат. Возвращено значение архива #cfdcfb — 1,22:1 на --mz-surface-0,
     оговорка в scripts/contrast-waivers.json. */
  --mz-human-line: #cfdcfb;
  --mz-human-soft: #eaf0ff;
  --mz-primary: var(--mz-agent);

  /* Neutrals — Foundations.dc.html, "02 Закон цвета" (Нейтрали) */
  --mz-ink: #11182d;
  --mz-ink-2: #4a5468;
  /* ПОРОГ WCAG — РЕШЕНИЕ ВЛАДЕЛЬЦА 2026-09-04, а не дефект переноса. В
     артбордах стоит #8b93a7 — и Accessibility.dc.html «10.1» сама числит его
     одним из двух известных нарушений, так что подъём закрывает то, что
     архив уже признал провалом.
     Было #8b93a7 — 2,72:1 на --mz-surface-0 при пороге 4,5:1 (текст: это
     ступень, на которую ссылается --mz-text-subtle).
     Стало #656e85 — 4,51:1 на --mz-surface-0 (4,87 / 5,01 / 5,09 на -1/-2/-3).
     Тон и насыщенность сохранены (H 222,9°→223,1°, S 13,7%→13,7%), светлота
     60,0%→46,0%, минимальный проходящий сдвиг.
     ПОСЛЕДСТВИЕ, вынесенное в отчёт владельцу: --mz-ink-3, --mz-ink-4 и
     --mz-text-faint пришпилены одним и тем же порогом к одной и той же худшей
     поверхности, поэтому все три сходятся к светлоте ~46% и различаются
     тремя-четырьмя единицами на канал — светлая шкала тихого текста
     практически схлопывается в одну ступень. */
  --mz-ink-3: #656e85;
  /* Самый тихий текст — ступень между --mz-ink-3 (приглушённый) и --mz-ink-4
     (выключенный). Измерено: #a2a9ba встречается 32 раза на 6 из 16 страниц
     архива (Components 16, Shell 5, AIElements 3, Foundations 3, Patterns 3,
     States 2), и это не оттенок внутри одной роли, а тир из пяти:
     плейсхолдер поля/палитры (6), капс-заголовок группы и разделитель списка
     (8), мнемоника клавиши в моно (5), моно-счётчик и вторичное значение в
     конце строки (9), погашенная строка списка «да/нет» (2). Тридцать второе
     употребление — единственное фоновое: заливка знака 8×8 на карточке
     «Раздел недоступен для вашей роли» (States.dc.html, «04 Сбой, отказ,
     потеря контура»).
     Номера --mz-ink-N ступень не получает: шкала пронумерована по позиции, а
     эта стоит между 3 и 4 — перенумерация сдвинула бы --mz-ink-4, на который
     ссылаются файлы @mozaika/ui. Роль --mz-text-subtle ей не подходит: это
     --mz-ink-3, он на ступень темнее и занят приглушённым текстом; --mz-ink-4
     ближе по расстоянию, но это чернила выключенного элемента.
     Контраст (вычислено по WCAG): 2,35:1 на #fff, 2,22:1 на --mz-surface-inset.
     Тир заведомо ниже порога — тем и назван; текст, который обязан читаться,
     берёт --mz-text-subtle.
     ↑ Абзац выше описывает ПРЕЖНЕЕ значение #a2a9ba и оставлен как запись
     основания: замер употреблений (32 раза на 6 страницах) правкой не
     отменяется, отменяется только вывод «тир заведомо ниже порога».
     ПОРОГ WCAG — РЕШЕНИЕ ВЛАДЕЛЬЦА 2026-09-04, а не дефект переноса.
     Было #a2a9ba — 2,08:1 на --mz-surface-0 при пороге 4,5:1 (роль текстовая,
     карта ролей в scripts/contrast-waivers.json).
     Стало #646e87 — 4,51:1 на --mz-surface-0 (4,87 / 5,01 / 5,09 на -1/-2/-3).
     Тон и насыщенность сохранены (H 222,5°→222,9°, S 14,8%→14,9% — округление
     до 8 бит), светлота 68,2%→46,2%, минимальный проходящий сдвиг.
     Порядок на шкале формально сохранён (ink-3 #656e85 темнее, ink-4 #626e88
     темнее ещё), но зазор между тремя ступенями теперь 1…2 единицы на канал:
     роль «самый тихий текст» после подъёма к порогу перестаёт быть различимой
     на глаз. Это прямое следствие решения, а не ошибка вычисления. */
  --mz-text-faint: #646e87;
  /* ПОРОГ WCAG — РЕШЕНИЕ ВЛАДЕЛЬЦА 2026-09-04, а не дефект переноса.
     Было #b0b7c6 — 1,78:1 на --mz-surface-0 при пороге 4,5:1; через псевдоним
     --mz-state-disabled-text это же значение давало 1,90:1 на собственной паре
     --mz-state-disabled-bg, то есть текст выключенного контрола был почти
     нечитаем.
     Стало #626e88 — 4,52:1 на --mz-surface-0 (4,89 / 5,03 / 5,12 на -1/-2/-3),
     и 4,74:1 против --mz-state-disabled-bg.
     Тон и насыщенность сохранены (H 220,9°→221,1°, S 16,2%→16,2%), светлота
     73,3%→46,0%, минимальный проходящий сдвиг.
     Тёмная ветка этого имени НЕ поднята: она пришпилена литералом в
     scripts/verify-tokens.mjs (DARK_INK) и осталась в оговорках; см. отчёт.
     ВИДИМОЕ ПОСЛЕДСТВИЕ: выключенный контрол в светлой теме перестаёт читаться
     как выключенный — его текст теперь темнее прежнего --mz-ink-3. */
  --mz-ink-4: #626e88;
  /* ОТКАТ ПОДЪЁМА 1a00c0f, РЕШЕНИЕ ВЛАДЕЛЬЦА 2026-09-05: облик важнее порога.
     Подъём до #778bb9 (3,01:1 на --mz-surface-0 при пороге 3:1 WCAG 1.4.11)
     превратил волосяную линию оболочки в отчётливую рамку — владелец увидел
     результат и отверг («линии между элементами ужасно»). Возвращено значение
     архива #e4e8f1 — 1,09:1 на --mz-surface-0, оговорка в
     scripts/contrast-waivers.json. */
  --mz-line: #e4e8f1;
  --mz-line-soft: #eef0f6;

  /* Neutral marker (list/role marker swatch) — Components.dc.html "02.1 Как
     читать страницу"; reconfirmed on Brand.dc.html "06 Брендбук"
     ОТКАТ ПОДЪЁМА 1a00c0f, РЕШЕНИЕ ВЛАДЕЛЬЦА 2026-09-05: облик важнее порога.
     Подъём до #7c8caa (3,00:1 на --mz-surface-0 при пороге 3:1 WCAG 1.4.11)
     дал видимую обводку — владелец отверг результат. Возвращено значение
     архива #c3cad8 — 1,46:1 на --mz-surface-0, оговорка в
     scripts/contrast-waivers.json. */
  --mz-neutral-marker: #c3cad8;

  /* System status — aliases, not values: source (tokens-v2.css:344-346)
     subs in the semantic colours below (--mz-success/-warning/-error),
     which carry their own light/dark branches. Do not duplicate literals
     here — that freezes the status colours on the light-theme values. */
  --mz-ok: var(--mz-success);
  --mz-warn: var(--mz-warning);
  --mz-stop: var(--mz-error);

  /* Cyan — atmosphere/site accent only, never used in the data interface.
     Foundations.dc.html, "02 Закон цвета" */
  --mz-cyan: #22d3ee;

  /* ---------------------------------------------------------------------
   * Surfaces — Foundations.dc.html, "01 Три поверхности" (Светлая тема)
   * --------------------------------------------------------------------- */
  --mz-surface-0: #eef1f8; /* + atmosphere gradient, see --mz-atmosphere */
  --mz-surface-1: rgba(255, 255, 255, 0.62);
  /* ОТКАТ 2026-09-05 — РЕШЕНИЕ ВЛАДЕЛЬЦА: подъём до порога WCAG 1.4.11 (3:1)
     (rgba(255, 255, 255, 0.72) -> rgba(99, 99, 99, 0.72), 2026-09-04) рисовал
     видимую серую обводку на стеклянной поверхности вместо белого блика.
     Владелец увидел результат и распорядился откатить — форма важнее порога.
     Контраст против --mz-surface-3 на откаченном значении: 1,00:1 (не 3:1),
     оговорка вернулась в scripts/contrast-waivers.json с этой причиной.
     Стенд docs/probe/surface-1-border/index.html не удалён, дополнен пометкой
     об отклонении. */
  --mz-surface-1-border: rgba(255, 255, 255, 0.72);
  --mz-surface-1-blur: 34px;
  --mz-surface-2: #fcfdff;
  /* НАМЕРЕННО НЕ СВЕДЕНО с --mz-white, хотя литерал совпадает: общий
     источник значения, разная область (тот же приём, что у
     --mz-agent-elevated/--mz-focus, см. докблок в тёмной ветке ниже,
     «Elevated accent tints»). --mz-surface-3 — третья из трёх поверхностей
     темы: в тёмной теме та же роль равна #171f45, а не белому, то есть
     совпадение с --mz-white здесь чисто светлотемное и меняется по теме
     независимо. Псевдоним на --mz-white привязал бы шаг поверхности к
     бренд-свотчу, у которого нет и не будет собственной тёмной ветки —
     не тот же вопрос, что «какой белый брать для инверсии текста». Решение
     зафиксировано 2026-09-05. */
  --mz-surface-3: #ffffff;
  --mz-surface-inset: #f7f8fc; /* nested field inside a card, not a 4th step */
  /* Колодец: скруглённый квадрат 20…26px под знаком или порядковым номером и
     заливка нейтральной пилюли. Не то же, что --mz-surface-inset, и значение
     inset поэтому не трогается: Foundations «01 Три поверхности», блок
     «Разработчику», называет его дословно — «Вложенное поле внутри карточки —
     --mz-surface-inset (#F7F8FC), не четвёртая ступень», и сам #f7f8fc стоит в
     архиве 30 раз именно вложенным полем, кнопкой в покое и лентой скелетона.
     Колодец — другой объект и другое значение: 10 употреблений на 5 страницах,
     #f2f4f9 восемь раз (Components 3, Onboarding 3, Limits 2) и #f2f4f8 два
     раза (States «04 Сбой, отказ, потеря контура» — колодец 26×26 под знаком
     нейтрального отказа; Patterns — полоса заглушки). Две записи расходятся на
     1/255 в синем канале; это одно значение, взят мажоритарный литерал.
     Спрос на имя уже записан в потребителе: RestrictedField.module.css и
     Alert.module.css несут комментарии о вынужденной подмене #f2f4f9/#f2f4f8
     на --mz-surface-muted (то есть на --mz-surface-inset). */
  --mz-surface-well: #f2f4f9;

  /* ---------------------------------------------------------------------
   * Atmosphere — Foundations.dc.html, "01 Три поверхности" (window
   * background). This is the owner-verified canonical recipe; the denser
   * ".22"-opacity gradient shown as a swatch on the same page/on
   * Components.dc.html is an illustrative demo only — not the token value.
   * --------------------------------------------------------------------- */
  --mz-atmosphere: radial-gradient(44% 50% at 6% 4%, rgba(111, 63, 245, .12), transparent 66%),
    radial-gradient(40% 46% at 96% 88%, rgba(36, 86, 216, .12), transparent 66%),
    radial-gradient(28% 32% at 60% 100%, rgba(34, 211, 238, .06), transparent 70%);

  /* ---------------------------------------------------------------------
   * Spacing — Foundations.dc.html, "04 Сетка и отступы"
   * Base 4px, seven steps.
   * --------------------------------------------------------------------- */
  --mz-space-1: 4px;  /* icon↔text gap in dense rows */
  --mz-space-2: 8px;  /* between buttons, between rows in a group */
  --mz-space-3: 12px; /* nav item inner padding */
  --mz-space-4: 16px; /* card header, nested blocks */
  --mz-space-5: 20px; /* card body, gap between cards */
  --mz-space-6: 28px; /* between sections within a page */
  --mz-space-7: 34px; /* workspace edge padding, shell gap */

  /* ---------------------------------------------------------------------
   * Radius — Foundations.dc.html, "05 Радиусы, границы, тени"
   * --------------------------------------------------------------------- */
  --mz-radius-1: 6px;   /* badge, chip */
  --mz-radius-2: 9px;   /* button, row */
  --mz-radius-3: 12px;  /* field, avatar */
  --mz-radius-4: 18px;  /* card */
  --mz-radius-5: 22px;  /* shell panel */
  --mz-radius-pill: 999px; /* pill, dot */

  /* ---------------------------------------------------------------------
   * Shadows — Foundations.dc.html, "05 Радиусы, границы, тени"
   * Real literal box-shadow values (the old package aliased these to
   * elevation-token names instead of literals — not repeated here).
   * --------------------------------------------------------------------- */
  --mz-shadow-1: 0 1px 1px rgba(17, 24, 45, .04);  /* button at rest */
  --mz-shadow-2: 0 1px 2px rgba(17, 24, 45, .14);  /* primary button */
  --mz-shadow-3: 0 12px 34px rgba(17, 24, 45, .08); /* panel, card */
  --mz-shadow-4: 0 24px 64px rgba(11, 16, 43, .28); /* palette, modal */

  /* ---------------------------------------------------------------------
   * Motion — Foundations.dc.html, "06 Движение"
   * --------------------------------------------------------------------- */
  --mz-dur-1: 120ms; /* hover, press, focus */
  --mz-dur-2: 180ms; /* palette, toast, tooltip appearing */
  --mz-dur-3: 320ms; /* sidebar collapse, drawer */
  --mz-ease: cubic-bezier(.2, .8, .2, 1);
  --mz-stagger: 60ms; /* assembly stagger between grouped elements */
  --mz-skeleton-shimmer: 1.2s; /* States.dc.html, "02 Загрузка" (skeleton shimmer) */

  /* ---------------------------------------------------------------------
   * Typography ladder — Foundations.dc.html, "03 Типографика"
   * micro has no spec'd line-height — see OPEN-QUESTIONS.md.
   * --------------------------------------------------------------------- */
  --mz-text-display-size: 38px;
  --mz-text-display-line: 40px;
  --mz-text-display-weight: 700;
  --mz-text-display-tracking: -.03em;

  --mz-text-h1-size: 28px;
  --mz-text-h1-line: 32px;
  --mz-text-h1-weight: 600;
  --mz-text-h1-tracking: -.02em;

  --mz-text-h2-size: 21px;
  --mz-text-h2-line: 25px;
  --mz-text-h2-weight: 600;
  --mz-text-h2-tracking: -.02em;

  --mz-text-body-l-size: 15px;
  --mz-text-body-l-line: 24px;
  --mz-text-body-l-weight: 400;

  --mz-text-body-size: 13.5px;
  --mz-text-body-line: 20px;
  --mz-text-body-weight: 400; /* ranges 400–600 depending on emphasis */

  --mz-text-caption-size: 12px;
  --mz-text-caption-line: 17px;
  --mz-text-caption-weight: 400;

  --mz-text-micro-size: 10.5px;
  --mz-text-micro-weight: 700;
  --mz-text-micro-tracking: .11em;

  /* Mono sizes — Foundations.dc.html, "03 Типографика" (Моно — что именно).
     Canonical names come from the legacy source (tokens-v2.css, "03
     Типографика — моно"); the four -size/-large names below were invented by
     the first port (no such names in the source) and are kept only as
     aliases so the two live references without a fallback
     (`var(--mz-mono-label)`) do not stay broken and the six/six/one existing
     `var(--mz-mono-*-size, …)` call sites keep resolving. */
  --mz-mono-label: 11px; /* источник: tokens-v2.css — метки, оси графиков, дни недели */
  --mz-mono-row: 11.5px; /* источник: tokens-v2.css — значения в строках таблицы */
  --mz-mono-value: 13px; /* источник: tokens-v2.css — значения, числа календаря */
  --mz-mono-display: 22px; /* источник: tokens-v2.css — крупные числа */
  --mz-mono-label-size: var(--mz-mono-label);
  --mz-mono-line-size: var(--mz-mono-row);
  --mz-mono-value-size: var(--mz-mono-value);
  --mz-mono-large-size: var(--mz-mono-display);

  /* ---------------------------------------------------------------------
   * Icons & tessera — Foundations.dc.html, "07 Иконки и тессера"
   * --------------------------------------------------------------------- */
  --mz-icon-size: 16px;        /* in-interface */
  --mz-icon-size-heading: 20px; /* section headings */
  --mz-icon-stroke: 1.5px;     /* Icons.dc.html, "01" (Штрих 1,5) */

  --mz-tessera-size-label: 6px;
  --mz-tessera-size-row-min: 8px;
  --mz-tessera-size-row-max: 9px;
  --mz-tessera-size-heading: 11px;
  --mz-tessera-radius-min: 1px;
  --mz-tessera-radius-max: 2px;

  /* ---------------------------------------------------------------------
   * Density — Components.dc.html, "02.11 Размеры и плотность"
   * --------------------------------------------------------------------- */
  --mz-density-dense-height: 28px;
  --mz-density-dense-text: 12.5px;
  --mz-density-default-height: 34px;
  --mz-density-default-text: 13.5px;
  --mz-density-comfort-height: 40px;
  --mz-density-comfort-text: 14.5px;

  /* ---------------------------------------------------------------------
   * Focus ring — Components.dc.html, "02.1 Как читать страницу"
   * (outline: 2px solid #2456d8; outline-offset: 1px). Accessibility.dc.html
   * shows its own skip-link demo with outline-offset:2px — that is page-local
   * styling, not the token spec, and is not used here.
   *
   * КАНОН цвета кольца — `--mz-focus` (объявлен ниже в этом же :root).
   * В новом коде писать его. `--mz-focus-color` — второе имя того же объекта,
   * оставленное ради существующих ссылок: пять правил в @mozaika/ui (четыре в
   * shell/, каждое с пометкой о переименовании --mz-focus -> --mz-focus-color)
   * и одно в apps/site красят обводку через него. Ширина и отступ живут только
   * под именами --mz-focus-width/-offset — у них второго имени нет.
   *
   * Литерал у цвета один и лежит у --mz-focus. Здесь стоял второй, #2456d8, и
   * его же пришпиливал scripts/verify-tokens.mjs — гейт защищал дубль. Тёмную
   * ветку тогда получило только одно из имён, и светлое кольцо доживало до
   * тёмной темы; ровно тот дефект «у сущности два дома», что уже стоил
   * --mz-agent-line/--mz-ai-border. Проверка развёрнута: теперь она требует не
   * литерал, а совпадение РАЗРЕШЁННЫХ значений обоих имён в каждой из трёх
   * веток.
   * --------------------------------------------------------------------- */
  --mz-focus-color: var(--mz-focus); /* совместимость; канон — --mz-focus */
  --mz-focus-width: 2px;
  --mz-focus-offset: 1px;

  /* ---------------------------------------------------------------------
   * Interactive states — Components.dc.html, "02.1"–"02.4"
   * --------------------------------------------------------------------- */
  /* ОТКАТ ПОДЪЁМА 1a00c0f, РЕШЕНИЕ ВЛАДЕЛЬЦА 2026-09-05: облик важнее порога.
     Подъём до #7d90b7 (3,02:1 против --mz-state-disabled-bg при пороге 3:1
     WCAG 1.4.11) дал видимую обводку — владелец отверг результат. Возвращено
     значение архива #ebeef4 — 1,10:1 против --mz-state-disabled-bg, оговорка
     в scripts/contrast-waivers.json. */
  --mz-state-disabled-border: #ebeef4;
  /* Дубль литерала --mz-surface-inset — заведено при переносе отдельным
     именем; в артбордах имени --mz-state-disabled-bg нет, значение есть.
     Сведено 2026-09-04. */
  --mz-state-disabled-bg: var(--mz-surface-inset);
  /* Дубль литерала --mz-ink-4 — заведено при переносе отдельным именем;
     в артбордах имени нет, значение есть. Сведено 2026-09-04. */
  --mz-state-disabled-text: var(--mz-ink-4);

  /* Отступление от архива по решению владельца, 2026-09-04 — в артбордах
     стоит #d98a8a. Причина: #d98a8a даёт 2.64 / 2.59 / 2.34 на трёх светлых
     поверхностях (--mz-surface-3, --mz-surface-2, --mz-surface-0), ниже
     порога 3:1 WCAG 1.4.11 для нетекстового контраста, а все три
     употребления в архиве (Components.dc.html: кнопка «Опасная», поле ввода
     в состоянии ошибки, чекбокс «Ошибка») — именно границы контролов.
     Значение вычислено, не подобрано: тон исходного сохранён точно
     (H 0.0°), насыщенность — с точностью до округления до 8 бит
     (S 51.0% → 50.8%), светлота опущена 69.6% → 62.5% — минимум,
     при котором порог берётся на всех трёх поверхностях. Новые контрасты:
     3.40 / 3.34 / 3.00. */
  --mz-state-error-border: #d06f6f;
  --mz-state-error-text: #aa3333;

  /* Дубль литерала роли --mz-human — заведено при переносе отдельным именем;
     в артбордах имени нет, значение есть. Роль неизменна по теме, но линию
     на тёмной поверхности рисует поднятый вариант — см. обе тёмные ветки
     ниже. Сведено 2026-09-04. */
  --mz-state-selected-border: var(--mz-human);
  /* Дубль литерала --mz-human-soft — заведено при переносе отдельным
     именем; в артбордах имени нет, значение есть. Сведено 2026-09-04. */
  --mz-state-selected-bg: var(--mz-human-soft);
  --mz-state-selected-text: #1c449f;
  --mz-state-selected-weight: 600;
  --mz-state-selected-radius: 8px;

  /* Дубль литерала --mz-agent-line — заведено при переносе отдельным
     именем; в артбордах имени нет, значение есть. Сведено 2026-09-04. */
  --mz-state-agent-proposed-border: var(--mz-agent-line);
  --mz-state-agent-proposed-bg: #faf8ff;

  /* ---------------------------------------------------------------------
   * Brand scale — Brand.dc.html, "01" (logo in interface) and "03" (Tess
   * avatar in product)
   * --------------------------------------------------------------------- */
  --mz-brand-logo-min: 18px;
  --mz-brand-logo-max: 22px;
  --mz-brand-tess-avatar-min: 26px;
  --mz-brand-tess-avatar-max: 30px;

  /* ---------------------------------------------------------------------
   * Shell — owner-mandated outer margin = 10px. Patterns.dc.html is
   * internally inconsistent about this number: its intro section "01
   * Оболочка" states "зазор 14px, внешние поля 14px" in prose but its own
   * demo grid literal is gap:12px; only the later "05.1 Оболочка целиком"
   * demo's literal (grid-template-columns ... gap:10px, inside a
   * padding:14px wrapper) matches the owner's mandated value. That later,
   * more specific section is treated as canonical for the gap; the 14px
   * wrapper padding is captured separately. See report for the full
   * disagreement.
   * --------------------------------------------------------------------- */
  --mz-shell-outer-gap: 10px;   /* Patterns.dc.html, "05.1 Оболочка целиком" */
  --mz-shell-window-padding: 14px; /* Patterns.dc.html, "05.1 Оболочка целиком" */
  --mz-shell-sidebar-width: 272px; /* Foundations.dc.html, "04 Сетка и отступы" */
  --mz-shell-rail-width: 60px;
  --mz-shell-detail-width: 384px;
  --mz-shell-header-height: 58px;
  --mz-shell-breakpoint-detail: 1200px;
  --mz-shell-breakpoint-rail: 860px;

  /* =========================================================================
   * Legacy MOZAIKA V6 port — names used by ported packages/ui components that
   * this file did not yet declare. Source: the old package's two token
   * layers, worktrees/mozaika-design/lib/mozaika-design/src/tokens/
   * {tokens.css = "v1", tokens-v2.css = "v2"}. Resolution rule for a name
   * present in both (owner decision): v2 wins outright, v1's value is not
   * carried over at all. v2 is treated as the base edition here, so it is
   * declared directly on :root — the source's `[data-mz-version="v2"]`
   * gate and its `[data-skin="tess"]` specificity ladder are a device of the
   * old package's dual-edition switch and are not reproduced.
   * A v1/v2 value phrased as `var(--mz-x)` in the source is carried over as
   * the same reference wherever this file already declares `--mz-x` — never
   * flattened to a literal, so the alias keeps tracking whatever this file's
   * own canonical role token resolves to (which is why some of these no
   * longer match the source's literal hex — this file's own token is
   * authoritative). Where the source's reference target is v1/v2-internal
   * and this file has no such name (its typographic ladder
   * `--mz-type-*`, its semantic radius names `--mz-radius-badge/-control/
   * -field/-card/-shell`, or its neutral/blue/violet ramps), the reference is
   * resolved down to this file's equivalent already-declared name, or — where
   * no equivalent exists — to the literal the source itself computes.
   * ------------------------------------------------------------------- */

  /* Role aliases — v2, "БАЗОВЫЙ СЛОЙ" / "02 Закон цвета" / "01 Три
     поверхности". `--mz-ai` etc. are the source's pre-v2 names, kept alive as
     aliases onto this file's own `--mz-agent`/`--mz-human`/`--mz-ink`/
     `--mz-line`/`--mz-surface-*` roles (collision, v2 wins). */
  --mz-ai: var(--mz-agent);
  --mz-ai-subtle: var(--mz-agent-soft);
  /* Псевдоним, а НЕ вторая копия литерала. Копия здесь уже стоила дефекта:
     светлые `--mz-agent-line` и `--mz-ai-border` держали один и тот же #ded2ff
     двумя литералами, а тёмную ветку получил только второй — в тёмной теме
     `--mz-agent-line` оставался светлой сиреневой границей. Одно значение —
     один дом. */
  --mz-ai-border: var(--mz-agent-line);
  /* КАНОНИЧЕСКОЕ имя цвета кольца фокуса и единственный дом его значения: v2 —
     «решение человека», поэтому цвет берётся от --mz-human. --mz-focus-color
     выше — алиас на эту строку (совместимость), --mz-focus-width/-offset —
     самостоятельные токены, не имена этого. */
  --mz-focus: var(--mz-human);
  --mz-bg: var(--mz-surface-0);
  --mz-border: var(--mz-line);
  --mz-border-strong: var(--mz-line);
  --mz-surface: var(--mz-surface-3);
  --mz-surface-hover: var(--mz-surface-inset);
  --mz-surface-muted: var(--mz-surface-inset);
  --mz-surface-inverse: var(--mz-neutral-900); /* только v1 */
  --mz-text: var(--mz-ink);
  --mz-text-muted: var(--mz-ink-2);
  --mz-text-subtle: var(--mz-ink-3);
  /* --mz-text-faint — ступень тише --mz-text-subtle; объявлена литералом в
     блоке нейтралей выше, там же её место на шкале и обоснование. */
  /* Дубль литерала #ffffff — заведено при переносе отдельным литералом;
     в архиве это «v1 neutral-0» дословно, то есть тот же объект, что уже
     стоит каноном у --mz-white (Neutral scale блок). Псевдоним, а не второй
     литерал: --mz-info-fg/--mz-ai-fg ниже уже ссылаются var(--mz-white) —
     сведение продолжает уже начатый приём. Тёмная ветка не трогается: там
     --mz-text-inverse инвертируется к var(--mz-neutral-900), что не имеет
     отношения к белому. Сведено 2026-09-05. */
  --mz-text-inverse: var(--mz-white);

  /* Product-accent (human) extension — v2 "02 Закон цвета", collision (v2 wins). */
  --mz-primary-hover: #1d4ac3;
  --mz-primary-active: #1941ad;
  /* Дубль литерала #ffffff — тот же приём и то же основание, что у
     --mz-text-inverse выше: «v1 neutral-0» дословно, канон — --mz-white.
     Тёмная ветка не трогается: там значение сведено к другому канону,
     --mz-neutral-950 (см. блок «Neutral scale»). Сведено 2026-09-05. */
  --mz-primary-fg: var(--mz-white);
  --mz-primary-subtle: var(--mz-human-soft);
  --mz-primary-border: var(--mz-human-line);

  /* Semantic status palette — только v1 (tokens.css), параллельная схема к
     уже объявленным --mz-ok/--mz-warn/--mz-stop. */
  --mz-success: #16a36a;
  --mz-success-fg: var(--mz-neutral-900);
  --mz-success-subtle: #eaf8f2;
  /* Янтарь предупреждения. Здесь стоял #e99015 — оранжевый, и это ошибка
     значения, а не расщепление роли. Brand.dc.html «06 Брендбук» пишет
     дословно: «Оранжевый в осколках Тесс — единственное место, где палитра
     выходит за violet, синий и cyan. В UI оранжевого нет; предупреждение —
     янтарное #C98A1A, и это системный цвет, а не бренд.» Замер это
     подтверждает с обеих сторон: #e99015 встречается по 16 страницам ОДИН раз,
     и это один из квадратов 8×8 в мозаике знака на SiteSignature — ровно тот
     оранжевый осколок, который цитата из UI и исключает. Янтарь же встречается
     15 раз и всегда в одной роли — заливка знака состояния: #c98a1e двенадцать
     раз (Governance, ромб 8×8 у строк открытых вопросов, карточка #fdf9f2 /
     рамка #e6cfa8 / текст #5c4622), #c98a1a дважды (AIElements «Риск», ромб
     9×9; States «04 Сбой, отказ, потеря контура», круг 8×8 на карточке лимита
     токенов). Две записи расходятся на 4/255 в синем канале — один цвет; взят
     литерал, названный брендбуком.
     Разводить роли нечем: текстом на белом архив янтарь не ставит ни разу, так
     что «заливка знака против текста» осталась бы догадкой без замера.
     Контраст (вычислено по WCAG): 2,94:1 на #ffffff и 2,89:1 на карточке
     #fcfdff — против 2,48:1 и 2,44:1 у прежнего #e99015, то есть правка
     контраст улучшает. Порога 4,5:1 не берёт ни то, ни другое, а
     @mozaika/ui ставит это имя в `color:` пятнадцать раз; отдельная ступень
     «янтарь, читаемый текстом на светлом» в архиве не нарисована и здесь не
     выдумывается — строка отчёта владельцу. */
  --mz-warning: #c98a1a;
  --mz-warning-subtle: #fff5e2;
  /* ПОРОГ WCAG — РЕШЕНИЕ ВЛАДЕЛЬЦА 2026-09-04. Двигается ЗАЛИВКА, а не текст:
     провал числится за --mz-error-fg (белый на заливке ошибки, 4,36:1 при
     пороге 4,5:1), но белый здесь — канон v1 neutral-0, и залить его в тёмный
     значило бы чинить не то место.
     Было #dc3d4b — 4,36:1 под белым текстом. Стало #db3846 — 4,50:1.
     Тон и насыщенность сохранены (H 354,7°→354,8°, S 69,4%→69,4%), светлота
     55,1%→54,0%, минимальный проходящий сдвиг — правка почти незаметна глазом
     и держится ровно на пороге. */
  --mz-error: #db3846;
  /* Дубль литерала #ffffff — тот же приём, что у --mz-text-inverse/-primary-fg
     выше: «v1 neutral-0» дословно, канон — --mz-white. Тёмная ветка не
     трогается: там значение сведено к --mz-neutral-950 (см. «Neutral scale»).
     Сведено 2026-09-05. */
  --mz-error-fg: var(--mz-white);
  --mz-error-subtle: #fdecef;
  --mz-error-hover: #c13240;
  --mz-error-active: #af2b38;
  /* ПОРОГ WCAG — РЕШЕНИЕ ВЛАДЕЛЬЦА 2026-09-04. Двигается ЗАЛИВКА, не текст —
     то же основание, что у --mz-error выше (--mz-info-fg = --mz-white, канон).
     Было #2878e6 — 4,27:1 под белым текстом при пороге 4,5:1.
     Стало #2173e5 — 4,52:1.
     Тон и насыщенность сохранены (H 214,7°→214,9°, S 79,2%→79,0%), светлота
     52,9%→51,4%, минимальный проходящий сдвиг. */
  --mz-info: #2173e5;
  --mz-info-subtle: #eef2ff;

  /* Neutral scale + brand swatches — только v1 (tokens.css). Пять ступеней
     шкалы нужны ссылками выше и ниже; остальные не переносятся (не
     запрошены). */
  --mz-neutral-200: #d7deea;
  --mz-neutral-300: #c6cfde;
  --mz-neutral-400: #aab5ce;
  --mz-neutral-900: #11182d;
  /* Дубль литерала #080e22 — заведено при переносе девятью отдельными именами
     (--mz-primary-fg/-success-fg/-error-fg/-warning-fg/-info-fg/-ai-fg и
     тремя --mz-elevation-N) в ОБОИХ тёмных механизмах — 18 повторов одного
     значения без единого var(). Комментарий у «Third measurement pass» ниже
     уже называл источник (tokens-v2.css: --mz-neutral-950) прозой, но токен
     не заводил. Заведено здесь настоящим именем — девять ролей ниже читают
     var(--mz-neutral-950) в обеих тёмных ветках. Сведено 2026-09-05. */
  --mz-neutral-950: #080e22;
  --mz-navy: #0b102b;
  --mz-white: #ffffff;

  /* Code block palette — только v1. Тёмная поверхность своя, не привязана к
     теме (в источнике одни и те же значения в обеих темах). */
  --mz-code-bg: #101b42;
  --mz-code-header: #151f43;
  --mz-code-fg: #dce4ff;
  --mz-code-accent: #a8b5ff;
  --mz-code-label: #8ea0c8;
  --mz-code-gutter: #7a88ad;

  /* Active nav item — v2 "Shell 15.1": светлая редакция — белая карточка с
     рамкой и полосой, без заливки цветом. */
  --mz-nav-active-bg: var(--mz-surface-3);
  --mz-nav-active-border: var(--mz-line);
  --mz-nav-active-fg: var(--mz-ink);
  --mz-nav-marker: #6f3ff5; /* v2 — Foundations 02, violet дословно */

  /* Spacing extension — только v1, плюс --mz-space-8 (collision, v2 wins:
     32 → 34) и --mz-space-edge (только v2, "Foundations 04"). */
  --mz-space-1-5: 6px;
  --mz-space-8: 34px;
  --mz-space-10: 40px;
  --mz-space-12: 48px;
  --mz-space-16: 64px;
  --mz-space-edge: 34px;
  --mz-avatar-overlap: 10px; /* только v1 */

  /* Radius extension — v2 "05 Радиусы", collision (v2 wins). v2's semantic
     names (`--mz-radius-badge/-control/-field/-card/-shell`) are not carried
     over as separate tokens — this file's own numbered scale
     (`--mz-radius-1`…`-5`) already holds the same values under the same
     roles, so the aliases resolve onto that scale instead. */
  --mz-radius-xs: var(--mz-radius-1); /* badge/chip — 5 → 6 */
  --mz-radius-sm: var(--mz-radius-control); /* button/row — 8 → 9, см. ниже */
  --mz-radius-md: var(--mz-radius-3); /* field/avatar — 12 → 12 дословно */
  --mz-radius-lg: var(--mz-radius-4); /* card — 16 → 18 */
  --mz-radius-control: 9px; /* v2 — "кнопка, строка (v1 держал 10)" */

  /* Elevation — только v1, легаси-имя тени, параллельное уже объявленным
     --mz-shadow-1…4. */
  --mz-elevation-0: none;
  --mz-elevation-1: 0 1px 2px color-mix(in srgb, var(--mz-neutral-900) 6%, transparent);
  --mz-elevation-2: 0 8px 24px color-mix(in srgb, var(--mz-neutral-900) 10%, transparent);
  --mz-elevation-3: 0 16px 48px color-mix(in srgb, var(--mz-neutral-900) 14%, transparent);

  /* Motion extension — v2 "06 Движение", collision (v2 wins). Alias onto
     this file's own --mz-dur-1/2/3 (уже объявлены, те же значения). */
  --mz-duration-fast: var(--mz-dur-1);
  --mz-duration-standard: var(--mz-dur-2);
  --mz-duration-slow: var(--mz-dur-3);

  /* Typography extension — v2 "03 Типографика", collision (v2 wins).
     v2's own `--mz-type-*` ladder is not carried over as a separate scale —
     this file's `--mz-text-*-size/-line` names already hold the same
     numbers under the same roles, so the aliases resolve onto those. */
  --mz-text-body: var(--mz-text-body-l-size); /* 16 → 15 */
  --mz-leading-body: var(--mz-text-body-l-line);
  --mz-text-small: var(--mz-text-body-size); /* 14 → 13.5 */
  --mz-leading-small: var(--mz-text-body-line);
  --mz-text-dense: var(--mz-text-body-size); /* 13 → 13.5 */
  --mz-leading-dense: var(--mz-text-body-line);
  --mz-text-meta: var(--mz-text-caption-size); /* 12 → 12 дословно */
  --mz-leading-meta: var(--mz-text-caption-line);
  --mz-text-caption: var(--mz-text-caption-size); /* 11 → 12 */
  --mz-leading-caption: var(--mz-text-caption-line);
  --mz-text-micro: var(--mz-text-micro-size); /* 10 → 10.5 */
  --mz-leading-micro: 14px; /* вычислено в v2 — у micro нет заданного интерлиньяжа в источнике */
  --mz-text-h3: var(--mz-text-h2-size); /* 26 → 21 */
  --mz-leading-h3: var(--mz-text-h2-line);
  --mz-text-title: var(--mz-text-h2-size); /* 20 → 21 */
  --mz-leading-title: var(--mz-text-h2-line);
  /* Веса. Замерено по всем 16 страницам архива (`grep -a` по
     spec/*.dc.html, регулярка `font-weight:\d{3}`): 600 — 378 раз, 700 — 354,
     500 — 21, 800 — 4, 400 — 3; 100/200/300 не встречаются ни разу.
     Тёмной ветки у весов нет — это числа, а не цвета, и правило «объявить во
     всех трёх ветках» к ним не относится. */
  /* 300. ЭТОГО ЗНАЧЕНИЯ В АРХИВЕ НЕТ. Не порт, а решение владельца от
     2026-09-04, и записано здесь именно затем, чтобы следующая сверка словаря
     с архивом не прочитала его как дефект переноса и не «починила» обратно —
     ровно так в этом репозитории уже сломали --mz-surface-inset.
     Замер, на котором стоит слово «нет»: по всем 16 страницам spec/*.dc.html
     веса 100, 200 и 300 не встречаются ни разу.
     Начертание при этом настоящее, а не синтетическое: --mz-font-sans — это
     'Manrope Variable', а её ось веса по
     node_modules/@fontsource-variable/manrope/metadata.json идёт 200…800 с
     шагом 1, то есть 300 шрифт действительно умеет. */
  --mz-weight-light: 300;
  --mz-weight-regular: 400; /* только v1 */
  --mz-weight-medium: 500;
  --mz-weight-semibold: 600;
  --mz-weight-bold: 700;
  /* 800 в архиве есть, но ровно в одном употреблении, и оно спорит с правилом
     той же системы. Все 4 случая — `SiteSignature`, колонка цифр итога
     (`font-size:clamp(22px,2.4vw,30px);font-weight:800;letter-spacing:-.03em`),
     то есть крупные числа, а не заголовки. При этом `Foundations` 03 говорит
     дословно: «Заголовки — вес 600–700, не 800: на кириллице 800 читается как
     крик». Противоречия здесь нет ровно до тех пор, пока 800 не попадает на
     заголовок: имя объявлено потому, что рисунок его использует, но заголовкам
     оно запрещено правилом, и это не «на усмотрение вызывающего». Столкновение
     вынесено владельцу, а не разрешено здесь молча. */
  --mz-weight-extrabold: 800;

  /* Icon size — только v2 (не путать с уже объявленным --mz-icon-size). */
  --mz-icon-ui: 16px;

  /* Track / skeleton — только v1. Один литерал, два имени — канон --mz-track
     (три потребителя в @mozaika/ui: Progress, DataChart, Slider, против
     одного у --mz-skeleton), второе — ссылка. Сведено 2026-09-05: обе тёмные
     ветки несли тот же дубль (#293555 дважды на каждый механизм) — см. там же. */
  --mz-track: #e8ecf3;
  --mz-skeleton: var(--mz-track);

  /* Density (row height / text size / line height) — только v2, "Components
     02.11". Три ступени: значение по умолчанию здесь, dense/comfort — в
     собственных селекторах ниже файла (атрибут `data-mz-density`, а не
     `data-mz-version`: это рабочее состояние компонента, не редакция). */
  --mz-density-row: 34px;
  --mz-density-text: var(--mz-text-body-size);
  --mz-density-line: var(--mz-text-body-line);

  /* Z-index scale — только v1. */
  --mz-z-sticky: 1100;
  --mz-z-overlay: 1200;
  --mz-z-modal: 1300;
  --mz-z-popover: 1400;
  --mz-z-toast: 1500;
  --mz-z-tooltip: 1600;

  /* Second measurement pass (после появления packages/ui/src/shell/
     AgentTerminal.module.css у параллельной ветки) — та же методология,
     имена дословно из источника. */
  --mz-blink: 2s; /* v2 — "поднят в базовый слой файла, 2s нужны обеим редакциям" */
  --mz-blue-300: #6c7dff; /* только v1 — акцент тёмной темы, используется как значение и в светлой, и в тёмной редакции без отдельного override */
  --mz-violet-300: #b69cff; /* только v1 — акцент AI тёмной темы, та же логика */
  --mz-radius-field: var(--mz-radius-3); /* v2 — "поле, аватар", тот же смысл и значение (12px), что и уже объявленный --mz-radius-3 */
  --mz-type-micro-size: var(--mz-text-micro-size); /* v2 raw name; тот же смысл и значение, что уже объявленный --mz-text-micro-size (10.5px) */
  --mz-type-micro-line: var(--mz-leading-micro); /* v2 raw name; тот же смысл и значение (14px), что --mz-leading-micro */
  --mz-type-micro-tracking: 0.11em; /* только v2 — "единственная капительная ступень", эквивалента в файле ещё нет */

  /* Third measurement pass — required by packages/tokens/color.ts's colorVar
     map (correction #2), not by verify-ui-tokens.mjs directly: color.ts
     references these CSS variable names for role colors, and every one it
     references must resolve to something real. Source: только v1
     tokens.css, base brand swatches (страница "Swatch") — static, no
     dark-theme override in the source. */
  --mz-violet: #6f3ff5;
  --mz-indigo: #5b4cf0;
  --mz-blue: #2456d8;
  --mz-cobalt: #2367e8;
  /* -fg companions for warning/info/ai — только v1, "решение": greater
     contrast than white in each case. Light: v1's --mz-neutral-900 has a
     direct equivalent already in this file; dark value is
     var(--mz-neutral-950) — тот же токен, что --mz-primary-fg/-success-fg/
     -error-fg берут в обеих тёмных ветках (см. «Neutral scale» выше). */
  --mz-warning-fg: var(--mz-neutral-900);
  --mz-info-fg: var(--mz-white);
  --mz-ai-fg: var(--mz-white);

  /* =========================================================================
   * ПЕЧАТНАЯ ГЕОМЕТРИЯ ДОКУМЕНТА — 32 имени, единственный дом.
   *
   * ОТКУДА И ПОЧЕМУ ЗДЕСЬ. До 2026-09-05 эти имена жили в apps/documents:
   * 22 общих в apps/documents/src/index.css (:root) и 10 в
   * apps/documents/src/proposal/proposal.module.css (блок .sheet). Докблок
   * index.css называл их дословно «НЕ ТОКЕНЫ». Решением владельца от
   * 2026-09-05 («должны быть единый источник правды») это утверждение
   * перестало быть верным: остров перенесён сюда целиком. Решение записано
   * заранее и отдельно — apps/showcase/src/spec/gaps/Documents.md, раздел
   * «Геометрия --doc-* — решено, переезд отдельной правкой (2026-09-05)».
   *
   * ЗНАЧЕНИЯ ПЕРЕНЕСЕНЫ ДОСЛОВНО. Ни одно число не тронуто, вместе с числами
   * перенесены и обоснования, которые при них стояли: комментарий, оторванный
   * от своего значения, перестаёт быть основанием и становится преданием.
   *
   * ПРЕФИКС --mz-doc-, А НЕ ПРЕЖНИЙ --doc-. Причина механическая: этот файл
   * публикуется как пакет (@mozaika/tokens, publishConfig в package.json), и
   * всё, что он объявляет на :root, попадает в глобальное пространство
   * пользовательских свойств КАЖДОГО потребителя. Непрефиксованное --doc-* в
   * опубликованном пакете — заявка на чужое имя: любой потребитель, у которого
   * есть собственное --doc-page-w, получил бы молчаливое столкновение. Внутри
   * этого дерева тот же приём уже действует (у лендинга своё --site-*), и
   * единственное, чем словарь пакета отличается от чужого, — префикс --mz-.
   *
   * ОДНА ВЕТКА, А НЕ ТРИ. Весь остальной файл объявляет значение трижды
   * (:root, [data-theme='dark'], @media (prefers-color-scheme: dark)); здесь —
   * один раз в :root, и это не пропуск. Правило «во всех трёх ветках»
   * относится к ЦВЕТУ (scripts/verify-tokens.mjs, «No colour role may reach
   * dark theme wearing its light value»), а среди этих 32 имён нет ни одного
   * цвета: миллиметры листа, пункты кегля, em трекинга, два пикселя линии. У
   * бумаги нет тёмной темы; обе точки входа документов вдобавок пришпиливают
   * data-theme="light" на <html>. Единственная ветвь, которая у печатной
   * геометрии есть, — НОСИТЕЛЬ, и она стоит ниже, сразу за :root: @media print.
   * ========================================================================= */

  /* --- лист ---------------------------------------------------------------
   * A4 книжная. Поля 18 мм по бокам, 16 мм сверху и снизу — минимум, при
   * котором строка в 90–100 знаков не растягивается сверх читаемого и остаётся
   * запас под дырокол слева. Архив печатной полосы не задаёт вовсе, и это
   * решение принято от механики, а не от вкуса. */
  --mz-doc-page-w: 210mm;
  --mz-doc-page-h: 297mm;
  --mz-doc-page-margin-y: 16mm;
  --mz-doc-page-margin-x: 18mm;

  /* --- слайд --------------------------------------------------------------
   * На экране слайд — 16:9. На бумаге лист A4 альбомный (297 × 210 мм), и
   * 16:9 от его ширины даёт 297 × 167,06 мм: композиция переносится на бумагу
   * ровно той, какой её видели на экране, а остаток листа уходит в поля.
   * Растянуть слайд на весь лист было бы вторым вариантом, но тогда печать
   * перевёрстывает то, что докладчик уже утвердил на экране. */
  --mz-doc-slide-w: 297mm;
  --mz-doc-slide-h: 167mm;
  --mz-doc-slide-pad: 14mm;

  /* --- линии и рамки ------------------------------------------------------
   * Волосяная линия: 1px на экране, но на печати 1px при 96dpi — это 0,26 мм,
   * что тоньше уверенно печатаемого штриха у лазерного принтера. В печатной
   * ветке она поднимается до 0,2 мм явно — см. @media print сразу за :root. */
  --mz-doc-hairline: 1px;
  --mz-doc-frame-min: 88px;

  /* --- маркетинговая шкала документа --------------------------------------
   * Интерфейсный ладдер выше по файлу доходит до --mz-text-display-size;
   * титульному листу и первому слайду нужен размер крупнее, а печатному тексту
   * — фиксированный кегль в пунктах, потому что vw на бумаге не значит ничего.
   * Те же основания, что у --site-* в лендинге. */
  --mz-doc-text-cover: 34pt;
  --mz-doc-text-slide-title: 30pt;
  --mz-doc-text-slide-lead: 15pt;
  --mz-doc-text-h2: 16pt;
  --mz-doc-text-h3: 12pt;
  --mz-doc-text-body: 10pt;
  --mz-doc-text-small: 9pt;
  --mz-doc-text-micro: 7.5pt;

  /* Трекинг: свод (Voice 04 «Кириллица») требует −0,03em на крупных
     заголовках и НИКАКОГО сжатия на мелком тексте. Здесь это два имени, а не
     число, вписанное в каждое правило. */
  --mz-doc-track-display: -0.03em;
  --mz-doc-track-label: 0.1em;

  /* Вертикальный ритм печатной полосы. */
  --mz-doc-gap-section: 9mm;
  --mz-doc-gap-block: 5mm;
  --mz-doc-gap-row: 2.5mm;

  /* --- ступени, у которых потребитель сегодня один: «Коммерческое
   * предложение» ------------------------------------------------------------
   * Эти десять стояли на .sheet в proposal.module.css, и стояли там по
   * названной причине: «CSS-модуль скоупит классы, но не :root, и объявление в
   * :root протекло бы в колоду, у которой лист альбомный и колонки другие».
   * Причина снята замером, а не решением: ни одно из десяти имён колода не
   * читает (0 употреблений на deck.module.css и DeckDocument.tsx), поэтому
   * протекать в ней нечему — затенить или подменить эти имена колода не может,
   * она к ним не обращается. Своя колонка у колоды, когда понадобится, возьмёт
   * СВОЁ имя, а не переобъявит это: переобъявление одного имени двумя
   * значениями — ровно тот дефект, ради которого весь блок сюда и переехал.
   *
   * Почему ширина колонки таблицы КП вообще токен: печатная геометрия здесь
   * одна лестница, и разрезать её по двум домам («общее — в пакете, частное —
   * в приложении») значит вернуть остров, только меньше. Один документ как
   * единственный потребитель — свойство сегодняшнего дня, а не свойство
   * величины. */

  /* Кегли, которых нет ни в интерфейсном ладдере, ни в общей печатной шкале
     выше: у КП две ступени выше --mz-doc-text-h2 — название документа и
     заголовок предмета. В колоде они другие. */
  --mz-doc-title: 20pt;
  --mz-doc-hero: 17pt;

  /* Колонки таблиц. Полоса набора — 210 − 18 − 18 = 174 мм, и все ширины ниже
     считаны от неё. Держать их именами, а не числами по месту, нужно затем,
     что колонка цены и колонка объёма должны совпадать между таблицей
     стоимости и итоговыми строками. */
  --mz-doc-price-no: 10mm;
  --mz-doc-price-vol: 34mm;
  --mz-doc-price-sum: 38mm;
  --mz-doc-role-col: 44mm;
  --mz-doc-sec-term: 40mm;
  --mz-doc-sec-state: 34mm;
  --mz-doc-stage-due: 40mm;
  --mz-doc-sign-line: 14mm;
}

/* ===========================================================================
 * Печатный носитель — единственная ветвь печатной геометрии.
 *
 * 1px при 96dpi — 0,26 мм; лазерный принтер такую линию печатает неровно,
 * поэтому на бумаге волосяная линия поднимается до 0,2 мм. Переопределение
 * стоит здесь, а не в apps/documents, по той же причине, по которой сюда
 * переехало само значение: у величины один дом, и «сколько это на бумаге» —
 * часть того же ответа, что «сколько это на экране», а не отдельный ответ
 * приложения.
 *
 * Блок стоит СРАЗУ за :root и до тёмных веток намеренно: одинаковая
 * специфичность решается порядком, и это единственное место файла, где
 * порядок несёт смысл. Ни одна ветка ниже --mz-doc-* не трогает.
 * ========================================================================= */
@media print {
  :root {
    --mz-doc-hairline: 0.2mm;
  }
}

/* ===========================================================================
 * Density variants — v2 "Components 02.11": "Три размера, не больше."
 * `data-mz-density` is a component runtime state (dense/default/comfort),
 * not an edition switch — unlike the source's `data-mz-version` gate it is
 * kept as-is. The narrow-viewport override folds `dense` back to `default`
 * per the source's own accessibility rule (10.5: "dense at 200% zoom
 * automatically becomes default"); 1200px is `--mz-shell-breakpoint-detail`
 * above, taken as the zoom-independent proxy the source itself uses.
 * ========================================================================= */
[data-mz-density='dense'] {
  --mz-density-row: 28px;
  --mz-density-text: var(--mz-text-caption-size);
  --mz-density-line: var(--mz-text-caption-line);
}

[data-mz-density='comfort'] {
  --mz-density-row: 40px;
  --mz-density-text: var(--mz-text-body-l-size);
  --mz-density-line: var(--mz-text-body-l-line);
}

@media (max-width: 1200px) {
  [data-mz-density='dense'] {
    --mz-density-row: 34px;
    --mz-density-text: var(--mz-text-body-size);
    --mz-density-line: var(--mz-text-body-line);
  }
}

/* ===========================================================================
 * Dark theme — Foundations.dc.html, "01 Три поверхности" (Тёмная тема — без
 * стекла). Explicit [data-theme] always wins over system preference.
 * ========================================================================= */
[data-theme='dark'] {
  --mz-surface-0: #0b102b;
  --mz-surface-1: #141b3d; /* opaque in dark — no blur */
  /* Граница оболочки в тёмной теме. Здесь стояло .14 — та же опровергнутая
     обводка: `--mz-surface-1-border` и `--mz-line` завелись одним коммитом с
     одним и тем же .14, взятым с ДЕМОНСТРАЦИОННЫХ образцов 22px в блоке
     «Тёмная тема — без стекла» (Foundations.dc.html, «01 Три поверхности»);
     `--mz-line` с этого основания уже сняли. Своего основания у .14 нет:
     собственная внешняя рамка того же блока — настоящая тёмная поверхность,
     а не образец — несёт `border:1px solid rgba(255,255,255,.1)`, и то же
     число даёт проза «05 Радиусы, границы, тени» дословно: «В тёмной теме
     тени почти не видны — уровень держится тоном поверхности и
     rgba(255,255,255,.1) на границе». Shell.dc.html тёмные панели обводит
     .08 / .09 / .1 и .14 не использует нигде. Имя остаётся отдельным от
     `--mz-line` (в светлой теме это .72 стекла против #e4e8f1), но в тёмной
     теме стекло выключено (`--mz-surface-1-blur: 0px`) и число у них одно.
     ОТКАТ 2026-09-05 — РЕШЕНИЕ ВЛАДЕЛЬЦА: подъём до 1.4.11 (3:1) (.1 -> .34,
     2026-09-04) рисовал видимую обводку. Владелец отказался от результата,
     распоряжение — откатить обе тёмные ветки. Контраст на откаченной .1
     против --mz-surface-0: 1,29:1 (не 3:1); оговорка вернулась в
     scripts/contrast-waivers.json. Число у --mz-surface-1-border и --mz-line
     по-прежнему одно. */
  --mz-surface-1-border: rgba(255, 255, 255, .1);
  --mz-surface-1-blur: 0px;
  --mz-surface-2: #111838;
  --mz-surface-3: #171f45;
  /* Вложенное поле тёмной темы. Значение источника
     (tokens-v2.css, ветка v2 [data-theme="dark"]) вместе с его обоснованием:
     «вычислено — вложенного поля тёмной темы страница не называет. Взят тот же
     шаг яркости от карточки, что в светлой (1,0613), но в сторону света: вниз
     от #171F45 идти некуда — там рабочая область и атмосфера.»
     Здесь стоял #131a38 со ссылкой на Shell.dc.html «15.2 Рельс · 60px и тёмная
     тема» — цитата верная, но она о ДРУГОМ объекте: «Поверхность не чернее
     #131a38» сказано про тёмный сайдбар (тот же абзац начинается словами
     «Тёмный сайдбар — только в тёмной теме»), а не про вложенное поле внутри
     карточки. Ограничение чужого токена, приписанное этому, уводило наведение
     на карточку #171f45 ВНИЗ, тогда как источник ведёт его вверх. */
  --mz-surface-inset: #1c2449;
  /* Тёмный колодец. Источника нет — тёмной прорисовки этих мест архив не даёт;
     значение вычислено тем же приёмом, каким в этом файле построен тёмный
     --mz-surface-inset: тот же шаг яркости, что в светлой теме, но в сторону
     света. Светлый шаг inset→колодец — 1,0369 по (L+0,05); тот же множитель
     вверх от #1c2449 даёт #1e274e. Приём здесь же и проверен: заявленный в
     соседнем комментарии светлый шаг карточка→inset 1,0613 воспроизводится
     формулой точно, а тёмная пара #171f45→#1c2449 даёт 1,0604 — то есть
     нынешний тёмный inset этим приёмом и построен. Канарейка самого пересчёта:
     обратный ход на светлой паре возвращает #f3f4f8 при измеренном #f2f4f9 —
     расхождение 1/255 в двух каналах, потому что светлая пара не чистое
     масштабирование яркости; на заведомо ложном множителе 2,0 тот же пересчёт
     даёт #b1b2b5 и ничего не подтверждает. */
  --mz-surface-well: #1e274e;

  --mz-ink: #f8fafd;
  --mz-ink-2: #aab5ce; /* источник: v1, измерено — 7,30…9,09 на страничных тёмных */
  /* источник, вычислено: «#667085 из v1 даёт 2,1…2,5 на страничных тёмных…
     Тон поднят по светлоте до 4,5:1 на самой светлой тёмной поверхности:
     4,51 на inset, 4,78 на карточке.» */
  /* ПОДЪЁМ ПО ПРОПОРЦИИ ЛЕСТНИЦЫ — РЕШЕНИЕ ВЛАДЕЛЬЦА 2026-09-05, вслед за уже
     поднятым --mz-ink-4 (комментарий ниже, «ВИДИМОЕ ПОСЛЕДСТВИЕ»). Причина:
     подъём --mz-ink-4 до порога WCAG (#6f778d -> #868d9f) поднял его WCAG
     relative luminance с 0,18496 до 0,26621 — мимо --mz-ink-3 (0,26528),
     третья и четвёртая ступени лестницы схлопнулись и почти поменялись
     местами (0,26528 против 0,26621).
     Значение выведено не на глаз, а переносом пропорции шагов исходной
     лестницы. Шаги по WCAG L: ink/ink-2 = 2,072, ink-2/ink-3 = 1,736,
     ink-3/ink-4 = 1,434 — неравномерны, поэтому равные интервалы по L не
     годятся. Концы пролёта закреплены: --mz-ink-2 (L 0,46050, не трогается)
     и уже поднятый --mz-ink-4 (L 0,26621). Доля шага ink-2→ink-3 в сумме двух
     логарифмов сохранена: ln(1,736)/(ln(1,736)+ln(1,434)) = 0,605. Полный
     пролёт ln(0,46050/0,26621) = 0,548. Отсюда целевая L(ink-3) =
     0,46050 / exp(0,605·0,548) ≈ 0,3306.
     От целевой WCAG-светлоты цвет подобран тем же приёмом, каким выведен
     --mz-ink-4: тон и насыщенность HSL прежнего значения зафиксированы
     (H 221,4°, S 13,4%), лестница HSL-светлоты подбирается перебором до
     совпадения WCAG L с целью — совпадение при HSL L≈63,00%. Округление до
     8-битного канала даёт H 220,8°, S 13,2% (уход — артефакт округления, тот
     же порядок величины, что у --mz-ink-4).
     Было #848da1 — WCAG L 0,26528, контраст 4,78 / 4,51 (--mz-surface-3 /
     --mz-surface-inset). Стало #949cad — WCAG L 0,33090 (8-битное округление
     даёт не точные 0,3306, а 0,33090), контраст 5,78 / 5,45 там же, 6,78 на
     --mz-surface-0.
     Лестница вновь монотонна и разведена: L(ink-2) 0,46050 > L(ink-3)
     0,33090 > L(ink-4) 0,26621 — проверено scripts/verify-tokens.mjs (ранее
     эту проверку нельзя было поставить: она краснела бы на законном подъёме
     --mz-ink-4; теперь можно). */
  --mz-ink-3: #949cad;
  /* Тёмная ветка самого тихого текста. Источника нет: тёмной прорисовки этих
     мест архив не даёт вовсе (страницы содержат ровно два data-theme="dark"
     фрагмента — образцы поверхностей на Foundations «01 Три поверхности» и
     сайдбар на Shell «15.2», и ни в одном из них этих ролей нет). Значение
     вычислено переносом той же операции, что связывает светлую тройку:
     интерполяция в линейном sRGB между --mz-ink-3 и --mz-ink-4 при t=0,58.
     Канарейка операции: та же формула на СВЕТЛОЙ паре (#8b93a7 → #b0b7c6)
     воспроизводит измеренный #a2a9ba байт в байт; при t=0,5 и t=0,6 — уже нет.
     Тёмной ветке ink-4 при этом не отдаётся: тёмная шкала сжата (ink-3→ink-4
     это 8,0 п.п. светлоты против 13,3 п.п. в светлой), и «одна ступень тише
     ink-3» упёрлась бы ровно в ink-4 — то самое слипание с чернилами
     выключенного, из-за которого роль и заводится отдельным именем.
     Порядок сохранён в обеих темах; контраст (вычислено): 4,79 на #0b102b,
     4,08 на карточке #171f45, 3,85 на #1c2449 — между ink-3 (4,51…5,61) и
     ink-4 (3,36…4,18).
     ↑ Абзац выше описывает ПРЕЖНЕЕ значение #788196 и оставлен как запись
     основания: интерполяция и её канарейка правкой не отменяются, отменяется
     только сам результат.
     ПОРОГ WCAG — РЕШЕНИЕ ВЛАДЕЛЬЦА 2026-09-04. Было #788196 — 4,08:1 на
     худшей странично-тёмной поверхности (--mz-surface-3 #171f45) при пороге
     4,5:1. Стало #80899c — 4,53:1 на --mz-surface-3 (5,32 / 4,77 / 4,93 на
     -0/-1/-2). Тон и насыщенность сохранены (H 222,0°→220,7°, S 12,5%→12,4% —
     уход тона артефакт округления до 8 бит), светлота 52,9%→55,7%, вверх, и
     это минимальный проходящий сдвиг.
     Порядок на тёмной шкале сохранён: ink-3 #848da1 светлее, ink-4 #6f778d
     темнее. Зазор до ink-3 сжался с 12 до 4 единиц на канал — сжатие тёмной
     шкалы, о котором предупреждает абзац выше, правка усугубляет.
     Тёмная ветка ЭТОГО имени поднята, а соседняя ink-4 — нет: ink-4 пришпилена
     литералом в scripts/verify-tokens.mjs (DARK_INK), --mz-text-faint там не
     пришпилена. Асимметрия внешняя, не выбор. */
  --mz-text-faint: #80899c;
  /* ПОРОГ WCAG — РЕШЕНИЕ ВЛАДЕЛЬЦА, подъём до 4,5 (текстовая роль).
     Было #6f778d (источник, вычислено — ступень ниже ink-3 тем же тоном;
     3,36…4,18 на страничных тёмных): 3,56:1 против --mz-surface-3.
     Стало #868d9f — 4,80:1 там же.
     Связывает значение не эта проверка, а более жёсткая соседняя: та же
     ступень под именем --mz-state-disabled-text меряется против собственной
     пары --mz-state-disabled-bg (= --mz-surface-inset, #1c2449) и давала 3,36.
     Светлота выведена по 4,5 именно на inset — там #868d9f даёт 4,52, а шаг
     ниже (#858c9e) уже 4,47 и порог не берёт. Тон и насыщенность сохранены
     (H 224,0°→223,2°, S 11,9%→11,5% — округление до 8 бит).
     ВИДИМОЕ ПОСЛЕДСТВИЕ (на момент этой правки): тишайшая ступень тёмной
     чернильной лестницы сравнялась по светлоте с --mz-ink-3 (L 0,2661 против
     0,2652) — «тише» между этими двумя ступенями больше не читалось по
     светлоте. Ступень не была удалена и не слита: имена держат разные роли, а
     развести их дальше в моменте правки означало бы уронить --mz-ink-3 ниже
     порога 4,5:1.
     ЗАКРЫТО 2026-09-05: --mz-ink-3 поднят следом (#848da1 -> #949cad, см.
     комментарий у его объявления выше) по пропорции исходной лестницы, а не
     на глаз — это и было развести дальше, просто без «в моменте», которого
     тогда не было: подъём ink-3 больше не требовал ронять его ниже порога,
     потому что порог 4,5:1 у него был не впритык (4,78/4,51 против 4,53 у
     text-faint). Лестница вновь монотонна: L(ink-2) 0,46050 > L(ink-3)
     0,33090 > L(ink-4) 0,26621. */
  --mz-ink-4: #868d9f;
  /* Foundations.dc.html, "05 Радиусы, границы, тени", блок «Разработчику»,
     дословно: «В тёмной теме тени почти не видны — уровень держится тоном
     поверхности и rgba(255,255,255,.1) на границе». Здесь стояло .14 — это
     обводка ДЕМОНСТРАЦИОННЫХ образцов 22px в блоке «Тёмная тема — без стекла»
     той же страницы, приём показа тёмных плашек, а не значение токена.
     ОТКАТ 2026-09-05 — РЕШЕНИЕ ВЛАДЕЛЬЦА: подъём до 1.4.11 (3:1) (.1 -> .34,
     2026-09-04) рисовал видимую обводку. Владелец отказался от результата,
     распоряжение — откатить обе тёмные ветки; облик важнее порога. Контраст
     на откаченной .1 против --mz-surface-0: 1,29:1 (не 3:1); оговорка
     вернулась в scripts/contrast-waivers.json. Псевдонимы --mz-border и
     --mz-nav-active-border идут следом — дом у значения один. */
  --mz-line: rgba(255, 255, 255, .1);
  --mz-line-soft: rgba(255, 255, 255, .06); /* источник, вычислено — тот же приём вдвое слабее: страница даёт одну тёмную границу, а ролей две */

  /* Elevated accent tints used inside the dark sidebar — Shell.dc.html.
     --mz-focus ниже несёт то же #4d7ae8 собственным литералом, и это
     намеренно: у этих двух имён общий источник значения, но разная область —
     здесь акценты внутри рельса, там обводка на всех поверхностях. Основание
     разделения расписано у --mz-focus. */
  --mz-agent-elevated: #9d7bff;
  --mz-human-elevated: #4d7ae8;

  /* Тёмные тинты роли агента — источник, измерено (v1: заливка панели вывода AI
     и граница AI Message/v6 Role=Tess в тёмной теме). Тона роли здесь не
     поднимаются: подъём светлоты по Shell.dc.html «15.2» относится к тёмному
     сайдбару и живёт отдельными именами --mz-agent-elevated/--mz-human-elevated
     выше. Без этих двух строк светлые сиреневые #f0ecff/#ded2ff доживали до
     тёмной темы.
     ОТКАТ 2026-09-05 — РЕШЕНИЕ ВЛАДЕЛЬЦА: подъём --mz-agent-line до 1.4.11
     (3:1) через псевдоним var(--mz-agent-elevated) (#49367d -> #9d7bff,
     2026-09-04) рисовал видимую обводку. Владелец отказался от результата,
     распоряжение — откатить обе тёмные ветки; облик важнее порога. Значение
     возвращено литералом #49367d — контраст 1,60:1 против --mz-surface-3
     (не 3:1); оговорка вернулась в scripts/contrast-waivers.json. Псевдоним
     --mz-ai-border идёт следом. */
  --mz-agent-soft: #211a47;
  /* Тёмная ветка фона предложения агента. Значение оставлено как есть при
     откате линии выше: это чинит настоящий дефект — «имя объявлено только в
     :root и доживает до тёмной темы светлым» (без этой строки светлый почти
     белый #faf8ff доживал бы до тёмной темы), а не рисует линию. Порядок
     строк (эта — до --mz-agent-line) был обязателен, когда линия стояла
     поднятой var(--mz-agent-elevated); при откаченном литерале #49367d
     соседняя проверка --mz-state-agent-proposed-border не зависит от этого
     порядка так же остро, но строка сохранена на своём месте, чтобы не
     переставлять код без причины.
     Значение взято из словаря, а не выведено на глаз: тёмный контрагент этого
     семейства — --mz-agent-soft, что прямо записано в
     scripts/dark-branch-waivers.json в причине снятой отсюда оговорки
     («the dark counterpart of that family is --mz-agent-soft #211a47»), и тот
     же #211a47 уже стоит тёмной веткой у соседнего фона выделения
     --mz-nav-active-bg. Псевдонимом, а не копией литерала. */
  --mz-state-agent-proposed-bg: var(--mz-agent-soft);
  --mz-agent-line: #49367d;

  /* Тёмные тинты роли человека — источник (tokens-v2.css, ветка v2
     [data-theme="dark"]) даёт их не литералом, а относительной формулой.
     Формула перенесена ДОСЛОВНО, а не развёрнута в вычисленный хекс: она
     разрешается против того --mz-human и того --mz-surface, что стоят в своей
     ветке, и литерал здесь перестал бы следовать за ролью — ровно тот дефект,
     который эти две строки и закрывают. Без них светлые голубые
     #eaf0ff/#cfdcfb доживали до тёмной темы на карточке #171f45.
     Тон самой роли здесь не поднимается: подъём светлоты по Shell.dc.html
     «15.2» («Тёмный сайдбар — только в тёмной теме. Violet и синий подняты по
     светлоте (#9d7bff, #4d7ae8)») относится к тёмному сайдбару и живёт
     отдельным именем --mz-human-elevated выше — то же разделение, что у роли
     агента. Правка задевает и псевдонимы --mz-primary-subtle/--mz-primary-border,
     которые ссылаются на эти два имени и потому получают тёмные значения
     вместе с ними. */
  /* ОТКАТ 2026-09-05 — РЕШЕНИЕ ВЛАДЕЛЬЦА: подъём до 1.4.11 (3:1) (роль внутри
     формулы var(--mz-human) -> var(--mz-human-elevated), доля 35% -> 79%,
     2026-09-04) рисовал видимую обводку. Владелец отказался от результата,
     распоряжение — откатить обе тёмные ветки; облик важнее порога.
     Формула возвращена на прежнюю роль и прежнюю долю — остаётся формулой,
     не разворачивается в литерал, ровно по той же причине, по которой она и
     заведена формулой: оторвать линию от роли значит вернуть дефект, ради
     которого эти строки написаны. Контраст на откаченной формуле (35% на
     обычной роли) — 1,35:1 против --mz-surface-3 (не 3:1); оговорка
     вернулась в scripts/contrast-waivers.json. Псевдоним --mz-primary-border
     идёт следом. */
  --mz-human-line: color-mix(in srgb, var(--mz-human) 35%, var(--mz-surface));
  --mz-human-soft: color-mix(in srgb, var(--mz-human) 12%, transparent);

  /* Legacy MOZAIKA V6 port — dark values, mirrors the light-side block added
     to :root above. Same source/collision rule (v2 wins); only names that
     carry an explicit dark override in v1/v2 appear here — everything else
     stays whatever its light-side alias resolves to. */
  /* Было var(--mz-agent) — см. решение у --mz-primary ниже в этом же блоке.
     Псевдоним, а не второй литерал: заливка одна, дом у неё один. */
  --mz-ai: var(--mz-primary);
  --mz-ai-subtle: var(--mz-agent-soft);
  --mz-ai-border: var(--mz-agent-line);
  /* Кольцо фокуса тёмной темы. РАСШИРЕНИЕ ОБЛАСТИ ПРИМЕНЕНИЯ, решение
     2026-09-04 — не подбор оттенка и не дефект переноса; следующая сверка
     должна прочитать эту запись, а не «поправить» значение обратно.
     Здесь стояло var(--mz-human), то есть светлый #2456d8 доживал до тёмной
     темы. Замер (вычислено по WCAG, порог 1.4.11 — 3:1):
       #2456d8 — 3,02 на --mz-surface-0, 2,71 на --mz-surface-1, 2,80 на
                 --mz-surface-2, 2,58 на --mz-surface-3, 2,43 на
                 --mz-surface-inset: четыре поверхности из пяти ниже порога;
       #4d7ae8 — 4,69 / 4,20 / 4,35 / 3,99 / 3,77 на тех же пяти: проходит везде.
     Оговорки, освобождающей кольцо, в архиве нет: таблица контраста
     Accessibility «10.1» числит его ПРОХОДЯЩИМ («Кольцо фокуса · 3:1 ·
     #2456d8 · 5,9:1», и цифра набрана зелёным #2f7a4f, тогда как провалы в той
     же таблице идут жёлтым с ⚠), в карточке «Два известных нарушения» его нет
     (там #8b93a7 и #cfd6e6), а слова «тёмн…» на всей странице не встречается
     ни разу — 5,9 относится к светлой теме, тёмную никто не считал.
     Значение не выдумано: его называет сам архив. Shell «15.2» дословно —
     «Тёмный сайдбар — только в тёмной теме. Violet и синий подняты по
     светлоте (#9d7bff, #4d7ae8)». Область там рельс сайдбара; кольцу фокуса
     тёмного значения архив не даёт вовсе. Расширяется именно область: принцип
     «в тёмной синий поднят» берётся со страницы вместе с её же числом.
     ЭТО НЕ ПРОТИВОРЕЧИТ соседнему запрету поднимать тон роли (см. комментарий
     у --mz-human-soft/-line ниже): --mz-human — семантика «решает человек»
     (заливка бейджа, основная кнопка, выбранный чекбокс) и в тёмной теме
     остаётся #2456d8. Кольцо — не роль, а средство ввода, и почему оно вообще
     синее, записано отдельным решением: Governance, журнал решений, 03.09.2026
     — «Кольцо фокуса — синее. Фокус — след человеческого ввода, значит цвет
     человека. Violet оставлен агенту.» След цвета роли, а не сама роль;
     поэтому кольцо и может подняться, не поднимая роли.
     Собственный литерал, а не var(--mz-human-elevated), хотя значение сегодня
     то же: у того имени объявленная область — акценты ВНУТРИ тёмного сайдбара
     (см. его комментарий выше), кольцо же выходит на все пять поверхностей, и
     общее имя утащило бы кольцо за собой при следующей правке рельса. Совпадение
     значений здесь — ответ на один и тот же вопрос («синий, поднятый для
     тёмной»), а не один объект. Это НЕ тот дефект, что описан у
     --mz-agent-line/--mz-ai-border: там одна роль жила двумя литералами и
     тёмную ветку получил только один; здесь два разных объекта объявлены
     рядом в одной ветке, и потерять ветку поодиночке они не могут. */
  --mz-focus: #4d7ae8;
  /* Второе имя того же кольца: пять правил в @mozaika/ui и apps/site красят
     обводку через --mz-focus-color, а не через --mz-focus, и без этой строки
     подъём не дошёл бы до них — половина проводки. Алиасом, а не вторым
     литералом. Светлая ветка сведена так же (:root), так что формально эта
     строка теперь избыточна — алиас в :root наследуется сюда сам. Оставлена
     намеренно и закреплена проверкой: подъём кольца должен читаться в той
     ветке, где он происходит, и снимать её можно только вместе с проверкой
     алиаса в :root. */
  --mz-focus-color: var(--mz-focus);
  --mz-state-selected-border: var(--mz-human-elevated); /* поднятая линия роли, см. :root */
  --mz-bg: var(--mz-surface-0);
  --mz-border: var(--mz-line);
  --mz-border-strong: var(--mz-line);
  --mz-surface: var(--mz-surface-3);
  --mz-surface-hover: var(--mz-surface-inset);
  --mz-surface-muted: var(--mz-surface-inset);
  /* НАМЕРЕННО НЕ СВЕДЕНО с --mz-white, хотя в тёмной теме литерал совпадает —
     тот же приём разделения, что у --mz-surface-3 в :root (см. докблок там)
     и у --mz-agent-elevated/--mz-focus ниже: общий ответ, разный вопрос.
     --mz-surface-inverse — инверсия ТЕКУЩЕЙ поверхности (в светлой теме это
     var(--mz-neutral-900), почти чёрный, см. :root), а не бренд-свотч белого;
     то, что тёмная инверсия численно равна --mz-white, — свойство темы, а не
     тождество объектов. Псевдоним на --mz-white сцепил бы эту роль с
     литералом, у которого нет собственной светлой/тёмной пары ролей.
     Решение зафиксировано 2026-09-05. */
  --mz-surface-inverse: #ffffff;
  --mz-text: var(--mz-ink);
  --mz-text-muted: var(--mz-ink-2);
  --mz-text-subtle: var(--mz-ink-3);
  --mz-text-inverse: var(--mz-neutral-900);
  /* ПОРОГ WCAG — РЕШЕНИЕ ВЛАДЕЛЬЦА 2026-09-04. Новая строка: тёмной ветки у
     --mz-primary не было вовсе, имя доживало до тёмной темы светлым
     var(--mz-agent) = #6f3ff5, и почти чёрный --mz-primary-fg #080e22 на нём
     давал 3,38:1 при пороге 4,5:1 — текст основной кнопки в тёмной теме.
     Стало #865ef7 — 4,52:1 под тем же текстом. Тон и насыщенность взяты у
     роли и сохранены (H 255,8°→255,7°, S 90,1%→90,5% — округление до 8 бит),
     светлота 60,4%→66,7%, вверх, минимальный проходящий сдвиг.
     ПОЧЕМУ ВЕТКА ЗДЕСЬ, А НЕ У --mz-agent. Поднять саму роль было бы одной
     правкой вместо двух, но роль заморожена: --mz-agent объявлен
     тема-инвариантным решением владельца (scripts/dark-branch-waivers.json,
     remove_when "never"), и scripts/verify-tokens.mjs держит эту заморозку
     двусторонним ратчетом — появление у --mz-agent тёмного объявления делает
     оговорку устаревшей и роняет гейт. Провал же числится за ДВУМЯ именами
     заливки — --mz-primary-fg (пара --mz-primary) и --mz-ai-fg (пара --mz-ai),
     — поэтому ветка только у --mz-primary закрыла бы половину, а второй
     литерал у --mz-ai воспроизвёл бы дефект «одно значение, два дома», уже
     стоивший этому файлу светлого --mz-agent-line в тёмной теме. Отсюда:
     литерал один, стоит здесь (--mz-primary — каноническое имя заливки,
     probe.html красит им #btn-primary), --mz-ai выше сделан псевдонимом на
     него. Роль --mz-agent не тронута и остаётся #6f3ff5 в обеих темах.
     Рядом стоящее расхождение, НЕ создано этой правкой и не чинится ею:
     --mz-primary-hover/-active в тёмной теме синие (#668deb/#4674e7), тогда
     как сама заливка фиолетовая. Строка отчёта владельцу. */
  --mz-primary: #865ef7;
  --mz-primary-hover: #668deb;
  --mz-primary-active: #4674e7;
  --mz-primary-fg: var(--mz-neutral-950);
  --mz-primary-subtle: var(--mz-human-soft);
  --mz-primary-border: var(--mz-human-line);
  --mz-success: #43d59b;
  --mz-success-fg: var(--mz-neutral-950);
  --mz-success-subtle: #123a31;
  --mz-warning: #ffc26a;
  --mz-warning-subtle: #4a3318;
  --mz-error: #ff7885;
  --mz-error-fg: var(--mz-neutral-950);
  --mz-error-subtle: #4d1e2a;
  --mz-error-hover: #ff929b;
  --mz-error-active: #e06372;
  --mz-info: #6c7dff;
  --mz-info-subtle: #202c62;
  --mz-nav-active-bg: #211a47;
  --mz-nav-marker: #9d7bff;
  /* Дубль литерала #293555 — тот же приём и решение, что у светлой пары
     выше (Track / skeleton, :root): канон --mz-track, --mz-skeleton — ссылка.
     Сведено 2026-09-05. */
  --mz-skeleton: var(--mz-track);
  --mz-track: #293555;
  /* Дубль литерала #080e22 — заведён здесь вместе с --mz-primary-fg/-success-fg/
     -error-fg/-warning-fg/-info-fg/-ai-fg этой же ветки; канон —
     --mz-neutral-950 (см. «Neutral scale» в :root). Сведено 2026-09-05. */
  --mz-elevation-1: 0 1px 2px color-mix(in srgb, var(--mz-neutral-950) 24%, transparent);
  --mz-elevation-2: 0 8px 24px color-mix(in srgb, var(--mz-neutral-950) 40%, transparent);
  --mz-elevation-3: 0 16px 48px color-mix(in srgb, var(--mz-neutral-950) 56%, transparent);
  --mz-warning-fg: var(--mz-neutral-950);
  --mz-info-fg: var(--mz-neutral-950);
  --mz-ai-fg: var(--mz-neutral-950);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    --mz-surface-0: #0b102b;
    --mz-surface-1: #141b3d;
    --mz-surface-1-border: rgba(255, 255, 255, .1); /* откат 2026-09-05, решение владельца — см. явную тёмную ветку выше */
    --mz-surface-1-blur: 0px;
    --mz-surface-2: #111838;
    --mz-surface-3: #171f45;
    --mz-surface-inset: #1c2449; /* см. явную тёмную ветку выше */
    --mz-surface-well: #1e274e; /* см. явную тёмную ветку выше */

    --mz-ink: #f8fafd;
    --mz-ink-2: #aab5ce;
    --mz-ink-3: #949cad; /* поднято по пропорции лестницы вслед за --mz-ink-4 — см. явную тёмную ветку выше */
    --mz-text-faint: #80899c; /* поднято до порога 4,5 — см. явную тёмную ветку выше */
    --mz-ink-4: #868d9f; /* поднято до порога 4,5 — см. явную тёмную ветку выше */
    --mz-line: rgba(255, 255, 255, .1); /* откат 2026-09-05, решение владельца — см. явную тёмную ветку выше */
    --mz-line-soft: rgba(255, 255, 255, .06);

    --mz-agent-elevated: #9d7bff;
    --mz-human-elevated: #4d7ae8;

    --mz-agent-soft: #211a47; /* см. явную тёмную ветку выше */
    --mz-state-agent-proposed-bg: var(--mz-agent-soft); /* реальный дефект, оставлено — см. явную тёмную ветку выше */
    --mz-agent-line: #49367d; /* откат 2026-09-05, решение владельца — см. явную тёмную ветку выше */

    /* Формулой, а не литералом — см. явную тёмную ветку выше. */
    --mz-human-line: color-mix(in srgb, var(--mz-human) 35%, var(--mz-surface)); /* откат 2026-09-05, решение владельца — см. явную тёмную ветку выше */
    --mz-human-soft: color-mix(in srgb, var(--mz-human) 12%, transparent);

    --mz-ai: var(--mz-primary); /* псевдоним поднятой заливки — см. явную тёмную ветку выше */
    --mz-ai-subtle: var(--mz-agent-soft);
    --mz-ai-border: var(--mz-agent-line);
    --mz-focus: #4d7ae8; /* поднятое кольцо — см. явную тёмную ветку выше */
    --mz-focus-color: var(--mz-focus); /* см. явную тёмную ветку выше */
    --mz-state-selected-border: var(--mz-human-elevated); /* поднятая линия роли, см. :root */
    --mz-bg: var(--mz-surface-0);
    --mz-border: var(--mz-line);
    --mz-border-strong: var(--mz-line);
    --mz-surface: var(--mz-surface-3);
    --mz-surface-hover: var(--mz-surface-inset);
    --mz-surface-muted: var(--mz-surface-inset);
    --mz-surface-inverse: #ffffff; /* намеренно не сведено с --mz-white — см. явную тёмную ветку выше */
    --mz-text: var(--mz-ink);
    --mz-text-muted: var(--mz-ink-2);
    --mz-text-subtle: var(--mz-ink-3);
    --mz-text-inverse: var(--mz-neutral-900);
    --mz-primary: #865ef7; /* поднятая заливка под #080e22 — см. явную тёмную ветку выше */
    --mz-primary-hover: #668deb;
    --mz-primary-active: #4674e7;
    --mz-primary-fg: var(--mz-neutral-950); /* канон см. явную тёмную ветку выше */
    --mz-primary-subtle: var(--mz-human-soft);
    --mz-primary-border: var(--mz-human-line);
    --mz-success: #43d59b;
    --mz-success-fg: var(--mz-neutral-950);
    --mz-success-subtle: #123a31;
    --mz-warning: #ffc26a;
    --mz-warning-subtle: #4a3318;
    --mz-error: #ff7885;
    --mz-error-fg: var(--mz-neutral-950);
    --mz-error-subtle: #4d1e2a;
    --mz-error-hover: #ff929b;
    --mz-error-active: #e06372;
    --mz-info: #6c7dff;
    --mz-info-subtle: #202c62;
    --mz-nav-active-bg: #211a47;
    --mz-nav-marker: #9d7bff;
    --mz-skeleton: var(--mz-track); /* канон см. явную тёмную ветку выше */
    --mz-track: #293555;
    --mz-elevation-1: 0 1px 2px color-mix(in srgb, var(--mz-neutral-950) 24%, transparent);
    --mz-elevation-2: 0 8px 24px color-mix(in srgb, var(--mz-neutral-950) 40%, transparent);
    --mz-elevation-3: 0 16px 48px color-mix(in srgb, var(--mz-neutral-950) 56%, transparent);
    --mz-warning-fg: var(--mz-neutral-950);
    --mz-info-fg: var(--mz-neutral-950);
    --mz-ai-fg: var(--mz-neutral-950);
  }
}
