/* Библиотека компонентов портала.

   Ни одного hex-цвета: все цвета — переменные из tokens.css. Это держит линтер
   (eslint.config.js, elbez/no-hex-color), а не аккуратность.

   Язык вёрстки снят с эталона ЗАМЕРАМИ, а не по памяти: docs/design/croc-reference.md.
   Пять приёмов, и каждый — правка того, что мы сделали 29.07.2026 неверно:
     1. главное действие — ТЁМНЫЙ ТЕКСТ НА СВЕТЛОМ КИСЛОТНОМ (было: белый на тёмном
        изумруде, корректно по контрасту и мертво);
     2. зелёный — акцент и тинт, а не заливка всего подряд;
     3. веса лёгкие: абзац 300, заголовок 500 (было 700–800);
     4. радиус кнопки 10 px, НЕ пилюля: пилюли были ошибкой чтения эталона;
     5. воздух щедрый — под шапкой до первого заголовка почти экран.
   Чего здесь нет и не будет: острых углов с офсет-тенями и «чертёжного» стиля — забраковано
   лично на четырёх пробах редизайна entrend.ru.

   Проверки, которые прошли компоненты:
   — контраст текста к фону ≥ 4.5:1, границ и крупного текста ≥ 3:1 (tests/brand/contrast.test.js);
   — цель нажатия не меньше --tap: аудитория читает с телефона в поле, пальцем, в перчатке;
   — 390 px: ничего не разъезжается (скриншоты в docs/design/screens/). */

/* ─── Основание ────────────────────────────────────────────────────────────────────────── */

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

/* ЯКОРНАЯ ССЫЛКА НЕ ОБЯЗАНА ПРЯТАТЬСЯ ПОД ЛИПКОЙ ШАПКОЙ. Браузер ставит цель прыжка
   вплотную к верху окна, а верх окна занят шапкой (`position: sticky`) — и заголовок, ради
   которого человек нажал ссылку, оказывается ПОД ней. Это касается и «К содержимому», и
   ссылок в пункты нормы, и возврата по крошкам к разделу.
   Высота шапки объявлена переменной (--header-height, определяется в разделе шапки вместе
   с самой шапкой), поэтому отступ не расходится с ней при следующей правке. */
html {
  -webkit-text-size-adjust: 100%;
  scroll-padding-top: calc(var(--header-height) + var(--sp-4));
}

body {
  margin: 0;
  /* Длинное неразрывное слово (имя файла, адрес, код области аттестации «Б.9.31») на 390 px
     иначе распирает страницу вбок, и вместе с ней уезжает вся вёрстка. */
  overflow-wrap: break-word;
  background: var(--c-bg);
  color: var(--c-ink);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  /* 300, а не 400. Лёгкий вес — половина того, что читается как «дорого»: на 400 та же
     страница выглядит объявлением, на 300 — набором. Мелкое ниже возвращается к 400. */
  font-weight: var(--w-light);
  /* Кириллица в Manrope выигрывает от включённого кернинга: иначе «АУ», «ГД» разъезжаются. */
  font-kerning: normal;
}

img, svg, video { max-width: 100%; height: auto; }

/* Видимость фокуса — эвристика Нильсена №1 (состояние системы видно). Обводка бренд-цветом,
   а не отключённый outline: клавиатура здесь не экзотика, кабинет заполняют с ноутбука. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 3px solid var(--c-brand);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ─── Полоса и сетка ───────────────────────────────────────────────────────────────────── */

.shell {
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--sp-4);
}

@media (min-width: 48rem) {
  .shell { padding-inline: var(--sp-6); }
}

.stack > * + * { margin-top: var(--sp-4); }
.stack-wide > * + * { margin-top: var(--sp-8); }

/* Воздух между разделами поднят с 4rem до 6rem. Замер эталона: там между блоками столько,
   что глаз успевает закончить один прежде, чем начать следующий. У нас разделы стояли
   впритык, и страница читалась одной непрерывной лентой. */
.section { padding-block: var(--sp-9); }
.measure { max-width: var(--measure); }
/* Колонка по центру полосы. Только для страниц с одним предметом на экране — вход и
   короткие сообщения. Текст, который читают подряд (документы, кабинет), центрировать
   нельзя: глаз ищет начало строки у одного и того же края. */
.measure--centered { margin-inline: auto; }

/* Щель между частями раздела. Раньше её не было вовсе: у h2 поля обнулены (иначе заголовки
   разной величины дают разную щель), а .stack-wide разводит только СВОИХ детей между собой —
   и заголовок «Как это устроено» упирался прямо в контурный номер первого шага. Единственное,
   что их разделяло, — поля <p> от браузера, то есть случайность. Ставим щель здесь, на уровне
   раздела: одну и ту же для заголовка, вводного абзаца, сетки и списка шагов. */
.section > * + * { margin-top: var(--sp-6); }

/* Телефон: та же композиция, но плотнее. Замер главной на 390 px дал 9313 px высоты —
   это двадцать четыре экрана прокрутки, и половина из них воздух между разделами. Воздух
   нужен, но 4rem сверху и снизу у каждого из семи разделов на узком экране читаются не как
   ритм, а как пустота (Бирман: воздух показывает связь, а не заполняет страницу).
   Правится шкалой отступов, а не удалением содержимого — ни одного смысла со страницы
   не убрано. */
@media (max-width: 47.99rem) {
  .section { padding-block: var(--sp-7); }
  .section > * + * { margin-top: var(--sp-5); }
  .stack-wide > * + * { margin-top: var(--sp-6); }
}

/* ─── Типографика ──────────────────────────────────────────────────────────────────────── */

.display {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-display);
  /* Межстрочный РОВНО единица — замер эталона. При весе 500 и таком кегле строки встают
     плотным блоком, и это главный признак «набрано», а не «напечатано плакатом». */
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-tight);
  font-weight: var(--w-display);
  text-wrap: balance;
  max-width: 20ch;
}

/* На телефоне заголовок переносится в три-четыре строки, и при единице выносные элементы
   букв («у», «р», «ц») цепляют строку снизу. Разжимаем — только там, где это нужно. */
@media (max-width: 47.99rem) {
  .display { line-height: 1.08; max-width: none; }
}

/* Залитое слово выше строки текста: у плашки свои поля. Разжимаем ТОЛЬКО заголовки, в
   которых плашка есть, — без неё тугой межстрочный это приём, а не недосмотр. */
.display:has(.fill-brand, .fill-accent) { line-height: 1.12; }

/* h1 и h2 ОДНОГО КЕГЛЯ. Это не экономия на шкале, а замер эталона: там оба 41 px, и
   разницу между «главным заголовком страницы» и «заголовком раздела» держит воздух вокруг
   них. Кегль сообщает громкость, расстояние сообщает связь (Бирман) — и когда громкость
   отдана всему подряд, страница кричит ровным фоном. */
h1, .h1 { margin: 0; font-family: var(--font-display); font-size: var(--fs-h1); line-height: var(--lh-heading); letter-spacing: var(--tracking-tight); font-weight: var(--w-display); }
h2, .h2 { margin: 0; font-family: var(--font-display); font-size: var(--fs-h2); line-height: var(--lh-heading); letter-spacing: var(--tracking-tight); font-weight: var(--w-display); }
h3, .h3 { margin: 0; font-size: var(--fs-h3); line-height: 1.25; font-weight: var(--w-medium); }

/* ПЕРЕНОС ПО СЛОГАМ В ЗАГОЛОВКАХ — НА ЛЮБОЙ ШИРИНЕ. Правило было заведено 30.07.2026
   только для телефона, и это оказалось ошибкой: на 1440 заголовок карточки товара
   «Электробезопасность, полный доступ, 7 суток» разорвался посреди слова —
   «Электробезопасност / ь». Узкой карточка бывает не только на телефоне: на 1440 она
   стоит в колонке шириной в строку текста, и слово в 20 букв на кегле 41 px в неё не
   влезает точно так же.

   Ломает его не небрежность, а `overflow-wrap: break-word` на теле страницы — правило
   нужное (без него длинный код области распирает страницу вбок), но рвёт оно ГДЕ ПРИДЁТСЯ
   и без чёрточки. `hyphens: auto` при `lang="ru"` даёт браузеру словарь и переносит по
   слогам, с дефисом; аварийный разрыв остаётся последним средством, до которого дело
   доходит, только если слово не переносится вовсе.

   Ширину в условии не пишем намеренно: «узко» — это отношение слова к его контейнеру,
   а не число пикселей в окне. Любое правило вида «только до 48rem» ловит телефон и
   пропускает карточку на ноутбуке, что и произошло.

   ЗАГОЛОВОК ПЕРВОГО ЭКРАНА (.display) СЮДА НЕ ВХОДИТ, и это проверено снимком, а не
   рассуждением. Он короткий, помещается и без переносов, а `hyphens: auto` работает жадно:
   браузер переносит там, где ему удобнее уложить строку, и на 390 «и открыты все группы»
   превращалось в «и откры-ты все группы». В главном заголовке страницы это выглядит как
   опечатка. Его переносы расставлены руками неразрывными пробелами — там, где надо. */
h1, .h1, h2, .h2, h3, .h3 {
  hyphens: auto;
  /* ПЕРЕНОС — АВАРИЙНОЕ СРЕДСТВО, А НЕ СПОСОБ ПЛОТНЕЕ НАБИТЬ СТРОКУ. Без этого предела
     браузер переносит жадно: заголовок карточки товара он разложил как
     «Электробезопасность, пол- / ный доступ», хотя «полный» спокойно уезжало на следующую
     строку целиком. Слово из шести букв, разрубленное ради трёх символов в конце строки,
     читается как опечатка.
     Предел 14 знаков оставляет перенос ровно тем словам, ради которых он и нужен, —
     «электробезопасности», «непромышленных», «электроустановках»; всё короче переносится
     целиком. Семь букв до чёрточки и шесть после — чтобы обрубок был читаемым куском
     слова, а не слогом. */
  hyphenate-limit-chars: 14 7 6;
}

/* Отдельной строкой и после: заголовок первого экрана — это тег h1 С КЛАССОМ display,
   то есть под правило выше он попадает по тегу. Убрать его из перечня тегов нельзя,
   отменить переносы — можно. Поймано снимком: первая правка выглядела сделанной, а
   «откры-ты» осталось на месте. */
.display { hyphens: manual; }

