@font-face {
  font-family: "Onest Fallback";
  src: local("Segoe UI"), local("Roboto"), local("Helvetica Neue"), local("Arial"), local("Noto Sans"), local("DejaVu Sans");
  ascent-override: 97%;
  descent-override: 30.5%;
  line-gap-override: 0%;
  unicode-range: U+0000-00FF, U+0100-02BA, U+02BB-02BC, U+02BD-02C5, U+02C6, U+02C7-02CC, U+02CE-02D7,
    U+02DA, U+02DC, U+02DD-02FF, U+0301, U+0304, U+0308, U+0329, U+0400-045F, U+0460-052F,
    U+0490-0491, U+04B0-04B1, U+0131, U+0152-0153, U+1C80-1C8A, U+1D00-1DBF, U+1E00-1E9F,
    U+1EF2-1EFF, U+2000-206F, U+20A0-20AB, U+20AC, U+20AD-20C0, U+20B4, U+2113, U+2116, U+2122,
    U+2191, U+2193, U+2212, U+2215, U+2C60-2C7F, U+2DE0-2DFF, U+A640-A69F, U+A720-A7FF, U+FE2E-FE2F,
    U+FEFF, U+FFFD;
}
:root {
  /* Основной интерфейсный слой: проза, заголовки, названия, подписи, кнопки. */
  --font-display: "Onest", "Onest Fallback", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-ui: "Onest", "Onest Fallback", system-ui, -apple-system, "Segoe UI", sans-serif;
  /* Аналитический слой: ID, даты, числа, единицы, классификация и служебные метки. */
  --font-data: "Fira Sans Condensed", "PT Sans Narrow", "Arial Narrow", "Segoe UI", sans-serif;
  /* Палитра Flowline — единственный источник. Имена v7 ниже стали псевдонимами на неё,
     поэтому за пределами этого файла ничего менять не пришлось.

     Тем две, и основная — тёмная: язык отчёта СПЕКТР ночью. Решение автора 21 октября 2026, спека
     `docs/superpowers/specs/2026-10-21-dark-theme-design.md`. Здесь, в голом `:root`, стоит тёмная:
     основная тема получается без признака — на странице без скрипта, на аварийном экране и у сайта,
     который читает этот блок. Бумага отчёта (20 октября 2026) — блок `:root[data-theme="light"]`
     сразу ниже, и в нём только цвета, тени, затемнения и `color-scheme`. Каждая проверка палитры
     проходит обе темы (`tests/fixtures/themes.mjs`).

     Ночью карточка светлее страницы, приподнятое и активное — светлее ещё: высота слоя ночью
     светлеет. Холст (`--flow-carbon`) — самый глубокий тон. Цвета подобраны числом под те же правила,
     что держат бумагу. */
  --flow-carbon: #111214;
  --flow-field: #16171a;
  --flow-surface: #1d1f22;
  --flow-surface-raised: #25272b;
  --flow-surface-active: #2d3034;

  --bg: var(--flow-field);
  --panel: var(--flow-surface);
  --panel-2: var(--flow-surface-raised);
  --panel-3: var(--flow-surface-active);
  /* Волосяная линия отчёта — самый частый цвет его файла, 2565 обводок. Ночью она тише бумажной:
     светлая линия на тёмном режет сильнее тёмной на светлом. */
  --flow-line: #363a3e;
  --flow-line-strong: #565a60;

  --line: var(--flow-line);
  --line-strong: var(--flow-line-strong);
  /**
   * Три уровня серого, и нижний обязан читаться.
   *
   * `--quiet` был #5e686e и давал от 2.5:1 до 3.6:1 на подложках продукта при норме 4.5:1 для
   * текста такого размера. Хуже всего на пергаментной подложке выделения: 2.5:1. Читать это
   * трудно, а стоит там служебное — даты, идентификаторы, подписи под полями.
   *
   * Поднят до минимума, который проходит норму на всех шести подложках сразу: худший случай
   * 4.59:1. Вместе с ним поднят `--muted`, иначе два уровня сошлись бы в один.
   *
   * Ступени стали короче: было 1.96 между тихим и приглушённым, стало 1.39. Это цена нижнего
   * края — когда самый тёмный уровень обязан пройти 4.5:1, места под три уровня остаётся меньше.
   * Различать их теперь помогают ещё размер и насыщенность, а не один цвет.
   */
  /* Текст ночью — бумага отчёта. По самой светлой из ночных подложек (`--flow-surface-active`) тихий
     уровень держит 4.5:1 с запасом в десятые. */
  --text: #ecebe6;
  --muted: #bab9b3;
  --quiet: #9b9a95;
  /* Ржавчина отчёта ночью светлеет: красный стоит и текстом (подпись риска, строка ошибки, кнопка
     удаления), поэтому и заливка держит 4.5:1 на самой светлой подложке. */
  --red: #f07460;
  /**
   * Красный текстового веса.
   *
   * Тот же приём, что у доказательства: заливка на холсте одна, текст на панели другая. Ночью
   * текстовый вес светлее заливки, на бумаге темнее.
   */
  --red-text: #f48a76;
  --red-soft: rgba(240, 116, 96, 0.14);
  /* «На проверке» и «проверено». Заливка «проверено» ночью средней светлоты намеренно: кольцо фокуса
     ночью — цвет текста, и от «проверено» оно обязано отличаться не меньше чем на 3:1. */
  --flow-review: #d39b2e;
  --flow-verified: #3a9257;
  /* Кобальт доказательства. У «Организации» этот цвет. */
  --flow-evidence: #5a82ea;
  --flow-alert: #e0568f;
  /* Доказательство в двух весах: заливка на холсте и читаемый текст на панели. */
  --flow-evidence-text: #9db7ff;
  /* Сигнал тоже в двух весах, по тому же образцу: `--flow-alert` — точка и кромка, этот — текст. */
  --flow-alert-text: #f593b8;
  /* «Проверено» и «на проверке» в двух весах с 21 октября 2026: словами они стоят на подкраске своего
     же цвета, и заливка там не читается. Считан текстовый вес по самой плотной подкраске, 22% заливки
     над самой светлой землёй темы. Держит это `tests/palette-contrast.test.mjs` в обеих темах. */
  --flow-verified-text: #86d69e;
  --flow-review-text: #e8bd62;
  /**
   * Язык отчёта, которого у тёмной палитры не было.
   *
   * `--accent` — кобальт отчёта: номера разделов «2.1», метки «СХЕМА 2», линейка под шапкой. Это текст и
   * линия, а не заливка кнопки, и красным он не становится никогда.
   *
   * Плашка — шапка таблицы, активный раздел, главный материал, уведомление. Это самый сильный контраст
   * листа: на бумаге чернила на бумаге, ночью бумага на чернилах. Чернила на плашке в трёх весах:
   * основной, приглушённый и кобальт. Каждый держит порог текста в обеих темах — `tests/paper-ground.test.mjs`.
   */
  --accent: #86a2ff;
  --accent-tint: #1f2741;
  --ink-panel: #e9e7e1;
  --on-ink: #1c1e21;
  --on-ink-muted: #45484b;
  --on-ink-accent: #2450d6;

  /* Приглушённый текст на записи холста. Своё имя у него осталось с тех пор, как значения были вписаны
     числом в двух файлах сразу — `styles.css` и `graph.css`, — то есть разошлись бы первой же правкой.
     Зелёный отлив, намеренный до 22 сентября 2026, снят вместе с остальной землёй. */
  /* Высота слоя. Тринадцать значений на тринадцать мест — это не шкала, а «что подошло здесь»:
     0 10px 30px и 0 12px 30px стояли рядом, 0 24px 60px и 0 22px 54px тоже. Уровней на деле
     четыре, и каждый отвечает на вопрос «над чем это висит».
     По контракту тень положена только плавающим слоям; статичная карточка остаётся плоской. */
  /* Слои приложения. Это не сжатие числа значений, а порядок, названный вслух: полос столько,
     сколько вещей действительно обязаны стоять друг над другом. Смысл в том, что новая панель
     выбирает полосу, а не придумывает 117.

     Числа оставлены прежними намеренно — тогда перестановка провабельно ничего не сдвинула, кроме
     одного места, ради которого всё и делалось.

     У холста своя шкала (`--flow-layer-*`): он сам стоит на полосе и внутри себя заводит
     отдельный контекст наложения, поэтому его полосы наружу не спорят. */
  --layer-canvas: 9;
  --layer-raised: 10;
  --layer-chrome: 50;
  --layer-overlay: 60;
  --layer-palette: 100;
  --layer-modal: 110;
  --layer-popover: 115;
  --layer-menu: 118;
  --layer-scrim: 125;
  --layer-drawer: 130;
  /* Было 120, то есть ниже затемнения выдвижной панели: продукт говорил и не мог показать. */
  --layer-toast: 140;
  --layer-presentation: 200;

  /* Ночью тень плотнее: тёмная земля прячет лёгкую. На бумаге она вдвое легче. */
  --shadow-raised: 0 12px 30px rgba(0, 0, 0, .28);
  --shadow-floating: 0 22px 56px rgba(0, 0, 0, .46);
  --shadow-modal: 0 28px 90px rgba(0, 0, 0, .56);
  /* Выдвижная панель светит вбок по своей природе, а не вниз: это тот же уровень, другая геометрия. */
  --shadow-drawer: -30px 0 80px rgba(0, 0, 0, .42);
  /*
   * Затемнение под слоем — две роли: под окном и под выдвижной панелью. До 30 сентября 2026 подложек
   * было пять, а рецептов четыре; палитра, справочник и диалог кластера — такие же окна.
   */
  --scrim-modal: rgba(0, 0, 0, .6);
  --scrim-modal-blur: 4px;
  --scrim-drawer: rgba(0, 0, 0, .36);
  --scrim-drawer-blur: 2px;

  --flow-record-label: #a8a7a2;
  --flow-record-quiet: var(--quiet);
  /* Линия связи в покое. Это не текст, и порог у неё другой — 3 на холсте. */
  --flow-relation: #878b90;

  --amber: var(--flow-review);
  --green: var(--flow-verified);
  --cyan: var(--flow-evidence-text);
  --blue: var(--flow-evidence);
  /* Категориальные цвета схемы «по типу». Не семантические роли: они значат «этот тип записи»
     и только когда выбрана эта схема. Остальные четыре типа берут цвета существующих ролей —
     схемы взаимоисключающи, и легенда всегда называет активную. Тона одни в обеих темах с точностью
     до нескольких градусов: семь цветов развести на 30° можно только так. Ночью все семь светлее,
     на бумаге темнее, и в обеих держат 3:1 на карточке. */
  --type-person: #b07ce3;
  --type-channel: #3bb2bb;
  --type-event: #8fbd3c;

  /**
   * Выделение — кобальт, и токен назван по роли.
   *
   * До 22 сентября 2026 выделение было пергаментом, потом бледным холодным синим `#c3cdea`. В обеих
   * темах выделение лежит между землёй и кольцом фокуса: только так оно держит 3:1 на всех пяти
   * подложках и 3:1 против кольца.
   */
  --flow-selection: #5b7cf0;
  /**
   * Кольцо фокуса — цвет текста темы: ночью бумага, на бумаге чернила. Фиолетовый `#6e5ae6` не сходится
   * ни там, ни там, и это посчитано: кольцо обязано отличаться на 3:1 и от земли, и от выделения, и от
   * «проверено». Кольцо стоит с отступом, поэтому у плашки оно лежит на земле, а не на плашке.
   */
  --flow-focus: #ecebe6;
  --nav-active: #e9e7e1;
  /* Боковая панель, шапка и лента этапов — та же земля, что страница: обвязка и холст читаются
     одним листом, разделённым волосяными линиями. */
  --chrome: #16171a;
  --scrollbar-track: var(--flow-field);
  --scrollbar-thumb: #3a3d42;
  --scrollbar-thumb-hover: #575b61;
  /**
   * Скругления — два токена и ничего больше.
   *
   * Их и раньше было два, но пользовались ими шесть правил из ста пятидесяти трёх: остальные
   * писали число прямо в правиле, и значений набралось девять — 3, 4, 5, 6, 7, 8, 9, 10 и 12
   * пикселей. Девять радиусов это не система, а «что подошло вот здесь», и скруглённый
   * прямоугольник со средним радиусом — самый частый шаблон, какой бывает.
   *
   * С 8 сентября 2026 углы были прямыми. 20 октября 2026 скругления вернулись вместе с бумагой отчёта:
   * у него ячейки около трёх пикселей, плашки около шести, бегущая шапка и номера этапов — таблетки и
   * круги. Вернуть прямые углы — снова эти две строки. Круг и таблетка остаются формой, а не размером,
   * и токенов не берут.
   */
  --radius-sm: 3px;
  --radius: 6px;

  /**
   * Палитра бумаги — вторая палитра продукта, и это решение, а не недосмотр.
   *
   * Документ печатается на белой бумаге в обеих темах. Светлая тема продукта тоже бумага, но другая:
   * тёплая бумага отчёта, с плашками и кобальтом. Лист вывода остаётся белым и своими чернилами — это
   * вид документа, а не продукта. Приставка `--paper-` и есть граница между палитрами, и светлая тема
   * эти токены не переопределяет.
   *
   * Роли четыре: сама бумага, чернила трёх весов, линии двух весов и заливка блока кода. Контраст
   * на бумаге измерен: 17.87 у основного текста, 7.77 у приглушённого, 4.78 у самого слабого —
   * последний идёт только на подпись рода записи и стоит у самого порога 4.5.
   */
  /**
   * Приглушённый текст на панелях: у него появилось имя и порог.
   *
   * Имя завели, когда панель, хром и поля были подкрашены зелёным ради холста графа и общий
   * `--quiet` читался там чужим. Раньше вместо него стояли шесть значений, подобранных на месте, и **четыре
   * из них не проходили порог контраста**: порядковый номер вложения 3.75, строка «нет
   * доказательства» у связи 3.37, неактивная вкладка инспектора 3.75, подсказка поля поиска 3.41.
   *
   * Значение выбрано по фону, на котором текст читается хуже всего. На зелёной земле это был
   * `#82998b`, на графите `#93939b`. На бумаге своего оттенка у поверхностей нет, и этот вес совпал с
   * общим тихим: 4.68:1 на самой тёмной подложке. Имя оставлено, потому что его читают восемь мест.
   */
  --flow-panel-quiet: var(--quiet);
  /**
   * Порядковый номер вложения и шага истории — метка, а не подпись, и она тише соседней строки
   * намеренно. Имя дано по хрому, а лежит она и на плотной подложке списка вложений: на бумаге первый
   * выбор `#6a6d70` давал там 4.36:1, и это поймал браузерный сценарий контраста. Ночью метка совпадает
   * с тихим уровнем почти до знака: ниже 4.5:1 на светлой ночной подложке её не опустить.
   */
  --flow-chrome-faint: #9a9994;
  --paper: #fff;
  --paper-ink: #14181a;
  --paper-ink-quiet: #4a5459;
  --paper-ink-faint: #6b7478;
  --paper-line: #c9d1d4;
  --paper-line-soft: #d5dbde;
  --paper-fill: #f2f4f5;
  /**
   * Шкала размеров, и у неё есть нижний край.
   *
   * Размеров было девятнадцать, и сто девяносто два объявления стояли ниже 11px: пятнадцать
   * правил на 7px, восемьдесят пять на 8px, восемьдесят девять на 9px и пятьдесят на 10px.
   * Семипиксельный текст в инструменте, который читают весь день, — это не плотность, а
   * нечитаемость; дизайн-аудит называл это семнадцатью замечаниями подряд.
   *
   * Ниже 11px не опускается ничего. Четыре уровня «очень мелкого» сведены в один: различать их
   * глазом всё равно было нельзя, а разницу там несут цвет, насыщенность и регистр, и они
   * остались. У заголовков своя шкала, она сразу ниже.
   */
  --fs-meta: 11px;
  --fs-body: 12px;
  --fs-strong: 13px;
  --fs-lead: 15px;
  /**
   * Заголовки и крупные числа — четыре ступени через четыре пикселя. До 30 сентября 2026 размеров было
   * девять: 17, 18, 19 и 20 рядом, 22 и 23, 26 и 27, и разницу в пиксель между ними никто не выбирал.
   * Соседи слиты к ближайшей ступени, самый большой сдвиг — 2px у заголовка диалога кластера (20 → 22,
   * как у остальных окон).
   */
  --fs-title: 18px;
  --fs-heading: 22px;
  --fs-headline: 26px;
  --fs-display: 30px;
  /**
   * Лесенка отступов — восемь ступеней, и других расстояний в продукте нет.
   *
   * До неё была перепись: 1057 значений, 41 разное число, и все целые от 2 до 18 заняты. Не шкала,
   * а сплошной ряд, и разницу в нём никто не выбирал — видно по одному объявлению,
   * `padding: 9px 10px` у пункта списка пространств.
   *
   * Ступени выбраны из самих данных, а не из общего правила: мелкий шаг внизу продукту нужен —
   * двойка держит зазоры между отметками проверенности, шестёрка ставит подпись рядом со
   * значением, и обе стоят в десятках мест. Наверху шаг редеет, потому что там расстояния и так
   * различаются вдвое.
   *
   * Выбрано из четырёх вариантов, показанных на живом разделе; сравнение — в `CLAUDE.md`.
   */
  --space-2xs: 2px;
  --space-xs: 4px;
  --space-sm: 6px;
  --space-md: 8px;
  --space-lg: 12px;
  --space-xl: 16px;
  --space-2xl: 24px;
  --space-3xl: 34px;
  --sidebar: 236px;
  /**
   * Четыре порога ширины, и каждый назван тем, что при нём ломается.
   *
   * Решение автора 19 сентября 2026: пороги объявляются темой, чтобы оформление компонента — включая
   * узкие экраны — жило рядом с его разметкой. Имя говорит про вёрстку, а не про устройство:
   * устройства меняются, а точка, где перестаёт помещаться конкретная раскладка, свойство этой
   * раскладки.
   *
   * **Числа здесь и в медиазапросах ниже — одни и те же, и по-другому нельзя.** Медиазапрос не читает
   * `var()`: `@media (max-width: var(--bp-phone))` не работает вовсе. Поэтому значение объявлено
   * токеном для утилит и повторено числом в правилах, а связать их может только проверка —
   * `tests/breakpoint-scale.test.mjs` требует, чтобы ни один медиазапрос ширины в этом файле и в
   * `graph-react/graph.css` не пользовался числом мимо этих четырёх.
   *
   * Пятого порога быть не должно: до появления шкал в продукте так же подбирали радиусы, размеры и
   * отступы. **У холста до 1 октября 2026 был свой 720px**, три блока в `graph-react/graph.css`.
   * Разницу в 40 пикселей с `--bp-shell-stack` никто не выбирал, и автор свёл их: Хронология, «График»
   * и «Анализ» сужаются вместе со страницей, вариантом `narrow-shell-stack` в своих компонентах.
   */
  --bp-shell-rail: 1080px;
  --bp-toolbar-wrap: 900px;
  --bp-shell-stack: 760px;
  --bp-fields-stack: 520px;
  /**
   * Подложка карты в теме. Плитки OpenStreetMap нарисованы для белого листа: ночью их выворачивают с
   * поворотом тона на 180° (вода снова синяя, лес зелёный), на бумаге они стоят как нарисованы. Снимок
   * — фотография местности, и выворачивать его нельзя ни в одной теме, он только приглушается.
   * Приглушение нужно в обеих: маркеры обязаны быть ярче местности.
   */
  --map-tiles-drawn-filter: invert(1) hue-rotate(180deg) brightness(0.85) contrast(0.95) saturate(0.55);
  --map-tiles-drawn-opacity: 0.72;
  --map-tiles-photo-filter: brightness(0.82) contrast(1.05) saturate(0.8);
  --map-tiles-photo-opacity: 0.9;
  color-scheme: dark;
}

:root {
  /**
   * Чернила теплые, а не белые. Правка 10 сентября 2026 по образцу, присланному автором.
   *
   * Причина не во вкусе. Чистый белый на холодном сине-черном — умолчание, положенное поверх
   * умолчания: сочетание, которое читатель видел тысячу раз и потому не видит вовсе. Кремовый на
   * нейтральном почти-черном читается как печать на бумаге, а не как экран, и дает странице
   * характер, которого не даст ни один добавленный элемент.
   *
   * Красный остался тем же — цветом риска и единственным акцентом. Теплые чернила его не заменяют
   * и с ним не спорят: он по-прежнему один на полосу.
   */
  /**
   * С 20 октября 2026 палитра взята у организационной модели центра: графит вместо теплого
   * почти-черного, холодные серые вместо кремовых. Документ набран графитом по серой бумаге, и
   * сайт, перерисованный под него, говорит тем же тоном (спека 2026-10-20-centre-for-advanced-studies-design.md).
   */
  --site-bg: #141517;
  --site-panel: #1b1d1f;
  --site-panel-hover: #232528;
  --site-line: #2c2f32;
  --site-line-strong: #4b4f53;
  --site-head: #ececea;
  --site-body: #b6b9bc;
  --site-muted: #8d9195;
  --site-accent: var(--red);
  /**
   * Синий документа: номера разделов, шагов и пунктов, маркеры перечней. Это цвет порядка, а не
   * внимания: красный остается риску и шлюзам, синий говорит «здесь счет».
   */
  --site-blue: #8ea7ee;
  /**
   * Обложка: графитовая панель, на которой в документе стоит титул. На сайте так выглядит первый
   * экран главной. Панель темная в обеих темах, поэтому ее чернила свои, а не листа.
   */
  --site-cover: #1c1e20;
  --site-cover-line: #383b3f;
  --site-cover-head: #eeeeec;
  --site-cover-body: #babdc0;
  --site-cover-muted: #959a9e;
  --site-cover-blue: #8ea7ee;
  /**
   * Бумага и чернила. Лежат здесь, а не в правиле печати, потому что цвет сайта живёт в одном
   * месте: вписанный прямо в правило, он через месяц утянул бы туда половину палитры. При печати
   * эти три подставляются вместо экранных, и лист выходит белым без единой правки в остальном CSS.
   */
  --site-paper: #ffffff;
  --site-ink: #111111;
  --site-ink-quiet: #555555;
  --site-rule: #cccccc;
  /**
   * Светлая палитра. Значения лежат здесь, рядом с тёмными, а применяются ниже двумя правилами.
   *
   * Причина такая: выбор темы читателя приходит с двух сторон — от настройки системы и от кнопки
   * в шапке, — и селекторы у них разные. Написать девять цветов дважды значило бы завести вторую
   * палитру, которая разойдётся с первой на первой же правке. Здесь они названы один раз, а ниже
   * стоит только сопоставление имён.
   */
  --l-bg: #f2f2ef;
  --l-panel: #e5e5e1;
  --l-panel-hover: #dcdcd8;
  --l-line: #d1d1cc;
  --l-line-strong: #999a95;
  --l-head: #18191b;
  --l-body: #2c2e31;
  --l-muted: #575b5f;
  --l-accent: #a8321f;
  /**
   * Синий листа исходный. 5 ноября 2026 его несколько часов заменяли «Чернилами» (#3f5b8a), и автор
   * вернул этот: «полоски верни к тому синему, какие были изначально». Им набраны номера и полоса
   * карточек.
   */
  --l-blue: #2b55c2;
  /* Обложка темная и на светлом: значения те же, что у темной палитры, имена свои. */
  --l-cover: #1c1e20;
  --l-cover-line: #383b3f;
  --l-cover-head: #eeeeec;
  --l-cover-body: #babdc0;
  --l-cover-muted: #959a9e;
  --l-cover-blue: #8ea7ee;
  /**
   * Стол в светлой теме. Досье на столе, спека 2026-09-26: лист светлый в обеих темах, поэтому в
   * светлой теме темнеть приходится столу. Стол выведен из линовки листа, а не заведен новым цветом:
   * у темной палитры такого цвета нет, и третий оттенок серого без пары был бы цветом ниоткуда.
   * Приглушенный текст на столе свой: обычный приглушенный на сером не проходит порог контраста.
   */
  --l-desk: color-mix(in srgb, var(--l-line) 80%, var(--l-line-strong));
  --l-desk-muted: color-mix(in srgb, var(--l-muted) 70%, var(--l-head));
  /* Красный на сером столе темнее, чем на бумаге: обычный дает контраст 3,56 при пороге 4,5. */
  --l-desk-accent: color-mix(in srgb, var(--l-accent) 80%, var(--l-head));
  /**
   * ## Светлая тема живёт ниже, в своём медиазапросе
   *
   * 11 сентября 2026 здесь было записано, что светлой темы нет, и названо, чего она стоит: не
   * инверсия трёх значений, а вторая палитра с пересчётом контраста всех пар. 12 сентября автор
   * попросил её сделать, и она сделана именно так — полной второй палитрой.
   *
   * **По умолчанию тема идёт за настройкой системы** через prefers-color-scheme: читатель,
   * у которого выбрано светлое, получает светлое, остальные — прежний лист. С 12 сентября 2026
   * рядом стоит переключатель в шапке: до него тегов script на сайте было ноль, и это стояло
   * обещанием, но выбор темы не переживает перехода между страницами без хранилища, а хранилище —
   * без скрипта. Довод и цена записаны в lib/page.mjs, там же лежит единственный скрипт сайта.
   * Обратных кавычек здесь нет намеренно: этот комментарий стоит внутри шаблонной строки,
   * и кавычка закрыла бы её посреди таблицы стилей.
   */
  /**
   * Кривая движения. Одна на весь сайт, как и цвета.
   *
   * Приём снят с сайта Anduril 10 сентября 2026: у них интерфейс движется 200–350 мс и по сильной
   * кривой замедления, а не по ровной. Разница слышна не в длительности, а именно в кривой: движение
   * начинается быстро и мягко останавливается, и перестает читаться механическим. Наш потолок в
   * 300 мс не трогается, его держит проверка.
   */
  --site-ease: cubic-bezier(.16, 1, .3, 1);
  /**
   * Длительности. Две, и обе применяются — придумывать третью «про запас» незачем.
   *
   * Цвет и движение разведены намеренно: смена цвета читается мгновенно, а перемещение глаз
   * отслеживает, и на той же длительности оно кажется дёрганым. Отсюда правило — **движение
   * медленнее окраски**, но обе под потолком в 300 мс, который держит проверка.
   */
  --t-colour: 160ms;
  --t-move: 220ms;
  /**
   * Поле от края окна. Одно на шапку, полосы и подвал — иначе они разъедутся по вертикали.
   *
   * Растёт с окном и упирается в 96px: на большом мониторе поле в 24 пикселя читается как обрезка,
   * а на телефоне поле в 96 не оставляет места тексту.
   */
  --edge: clamp(20px, 4.4vw, 96px);
  --gutter: 92px;
  /**
   * Отступ содержимого от края листа: колонка полей плюс промежуток.
   *
   * Заведён 11 сентября 2026, когда обмер показал, что заголовки стоят на пяти разных вертикалях:
   * имя страницы на 63 на главной и на 341 в публикациях, заголовки разделов на 187, 305, 341 и
   * 515. Шапка, подвал и знак при этом всегда начинались на 63. Для листа, который весь держится
   * на разлиновке, гуляющий левый край — худшее, что может случиться: страница читается как
   * недоделанная, хотя каждая отдельная раскладка была верной.
   *
   * Правило теперь одно и произносится вслух: **на краю листа стоят обвязка, имя страницы и все
   * линейки; на отступе — всё остальное**. В колонке полей между ними живут номера полос, даты
   * записей и роли — то, что подписывает строку, а не входит в неё.
   */
  --indent: calc(var(--gutter) + var(--s-32));
  /**
   * Шкала отступов. Шаг в четыре пикселя, ступени названы своим значением.
   *
   * Снята с кода, а не придумана: чаще всего в файле уже стояли 8, 12, 16, 20 и 24. Всё, что лежало
   * между ступенями, подтянуто к ближайшей, при равном расстоянии — к большей; ни одно место не
   * сдвинулось больше чем на два пикселя. До этого разных значений было двадцать с лишним, и отступ
   * в каждом месте выбирался заново — на глаз и без возможности рассуждать о раскладке целиком.
   *
   * Имя ступени — её размер, а не порядковый номер: правило читается без обращения к этому блоку,
   * и не нужно помнить, что скрыто за третьим шагом. Два пикселя и меньше остаются числом: это
   * волосяная подгонка, а не отступ.
   */
  --s-4: 4px;
  --s-8: 8px;
  --s-12: 12px;
  --s-16: 16px;
  --s-20: 20px;
  --s-24: 24px;
  --s-32: 32px;
  --s-40: 40px;
  --s-48: 48px;
  --s-64: 64px;
  --s-80: 80px;
  /**
   * Шкала размеров. Пять ступеней и три заголовка — всё, что есть у сайта.
   *
   * Заведена 11 сентября 2026. До неё в файле стояло одиннадцать разных размеров: 11, 12, 13, 14,
   * 15, 16, 17, 18, 19, 20 и 21. Четыре из них встречались по одному разу, а 12, 13 и 14 стояли
   * рядом и на глаз не различались вовсе — то есть выбор размера в каждом месте делался заново и
   * ничего не означал. Ступени разведены примерно в 1.2 раза: разницу видно, и она значит разное.
   *
   * Имя ступени говорит роль, а не размер, — в отличие от шкалы отступов выше. Отступ переставляют
   * на соседний, когда тесно; размер меняют, когда меняется роль строки, и имя должно спрашивать
   * именно об этом.
   *
   * Ниже одиннадцати пикселей на сайте нет ничего — это порог аудита дизайна у продукта, и здесь
   * он держится с первого дня. Заголовки считаются от ширины окна и живут в своих правилах: у них
   * три ступени, и промежуточной между 34 и 70 не нужно.
   *
   * **Приставка в имени не для красоты.** У продукта своя шкала с теми же короткими именами, и
   * его токены сайт читает целиком. Первая версия называлась просто --fs-body, и сайт молча
   * переписал продукту два размера из четырёх; поймала это проверка, а не глаз.
   */
  --site-fs-mark: 11px;
  --site-fs-note: 13px;
  --site-fs-body: 15px;
  --site-fs-lead: 17px;
  --site-fs-sub: 21px;
  /**
   * Скруглений на сайте нет нигде, включая контролы.
   *
   * До 12 сентября 2026 здесь стояла таблетка — «одна мягкая форма посреди прямой сетки». Автор
   * показал на снимке переключатель языка и сказал: блок должен быть прямоугольным. Он прав, и
   * довод сильнее прежнего: главный призыв на сайте и так прямоугольный, а во всех четырнадцати
   * присланных макетах прямоугольна каждая кнопка. Таблетка была единственной круглой формой на
   * листе и читалась не «сюда нажимают», а «эта деталь из другого набора».
   *
   * **Прежний комментарий врал про проверку.** Здесь было написано, что таблетку держит браузерный
   * сценарий. Такого сценария нет и не было — запись про проверку, которой не существует, хуже
   * отсутствия записи: на неё опираются.
   *
   * Токен оставлен, а не удалён: он стоит в четырёх правилах, и вернуть скругление — это одна
   * строка здесь, а не поиск по файлу.
   */
  --site-control-radius: 0;
  /**
   * Единая типографическая система из двух семейств.
   *
   * Onest — основной голос Galatine: проза, заголовки и навигация. Fira Sans Condensed —
   * аналитический слой: коды, даты, служебные подписи, таблицы и последовательность метода.
   * Контраст создаётся не третьей гарнитурой, а разницей ширины, размера и веса.
   *
   * «Onest Fallback» - замена из продукта на время, пока грузится Onest: строки той же высоты, и
   * крупный заголовок не прыгает. Правило @font-face для нее берется из продукта
   * (readFontFallbacks), с 2 октября 2026.
   */
  --site-display: "Onest", "Onest Fallback", system-ui, -apple-system, "Segoe UI", sans-serif;
}

