/*
 * ПЕЧАТЬ (Дизайн-спринт, Task 7, спек §2.7 «Печать»).
 *
 * До этой задачи в проекте не было ни строки печатных стилей: на бумагу
 * уходил чернильный фон дизайн-темы на весь лист, липкий топбар и подвал
 * читалки поверх текста, пустые рамки кнопок, панель настроек читалки,
 * ссылки без адресов и карточки, разорванные посередине. Самое серьёзное —
 * предупреждения к работе лежат в свёрнутом `<details>` (DESIGN_BRIEF:
 * «развёрнутый список спойлерит содержание, поэтому по умолчанию свёрнут»)
 * и на распечатке пропадали ровно в тот момент, когда читатель обязан их
 * увидеть ДО чтения. Все решения ниже подчинены одному: лист бумаги — это
 * отдельный носитель с собственным контрактом, а не испорченный скриншот
 * экрана.
 *
 * ============================================================
 * РЕШЕНИЕ 1 — ФАЙЛ НЕ ОБЁРНУТ В `@layer utilities`.
 * ============================================================
 *
 * ЭТО РЕШЕНИЕ ЗАПИСАНО В ГЕЙТЕ, А НЕ ТОЛЬКО ЗДЕСЬ: `apps/shared/ui/
 * css-layers.test.ts` требует, чтобы авторские правила лежали в слоях, и
 * этот файл занесён в нём в список `EXEMPT` — с той же аргументацией и
 * отдельно от потолков-ратчетов унаследованных файлов. Разница
 * принципиальная: потолок означает «когда-нибудь опустим до нуля», а тут
 * опускать нечего — пока хоть один экранный файл неслоёный, слой здесь
 * строго вреден.
 *
 * Бриф задачи предполагал `@layer utilities { @media print { … } }` —
 * «печатный слой побеждает всё осознанно». На практике это не работает, и
 * вот почему.
 *
 * `assets/css/layers.css` объявляет порядок `tokens, base, primitives,
 * components, pages, utilities`, но фактически ВНУТРИ именованных слоёв
 * лежат только `assets/css/ui.css` (`@layer primitives { … }`) и, с
 * дизайн-спринта Task 13.5, `assets/css/base.css` (`@layer base { … }`) —
 * оба слоя между собой конкурируют честно, но ни один не помогает против
 * ЭТОГО файла (см. ниже). `tokens.css`, `app.css`, `reader.css` — по-
 * прежнему НЕ обёрнуты ни в один слой, то есть являются НЕслоёным
 * CSS. По спецификации каскада (CSS Cascade Layers, §6) неслоёный
 * авторский CSS побеждает ЛЮБОЙ слоёный авторский CSS независимо от того,
 * какой слой объявлен последним и какая у слоёного правила специфичность.
 * Это НЕ гипотеза — это ровно тот случай, что уже задокументирован в
 * `assets/css/base.css` (докблок у сброса `prefers-reduced-motion`):
 * первый проход той задачи обернул сброс в `@layer utilities`, понадеявшись
 * на порядок слоёв, и `header.chrome`/`#sheet`/`.card` (на момент замера
 * карточка ещё называлась так — переименована в `.rf-card` дизайн-спринтом,
 * Task 11, и с той же правкой переехала в `@layer components`, докблок
 * ниже объясняет, почему это НЕ меняет вывод) в `reader.css`/`app.css`
 * продолжали двигаться, хотя человек явно попросил не двигать интерфейс —
 * слой проиграл файлу без слоя.
 *
 * У печати то же самое уравнение: `.chrome`, `.rf-card`, `.badge`, палитра
 * (`--bg`/`--text`/…) объявлены в `app.css`/`reader.css`/`tokens.css` БЕЗ
 * СЛОЯ — КРОМЕ `.rf-card` (Task 11 переехал в `@layer components`, но
 * вывод не меняется: неслоёный CSS побеждает ЛЮБОЙ слоёный независимо от
 * того, какой слой, — печать остаётся неслоёной ИМЕННО поэтому, читайте
 * дальше). Если положить печатные правила в `@layer utilities`, они
 * ГАРАНТИРОВАННО проиграют экранным правилам той же специфичности —
 * ровно то следствие, которого пришлось бы чинить `!important`ом, а
 * `!important` в проекте зарезервирован ЕДИНСТВЕННЫМ документированным
 * исключением (`prefers-reduced-motion` в `base.css`) и по ruling'у
 * контроллера дизайн-спринта на печать не распространяется.
 *
 * Выход — тот, что явно разрешён ruling'ом задачи: НЕ оборачивать файл ни
 * в какой слой вовсе. Это не костыль, а точный по смыслу выбор: печать —
 * ОТДЕЛЬНЫЙ носитель (`@media print`), который физически не может
 * конкурировать с `@media screen`-контекстом за один и тот же рендер —
 * когда действует один медиа-контекст, второй попросту не применяется.
 * Единственное реальное состязание — это состязание МЕЖДУ печатными
 * правилами ЭТОГО файла и печатающимися-по-умолчанию экранными правилами
 * (тем же селектором без медиа-условия, которые применяются ВО ВСЕХ
 * медиа, если их не переопределить). Оставаясь неслоёным CSS, этот файл
 * встаёт с ними на одну доску — решает обычный каскад: специфичность,
 * при равенстве специфичности — порядок исходников. Специфичность здесь
 * держится в пределах проектной дисциплины (ни одного id-селектора, ни
 * одной цепочки из трёх классов — `apps/shared/ui/css-specificity.test.ts`
 * следит и за этим файлом тоже, см. РЕШЕНИЕ 3), а порядок обеспечен
 * подключением: `<link rel="stylesheet" media="print">` в
 * `templates/base.html.twig` стоит ПОСЛЕДНИМ тегом стиля в `<head>` —
 * позже `ui.css`/`app.css` и позже `{% block stylesheets %}`, куда
 * `reader.css` (читалка) вставляет свою ссылку. Более поздний источник при
 * равной специфичности побеждает без единого `!important`.
 *
 * ============================================================
 * РЕШЕНИЕ 2 — ПАЛИТРА СОБРАНА ИЗ ПОЛЮСОВ, А НЕ ИЗ ЛИТЕРАЛОВ.
 * ============================================================
 *
 * Соблазн брифа — `--bg: #FFFFFF`. Гейт `no-hardcoded-colors.test.ts`
 * запрещает цветовые литералы во ВСЕХ CSS-файлах проекта, кроме
 * `tokens.css` и `reader.css` (см. докблок гейта), и печать этого
 * исключения не получает — печать не «единственный источник» цвета для
 * какого-то виджета, у неё нет причины дублировать палитру в четвёртый раз.
 *
 * Вместо этого лист собран из уже существующих в системе ПОЛЮСОВ,
 * которые от темы не зависят (`assets/css/tokens.css`, блок «КОНТРАСТНЫЕ
 * ПОЛЮСА»): `--ink`/`--paper` — два конца шкалы, одинаковые во всех трёх
 * темах сайта. `--border`/`--text-muted` получены тем же приёмом
 * `color-mix()`, каким уже построены тени (`--shadow-*` в `tokens.css`) —
 * не новый паттерн, а тот же самый.
 *
 * `--primary` — дополнительный случай, которого нет в исходном ruling'е,
 * но контраст потребовал его тоже. Причина не гипотетическая: чтобы её
 * найти, было достаточно прочитать `assets/css/base.css` — `a { color:
 * var(--primary); }` красит АБСОЛЮТНО ЛЮБУЮ ссылку без более специфичного
 * правила, включая оглавление главы на странице работы (`work_show`, самый
 * частый печатаемый контент) и подпись автора в карточке (`.rf-card__by a`,
 * `app.css`). Тёмная тема держит `--primary:#A78BFA` — светло-лавандовый,
 * подобранный для ТЁМНОГО фона; на белом листе тот же цвет читается почти
 * не читается (низкий контраст «светлое на светлом»). Решение — снова
 * ссылка на уже существующий полюс, а не новый токен: `--primary-deep`
 * ТЁМНОЙ темы (`#6D53E0`) БУКВАЛЬНО совпадает с `--primary` СВЕТЛОЙ темы
 * (сверено по `tokens.css`: оба `#6D53E0`) — то есть «глубокий вариант»
 * акцента уже подобран как раз под светлый фон, его нужно только позвать
 * по имени: `--primary: var(--primary-deep);`. Ни одного нового литерала,
 * ни одного нового токена в `tokens.css` — токен уже был, задача лишь
 * читает его для другого назначения, ровно как рекомендует ruling
 * («если оттенка полюсами не выразить — добавьте токен в tokens.css, а не
 * литерал сюда»): здесь дошло до подбора среди уже существующих полюсов
 * без необходимости добавлять новый.
 *
 * `--hero-glow` (радиальный градиент hero-обложки, `tokens.css`) обнулён
 * до `none` — иначе цветное свечение темы осталось бы на листе даже после
 * того, как `--bg`/`--surface` побелели: `.hero` в `app.css` красит фон
 * ИМЕННО через `background-image: var(--hero-glow)`, а не через `--bg`.
 *
 * Рейтинговые цвета (`--rating-g/pg13/r/nc17`) НЕ трогаются: это отдельная
 * семантика (см. РЕШЕНИЕ про бейджи ниже), и спек §2.7 отвечает на их
 * потерю в ЧБ печати не заменой цвета, а рамкой — текст-подпись рейтинга
 * уже есть внутри бейджа, рамка — второй, не завязанный на цвет сигнал.
 *
 * ============================================================
 * РЕШЕНИЕ 3 — `<details>` РАСКРЫВАЮТСЯ ПРИНУДИТЕЛЬНО.
 * ============================================================
 *
 * Предупреждения к работе (`templates/works/show.html.twig`,
 * `<details class="warnings">`) свёрнуты по умолчанию нарочно
 * (DESIGN_BRIEF: список спойлерит содержание) — но это решение для
 * ЭКРАНА, где читатель может раскрыть их одним кликом. На бумаге кликнуть
 * нечем, и свёрнутый список — это молча пропавшее предупреждение, а не
 * компактная вёрстка.
 *
 * Родной способ браузера скрывать содержимое закрытого `<details>` — это
 * встроенное (user-agent) правило вида `details:not([open]) > *:not(summary)
 * { display: none; }`. АВТОРСКИЙ CSS того же селектора побеждает
 * user-agent-правило БЕЗ единого `!important` — это не состязание
 * специфичностей, а состязание ПРОИСХОЖДЕНИЙ каскада (cascade origin):
 * origin автора всегда старше origin браузера. Отсюда и правило ниже: оно
 * не хитрость, а прямое использование origin-приоритета для ровно того
 * случая, для которого он существует.
 *
 * **ЭТОГО ОДНОГО ПРАВИЛА НЕДОСТАТОЧНО НА ТЕКУЩЕМ CHROMIUM (147, проверено
 * визуально Playwright'ом при подготовке задачи).** Найдено руками, не по
 * документации: `.subfandoms` c бейджем предупреждения получал корректный
 * `display: block` (подтверждено `getComputedStyle`), геометрия
 * (`getBoundingClientRect`) была ненулевой, но `locator.isVisible()`
 * Playwright и реальный PDF (`page.pdf()`) сходились в одном — элемент
 * не рисуется. Причина — новый механизм разворачивания `<details>`
 * («Customizable and animatable `<details>`», в Chromium начиная со 131):
 * несводное содержимое теперь лежит не под `display: none`, а под
 * псевдоэлементом `::details-content` со своим UA-правилом (примерно
 * `block-size: 0; overflow: clip; content-visibility: hidden;` до
 * раскрытия). `display: block` на самом узле эту обёртку не трогает —
 * узел «хочет» быть видимым, но помещён в контейнер нулевой высоты со
 * скрытым content-visibility. Второе правило ниже адресуется именно к
 * `::details-content` и снимает все три ограничения — без него
 * предупреждения переставали доезжать до бумаги ровно там, где спек
 * называет это главным провалом текущего состояния.
 *
 * Два места требуют отдельного уточнения специфичностью (`>` с классом
 * родителя, а не голый класс формы): `.comment-form` внутри
 * `<details class="comments">` читалки и `.subscribe-form` внутри
 * `<details class="chip-menu">` (подписка на пейринг, `works/show.html.twig`).
 * Оба — ПРЯМЫЕ дети `<details>`, то есть тот же элемент, на который
 * претендует правило раскрытия выше (специфичность `details:not([open]) >
 * :not(summary)` — 2 типовых + 1 псевдокласс — ВЫШЕ голого класса формы:
 * 0 id, 0 классов у типового счётчика против 1 класса формы). Родительский
 * класс в селекторе (`.comments > .comment-form`) добавляет второй
 * класс-компонент специфичности и перебивает правило раскрытия, оставляя
 * формы скрытыми, — иначе на печати ожили бы кнопки «Отправить»/
 * «Подписаться», которых там быть не должно (спек: «формы комментариев,
 * кнопки лайка и подписки» — среди того, что прячет печатный хром).
 *
 * ============================================================
 * КОЛОНТИТУЛ — `main[data-print-title]::before`, А НЕ `@page`-МАРГИНАЛИИ.
 * ============================================================
 *
 * Классический печатный колонтитул — это margin-box'ы `@page` (`@top-center`
 * и соседи, CSS Generated Content for Paged Media). Chromium — движок,
 * которым распечатывает Playwright/`page.pdf()` в проверке этой задачи, —
 * их НЕ реализует вовсе (единственная поддержка `@page` в Chromium — это
 * `size`/`margin` самого блока, без вложенных margin-box правил); `string-set`/
 * `running()`/`element()`, которыми margin-box обычно наполняют динамическим
 * текстом, в Chromium тоже не работают. Отсюда и не буквальное прочтение
 * брифа («колонтитул через `@page`»): `@page` здесь отвечает только за
 * физические поля листа (см. ниже), а сам текст колонтитула — обычный
 * псевдоэлемент документа, который просто ВЫГЛЯДИТ как шапка страницы,
 * поскольку стоит первым в потоке `<main>`.
 *
 * Источник текста — `attr()`, а не дублирование строки в CSS (литерал был
 * бы недостижим: у CSS нет доступа к произвольному тексту чужого узла).
 * `data-position` на `<main id="text">` (`templates/works/chapter.html.twig`)
 * уже существовал (его читает `assets/js/reader.js`) — а `data-print-title`/
 * `data-print-author` пришлось добавить туда же ЭТОЙ задачей: без них
 * составить «название работы + автор» неоткуда, авторство читалка иначе
 * показывает только внутри `.chrome` (шапка читалки), которую печать
 * прячет целиком по прямому требованию спека («прячет весь хром... .chrome»).
 * Это единственное расхождение с списком файлов в брифе задачи (там
 * заявлены только `print.css`+`base.html.twig`+тест) — минимальное,
 * не затрагивающее ни вёрстку, ни JS, ни один существующий тест (оба новых
 * атрибута нигде не проверяются по имени).
 *
 * Селектор `main[data-print-title]::before` СОЗНАТЕЛЬНО не совпадает с
 * голым `main`: на остальных трёх проверяемых страницах (работа, лента
 * фандома, поиск) атрибута нет, `attr()` вернул(и) бы пустые строки, и
 * колонтитул превратился бы в пустую строку с лишними разделителями
 * (« · · Глава »). Это НЕ пробел в покрытии — на тех страницах заголовок
 * работы уже стоит первым видимым элементом контента (`<h1>` в `.hero`,
 * НЕ внутри хрома, печатью не скрывается), и колонтитул им не нужен.
 */