/* ЗАГОЛОВОК ИЗ ДАННЫХ. Имя товара и текст вопроса билета приходят из каталога, и укоротить
   их вёрсткой нельзя. На общем кегле h2 (41 px) имя товара не помещалось в карточку ни на
   одной ширине: на 1440 рвалось посреди слова, на 390 набиралось тремя строками с двумя
   переносами подряд («Электробезопас- / ность, полный до- / ступ»). Три переноса в трёх
   строках — это не набор, это лестница.
   Своя ступень кегля (--fs-h2-data) решает причину, а не следствие: самое длинное слово
   каталога влезает в карточку целиком и на телефоне, и на ноутбуке. Гарнитура, вес и
   межстрочный остаются заголовочными — уровень в иерархии не понижается, понижается
   только громкость. */
.h2--data { font-size: var(--fs-h2-data); }

/* ЗАГОЛОВОК СТРАНИЦЫ ВОПРОСА. Отдельный класс, потому что здесь заголовок — не название, а
   целая формулировка из перечня: в энергонадзоре встречаются вопросы по 250 знаков вместе с
   реквизитами приказа. Замер снимком 390×844: на кегле заголовка из данных (20,8 px) такой
   вопрос занимал полтора экрана, и варианты ответа — то, за чем человек пришёл, — начинались
   за прокруткой.
   Поэтому кегль здесь на ступень ниже (кегль h3), а начертание — обычное, не дисплейное:
   двести знаков дисплейным весом читаются как плакат, а не как вопрос. Крупным остаётся
   короткий заголовок, и это верно: длина текста и есть тот сигнал, по которому кегль
   выбирается. */
.question-title {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--fs-h3);
  line-height: 1.3;
  font-weight: var(--w-medium);
  letter-spacing: 0;
}

@media (min-width: 48rem) {
  .question-title { font-size: var(--fs-h2-data); }
}


.lead { font-size: 1.1875rem; line-height: 1.5; font-weight: var(--w-light); color: var(--c-ink-muted); }
.muted { color: var(--c-ink-muted); }
/* Мелкое возвращается к 400: 300 на 15 и 13 px читается с телефона на улице плохо, и это
   не «на глаз» — это аудитория, половине которой за сорок. */
.small { font-size: var(--fs-small); font-weight: var(--w-regular); }
.micro { font-size: var(--fs-micro); font-weight: var(--w-regular); }

/* Кикер — надзаголовок. Эффект «моно» держится на разрядке и капитализации, а не на
   моноширинном шрифте: системные моно на Android кириллицу почти не покрывают, и строка
   поплыла бы разными гарнитурами (feedback/webfont-latin-cyrillic-subsets-both.md). */
.kicker {
  display: inline-block;
  font-size: var(--fs-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--tracking-kicker);
  text-transform: uppercase;
  color: var(--c-brand-tint-ink);
}

/* Зелёный АКЦЕНТОМ: слово внутри заголовка подкрашивается. Раньше здесь было «щедро» и
   заливка — от этого зелёного стало много, а звенеть он перестал. */
.ink-brand { color: var(--c-link); }

/* Залитое слово — не строка текста, а плашка, и высота у неё своя. Пока плашка была
   строчной, её высоту задавали метрики Manrope (≈1.36em), и при любом межстрочном меньше
   этого соседние плашки наезжали друг на друга. Отсюда inline-block со своим межстрочным и
   своими полями: высота плашки становится предсказуемой (1.16em) и укладывается в 1.2
   межстрочного заголовка.
   Заливка не рвётся посреди суммы: на 390 px «199 ₽» разъезжалось на две строки, и
   половинка плашки наезжала на следующую строку. Отсюда nowrap. */
.fill-brand,
.fill-accent {
  display: inline-block;
  line-height: 1.02;
  /* Тесная посадка: заливка обнимает буквы, а не висит вокруг них. Снизу больше, чем
     сверху, — под выносные элементы букв «р» и «у». */
  padding: 0.04em 0.24em 0.09em;
  border-radius: var(--r-sm);
  white-space: nowrap;
}

/* Тёмный текст на светлом кислотном — тот же приём, что на главной кнопке, и заливка
   здесь ОДНА на экран. Прежняя версия (белым по тёмному изумруду) давала корректные
   5.36:1 и мёртвый цвет; эта даёт 10.11:1 и живой. */
.fill-brand {
  background: var(--c-cta);
  color: var(--c-cta-ink);
}

.fill-accent {
  background: var(--c-accent);
  color: var(--c-accent-ink);
}

a { color: var(--c-link); text-underline-offset: 0.18em; }
a:hover { color: var(--c-brand); }

/* ─── Логотип ──────────────────────────────────────────────────────────────────────────── */

/* ЛОГОТИП — ЭТО СЛОВО. Имя платформы, набранное заголовочной гарнитурой, слитно: предлог
   строчными, последнее слово капсом белым в залитой бренд-зелёной плашке, зазора между ними
   нет (решение Алексея 30.07.2026, docs/design/logo/README.md).

   Картинка собирается на лету из имени (render.js, `brandLogo`), поэтому здесь только
   посадка: высота и цвет чернил. Ширину задаёт сама надпись — у SVG свой viewBox, и при
   `height` + `width: auto` она считается сама. Ниже 16 px логотип не опускать: проверено
   снимком, там строчное «по» перестаёт читаться.

   ЦВЕТ ЗАДАЁТСЯ ЗДЕСЬ ТОЛЬКО ОДИН — ЧЕРНИЛА. Внутри картинки три роли краски: предлог берёт
   `currentColor` (то есть это свойство), плашка — `var(--c-brand)`, слово в плашке —
   `var(--c-brand-ink)`, и последние две живут своей жизнью в обеих темах.

   Чернила — `--c-ink`, а НЕ `--c-brand`, как было до 30.07.2026. Тогда логотип был
   одноцветным и весь красился бренд-зелёным; теперь зелёный ушёл в плашку, и зелёный предлог
   рядом с ней означал бы три цвета вместо двух — ровно то, за что забракован отвергнутый
   вариант C. На тёмной теме `--c-ink` светлеет сам, поэтому второй версии файла не нужно. */
.site-logo {
  display: inline-flex;
  align-items: center;
  flex: none;
  min-width: 0;
  text-decoration: none;
  color: var(--c-ink);
}

.site-logo .logo { display: block; height: 1.75rem; width: auto; max-width: 100%; }
/* Наведение мышью гасит ЧЕРНИЛА до бренд-зелёного, плашка остаётся на месте: подсветить
   заливку нечем — она и так самое яркое пятно в шапке. */
.site-logo:hover { color: var(--c-brand); }
.site-logo--footer .logo { height: 1.5rem; }

/* ─── Знак и локап ─────────────────────────────────────────────────────────────────────── */

/* Знак геометрический, без букв: имя стоит рядом текстом, и правка имени знак не трогает.
   Рисуется scripts/brand-mark.mjs (вариант — ACTIVE_MARK), здесь только посадка.

   На страницах портала локап больше не стоит — там логотип. Правила остались ради двух
   мест, где разметку собирает не наш шаблон: шапки тренажёра (её пишет showcase/build.py)
   и страницы-эталона /ui-kit, где локап показан как отдельный предмет библиотеки. */
.lockup { display: inline-flex; align-items: center; gap: var(--sp-3); text-decoration: none; color: inherit; }
.lockup__mark { width: 2.25rem; height: 2.25rem; flex: none; display: block; color: var(--c-brand); }
/* Фигуры знака берут currentColor, выделенный пункт — акцент. Так знак перекрашивается вместе
   с темой: через <img> это невозможно, картинка цвет страницы не наследует.
   И fill, и stroke — потому что у разных вариантов знака акцент нарисован по-разному:
   у включённого это залитая полоса, у «галочки» — обводка. Правило обязано пережить смену
   варианта, иначе акцент молча станет цветом текста. */
.mark { display: block; width: 100%; height: 100%; }
.mark__dot { fill: var(--c-accent); }
.mark__dot--stroke { fill: none; stroke: var(--c-accent); }
.lockup__text { display: flex; flex-direction: column; line-height: 1.1; }
.lockup__name { font-weight: var(--w-semibold); letter-spacing: var(--tracking-tight); font-size: 1.1875rem; }
.lockup__tagline { font-size: var(--fs-micro); color: var(--c-ink-muted); }

/* ─── Шапка ────────────────────────────────────────────────────────────────────────────── */

/* ВЫСОТА ШАПКИ ОБЪЯВЛЕНА ЧИСЛОМ, потому что на неё смотрит не только сама шапка: от неё
   считается `scroll-padding-top` (иначе якорь уезжает под липкую шапку). Держать это число
   в двух местах значит однажды поправить одно и забыть второе. */
:root { --header-height: 5rem; }

.site-header {
  position: sticky;
  top: 0;
  z-index: 20;
  background: color-mix(in srgb, var(--c-bg) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--c-line);
}

.site-header__row {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  min-height: 5rem;
}

/* Форма поиска в первом экране главной. Поле и кнопки стоят в столбик, а не в строку:
   подпись поля («Кусок вопроса или пункт нормы») объясняет, что сюда набирать, и на строке
   рядом с кнопкой ей места нет. Ограничение по ширине — чтобы поле не растягивалось на всю
   колонку первого экрана: строка ввода длиной в полметра выглядит как поле для абзаца. */
.form--inline { max-width: 34rem; margin-block-end: var(--sp-4); }
.form--inline .field { margin-block-end: var(--sp-3); }

/* ПОЛЕ ПОИСКА В ШАПКЕ. Поиск — первый шаг механики продукта и единственный бесплатный
   инструмент привлечения: он обязан быть виден с любой страницы, а не лежать пунктом меню.

   Поле забирает всю свободную ширину строки (`flex: 1`), а не имеет фиксированный размер:
   в шапке рядом стоят логотип, меню и кнопка кабинета, и любая фиксированная ширина
   разъедется на первом же новом пункте меню.

   Кнопка «Найти» рядом с полем, а не вместо него: форма обязана отправляться и без
   Enter — на телефоне клавиатура показывает «перейти», а не «искать», и часть людей ищет
   глазами кнопку. */
.site-search {
  display: flex;
  align-items: stretch;
  gap: var(--sp-1);
  /* Растягиваться полю незачем, а сжиматься ниже 12rem нельзя: замер снимком на 1440 показал
     обрезанную подсказку («Найти вопр…»), то есть поле, которое не объясняет, что в него
     набирать. Основание 20rem, нижняя граница 12rem. */
  flex: 0 1 20rem;
  min-width: 12rem;
}

.site-search__input {
  flex: 1 1 auto;
  min-width: 0;
  font: inherit;
  /* РОВНО 16 px, И ЭТО НЕ ВОПРОС ВКУСА. Safari на iOS при фокусе в поле с текстом мельче
     16 px увеличивает страницу целиком — и обратно сам не возвращает. Шапка после этого
     уезжает за край, а вместе с ней логотип, меню и кнопка кабинета: человек, набравший
     запрос, теряет навигацию. Стояло var(--fs-small) = 15 px, то есть на один пиксель
     ниже порога. Токен мельче сюда ставить нельзя — поле ввода единственное место, где
     размер шрифта меняет поведение браузера, а не только вид. */
  font-size: 1rem;
  color: var(--c-ink);
  background: var(--c-surface);
  border: 1px solid var(--c-line);
  border-radius: var(--r-pill);
  padding: 0.45rem 0.9rem;
}