/**
 * Светлая тема: вторая палитра целиком, а не инверсия трёх значений.
 *
 * Сделана 12 сентября 2026 по просьбе автора, в тот же день, что и переключатель в шапке. Палитры
 * две, и третьей («как в системе») в переключателе нет намеренно: системная настройка и есть
 * умолчание, пока читатель не нажал кнопку, — отдельный пункт назвал бы состояние, которое и так
 * действует само.
 *
 * **Бумага тёплая, а не белая** — по той же причине, по какой чернила кремовые, а не белые: чистый
 * белый под чистым чёрным это умолчание поверх умолчания. Цвета подобраны так, чтобы каждая пара
 * прошла тот же порог, что и на тёмном; считает это браузерный сценарий, и он обходит обе темы.
 *
 * **Снимки сервисов остаются тёмными, и это не дефект.** Они сняты с тёмного холста продукта, и
 * перекрашивать их нельзя — это его настоящий вид. На светлом листе кадр стоит в светлой рамке и
 * читается врезкой; ровно так устроены и макеты, которые прислал автор.
 *
 * **Красный здесь свой.** Красный продукта светлый (он нарисован для тёмного холста) и на бумаге
 * даёт 2.95 к 1 — ниже порога для текста. Поэтому у знака внимания на светлом свой тон; токен
 * продукта при этом не переписан, меняется только собственный --site-accent.
 */
/**
 * Светлая тема приходит с двух сторон, и у них разный вес.
 *
 * Настройка системы действует, пока читатель не выбрал сам. Выбор кнопкой сильнее — он назван
 * прямо и переживает переход между страницами.
 *
 * Состояний ровно три, и третье важно: нет атрибута — значит «как в системе». Кнопка ставит
 * «светлую» или «тёмную», и обратно в «как в системе» вернуться нечем. Это осознанный предел:
 * третье положение переключателя объяснить на экране дороже, чем оно стоит.
 */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    color-scheme: light;
    --site-bg: var(--l-desk);
    --site-panel: var(--l-panel);
    --site-panel-hover: var(--l-panel-hover);
    --site-line: var(--l-line);
    --site-line-strong: var(--l-line-strong);
    --site-head: var(--l-head);
    --site-body: var(--l-body);
    --site-muted: var(--l-desk-muted);
    --site-accent: var(--l-desk-accent);
    --site-blue: var(--l-blue);
    --site-cover: var(--l-cover);
    --site-cover-line: var(--l-cover-line);
    --site-cover-head: var(--l-cover-head);
    --site-cover-body: var(--l-cover-body);
    --site-cover-muted: var(--l-cover-muted);
    --site-cover-blue: var(--l-cover-blue);
  }
}
:root[data-theme="light"] {
  color-scheme: light;
  --site-bg: var(--l-desk);
  --site-panel: var(--l-panel);
  --site-panel-hover: var(--l-panel-hover);
  --site-line: var(--l-line);
  --site-line-strong: var(--l-line-strong);
  --site-head: var(--l-head);
  --site-body: var(--l-body);
  --site-muted: var(--l-desk-muted);
  --site-accent: var(--l-desk-accent);  --site-blue: var(--l-blue);
  --site-cover: var(--l-cover);
  --site-cover-line: var(--l-cover-line);
  --site-cover-head: var(--l-cover-head);
  --site-cover-body: var(--l-cover-body);
  --site-cover-muted: var(--l-cover-muted);
  --site-cover-blue: var(--l-cover-blue);
}

* { box-sizing: border-box; }
/* Движение объявляется только под no-preference, а не отменяется под reduce: отмена по одному
   однажды забылась у стрелки призыва. Держит тест «motion runs only for readers…». */
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }
/**
 * Место под закрепленной шапкой. Ноль, пока шапка не закреплена; в правиле шапки ниже оно получает
 * ее высоту. Переход по ссылке на раздел ставит заголовок под шапку, а не за нее: до 26 сентября
 * 2026 пункт оглавления «Метода» уводил заголовок раздела под шапку, а само оглавление липло на 20
 * пикселях от верха и тоже пряталось под ней верхней строкой.
 */
:root { --head-room: 0px; }
html { scroll-padding-top: var(--head-room); }
body {
  margin: 0;
  padding: 0;
  background: var(--site-bg);
  color: var(--site-body);
  font-family: var(--font-ui);
  font-size: var(--site-fs-body);
  line-height: 1.6;
}
a { color: var(--site-head); text-decoration-color: var(--site-line-strong); text-underline-offset: 3px; }
a:hover { text-decoration-color: var(--site-accent); }
a:focus-visible, button:focus-visible { outline: 2px solid var(--flow-focus); outline-offset: 2px; }

/**
 * Полосы идут во всю ширину окна, а не в колонке посередине.
 *
 * До 11 сентября 2026 всё лежало в колонке 1120px: страница читалась как документ, положенный на
 * середину экрана, и на широком мониторе половина его пустовала по краям. Приём из разбора сайта
 * Anduril: раздел занимает окно целиком, а внутри стоит сетка — линейка полосы идёт от края до
 * края, и у страницы появляется край, вдоль которого идёт глаз.
 *
 * Ширину теперь ограничивает не контейнер, а **мера строки**: проза не длиннее 72 знаков, сколько
 * бы ни было окна. Ограничивать надо именно текст — таблица, снимок и указатель от ширины только
 * выигрывают, а строка в 200 знаков нечитаема на любом мониторе.
 */
main, .site-head, .sub-nav, .site-foot { margin: 0 auto; width: 100%; max-width: 1680px; padding-inline: var(--edge); }
/**
 * Один лист и одна разлиновка на все страницы.
 *
 * Замечание автора 11 сентября 2026 — «все разделы идут слева, читается тяжело» — сначала было
 * закрыто тем, что колонка чтения встала посередине. Меру строки это починило, а левый край
 * разъехало: у половины страниц заголовок оказался на 341, у другой на 63, а подзаголовки встали
 * на четырёх разных вертикалях. Обмер 11 сентября это и показал.
 *
 * Мера строки и положение колонки — разные вопросы, и второй решается иначе. Читать тяжело от
 * длинной строки, а не от того, что колонка стоит слева: потолок в 72 знака держится всюду и без
 * середины. Зато середина ломает то, ради чего лист и сделан, — общий край, вдоль которого идёт
 * глаз, и колонку полей, где стоят номера и даты.
 *
 * Поэтому раскладка теперь одна: **обвязка, имя страницы и линейки — на краю листа; всё
 * остальное — на отступе**, а проза внутри не длиннее 72 знаков. Так уже были устроены главная
 * и «Услуги»; остальные страницы приведены к ним, а не наоборот.
 */
.band p, .band .lead, .hero .lead, .hero-list li, .page-head + p { max-width: 64ch; }
/* Списки прозы держат ту же меру, что абзац. Указатели и сетки — свои, они названы классом. */
.reading ul:not([class]), .reading ol:not([class]), .wide-body ul:not([class]), .wide-body ol:not([class]) { max-width: 64ch; }
/* Список в тексте стоит на шаге абзаца, 16 px. До 4 ноября 2026 он брал отступ браузера 1em, то есть
   15 px (аудит unslop). Вложенный список отступа снизу не дает. */
.reading ul:not([class]), .reading ol:not([class]), .wide-body ul:not([class]), .wide-body ol:not([class]) { margin: 0 0 var(--s-16); }
:is(.reading, .wide-body) li > :is(ul, ol):not([class]) { margin-bottom: 0; }
@media (min-width: 900px) {
  .reading, .wide-body { padding-left: var(--indent); }
  /* Титул страницы — ярлык, имя и ведущий абзац — стоит на краю листа вместе со знаком в шапке. */
  .reading > .page-head, .wide-body > .page-head,
  .reading > .page-head + p, .wide-body > .page-head + p { margin-left: calc(-1 * var(--indent)); }
  /* У раздела линейка идёт от края, а текст — с отступа: та же форма, что у полосы на главной. */
  .reading > h2, .wide-body > h2,
  .reading > .read-next, .wide-body > .read-next,
  .reading > section, .wide-body > section { margin-left: calc(-1 * var(--indent)); padding-left: var(--indent); }
  /* Колонку полей на странице с оглавлением занимает само оглавление, и второй раз её не берут. */
  .has-toc .reading, .has-toc .wide-body { padding-left: 0; }
  .has-toc .reading > .page-head, .has-toc .wide-body > .page-head,
  .has-toc .reading > .page-head + p { margin-left: 0; }
}
/**
 * Заголовок раздела в прозе получает линейку.
 *
 * Страницы из markdown — миссия, контакты, документы — состоят из заголовков и абзацев подряд, без
 * единой линейки: правило раздела до них не доходило, потому что разделов в разметке нет. Рядом с
 * главной, где полосы разделены линейками во всю ширину, это читалось как другой сайт.
 */
.reading > h2, .wide-body > h2 { margin-top: var(--s-48); padding-top: var(--s-32); border-top: 1px solid var(--site-line); }

/* Ярлык: моно, заглавными, с разрядкой. Так подписаны только данные — раздел, дата, роль. */
/**
 * Текст только для скринридера: в разметке есть, на экране нет. Нужен кнопке темы - видимое слово
 * называет тему, на которую она переключит, а скрытое добавляет само действие.
 */
.sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
/**
 * Ссылка к содержимому. Разбор дизайна 23 сентября 2026 насчитал восемь нажатий Tab до текста
 * страницы: знак, четыре раздела, два языка, тема. Ссылка стоит первой и видна, только пока на ней
 * фокус.
 */
.skip { position: absolute; left: var(--s-16); top: var(--s-8); z-index: 20; padding: var(--s-8) var(--s-12); background: var(--site-head); color: var(--site-bg); font-size: var(--site-fs-body); font-weight: 600; text-decoration: none; transform: translateY(-300%); }
.skip:focus { transform: none; }
.kicker, .band-label, .person-role, .toc-title, .post-meta, .figure-tag, .passport-fields dt {
  font-family: var(--font-data);
  font-size: var(--site-fs-mark);
  font-weight: 500;
  letter-spacing: .12em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
}
/**
 * Цифры шрифта данных одной ширины (tabular-nums), с 4 ноября 2026, аудит unslop. Без этого номера
 * G.0xx в списке публикаций были шириной от 25.7 до 28.6 px, и даты после них стояли неровно.
 */
.sheet-code, .person-step, .cycle li::before, .steps-row li::before, .columns.is-cards.is-numbered li::before { font-variant-numeric: tabular-nums; }

/* Шапка. Меню обычным шрифтом строчными: моно заглавными в пятом месте экрана было манерностью. */
.site-head { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s-12) var(--s-32); padding-block: var(--s-20); border-bottom: 1px solid var(--site-line); }
/**
 * Шапка остается на экране и сжимается при прокрутке.
 *
 * Приём из разбора сайта Anduril 10 сентября 2026: пока читатель наверху, шапка просторная; стоило
 * начать читать — она ужимается вдвое и перестает занимать место, но остается под рукой. Сделано на
 * чистом CSS, скриптов по-прежнему ноль: сжатие привязано к положению прокрутки тем же способом,
 * каким полосы ниже появляются в кадре. Первые 180 пикселей прокрутки — весь ход сжатия.
 *
 * Закрепленной шапке нужен фон, иначе текст просвечивал бы сквозь нее.
 *
 * **Закреплена она у всех, а сжимается только там, где можно двигаться,** — с 21 октября 2026.
 * До этого браузер без поддержки scroll() и читатель с отключенным движением получали шапку,
 * которая уезжает вместе со страницей: закрепление жило внутри того же условия, что и сжатие. Но
 * закрепление это не движение. Автор не видел закрепленной шапки вовсе: в Windows у него
 * выключены анимации, а Chrome передает это сайтам как просьбу о меньшем движении. Теперь в этих
 * двух случаях шапка закреплена и сразу сжата: высота та же, что после сжатия, поэтому место под
 * нее (--head-room) одно на все случаи.
 *
 * **Низкому окну шапка не закрепляется — с 16 сентября 2026.** Закреплённая шапка платит высотой
 * экрана, а не шириной, поэтому и условие про высоту. Телефон, повёрнутый боком, — это 375–430
 * пикселей в высоту, и даже сжатая шапка отнимала бы у текста восьмую часть. Порог 500 отделяет
 * такой экран от любого телефона в портрете (от 568) и от любого планшета.
 */
@media (min-height: 500px) {
  .site-head {
    position: sticky;
    top: 0;
    z-index: 5;
    background: var(--site-bg);
    padding-block: var(--s-8);
  }
  /**
   * Высота сжатой шапки, замер 26 сентября 2026 по ширинам от 380 до 1200 на двух языках. Уже 680
   * это одна строка со знаком и кнопкой меню, 61 пиксель. От 680 до 960 меню стоит в шапке и
   * переносится до трех строк, 134. От 960 до двух строк, 92; с 1060 одна строка, 57. Взят верх
   * каждого диапазона с запасом: на широком окне заголовок раздела встает ниже шапки, чем мог бы,
   * зато ни на одной ширине не уходит под нее. Проверяет это сценарий оглавления «Метода».
   */
  :root { --head-room: 72px; }
  @media (min-width: 680px) { :root { --head-room: 148px; } }
  @media (min-width: 960px) { :root { --head-room: 104px; } }
}
@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) and (min-height: 500px) {
    .site-head {
      animation: head-condense linear both;
      animation-timeline: scroll();
      animation-range: 0 180px;
    }
  }
}
@keyframes head-condense {
  from { padding-block: var(--s-20); }
  to { padding-block: var(--s-8); }
}
/**
 * Полоса прочтения (этап 5 карты сайта, 3 ноября 2026): 2 пикселя по нижней кромке шапки, растет
 * вместе с прокруткой страницы. Только на страницах для чтения без оглавления: где оглавление-линейка
 * есть, место в тексте уже показано, и полоса повторила бы ее. Только там, где шапка закреплена: на
 * низком экране она уезжает вместе со страницей, и полосу было бы не видно. Страница, которую некуда
 * листать, полосы не получает: прокрутки нет, и анимация не действует. Синий - цвет меток порядка.
 *
 * Это состояние, а не движение: полоса меняется только вместе с прокруткой читателя. Поэтому просьба
 * «меньше движения» ее не снимает, как маски таблиц и отметку в оглавлении.
 *
 * Список публикаций и страницы рубрик набраны тем же листом для чтения, но это списки: их не читают
 * подряд до конца. Узнаются они по строке рубрик над списком. До 3 ноября 2026 полоса стояла и там.
 */
@supports (animation-timeline: scroll()) {
  @media (min-height: 500px) {
    body.is-reading:not(:has(main.has-toc)):not(:has(main .rubric-nav)) .site-head::after { content: ""; position: absolute; right: 0; bottom: -1px; left: 0; height: 2px; background: var(--site-blue); transform: scaleX(0); transform-origin: left; animation: read-progress linear both; animation-timeline: scroll(root block); }
  }
}
@keyframes read-progress {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}
.mark { position: relative; display: inline-flex; align-items: center; white-space: nowrap; color: var(--site-head); text-decoration: none; flex: 0 0 auto; min-width: 0; }
/* Одна горизонтальная векторная версия на больших и компактная на узких экранах. */
.mark img { display: block; width: clamp(148px, 24vw, 176px); height: auto; object-fit: contain; }
.foot-mark .mark img { width: 148px; height: auto; }
/**
 * Приписка «lab» в нижнем правом углу знака (решение автора 3 ноября 2026, вместе с адресом
 * galatinelab.com; вид - вариант D из показанных): плашка цвета знака, внутри слово шрифтом Onest
 * цветом фона, сразу за концом слова GALATINE и под его нижней линией. В теме плашка меняется вместе
 * со знаком: белая на темной шапке, черная на светлой. 5 ноября 2026 плашка несколько часов была
 * синей, и автор вернул цвет знака.
 *
 * Слово в файле знака кончается у самого правого края картинки (1872 из 1894), поэтому плашка стоит в
 * общем ряду знака сразу за картинкой: место за знаком она занимает сама, и подпись в подвале на нее
 * не наезжает. По высоте ряд приписки делится так же, как знак: нижняя линия слова на 323 из 525,
 * плашка - во второй доле. Знак меняет размер, а доли остаются.
 */
.mark-lab { align-self: stretch; display: grid; grid-template-rows: minmax(0, 323fr) minmax(0, 202fr); margin-left: 1px; font-style: normal; }
.mark-lab b { grid-row: 2; align-self: start; margin-top: 2px; padding: 1px 4px 2px; background: var(--site-head); color: var(--site-bg); font-family: var(--site-display); font-size: var(--site-fs-mark); font-weight: 600; line-height: 1; letter-spacing: .01em; }
/**
 * Какой знак показать, решают те же правила, что красят лист.
 *
 * До 16 сентября 2026 выбирал медиазапрос картинки, и он видел только тему системы: читатель,
 * выбравший тему кнопкой, получал знак не для своего листа. Правило CSS видит выбор — атрибут на
 * корне, — поэтому четыре сочетания системы и выбора дают знак, совпадающий с листом. Порядок и
 * селекторы повторяют подпись кнопки темы выше: это один и тот же вопрос.
 */
.mark .for-light { display: none; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .mark .for-dark { display: none; }
  :root:not([data-theme="dark"]) .mark .for-light { display: block; }
}
:root[data-theme="light"] .mark .for-dark { display: none; }
:root[data-theme="light"] .mark .for-light { display: block; }
.site-head nav { display: flex; flex-wrap: wrap; gap: 2px; margin-left: auto; }
/* В правом углу шапки стоял слоган; с 12 сентября 2026 там призыв — правило у него общее с
   главным призывом страницы, ниже по файлу. Слоган остался в подвале, где и был. */
/**
 * Пункт меню — таблетка, а не подчеркнутое слово.
 *
 * Текущий раздел раньше помечался полосой снизу. Полоса читается, но она же еще одна линия среди
 * линий разметки; заливка формой контрола отделяет меню от текста и говорит «это переключатель».
 * Форма одна на все контролы страницы — та же, что у призыва.
 */
.site-head nav a { padding: var(--s-8) var(--s-12); border-radius: var(--site-control-radius); color: var(--site-body); font-size: var(--site-fs-body); font-weight: 500; text-decoration: none; transition: background-color var(--t-colour) var(--site-ease), color var(--t-colour) var(--site-ease); }
.site-head nav a:hover { color: var(--site-head); background: var(--site-panel); }
.site-head nav a[aria-current="page"] { color: var(--site-head); background: color-mix(in srgb, var(--site-head) 13%, transparent); }
/**
 * Переключатель языка — маленький блок из двух сокращений в общей рамке.
 *
 * Слово целиком («English») было длиннее любого пункта меню и весило больше, чем значит. Два знака
 * набраны моно: это код, а не проза, и правило продукта «моно значит запись» тут работает прямо.
 * Текущий язык залит, второй — ссылка; переключатель читается как один предмет, а не как ещё один
 * пункт меню.
 */
.lang-switch { display: inline-flex; align-items: center; margin-left: var(--s-12); padding: 2px; border: 1px solid var(--site-line); border-radius: var(--site-control-radius); font-family: var(--font-data); font-size: var(--site-fs-mark); letter-spacing: .06em; }
.lang-switch > * { padding: var(--s-4) var(--s-8); border-radius: var(--site-control-radius); line-height: 1; }
.lang-on { color: var(--site-head); background: color-mix(in srgb, var(--site-head) 13%, transparent); }
.site-head nav .lang { margin: 0; color: var(--site-muted); font-size: var(--site-fs-mark); font-weight: 500; }
.site-head nav .lang:hover { color: var(--site-head); background: var(--site-panel); }

/**
 * Переключатель темы: одна кнопка, названная тем, что будет после нажатия.
 *
 * Две ячейки, как у языка, здесь не подошли: слова «тёмная» и «светлая» вдвое длиннее сокращений
 * RU и EN, и шапка от них разъезжается. Кнопка, названная целью, короче и читается сразу.
 *
 * **Пока скрипт не отработал, кнопки нет вовсе.** Она появляется только после того, как он поставит
 * признак готовности на корень страницы. Причина прямая: без скрипта она не сделала бы ничего, а
 * контрол, который молча ничего не делает, хуже отсутствующего — это правило продукта, и здесь оно
 * работает так же. Читатель с выключенным JavaScript получает страницу целиком и тему по настройке
 * системы.
 */