@page {
    /* Только физические поля листа — margin-box'ы (`@top-center` и
       соседи) Chromium не поддерживает (см. докблок выше), поэтому этот
       `@page` не пытается нести текст, только задаёт печатный отступ. */
    margin: 18mm 16mm;
}

@media print {

    /* ── светлая палитра из полюсов (РЕШЕНИЕ 2) ──────────────────────── */

    :root,
    /* `:root[data-preset]` — читалка держит пресет оформления
       (`reader.css`) на `<html data-preset="night">` со специфичностью
       ВЫШЕ голого `:root` (атрибут + псевдокласс против одного
       псевдокласса); без второй ветки селектора пресет читалки победил бы
       наши поля на странице главы.
       `:root[data-font]` — читатель выбрал гротеск (`reader.css:38`), и этот
       выбор сохранён как `data-font="sans"`; той же парой селекторов печать
       переопределяет шрифт обратно на сериф, независимо от выбора читателя
       (спец §2.7: печать — на сериф, несмотря на экранные предпочтения). */
    :root[data-preset],
    :root[data-font] {
        --bg: var(--paper);
        --surface: var(--paper);
        --text: var(--ink);
        --border: color-mix(in srgb, var(--ink) 20%, var(--paper));
        --text-muted: color-mix(in srgb, var(--ink) 65%, var(--paper));
        /* см. докблок выше — глубокий акцент тёмной темы совпадает со
           светлым акцентом светлой темы, ссылка на уже существующий полюс. */
        --primary: var(--primary-deep);
        /* `--accent`/`--accent2` — токены ЧИТАЛКИ (`reader.css`,
           `:root[data-preset="…"]`), не токены темы сайта; без строки здесь
           страница главы (умолчание `data-preset="night"`,
           `templates/works/chapter.html.twig`) печатает их пресетными
           значениями поверх, потому что `:root[data-preset="night"]` держит
           оба свойства безусловно, а печать по ним молчала (Дизайн-спринт,
           Task 25b): номер главы и буквица (`.ch-head .num`,
           `.reader article > p:first-of-type::first-letter`) — 1.76:1
           (`#FFB454` на белом), ссылки обсуждения и «Дальше в серии»
           (`.rf-comments-tease a`, `.rf-series-next a`) — 2.72:1 (`#A78BFA`
           на белом), оба ниже AA. У `--accent` (роль «spark» пресета) нет
           печатного «глубокого» полюса — в отличие от `--primary` выше,
           `tokens.css` не хранит `--spark-deep` ни для одной темы, а
           затемнять `#FFB454` литералом здесь запрещено тем же гейтом
           `no-hardcoded-colors.test.ts`, что и в РЕШЕНИИ 2. Оба сведены к
           `--primary` ЭТОГО ЖЕ правила (не напрямую к `--primary-deep`
           `tokens.css` — значение то же, но ссылка на соседнее объявление
           внутри одного правила переживёт будущую правку `--primary` без
           второго места для синхронизации): `--accent2` пресета и так уже
           играет ту же роль, что `--primary` темы (докблок `tokens.css`,
           сепия: «--accent/--accent2 — это те же роли, что здесь зовутся
           --spark/--primary»), а `--accent` в печати делит с ним одно
           значение за неимением отдельного полюса — ratio та же, что уже
           замерена и задокументирована для `--primary` строкой выше:
           5.30:1 (`#6D53E0` на белом). */
        --accent: var(--primary);
        --accent2: var(--primary);
        /* цветное радиальное свечение hero — декоративная деталь тёмного
           фона; на белом листе оно не нужно и не выражается полюсами. */
        --hero-glow: none;
        /* Канонический серифный стек печати, игнорирует выбор читателя
           гротеска (Finding 3, фикс-раунд 1): печать всегда на сериф,
           `--font-serif` определён в `fonts.css` как одно определение на
           понятие (принцип СПЕЦ). */
        --font-read: var(--font-serif);
    }

    /* ── типографика читальной колонки (спек: сериф, 11–12pt, ~1.45) ──── */

    :root,
    :root[data-preset] {
        /*
         * `--fs`/`--lh` — переменные ЧИТАЛКИ (`reader.css`), их читает
         * только `.reader article`: читатель управляет ими панелью
         * настроек (кегль/интерлиньяж), и то, что удобно НА ЭКРАНЕ (вплоть
         * до кегля «А++»), на бумаге тратит лишние листы. Печать берёт свои
         * фиксированные значения независимо от выбора читателя. Единицы —
         * `pt`, физическая величина печатного текста (спек §2.7: «11–12pt»),
         * не «магический пиксель» — гейт `no-magic-space.test.ts` ловит
         * только `px` (см. докблок задачи), pt вне его периметра осознанно.
         */
        --fs: 11.5pt;
        --lh: 1.45;
    }

    /* Читальная колонка — на всю ширину листа: `--col` (`reader.css`, значение
       из токена `--read-col`) держит удобную ЭКРАННУЮ меру строки, а
       верхний/нижний паддинг там же (76px/90px) резервирует место под
       фиксированный `.chrome`, которого в печати больше нет. */
    .reader main {
        max-width: none;
        padding: 0;
        margin: 0;
    }

    /* ── хром прячется целиком (спек: топбар, подвал, панель настроек,
         кнопки действий, «⋯», формы, лайк/подписка, поиск) ────────────── */

    /* `.rf-topbar` — Дизайн-спринт, Task 10: прежний `.site-head` (Sprint
       1.1) заменён топбаром при переносе шапки на новый словарь, и это
       правило обязано ссылаться на актуальное имя — иначе селектор молча
       перестаёт что-либо находить, а хром начинает печататься целиком
       (найдено именно так: в снимке `styleguide-390-print` топбар остался
       на бумаге до этой правки). */
    .rf-topbar,
    .chrome,
    .rf-progress,
    .overlay,
    .sheet,
    .rf-sheet,
    .rf-sheet-host,
    .rf-sheet-backdrop,
    .rf-btn,
    .bookmark-control,
    .hide-menu,
    .site-search,
    .search-form,
    /* `.rf-searchbar` (Дизайн-спринт, Task 15) — форма поиска после переезда
       на словарь `rf-*`. Дописана В ОДНОМ КОММИТЕ с переименованием, ровно
       по уроку из докблока выше: `.search-form` на `/search` перестал
       что-либо находить, и на бумагу поехали поле запроса, переключатель
       области, селект сортировки и ряд фасетов — весь хром страницы, кроме
       листов (их гасит `.rf-sheet-host` строкой выше). `.search-form`
       остаётся: его по-прежнему печатает онбординг. Строка чипов
       выбранных фильтров (`.rf-active-filters`) СОЗНАТЕЛЬНО не скрыта —
       она и раньше печаталась (`.active-filters` в списке не стояла) и
       единственная говорит с листа, ЧТО именно искали. */
    .rf-searchbar,
    .like-form,
    .like-anon,
    .subscribe-form,
    footer {
        display: none;
    }

    /* Специфичность выше правила раскрытия `<details>` ниже — обе формы
       лежат ПРЯМЫМИ детьми закрытого `<details>` (см. РЕШЕНИЕ 3). */
    .comments > .comment-form,
    .chip-menu > .subscribe-form {
        display: none;
    }

    /* `.like-anon` (CTA для анонима: «Войдите, чтобы отметить/комментировать»)
       находится ВНУТРИ `<details class="comments">` и попадает под раскрытие
       `details:not([open]) > :not(summary)`, которое имеет специфичность
       0,1,2 (выше, чем 0,1,0 у голого класса). Это правило фиксит это,
       используя специфичность 0,2,1. */
    details:not([open]) .like-anon {
        display: none;
    }

    /* ── `<details>` раскрываются принудительно (РЕШЕНИЕ 3) ──────────── */

    details:not([open]) > :not(summary) {
        display: block;
    }

    /* Современный Chromium прячет содержимое закрытого `<details>` через
       `::details-content`, а не через `display: none` на детях — правило
       выше снимает старый механизм, это снимает новый (докблок РЕШЕНИЯ 3
       объясняет, как находка была подтверждена руками, версия движка). */
    details::details-content {
        block-size: auto;
        content-visibility: visible;
        overflow: visible;
    }

    /* Треугольник раскрытия — управляющий элемент интерфейса, а не
       контент; на бумаге, где раскрыто уже ВСЁ, он вводит в заблуждение
       (указывает «закрыто», хотя содержимое напечатано). */
    details > summary {
        list-style: none;
    }

    details > summary::-webkit-details-marker {
        display: none;
    }

    /* ── колонтитул главы (см. докблок выше) ─────────────────────────── */

    main[data-print-title]::before {
        content: attr(data-print-title) " · " attr(data-print-author) " · Глава " attr(data-position);
        display: block;
        margin-block-end: var(--space-5);
        padding-block-end: var(--space-3);
        border-block-end: 1px solid var(--border);
        font-family: var(--font-ui);
        font-size: 10pt;
        color: var(--text-muted);
    }

    /* ── разрывы страниц (спек §2.7) ──────────────────────────────────── */

    /* Карточки ленты/поиска и бейджи (рейтинг/направленность/статус/
       предупреждение) не должны разрезаться пополам между листами; чипы —
       та же логика, что бейджи (маленький самодостаточный блок); блок
       предупреждений — контейнер бейджей, целиком. `.card` → `.rf-card`
       (дизайн-спринт, Task 11: класс карточки переименован целиком,
       докблок `app.css`, секция «карточка работы»); `.rf-chip` добавлен
       той же правкой — теги карточки печатают его вместо `.chip`.
       `.warnings` → `.rf-warnings` (дизайн-спринт, Task 14: шапка работы
       переименовала класс целиком, докблок `app.css`, секция «шапка
       работы и обсуждение» — класс принадлежал только этой странице, чужих
       потребителей не было). */
    .rf-card,
    .badge,
    .rf-badge,
    .chip,
    .rf-chip,
    .rf-warnings {
        break-inside: avoid;
    }

    /* Заголовок, оставшийся один на листе без своего содержимого — то же,
       что «висящая» строка ниже, только для структуры, а не абзаца.
       `summary` — заголовок раскрытого `<details>` (см. выше), та же
       логика. */
    h1, h2, h3, h4, h5, h6,
    summary {
        break-after: avoid;
    }

    /* Классические типографские сироты/вдовы — одна строка абзаца или
       пункта списка не должна оставаться одна на листе. */
    p, li {
        orphans: 2;
        widows: 2;
    }

    /* ── адреса дописываются только контентным ссылкам ────────────────── */

    /*
     * `a[href^="http"]` УЖЕ отсекает почти всю навигацию сам по себе:
     * `path()` Twig генерирует ОТНОСИТЕЛЬНЫЕ адреса (`/works/…`), и только
     * действительно ВНЕШНИЕ ссылки (примечания автора, текст главы —
     * `chapter.html|raw`, санитизированный, но не переписывающий чужие
     * `href`) начинаются с `http`. Скоуп на `main` — вторая, защитная
     * граница: `.rf-topbar`/`.chrome` физически лежат СНАРУЖИ `<main>` на
     * всех четырёх проверяемых страницах, так что даже гипотетическая
     * абсолютная ссылка в хроме сюда не попадёт.
     */
    main a[href^="http"]::after {
        content: " (" attr(href) ")";
        font-size: .85em;
        color: var(--text-muted);
    }

    /* ── рейтинг-бейджи не опираются на цвет ──────────────────────────── */

    /*
     * Текст-подпись рейтинга/направленности/статуса/предупреждения уже
     * есть внутри бейджа (макросы `ui.badge_*`, легаси `.badge`/`.b-*`) —
     * цвет там ДОПОЛНЯЕТ подпись, а не заменяет её. На чёрно-белом принтере
     * цветовая пара «зелёный (G) → красный (NC-17)» неразличима вовсе;
     * рамка — второй, не завязанный на цвет сигнал «это бейдж», а не
     * попытка починить сам цвет. `currentColor`, а не токен — рамка обязана
     * совпасть с ЛЮБЫМ цветом подписи, включая четыре разных рейтинговых
     * оттенка одним правилом, без четырёх модификаторов.
     */
    .badge,
    .rf-badge {
        border: 1px solid currentColor;
    }
}