.site-search__input::placeholder { color: var(--c-ink-muted); }
.site-search__input:focus-visible { outline: 2px solid var(--c-accent); outline-offset: 1px; }

.site-search__submit {
  flex: none;
  font: inherit;
  font-size: var(--fs-small);
  color: var(--c-ink);
  background: transparent;
  border: 1px solid var(--c-line);
  border-radius: var(--r-pill);
  padding: 0.45rem 0.9rem;
  cursor: pointer;
}

.site-search__submit:hover { border-color: var(--c-accent); }

/* Правый верхний угол. Там человек ищет «войти» или «купить» — и до 29.07.2026 находил
   тумблер темы. Это было названо ошибкой дважды подряд, поэтому здесь теперь вход в
   кабинет, а тумблер уехал в подвал.
   Кнопка КОНТУРНАЯ ПИЛЮЛЯ зелёным — единственное место, где пилюля осталась: так стоит
   кнопка в шапке эталона, и так она не спорит с залитой главной кнопкой на странице. */
.site-header__action { margin-inline-start: var(--sp-2); flex: none; }

/* Обёртка меню. На широком экране она — обычный элемент строки и занимает место, которое
   раньше держало само меню: отступ «прижаться к правому краю» переехал сюда. */
.site-menu { margin-inline-start: auto; }

/* Заголовок списка. На широком экране он не нужен — меню и так видно целиком. Прячется
   `display: none`, а не `visibility`: пустая строка на его месте сдвинула бы пункты. */
.site-menu__toggle { display: none; }

/* ГРАНИЦА МЕЖДУ «МЕНЮ СТРОКОЙ» И «МЕНЮ СПИСКОМ» — 1080 px, А НЕ 768.
   Стояло 48rem (768 px), и диапазон 768-1024 разваливался: строка меню в шапке уже не
   помещалась в одну линию, но список ей на смену ещё не приходил. Замер 25.08.2026 на
   главной: 768 px — шапка 237 px (пункты меню легли в четыре ряда), 900 px — 93 px в двух
   рядах, и только около 1024 строка сходилась. Четверть экрана планшета отдавалась меню,
   которым пользуются раз за визит, — ровно та беда, ради которой список и заведён.
   1080 px выбрано с запасом над самой широкой раскладкой строки: пять пунктов, поле поиска
   на 20rem, логотип и кнопка кабинета. Ниже границы работает раскрывающийся список — он
   держит шапку в одну строку на любой ширине. */
@media (min-width: 67.5rem) {
  /* Содержимое раскрывается принудительно: на широком экране меню обязано быть видно, не
     спрашивая. Это единственный законный способ показать содержимое закрытого <details> —
     отменить `content-visibility: hidden`, который вешает на него сам браузер. */
  .site-menu::details-content { content-visibility: visible; block-size: auto; }
}

.site-nav {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  flex-wrap: wrap;
}

.site-nav__link {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  padding-inline: var(--sp-3);
  border-radius: var(--r-sm);
  color: var(--c-ink);
  text-decoration: none;
  font-size: var(--fs-small);
  font-weight: var(--w-regular);
  transition: background var(--motion), color var(--motion);
}

.site-nav__link:hover { background: var(--c-brand-tint); color: var(--c-brand-tint-ink); }
.site-nav__link[aria-current='page'] { background: var(--c-brand-tint); color: var(--c-brand-tint-ink); font-weight: var(--w-medium); }

/* На телефоне и планшете меню уезжает в раскрывающийся список — иначе шапка ломается
   в четыре ряда и съедает первый экран. Граница та же 1080 px, что и у правила выше:
   две половины одного решения, разъехаться им нельзя. */
@media (max-width: 67.49rem) {
  /* Шапка здесь в одну строку: логотип, заголовок списка, поле поиска, кнопка кабинета. */
  :root { --header-height: 4.5rem; }
  /* Отбивка в строке шапки уменьшена: в 390 px стоят три вещи — логотип, заголовок меню и
     кнопка кабинета, — и на отбивке 16 px строка не сходится тремя пикселями. Кнопка
     кабинета переносилась на вторую строку, съедая ровно ту высоту, ради которой меню и
     убрали в список. Поймано снимком. */
  .site-header__row { flex-wrap: wrap; gap: var(--sp-2); padding-block: var(--sp-3); min-height: 4rem; }
  /* ПОЛЕ ПОИСКА ЗДЕСЬ УСТУПАЕТ, А НЕ ДЕРЖИТ СВОИ 20rem. Замер на 768 px: логотип 193 +
     заголовок списка 95 + поле 320 + кнопка кабинета 95 + отбивки 24 + поля полосы 64 = 791
     при полосе 768, и кнопка кабинета уезжала на вторую строку — шапка 121 px вместо 69.
     При переносе строки браузер сначала переносит и только потом сжимает, поэтому лечится
     это не минимальной шириной, а основанием: поле забирает остаток строки, а не требует
     своего. Ниже 12rem оно всё равно не сожмётся (min-width в основном правиле). */
  .site-search { flex: 1 1 12rem; }
  /* Кнопка кабинета прижата к ПРАВОМУ краю и на телефоне тоже. До 30.07.2026 она стояла
     впритык к логотипу, а правая половина строки пустовала: угол, ради которого тумблер
     темы выселили в подвал, на телефоне был отдан пустоте. Поймано на снимке 390 px. */
  .site-header__action { margin-inline-start: auto; }

  /* ТРИ РЕШЕНИЯ ОДНОЙ БЕДЫ, ПО ПОРЯДКУ — ЧТОБЫ НЕ ВЕРНУЛИСЬ К ОТВЕРГНУТЫМ.

     Беда: пять пунктов меню, ширина 390 px. В строку помещаются три; «Актуальность» и
     «Вопрос-ответ» лежат за правым краем. Строка при этом читается целой — повода тянуть
     её пальцем не возникает, и два раздела из пяти на телефоне просто не существуют.
     Это Нильсен №6: узнавание вместо припоминания, человек не обязан догадываться, что
     список длиннее, чем выглядит.

     1. ПЕРЕНОС НА ВТОРУЮ СТРОКУ — отвергнут замером: пять пунктов ложатся в три ряда,
        липкая шапка вырастает с 90 до 205 px. Четверть экрана телефона отдана меню,
        которым пользуются раз за визит.
     2. ПРОКРУТКА ВБОК С РАСТВОРЕНИЕМ КРАЯ — стояло до 31.07.2026. Лечило симптом:
        растворение сообщает «здесь есть ещё», но не «здесь есть ещё ДВА РАЗДЕЛА», и
        тянуть строку всё равно нужно догадаться. Приёмка назвала это недоделкой честно.
     3. РАСКРЫВАЮЩИЙСЯ СПИСОК — здесь. Закрытый он занимает одну строку, открытый показывает
        все пять пунктов столбиком, по одному на палец. Липкая шапка не растёт вовсе:
        открывает список человек, и открытое состояние — его решение, а не наша плата
        за длинное меню. Ни строки скрипта: <details> открывается браузером, знает
        клавиатуру и экранный диктор сам. */
  /* Обёртка остаётся В ПЕРВОЙ СТРОКЕ, и это второй выигрыш от списка после самих разделов.
     Своей строки меню больше не занимает — липкая шапка на телефоне ужалась со 125 до 64 px,
     то есть полсотни пикселей возвращены содержимому на КАЖДОЙ странице, а не только на
     главной. Открытый список ложится поверх страницы, а не раздвигает шапку. */
  .site-menu {
    margin-inline-start: var(--sp-2);
    margin-inline-end: auto;
  }

  /* Отбивка перед кнопкой кабинета на телефоне снимается: между ней и меню и так стоит
     отбивка строки. Восемь пикселей здесь — не косметика, а разница между одной строкой
     и двумя. ЗАМЕР на 390 px: полоса содержимого 358, а строке нужно было 360. */
  .site-header__action { margin-inline-start: 0; }

  /* Заголовок списка — настоящая кнопка на палец: 44 px высоты, как все прочие цели.
     Стрелка своя, треугольник браузера убран: он разного вида в каждом движке и не
     подчиняется цвету темы. */
  .site-menu__toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    min-height: var(--tap);
    padding-inline: var(--sp-2);
    border-radius: var(--r-sm);
    color: var(--c-ink);
    font-size: var(--fs-small);
    font-weight: var(--w-medium);
    cursor: pointer;
    list-style: none;
    user-select: none;
  }
  .site-menu__toggle::-webkit-details-marker { display: none; }

  /* Стрелка — та же фигура в обоих состояниях, поворотом. Два разных знака («+» и «−»,
     «▾» и «▴») человек сверяет между собой; один повёрнутый читается сразу. */
  .site-menu__toggle::after {
    content: '';
    width: 0.5rem;
    height: 0.5rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: translateY(-0.15em) rotate(45deg);
    transition: transform var(--motion);
  }
  .site-menu[open] .site-menu__toggle::after { transform: translateY(0.1em) rotate(-135deg); }

  .site-menu__toggle:hover { background: var(--c-brand-tint); color: var(--c-brand-tint-ink); }

  /* Открытый список ЛОЖИТСЯ ПОВЕРХ страницы, во всю её ширину, а не раздвигает шапку.
     Отсчёт от .site-header: она липкая, а липкая — уже позиционированная, отдельной
     обёртки не нужно.

     Фон СПЛОШНОЙ, не полупрозрачный как у шапки: сквозь размытие проступал бы текст
     страницы, и пункты меню читались бы поверх чужих букв. Тень отделяет список от
     содержимого — иначе на длинной странице непонятно, где он кончается. */
  .site-nav {
    position: absolute;
    inset-inline: 0;
    top: 100%;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: var(--sp-2) var(--sp-4) var(--sp-3);
    background: var(--c-bg);
    border-bottom: 1px solid var(--c-line);
    box-shadow: var(--shadow-card);
  }
  .site-nav__link { justify-content: flex-start; }
}

/* ТЕСНОТА ТЕЛЕФОНА — ОТДЕЛЬНО ОТ ГРАНИЦЫ МЕНЮ, и это не мелочь. До 25.08.2026 обе беды
   лечились одним медиазапросом на 768 px, и когда граница меню уехала на 1080, поле поиска
   вместе с ней уехало бы на свою строку даже на планшете 1024 — где рядом с логотипом,
   заголовком меню и кнопкой кабинета ему хватает места с запасом. Лишняя строка в липкой
   шапке на КАЖДОЙ странице — цена, которую платить не за что.
   Здесь остаётся только то, что вызвано теснотой физически: 390 px. */