.theme-toggle { display: none; align-items: center; margin-left: var(--s-8); padding: var(--s-4) var(--s-8); border: 1px solid var(--site-line); border-radius: var(--site-control-radius); background: none; color: var(--site-muted); font-family: var(--site-display); font-size: var(--site-fs-note); font-weight: 500; line-height: 1; cursor: pointer; transition: color var(--t-colour) var(--site-ease), border-color var(--t-colour) var(--site-ease); }
:root[data-theme-ready] .theme-toggle { display: inline-flex; }
.theme-toggle:hover { color: var(--site-head); border-color: var(--site-line-strong); }
/* Слово выбирает тот же селектор, что красит лист: скрипт надписи не трогает вовсе. */
.theme-toggle .to-dark { display: none; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .theme-toggle .to-light { display: none; }
  :root:not([data-theme="dark"]) .theme-toggle .to-dark { display: inline; }
}
:root[data-theme="light"] .theme-toggle .to-light { display: none; }
:root[data-theme="light"] .theme-toggle .to-dark { display: inline; }

/**
 * Поле поиска, с 4 ноября 2026. Решение автора: в шапке, на пустом месте между знаком и языком. Рамка и
 * прямой угол те же, что у переключателей рядом; фокус виден рамкой всей формы, а не одного поля.
 * Ширина задана числом, а не долей строки: меню в шапке переносится по ширине своих пунктов, и поле с
 * плавающей шириной уводило переключатель темы во вторую строку даже на 1440.
 *
 * Уже 960 пикселей поле в строку шапки не встает: от него остается кнопка с лупой. Она
 * отправляет пустой запрос, то есть открывает страницу поиска, и поле ждет там. На телефоне поле
 * целиком стоит первой строкой меню.
 */
.site-search { display: flex; align-items: stretch; flex: none; width: 240px; margin-right: var(--s-8); border: 1px solid var(--site-line); border-radius: var(--site-control-radius); background: none; transition: border-color var(--t-colour) var(--site-ease); }
.site-search:hover { border-color: var(--site-line-strong); }
.site-search:focus-within { border-color: var(--site-line-strong); outline: 2px solid var(--flow-focus); outline-offset: 2px; }
.site-search input { flex: 1; min-width: 0; padding: var(--s-4) var(--s-8); border: 0; background: none; color: var(--site-head); font-family: var(--site-display); font-size: var(--site-fs-note); line-height: 1.3; outline: none; }
.site-search input::placeholder { color: var(--site-muted); opacity: 1; }
.site-search button { display: grid; place-items: center; padding: 0 var(--s-8); border: 0; background: none; color: var(--site-muted); cursor: pointer; transition: color var(--t-colour) var(--site-ease); }
.site-search button:hover { color: var(--site-head); }
.site-search button:focus-visible { outline: none; }
@media (min-width: 680px) and (max-width: 959px) {
  .site-head .site-search { width: auto; }
  .site-head .site-search input { display: none; }
  .site-head .site-search button { padding: var(--s-4) var(--s-8); }
}
/* Страница поиска: та же форма во всю колонку текста, кегль не меньше 16 px - на меньшем iPhone
   увеличивает страницу при вводе. */
.site-search.is-page { width: 100%; max-width: 64ch; min-height: 48px; margin: 0 0 var(--s-16); }
.site-search.is-page input { padding: var(--s-8) var(--s-16); font-size: var(--site-fs-lead); }
.search-status { min-height: 1.5em; margin: 0 0 var(--s-16); color: var(--site-muted); font-size: var(--site-fs-note); }
.search-results { max-width: 64ch; margin: 0; padding: 0; list-style: none; border-top: 1px solid var(--site-line); }
.search-results li { margin: 0; padding: var(--s-20) 0; border-bottom: 1px solid var(--site-line); }
.search-results .search-path { margin: 0 0 var(--s-4); color: var(--site-muted); font-family: var(--font-data); font-size: var(--site-fs-mark); font-variant-numeric: tabular-nums; }
.search-results h2 { margin: 0 0 var(--s-8); font-size: var(--site-fs-sub); }
.search-results h2 a { color: var(--site-head); text-decoration: none; }
.search-results h2 a:hover { text-decoration: underline; text-decoration-color: var(--site-line-strong); text-underline-offset: 4px; }
.search-results p { margin: 0; }
.search-results mark { background: var(--site-panel-hover); color: var(--site-head); }

/**
 * Меню на узком экране — за кнопкой.
 *
 * До 16 сентября 2026 шапка телефона разваливалась на четыре ряда и, закреплённая, закрывала при
 * чтении от 23 до 41 процента экрана. Теперь на узком экране в ней одна строка: знак и кнопка
 * «Меню». Меню открывается листом сверху, и пункты в нём — строки высотой в палец.
 *
 * Лист — встроенный в HTML всплывающий слой, атрибут popover, а не скрипт. Браузер сам его
 * открывает и закрывает, сам закрывает по Escape и по нажатию мимо и сам сообщает скринридеру,
 * раскрыта ли кнопка. Скрипт на сайте по-прежнему один, и он про тему.
 *
 * Сворачивание стоит под проверкой поддержки. Браузер, не знающий popover, не прячет меню и не
 * показывает кнопку: он получает прежнюю шапку — на телефоне некрасивую, но целую.
 */
/*
 * Скрытие написано через шапку, а не одним классом, и это не лишнее. Общее правило призыва стоит в
 * файле ниже и весит столько же, поэтому одиночный класс проигрывал ему: копия призыва внутри меню
 * была видна на планшете и уводила меню в третью строку. Нашёл это обмер, а не глаз.
 */
.site-head .menu-toggle, .site-head .menu-close, .site-head .menu-cta { display: none; }
/* Слой не всплывает, пока его не открыли, и умолчания браузера для всплывающего слоя ему только мешают. */
.site-menu[popover] { position: static; inset: auto; width: auto; height: auto; margin: 0 0 0 auto; border: 0; padding: 0; overflow: visible; color: inherit; background: none; }

@supports selector(:popover-open) {
  .site-menu[popover]:not(:popover-open), .site-head .head-cta { display: none; }
  .menu-toggle, .menu-close { align-items: center; min-height: 44px; padding: 0 var(--s-16); border: 1px solid var(--site-line); border-radius: var(--site-control-radius); background: none; color: var(--site-head); font-family: var(--site-display); font-size: var(--site-fs-note); font-weight: 600; cursor: pointer; }
  .site-head .menu-toggle { display: inline-flex; margin-left: auto; }

  /* Открытый лист: во всю ширину сверху, по краям те же поля, что у страницы. */
  .site-menu[popover]:popover-open {
    position: fixed; inset: 0 0 auto 0; margin: 0; width: 100%; max-height: 100dvh; overflow: auto;
    display: flex; flex-wrap: wrap; align-items: center; gap: 0 var(--s-8);
    padding: var(--s-16) var(--edge) var(--s-24);
    background: var(--site-bg); border-bottom: 1px solid var(--site-line-strong);
  }
  .site-menu::backdrop { background: color-mix(in srgb, var(--site-bg) 70%, transparent); }
  /* Кнопка закрытия стоит там же, где была кнопка открытия: палец не ищет её заново. */
  .site-menu:popover-open .menu-close { display: inline-flex; margin: 0 0 var(--s-8) auto; }
  /* Поиск на телефоне - первой строкой открытого меню, в палец высотой. Кегль 17 px, как у пунктов
     меню: на кегле меньше 16 px iPhone увеличивает страницу при вводе. */
  .site-menu:popover-open .site-search { flex: 1 0 100%; width: auto; min-height: 48px; margin: 0 0 var(--s-8); }
  .site-menu:popover-open .site-search input { font-size: var(--site-fs-lead); }
  .site-menu:popover-open > a:not(.cta) { flex: 1 0 100%; display: flex; align-items: center; min-height: 48px; border-bottom: 1px solid var(--site-line); font-size: var(--site-fs-lead); }
  .site-menu:popover-open .lang-switch { margin: var(--s-16) 0 0; }
  .site-menu:popover-open .lang-switch > * { display: inline-flex; align-items: center; min-height: 40px; padding: 0 var(--s-16); }
  .site-menu:popover-open .theme-toggle { min-height: 44px; margin: var(--s-16) 0 0; padding: 0 var(--s-16); }
  .site-menu:popover-open .menu-cta { display: inline-flex; flex: 1 0 100%; justify-content: space-between; min-height: 48px; margin: var(--s-16) 0 0; padding: 0 var(--s-16); }
}

@media (min-width: 680px) {
  .site-head .menu-toggle { display: none; }
  .site-menu[popover]:not(:popover-open) { display: flex; }
  .site-head .head-cta { display: inline-flex; }
  /**
   * Шапка — сетка из трёх колонок: знак, меню, призыв.
   *
   * Меню стоит в средней колонке и переносится внутри неё. Поэтому на планшете знак остаётся
   * слева, призыв справа, а пункты меню встают ровно в две строки, вместо того чтобы вся шапка
   * падала лесенкой в три ряда, как было на 768 до 16 сентября 2026. На широком окне всё в одну
   * строку, как и раньше: сетка ничего не меняет там, где места хватает.
   */
  .site-head { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; }
  .site-menu { justify-content: flex-end; }
}

/* Только по вертикали, как у шапки и подвала: сокращенная запись затирает поля от края окна. */
.sub-nav { display: flex; flex-wrap: wrap; gap: 2px; padding-block: var(--s-8); border-bottom: 1px solid var(--site-line); }
.sub-nav a { padding: var(--s-8) var(--s-12); border-radius: var(--site-control-radius); color: var(--site-muted); font-size: var(--site-fs-body); text-decoration: none; transition: background-color var(--t-colour) var(--site-ease), color var(--t-colour) var(--site-ease); }
.sub-nav a:hover { color: var(--site-head); background: var(--site-panel); }
.sub-nav a[aria-current="page"] { color: var(--site-head); background: color-mix(in srgb, var(--site-head) 10%, transparent); }

/**
 * Досье на столе. Спека docs/superpowers/specs/2026-09-26-dossier-redesign-design.md.
 *
 * Страница - лист на столе. Стол - фон окна, лист - колонка содержимого. Лист светлый в обеих
 * темах: внутри него токены сайта переопределены бумажными, поэтому все, что стоит на листе, само
 * рисуется чернилами по бумаге. Шапка окна, вкладки и подвал лежат на столе и следуют теме.
 *
 * Лист не наклоняется, не мнется и не отбрасывает тень: тень на сайте только у всплывающих слоев,
 * а фактура бумаги и загнутые углы - украшение, а не прием документа.
 */
.desk { margin: 0 auto; width: 100%; max-width: 1680px; }
.sheet {
  color-scheme: light;
  --site-bg: var(--l-bg);
  --site-panel: var(--l-panel);
  --site-panel-hover: var(--l-panel-hover);
  --site-line: var(--l-line);
  --site-line-strong: var(--l-line-strong);
  --site-head: var(--l-head);
  --site-body: var(--l-body);
  --site-muted: var(--l-muted);
  --site-accent: var(--l-accent);
  --site-blue: var(--l-blue);
  --sheet-pad: clamp(20px, 3.4vw, 64px);
  padding-inline: var(--sheet-pad);
  background: var(--site-bg);
  color: var(--site-body);
}
/* На листе поля задает сам лист: общее правило полей от края окна здесь лишнее. */
.sheet main, .sheet .sub-nav { max-width: none; padding-inline: 0; }
/* Шифр листа - адрес страницы, набран как запись. */
.sheet-head { display: grid; gap: var(--s-4); padding-top: var(--s-16); border-bottom: 1px solid var(--site-line); }
.sheet-line { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--s-4) var(--s-16); }
.sheet-code, .sheet-date { margin: 0; color: var(--site-muted); font-family: var(--font-data); font-size: var(--site-fs-mark); letter-spacing: .06em; }
/**
 * Колонтитул листа, как у организационной модели (20 октября 2026): шифр слева рамкой-меткой. Шифр
 * по-прежнему адрес страницы; рамка только делает его меткой. Имя организации справа снято 28 октября
 * 2026 по замечанию автора: «лаборатория перспективных исследований» стояла на каждой странице еще и
 * в шапке, в подвале и на главной.
 */
/* Рамка вынесена в поле: текст шифра стоит на той же вертикали, что подменю и содержимое листа.
   Тот же прием, что у второй строки меню ниже. */
.sheet-code { margin-left: calc(-1 * var(--s-8)); padding: 2px var(--s-8); border: 1px solid var(--site-line-strong); color: var(--site-head); }
.sheet-head .sub-nav { border-bottom: 0; padding-block: var(--s-4) var(--s-8); margin-inline: calc(-1 * var(--s-12)); }
/**
 * Вкладки папки на верхней кромке листа. Открытый раздел цвета листа и сливается с ним: это та же
 * бумага. Остальные вкладки - тон стола посветлее. На телефоне вкладок нет: четыре русских названия
 * в строку не встают, и разделы открывает кнопка меню. Пункты разделов в шапке на широком окне
 * прячутся: их место заняли вкладки, а два одинаковых меню друг над другом говорят одно дважды.
 */
.tabs { display: none; }
@media (min-width: 680px) {
  .desk { padding-inline: var(--edge); }
  .tabs { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--s-4); padding-top: var(--s-24); }
  .tabs a { padding: var(--s-8) var(--s-20); border: 1px solid var(--site-line); border-bottom: 0; background: var(--site-panel); color: var(--site-muted); font-size: var(--site-fs-body); font-weight: 500; text-decoration: none; transition: background-color var(--t-colour) var(--site-ease), color var(--t-colour) var(--site-ease); }
  .tabs a:hover { background: var(--site-panel-hover); color: var(--site-head); }
  .tabs a[aria-current="page"] { border-color: var(--l-bg); background: var(--l-bg); color: var(--l-head); }
  .site-menu > a:not(.cta) { display: none; }
}

/* Только низ: поля по краям задает общее правило выше, а сокращенная запись затирала бы их. */
main { padding-bottom: var(--s-80); }
.page-head { padding: var(--s-48) 0 var(--s-24); }
/**
 * Ярлык над заголовком - темная плашка с именем раздела заглавными, как «02 КОНЦЕПЦИЯ» над
 * заголовком главы в организационной модели (20 октября 2026). На обложке главной токены
 * перевернуты, и плашка выходит светлой на графите.
 */
.kicker { display: inline-block; margin: 0 0 var(--s-16); padding: var(--s-4) var(--s-8); background: var(--site-head); color: var(--site-bg); line-height: 1.3; }
/**
 * Нижняя граница кегля — 32, а не 34, и число снято обмером, а не выбрано.
 *
 * 16 сентября 2026 на экране шириной 320 слово «документировать» в заголовке записи об отчётности
 * набиралось в 284 пикселя при колонке в 280 и вылезало за край. Помещается оно при кегле до 33;
 * следующее по длине слово заголовков сайта, «расследователя», — до 36. Два пикселя запаса.
 *
 * Перенос длинного слова стоит страховкой, а не приёмом: он срабатывает, только когда слово шире
 * всей строки, и обычный перенос по пробелам не трогает. Автоматическая расстановка переносов сюда
 * не взята намеренно — она переставляла бы строки и на широком окне, где сейчас ничего не ломается.
 */
h1 { margin: 0; color: var(--site-head); font-family: var(--site-display); font-weight: 400; font-size: clamp(32px, 5.4vw, 70px); line-height: 1.04; letter-spacing: -.02em; text-wrap: balance; overflow-wrap: break-word; }
/* Имя свежей записи набрано тем же правилом, что заголовок раздела: четвёртой ступени заводить
   незачем, а по месту в полосе это и есть заголовок. */
h2, .post-lead h3 { margin: 0 0 var(--s-16); color: var(--site-head); font-family: var(--site-display); font-weight: 400; font-size: clamp(25px, 2.6vw, 36px); line-height: 1.18; letter-spacing: -.015em; }
h3 { margin: var(--s-24) 0 var(--s-8); color: var(--site-head); font-family: var(--site-display); font-weight: 500; font-size: var(--site-fs-lead); line-height: 1.3; }
/* Перенос в заголовках выровнен, как у h1: строки примерно равны, и одно слово не остается на
   последней строке. До 3 ноября 2026 так было только у h1, а заголовки списка публикаций и
   документов кончались строкой в одно слово. Висящие предлоги снимает сборка (lib/typeset.mjs). */
h2, h3, h4 { text-wrap: balance; }
section { padding: var(--s-32) 0; border-top: 1px solid var(--site-line); }
section:first-of-type { border-top: 0; }
/**
 * Строка прозы - 64 знака. До 23 сентября 2026 стояло 72ch, и детектор намерил 68-98 знаков в
 * строке: английский на странице формата доходил до 98. Удобно читать 60-75, и 64ch держит
 * русский и английский внутри этого коридора.
 */
p { margin: 0 0 var(--s-16); max-width: 64ch; }
li { margin: 0 0 var(--s-8); }
/* 600, а не 700 по умолчанию: у Onest файлы 400, 500 и 600 (аудит unslop, 4 ноября 2026). */
strong { color: var(--site-head); font-weight: 600; }
code { font-family: var(--font-data); font-size: .92em; }
/* Короткий код без пробелов не рвется посередине; класс ставит сборка (lib/document.mjs). */
code.is-whole { white-space: nowrap; }
/**
 * Маркер на ключевом слове: автор записи обрамляет слова двумя знаками равенства с каждой стороны.
 * Прием из разбора Spell UI, 27 сентября
 * 2026. Тон - линовка листа, как карандашом: свободного цвета на сайте нет, красный значит риск, а
 * янтарный в продукте значит «на проверке». Выделение проявляется слева направо, когда абзац
 * доезжает до экрана; без поддержки и у просившего меньше движения стоит сразу целиком.
 */
mark { padding: 0 .12em; background: linear-gradient(color-mix(in srgb, var(--site-line-strong) 45%, transparent), color-mix(in srgb, var(--site-line-strong) 45%, transparent)) no-repeat 0 0 / 100% 100%; color: inherit; box-decoration-break: clone; -webkit-box-decoration-break: clone; }
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* Прочерк кончается, когда строка еще в нижней трети окна: к тому моменту, как глаз до нее
       дошел, выделение уже стоит целиком. Первая версия кончала его на середине окна, и строка,
       поставленная посередине, оставалась выделенной на две трети. */
    mark { animation: mark-draw linear both; animation-timeline: view(); animation-range: entry 40% cover 30%; }
  }
}
@keyframes mark-draw {
  from { background-size: 0 100%; }
  to { background-size: 100% 100%; }
}
.lead { max-width: 60ch; font-size: var(--site-fs-lead); }

/**
 * Таблица организационной модели (20 октября 2026): темная строка шапки с подписями заглавными,
 * строки данных на листе, волосяная линейка под каждой. Вертикальных линий нет: столбцы держит
 * выравнивание, а клетка в рамке читалась бы бланком.
 *
 * Таблица из markdown своей обертки не имеет, поэтому на узком окне она сама становится блоком с
 * прокруткой; на широком стоит во всю колонку. Таблица полос (data-table) лежит в обертке
 * table-scroll и прокручивается внутри нее.
 */
table { display: block; max-width: 100%; overflow-x: auto; margin: 0 0 var(--s-16); border-collapse: collapse; font-size: var(--site-fs-note); line-height: 1.5; }
@media (min-width: 760px) { table { display: table; width: 100%; } }
.table-scroll { max-width: 100%; overflow-x: auto; margin: 0 0 var(--s-16); }
.table-scroll table { display: table; width: 100%; min-width: 34rem; margin: 0; }
/**
 * Маски по краям широкой таблицы (этап 5 карты сайта, 3 ноября 2026; образец - Stripe): край, за
 * которым таблица продолжается, растворяется, и на телефоне видно, что ее можно листать. Маску ведет
 * прокрутка самой таблицы, scroll(self inline). Когда листать некуда, такой прокрутки у таблицы
 * нет, анимация не действует, и маски нет.
 *
 * Это состояние, а не движение: края меняются только вместе с пальцем читателя. Поэтому просьба
 * «меньше движения» маски не снимает, как не снимает отметку в оглавлении. Маска читает только
 * прозрачность, и black в градиенте - не цвет страницы.
 *
 * С 3 ноября 2026 край гаснет не шире, чем осталось листать: у каждого края свой отрезок прокрутки
 * длиной 40 пикселей, у начала первые 40, у конца последние 40. Раньше отрезки считались долями
 * прокрутки, и таблица, которую можно сдвинуть всего на 3 пикселя, гасила 40 пикселей и концы слов
 * (разбор сайта, «Метод TXINT» на телефоне). С того же дня маску получает и блок кода: команда на
 * «Формате» обрезалась краем без всякого знака, что за ним есть продолжение.
 */
@property --table-fade-start { syntax: "<length>"; inherits: false; initial-value: 0px; }
@property --table-fade-end { syntax: "<length>"; inherits: false; initial-value: 0px; }
@supports (animation-timeline: scroll()) {
  .table-scroll, table:not(.table-scroll table), pre:not(.licence) {
    mask-image: linear-gradient(to right, transparent, black var(--table-fade-start), black calc(100% - var(--table-fade-end)), transparent);
    animation: table-edge-start linear both, table-edge-end linear both;
    animation-timeline: scroll(self inline), scroll(self inline);
    animation-range: 0px 40px, calc(100% - 40px) 100%;
  }
}
@keyframes table-edge-start {
  from { --table-fade-start: 0px; }
  to { --table-fade-start: 40px; }
}
@keyframes table-edge-end {
  from { --table-fade-end: 40px; }
  to { --table-fade-end: 0px; }
}
th, td { padding: var(--s-8) var(--s-12); border: 0; border-bottom: 1px solid var(--site-line); text-align: left; vertical-align: top; }
thead th { border-bottom: 0; background: var(--site-head); color: var(--site-bg); font-family: var(--font-data); font-size: var(--site-fs-mark); font-weight: 500; letter-spacing: .1em; text-transform: uppercase; }
/* Таблица из двух колонок: первая занимает треть, и у соседних таблиц на странице вторая колонка
   начинается на одном месте. До 4 ноября 2026 ширину делил браузер по длине текста, и на «Методе
   TXINT» вторые колонки четырех таблиц начинались на 297, 316, 514 и 657 пикселях (разбор сайта). */
@media (min-width: 760px) {
  table:not(.table-scroll table):has(tr > :nth-child(2):last-child) tr > :first-child { width: 33%; }
}
tbody th { color: var(--site-head); font-weight: 500; }
td { color: var(--site-body); }
pre { overflow-x: auto; margin: 0 0 var(--s-16); padding: var(--s-16) var(--s-16); border: 1px solid var(--site-line); background: var(--site-panel); font-family: var(--font-data); font-size: var(--site-fs-note); line-height: 1.55; }
pre.licence { white-space: pre-wrap; overflow-wrap: anywhere; }

/* Оговорка — не абзац: она стоит рядом с утверждением и говорит о его границах. */
.note { padding: var(--s-12) var(--s-16); border-left: 2px solid var(--site-line-strong); color: var(--site-muted); font-size: var(--site-fs-body); }

/* Цепочка нарисована разметкой, а не картинкой: так она переводится вместе с текстом. */
/**
 * Цепочка метода: семь шагов слева направо.
 *
 * Замечание автора 10 сентября 2026: выровнять по центру, добавить стрелки или градацию цвета.
 * Сделано и то, и другое, и оба говорят одно: шаги идут по порядку и ведут к публикации. Стрелка
 * стоит между шагами, а не после каждого; текст шага светлеет от первого к последнему — это
 * градация проверенности, и последний шаг самый светлый. Красного нет — он цвет риска.
 *
 * **11 сентября 2026 полоса стала схемой.** Была серая плашка с семью подписями по 11px — на
 * странице, которая вся про эту цепочку, она читалась подписью под чем-то, а не самим предметом.
 * В референсах, присланных автором, порядок работы всегда набран крупно и пронумерован: номер,
 * слово, стрелка к следующему. Взято именно это.
 *
 * Номер рисует счётчик самого списка, а не разметка: список шагов уже пронумерован по смыслу, и
 * второй список чисел в тексте разошёлся бы с первым при первой же вставке шага. Линейка над
 * шагом — та же, что над столбцами уровней: два пикселя на цвете сильной линии.
 *
 * **Гарнитура осталась узкой, и это поймал тест, а не глаз.** Первая версия правки набрала шаги
 * заглавной гарнитурой — как в референсах, где процесс всегда крупный и прозаический. Правило
 * проекта другое и старше: узкая гарнитура держит всё, что читается как запись, а шаг цепочки —
 * ярлык стадии, а не фраза. Выросли размер и вес, семейство осталось прежним.
 */
/* Отступ снизу с 3 ноября 2026: на «Методе» под цепочкой идет блок в рамке, и двухстрочные
   подписи «Задача проверки» и «Подтвержденный материал» касались ее верхней линии. */
.chain { display: flex; flex-direction: column; gap: var(--s-20); margin: 0 0 var(--s-24); padding: 0; list-style: none; counter-reset: chain; }
.chain li { position: relative; flex: none; margin: 0; padding: var(--s-12) var(--s-32) 0 0; border-top: 2px solid var(--site-line-strong); counter-increment: chain; font-family: var(--font-data); font-size: var(--site-fs-body); font-weight: 500; line-height: 1.25; letter-spacing: .04em; text-wrap: balance; color: color-mix(in srgb, var(--site-head) 45%, var(--site-muted)); }
.chain li::before { content: counter(chain, decimal-leading-zero); display: block; margin-bottom: var(--s-8); color: var(--site-muted); font-family: var(--font-data); font-size: var(--site-fs-mark); font-weight: 500; letter-spacing: .12em; }
.chain li:nth-child(2) { color: color-mix(in srgb, var(--site-head) 55%, var(--site-muted)); }
.chain li:nth-child(3) { color: color-mix(in srgb, var(--site-head) 65%, var(--site-muted)); }
.chain li:nth-child(4) { color: color-mix(in srgb, var(--site-head) 74%, var(--site-muted)); }
.chain li:nth-child(5) { color: color-mix(in srgb, var(--site-head) 83%, var(--site-muted)); }
.chain li:nth-child(6) { color: color-mix(in srgb, var(--site-head) 92%, var(--site-muted)); }
.chain li:last-child { color: var(--site-head); font-weight: 600; }
/* Стрелка стоит в промежутке между шагами, на строке номера: там она читается как переход. */
.chain li:not(:last-child)::after { content: "↓"; position: absolute; right: 0; top: var(--s-12); color: var(--site-muted); font-family: var(--font-data); font-size: var(--site-fs-note); line-height: 1.3; }
@media (min-width: 1240px) {
  .chain { flex-direction: row; gap: var(--s-16) var(--s-24); }
  .chain li { flex: 1 1 0; padding-right: 0; }
  .chain li:not(:last-child)::after { content: "→"; right: -19px; }
}

.steps, .cards, .swatches { margin: 0 0 var(--s-16); padding: 0; list-style: none; display: grid; gap: 1px; background: var(--site-line); border: 1px solid var(--site-line); }
/**
 * Карточки идут рядами с переносом, и последний ряд растягивается на всю ширину. До 3 ноября 2026
 * это была сетка с числом колонок по ширине, и неполный последний ряд оставлял пустые ячейки цвета
 * линии: пять карточек на 1440 стояли 4+1 с серым прямоугольником в три колонки, три карточки на
 * 1000 - 2+1. Пять карточек встают не больше чем по три в ряд, то есть 3+2: основа шире четверти
 * ряда, и четыре в ряд не входят.
 */
.cards { display: flex; flex-wrap: wrap; }
.cards > li { flex: 1 1 220px; }
.cards:has(> li:nth-child(5):last-child) > li { flex-basis: max(220px, calc(25% + 1px)); }
/**
 * Содержимое карточки прижато к верху, и это не косметика.
 *
 * Карточки в ряду равны по высоте, а внутри каждой — сетка из двух строк: имя и текст. При
 * умолчании (stretch) лишняя высота делится **между строками**, то есть у карточки с коротким
 * текстом отрастает строка имени и текст начинается ниже, чем у соседки. Замер 16 сентября 2026 на
 * разборе метода: в одном ряду текст начинался на 44, 68 и 68 пикселях от верха карточки.
 *
 * Глазом это читается как сбитая строка, а не как воздух. Прижатие к верху ставит все имена и все
 * тексты на одну линию, и лишняя высота уходит вниз — туда, где её никто не читает.
 */
.steps li, .cards li, .swatches li { margin: 0; display: grid; align-content: start; gap: var(--s-4); padding: var(--s-16) var(--s-20); background: var(--site-bg); }
.steps strong, .cards strong, .swatches strong { font-size: var(--site-fs-body); }
.steps span, .cards span, .swatches span { color: var(--site-body); font-size: var(--site-fs-body); }

.swatches li { grid-template-columns: 44px 1fr; align-items: baseline; }
.swatches i { grid-row: span 2; align-self: center; height: 0; border-top: 2px solid var(--site-muted); }
.swatches .line-dashed { border-top-style: dashed; }
.swatches .line-disputed { border-top-style: dashed; border-top-color: var(--red); }

.actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-24); margin: var(--s-32) 0 0; }
/**
 * Призыв: прямой угол, тонкая рамка, при наведении инвертируется в заливку.
 *
 * Выбор автора из шести показанных вариантов, 10 сентября 2026. Таблетка со светлой заливкой и
 * кружком под стрелкой стояла здесь один день: она была заметна, но приносила на страницу форму,
 * которой больше нигде нет. Прямой угол — решение страницы, и кнопка теперь ему не исключение.
 * Скругление осталось только у переключателей: пункт меню и выбор языка.
 *
 * Стрелка цвета самой кнопки. До 4 ноября 2026 она была красной, «помечала единственное действие», и
 * стояла так в шапке на всех страницах. Решение автора по аудиту unslop: красный на сайте значит
 * только риск и опасное действие, а чтение публикаций ни то, ни другое.
 *
 * Шрифт задан явно: элемент button его не наследует, и «Открыть холст» на главной был набран Arial.
 *
 * Переходы 160 мс и только по цвету и сдвигу. Правило взято из эссе Эмиля Ковальски «You don't
 * need animations»: частое действие анимировать не надо, а все, что анимируется, обязано уложиться
 * заметно быстрее трех десятых секунды — иначе интерфейс начинает ждать себя сам.
 */
.cta { display: inline-flex; align-items: center; gap: var(--s-16); padding: var(--s-12) var(--s-24); border: 1px solid var(--site-head); color: var(--site-head); font-family: var(--site-display); font-size: var(--site-fs-body); font-weight: 600; text-decoration: none; transition: background-color var(--t-colour) var(--site-ease), color var(--t-colour) var(--site-ease); }
.cta .arrow { font-family: var(--font-data); font-size: var(--site-fs-note); }
.cta:hover { background: var(--site-head); color: var(--site-bg); }
@media (prefers-reduced-motion: no-preference) {
  .cta .arrow { transition: transform var(--t-move) var(--site-ease); }
  .cta:hover .arrow { transform: translateX(3px); }
}
/**
 * Тот же призыв, сжатый до размера шапки.
 *
 * Второй формы кнопки на сайте нет: меняются только поля и кегль, а рамка, инверсия при наведении
 * и стрелка остаются теми же. Надпись строчными с 4 ноября 2026 (аудит unslop): заглавные с
 * разрядкой на сайте только у подписей данных, а это кнопка. Кнопка в шапке, нарисованная по-своему, читалась бы другим
 * элементом — а это тот же призыв, просто стоящий выше.
 *
 * Скрывать её на узком окне я не стал, хотя слоган на её месте скрывался: слоган там повторял
 * подвал, а призыв — единственный на странице путь к разговору.
 */
.cta.is-compact { gap: var(--s-8); margin-left: var(--s-16); padding: var(--s-8) var(--s-12); font-size: var(--site-fs-note); font-weight: 600; }
.link-plain { color: var(--site-body); font-size: var(--site-fs-body); text-decoration: underline; text-decoration-color: var(--site-line-strong); text-underline-offset: 4px; }
.link-plain:hover { color: var(--site-head); text-decoration-color: var(--site-accent); }

/**
 * Полоса: ярлык и номер стоят на полях, содержимое — в главной колонке.
 *
 * Так читается лист, а не лента: у страницы появляется край, вдоль которого идет навигация глазом.
 * На узком экране поля схлопываются, и ярлык встает над содержимым.
 */
/**
 * Воздуха между полосами стало вдвое больше — правка 11 сентября 2026.
 *
 * Сорок пикселей сверху и снизу укладывали полосы вплотную: на экран попадало три сразу, и ни одна
 * не читалась как отдельное утверждение. Отступ считается от высоты окна, а не от числа: на ноутбуке
 * он меньше, на большом мониторе больше, и в обоих случаях на экран попадает по одной мысли.
 */
.band { display: grid; gap: var(--s-16); padding: clamp(52px, 8vh, 108px) 0; border-top: 1px solid var(--site-line); }
/* Колонка полосы на узком окне не растет под широкую таблицу: таблица прокручивается в своей
   обертке. Без этого колонка сетки брала минимальную ширину таблицы, и главная на телефоне уезжала
   вбок на 174 пикселя (замер 20 октября 2026). */
.band > div { min-width: 0; }
/**
 * Номер полосы - темная плашка, как номер раздела в организационной модели центра (20 октября 2026).
 * Плашка залита чернилами листа, а цифра набрана цветом самого листа: так номер виден сразу и не
 * спорит с заголовком размером.
 */
.band-label { justify-self: start; align-self: start; margin: 0; padding: var(--s-4) var(--s-8); background: var(--site-head); color: var(--site-bg); font-size: var(--site-fs-note); line-height: 1.2; letter-spacing: .1em; }
/**
 * На широком окне плашка стоит в колонке полей и прилипает под шапкой, пока полоса на экране.
 *
 * До 20 октября 2026 номер в поле был огромным (до 54 пикселей) и тихого цвета: колонка полей была
 * пустой, и крупная цифра делала ее частью ритма. Организационная модель центра ставит номер
 * раздела плашкой, и сайт, перерисованный под нее, сделал так же: колонка полей держится плашкой,
 * а размер вернулся к подписи.
 *
 * Порядок в разметке не тронут: номер читается до имени полосы и вслух, и глазами.
 */
@media (min-width: 900px) {
  .band { grid-template-columns: var(--gutter) minmax(0, 1fr); gap: 0 var(--s-32); }
  .band-label {
    position: sticky;
    top: calc(var(--head-room) + var(--s-24));
    margin-top: var(--s-8);
    padding: var(--s-8) var(--s-12);
    font-size: var(--site-fs-body);
    font-weight: 500;
    letter-spacing: .08em;
  }
}

/**
 * Первая полоса стала крупнее и просторнее.
 *
 * «Слишком просто» на этой странице означало не отсутствие элементов, а отсутствие масштаба:
 * заголовок был лишь немного больше остального текста, и первый экран читался как первый абзац.
 * Кинематографичный кегль — самое дешевое средство характера: он ничего не добавляет на страницу,
 * он меняет вес того, что там уже есть.
 *
 * Свет в углу пробовали и убрали 10 сентября 2026 по решению автора. Радиальный градиент от цвета
 * чернил давал «затемнение» — пятно, которое читается как грязь на экране или как неудачно сжатая
 * картинка, а не как свет. На равномерно черном листе любая мягкая тень выглядит дефектом печати.
 */
.hero { padding: clamp(40px, 7vh, 88px) 0 clamp(64px, 11vh, 120px); }
/* У внутренней страницы под первой полосой сразу идет первый раздел со своим отступом сверху. Вместе
   они давали 172 пикселя пустоты между вводным абзацем и первым разделом на окне 900 пикселей, больше,
   чем между двумя разделами (замечание автора 28 октября 2026). */
.hero.is-plain { padding-bottom: clamp(32px, 5vh, 56px); }
/**
 * Потолок кегля — 68px, а не «сколько влезет».
 *
 * На 80px русский заголовок из шести слов занимал пять строк и уводил призыв за нижнюю кромку
 * окна: масштаб начинал работать против того, ради чего он взят. Английский вариант короче и на
 * том же кегле стоит в три строки, поэтому потолок ставится по длинному языку — иначе половина
 * читателей увидит первый экран без кнопки.
 */
/* Потолок опущен с 68px до 58px по замечанию автора 10 сентября 2026: заголовок перевешивал лид. */
/**
 * Утверждение на первом экране набрано **обычным весом**, а не жирным, и это приём из разбора
 * сайта Anduril 10 сентября 2026.
 *
 * Там крупная фраза стоит весом 400 с отрицательным трекингом, а жирный оставлен мелкому
 * подзаголовку под ней. Работает это на противоходе привычке: на большом кегле жирный добавляет не
 * веса, а шума, потому что и так крупно. Вес фразы решает размер, а плотность — трекинг.
 *
 * У нас взято 500, а не 400: Onest на темном тоньше, чем на светлом, и четырехсотый на кремовом по
 * почти-черному начинает светиться по краям букв.
 */
/**
 * 10 сентября потолок подняли до 82px вместе с широкими полосами. Разбор дизайна 23 сентября
 * намерил цену: по-английски 5 строк, и на окне 1280 на 800 кнопка героя уходила под край (y=826).
 * Потолок возвращен к 58px, записанным выше, и считается от ширины окна мягче.
 */
.hero h1 { max-width: 17ch; font-weight: 400; font-size: clamp(34px, 4.4vw, 58px); line-height: 1.06; letter-spacing: -.026em; }
/**
 * С 20 октября 2026 обе половины заголовка набраны весом 400, как титул организационной модели, и
 * граница между ними сказана тоном: вторая половина тише. Насыщенностью ее больше не сказать -
 * легче 400 у Onest в проекте начертания нет.
 */
.hero h1 .head-tail { font-weight: 400; color: var(--site-muted); }
/**
 * Знак сервиса у заголовка его страницы (CATK, 22 октября 2026). С 25 октября у знака нет своей
 * плашки: вырезы на теле змеи сделаны маской, и сквозь них виден лист. Подкладывать под знак
 * нечего, он стоит прямо на бумаге, как имя сервиса рядом.
 *
 * С 3 ноября 2026 место знака называется .hero-mark, и знак у заголовка есть и у TXINT. До этого оно
 * звалось .service-mark, как знаки линий услуг с 31 октября, и правило их темной плашки легло под
 * змею CATK: на листе стоял черный квадрат с едва видным знаком (разбор сайта, заметил автор).
 * Высота у знаков одна, ширина своя: рыба TXINT шире змеи CATK.
 */
.hero h1.has-mark { display: flex; align-items: center; gap: var(--s-20); }
.hero-mark { flex: none; display: block; height: 56px; }
.hero-mark img { display: block; width: auto; height: 100%; }
.hero .lead { margin: var(--s-24) 0 0; }
/* Строка с двоеточием ведет перечень, поэтому она ближе к нему, чем к абзацу над собой. */
.hero .lead-label { margin: var(--s-20) 0 0; }
.hero-list { margin: var(--s-8) 0 0; padding: 0; list-style: none; display: grid; gap: var(--s-8); }
.hero-list li { position: relative; margin: 0; padding-left: var(--s-20); max-width: 62ch; font-size: var(--site-fs-lead); color: var(--site-body); }
.hero-list li::before { content: ""; position: absolute; left: 0; top: .72em; width: 10px; border-top: 1px solid var(--site-line-strong); }
/* На узком окне снимок стоит под кнопкой, и до 3 ноября 2026 стоял вплотную к ней. Отступ тот же,
   что у графа на главной; на широком окне снимок уходит в правую колонку, и отступ дает сетка. */
.hero > .shot { margin-top: var(--s-40); }
@media (min-width: 1000px) {
  /**
   * Колонка снимка растёт вместе с окном, а не стоит на 470 пикселях.
   *
   * Жёсткое число годилось, пока первый экран делили только страницы сервисов, где в заголовке
   * стоит короткое имя. Стоило вернуть снимки на главную — и на 1024 слово «Расследование,»
   * перестало помещаться в оставшиеся 400 пикселей и ушло под снимок: имя страницы, разрезанное
   * картинкой. Нашёл это детектор наложений, а не глаз, и нашёл в тот же час.
   *
   * Треть окна при потолке в 470 оставляет заголовку 508 пикселей в самом узком случае — на
   * шестьдесят больше самой длинной его строки. Потолок нужен с другой стороны: на широком
   * мониторе снимок, растущий без предела, начинает спорить с утверждением.
   */
  .hero { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, clamp(300px, 34vw, 470px)); gap: var(--s-64); align-items: center; }
  .hero > .shot { margin-top: 0; }
  /* Первая полоса без снимка идет в одну колонку: вторая колонка, оставшись пустой, сжимала бы
     утверждение вдвое ради ничего. */
  .hero.is-plain { grid-template-columns: minmax(0, 1fr); }
  .hero.is-plain .lead { max-width: 62ch; }
  /* У главной первый экран - утверждение и под ним граф во всю ширину листа. Правой колонки у
     заголовка больше нет, и в 17 знаках он вставал в четыре строки при пустой половине листа, а граф
     уходил под край окна. */
  .hero.is-graph { display: block; }
  .hero.is-graph h1 { max-width: 26ch; }
}
/**
 * Обложка. Первый экран главной стоит графитовой панелью на листе, как титул организационной
 * модели центра (20 октября 2026). Внутри панели токены листа заменены токенами обложки, поэтому
 * заголовок, абзац и граф организации сами рисуются светлым по графиту, без второго набора правил.
 * Панель темная в обеих темах: это лицо документа, а не цвет стола.
 */
.hero.is-graph {
  --site-bg: var(--site-cover);
  --site-panel: color-mix(in srgb, var(--site-cover-head) 5%, var(--site-cover));
  --site-panel-hover: color-mix(in srgb, var(--site-cover-head) 9%, var(--site-cover));
  --site-line: var(--site-cover-line);
  --site-line-strong: color-mix(in srgb, var(--site-cover-head) 30%, var(--site-cover));
  --site-head: var(--site-cover-head);
  --site-body: var(--site-cover-body);
  --site-muted: var(--site-cover-muted);
  --site-blue: var(--site-cover-blue);
  color-scheme: dark;
  margin: var(--s-24) 0 0;
  padding: clamp(28px, 4.4vw, 64px) clamp(20px, 4vw, 56px);
  background: var(--site-bg);
  color: var(--site-body);
}

/**
 * Граф организации на главной. Спека 2026-09-26-dossier-redesign-design.md, модуль lib/org-graph.mjs.
 *
 * Разметка - список ссылок; граф из него делает раскладка. На узком окне это дерево: узлы
 * столбцом, у каждого отвод слева и подпись связи над именем. С ширины BREAK.split узлы встают на
 * свои места в процентах, под ними линии SVG, на серединах линий подписи. Подписи на широком окне
 * рисует отдельный слой, скрытый от читалки, а подпись в узле остается для читалки невидимой
 * строкой: связь узла слышно на любой ширине.
 *
 * Карточка узла - как карточка записи на холсте CATK: ярлык рода шрифтом данных, имя, строка
 * пояснения. Нажимается вся карточка. Организация обведена чернилами: от нее идут связи.
 */
.org-graph { position: relative; margin: var(--s-40) 0 0; }
.graph-lines, .graph-verbs { display: none; }
.graph-nodes { display: grid; gap: var(--s-12); margin: 0; padding: 0; list-style: none; }
.graph-node { position: relative; display: grid; gap: var(--s-4); margin: 0; padding: var(--s-12) var(--s-16); border: 1px solid var(--site-line-strong); background: var(--site-bg); transition: border-color var(--t-colour) var(--site-ease); }
.graph-node:hover { border-color: var(--site-head); }
.graph-node:has(a:focus-visible) { outline: 2px solid var(--flow-focus); outline-offset: 2px; }
.graph-node a { color: var(--site-head); font-family: var(--site-display); font-size: var(--site-fs-lead); font-weight: 600; line-height: 1.2; text-decoration: none; }
.graph-node a:focus-visible { outline: 0; }
.graph-node a::after { content: ""; position: absolute; inset: 0; }
.graph-kind, .graph-edge { font-family: var(--font-data); font-size: var(--site-fs-mark); letter-spacing: .12em; line-height: 1.3; text-transform: uppercase; color: var(--site-muted); }
.graph-edge { letter-spacing: .04em; text-transform: none; }
/* Невидимая подпись для читалки нужна только на широком окне; на узком читалка слышит видимую. */
.graph-edge-said { display: none; }
.graph-note { color: var(--site-body); font-size: var(--site-fs-note); line-height: 1.4; }
.graph-node.is-organisation { border-color: var(--site-head); }
/* Дерево на узком окне: разделы и сервисы на ступень правее организации. */
.graph-node.is-section, .graph-node.is-service { margin-left: var(--s-24); }
.graph-node:not(.is-organisation)::before { content: ""; position: absolute; top: calc(-1 * var(--s-12)); right: 100%; width: var(--s-12); height: calc(50% + var(--s-12)); border-left: 1px solid var(--site-line-strong); border-bottom: 1px solid var(--site-line-strong); }
@media (min-width: 1000px) {
  /* Высота выросла с 540 до 700 пикселей 20 октября 2026: узлов справа стало пять, и на 540 соседние
     карточки накрывали друг друга. С 3 ноября 2026 до ширины цепочки - 800: при 1000-1100 пикселях
     карточки уже, текст в них переносится чаще, и по-русски «Исследования» наезжали на «Услуги» на
     11 пикселей. Сценарий графа мерил только 1440 и этого не видел. */
  .org-graph { height: 800px; }
  .graph-lines { display: block; position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
  .graph-lines line { stroke: var(--site-line-strong); stroke-width: 1; vector-effect: non-scaling-stroke; }
  .graph-verbs { display: block; position: absolute; inset: 0; pointer-events: none; }
  /* Место задано числом без единицы: знак процента в разметке читался бы проверкой как процент в
     тексте, а единицу добавляет правило. */
  .graph-verb { position: absolute; left: calc(var(--x) * 1%); top: calc(var(--y) * 1%); transform: translate(-50%, -50%); padding: 1px var(--s-4); border: 1px solid var(--site-line); background: var(--site-bg); color: var(--site-muted); font-family: var(--font-data); font-size: var(--site-fs-mark); line-height: 1.3; white-space: nowrap; }
  .graph-nodes { display: block; position: absolute; inset: 0; }
  .graph-node, .graph-node.is-section, .graph-node.is-service { position: absolute; left: calc(var(--x) * 1%); top: calc(var(--y) * 1%); width: min(15rem, 24%); margin: 0; transform: translate(-50%, -50%); }
  .graph-node::before, .graph-node:not(.is-organisation)::before { display: none; }
  .graph-edge { display: none; }
  .graph-edge-said { display: block; }
}
@media (min-width: 1240px) {
  .org-graph { height: 700px; }
}
/**
 * Связи графа прочерчиваются один раз при загрузке. Прием из разбора Motion (прорисовка путей), 27
 * сентября 2026. Граф стоит на первом экране, поэтому движение привязано ко времени, а не к
 * прокрутке: привязанные к прокрутке, связи застыли бы полупрочерченными на только что открытой
 * странице. Прочерк идет слева направо, от организации к тому, что она делает, и все связи разом:
 * порядок прорисовки не должен читаться как порядок причин. Одно движение на первый экран, короче
 * трети секунды; просившему меньше движения - неподвижный граф.
 */
@media (prefers-reduced-motion: no-preference) and (min-width: 1000px) {
  .graph-lines { animation: graph-draw var(--t-move) var(--site-ease) both; }
}
@keyframes graph-draw {
  from { clip-path: inset(0 100% 0 0); }
  to { clip-path: inset(0); }
}

/**
 * Негатив.
 *
 * Инверсия убрана по решению автора 8 сентября 2026: снимок показывается как есть. Продукт и сайт
 * лежат на одном темном листе, поэтому кадр садится в полосу без обработки.
 *
 * У инверсии была вторая работа — она **гасила читаемость**: на снимках стоят имена
 * демонстрационного набора. Держит это теперь одна подпись под кадром, и она обязательна:
 * «все лица и компании вымышлены». Она и раньше была тем, на чем все держалось, — на размытие
 * полагаться было нельзя.
 *
 * Зерно осталось, но наложением, а не умножением: умножение затемняет, и на неинвертированном
 * снимке кадр становился черным. Рисуется своим SVG внутри страницы — чужой адрес сюда не ходит,
 * это проверяется сценарием. Красная метка на рамке — та же точка, что в знаке: один красный на
 * полосу, а не пять.
 */
.shot { position: relative; margin: 0; border: 1px solid var(--site-line); background: var(--site-panel); }
/* Метка сидит на самом углу рамки, а не внутри кадра: на снимке там содержимое, и раньше она
   закрывала подпись панели продукта. Инверсия это скрадывала, снимок как есть — нет. */
.shot::before { content: ""; position: absolute; top: -4px; left: -4px; z-index: 2; width: 7px; height: 7px; background: var(--site-accent); }
.shot-frame { position: relative; overflow: hidden; }
/* Строка окна над снимком сервиса: три квадрата и путь внутри сервиса. Углы прямые, как везде на
   сайте; красного нет, он остается метке на углу рамки. */
.shot-bar { display: flex; align-items: center; gap: var(--s-12); padding: var(--s-8) var(--s-12); border-bottom: 1px solid var(--site-line); }
.shot-dots { display: flex; gap: var(--s-4); flex: none; }
.shot-dots i { width: 7px; height: 7px; border: 1px solid var(--site-line-strong); }
.shot-address { flex: 1; min-width: 0; overflow: hidden; padding: var(--s-4) var(--s-8); border: 1px solid var(--site-line); color: var(--site-muted); font-family: var(--font-data); font-size: var(--site-fs-note); line-height: 1.3; white-space: nowrap; text-overflow: ellipsis; }
.shot img { display: block; width: 100%; height: auto; }
/* Обертка picture (WebP с запасным PNG, с 22 октября 2026) не должна вставать между рамкой и
   картинкой: кадрирование is-crop считает высоту картинки от рамки. */
.shot picture { display: contents; }
/**
 * Штамп «Образец» на снимке продукта: демонстрационные данные. Красный здесь предупреждение, а не
 * украшение, поэтому метка на углу рамки у такого снимка снята: двух красных на одном снимке быть
 * не должно, а из двух остается тот, что что-то говорит. Штамп стоит на бумажной подложке: красный
 * прямо по темному снимку не проходит порог контраста.
 */
.shot:has(.shot-stamp)::before { display: none; }
/**
 * Снимок продукта стоит как есть, без наложений. С 27 сентября до 23 октября 2026 на нем лежал
 * точечный растр (прием из разбора Cult UI, dither-image) и уходил под курсором. Решение автора
 * 23 октября 2026: растра нет. Зерно из общего правила .shot-frame::after ниже тоже снято: под ним
 * мелкий текст интерфейса читается хуже. Кадр помечен как образец штампом «Образец».
 */
.shot:has(.shot-stamp) .shot-frame::after { display: none; }
/**
 * Снимок продукта раскрывается сверху вниз, как при сканировании (этап 5 карты сайта, 3 ноября 2026;
 * образец - Palantir). Раскрытие идет вслед за въездом снимка в окно, с отставанием: начинается, когда
 * въехала треть, и кончается, когда въехал весь. Только снимки в полосах внутренних страниц: на первом
 * экране главной одно движение уже есть, а снимок у заголовка страницы виден сразу, и раскрываться ему
 * некуда. Живая карточка записи CATK - не снимок и не раскрывается. При «меньше движения», без
 * поддержки и на бумаге снимок стоит целиком.
 */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    body:not(.is-home) .band .shot:has(.shot-stamp) .shot-frame:not(.is-card) { animation: shot-scan linear both; animation-timeline: view(); animation-range: entry 30% entry 100%; }
  }
}
@keyframes shot-scan { from { clip-path: inset(0 0 100% 0); } to { clip-path: inset(0); } }
/**
 * Живой холст CATK на главной (canvas/loader.js). Встает на место снимка в том же окне, со штампом
 * «Образец». Растр копии с живого холста снят: это уже не копия, а сам холст, и точки поверх
 * карточек мешали бы их читать. Точки подключения спрятаны: связь здесь не проводят, а кнопка
 * «Потяните, чтобы связать» обещала бы то, чего холст не сделает. Прячутся видимостью, а не
 * удалением: по ним холст считает, где начинаются связи.
 */