@media (max-width: 47.99rem) {
  /* На телефоне шапка в две строки: поле поиска стоит под первой. Замер на 390 px — 119 px. */
  :root { --header-height: 7.5rem; }
  /* Поле поиска на телефоне уезжает на свою строку целиком. В 390 px рядом с логотипом,
     заголовком меню и кнопкой кабинета ему осталось бы сантиметра три — а поле, в которое
     не видно набранного, хуже поля на отдельной строке. */
  .site-search { flex-basis: 100%; order: 5; }
  /* Логотип на телефоне мельче: в 390 px рядом с ним стоят ещё две цели, и на 28 px
     строка перестаёт помещаться — страница начинает ездить вбок. Поймано скриншотом,
     а не прикинуто. Ниже 16 px не опускаемся: там плашка залипает. */
  .site-header .site-logo .logo { height: 1.25rem; }
}

/* УЗКИЕ ТЕЛЕФОНЫ, 360 px и ниже. Замер: полоса содержимого 328 px, строке шапки нужно 344 —
   и кнопка кабинета снова уезжала во вторую строку, возвращая ту самую высоту, ради которой
   меню и убрали в список. Логотип здесь 16 px вместо 20: это нижняя граница, ниже плашка
   «ПУНКТУ» залипает в сплошное пятно. 360 dp — не экзотика, а расхожий недорогой Android. */
@media (max-width: 23.5rem) {
  .site-header .site-logo .logo { height: 1rem; }
}

/* ЗАПАСНОЙ ПУТЬ ДЛЯ БРАУЗЕРОВ БЕЗ `::details-content`. Там содержимое закрытого <details>
   спрятано самим браузером, и отменить это нечем: спрятанный заголовок списка означал бы
   шапку вообще без меню. Поэтому на широком экране заголовок остаётся видимым, и меню
   работает списком — хуже, чем строкой, но целым. Проверка стоит ПОСЛЕ основного правила
   намеренно: где селектор поддержан, ничего не срабатывает. */
@supports not selector(::details-content) {
  @media (min-width: 67.5rem) {
    .site-menu__toggle {
      display: inline-flex;
      align-items: center;
      min-height: var(--tap);
      padding-inline: var(--sp-3);
      border-radius: var(--r-sm);
      color: var(--c-ink);
      font-size: var(--fs-small);
      cursor: pointer;
    }
  }
}

/* ─── Тумблер темы (В ПОДВАЛЕ) ─────────────────────────────────────────────────────────── */

/* ГДЕ ОН СТОИТ И ПОЧЕМУ НЕ В ШАПКЕ. Правый верхний угол — место, куда человек смотрит за
   «войти» и «купить». Тумблер темы там перехватывает этот взгляд и ничего за него не
   отдаёт: тему меняют один раз в жизни, а вход ищут каждый раз. Замечание про угол
   прозвучало дважды подряд, поэтому тумблер переехал в подвал — туда, где живут
   настройки и служебное, а разметка (theme.js) на переезд рассчитана изначально:
   слушатель висит на документе, а не на кнопках шапки.

   Сам тумблер остаётся двумя подписями, а не иконкой-загадкой: какое состояние сейчас —
   видно, не наведя курсор (эвристика Нильсена №1 и «узнавание вместо припоминания» №6). */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: 0.25rem;
  border: 1px solid var(--c-line);
  border-radius: var(--r-pill);
  background: var(--c-surface);
}

/* В подвале тумблер стоит своей строкой над юридической — не в ряду ссылок: он не раздел
   и не документ. */
.site-footer__theme { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; padding-block: var(--sp-5); border-top: 1px solid var(--c-line); }
.site-footer__theme-label { font-size: var(--fs-micro); color: var(--c-ink-muted); }

/* 44 px, как у всех прочих целей: стояло 2.25rem (36 px) — тумблер темы был самой мелкой
   нажимаемой вещью на странице. */
.theme-toggle__button {
  min-height: var(--tap);
  padding-inline: var(--sp-3);
  border: 0;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--c-ink-muted);
  font: inherit;
  font-size: var(--fs-micro);
  font-weight: var(--w-medium);
  cursor: pointer;
  transition: background var(--motion), color var(--motion);
}

.theme-toggle__button[aria-pressed='true'] {
  background: var(--c-brand);
  color: var(--c-brand-ink);
}

/* ─── Кнопка ───────────────────────────────────────────────────────────────────────────── */

/* РАДИУС 10 px, НЕ ПИЛЮЛЯ. Пилюля была ошибкой чтения эталона: замер даёт 10,25 px
   (docs/design/croc-reference.md). Разница не косметическая — пилюля читается как чип
   или тег, прямоугольник со скруглением читается как кнопка. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: var(--tap);
  padding: 0.75rem var(--sp-5);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  font: inherit;
  font-weight: var(--w-medium);
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: transform var(--motion), background var(--motion), color var(--motion), border-color var(--motion);
}

.btn:active { transform: translateY(1px); }

/* ГЛАВНОЕ ДЕЙСТВИЕ: светлая кислотная заливка, почти чёрный текст, 10.11:1.
   Тени под ней нет намеренно — цвет и так вспышка, тень делала бы из неё «объект»
   вместо пятна. */
.btn--primary { background: var(--c-cta); color: var(--c-cta-ink); }
.btn--primary:hover { background: var(--c-cta-hover); color: var(--c-cta-hover-ink); }

.btn--accent { background: var(--c-accent); color: var(--c-accent-ink); }
.btn--accent:hover { filter: brightness(0.96); }

.btn--outline { background: transparent; color: var(--c-ink); border-color: var(--c-line-strong); }
.btn--outline:hover { background: var(--c-brand-tint); color: var(--c-brand-tint-ink); border-color: transparent; }

.btn--ghost { background: transparent; color: var(--c-link); padding-inline: var(--sp-3); }
.btn--ghost:hover { background: var(--c-brand-tint); }

/* Контурная пилюля зелёным — кнопка ШАПКИ, и единственное место, где пилюля уместна.
   Так стоит кнопка в шапке эталона: она не спорит с залитой кнопкой на странице, потому
   что отличается и формой, и весом цвета. */
.btn--pill {
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--c-link);
  border-color: var(--c-brand);
  padding-inline: var(--sp-4);
  min-height: 2.75rem;
  font-size: var(--fs-small);
}

.btn--pill:hover { background: var(--c-brand-tint); color: var(--c-brand-tint-ink); border-color: var(--c-brand-tint); }

.btn--lg { min-height: 3.5rem; padding-inline: var(--sp-6); font-size: 1.0625rem; }
.btn--block { width: 100%; }

.btn[aria-disabled='true'], .btn:disabled {
  background: var(--c-surface-sunken);
  color: var(--c-ink-muted);
  border-color: var(--c-line);
  cursor: not-allowed;
  box-shadow: none;
}

.btn-row { display: flex; flex-wrap: wrap; gap: var(--sp-4); align-items: center; }

/* ─── Карточка-«пузырь» ────────────────────────────────────────────────────────────────── */

.bubble {
  background: var(--c-surface);
  border: 1px solid var(--c-line);
  border-radius: var(--r-xl);
  padding: var(--sp-6);
  box-shadow: var(--shadow-card);
}

/* Воздух по Бирману: внутри карточки элементы жмутся друг к другу, между карточками —
   расстояние втрое больше. Иначе глаз не собирает карточку в одно целое. */
.bubble > * + * { margin-top: var(--sp-3); }
.bubble--tint { background: var(--c-brand-tint); border-color: transparent; color: var(--c-brand-tint-ink); }

/* Пояснительная карточка: БЕЛАЯ, с короткой зелёной чертой сверху.
   Она заменила тинт на главной, и вот из-за чего. Мятных плашек в первом развороте
   набралось четыре штуки крупным планом — «зелёного по площади много, а звенеть ему негде».
   У эталона зелёный работает акцентом и тинтом на десять процентов, а не заливкой карточек
   в полполосы. Черта в 2,5rem даёт ту же принадлежность к бренду при площади в тридцать раз
   меньше — и белая карточка на сером фоне читается дороже мятной. */
.bubble--note { position: relative; padding-top: var(--sp-7); }
.bubble--note::before {
  content: '';
  position: absolute;
  top: var(--sp-6);
  inset-inline-start: var(--sp-6);
  width: 2.5rem;
  height: 3px;
  border-radius: var(--r-pill);
  background: var(--c-brand);
}

/* Карточка цены. БЫЛА сплошной заливкой бренд-зелёным — ровно тот случай, про который
   сказано «зелёного много, а он не звенит»: цвет уходил в фон трёхсот квадратных
   сантиметров и переставал что-либо значить. Стала тинтом с контурной чертой бренда, а
   звенит на ней кислотная кнопка. Зелёного стало меньше, зелёный стало видно. */
/* Карточка цены. Она единственная крупная мятная плашка, оставшаяся на главной, — и это
   осознанно: если тинт бренда где-то и нужен полем, то на предложении, а не на пояснениях.
   Колонкой она сделана ради нижней кнопки: в сетке карточки тянутся до высоты соседа, и
   под кнопкой оставалось полкарточки пустой мяты. Кнопка прижата к низу — поле работает. */
.bubble--brand {
  display: flex;
  flex-direction: column;
  background: var(--c-brand-tint);
  border-color: var(--c-brand);
  color: var(--c-brand-tint-ink);
}

/* `align-self` обязателен: в колонке-флексе элемент растягивается на всю ширину, и кнопка
   стала бы полосой во всю карточку — а главная кнопка страницы уже стоит в первом экране
   своего размера. Две кнопки одного действия разной ширины читаются как две разные. */
.bubble--brand > .btn { margin-top: auto; align-self: start; }
.bubble--brand .muted, .bubble--brand .kicker { color: var(--c-brand-tint-ink); opacity: 0.8; }
.bubble--flat { box-shadow: none; }
.bubble--tight { padding: var(--sp-5); border-radius: var(--r-lg); }

/* Карточка-ссылка — КОЛОНКА, а не блок, и хвост в ней прижат к низу. Замечено на снимке
   каталога: в ряду из трёх карточек описания разной длины, и служебная строка снизу
   («17 из 22 областей открыто · 675 вопросов») вставала у каждой на своей высоте — три
   карточки одной высоты с разъехавшимися подвалами читаются как три разных компонента.
   Прижатый низ даёт ряду общую нижнюю линию; `padding-top` держит минимальный воздух там,
   где карточка ровно по содержимому и прижимать нечего. */