/* Ширина задана явно: без снимка у рамки нет своей ширины, и она сжималась, оставляя справа пустую
   полосу. */
.shot-frame.is-live { width: 100%; height: min(600px, 70vh); aspect-ratio: auto; background: var(--flow-surface); }
.shot-frame.is-live::after { display: none; }
/* База холста - та же, что у body продукта. Без нее холст наследовал базу сайта, межстрочный 1.6
   вместо обычного: карточка вырастала со 132 до 176 пикселей при шаге 170 в демо-наборе, и соседние
   карточки накрывали друг другу нижнюю строку. Замер 1 октября 2026. */
.canvas-surface { position: absolute; inset: 0; color: var(--text); font-family: var(--font-ui); font-size: var(--fs-lead); line-height: normal; letter-spacing: -.01em; }
.canvas-surface .react-flow__handle { visibility: hidden; }
/* Меню кластера на холсте - «Снять группировку», то есть правка. Холст только для чтения, и кнопка
   меню спрятана: свернуть и развернуть можно, это взгляд, а снять группировку здесь нельзя. */
.canvas-surface .react-flow__node-groupFrame [aria-haspopup="menu"] { display: none; }
/* Правила сайта на голые теги дотягиваются внутрь холста. strong красил имя кластера в чернила
   листа, черным по темной шапке. h3 давал имени записи поля 24 и 8 пикселей и вес 600, и карточка
   вырастала со 135 до 167 пикселей (замер 1 октября 2026). Внутри холста они заменены базой
   продукта: заголовки - как в его styles.css, абзац и пункт списка - с полями браузера. Вес
   :where() внутри класса холста - (0,1,0): этого хватает, чтобы перебить правило сайта на голый
   тег, и мало, чтобы спорить с утилитами продукта: те грузятся позже и при равном весе выигрывают. */
.canvas-surface :where(h1, h2, h3) { margin: 0; color: inherit; font-family: var(--font-display); font-weight: bold; line-height: inherit; letter-spacing: -.035em; }
.canvas-surface :where(h1) { font-size: var(--fs-heading); line-height: 1.08; }
.canvas-surface :where(h2) { font-size: var(--fs-title); }
.canvas-surface :where(h3) { font-size: var(--fs-strong); }
.canvas-surface :where(p, li) { margin: revert; }
.canvas-surface :where(strong) { color: inherit; }
/**
 * Живая карточка записи CATK на странице сервиса (пункт 4.1 карты сайта, 31 октября 2026) вместо
 * снимка node-card.png. Карточку рисует компонент продукта при сборке (scripts/build-node-card.mjs),
 * токены и утилиты приходят в /assets/node-card.css. Здесь только то, что вокруг нее: поле холста с
 * точками сетки, как на снимке, увеличение в полтора раза («вблизи») и легенда отрезков. Токены,
 * которые берут эти правила, перечислены в FRAME_TOKENS сборки; тест сверяет оба списка.
 *
 * Правила сайта на голые теги дотянулись бы и сюда, как в холст: заголовок и strong внутри
 * карточки получают базу продукта, а не листа.
 */
.shot-frame.is-card::after { display: none; }
.catk-card { display: grid; justify-items: center; gap: var(--s-24); padding: var(--s-48) var(--s-16) var(--s-32); background-color: var(--flow-field); background-image: radial-gradient(circle, color-mix(in srgb, var(--flow-line-strong) 22%, transparent) 1.15px, transparent 1.3px); background-size: 24px 24px; color: var(--text); font-family: var(--font-ui); font-size: var(--fs-lead); line-height: normal; letter-spacing: -.01em; }
.catk-card-stage { zoom: 1.5; }
/* На телефоне в 320 пикселей увеличенная карточка (282) не помещалась в рамку, и страница уезжала
   вбок на 16 пикселей. Увеличение меньше там, где меньше сама рамка, а не окно. Контейнер меряется
   без своих полей: 281 - на пиксель меньше увеличенной карточки. */
.catk-card { container-type: inline-size; }
@container (max-width: 281px) {
  .catk-card-stage { zoom: 1.25; }
}
.catk-card :where(h3) { margin: 0; color: inherit; font-weight: bold; letter-spacing: -.035em; }
.catk-card :where(strong) { color: inherit; }
.catk-card-legend { display: grid; gap: var(--s-8); width: min(282px, 100%); color: var(--quiet); font-family: var(--font-data); font-size: var(--site-fs-note); letter-spacing: .04em; }
.catk-card-legend p { margin: 0; color: var(--text); font-weight: 600; letter-spacing: .1em; text-transform: uppercase; }
.catk-card-legend ol { display: grid; gap: var(--s-4); margin: 0; padding: 0; list-style: none; }
.catk-card-legend li { display: flex; align-items: center; gap: var(--s-8); margin: 0; }
.catk-card-legend i { flex: none; width: 18px; height: 3px; border-radius: 3px; background: var(--flow-line); }
.catk-card-legend .is-done { color: var(--text); }
.catk-card-legend .is-done i { background: color-mix(in srgb, var(--text) 52%, transparent); }
.shot-stamp { position: absolute; top: var(--s-12); right: var(--s-12); z-index: 1; padding: var(--s-4) var(--s-8); border: 2px solid var(--site-accent); background: var(--site-bg); color: var(--site-accent); font-family: var(--font-data); font-size: var(--site-fs-mark); font-weight: 600; letter-spacing: .14em; line-height: 1.2; text-transform: uppercase; }

.shot-frame::after {
  content: "";
  position: absolute; inset: 0; pointer-events: none;
  /* Умножение затемняет: на светлом негативе оно давало зерно, на самом снимке — черный
     прямоугольник. Наложение работает от середины, поэтому фактура остается видна на темном. */
  opacity: .14; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%27200%27 height=%27200%27%3E%3Cfilter id=%27g%27%3E%3CfeTurbulence type=%27fractalNoise%27 baseFrequency=%270.9%27 numOctaves=%273%27/%3E%3CfeColorMatrix type=%27saturate%27 values=%270%27/%3E%3C/filter%3E%3Crect width=%27200%27 height=%27200%27 filter=%27url(%23g)%27/%3E%3C/svg%3E");
}
.post-cover { margin: var(--s-24) 0 var(--s-32); }
/* Схема по ходу записи (22 октября 2026) лежит ровно, без поворота обложки: ее читают вместе с текстом. */
.post-figure { margin: var(--s-32) 0; }
/* Схема записи - ссылка на свой файл в полную величину (с 3 ноября 2026, разбор сайта): на телефоне
   ее подписи выходили в 4-5 пикселей. Строка «Открыть схему крупно» стоит под схемой на узком окне,
   на широком ее нет; имя ссылки для читалки - aria-label, оно есть на любой ширине. */
.shot-open { display: block; color: inherit; text-decoration: none; }
.shot-open-label { display: block; padding: var(--s-8) var(--s-12); border-top: 1px solid var(--site-line); background: var(--site-panel); color: var(--site-head); font-family: var(--site-display); font-size: var(--site-fs-note); font-weight: 600; }
.shot-open-label::after { content: " ↗" / ""; color: var(--site-muted); }
.shot-open:hover .shot-open-label::after { color: var(--site-accent); }
@media (min-width: 760px) {
  .shot-open-label { display: none; }
}
/**
 * Обложка записи - отчет на листе: корешок слева, обрез страниц справа и снизу, рамки и подложки
 * нет. С 27 сентября до 5 ноября 2026 она стояла книгой в перспективе, повернутой на 9 градусов
 * (прием Perspective Book из разбора Spell UI). Решение автора 5 ноября 2026: поворот искажал
 * картинку, подписи на схеме шли вкось. Обложка лежит ровно, корешок остался.
 */
.post-cover.shot { border: 0; background: none; }
.post-cover .shot-frame { border-left: var(--s-12) solid var(--site-head); border-right: 6px solid var(--site-line-strong); border-bottom: 3px solid var(--site-line-strong); }
.post-cover figcaption { border-top: 0; padding-inline: 0; }
.shot figcaption { display: grid; gap: var(--s-4); padding: var(--s-12) var(--s-16); border-top: 1px solid var(--site-line); color: var(--site-muted); font-size: var(--site-fs-note); line-height: 1.55; }
.figure-tag { color: var(--site-head); }
/* Кадры разной высоты: показываем верх снимка, где стоит сама графика. */
/**
 * Пара снимков режется по пропорции 3:2, а не по высоте 250 пикселей. При высоте 250 карта
 * теряла три точки из пяти: они лежали ниже кромки. На 3:2 кадр карты помещается целиком.
 */
.shot-frame.is-crop { aspect-ratio: 3 / 2; }
.shot-frame.is-crop img { height: 100%; object-fit: cover; object-position: top center; }

/* Пара снимков рядом: на узком экране они встают друг под другом. */
.figure-pair { display: grid; gap: var(--s-16); margin-top: var(--s-24); }
@media (min-width: 760px) { .figure-pair { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/**
 * Указатель направлений: строки с номером на полях, а не четыре карточки в ряд.
 *
 * Карточки читаются как витрина товаров; строка с номером и линейкой — как опись. Второе и есть то,
 * чем занята организация.
 */
.index { margin: 0; padding: 0; list-style: none; border-top: 1px solid var(--site-line); }
.index li { margin: 0; }
.index a { display: grid; gap: var(--s-4) var(--s-24); padding: var(--s-20) 0; border-bottom: 1px solid var(--site-line); color: var(--site-body); text-decoration: none; }
.index a:hover { background: var(--site-panel); }
.index-title { color: var(--site-head); font-family: var(--site-display); font-weight: 600; font-size: var(--site-fs-sub); letter-spacing: -.005em; }
.index-note { color: var(--site-body); font-size: var(--site-fs-body); max-width: 62ch; }
.index-go { color: var(--site-muted); font-family: var(--font-data); }
.index a:hover .index-go { color: var(--site-accent); }

/**
 * Стопка листов: шаги «Как начать» у TXINT. Прием из разбора useLayouts (stack scroll), 27 сентября
 * 2026. Шаг прилипает под шапкой окна, следующий лист ложится на него чуть ниже, и по кромкам видно,
 * сколько шагов уже позади. Это прилипание (sticky), а не анимация: скрипта нет, и просьба уменьшить
 * движение его не касается - лист движется ровно так, как читатель прокручивает страницу.
 *
 * Лист непрозрачный и в рамке: иначе следующий шаг не закрывал бы предыдущий, а просвечивал сквозь
 * него. Сдвиг каждого следующего листа - номер шага, записанный в разметке числом (--i), умноженный
 * на высоту строки заголовка: у лежащего листа видны номер и заголовок целиком. Первая версия
 * сдвигала на 12 пикселей, и заголовок предыдущего шага резался пополам - это читалось как
 * сломанная верстка, а не как стопка. На узком окне стопки нет: шаги идут списком, прилипший лист
 * занимал бы там половину экрана.
 */
.stack { display: grid; gap: var(--s-12); margin: var(--s-24) 0 0; padding: 0; list-style: none; }
/* На телефоне номер стоит над заголовком, а не колонкой слева: колонка с промежутком съедала 56
   пикселей из 285, и текст пятого шага шел в 15 строк (разбор сайта 3 ноября 2026). */
.stack > li { display: grid; gap: var(--s-4) var(--s-16); margin: 0; padding: var(--s-12) var(--s-16) var(--s-20); border: 1px solid var(--site-line-strong); background: var(--site-bg); }
.stack-number { color: var(--site-muted); font-family: var(--font-data); font-size: var(--site-fs-note); line-height: 1.8; }
@media (min-width: 760px) {
  .stack > li { grid-template-columns: var(--s-40) minmax(0, 1fr); padding-inline: var(--s-24); }
  .stack-number { grid-row: span 2; }
}
.stack strong { color: var(--site-head); font-family: var(--site-display); font-size: var(--site-fs-lead); font-weight: 600; line-height: 1.3; text-wrap: balance; }
.stack > li > span:last-child { max-width: 64ch; color: var(--site-body); }
@media (min-width: 900px) {
  .stack { gap: var(--s-48); }
  .stack > li { position: sticky; top: calc(var(--head-room) + var(--s-24) + var(--i) * var(--s-40)); }
}

/**
 * Линейная обвязка указателя: направления читаются как один ряд, а не как пять блоков.
 *
 * Приём тот же, что у цепочки на главной, и язык тот же: чертеж, опись. Вертикаль идет вдоль всего
 * указателя, к каждому пункту от нее отходит короткий отвод. До этого
 * пункты держались только горизонтальными линейками снизу и читались как пять отдельных записей.
 *
 * Номера у пунктов сняты 23 сентября 2026: порядок направлений ничего не значит, а пять рядов
 * номеров на одной странице читались узором. Номера остались у цепочки и у полос.
 */
.index { position: relative; }
.index::before {
  content: "";
  position: absolute;
  left: 7px;
  top: var(--s-16);
  bottom: var(--s-16);
  width: 1px;
  background: linear-gradient(var(--site-line-strong), var(--site-line));
}
.index li { position: relative; }
/**
 * Отвод привязан к заголовку пункта, а не к строке. Пока в строке стоял номер, отвод упирался в
 * него. Без номера высота над заголовком на разных страницах разная (на главной центр заголовка на
 * 41 пикселе от верха строки, на странице CATK на 58), и отвод с одним отступом от строки висел
 * над заголовком. От заголовка он встает по центру первой строки везде: заголовок начинается на 38
 * пикселях, отвод занимает 7-29.
 */
.index-title { position: relative; }
.index-title::before {
  content: "";
  position: absolute;
  right: calc(100% + 9px);
  top: .62em;
  width: 22px;
  height: 1px;
  background: var(--site-line-strong);
  transform-origin: left center;
}
.index a, .index-row { padding-left: 38px; }
@media (min-width: 760px) {
  .index a { grid-template-columns: minmax(0, 210px) minmax(0, 1fr) 28px; align-items: baseline; padding: var(--s-24) var(--s-12) var(--s-24) 38px; }
  .index-go { justify-self: end; }
  /**
   * Строка без пояснения: стрелка идёт следом за именем, а не уезжает к правому краю.
   *
   * На странице сервисов у строк не было пояснения вовсе, и пустая колонка растягивалась на всё
   * свободное место: слово «Обзор» кончалось на 473, стрелка стояла на 1298. Восемьсот пикселей
   * пустоты между словом и его же стрелкой — строка переставала читаться как одна вещь.
   *
   * Линейка при этом по-прежнему идёт во всю ширину: строка списка кончается там же, где кончается
   * лист, и ряд остаётся рядом. Двигаются только колонки внутри неё.
   */
  .index.is-plain a { grid-template-columns: minmax(0, max-content) 28px; justify-content: start; }
}

/* Строка указателя, которая никуда не ведет: шаг «как начать» это не ссылка, а порядок действий. */
.index-row { display: grid; gap: var(--s-4) var(--s-24); padding: var(--s-20) 0 var(--s-20) 38px; border-bottom: 1px solid var(--site-line); }
.index li:last-child a, .index li:last-child .index-row { border-bottom: 0; }
/* Файлы шаблонов под текстом своей строки, в колонке текста (с 3 ноября 2026). */
.index-files .files { margin-top: var(--s-8); }
.index-files .note { margin: var(--s-8) 0 0; }
@media (min-width: 760px) {
  .index-row { grid-template-columns: minmax(0, 240px) minmax(0, 1fr); align-items: baseline; padding: var(--s-24) var(--s-12) var(--s-24) 38px; }
  /* Форматы на «Услугах» (с 5 ноября 2026): строки со ссылкой и без стоят в одном списке, и
     пояснения всех строк начинаются с одной вертикали. */
  .index.is-formats a, .index.is-formats .index-row { grid-template-columns: minmax(0, 240px) minmax(0, 1fr) 28px; }
  .index-files { grid-column: 2; }
}
/**
 * Матрица точек демонстрационного набора CATK у его строки (пункт 4.3 карты сайта, 31 октября 2026;
 * рисует lib/demo-matrix.mjs). Цвета листа: записи - цветом заголовка, связи - приглушенным, сетка -
 * линией листа. На узком окне матрица стоит под текстом строки, на широком - третьей колонкой.
 */
.index-figure { grid-column: 1 / -1; margin-top: var(--s-12); }
.demo-matrix { display: block; width: min(100%, 420px); height: auto; color: var(--site-head); }
.demo-matrix circle { fill: var(--site-line-strong); }
.demo-matrix line { stroke: var(--site-muted); stroke-width: 1.25px; vector-effect: non-scaling-stroke; }
.demo-matrix .record { fill: currentColor; }
/* Третьей колонкой - от ширины цепочки: на 1024 пикселях текст строки сжимался до 42 пикселей
   (нашел обход страниц). Текст строки стоит по верху, как у соседних строк без матрицы. */
@media (min-width: 1240px) {
  .index-row:has(.index-figure) { grid-template-columns: minmax(0, 240px) minmax(0, 1fr) minmax(0, 340px); }
  .index-figure { grid-column: auto; align-self: start; margin-top: 0; }
}

/**
 * Возможность: текст слева, снимок справа.
 *
 * Возможность без снимка не получает пустую рамку, а занимает всю ширину: рамка-заглушка
 * читается как ненайденная картинка. Это уже проходили на «Экспертах» с пустыми аватарами.
 */
.features { display: grid; margin: 0; padding: 0; list-style: none; }
.feature { margin: 0; display: grid; gap: var(--s-20); padding: var(--s-32) 0; border-top: 1px solid var(--site-line); }
.feature:first-child { border-top: 0; padding-top: var(--s-12); }
/* Живая карточка CATK (31 октября 2026) стоит внутри пункта, и правила его текста весят больше утилит
   продукта: имя записи вырастало до кегля подзаголовка и обрезалось. Карточка из них исключена. */
.feature h3:not(.catk-card h3) { margin: 0 0 var(--s-12); font-size: var(--site-fs-sub); }
.feature p:not(.catk-card p) { margin: 0 0 var(--s-12); font-size: var(--site-fs-body); max-width: 62ch; }
.feature p:not(.catk-card p):last-child { margin-bottom: 0; }
/* Текст стоит по верху снимка. До 3 ноября 2026 он стоял по середине, и рядом с высоким снимком
   под заголовком полосы оставалось около 200 пикселей пустоты. */
@media (min-width: 900px) {
  .feature { grid-template-columns: minmax(0, .9fr) minmax(0, 1fr); gap: var(--s-40); align-items: start; }
  .feature.is-plain { grid-template-columns: minmax(0, 66ch); }
  /* Пара снимков занимает всю ширину: в половине строки они ужимаются до нечитаемости. */
  .feature.is-wide { grid-template-columns: minmax(0, 1fr); gap: var(--s-4); }
}

/**
 * Паспорт сервиса на «Продуктах». Досье на столе, 30 сентября 2026: описание и три поля слева,
 * снимок в окне справа, указатель страниц под ними. Поля набраны строками бланка: имя поля шрифтом
 * данных, значение прозой, волосяная линия под каждой строкой. На узком окне снимок уходит под
 * паспорт, а не ужимается рядом с ним.
 */
.passport { display: grid; gap: var(--s-32); margin: 0 0 var(--s-40); }
.passport-text .lead { margin: 0 0 var(--s-24); }
.passport-fields { margin: 0; border-top: 1px solid var(--site-line-strong); }
.passport-fields div { display: grid; gap: var(--s-4); padding: var(--s-12) 0; border-bottom: 1px solid var(--site-line); }
.passport-fields dt { color: var(--site-muted); }
.passport-fields dd { margin: 0; color: var(--site-body); font-size: var(--site-fs-body); max-width: 62ch; }
@media (min-width: 1000px) {
  .passport { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s-40); align-items: start; }
  .passport-fields div { grid-template-columns: 112px minmax(0, 1fr); gap: var(--s-16); align-items: baseline; }
}

/* Файлы для скачивания: подписаны моно, потому что это имена файлов, а не проза. */
.files { display: flex; flex-wrap: wrap; gap: var(--s-8) var(--s-24); margin: var(--s-24) 0 0; padding: 0; list-style: none; }
.files li { margin: 0; }
.files a { display: inline-flex; gap: var(--s-8); align-items: baseline; padding: var(--s-4) 0; color: var(--site-head); font-size: var(--site-fs-body); text-decoration: underline; text-decoration-color: var(--site-line-strong); text-underline-offset: 3px; }
.files a::after { content: "↓"; color: var(--site-muted); font-family: var(--font-data); }
.files a:hover { text-decoration-color: var(--site-accent); }

/* Вывод полосы: одна фраза, ради которой полоса и написана. */
.claim { margin: var(--s-24) 0 0; max-width: 60ch; color: var(--site-head); font-size: var(--site-fs-lead); }

/**
 * Столбцы: три равные вещи рядом, ни одна не первая.
 *
 * Замечание автора 10 сентября 2026: «все сливается». Волосяная линейка между столбцами не
 * отделяла уровни друг от друга и от границ под ними — заголовок, пояснение и линейка были одного
 * тона. Тогда у каждого столбца появилась плашка с рамкой и градиентом.
 *
 * **11 сентября 2026 плашка снята, и разделение осталось.** Прямоугольник в рамке с заливкой —
 * самая частая форма, какая бывает на сайте, и она стояла на трёх страницах подряд: столбец
 * читался как готовый элемент чужого набора, а не как часть этого листа. Отделяет теперь линейка
 * в два пикселя над каждым столбцом: она вдвое толще линии раздела, стоит на цвете линии сильной
 * и промахнуться мимо неё нельзя — то есть жалоба «все сливается» закрыта тем же способом, каким
 * на сайте сделано всё остальное. Заголовок столбца набран заглавной гарнитурой и пронумерован
 * моно: номер говорит, что уровни идут по возрастанию.
 *
 * Пары (is-pairs) стоят перечнем с линейкой слева: тот же прием, повернутый на четверть оборота.
 * До 24 сентября 2026 так набирались границы «чего в работу не входит», теперь полоса «Как
 * устроена работа». Красного здесь нет: он цвет риска, а не оформления.
 *
 * Правило align-content: start у ячейки обязательно, как у карточек метода. Ячейки в ряду равны по высоте,
 * и без него лишняя высота делится между строкой имени и строкой текста: текст соседних ячеек
 * начинался на разной высоте, в составе услуг с разницей до 24 пикселей.
 */
.columns { display: grid; gap: var(--s-12); margin: 0; padding: 0; list-style: none; }
.columns li { margin: 0; display: grid; align-content: start; gap: var(--s-8); padding: var(--s-16) 0 var(--s-20); border-top: 2px solid var(--site-line-strong); }
.columns strong { font-family: var(--site-display); font-size: var(--site-fs-lead); letter-spacing: -.01em; }
.columns span { color: var(--site-body); font-size: var(--site-fs-body); }
.columns.is-pairs { gap: 0; }
/* Линия пары доходит до нижнего края колонки, и абзац под ней сливался с линией (замечание автора
   28 октября 2026). Отступ под парой равен отступу абзаца снизу. */
.columns.is-pairs:not(:last-child) { margin-bottom: var(--s-16); }
.columns.is-pairs li { padding: var(--s-12) 0 var(--s-12) var(--s-20); background: none; border: 0; border-left: 2px solid var(--site-line-strong); }
.columns.is-pairs strong { font-family: var(--font-ui); font-size: var(--site-fs-body); letter-spacing: 0; }
.columns.is-pairs strong::before { display: none; }
/* Образцы инструментов на главной, 23 октября 2026: два столбца под двумя описаниями, с тем же
   зазором, что у колонок над ними. Столбцы делят ширину поровну и по содержимому не растут, иначе
   широкий снимок раздвигал бы свой. Открытый холст CATK занимает оба, кадр TXINT уходит под него. */
.tool-shots { display: grid; gap: var(--s-24); align-items: start; }
@media (min-width: 820px) {
  .columns { grid-template-columns: repeat(3, 1fr); }
  /* Четыре вещи встают в два ряда по две: три столбца оставили бы пустую ячейку. */
  .columns.is-pairs { grid-template-columns: repeat(2, 1fr); gap: var(--s-4) var(--s-32); }
  .tool-shots { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-24) var(--s-32); }
  .tool-shots:has(.is-live) > .catk-live { grid-column: 1 / -1; }
}

/**
 * Карточки организационной модели (20 октября 2026): серая плашка, синий номер, имя и строка
 * пояснения. Номер рисует счетчик списка, а не разметка: пункты уже идут по порядку, и второй ряд
 * чисел в тексте разошелся бы с первым при первой вставке. Пять задач лаборатории встают в пять
 * колонок на широком окне, остальные карточки в три.
 *
 * Полоса сверху синяя, тем же синим листа, что и номер. 5 ноября 2026 полосу по аудиту unslop
 * делали серой, и автор вернул синий: без него сайт стал почти серым. Светлый синий обложек и
 * «Чернила» в тот же день не прижились, и полоса снова исходного синего.
 */
.columns.is-cards { gap: var(--s-12); margin-bottom: var(--s-16); counter-reset: card; }
.columns.is-cards li { gap: var(--s-8); padding: var(--s-16) var(--s-20) var(--s-20); border-top: 3px solid var(--site-blue); background: var(--site-panel); counter-increment: card; }
/* Номер стоит только там, где порядок что-то значит (is-numbered): пять задач лаборатории нумерует сам
   документ. У ролей, уровней уверенности, форматов и программ порядка нет, и номер читался бы
   узором (замечание контрольного прохода 21 октября 2026). */
.columns.is-cards.is-numbered li::before { content: counter(card, decimal-leading-zero); color: var(--site-blue); font-family: var(--font-data); font-size: var(--site-fs-mark); font-weight: 600; letter-spacing: .12em; }
.columns.is-cards strong { font-size: var(--site-fs-body); font-weight: 600; letter-spacing: 0; line-height: 1.3; }
.columns.is-cards span { font-size: var(--site-fs-note); line-height: 1.5; }
/* Строка «для кого» у формата услуги и «результат» у направления на «Организации»: ярлык данными,
   значение прозой. */
.card-for { display: block; margin-top: var(--s-4); color: var(--site-muted); font-size: var(--site-fs-note); line-height: 1.45; }
.card-for span { font-family: var(--font-data); font-size: var(--site-fs-mark); font-weight: 600; letter-spacing: .1em; text-transform: uppercase; }
@media (min-width: 1240px) {
  .columns.is-five { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}
/* Четыре направления ядра (с 22 октября 2026): две по две, на широком окне четыре в ряд. Три
   столбца оставили бы одну карточку во втором ряду. */
@media (min-width: 820px) {
  .columns.is-four { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1240px) {
  .columns.is-four { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* Перечень со стрелкой-маркером, как в документе модели. Стрелка синяя: порядок, а не внимание. */
.arrow-list { display: grid; gap: var(--s-8); margin: 0 0 var(--s-16); padding: 0; list-style: none; }
.arrow-list li { position: relative; margin: 0; padding-left: var(--s-24); max-width: 64ch; color: var(--site-body); }
/* Стрелки на сайте темные, цвета рамок схем (поправка автора 5 ноября 2026): синие выбивались. */
.arrow-list li::before { content: "→"; position: absolute; left: 0; color: var(--site-muted); font-family: var(--font-data); }

/**
 * Словарь лаборатории (23 октября 2026): термин слева, определение и ссылка справа, линейка между
 * терминами. На узком экране термин стоит над определением.
 */
.glossary { margin: 0; padding: 0; }
.glossary-term { display: grid; gap: var(--s-4) var(--s-24); padding: var(--s-16) 0; border-top: 1px solid var(--site-line); scroll-margin-top: var(--head-room); }
.glossary-term dt { color: var(--site-head); font-size: var(--site-fs-lead); font-weight: 600; line-height: 1.3; }
.glossary-term dd { margin: 0; display: grid; gap: var(--s-8); justify-items: start; }
.glossary-term dd p { margin: 0; max-width: 64ch; color: var(--site-body); }
@media (min-width: 820px) {
  .glossary-term { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }
}

/**
 * Выделенное правило. В документе модели красной рамкой стоят «Граница модели» и
 * «Методологический запрет»: правило, которое работа не нарушает. Красный здесь законен - он
 * говорит «здесь граница», то есть ровно то, для чего он и заведен.
 */
.callout { margin: var(--s-24) 0; padding: var(--s-16) var(--s-20); border-left: 3px solid var(--site-accent); background: color-mix(in srgb, var(--site-accent) 7%, var(--site-bg)); }
.callout p { margin: 0; max-width: 68ch; color: var(--site-head); }
.callout .callout-title { margin: 0 0 var(--s-8); color: var(--site-accent); font-family: var(--font-data); font-size: var(--site-fs-mark); font-weight: 600; letter-spacing: .12em; text-transform: uppercase; }

/* Программы исследований на главной: имена квадратными метками, без пояснений. */
.tags { display: flex; flex-wrap: wrap; gap: var(--s-8); margin: var(--s-16) 0 0; padding: 0; list-style: none; }
.tags li { margin: 0; padding: var(--s-4) var(--s-12); border: 1px solid var(--site-line-strong); color: var(--site-head); font-size: var(--site-fs-note); }

/**
 * Аналитический цикл: девять шагов, два из них - шлюзы. Схема 3 организационной модели нарисована
 * разметкой: шаг - рамка с синим номером, шлюз - красная рамка и слово «Шлюз». На широком окне шаги
 * встают сеткой три на три и читаются строками слева направо.
 */
.cycle { display: grid; gap: var(--s-12); margin: var(--s-8) 0 var(--s-16); padding: 0; list-style: none; counter-reset: step; }
.cycle li { position: relative; display: grid; align-content: start; gap: var(--s-4); margin: 0; padding: var(--s-12) var(--s-16) var(--s-16); border: 1px solid var(--site-line-strong); background: var(--site-bg); counter-increment: step; }
.cycle li::before { content: counter(step, decimal-leading-zero); color: var(--site-blue); font-family: var(--font-data); font-size: var(--site-fs-mark); font-weight: 600; letter-spacing: .12em; }
.cycle strong { font-size: var(--site-fs-body); font-weight: 600; line-height: 1.3; }
.cycle span { color: var(--site-body); font-size: var(--site-fs-note); line-height: 1.5; }
/* Вторая линия рамки шлюза - тенью внутрь: рамка в два пикселя сдвигала имя шага на пиксель
   относительно соседей, и это поймал сценарий карточек. */
.cycle .is-gate { border-color: var(--site-accent); box-shadow: inset 0 0 0 1px var(--site-accent); }
.cycle .gate-tag { position: absolute; top: var(--s-12); right: var(--s-16); color: var(--site-accent); font-family: var(--font-data); font-size: var(--site-fs-mark); font-weight: 600; letter-spacing: .12em; text-transform: uppercase; }
.cycle.is-compact li { padding: var(--s-12) var(--s-16); }
/**
 * Публикации у карточки шага (пункт 3.2 карты сайта, 31 октября 2026): под чертой, номер записи и ее
 * имя ссылкой. Правила карточки (.cycle li, .cycle span) задели бы и строки этого списка - рамкой,
 * отступами и номером шага, - поэтому здесь они сняты.
 */
.step-posts { margin-top: var(--s-8); padding-top: var(--s-8); border-top: 1px solid var(--site-line); }
.step-posts small { color: var(--site-muted); font-family: var(--font-data); font-size: var(--site-fs-mark); font-weight: 600; letter-spacing: .12em; text-transform: uppercase; }
.step-posts ul { display: grid; gap: var(--s-4); margin: var(--s-4) 0 0; padding: 0; list-style: none; }
.cycle .step-posts li { display: block; padding: 0; border: 0; background: none; counter-increment: none; font-size: var(--site-fs-note); line-height: 1.4; }
.cycle .step-posts li::before { content: none; }
.step-posts a { color: var(--site-body); text-decoration: none; }
.step-posts a:hover { color: var(--site-head); text-decoration: underline; text-decoration-color: var(--site-accent); text-underline-offset: 4px; }
.cycle .step-posts .post-number { margin-right: var(--s-8); color: var(--site-head); font-family: var(--font-data); font-size: inherit; font-weight: 600; letter-spacing: .04em; }
@media (min-width: 760px) {
  .cycle { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/**
 * Схема цикла над карточками «Методологии» - образец схем этапа 2 карты сайта (29 октября 2026).
 *
 * Карточки встают сеткой три на три и читаются строками, а цикл - это одна линия с возвратами.
 * Схема показывает то, что карточки говорят только словами: порядок одной линией, шлюзы залитыми
 * квадратами, а под линией отказ на первом шлюзе, возврат на доработку и замыкание цикла.
 *
 * Рисунок по правилам набора G: линия 2, прямые углы, один цвет. Цвет приглушенный, а не цвет
 * заголовка: схема поясняет карточки и не должна спорить с ними за взгляд. Номер шага синий, как у
 * карточки, - по нему схема и карточка находят друг друга.
 *
 * Геометрия считается от числа шагов (--steps): шаг занимает свою долю ширины, центр шага - середина
 * доли, и возврат идет от центра к центру. Пиксели здесь только у толщины линии и размера метки.
 * Возвраты уходят вниз ступенями (--rung): ступень выше подписи нижнего возврата, поэтому подпись
 * верхнего на линию нижнего не ложится.
 */
.cycle-scheme { --rung: 34px; margin: var(--s-24) 0 var(--s-20); color: var(--site-muted); font-family: var(--font-data); font-size: var(--site-fs-mark); font-weight: 600; letter-spacing: .1em; line-height: 1.2; text-transform: uppercase; }
.scheme-track { position: relative; display: grid; grid-template-columns: repeat(var(--steps), minmax(0, 1fr)); }
.scheme-track::before { content: ""; position: absolute; left: calc(50% / var(--steps)); right: calc(50% / var(--steps)); bottom: 9px; border-top: 2px solid currentColor; transform-origin: left; }
.scheme-step { display: grid; grid-template-rows: auto auto 20px; justify-items: center; gap: var(--s-4); min-width: 0; text-align: center; }
.scheme-step b { grid-row: 1; color: var(--site-blue); font-weight: 600; }
/* На узком экране слово шага не помещается в долю ширины, и остается номер: его ищут на карточке. */
.scheme-step > span { display: none; grid-row: 2; max-width: 100%; }
/* Строка метки названа явно: без слова метка поднималась во вторую строку, а линия оставалась в третьей. */
.scheme-step i { grid-row: 3; position: relative; z-index: 1; place-self: center; width: 12px; height: 12px; border: 2px solid currentColor; background: var(--site-bg); }
.scheme-step.is-gate i { width: 16px; height: 16px; background: currentColor; }
.scheme-paths { position: relative; height: calc(var(--levels) * var(--rung) + var(--s-20)); }
/* Линия выхода и возврата начинается в центре метки: метка стоит выше и закрывает ее начало. */
.scheme-exit, .scheme-back { position: absolute; top: -10px; height: calc(var(--level) * var(--rung) + 10px); }
.scheme-exit { left: calc((var(--at) - .5) / var(--steps) * 100% - 1px); width: 2px; }
.scheme-back { left: calc((var(--to) - .5) / var(--steps) * 100% - 1px); width: calc((var(--from) - var(--to)) / var(--steps) * 100% + 2px); }
.scheme-exit > i, .scheme-back > i { position: absolute; inset: 0; }
.scheme-exit > i { border-left: 2px solid currentColor; }
/* Выход упирается в черту: работа дальше не идет. */
.scheme-exit > i::after { content: ""; position: absolute; left: -7px; bottom: 0; width: 12px; border-top: 2px solid currentColor; }
.scheme-back > i { border: 2px solid currentColor; border-top: 0; }
/* Наконечник у шага, куда работа возвращается, и смотрит в него. */
.scheme-back > i::before { content: ""; position: absolute; left: -1px; top: 10px; width: 9px; height: 9px; border-top: 2px solid currentColor; border-left: 2px solid currentColor; transform: translateX(-50%) rotate(45deg); }
.scheme-exit > span, .scheme-back > span { position: absolute; top: 100%; margin-top: var(--s-4); color: var(--site-body); white-space: nowrap; transform: translateX(-50%); }
.scheme-exit > span { left: 1px; }
.scheme-back > span { left: 50%; }
/**
 * Шаг выделяется с обеих сторон: наведение на карточку зажигает шаг схемы, наведение на шаг схемы -
 * карточку. Образец - вкладки Janes, только без вкладок: все карточки и так на виду, выделение
 * показывает, где шаг стоит в цикле. Фокуса здесь нет: карточки - текст, а не ссылки, и девять пустых
 * остановок обхода только удлинили бы путь с клавиатуры. Правила по номеру шага собираются из
 * данных страницы, поэтому десятый шаг получит свое правило сам.
 */
.cycle-scheme ~ .cycle > li:hover { background: var(--site-panel); }
.scheme-step:hover { color: var(--site-head); }
.band:has(.cycle > li:nth-child(1):hover) .scheme-step:nth-child(1) { color: var(--site-head); }
.band:has(.scheme-step:nth-child(1):hover) .cycle > li:nth-child(1) { background: var(--site-panel); }
.band:has(.cycle > li:nth-child(2):hover) .scheme-step:nth-child(2) { color: var(--site-head); }
.band:has(.scheme-step:nth-child(2):hover) .cycle > li:nth-child(2) { background: var(--site-panel); }
.band:has(.cycle > li:nth-child(3):hover) .scheme-step:nth-child(3) { color: var(--site-head); }
.band:has(.scheme-step:nth-child(3):hover) .cycle > li:nth-child(3) { background: var(--site-panel); }
.band:has(.cycle > li:nth-child(4):hover) .scheme-step:nth-child(4) { color: var(--site-head); }
.band:has(.scheme-step:nth-child(4):hover) .cycle > li:nth-child(4) { background: var(--site-panel); }
.band:has(.cycle > li:nth-child(5):hover) .scheme-step:nth-child(5) { color: var(--site-head); }
.band:has(.scheme-step:nth-child(5):hover) .cycle > li:nth-child(5) { background: var(--site-panel); }
.band:has(.cycle > li:nth-child(6):hover) .scheme-step:nth-child(6) { color: var(--site-head); }
.band:has(.scheme-step:nth-child(6):hover) .cycle > li:nth-child(6) { background: var(--site-panel); }
.band:has(.cycle > li:nth-child(7):hover) .scheme-step:nth-child(7) { color: var(--site-head); }
.band:has(.scheme-step:nth-child(7):hover) .cycle > li:nth-child(7) { background: var(--site-panel); }
.band:has(.cycle > li:nth-child(8):hover) .scheme-step:nth-child(8) { color: var(--site-head); }
.band:has(.scheme-step:nth-child(8):hover) .cycle > li:nth-child(8) { background: var(--site-panel); }
.band:has(.cycle > li:nth-child(9):hover) .scheme-step:nth-child(9) { color: var(--site-head); }
.band:has(.scheme-step:nth-child(9):hover) .cycle > li:nth-child(9) { background: var(--site-panel); }
@media (min-width: 1240px) {
  .scheme-step > span { display: block; }
}
/**
 * Прочерк по прокрутке, тем же таймлайном, что у всей схемы: сперва линия слева направо, за ней
 * выход вниз и возвраты справа налево - в ту сторону, куда идет работа. Подпись проявляется, когда
 * ее линия дочерчена. При «меньше движения» и без поддержки схема стоит готовой.
 */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .cycle-scheme { view-timeline-name: --cycle-scheme; }
    .scheme-track::before, .scheme-exit > i, .scheme-back > i, .scheme-exit > span, .scheme-back > span { animation-duration: auto; animation-timing-function: linear; animation-fill-mode: both; animation-timeline: --cycle-scheme; }
    .scheme-track::before { animation-name: scheme-line; animation-range: cover 16% cover 32%; }
    .scheme-exit > i { animation-name: scheme-drop; animation-range: cover 28% cover 34%; }
    .scheme-back > i { animation-name: scheme-return; animation-range: cover 30% cover 40%; }
    .scheme-exit > span, .scheme-back > span { animation-name: scheme-label; animation-range: cover 38% cover 42%; }
  }
}
@keyframes scheme-line { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes scheme-drop { from { clip-path: inset(0 -12px 100% -12px); } to { clip-path: inset(0 -12px 0 -12px); } }
@keyframes scheme-return { from { clip-path: inset(-12px 0 0 100%); } to { clip-path: inset(-12px -12px 0 -12px); } }
@keyframes scheme-label { from { opacity: 0; } to { opacity: 1; } }

/**
 * Схема устройства на «Организации» (этап 2 карты сайта, 31 октября 2026): ядро и экспертная сеть
 * собирают проектную команду, команда готовит варианты решений, решение принимает заказчик.
 *
 * Рамка пунктиром обводит лабораторию, заказчик стоит за ней: так схема говорит, кто решает. Знак у
 * направления ядра - знак его руководителя на той же черной плашке, что у карточек «Экспертов».
 * Линии и рамки одного приглушенного цвета, как у схемы цикла; названия набраны цветом заголовка.
 *
 * На узком экране схема идет столбцом сверху вниз и не шире 560 пикселей: растянутые во всю колонку
 * планшета рамки с одной строкой текста читаются пустыми. Строкой она встает там же, где слова шагов
 * у схемы цикла: уже ящику команды не хватает места на две строки.
 */
.org-scheme { display: grid; max-width: 560px; margin: var(--s-24) 0 var(--s-8); color: var(--site-muted); }
.org-lab { position: relative; display: grid; padding: var(--s-24) var(--s-16) var(--s-16); border: 2px dashed currentColor; }
.org-lab-label { position: absolute; top: -9px; left: var(--s-12); margin: 0; padding: 0 var(--s-8); background: var(--site-bg); font-family: var(--font-data); font-size: var(--site-fs-mark); font-weight: 600; letter-spacing: .1em; line-height: 1.4; text-transform: uppercase; }
.org-sources { display: grid; gap: var(--s-8); }
/* Ядро и сеть собирают команду вместе: знак сложения между ними, иначе в столбце сеть читалась бы
   следующим шагом после ядра. */
.org-plus { position: relative; justify-self: center; width: 12px; height: 12px; }
.org-plus::before, .org-plus::after { content: ""; position: absolute; background: currentColor; }
.org-plus::before { top: 5px; left: 0; width: 12px; height: 2px; }
.org-plus::after { top: 0; left: 5px; width: 2px; height: 12px; }
.org-box { display: grid; align-content: start; gap: var(--s-4); min-width: 0; padding: var(--s-12) var(--s-16); border: 2px solid currentColor; background: var(--site-bg); }
.org-box b { color: var(--site-head); font-size: var(--site-fs-body); font-weight: 600; line-height: 1.3; }
.org-box span { color: var(--site-body); font-size: var(--site-fs-note); line-height: 1.4; }
.org-box ul { display: grid; gap: var(--s-8); margin: var(--s-8) 0 0; padding: 0; list-style: none; }
.org-box li { display: flex; align-items: center; gap: var(--s-12); margin: 0; }
/* Плашка - элемент i, а не span: правило подписей ящика (.org-box span) весит больше одного класса и
   перекрасило бы знак в цвет текста на темной плашке. */
.org-mark { display: grid; place-items: center; flex: none; width: 28px; height: 28px; border-radius: 50%; background: var(--site-head); color: var(--site-bg); }
.org-mark .role-mark { width: 18px; height: 18px; }
/* Стрелка цвета рамок схемы. 5 ноября 2026 ее на несколько часов делали синей, и автор вернул: синие
   стрелки выбивались из общего стиля. */
.org-arrow { position: relative; justify-self: center; width: 2px; height: var(--s-24); margin: var(--s-4) 0 var(--s-8); background: currentColor; }
/* Наконечник смотрит туда, куда идет работа: вниз в столбце, вправо в строке. */
.org-arrow::after { content: ""; position: absolute; left: 50%; bottom: 0; width: 8px; height: 8px; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: translateX(-50%) rotate(45deg); }
@media (min-width: 1240px) {
  .org-scheme { max-width: none; grid-template-columns: minmax(0, 1fr) 56px minmax(150px, 190px); align-items: center; }
  .org-lab { grid-template-columns: minmax(0, 1.5fr) 56px minmax(0, 1fr) 56px minmax(0, 1fr); align-items: center; }
  .org-arrow { justify-self: stretch; width: auto; height: 2px; margin: 0 var(--s-8); }
  .org-arrow::after { top: 50%; right: 0; bottom: auto; left: auto; border-top: 2px solid currentColor; border-bottom: 0; transform: translateY(-50%) rotate(45deg); }
  /* Поле рамки снизу равно полю сверху: при 24 сверху и 16 снизу ящики внутри рамки стояли на 4
     пикселя ниже ее середины, и заказчик за рамкой выходил выше соседей (разбор сайта 3 ноября 2026).
     С общей строкой ниже это уже не важно; поле нужно браузеру без subgrid. */
  .org-lab { padding-bottom: var(--s-24); }
}
/**
 * Команда, варианты решений и заказчик - одной высоты, в одной строке. Ящики стоят в двух разных
 * сетках, рамки и схемы, поэтому рамка берет строки у схемы (subgrid): у обеих сеток три строки, ядро
 * с сетью занимает все три, а три ящика и стрелки - среднюю. По-английски текст команды длиннее, и до
 * 3 ноября 2026 ее ящик был выше соседних на 18 пикселей. Без subgrid ящики стоят, как раньше, по
 * середине, каждый своей высоты.
 */
@supports (grid-template-rows: subgrid) {
  @media (min-width: 1240px) {
    .org-scheme { grid-template-rows: 1fr auto 1fr; }
    .org-lab { grid-row: 1 / -1; grid-template-rows: subgrid; }
    .org-sources { grid-row: 1 / -1; }
    .org-lab > .org-box, .org-scheme > .org-box { grid-row: 2; align-self: stretch; }
    .org-lab > .org-arrow, .org-scheme > .org-arrow { grid-row: 2; }
  }
}
/* Прочерк по прокрутке: стрелки по порядку работы. При «меньше движения» и без поддержки стоят готовыми. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .org-scheme { view-timeline-name: --org-scheme; }
    .org-arrow { animation: scheme-reach-down linear both; animation-timeline: --org-scheme; }
    .org-arrow[data-step="1"] { animation-range: cover 20% cover 28%; }
    .org-arrow[data-step="2"] { animation-range: cover 28% cover 36%; }
    .org-arrow[data-step="3"] { animation-range: cover 36% cover 44%; }
    @media (min-width: 1240px) {
      .org-arrow { animation-name: scheme-reach; }
    }
  }
}
@keyframes scheme-reach { from { clip-path: inset(-8px 100% -8px 0); } to { clip-path: inset(-8px -8px -8px 0); } }
@keyframes scheme-reach-down { from { clip-path: inset(0 -8px 100% -8px); } to { clip-path: inset(0 -8px -8px -8px); } }

/**
 * Схема горизонтов на «Исследованиях» (этап 2 карты сайта, 31 октября 2026): три горизонта таблицы на
 * одной оси времени, под ними скобками направления, которые их ведут, и от каждого направления
 * двойная линия к общему реестру слабых сигналов.
 *
 * Горизонт занимает свою долю ширины (--horizons), черта на оси стоит в начале доли, имя и период
 * горизонта - сразу за чертой. Скобка направления встает в те же доли, что и его горизонты, поэтому
 * ее края приходятся на черты оси. Цвет линий приглушенный, как у остальных схем; имена набраны цветом
 * заголовка.
 */
.horizon-scheme { margin: var(--s-24) 0 var(--s-8); color: var(--site-muted); }
.horizon-axis { position: relative; display: grid; grid-template-columns: repeat(var(--horizons), minmax(0, 1fr)); margin: 0; padding: 16px 0 0; list-style: none; }
.horizon-axis::before { content: ""; position: absolute; top: 7px; left: 0; right: 1px; border-top: 2px solid currentColor; transform-origin: left; }
/* Наконечник в конце оси: время идет вправо. */
.horizon-axis::after { content: ""; position: absolute; top: 8px; right: 1px; width: 9px; height: 9px; border-top: 2px solid currentColor; border-right: 2px solid currentColor; transform: translateY(-50%) rotate(45deg); }
.horizon-axis li { position: relative; display: grid; align-content: start; gap: 2px; min-width: 0; margin: 0; padding: var(--s-4) var(--s-8) 0 var(--s-8); }
.horizon-axis li::before { content: ""; position: absolute; top: -15px; left: 0; width: 2px; height: 14px; background: currentColor; }
/* На телефоне доля горизонта около 110 пикселей, и «Стратегический» кеглем текста подходил к черте соседа вплотную. */
.horizon-axis b { color: var(--site-head); font-family: var(--font-data); font-size: var(--site-fs-note); font-weight: 600; line-height: 1.25; }
@media (min-width: 760px) {
  .horizon-axis b { font-size: var(--site-fs-body); }
}
.horizon-axis span { color: var(--site-body); font-family: var(--font-data); font-size: var(--site-fs-note); line-height: 1.3; }
.horizon-owners { display: grid; grid-template-columns: repeat(var(--horizons), minmax(0, 1fr)); margin-top: var(--s-12); }
.horizon-owner { display: grid; grid-template-rows: auto auto minmax(var(--s-24), 1fr); justify-items: center; gap: var(--s-8); min-width: 0; text-align: center; }
.horizon-bracket { justify-self: stretch; height: 10px; margin: 0 var(--s-4); border: 2px solid currentColor; border-top: 0; }
.horizon-owner b { padding: 0 var(--s-8); color: var(--site-head); font-size: var(--site-fs-note); font-weight: 600; line-height: 1.3; }
/* Линия к реестру с наконечниками на обоих концах: сигналы идут в реестр и из него. */
.horizon-link { position: relative; width: 2px; background: currentColor; }
.horizon-link::before, .horizon-link::after { content: ""; position: absolute; left: 50%; width: 8px; height: 8px; border: 0 solid currentColor; }
.horizon-link::before { top: 0; border-top-width: 2px; border-left-width: 2px; transform: translateX(-50%) rotate(45deg); }
.horizon-link::after { bottom: 0; border-right-width: 2px; border-bottom-width: 2px; transform: translateX(-50%) rotate(45deg); }
/* Имя схемы в селекторе: правило абзацев полосы (.band p) весит больше одного класса и сузило бы рамку до 64 знаков.
   Рамка приглушенная, как остальные линии схемы, текст цвета заголовка. */
.horizon-scheme .horizon-register { max-width: none; margin: 0; padding: var(--s-12) var(--s-16); border: 2px solid var(--site-muted); color: var(--site-head); font-size: var(--site-fs-note); font-weight: 600; line-height: 1.3; text-align: center; }
/* Прочерк по прокрутке: ось слева направо, за ней линии к реестру сверху вниз. При «меньше движения» и без поддержки схема стоит готовой. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .horizon-scheme { view-timeline-name: --horizon-scheme; }
    .horizon-axis::before, .horizon-axis::after, .horizon-link { animation-duration: auto; animation-timing-function: linear; animation-fill-mode: both; animation-timeline: --horizon-scheme; }
    .horizon-axis::before { animation-name: scheme-line; animation-range: cover 16% cover 30%; }
    .horizon-axis::after { animation-name: scheme-label; animation-range: cover 29% cover 31%; }
    .horizon-link { animation-name: scheme-drop; animation-range: cover 30% cover 38%; }
  }
}

/**
 * Схема независимости выводов на «Беспристрастности» (этап 2 карты сайта, 31 октября 2026). В рамке
 * лаборатории цепочка: доказательства, выводы, анализ и варианты решений. Под рамкой заказчик,
 * который принимает решение. Сбоку за рамкой те, кто на выводы не влияет: их линия упирается в рамку
 * чертой на уровне выводов, как выход у схемы цикла.
 *
 * Сетка из трех столбцов: цепочка, линия, те, кто не влияет. Рамка - отдельный элемент в тех же
 * строках, что и цепочка, поэтому ящики стоят внутри нее без вложенности. Список тех, кто не влияет,
 * занимает строки от первой стрелки до второй и стоит по их середине, то есть на уровне выводов.
 * До 3 ноября 2026 он стоял в строке выводов, и на телефоне ящик выводов вытягивался под него на
 * 112 пикселей почти пустым.
 *
 * Схема не шире 600 пикселей: колонка у страницы-документа узкая. Ни один столбец не уже своего
 * самого длинного слова, поэтому на телефоне «Доказательства» не упираются в рамку, а остаток
 * ширины столбцы делят сами. Подпись «Не влияют» стоит в потоке, над линией в строке нулевой
 * высоты: так ее ширина задает ширину столбца линии, а на высоту строки не влияет. Уже 300 пикселей
 * схемы (телефон в 320) поля ящиков сжимаются: иначе три столбца в ширину не входят.
 */
.ind-scheme { display: grid; grid-template-columns: minmax(min-content, 1fr) minmax(min-content, 56px) minmax(min-content, 1fr); max-width: 600px; margin: var(--s-24) 0 var(--s-32); color: var(--site-muted); container-type: inline-size; }
.ind-lab { grid-column: 1; grid-row: 1 / 6; position: relative; border: 2px dashed currentColor; }
.ind-lab > span { position: absolute; top: -9px; left: var(--s-12); padding: 0 var(--s-8); background: var(--site-bg); font-family: var(--font-data); font-size: var(--site-fs-mark); font-weight: 600; letter-spacing: .1em; line-height: 1.4; text-transform: uppercase; }
.ind-box { position: relative; z-index: 1; display: grid; align-content: start; gap: var(--s-4); padding: var(--s-8); border: 2px solid currentColor; background: var(--site-bg); }
.ind-box b { color: var(--site-head); font-size: var(--site-fs-note); font-weight: 600; line-height: 1.3; }
.ind-box span { color: var(--site-body); font-size: var(--site-fs-note); line-height: 1.4; }
.ind-evidence, .ind-conclusions, .ind-options { grid-column: 1; margin: 0 var(--s-8); }
.ind-evidence { grid-row: 1; margin-top: var(--s-24); }
.ind-conclusions { grid-row: 3; }
.ind-options { grid-row: 5; margin-bottom: var(--s-16); }
.ind-client { grid-column: 1; grid-row: 7; }
.ind-outside { grid-column: 3; grid-row: 2 / 5; align-self: center; }
.ind-arrow { grid-column: 1; position: relative; justify-self: center; width: 2px; height: var(--s-24); margin: var(--s-4) 0 var(--s-8); background: currentColor; }
.ind-arrow[data-step="1"] { grid-row: 2; }
.ind-arrow[data-step="2"] { grid-row: 4; }
/* Третья стрелка начинается от рамки: варианты решений выходят из лаборатории к заказчику. */
.ind-arrow[data-step="3"] { grid-row: 6; margin-top: 0; }
.ind-arrow::after { content: ""; position: absolute; left: 50%; bottom: 0; width: 8px; height: 8px; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: translateX(-50%) rotate(45deg); }
.ind-block { grid-column: 2; grid-row: 3; align-self: center; display: grid; grid-template-rows: 0 2px; }
.ind-block > i { grid-row: 2; position: relative; background: currentColor; }
.ind-block > i::before { content: ""; position: absolute; left: 0; top: -7px; width: 2px; height: 16px; background: currentColor; }
.ind-block > span { grid-row: 1; align-self: end; margin: 0 var(--s-4) var(--s-8); color: var(--site-body); font-family: var(--font-data); font-size: var(--site-fs-mark); font-weight: 600; letter-spacing: .1em; line-height: 1.3; text-align: center; text-transform: uppercase; }
@container (max-width: 299px) {
  .ind-evidence, .ind-conclusions, .ind-options { margin-inline: var(--s-4); }
  .ind-box { padding-inline: 6px; }
  .ind-outside { padding-inline: var(--s-4); }
  .ind-block > span { margin-inline: 0; letter-spacing: .06em; }
}
@media (min-width: 760px) {
  .ind-scheme { grid-template-columns: minmax(min-content, 1.3fr) minmax(min-content, 84px) minmax(min-content, 1fr); }
  .ind-box { padding-inline: var(--s-12); }
  .ind-evidence, .ind-conclusions, .ind-options { margin-inline: var(--s-12); }
  .ind-box b { font-size: var(--site-fs-body); }
}
/* Прочерк по прокрутке: стрелки цепочки сверху вниз, линия влияния справа к рамке, стрелка к заказчику последней. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .ind-scheme { view-timeline-name: --ind-scheme; }
    .ind-arrow, .ind-block > i, .ind-block > span { animation-duration: auto; animation-timing-function: linear; animation-fill-mode: both; animation-timeline: --ind-scheme; }
    .ind-arrow { animation-name: scheme-reach-down; }
    .ind-arrow[data-step="1"] { animation-range: cover 16% cover 22%; }
    .ind-arrow[data-step="2"] { animation-range: cover 22% cover 28%; }
    .ind-arrow[data-step="3"] { animation-range: cover 34% cover 40%; }
    .ind-block > i { animation-name: scheme-reach-left; animation-range: cover 28% cover 34%; }
    .ind-block > span { animation-name: scheme-label; animation-range: cover 33% cover 35%; }
  }
}
@keyframes scheme-reach-left { from { clip-path: inset(-8px 0 -8px 100%); } to { clip-path: inset(-8px -8px -8px -8px); } }

/* Статусы экспертной сети: четыре ступени по порядку, номер синий, линейка над каждой. */
.steps-row { display: grid; gap: var(--s-12); margin: var(--s-8) 0 var(--s-16); padding: 0; list-style: none; counter-reset: status; }
.steps-row li { display: grid; align-content: start; gap: var(--s-4); margin: 0; padding: var(--s-12) 0 0; border-top: 2px solid var(--site-head); counter-increment: status; }
.steps-row li::before { content: counter(status, decimal-leading-zero); color: var(--site-blue); font-family: var(--font-data); font-size: var(--site-fs-mark); font-weight: 600; letter-spacing: .12em; }
.steps-row strong { font-size: var(--site-fs-body); font-weight: 600; }
.steps-row span { color: var(--site-body); font-size: var(--site-fs-note); line-height: 1.5; }
@media (min-width: 820px) {
  .steps-row { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-24); }
}

/**
 * Шкала вероятности: семь слов по возрастанию. Тон ячейки темнеет от первой к последней, и это
 * сведения, а не украшение: порядок слов и есть шкала. Процентов на странице нет (правило сайта), и
 * тон говорит «больше» без числа. Текст во всех ячейках остается цветом прозы, чтобы самая темная
 * ячейка проходила порог контраста.
 */
.scale { display: grid; gap: 2px; margin: var(--s-8) 0 var(--s-24); padding: 0; list-style: none; }
.scale li { display: grid; align-content: start; gap: var(--s-4); margin: 0; padding: var(--s-12) var(--s-12) var(--s-16); background: color-mix(in srgb, var(--site-head) var(--tone), var(--site-bg)); }
.scale strong { font-size: var(--site-fs-note); font-weight: 600; line-height: 1.3; }
.scale span { color: var(--site-body); font-size: var(--site-fs-note); line-height: 1.45; }
.scale li:nth-child(1) { --tone: 3%; }
.scale li:nth-child(2) { --tone: 6%; }
.scale li:nth-child(3) { --tone: 9%; }
.scale li:nth-child(4) { --tone: 12%; }
.scale li:nth-child(5) { --tone: 15%; }
.scale li:nth-child(6) { --tone: 18%; }
.scale li:nth-child(7) { --tone: 21%; }
@media (min-width: 1240px) {
  .scale { grid-template-columns: repeat(7, minmax(0, 1fr)); }
}

/**
 * Миссия - темная плашка с ярлыком, как «МИССИЯ» в разделе 2.1 организационной модели (20 октября
 * 2026). Цвета берутся у обложки: панель графитовая в обеих темах, на бумаге печати - лист.
 *
 * С 5 ноября 2026 плашка выделяет одно правило и на других страницах (решение автора): независимость
 * выводов, шлюзы, решение заказчика. Там под ней идет текст, и отступ снизу тот же, что сверху; на
 * «Миссии» плашка последняя в обложке, и отступ ей не нужен.
 */
.statement { max-width: 76ch; margin: var(--s-24) 0 0; padding: var(--s-16) var(--s-20) var(--s-20); background: var(--site-cover); }
.statement:not(:last-child) { margin-bottom: var(--s-24); }
/* На «Контактах» в плашке за первым абзацем список и еще абзац (передача материалов). Селектор с
   :not([class]) - чтобы перевесить общее правило списков в тексте (.wide-body ul:not([class])), иначе
   оно обнуляло отступ сверху и список прилипал к абзацу. */
.statement ul:not([class]) { margin: var(--s-12) 0 0; padding-left: 1.2em; color: var(--site-cover-head); font-size: var(--site-fs-lead); }
.statement li + li { margin-top: var(--s-4); }
.statement ul + .lead { margin-top: var(--s-12); }
.statement .statement-label { margin: 0 0 var(--s-8); color: var(--site-cover-blue); font-family: var(--font-data); font-size: var(--site-fs-mark); font-weight: 600; letter-spacing: .12em; text-transform: uppercase; }
.statement .lead { max-width: none; margin: 0; color: var(--site-cover-head); }

/**
 * Дорожная карта шкалой: три этапа на одной линии, у каждого ромб-веха на линии, как вехи на
 * дорожной карте документа. Этап, который идет, помечен синим и словом «Идет» первой строкой; цвет
 * повторяет слово, а не заменяет его. Ромб - квадрат, повернутый на четверть, без скруглений.
 *
 * Одной линией шкала стала 31 октября 2026, на этапе 2 карты сайта. До того у каждого этапа был свой
 * отрезок с просветом между ними, а на телефоне три коротких черты читались разделителями. Теперь
 * линия идет без просвета: на широком окне по верху этапов, на узком вдоль левого края сверху вниз.
 * Идущий этап - синяя сплошная линия, этапы после него - пунктир. Линию рисует ::after, а не рамка
 * этапа: так ее можно прочертить, не трогая текст.
 */
.roadmap { display: grid; margin: var(--s-24) 0 0; padding: 0; list-style: none; }
.roadmap > li { position: relative; display: grid; align-content: start; gap: var(--s-4); margin: 0; padding: 0 0 var(--s-32) var(--s-24); }
.roadmap > li:last-child { padding-bottom: 0; }
.roadmap > li::after { content: ""; position: absolute; top: 0; bottom: 0; left: 0; border-left: 2px solid var(--site-line-strong); transform-origin: top; }
.roadmap > li::before { content: ""; position: absolute; z-index: 1; top: 0; left: -5px; width: 12px; height: 12px; border: 2px solid var(--site-line-strong); background: var(--site-bg); transform: rotate(45deg); }
.roadmap > li.is-current::after { border-color: var(--site-blue); }
.roadmap > li.is-current::before { border-color: var(--site-blue); background: var(--site-blue); }
.roadmap > li.is-ahead::after { border-left-style: dashed; }
.stage-status { color: var(--site-muted); font-family: var(--font-data); font-size: var(--site-fs-mark); font-weight: 600; letter-spacing: .12em; text-transform: uppercase; }
.roadmap .is-current .stage-status { color: var(--site-blue); }
.roadmap strong { font-family: var(--site-display); font-size: var(--site-fs-lead); font-weight: 500; line-height: 1.3; }
.stage-goal { color: var(--site-body); font-size: var(--site-fs-body); }
.roadmap ul { display: grid; gap: var(--s-4); margin: var(--s-8) 0 0; padding: 0; list-style: none; }
.roadmap ul li { position: relative; margin: 0; padding-left: var(--s-20); color: var(--site-body); font-size: var(--site-fs-note); line-height: 1.5; }
.roadmap ul li::before { content: "→"; position: absolute; left: 0; color: var(--site-muted); font-family: var(--font-data); }
@media (min-width: 820px) {
  .roadmap { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .roadmap > li { padding: var(--s-24) var(--s-24) 0 0; }
  .roadmap > li:last-child { padding-right: 0; }
  .roadmap > li::after { right: 0; bottom: auto; border-left: 0; border-top: 2px solid var(--site-line-strong); transform-origin: left; }
  .roadmap > li::before { top: -5px; left: 0; }
  .roadmap > li.is-ahead::after { border-top-style: dashed; }
}
/**
 * Прочерк по прокрутке: у каждого этапа свой таймлайн, поэтому на телефоне линия этапа чертится, когда
 * этап въезжает в окно. На широком окне этапы стоят в одной строке, и очередь слева направо задана
 * долями. При «меньше движения» и без поддержки шкала стоит готовой.
 */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .roadmap > li { view-timeline-name: --roadmap-stage; }
    .roadmap > li::after { animation: scheme-grow-down linear both; animation-timeline: --roadmap-stage; animation-range: cover 12% cover 30%; }
    @media (min-width: 820px) {
      .roadmap > li::after { animation-name: scheme-line; animation-range: cover 15% cover 23%; }
      .roadmap > li:nth-child(2)::after { animation-range: cover 23% cover 31%; }
      .roadmap > li:nth-child(3)::after { animation-range: cover 31% cover 39%; }
    }
  }
}
@keyframes scheme-grow-down { from { transform: scaleY(0); } to { transform: scaleY(1); } }

/**
 * Команда: строка на роль, а не карточка.
 *
 * До 11 сентября 2026 роли стояли сеткой карточек с потолком ширины 1180px, и у этого было два
 * следствия, обоих никто не выбирал. Сетка кончалась на 1243, когда шапка и подвал шли до 1377, —
 * то есть блок не совпадал ни с чем на странице. А пятая карточка оставляла шестую ячейку пустой,
 * и рамка не закрывалась: первый ряд шёл до края блока, второй обрывался на двух третях. Ровно
 * так выглядит недоделанная страница.
 *
 * Строка снимает оба: рядов ровно столько, сколько людей, и пустой ячейке взяться неоткуда. Форма
 * взята у списка публикаций — роль в колонке полей, как там дата, — и это тот же лист, а не второй
 * приём для той же работы.
 *
 * У незанятой роли **нет кружка вовсе**. Пустая рамка на месте лица читается как ненайденная
 * картинка; слово «Открыта» говорит то же самое и не врёт.
 */
.people { display: grid; margin: 0 0 var(--s-8); padding: 0; list-style: none; border-top: 1px solid var(--site-line); }
.person { display: grid; gap: var(--s-8); margin: 0; padding: var(--s-24) 0; border-bottom: 1px solid var(--site-line); }
.person-name { display: flex; align-items: center; gap: var(--s-16); margin: 0; }
.person-face { display: grid; place-items: center; flex: none; width: 44px; height: 44px; border-radius: 50%; background: var(--site-panel-hover); color: var(--site-head); font-family: var(--font-data); font-size: var(--site-fs-body); letter-spacing: .04em; }
.person-role { margin: 0; color: var(--site-muted); }
.person strong { font-family: var(--site-display); font-weight: 600; font-size: var(--site-fs-sub); }
.person > span { display: block; max-width: 64ch; font-size: var(--site-fs-body); }
/**
 * Знак роли (28 октября 2026): пиктограмма работы на месте инициалов, с 5 ноября 2026 тонким
 * контуром (lib/role-marks.mjs). Плашка та же, что у номера раздела: цвет заголовка фоном, цвет листа рисунком, и в
 * темной теме они так же меняются местами. Цвета у знака нет, как у логотипа.
 *
 * Плашки знаков круглые, решение автора 4 ноября 2026: знаки роли, линий услуг, схемы организации и
 * инициалы. Это единственное исключение из правила «скруглений нет»; кнопки, карточки и поля
 * остаются с прямым углом.
 *
 * 5 ноября 2026 плашки знаков несколько часов были синими, и автор вернул черные: «lab и значки
 * верни в исходный вариант - черные».
 */
.person-mark { display: grid; place-items: center; flex: none; width: 56px; height: 56px; border-radius: 50%; background: var(--site-head); color: var(--site-bg); }
.role-mark { display: block; width: 30px; height: 30px; }
/**
 * Знак линии услуг в заголовке полосы (31 октября 2026), этап 2 карты сайта. Плашка та же, что у
 * знака роли. Сторона плашки равна строке заголовка (его line-height 1.18), поэтому знак стоит вровень
 * с первой строкой. Вторая строка длинного названия на телефоне начинается от текста, а не под знаком.
 * Рисунок занимает две трети плашки: на 36 px это ровно сетка 24.
 */
h2:has(> .service-mark) { display: flex; align-items: flex-start; gap: .45em; }
.service-mark, .page-mark { display: grid; place-items: center; flex: none; width: 1.18em; height: 1.18em; border-radius: 50%; background: var(--site-head); color: var(--site-bg); }
.service-glyph, .page-glyph { display: block; width: .667em; height: .667em; }
/* Значок справа от «404» (выбор автора 5 ноября 2026): плашка та же, что у знаков услуг, на высоте числа. */
h1:has(> .page-mark) { display: flex; align-items: center; gap: .3em; }
/* Направление ядра под объяснением роли: метка моно, название ссылкой на «Обзор». Метка и название -
   две колонки, и длинное название переносится в своей колонке. До 3 ноября 2026 строка переносилась
   целиком, и на телефоне у одной карточки из пяти название уходило под метку (разбор сайта). */
.person-area { display: grid; grid-template-columns: max-content minmax(0, 1fr); align-items: baseline; gap: var(--s-4) var(--s-12); margin: var(--s-4) 0 0; font-size: var(--site-fs-note); }
.person-area span { color: var(--site-muted); font-family: var(--font-data); font-size: var(--site-fs-mark); font-weight: 600; letter-spacing: .12em; text-transform: uppercase; }
.person-area a { color: var(--site-body); text-decoration: none; }
.person-area a:hover { color: var(--site-head); text-decoration: underline; text-decoration-color: var(--site-accent); text-underline-offset: 4px; }
/* Шаги цикла под объяснением роли: номер моно синим, как на «Методологии», шлюз красной меткой. */
.person-steps { display: flex; flex-wrap: wrap; gap: var(--s-4) var(--s-20); margin: var(--s-4) 0 0; padding: 0; list-style: none; font-size: var(--site-fs-note); }
.person-steps li { margin: 0; }
.person-steps a { color: var(--site-body); text-decoration: none; }
.person-steps a:hover { color: var(--site-head); text-decoration: underline; text-decoration-color: var(--site-accent); text-underline-offset: 4px; }
.person-step { margin-right: var(--s-8); color: var(--site-blue); font-family: var(--font-data); font-weight: 600; letter-spacing: .12em; }
.person-gate { margin-left: var(--s-8); color: var(--site-accent); font-family: var(--font-data); font-size: var(--site-fs-mark); font-weight: 600; letter-spacing: .12em; text-transform: uppercase; }
@media (min-width: 760px) {
  /* Колонка роли той же ширины, что колонка даты в публикациях: подпись строки — одна и та же работа. */
  .person { grid-template-columns: 150px minmax(0, 1fr); column-gap: var(--s-24); row-gap: var(--s-8); }
  .person-role { grid-column: 1; grid-row: 1; padding-top: var(--s-16); }
  .person-name, .person > span, .person-area, .person-steps { grid-column: 2; }
  /* Роль без имени: должность стоит у знака, колонки полей нет (решение автора 5 ноября 2026). */
  .person.is-unnamed { grid-template-columns: minmax(0, 1fr); }
  .person.is-unnamed > * { grid-column: 1; }
}

/**
 * «Контакты» (вернулись 28 октября 2026): строка на канал. Знак на той же черной плашке, что знаки
 * ролей на «Экспертах» и номера разделов, вид канала моно-меткой, адрес крупно, под ним что туда
 * приходит.
 */
.contacts { display: grid; margin: var(--s-24) 0 0; padding: 0; list-style: none; border-top: 1px solid var(--site-line); }
.contacts li { display: grid; grid-template-columns: 56px minmax(0, 1fr); gap: var(--s-4) var(--s-20); margin: 0; padding: var(--s-24) 0; border-bottom: 1px solid var(--site-line); }
.contact-icon { grid-row: 1 / span 3; display: grid; place-items: center; width: 56px; height: 56px; background: var(--site-head); color: var(--site-bg); font-family: var(--font-data); font-size: var(--site-fs-sub); }
.contact-icon .tg-icon, .contact-icon .mail-icon { width: 28px; height: 28px; }
.contacts p { margin: 0; }
.contact-kind { color: var(--site-muted); font-family: var(--font-data); font-size: var(--site-fs-mark); letter-spacing: .12em; text-transform: uppercase; }
.contact-link { font-family: var(--site-display); font-size: var(--site-fs-sub); font-weight: 600; }
.contact-link a { color: var(--site-head); text-decoration-color: var(--site-line-strong); text-underline-offset: 4px; }
.contact-link a:hover { text-decoration-color: var(--site-accent); }
.contact-note { max-width: 64ch; font-size: var(--site-fs-body); }

/* Публикации: дата моно, заголовок прозой, линейка между записями. */
.posts { margin: 0; padding: 0; list-style: none; border-top: 1px solid var(--site-line); }
.posts li { display: grid; gap: var(--s-8); margin: 0; padding: var(--s-24) 0; border-bottom: 1px solid var(--site-line); }
.posts h2, .posts h3 { margin: 0; font-family: var(--site-display); font-weight: 600; font-size: var(--site-fs-sub); line-height: 1.25; letter-spacing: -.005em; }
.posts h2 a, .posts h3 a { color: var(--site-head); text-decoration: none; }
.posts h2 a:hover, .posts h3 a:hover { text-decoration: underline; text-decoration-color: var(--site-accent); text-underline-offset: 4px; }
.posts p { margin: 0; }
.post-meta { display: flex; flex-wrap: wrap; gap: var(--s-4) var(--s-16); margin: 0; color: var(--site-muted); text-transform: none; letter-spacing: .04em; }
.post-meta time { color: var(--site-head); }
/* Рубрика записи (22 октября 2026) - цветом текста. До 5 ноября 2026 стояла синей, но синий значит
   счет, а рубрика не счет (аудит unslop). Номер G.0xx и дата рядом остаются данными. */
.post-meta .post-rubric { color: var(--site-body); }
/* С 31 октября 2026 рубрика - ссылка на свою страницу; подчеркивание появляется по наведению. */
.post-meta a.post-rubric { text-decoration: none; }
.post-meta a.post-rubric:hover { text-decoration: underline; text-underline-offset: 4px; }
/* Область нажатия у ссылок строки не ниже 24 пикселей: на телефоне рубрика была 51×13 (разбор сайта
   3 ноября 2026). Поле сверху и снизу съедено отрицательным отступом, и строка не выросла. */
.post-meta a { display: inline-block; padding-block: 6px; margin-block: -6px; }
/* На телефоне строка записи в списке - две одинаковые строки: номер, дата и минуты чтения, под ними
   рубрика и шаг метода. До 3 ноября 2026 она переносилась по длине слов, у каждой записи по-своему.
   Перенос делает пустой элемент во всю ширину между двумя группами. */
.posts .post-meta::after { content: ""; flex-basis: 100%; order: 1; }
.posts .post-meta > .post-rubric, .posts .post-meta > .post-method, .posts .post-meta > .post-only { order: 2; }
@media (min-width: 760px) {
  .posts .post-meta::after { content: none; }
  .posts .post-meta > .post-rubric, .posts .post-meta > .post-method, .posts .post-meta > .post-only { order: 0; }
}
/**
 * Строка рубрик над «Публикациями» и над страницей рубрики (пункт 3.2 карты сайта, 31 октября 2026):
 * все публикации и каждая рубрика с числом записей. Текущая - темной плашкой, как номер полосы.
 */
.rubric-nav { display: flex; flex-wrap: wrap; gap: var(--s-8); margin: 0 0 var(--s-24); }
.rubric-nav a { display: inline-flex; align-items: baseline; gap: var(--s-8); padding: var(--s-4) var(--s-12); border: 1px solid var(--site-line-strong); color: var(--site-body); font-family: var(--font-data); font-size: var(--site-fs-note); font-weight: 600; letter-spacing: .04em; text-decoration: none; }
.rubric-nav a span { color: var(--site-muted); }
.rubric-nav a:hover { border-color: var(--site-head); color: var(--site-head); }
.rubric-nav a[aria-current] { border-color: var(--site-head); background: var(--site-head); color: var(--site-bg); }
.rubric-nav a[aria-current] span { color: inherit; }
/**
 * Номер публикации (пункт 3.1 карты сайта, 31 октября 2026; образец - Forensic Architecture) стоит
 * первым и набран цветом заголовка: по нему запись называют. На странице записи строка стоит в
 * линейках под заголовком, как паспорт.
 */
.post-meta .post-number { color: var(--site-head); font-weight: 600; }
/* Ширина - вся колонка: правило первого абзаца под заголовком (.page-head + p, 64 знака) мерило бы
   линейки мелким шрифтом строки, и они обрывались на середине. */
.post-meta.is-passport { max-width: none; margin-bottom: var(--s-16); padding: var(--s-8) 0; border-top: 1px solid var(--site-line-strong); border-bottom: 1px solid var(--site-line-strong); }

/**
 * Свежая запись: крупно, с ведущим абзацем и линейкой под ней.
 *
 * Приём из макетов автора — там «последнее исследование» занимает своё место, а не идёт первой
 * строкой одинакового списка. После этого список читается продолжением, а не витриной из трёх
 * равных, и у полосы появляется вход.
 *
 * Линейка под свежей записью и есть верхняя линия списка. До 3 ноября 2026 у списка была своя, и
 * под свежей записью стояли две черты с просветом в 25 пикселей.
 */
.post-lead { margin: 0; padding-bottom: var(--s-24); border-bottom: 1px solid var(--site-line); }
.post-lead + .posts { border-top: 0; }
.post-lead .post-meta { margin-bottom: var(--s-12); }
.post-lead h3 { margin-bottom: var(--s-8); }
.post-lead h3 a { color: var(--site-head); text-decoration: none; }
.post-lead h3 a:hover { text-decoration: underline; text-decoration-color: var(--site-accent); text-underline-offset: 4px; }
.post-lead .lead { margin: 0; max-width: 62ch; }
.post-only { color: var(--site-muted); }
.post-summary { margin: 0 0 var(--s-24); font-size: var(--site-fs-lead); }
.more { margin: var(--s-20) 0 0; }
/*
 * Колонка даты и обложка стоят во всю высоту строки списка, а заголовок и краткое содержание - в двух
 * первых строках сетки. Лишняя высота уходит в третью, пустую строку. До 3 ноября 2026 строк было
 * две, обложка выше текста делила свой запас между ними, и под однострочным заголовком вырастало 20-32
 * пикселя пустоты против 7 под двухстрочным.
 */
@media (min-width: 760px) {
  .posts li { grid-template-columns: 150px minmax(0, 1fr); grid-template-rows: auto auto 1fr; gap: var(--s-4) var(--s-24); align-items: baseline; }
  .posts li > .post-meta { grid-row: 1 / -1; }
}
/**
 * Уменьшенная обложка в списке публикаций (28 октября 2026). Своя колонка между датой и заголовком:
 * там, где ее показал автор. На узком окне обложка встает над заголовком во всю ширину строки, но не
 * шире 420 пикселей, иначе на планшете она занимает экран целиком.
 */
.post-thumb { display: block; max-width: 420px; border: 1px solid var(--site-line); }
.post-thumb img { display: block; width: 100%; height: auto; }
@media (min-width: 760px) {
  .posts li.has-cover { grid-template-columns: 150px 300px minmax(0, 1fr); }
  .posts li.has-cover > .post-thumb { grid-column: 2; grid-row: 1 / -1; align-self: start; margin-top: var(--s-4); }
  .posts li.has-cover > h2, .posts li.has-cover > h3, .posts li.has-cover > p:not(.post-meta) { grid-column: 3; }
}
/**
 * Свежая запись на главной (29 октября 2026): обложка под заголовком, на месте двух первых колонок
 * строк ниже, а краткое содержание в третьей. Края обложки и текста совпадают со строками под ней.
 * На узком окне обложка встает между заголовком и кратким содержанием.
 */
.post-lead > .post-thumb { margin: 0 0 var(--s-12); }
@media (min-width: 760px) {
  .post-lead.has-cover { display: grid; grid-template-columns: 150px 300px minmax(0, 1fr); gap: 0 var(--s-24); }
  .post-lead.has-cover > .post-meta, .post-lead.has-cover > h3 { grid-column: 1 / -1; }
  .post-lead.has-cover > .post-thumb { grid-column: 1 / span 2; max-width: none; margin: var(--s-4) 0 0; align-self: start; }
  .post-lead.has-cover > .lead { grid-column: 3; }
}

/**
 * Чем кончается запись: соседи по разделу.
 *
 * Отбит от текста тем же, чем отбиты полосы главной, — расстоянием и волосяной линией сверху. Свой
 * список ему не нужен: список записей уже стоит на главной и в разделе публикаций, и третья
 * анатомия одного и того же разошлась бы с двумя первыми на первой правке.
 *
 * Заголовок набран мелко, а не как заголовок раздела: это конец текста, и крупная строка здесь
 * спорила бы с заголовком самой записи, до которого читатель дошёл своим трудом. Это ссылка, поэтому
 * строчными (с 4 ноября 2026, аудит unslop), и стрелка у нее цвета текста, как у призыва.
 */
.read-next { margin: var(--s-48) 0 0; padding-top: var(--s-32); border-top: 1px solid var(--site-line); }
.read-next-title { margin: 0 0 var(--s-16); font-family: var(--site-display); font-size: var(--site-fs-note); font-weight: 600; }
.read-next-title a { display: inline-flex; align-items: center; gap: var(--s-8); color: var(--site-muted); text-decoration: none; transition: color var(--t-colour) var(--site-ease); }
.read-next-title a:hover { color: var(--site-head); }
/* Внизу списка вторая линия не нужна: под ней сразу подвал со своей. */
.read-next .posts li:last-child { border-bottom: 0; padding-bottom: 0; }

/* Метод и длинные записи блога: оглавление держит место в тексте видимым. */
.with-toc { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-32); }
.toc { margin: 0; padding: var(--s-16) var(--s-20); border: 1px solid var(--site-line); background: var(--site-panel); }
.toc-title { margin: 0 0 var(--s-12); color: var(--site-muted); }
.toc ol { margin: 0; padding: 0; list-style: none; display: flex; flex-wrap: wrap; gap: var(--s-4) var(--s-16); }
/* Пункт оглавления в две строки делится поровну, как заголовок: по-английски пункты длиннее, и
   на 1440 второе слово оставалось на строке одно. */
.toc li { margin: 0; text-wrap: balance; }
.toc a { color: var(--site-body); font-size: var(--site-fs-note); text-decoration: none; }
.toc a:hover { color: var(--site-head); }
/* Ссылка к оглавлению под разделом «Метода» (с 4 ноября 2026): на узком окне оглавление только в
   начале страницы. Высота строки - область нажатия не ниже 24 пикселей. Липкое оглавление сбоку на
   широком окне ее заменяет. */
.to-contents { margin: 0 0 var(--s-8); font-family: var(--site-display); font-size: var(--site-fs-note); font-weight: 600; }
.to-contents a { display: inline-block; padding-block: var(--s-4); color: var(--site-muted); text-decoration: none; }
.to-contents a:hover { color: var(--site-head); }
@media (min-width: 960px) {
  .to-contents { display: none; }
  /* Колонка текста идёт до края листа, как на остальных страницах: меру держит сам абзац, а
     таблица, снимок и цепочка от ширины только выигрывают. Потолок в 720px резал их зря. */
  .with-toc { grid-template-columns: 210px minmax(0, 1fr); }
  /* Липнет под шапкой, а не под верхом окна; на низком окне прокручивается внутри себя. */
  .toc { position: sticky; top: calc(var(--head-room) + var(--s-20)); align-self: start; max-height: calc(100vh - var(--head-room) - var(--s-40)); overflow-y: auto; }
  .toc ol { display: grid; gap: var(--s-8); }
}

/* Только по вертикали: поля по краям задает общее правило выше, а сокращенная запись затирала бы
   их — подвал прилипал к левому краю окна, пока вся страница стояла с отступом. */
.site-foot { display: grid; gap: var(--s-16); padding-block: var(--s-32) var(--s-40); border-top: 1px solid var(--site-line); color: var(--site-muted); font-size: var(--site-fs-note); }
/* Адрес страницы: на экране не нужен — он в строке браузера, — а на бумаге это единственный след
   того, откуда взят текст. Показывается правилом печати. */
.print-source { display: none; margin: 10pt 0 0; font-family: var(--font-data); font-size: 9pt; color: var(--site-muted); word-break: break-all; }
/* Код QR записи - тоже только на бумаге: рядом с адресом он избавляет от набора адреса руками. */
.print-qr { display: none; }
.foot-mark { display: flex; flex-wrap: wrap; gap: var(--s-8) var(--s-16); align-items: center; margin: 0; }
.foot-mark .mark { font-size: var(--site-fs-body); }
.site-foot nav { display: flex; flex-wrap: wrap; gap: var(--s-4) var(--s-16); }
.site-foot nav a { color: var(--site-muted); font-size: var(--site-fs-note); text-decoration: none; }
.site-foot nav a:hover { color: var(--site-head); }
/**
 * Палец толще курсора.
 *
 * Замер 16 сентября 2026 на телефоне: строки оглавления метода были по 17 пикселей, ссылки подвала —
 * по 21, подменю, язык и тема — от 27 до 40. Порог WCAG 2.2 (критерий 2.5.8) всё это проходило: цель
 * мельче 24 пикселей там допустима, если соседние цели стоят достаточно далеко, и здесь они стояли.
 * Но пройти порог и попадать пальцем не целясь — разные вещи. Размер, в который палец попадает с
 * первого раза, — 44, и его не было ни у одного из этих элементов.
 *
 * Первая версия этого комментария называла оглавление провалом критерия. Это было неверно: вывод
 * сделан по голому размеру, без расстояния до соседей, а полная проверка на закоммиченной версии
 * провалов не нашла.
 *
 * Условие про то, чем нажимают, а не про ширину экрана. Мышь на любом окне получает прежнюю
 * плотность; планшет получает крупные цели и в горизонтальном положении, где оглавление стоит
 * колонкой сбоку.
 *
 * Место под крупную цель взято из просвета между строками, а не добавлено сверху: оглавление
 * вытягивается, но ровно настолько, насколько растёт сама цель.
 */
@media (pointer: coarse) {
  .toc ol { display: grid; gap: 0; }
  .toc li + li { border-top: 1px solid var(--site-line); }
  .toc a { display: flex; align-items: center; min-height: 44px; padding-block: var(--s-4); }
  .site-foot nav { gap: 0 var(--s-16); }
  .site-foot nav a { display: inline-flex; align-items: center; min-height: 44px; }
  .foot-channel a { width: 44px; height: 44px; }
  .foot-channel a:first-child { margin-left: -12px; }
  .sub-nav a, .site-head nav a { display: inline-flex; align-items: center; min-height: 44px; }
  .site-head .lang-switch > * { display: inline-flex; align-items: center; min-height: 38px; }
  .theme-toggle, .cta.is-compact { min-height: 44px; }
}
.foot-legal { margin: 0; }
/* Канал в Telegram - знаком, а не словом (просьба автора 28 октября 2026). Цель 32 пикселя, на
   сенсорном экране 44, как у остальных ссылок подвала. Подпись живет в aria-label и подсказке. */
.foot-channel { display: flex; gap: var(--s-4); margin: 0; }
.foot-channel a { display: inline-grid; place-items: center; width: 32px; height: 32px; color: var(--site-muted); }
.foot-channel a:first-child { margin-left: -6px; }
.foot-channel a:hover { color: var(--site-head); }
.tg-icon, .mail-icon { display: block; width: 20px; height: 20px; }
.foot-legal a { color: var(--site-muted); }

/**
 * Полосы проявляются по мере прокрутки — прием с присланного автором образца.
 *
 * Сделано без единой строки JavaScript: анимация привязана к положению полосы в окне
 * (scroll-driven animation, «анимация, ведомая прокруткой» — таймлайн берется не от времени, а от
 * того, насколько элемент въехал в окно). Браузер без такой поддержки показывает страницу как
 * есть: правило стоит под @supports, и полоса без анимации — это просто полоса.
 *
 * Меры удержания взяты из эссе Ковальски «You don't need animations». Двигается только вход в
 * окно, один раз, на 8px и до полной непрозрачности к трети окна; частые действия (наведение,
 * переходы по меню) этим не трогаются. Кто просил не двигать — тому не двигается:
 * prefers-reduced-motion выключает все разом. Подъем был 14px; 5 ноября 2026 по аудиту unslop
 * снижен до 8px: скилл разрешает появление с подъемом 6-12px.
 */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .band, .posts li, .people li {
      animation: band-rise linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 34%;
    }
  }
}
@keyframes band-rise {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

/**
 * Переход между страницами. Вариант «Чертеж», пункт 5, решение автора 26 сентября 2026; идея
 * Motion (связанное движение между страницами), сделанная средствами самого браузера: View
 * Transitions между документами. Скрипта для этого не нужно.
 *
 * Старый лист гаснет, новый проявляется за --t-move. Шапка одна на весь сайт, поэтому у нее свое имя
 * в переходе: она стоит на месте, пока меняется лист под ней, и только подстраивает высоту, если
 * на прежней странице была сжата прокруткой.
 *
 * Браузер без поддержки открывает страницу как раньше. Кто просил меньше движения, получает то же:
 * правило стоит внутри prefers-reduced-motion: no-preference.
 */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
  .site-head { view-transition-name: site-head; }
  /* Все группы перехода, включая карточку узла, перетекающую в шапку листа, - одной длительности:
     разная скорость у частей одного перехода читалась бы как рывок. */
  ::view-transition-group(*) {
    animation-duration: var(--t-move);
    animation-timing-function: var(--site-ease);
  }
  ::view-transition-old(*), ::view-transition-new(*) {
    animation-duration: var(--t-move);
  }
}