.bubble--link { display: flex; flex-direction: column; text-decoration: none; color: inherit; }
.bubble--link > :last-child:not(:only-child) { margin-top: auto; padding-top: var(--sp-3); }
.bubble--link:hover { transform: translateY(-3px); box-shadow: var(--shadow-float); color: inherit; }
.bubble--link { transition: transform var(--motion), box-shadow var(--motion); }

.grid { display: grid; gap: var(--sp-5); }
/* min(…, 100%) обязателен: голый minmax(20rem, 1fr) на экране 320 px требует колонку в
   320 px плюс поля полосы — и страница уезжает вбок на 16 px. Замерено, а не предположено.

   Порог поднят с 16rem до 21rem 30.07.2026, и это счёт, а не вкус. При 16rem в полосу
   1152 px влезало ЧЕТЫРЕ колонки, а карточек направлений девять: получалось 4 + 4 + 1, и
   последний ряд стоял одинокой карточкой при трёх пустых местах — страница выглядела
   недособранной. При 21rem колонок три, девять карточек ложатся ровно 3 × 3, а карточка
   становится шире и перестаёт рвать заголовки вроде «Гражданская оборона и защита от
   чрезвычайных ситуаций» на пять строк. */
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(21rem, 100%), 1fr)); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr)); }

/* КОЛОНКИ НЕ СХЛОПЫВАЮТСЯ ПОД ЧИСЛО КАРТОЧЕК. Нужно там, где рядом стоят перечни разной
   длины: на каталоге в разделе «Работает» карточка одна, в двух других по четыре. При
   auto-fit пустые колонки схлопываются, и одинокая карточка растянулась на все 1152 px —
   строка заголовка в ней шла по центру пустого поля, а на той же странице ниже точно такие
   же карточки стояли втрое уже. Три разные ширины одного компонента на одном экране — это
   уже не сетка (Нильсен №4: одинаковое выглядит одинаково).
   auto-fill колонки сохраняет: одинокая карточка занимает свою треть и остаётся карточкой. */
.grid--fill { grid-template-columns: repeat(auto-fill, minmax(min(21rem, 100%), 1fr)); }

/* Карточки НЕ тянутся до высоты соседа. Нужно там, где рядом стоят короткая и длинная:
   на «Сколько стоит» слева цена в пять строк, справа перечень в двадцать — и мятная
   карточка цены растягивалась вдвое, оставляя под кнопкой поле пустой мяты в полкарточки.
   Это ровно та «площадь зелёного, которой негде звенеть». Ряд снизу выходит рваным — и
   пусть: две карточки здесь не пара для сравнения, а предложение и его расшифровка. */
.grid--top { align-items: start; }

/* ─── Шапка внутренней страницы ────────────────────────────────────────────────────────── */

/* Заголовок слева, подводка справа. Приём для страниц-перечней, у которых в первом экране
   больше ничего нет: столбиком заголовок и абзац держат левую половину, а правая пустует до
   самого низа экрана. Ниже 62rem колонка одна — на телефоне читать заголовок и подводку
   рядом невозможно, они станут двумя узкими колодцами. */
.page-head { display: grid; gap: var(--sp-4); }

@media (min-width: 62rem) {
  .page-head {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--sp-8);
    /* По нижнему краю: заголовок высокий, абзац низкий, и выровненные по верху они
       выглядят как две несвязанные колонки. По низу они читаются одной строкой. */
    align-items: end;
  }
  .page-head > .lead { margin: 0; padding-bottom: 0.35em; }

  /* Выравнивание по ВЕРХУ — для шапки раздела, а не страницы. Разница не в декоре:
     у заголовка страницы кегль вдвое крупнее подводки, и выровненные по низу они читаются
     одной строкой. У заголовка раздела подводка длиннее самого заголовка, и по нижнему
     краю она начинается ВЫШЕ него — то есть человек по F-образному просмотру утыкается в
     абзац раньше, чем в заголовок, к которому абзац относится (Нильсен, метод 3: главное
     сообщение в первых двух строках). Поймано на снимке каталога. */
  .page-head--top { align-items: start; }
  .page-head--top > .lead { padding-bottom: 0; }
}

/* ─── Hero с парящими «пузырями» ───────────────────────────────────────────────────────── */

/* ВОЗДУХ СВЕРХУ — главная правка ритма 29.07.2026. У эталона под шапкой до первого
   заголовка почти экранная высота; у нас было 150 px, и страница начиналась сразу, без
   вдоха. Считаем от высоты окна (--sp-hero), а не от кегля: «почти экран» на ноутбуке и
   на телефоне — разные числа.
   Снизу по-прежнему почти ничего: под медиа-блоком висит оговорка со своим отступом, а
   раздел добавляет свои поля. */
.hero { position: relative; padding-block: var(--sp-hero) var(--sp-2); }

/* ДВЕ КОЛОНКИ НА ШИРОКОМ ЭКРАНЕ. Правка 30.07.2026 и исправление названной беды: на 1440
   текст занимал левую половину, а правая половина первого экрана оставалась пустой —
   снимок лежал ПОД текстом, за нижней кромкой окна. У эталона в первом экране медиа есть
   всегда, и человек видит его сразу, не листая.
   Порог 62rem (992 px) — там, где колонка шириной ~550 px ещё имеет смысл. Ниже колонка
   одна: текст, под ним снимок. */
.hero__grid { display: grid; gap: var(--sp-7); }

.hero__text > * + * { margin-top: var(--sp-5); }

/* Медиа-блок — только система координат для «пузырей». Своего фона и своей высоты у него
   нет намеренно: первая редакция держала тинт и min-height, и под невысокой заглушкой
   торчала голая сиреневая полоса — это было видно на первом же скриншоте. Высоту задаёт
   содержимое: фото или заглушка. */
.hero__media {
  position: relative;
  overflow: visible;
}

.hero__media > .photo-slot { min-height: 24rem; }

/* Оговорка «это иллюстрация» стоит в текстовой колонке, под кнопками: она объясняет снимок,
   но набрана мелко и в колонке со снимком превратилась бы в подпись под картинкой — то есть
   в подпись, которую читают. Читать её не нужно, нужно чтобы она БЫЛА. */
.hero__credit { font-size: var(--fs-micro); color: var(--c-ink-muted); }

/* Пузырь висит НАД медиа, частично выходя за его край, — приём эталона. На телефоне
   парение отменяется и карточки становятся обычным потоком: иначе они наезжают друг на
   друга. */
.hero__bubble { position: absolute; width: min(20rem, 78%); }
.hero__bubble--one { left: var(--sp-5); bottom: -2.5rem; }
.hero__bubble--two { right: var(--sp-6); top: -3rem; }
.hero__bubble--medallion { width: min(14rem, 44%); }

@media (min-width: 62rem) {
  /* Текст чуть шире снимка: в левой колонке живёт заголовок первого экрана, и ему нужна
     строка, в которую помещается мысль, а не два слова. */
  .hero__grid {
    grid-template-columns: minmax(0, 1.08fr) minmax(0, 1fr);
    gap: var(--sp-8);
    align-items: center;
  }
  /* Воздух под шапкой урезан РОВНО ЗДЕСЬ и по расчёту, а не по вкусу. «Почти экранная
     высота» до первого заголовка — приём эталона для случая, когда в первом экране кроме
     заголовка ничего нет. У нас теперь есть снимок высотой 4:5 от колонки в ~550 px, то
     есть ~690 px. Шапка 80 + воздух 200 + снимок 690 = 970 при окне 900: нижний край
     снимка уезжал бы за кромку, и приём «медиа видно сразу» ломался бы сам об себя. */
  .hero { padding-block: clamp(2.5rem, 7vh, 5rem) var(--sp-7); }
  /* Медальон свисает вправо в поле полосы (у неё 2rem запаса) — наружу страница от этого
     не едет. Проверяется замером scrollWidth при съёмке, а не на глаз. */
  .hero__bubble--two { right: -1.5rem; top: -2.5rem; }
  .hero__bubble--one { left: var(--sp-4); bottom: -2rem; }
}

@media (max-width: 61.99rem) {
  .hero__media { min-height: 0; background: transparent; }
  .hero__bubble { position: static; width: auto; }
  /* Круг в потоке становится обычной карточкой: круглая плашка во всю ширину телефона —
     это дыра в полстраницы, а не приём.
     ДВА КЛАССА В СЕЛЕКТОРЕ — не стилистика, а необходимость. У .medallion такой же вес
     (один класс), и объявлен он НИЖЕ по файлу, то есть выигрывал: на 390 px круг оставался
     кругом высотой в треть экрана. Правило молчало полсуток и было поймано снимком. */
  .hero__bubble.hero__bubble--medallion {
    aspect-ratio: auto;
    border-radius: var(--r-xl);
    text-align: start;
    /* Ширина тоже перебивается двумя классами: у медальона она min(14rem, 44%), и в потоке
       он вставал узкой карточкой рядом с широкой соседкой — две карточки одного ряда
       разной ширины читаются как недоделанная вёрстка. */
    width: auto;
  }
  .hero__bubble--medallion > * { max-width: none; }
  .hero__bubble + .hero__bubble { margin-top: var(--sp-4); }
  .hero { padding-block: var(--sp-hero) var(--sp-6); }
}

/* ─── Блок во всю ширину со скруглённым краем ──────────────────────────────────────────── */

/* Приём эталона, которого у нас не было: секция выходит за полосу на всю ширину окна и
   скругляется сверху (20 px) и снизу (54 px). Разделы перестают быть одинаковыми лентами
   на общем фоне — часть страницы получает собственную поверхность, и глаз находит, где
   раздел начался и где кончился, без единой разделительной черты.
   Полосу содержимого внутри держит .shell, как и везде: наружная ширина блока — вопрос
   фона, а не строки текста. */
.band {
  background: var(--c-brand-tint);
  color: var(--c-brand-tint-ink);
  border-radius: var(--r-md) var(--r-md) var(--r-band) var(--r-band);
}

.band--surface { background: var(--c-surface); color: var(--c-ink); }
.band--sunken { background: var(--c-surface-sunken); color: var(--c-ink); }
/* Блок, на который «наплывает» фото снизу: нижний край прямой, скруглён только верх. */
.band--top { border-radius: var(--r-md) var(--r-md) 0 0; }

/* Раздел внутри блока: те же поля, но своя тень отменена — фон уже отделяет. */
.band > .section { padding-block: var(--sp-9); }

@media (max-width: 47.99rem) {
  .band > .section { padding-block: var(--sp-7); }
  .band { border-radius: var(--r-md) var(--r-md) var(--r-xl) var(--r-xl); }
}

/* ─── Круглый медальон ─────────────────────────────────────────────────────────────────── */