/**
 * Цепочка прочерчивается по порядку, а не проявляется целиком.
 *
 * Семь шагов на главной — это порядок работы, и порядок здесь утверждение, а не украшение: сначала
 * источник, публикация последней. Полоса въезжает в окно одним куском, и цепочка вместе с ней
 * читалась как семь равноправных слов. Линейка над каждым шагом проводится слева направо, шаги
 * идут с задержкой друг за другом — глаз читает их в том порядке, в котором они и работают.
 *
 * Тем же приемом, что и вся остальная прокрутка: таймлайн от положения полосы в окне, сдвиг между
 * шагами задан не временем, а долей въезда. Значит на медленной прокрутке цепочка чертится
 * медленно, на быстрой — быстро, и догонять ее не приходится.
 *
 * Цвет — светлый тон текста, не акцент. Красный по контракту означает риск и главное действие; семь
 * красных линеек подряд были бы украшением, а это в проекте запрещено. Линейка сама по себе никуда
 * не делась: она нарисована рамкой и стоит на месте, прочерк идет поверх нее.
 */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /**
     * Ход считается по cover, а не по entry, и не у полосы, а у самой строки.
     *
     * Две попытки до этой провалились, и обе поймал замер, а не глаз. Entry у строки — это весь ее
     * въезд в окно, около шестидесяти пикселей прокрутки: семь прочерков на таком отрезке
     * сливаются в мигание. Entry у полосы дает ход в несколько сотен пикселей, но полоса въезжает
     * раньше цепочки, которая стоит в ее нижней части: к тому мигу, когда читатель видит семь
     * шагов, все семь уже прочерчены. Анимация, которая кончается до появления предмета, — это
     * отсутствие анимации.
     *
     * Cover считается от «строка показалась из-за нижней кромки» до «строка ушла за верхнюю», то
     * есть высота окна плюс высота строки — около километра прокрутки на любом экране. Доли 18–58
     * процентов приходятся ровно на то время, когда цепочка стоит в окне.
     *
     * **Прочерк рисуется фоном строки, а не псевдоэлементом.** У строки оба псевдоэлемента заняты:
     * перед ней стоит номер шага, после — стрелка к следующему. Первая версия этой правки взяла
     * псевдоэлемент after под прочерк и **стерла стрелки между шагами**; увидел это замер, а не глаз —
     * содержимое псевдоэлемента при включенном движении стало пустым.
     *
     * Слоев фона два: нижний — та же тихая линейка, что рисовала рамка, верхний — светлый прочерк,
     * который растет слева направо. Рамка на время движения становится прозрачной: она рисуется
     * поверх фона и закрыла бы оба слоя.
     */
    .chain li {
      border-top-color: transparent;
      background-image:
        linear-gradient(var(--site-head), var(--site-head)),
        linear-gradient(var(--site-line-strong), var(--site-line-strong));
      background-repeat: no-repeat;
      background-origin: border-box;
      background-position: 0 0;
      background-size: 0 2px, 100% 2px;
      animation: chain-draw linear both;
      animation-timeline: view();
    }
    .chain li:nth-child(1) { animation-range: cover 18% cover 34%; }
    .chain li:nth-child(2) { animation-range: cover 22% cover 38%; }
    .chain li:nth-child(3) { animation-range: cover 26% cover 42%; }
    .chain li:nth-child(4) { animation-range: cover 30% cover 46%; }
    .chain li:nth-child(5) { animation-range: cover 34% cover 50%; }
    .chain li:nth-child(6) { animation-range: cover 38% cover 54%; }
    .chain li:nth-child(7) { animation-range: cover 42% cover 58%; }
  }
}
@keyframes chain-draw {
  from { background-size: 0 2px, 100% 2px; }
  to { background-size: 100% 2px, 100% 2px; }
}