/* Круг с коротким текстом, наезжающий на снимок ниже, — второй приём эталона, которого у
   нас не было. Работает он не формой, а тем, ЧТО в него кладут: одно число и одна строка
   пояснения. Круг физически не даёт положить туда абзац — и в этом весь смысл: место, где
   нельзя быть многословным.
   Цель нажатия и порог контраста к нему применяются наравне со всем остальным: на
   кислотной заливке текст тёмный, 10.11:1. */
.medallion {
  display: grid;
  place-content: center;
  gap: var(--sp-1);
  aspect-ratio: 1;
  padding: var(--sp-5);
  text-align: center;
  border-radius: var(--r-round);
  background: var(--c-surface);
  border: 1px solid var(--c-line);
  box-shadow: var(--shadow-card);
  /* Потолок диаметра. Без него круг в широкой колонке раздувается до 360 px и превращается
     в зелёный диск на пол-экрана — ровно та ошибка «зелёного много», от которой уходим. */
  max-width: 15rem;
  margin-inline: auto;
}

.medallion > * { margin: 0; max-width: 14ch; margin-inline: auto; }
.medallion--cta { background: var(--c-cta); color: var(--c-cta-ink); border-color: transparent; }
.medallion--cta .muted, .medallion--cta .kicker { color: var(--c-cta-ink); opacity: 0.72; }
.medallion--tint { background: var(--c-brand-tint); color: var(--c-brand-tint-ink); border-color: transparent; }

/* ─── Фотография ───────────────────────────────────────────────────────────────────────── */

/* СНИМКИ ЦВЕТНЫЕ. Дуотон снят 30.07.2026, и это исправление ошибки, а не смена вкуса.

   Дуотон брали как «приём КРОКа»: обесцветить кадр и растянуть его между двумя цветами
   палитры. У эталона снимки при этом СОЧНЫЕ — сине-зелёные переливы, живой свет; тинт там
   лёгкий, поверх цвета. Мы же гасили цвет полностью и получали ровно то, за что дизайн и
   назвали мёртвым: зелёная плёнка на всём, все кадры на одно лицо, разницы между щитом и
   ЛЭП нет.

   Поэтому здесь не осталось ни фильтра, ни слоёв наложения: снимок показывается таким,
   какой он есть. Единственное, что рамка делает, — режет углы и держит пропорцию.
   Заодно это дешевле: два слоя с mix-blend-mode на каждой картинке браузер считает на
   каждый кадр прокрутки.

   Из палитры вместе с приёмом ушли токены --c-photo-shadow и --c-photo-highlight: цвета,
   которые больше нечем красить, обязаны исчезать из файла токенов, иначе через месяц
   кто-то покрасит ими что-нибудь другое. */

.photo {
  position: relative;
  overflow: hidden;
  border-radius: var(--r-xl);
  /* Пока картинка едет, на её месте стоит спокойная плашка, а не дыра в фоне страницы. */
  background: var(--c-surface-sunken);
}

.photo__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Пропорции. Задаются блоком, а не файлом: кадры нарезаны шире нужного, и на узком экране
   полосу приходится делать выше, иначе от снимка остаётся щель в 160 px. */
.photo--hero { aspect-ratio: 16 / 9; }
.photo--band { aspect-ratio: 21 / 9; }

/* В правой колонке первого экрана снимок вертикальный: там и кадр другой (partials/photo.njk
   отдаёт его через <source media>), и пропорция обязана совпасть с кадром — иначе object-fit
   отрежет у вертикального кадра верх и низ, то есть ровно тот воздух, ради которого он снят. */
@media (min-width: 62rem) {
  .photo--hero { aspect-ratio: 4 / 5; }
}

@media (max-width: 47.99rem) {
  .photo--hero { aspect-ratio: 4 / 3; }
  .photo--band { aspect-ratio: 3 / 2; }
}

/* Подпись под снимком. Она обязана быть, и вот почему: кадры сгенерированы, а выдавать
   сгенерированное за свою съёмку запрещено (feedback/no-stock-or-ai-people-as-ours.md).
   Поэтому под каждым снимком стоит строка, которая прямо говорит, что это иллюстрация. */
/* Снимок-полоса отделяется от кнопок сильнее прочего: кнопка-пилюля крупная, и на общей
   щели раздела она упиралась прямо в край фотографии. */
.photo-figure { margin: 0; }
.section > .photo-figure { margin-top: var(--sp-6); }
.photo-figure > figcaption {
  margin-top: var(--sp-2);
  font-size: var(--fs-micro);
  color: var(--c-ink-muted);
}

/* ─── Заглушка под фото ────────────────────────────────────────────────────────────────── */

/* Честная заглушка вместо стокового человека: своих фото на эту задачу нет, а чужие лица
   выдавать за свои запрещено (feedback/no-stock-or-ai-people-as-ours.md). Здесь написано,
   что сюда встанет, — чтобы место не забыли заполнить. */
.photo-slot {
  display: grid;
  place-content: center;
  gap: var(--sp-2);
  text-align: center;
  min-height: 12rem;
  padding: var(--sp-5);
  border: 2px dashed var(--c-line-strong);
  border-radius: var(--r-lg);
  background: var(--c-surface-sunken);
  color: var(--c-ink-muted);
  font-size: var(--fs-small);
}

.photo-slot__label { font-weight: var(--w-medium); color: var(--c-ink); }
/* Текст заглушки уже её самой: «пузыри» висят по краям, и широкая строка уезжала под них.
   Поймано скриншотом — на глаз в редакторе этого не видно. */
.photo-slot > * { max-width: 22rem; margin-inline: auto; }

/* Набранный шрифтом кода фрагмент: имена файлов и переменных. Моноширинный системный не
   берётся — он почти нигде не покрывает кириллицу, и «файл tokens.css» поехал бы двумя
   гарнитурами (feedback/webfont-latin-cyrillic-subsets-both.md). Держим свой шрифт, а
   «кодовость» даём фоном и разрядкой. */
code {
  padding: 0.1em 0.35em;
  border-radius: var(--r-sm);
  background: var(--c-surface-sunken);
  font-family: inherit;
  font-size: 0.94em;
  font-weight: var(--w-medium);
  letter-spacing: 0.01em;
}

/* ─── Гигантский контурный номер ───────────────────────────────────────────────────────── */

.numeral {
  display: block;
  font-family: var(--font-display);
  font-size: var(--fs-number);
  line-height: 0.82;
  /* Вес 500, обводка тоньше. При весе 800 и 136 px контурный номер весил больше заголовка
     рядом и спорил с ним; приём эталона — номер как отметка на полях, а не как плакат. */
  font-weight: var(--w-display);
  letter-spacing: -0.03em;
  color: transparent;
  -webkit-text-stroke: 1.5px var(--c-outline-number);
  /* Запасной путь для движков без -webkit-text-stroke: номер станет тинтованным, а не
     исчезнет. Пустое место вместо номера сломало бы нумерацию шагов. */
  text-shadow: none;
  user-select: none;
}

@supports not (-webkit-text-stroke: 1px currentColor) {
  .numeral { color: var(--c-outline-number); }
}

.numbered { display: grid; grid-template-columns: auto 1fr; gap: var(--sp-4) var(--sp-5); align-items: start; }

/* Четыре шага сеткой 2 × 2 на широком экране. Столбиком они держали левые шестьдесят
   процентов полосы, а правые сорок пустовали на всю высоту раздела. Порог тот же, что
   у первого экрана: ниже 62rem колонка одна, и шаги идут подряд. */
.steps { display: grid; gap: var(--sp-8) var(--sp-7); }

@media (min-width: 62rem) {
  .steps { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 47.99rem) {
  .steps { gap: var(--sp-6); }
}

/* На телефоне номер НЕ уезжает в свою строку, и это правка по замеру, а не по вкусу.
   Строкой выше он съедал ~90 px на каждый из четырёх шагов — при том что главная на
   390 px и без него вытягивалась на 9313 px. Номер остаётся слева, но мельче: приём
   сохранён, полтора экрана прокрутки возвращены человеку. */
@media (max-width: 47.99rem) {
  .numbered { grid-template-columns: auto 1fr; gap: var(--sp-2) var(--sp-4); }
  .numbered .numeral { font-size: 2.5rem; }
}

/* ─── Плашка статуса ───────────────────────────────────────────────────────────────────── */

/* Слова, а не только цвет: «работает» и «готовим» обязаны читаться при чёрно-белой печати
   и при дальтонизме. Точка перед словом — второй, необязательный признак. */
.status {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 0.3rem var(--sp-3);
  border-radius: var(--r-pill);
  font-size: var(--fs-micro);
  font-weight: var(--w-medium);
  white-space: nowrap;
}

.status::before { content: ''; width: 0.5rem; height: 0.5rem; border-radius: 50%; background: currentColor; }
/* Плашка обнимает своё слово и внутри колонки-флекса тоже. Без этого она растягивается на
   всю ширину карточки: `inline-flex` в флекс-контейнере теряет свою ширину, потому что
   поперечная растяжка сильнее. Поймано снимком в тот же час, когда карточки стали колонками
   ради выравнивания хвостов, — плашка «Готовим» превратилась в полосу во всю карточку.
   Правило безвредно там, где флекса нет: `align-self` в обычном потоке ничего не делает. */
.status { align-self: start; }
/* Строка «плашка + пояснение»: плашка отдельным предметом, у текста свой левый край.
   Внутри одного абзаца текст переносился под плашку, и вторая строка выглядела оторванной
   от первой. Выравнивание по базовой линии, а не по центру: плашка невысокая, и по центру
   она визуально всплывала над строкой. */
.check-line { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-2) var(--sp-3); }
.check-line > span:last-child { flex: 1 1 16rem; }

.status--ok { background: var(--c-ok-tint); color: var(--c-ok-ink); }
.status--wait { background: var(--c-wait-tint); color: var(--c-wait-ink); }
.status--off { background: var(--c-surface-sunken); color: var(--c-ink-muted); }

/* ─── Цена ─────────────────────────────────────────────────────────────────────────────── */

.price { display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap; }
.price__value { font-family: var(--font-display); font-size: clamp(2.75rem, 7vw, 4rem); line-height: 1; font-weight: var(--w-display); letter-spacing: var(--tracking-tight); }
.price__unit { font-size: var(--fs-h3); font-weight: var(--w-regular); }
.price__was { font-size: var(--fs-body); color: var(--c-ink-muted); text-decoration: line-through; }

/* ─── Поле формы ───────────────────────────────────────────────────────────────────────── */

/* Подпись СВЕРХУ и постоянно, placeholder — только пример формата (Бирман, метод 3):
   placeholder как единственная подпись исчезает при вводе, и человек забывает, чего от него
   хотели. Ошибка — рядом с полем, человеческим языком (эвристика Нильсена №9). */