/**
 * Отвод к пункту указателя прочерчивается, когда пункт показывается.
 *
 * Очередности руками здесь не задано, и это не упущение: у каждого отвода свой таймлайн, потому
 * что у каждого свое место на странице. Верхний пункт въезжает первым и чертится первым, за ним
 * второй. Список сам расставляет порядок, и при шестом направлении ничего дописывать не надо.
 *
 * Доля cover выбрана так, чтобы прочерк шел, когда строка уже в окне, а не под нижней кромкой: на
 * цепочке это стоило отдельной правки, и повторять ту же ошибку незачем. Ход короткий — отвод
 * длиной двадцать два пикселя, и растягивать его на пол-экрана значило бы смотреть, как ползет
 * черточка.
 */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .index-title::before {
      transform: scaleX(0);
      animation: tick-draw linear both;
      animation-timeline: view();
      animation-range: cover 16% cover 26%;
    }
  }
}
@keyframes tick-draw {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}
/* На бумаге прокрутки нет, и полоса, ждущая въезда в окно, осталась бы прозрачной навсегда. */
/**
 * Печать. Расследование чаще всего читают с листа или сохраняют в PDF, а до 10 сентября 2026 здесь
 * стояла одна строка — гашение анимаций, — и на бумагу уезжала тёмная страница целиком.
 *
 * Палитра переворачивается **токенами**, а не правилами: всё оформление уже читает site-токены,
 * поэтому трёх подстановок хватает на весь сайт. Тёмный лист — это не только чужие чернила: тонкий
 * кремовый по чёрному на бумаге становится серым по серому, то есть текст просто не читается.
 *
 * Со страницы уходит то, что на бумаге не работает: меню, переключатель языка, знак картинкой.
 * Нажать их нельзя, а место они занимают. Имя организации остаётся — набранное, а не картинкой.
 *
 * Адрес страницы печатается внизу. Это не украшение: текст, вынутый из сайта и положенный в папку,
 * обязан говорить, откуда он взят, — иначе через месяц его нечем проверить.
 */
@media print {
  /**
   * Селектор удвоен намеренно, и это стоило упавшего сценария.
   *
   * Правила темы называют корень вместе с признаком и потому весят больше простого корня. Пока
   * тем не было, печать побеждала; со светлой темой лист печати начал брать экранную бумагу
   * вместо белой, и поймал это старый сценарий печати, а не глаз.
   *
   * Удвоенное имя корня весит столько же, сколько правила тем, а стоит в файле последним — значит
   * побеждает. Лист печати белый при любой теме читателя: бумага не знает о его настройках.
   */
  :root:root {
    color-scheme: light;
    --site-bg: var(--site-paper);
    --site-panel: var(--site-paper);
    --site-panel-hover: var(--site-paper);
    --site-line: var(--site-rule);
    --site-line-strong: var(--site-ink-quiet);
    --site-head: var(--site-ink);
    --site-body: var(--site-ink);
    --site-muted: var(--site-ink-quiet);
    /* Графитовая обложка на бумаге была бы черным прямоугольником: печатается листом. */
    --site-cover: var(--site-paper);
    --site-cover-line: var(--site-rule);
    --site-cover-head: var(--site-ink);
    --site-cover-body: var(--site-ink);
    --site-cover-muted: var(--site-ink-quiet);
    --site-cover-blue: var(--site-ink-quiet);
  }
  @page { margin: 18mm; }
  body { padding: 0; font-size: 11pt; }
  .site-head nav, .sub-nav, .lang-switch, .site-foot nav, .mark img, .mark-lab, .toc { display: none; }
  /*
   * Меню шапки и обе её кнопки прячутся отдельной строкой, с весом выше экранных правил.
   *
   * С 16 сентября 2026 меню в строке шапки держит правило из трёх частей — класс, атрибут и
   * состояние, — и оно весит больше, чем «.site-head nav» выше: на листе меню снова напечаталось.
   * Поймал это сценарий печати. Призыв «Связаться с нами» печатался и раньше, с 12 сентября, —
   * про него здесь просто забыли, хотя правило листа одно: что нельзя нажать, на бумагу не идёт.
   */
  .site-head .site-menu[popover]:not(:popover-open), .site-head .menu-toggle, .site-head .head-cta { display: none; }
  /* Картинку знака выбирают правила темы весом в четыре части; простое «.mark img» выше им проигрывает. */
  :root:root .mark img.for-dark, :root:root .mark img.for-light { display: none; }
  /* Закрепленная шапка на бумаге повторялась бы на каждом листе. */
  .site-head { position: static; animation: none; padding: 0 0 10pt; }
  .band, .posts li, .people li { animation: none; opacity: 1; transform: none; }
  /* Схемы на бумаге стоят дочерченными: прокрутки, которая их чертит, у листа нет. */
  .scheme-track::before, .scheme-exit > i, .scheme-back > i, .scheme-exit > span, .scheme-back > span, .org-arrow, .horizon-axis::before, .horizon-axis::after, .horizon-link, .roadmap > li::after, .ind-arrow, .ind-block > i, .ind-block > span { animation: none; }
  /* Правило раскрытия снимка весит больше строки выше, поэтому снимается тем же селектором. */
  body:not(.is-home) .band .shot:has(.shot-stamp) .shot-frame:not(.is-card) { animation: none; clip-path: none; }
  /* Разрыв страницы посреди карточки или снимка рвёт вещь, которую читают целиком. */
  figure, .person, .columns li, .index li { break-inside: avoid; }
  h1, h2, h3 { break-after: avoid; }
  .print-source { display: block; }
  .print-qr { display: flex; gap: 10pt; align-items: center; margin: 18pt 0 0; font-family: var(--font-data); font-size: 9pt; color: var(--site-muted); word-break: break-all; }
  .print-qr-code svg { display: block; width: 26mm; height: 26mm; }
}


@media (min-width: 960px) {
  .toc { padding: 0; border: 0; background: none; }
  .toc-title { padding-left: var(--s-24); }
  .toc ol {
    gap: 0;
    border-left: 1px solid var(--site-line-strong);
    background: repeating-linear-gradient(to bottom, var(--site-line) 0 1px, transparent 1px var(--s-8)) no-repeat 0 0 / var(--s-4) 100%;
  }
  .toc li { position: relative; padding-block: var(--s-4); padding-left: var(--s-24); }
  .toc li + li { border-top: 0; }
  /* Название в три строки при общем интервале текста расползалось по колонке. Интервал задан пункту,
     а не ссылке: у строчного элемента высоту строки держит блок вокруг него. */
  .toc li { line-height: 1.45; }
  .toc li::before { content: ""; position: absolute; top: 50%; left: 0; width: var(--s-12); border-top: 1px solid var(--site-line-strong); }
}
@supports (animation-timeline: view()) {
  @media (min-width: 960px) {
    .with-toc { timeline-scope: --toc-1, --toc-2, --toc-3, --toc-4, --toc-5, --toc-6, --toc-7, --toc-8, --toc-9, --toc-10, --toc-11, --toc-12, --toc-13, --toc-14, --toc-15, --toc-16; }
    /* Где отметка есть, остальные пункты тише: цвет текста и цвет заголовка на темном листе почти
       не различаются, и помеченный пункт терялся среди соседей. Без отметки пункты остаются
       обычного цвета: приглушать их не ради чего. */
    .toc a { color: var(--site-muted); }
    .toc-body > section {
      view-timeline-axis: block;
      view-timeline-inset: calc(var(--head-room) + var(--s-16)) calc(100% - var(--head-room) - var(--s-16) - 1px);
    }
    .toc-body > section:nth-of-type(1) { view-timeline-name: --toc-1; }
    .toc li:nth-child(1)::before, .toc li:nth-child(1) a { animation-timeline: --toc-1; }
    .toc-body > section:nth-of-type(2) { view-timeline-name: --toc-2; }
    .toc li:nth-child(2)::before, .toc li:nth-child(2) a { animation-timeline: --toc-2; }
    .toc-body > section:nth-of-type(3) { view-timeline-name: --toc-3; }
    .toc li:nth-child(3)::before, .toc li:nth-child(3) a { animation-timeline: --toc-3; }
    .toc-body > section:nth-of-type(4) { view-timeline-name: --toc-4; }
    .toc li:nth-child(4)::before, .toc li:nth-child(4) a { animation-timeline: --toc-4; }
    .toc-body > section:nth-of-type(5) { view-timeline-name: --toc-5; }
    .toc li:nth-child(5)::before, .toc li:nth-child(5) a { animation-timeline: --toc-5; }
    .toc-body > section:nth-of-type(6) { view-timeline-name: --toc-6; }
    .toc li:nth-child(6)::before, .toc li:nth-child(6) a { animation-timeline: --toc-6; }
    .toc-body > section:nth-of-type(7) { view-timeline-name: --toc-7; }
    .toc li:nth-child(7)::before, .toc li:nth-child(7) a { animation-timeline: --toc-7; }
    .toc-body > section:nth-of-type(8) { view-timeline-name: --toc-8; }
    .toc li:nth-child(8)::before, .toc li:nth-child(8) a { animation-timeline: --toc-8; }
    .toc-body > section:nth-of-type(9) { view-timeline-name: --toc-9; }
    .toc li:nth-child(9)::before, .toc li:nth-child(9) a { animation-timeline: --toc-9; }
    .toc-body > section:nth-of-type(10) { view-timeline-name: --toc-10; }
    .toc li:nth-child(10)::before, .toc li:nth-child(10) a { animation-timeline: --toc-10; }
    .toc-body > section:nth-of-type(11) { view-timeline-name: --toc-11; }
    .toc li:nth-child(11)::before, .toc li:nth-child(11) a { animation-timeline: --toc-11; }
    .toc-body > section:nth-of-type(12) { view-timeline-name: --toc-12; }
    .toc li:nth-child(12)::before, .toc li:nth-child(12) a { animation-timeline: --toc-12; }
    .toc-body > section:nth-of-type(13) { view-timeline-name: --toc-13; }
    .toc li:nth-child(13)::before, .toc li:nth-child(13) a { animation-timeline: --toc-13; }
    .toc-body > section:nth-of-type(14) { view-timeline-name: --toc-14; }
    .toc li:nth-child(14)::before, .toc li:nth-child(14) a { animation-timeline: --toc-14; }
    .toc-body > section:nth-of-type(15) { view-timeline-name: --toc-15; }
    .toc li:nth-child(15)::before, .toc li:nth-child(15) a { animation-timeline: --toc-15; }
    .toc-body > section:nth-of-type(16) { view-timeline-name: --toc-16; }
    .toc li:nth-child(16)::before, .toc li:nth-child(16) a { animation-timeline: --toc-16; }
    .toc li::before { animation-name: toc-tick; }
    .toc li a { animation-name: toc-link; }
    .toc li::before, .toc li a { animation-duration: auto; animation-timing-function: linear; animation-fill-mode: none; animation-range: cover; }
  }
}
@keyframes toc-tick { from, to { width: var(--s-20); border-top-color: var(--site-head); } }
@keyframes toc-link { from, to { color: var(--site-head); } }