.field { display: flex; flex-direction: column; gap: var(--sp-2); }
.field__label { font-weight: var(--w-medium); font-size: var(--fs-small); }
.field__req { color: var(--c-danger-ink); }

.field__input {
  min-height: var(--tap);
  padding: 0.7rem var(--sp-4);
  border: 1px solid var(--c-line-strong);
  border-radius: var(--r-md);
  background: var(--c-surface);
  color: var(--c-ink);
  font: inherit;
  /* 16 px на телефоне обязательны: при меньшем размере Safari сам зумит страницу при
     фокусе, и вёрстка «разъезжается» без единой ошибки в CSS. */
  font-size: max(1rem, var(--fs-body));
}

.field__input::placeholder { color: var(--c-ink-muted); opacity: 1; }
.field__input:focus { border-color: var(--c-brand); }
.field__hint { font-size: var(--fs-micro); color: var(--c-ink-muted); }
.field__error { font-size: var(--fs-small); color: var(--c-danger-ink); font-weight: var(--w-medium); }
.field--invalid .field__input { border-color: var(--c-danger-ink); background: var(--c-danger-tint); }

/* СОГЛАСИЕ СТАВЯТ НАЖАТИЕМ НА ВСЮ СТРОКУ, А НЕ В КВАДРАТИК 18 px.
   Разметка (`<label>` вокруг поля и текста) переключала галочку по тексту и раньше — это
   проверено живьём, а не предположено. Не хватало двух вещей, и обе на пальце решающие:
   строка НЕ ВЫГЛЯДЕЛА нажимаемой (курсор оставался текстовым, и человек целился в квадрат
   размером с треть подушечки пальца), и у строки не было своей высоты цели. Теперь вся
   строка — цель в 44 px с курсором-указателем, а сам квадрат подрос до 22 px.
   `align-items: flex-start` держится намеренно: текст согласия занимает две-три строки, и
   по центру галочка уехала бы в середину абзаца, потеряв связь с его началом. */
.checkbox {
  display: flex;
  gap: var(--sp-3);
  align-items: flex-start;
  min-height: var(--tap);
  padding-block: var(--sp-1);
  font-size: var(--fs-small);
  cursor: pointer;
}
.checkbox input { margin-top: 0.15rem; width: 1.375rem; height: 1.375rem; accent-color: var(--c-brand); flex: none; cursor: pointer; }

.form { display: flex; flex-direction: column; gap: var(--sp-4); }

/* ─── Вариант ответа (билет) ───────────────────────────────────────────────────────────── */

.opt-card { display: flex; gap: 12px; align-items: flex-start; background: var(--c-surface-sunken);
  border: 1px solid var(--c-line); color: var(--c-ink); border-radius: var(--r-md);
  padding: 13px 15px; margin-bottom: 9px; cursor: pointer; font-size: 15px;
  line-height: 1.45; transition: var(--motion); width: 100%; text-align: left;
  font-family: inherit; min-height: var(--tap); }
.opt-card:hover:not(.opt-card--ok):not(.opt-card--no) { border-color: var(--c-brand); }
.opt-card--ok { background: var(--c-ok-tint); border-color: var(--c-ok-ink); color: var(--c-ink); cursor: default; }
.opt-card--no { background: var(--c-danger-tint); border-color: var(--c-danger-ink); color: var(--c-ink); cursor: default; }
.opt-card input { display: none; }
.opt-card__key { flex: none; width: 26px; height: 26px; border-radius: var(--r-sm);
  background: var(--c-surface); border: 1px solid var(--c-line);
  display: grid; place-items: center; font-size: 13px; font-weight: 600;
  color: var(--c-ink-muted); }
.opt-card--ok .opt-card__key { background: var(--c-ok-ink); color: var(--c-ok-tint); border-color: var(--c-ok-ink); }
.opt-card--no .opt-card__key { background: var(--c-danger-ink); color: var(--c-danger-tint); border-color: var(--c-danger-ink); }
.opt-card:has(input:checked) { border-color: var(--c-brand); background: var(--c-brand-tint); color: var(--c-brand-tint-ink); }
.opt-card:has(input:checked) .opt-card__key { border-color: var(--c-brand); color: var(--c-brand); }
/* Подпись словами рядом с заливкой: разбор обязан читаться и без различения цвета. */
.opt-card__tag { display: block; margin-top: 4px; font-size: var(--fs-small);
  font-weight: var(--w-medium); text-transform: uppercase; letter-spacing: .04em; }
.opt-card--ok .opt-card__tag { color: var(--c-ok-ink); }
.opt-card--no .opt-card__tag { color: var(--c-danger-ink); }

/* ─── Навигация по вопросам билета ─────────────────────────────────────────────────────── */

/* Группа полей без рамки. `fieldset` нужен ради `legend`: он связывает подпись с группой
   переключателей для экранного диктора, и заменить его на `div` с текстом значит эту связь
   потерять. Рамку и отступы браузера при этом снимаем — они из другой эпохи и спорят с
   карточкой, внутри которой стоят. */
.field > legend,
fieldset.field { border: 0; padding: 0; margin-inline: 0; }
fieldset.field > legend { padding: 0; }

/* ЧИПЫ ВЫБОРА: режим подхода и размер подхода.

   Приём снят с механики kto-app.ru/exam, где он обкатан людьми: режим виден весь сразу и
   переключается одним нажатием, без выпадающего списка и без отдельной страницы. Внутри
   чипа настоящий `radio` — отсюда бесплатно берутся клавиатура, экранный диктор и работа
   без единой строки скрипта (строгая политика содержимого инлайн всё равно гасит).

   Цель нажатия не меньше 44 px по высоте: аудитория работает в перчатках и с телефона. */
.chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); }

.chip { position: relative; display: inline-flex; }
.chip__input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }

.chip__face {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  padding: 0.5rem 1rem;
  border: 1px solid var(--c-line);
  border-radius: var(--r-pill);
  background: var(--c-surface);
  color: var(--c-ink);
  font-size: var(--fs-small);
  transition: border-color 0.12s, background 0.12s;
}

.chip__input:hover + .chip__face { border-color: var(--c-brand); }
.chip__input:focus-visible + .chip__face { outline: 2px solid var(--c-accent); outline-offset: 2px; }
.chip__input:checked + .chip__face {
  background: var(--c-brand);
  border-color: var(--c-brand);
  color: var(--c-brand-ink);
  font-weight: var(--w-medium);
}

/* ОТМЕЧЕННЫЙ, НО ЕЩЁ НЕ ОТПРАВЛЕННЫЙ ВАРИАНТ. Отличается от разбора намеренно: разбор
   говорит «верно/неверно», а это — только «отмечено». Спутать их значило бы показать оценку
   до ответа.
   Вид ровно тот же, что у отмеченного переключателя на странице без скрипта
   (`.opt-card:has(input:checked)` ниже), и с 25.08.2026 это принципиально: со скриптом и без
   него нажатие на вариант теперь делает одно и то же — отмечает, — и выглядеть по-разному
   эти два состояния не имеют права. Прежняя редакция подсвечивала только рамку на белом,
   и на солнце у щита такую отметку было не разглядеть. */
.opt-card--picked { border-color: var(--c-brand); background: var(--c-brand-tint); color: var(--c-brand-tint-ink); }
.opt-card--picked .opt-card__key { border-color: var(--c-brand); color: var(--c-brand); }

/* Полоса номеров вопросов описана НИЖЕ, в разделе «Навигация по вопросам билета». Здесь
   до 25.08.2026 лежала её вторая, более ранняя редакция (кружки 36 px, шрифт 13 px) —
   мёртвый код: те же селекторы ниже по файлу перебивали её целиком, и правка здесь ничего
   не меняла на экране. Убрана, чтобы следующий не чинил размер не в том месте. */

/* ─── Сообщение ────────────────────────────────────────────────────────────────────────── */

.notice { padding: var(--sp-4); border-radius: var(--r-lg); background: var(--c-brand-tint); color: var(--c-brand-tint-ink); }
.notice--error { background: var(--c-danger-tint); color: var(--c-danger-ink); }
.notice--ok { background: var(--c-ok-tint); color: var(--c-ok-ink); }

/* ─── Таблица ──────────────────────────────────────────────────────────────────────────── */

/* Обёртка прокручивается, а не страница: широкая таблица на 390 px иначе тянет вбок весь
   документ, и шапка уезжает за экран. */
.table-wrap { overflow-x: auto; border: 1px solid var(--c-line); border-radius: var(--r-lg); background: var(--c-surface); }

.table { width: 100%; border-collapse: collapse; font-size: var(--fs-small); }
.table th, .table td { padding: var(--sp-3) var(--sp-4); text-align: left; border-bottom: 1px solid var(--c-line); }
.table thead th { background: var(--c-surface-sunken); font-weight: var(--w-medium); white-space: nowrap; }
.table tbody tr:last-child td { border-bottom: 0; }
.table td.num, .table th.num { text-align: right; font-variant-numeric: tabular-nums; }

/* ─── Аккордеон ────────────────────────────────────────────────────────────────────────── */

/* На <details>, а не на скриптах: раскрытие обязано работать при отключённом JS и быть
   доступным с клавиатуры без единой строки кода. */
.accordion { border: 1px solid var(--c-line); border-radius: var(--r-lg); background: var(--c-surface); overflow: hidden; }
.accordion + .accordion { margin-top: var(--sp-3); }

.accordion__head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  min-height: var(--tap);
  padding: var(--sp-5);
  font-weight: var(--w-medium);
  cursor: pointer;
  list-style: none;
}

.accordion__head::-webkit-details-marker { display: none; }

.accordion__head::after {
  content: '+';
  margin-inline-start: auto;
  font-size: 1.5rem;
  line-height: 1;
  font-weight: var(--w-bold);
  color: var(--c-link);
}

.accordion[open] .accordion__head::after { content: '–'; }
.accordion[open] .accordion__head { background: var(--c-brand-tint); color: var(--c-brand-tint-ink); }
.accordion__body { padding: var(--sp-5); padding-top: var(--sp-2); }

/* ─── Хлебные крошки ───────────────────────────────────────────────────────────────────── */

/* Крошки отвечают на второй вопрос Trunk Test Крайга — «где я сейчас». На телефоне они
   прокручиваются вбок, а не переносятся: перенос ломает цепочку на два этажа. */
.crumbs { font-size: var(--fs-small); }
.crumbs__list { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; margin: 0; padding: 0; list-style: none; }
.crumbs__item { display: flex; align-items: center; gap: var(--sp-2); color: var(--c-ink-muted); }
.crumbs__item + .crumbs__item::before { content: '/'; color: var(--c-line-strong); }
.crumbs__item a { color: var(--c-ink-muted); }
.crumbs__item a:hover { color: var(--c-link); }
.crumbs__item[aria-current='page'] { color: var(--c-ink); font-weight: var(--w-medium); }

/* ─── Подвал ───────────────────────────────────────────────────────────────────────────── */

.site-footer { margin-top: var(--sp-7); border-top: 1px solid var(--c-line); background: var(--c-surface-sunken); }
.site-footer__row { display: flex; flex-wrap: wrap; gap: var(--sp-7); padding-block: var(--sp-8); }
.site-footer__col { min-width: 12rem; }
/* ПЕРВАЯ КОЛОНКА ОБЯЗАНА СЖИМАТЬСЯ. 22.08.2026 в подвал добавилась четвёртая колонка
   («Справка»: как готовиться, вопрос-ответ, о платформе, контакты — страница контактов до
   этого дня не была достижима вообще ниоткуда). Колонка с логотипом несёт абзац шириной
   в строку текста, и вчетвером они переставали помещаться в полосу: «Документы» уезжали
   на вторую строку и вставали под логотипом, читаясь его продолжением.
   Правило снимает у неё жёсткую ширину — сжимается та колонка, у которой есть что сжать. */
.site-footer__row > .site-footer__col:first-child { flex: 1 1 18rem; }
.site-footer__row > .site-footer__col:first-child .measure { max-width: none; }
.site-footer__col > * + * { margin-top: var(--sp-2); }
/* Подпись стоит вплотную под логотипом: она его пояснение, а не отдельная мысль. Правило
   воздуха Бирмана — связанное ближе несвязанного, и «ближе» здесь буквально шаг сетки. */
.site-footer__tagline { margin-top: var(--sp-1); font-size: var(--fs-small); color: var(--c-ink-muted); }
.site-footer__links { display: flex; flex-direction: column; gap: var(--sp-2); font-size: var(--fs-small); }
.site-footer__legal { padding-block: var(--sp-4); border-top: 1px solid var(--c-line); font-size: var(--fs-micro); color: var(--c-ink-muted); }

/* ─── Служебное ────────────────────────────────────────────────────────────────────────── */

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Ссылка «к содержимому» — первое, что получает фокус на клавиатуре. */
.skip-link {
  position: absolute;
  left: var(--sp-4);
  top: -4rem;
  z-index: 50;
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-sm);
  background: var(--c-brand);
  color: var(--c-brand-ink);
  text-decoration: none;
  transition: top var(--motion);
}

.skip-link:focus { top: var(--sp-3); color: var(--c-brand-ink); }

/* ─── Навигация по вопросам билета ─────────────────────────────────────────────────────── */

.q-nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-bottom: var(--sp-4);
}

/* ЦЕЛЬ НАЖАТИЯ 44 px, НЕ 40. Стояло 2.5rem — на четыре пикселя ниже нижней границы Apple HIG
   и Material, и это самая тесная сетка целей на всём портале: двадцать кружков подряд, между
   ними 8 px. Промах здесь стоит дорого — он переносит человека на ЧУЖОЙ вопрос. */
.q-nav__btn {
  display: grid;
  place-items: center;
  width: var(--tap);
  height: var(--tap);
  min-height: 0;
  padding: 0;
  border: 1px solid var(--c-line-strong);
  border-radius: var(--r-md);
  background: var(--c-surface);
  color: var(--c-ink-muted);
  font: inherit;
  font-size: var(--fs-small);
  font-weight: var(--w-medium);
  cursor: pointer;
  transition: background var(--motion), color var(--motion), border-color var(--motion);
}

.q-nav__btn:hover:not(.q-nav__btn--current) {
  background: var(--c-brand-tint);
  color: var(--c-brand-tint-ink);
  border-color: transparent;
}

.q-nav__btn--current {
  background: var(--c-brand);
  color: var(--c-brand-ink);
  border-color: transparent;
  cursor: default;
}

.q-nav__btn--done {
  background: var(--c-ok-tint);
  color: var(--c-ok-ink);
  border-color: transparent;
}

/* ─── Страница подхода: первый экран телефона ──────────────────────────────────────────── */

/* ЗАМЕР, РАДИ КОТОРОГО ВСЁ НИЖЕ И НАПИСАНО. iPhone 390×664, подход из двадцати вопросов,
   25.08.2026: первый вариант ответа начинался на отметке 882 px. Экран телефона — 664.
   То есть человек, нажавший «начать», не видел ни вопроса, ни того, что от него требуется;
   он видел заголовок «Тренажёр», три ряда кружков и зелёную плашку с названием программы —
   всё то, что он выбрал сам двумя секундами раньше.
   Ни одного смысла со страницы не убрано: заголовок остаётся в документе для экранного
   диктора, кружки — под своей строкой, контекст — строкой подписи. Убран ВЕС, не содержание. */

/* Полоса номеров под строкой «Вопрос N из M». Обёртка та же, что у меню в шапке: <details>
   раскрывает браузер сам, без скрипта, с клавиатурой и экранным диктором из коробки.
   На широком экране заголовок прячется, а содержимое показывается принудительно — полоса
   номеров остаётся ровно такой, какой была. */
.q-nav-box__toggle { display: none; }

@media (min-width: 48rem) {
  .q-nav-box::details-content { content-visibility: visible; block-size: auto; }
}

/* Запасной путь для браузеров без `::details-content`: там содержимое закрытого <details>
   спрятано самим браузером, и спрятанный заголовок означал бы полосу номеров, до которой
   не добраться вовсе. Заголовок остаётся видимым — хуже, чем полоса, но целое. */
@supports not selector(::details-content) {
  @media (min-width: 48rem) {
    .q-nav-box__toggle { display: inline-flex; }
  }
}

@media (max-width: 47.99rem) {
  .q-nav-box__toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    min-height: var(--tap);
    padding-inline: var(--sp-2);
    margin-inline-start: calc(var(--sp-2) * -1);
    border-radius: var(--r-sm);
    color: var(--c-ink-muted);
    font-size: var(--fs-small);
    font-weight: var(--w-medium);
    cursor: pointer;
    list-style: none;
    user-select: none;
  }
  .q-nav-box__toggle::-webkit-details-marker { display: none; }

  /* Стрелка одна и та же в обоих состояниях, поворотом: два разных знака человек сверяет
     между собой, один повёрнутый читается сразу. Приём взят у меню в шапке — на одной
     странице две разные раскрывалки не должны выглядеть по-разному. */
  .q-nav-box__toggle::after {
    content: '';
    width: 0.5rem;
    height: 0.5rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: translateY(-0.15em) rotate(45deg);
    transition: transform var(--motion);
  }
  .q-nav-box[open] .q-nav-box__toggle::after { transform: translateY(0.1em) rotate(-135deg); }
  .q-nav-box[open] .q-nav { margin-top: var(--sp-3); }

  /* Плашка контекста сворачивается до подписи: заливка, рамка, тень и отбивки в 32 px
     тратили 144 px высоты на строку текста. */
  .run-context {
    padding: 0;
    background: transparent;
    border: 0;
    box-shadow: none;
    color: var(--c-ink-muted);
  }

  /* Заголовок разворота («Тренажёр» и строка «подготовка — вопрос 1 из 20») уходит из
     потока, но остаётся в документе: h1 нужен экранному диктору и оглавлению страницы,
     а счётчик виден строкой над полосой номеров. Приём тот же, что у .visually-hidden. */
  .run-head {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  /* ПОИСК ВНУТРИ ПОДХОДА НЕ ПОКАЗЫВАЕМ. На телефоне поле занимает в липкой шапке целую
     строку — 48 px на каждом из двадцати вопросов, — а искать внутри решаемого билета
     нечего: человек уже нашёл. Уход из подхода при этом остаётся на месте (ссылка под
     карточкой) и меню в шапке тоже. Скрывается только на узком экране: на широком поле
     стоит в одной строке с логотипом и не стоит ничего. */
  .page-run .site-search { display: none; }

  /* Части подхода жмутся друг к другу плотнее, чем разделы витрины: они об одном и том же
     предмете, а не о разных. Общая шкала (--sp-6 = 32 px между блоками) на странице,
     где блоков четыре, отдавала первому экрану сотню пикселей пустоты. */
  .page-run .stack-wide > * + * { margin-top: var(--sp-4); }
  .page-run .section { padding-block: var(--sp-5); }
}

/* ─── Реквизит редакции нормы ────────────────────────────────────────────────────────────
   Три состояния, и ни одно не молчит: подтверждена, не подтверждена, подтвердить нечем.
   Цветом состояние НЕ сообщается в одиночку — состояние всегда написано словом в тексте
   строки. Аудитория продукта — электрики, дальтонизм у мужчин обычное дело, и это правило
   в проекте уже оплачено приёмкой 02.08.2026 на пометке верного варианта. */
.redaction {
  margin-top: var(--sp-2);
  padding: var(--sp-3);
  border-radius: var(--r-md);
  border-left: 3px solid var(--c-line-strong);
  background: var(--c-surface);
  font-size: var(--fs-small);
  color: var(--c-ink-muted);
}
.redaction--ok { border-left-color: var(--c-ok-ink); }
.redaction--warn { border-left-color: var(--c-danger-ink); }
.redaction strong { color: var(--c-ink); }

/* Та же вещь в выдаче поиска: там место только на состояние, без объяснения. */
.redaction-mark {
  display: inline-block;
  font-size: var(--fs-micro);
  color: var(--c-ink-muted);
  border: 1px solid var(--c-line);
  border-radius: var(--r-sm);
  padding: 0 var(--sp-2);
  white-space: nowrap;
}
.redaction-mark--ok { border-color: var(--c-ok-ink); }
.redaction-mark--warn { border-color: var(--c-danger-ink); }

/* ─── Постраничная навигация выдачи ─────────────────────────────────────────────────────
   Появилась 25.08.2026 вместе с постраничным поиском: до неё выдача отдавала двадцать
   находок из скольких угодно, и по обычному запросу 183 вопроса из 203 были недостижимы.
   Своих цветов у полосы нет — только токены; цели нажатия не мельче 44 px, потому что по
   этим цифрам жмут пальцем с телефона, стоя у щита. */
.pager {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  align-items: center;
  justify-content: center;
  margin-top: var(--sp-5);
}
.pager__numbers { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; }
.pager__numbers a,
.pager__current {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0 var(--sp-2);
  border-radius: var(--r-md);
  border: 1px solid var(--c-line);
  font-variant-numeric: tabular-nums;
}
.pager__numbers a { color: var(--c-ink); text-decoration: none; }
.pager__numbers a:hover { border-color: var(--c-brand); color: var(--c-brand); }
/* Текущая страница — не ссылка, и это видно и без цвета: рамка сильнее, фон залит. */
.pager__current { background: var(--c-brand-tint); color: var(--c-brand-tint-ink); border-color: transparent; font-weight: 600; }
