/* public/print.css
   Печатный слой клиентского PDF (Фаза 1 плана
   plans/2026-09-01-client-pdf-print-layout.md, спека
   plans/2026-09-01-client-pdf-print-layout-design.md, числовой стандарт V1c).

   Подключается ТОЛЬКО как <link rel="stylesheet" media="print"> в public/app.html -
   на экран ни одно правило этого файла не действует, экранный вид отчёта не меняется.
   До этой фазы печатные правила были раскиданы по @media print внутри public/app.html и
   public/components.css; теперь они живут в одном месте и покрывают оба печатаемых
   документа - основной отчёт (#report-section) и пакет внедрения
   (#implementation-package-print, переключается классом body.print-implementation-package).

   Инвариант проекта: цвета только через var(--...), hex-литералов здесь быть не должно
   (tests/ui/print-layer.test.ts, npm run check:print - Фаза 4).
*/

/* ==============================================================
   Страница: формат, поля, колонтитулы, нумерация
   ============================================================== */

@page {
  size: A4;
  margin: 16mm 18mm 16mm 18mm;

  /* Домен разбираемой страницы и дата документа подставляются в --print-header-left /
     --print-header-right на :root (Фаза 2 - preparePrintView, либо Фаза 3 - серверный
     рендер) - здесь только нейтральный дефолт на случай, если переменная ещё не задана
     (например, печать через Ctrl+P до Фазы 2). Поля @page НЕ наследуют font-family
     документа - без явного значения Chromium печатает их системным serif-шрифтом (замер
     брейншторма 2026-09-01: 2,8% текста ушло в Times New Roman) - прямое нарушение
     правила проекта no-serif-fonts. */
  @top-left {
    content: var(--print-header-left, "Разбор страницы");
    font-family: var(--font-sans);
    font-size: 8pt;
    color: var(--text-muted);
  }
  @top-right {
    content: var(--print-header-right, "");
    font-family: var(--font-sans);
    font-size: 8pt;
    color: var(--text-muted);
  }
  @bottom-center {
    content: "стр. " counter(page) " из " counter(pages);
    font-family: var(--font-sans);
    font-size: 8pt;
    color: var(--text-muted);
  }
}

/* Первый лист: шапка документа уже строится в теле (Фаза 2), верхний колонтитул её не дублирует. */
@page :first {
  @top-left { content: ""; }
  @top-right { content: ""; }
}

/* ==============================================================
   Плотность текста: токены V1c переопределены ТОЛЬКО для печати
   ============================================================== */

@media print {
  :root {
    --space-1: 2px;
    --space-2: 4px;
    --space-3: 6px;
    --space-4: 9px;
    --space-5: 12px;
    --space-6: 15px;
    --space-7: 18px;
    --space-8: 22px;

    --lh-tight: 1.18;
    --lh-snug: 1.30;
    --lh-body: 1.40;

    --fs-display: 21px;
    --fs-price: 18.7px;
    --fs-h1: 18px;
    --fs-h2: 15.3px;
    --fs-h3: 13.3px;
    --fs-lead: 13.3px;
    --fs-body: 12.7px;
    --fs-sm: 11.3px;
    --fs-xs: 11.3px;
    --fs-2xs: 9.7px;
    --fs-3xs: 8.7px;

    --radius: 2px;
  }

  html {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }

  body {
    background: var(--surface);
    orphans: 3;
    widows: 3;
  }

  /* Отчёт и пакет внедрения занимают всю полосу набора, без экранной сетки/паддингов .doc. */
  .doc {
    max-width: none;
    padding: 0;
    margin: 0;
  }

  #report-section {
    display: block !important;
  }

  /* -------------------- Служебное и продающее в печати не участвует -------------------- */
  .skip-link, .site-header,
  #input-section, #state-section,
  #operator-tier-toggle, #operator-format-row, #admin-panel, #export-panel,
  .selling-section, #packages-block, #tizer-block,
  .lp-footer, .upsell, .cookie-banner,
  .btn, .form-card, .status-bar, .input-hero, .state-running {
    display: none !important;
  }

  /* Полное ТЗ подрядчика печатается только в отдельном документе (пакет внедрения). */
  [data-block="tz"] {
    display: none !important;
  }

  /* Печать основного отчёта и печать пакета внедрения - взаимоисключающие тела одной
     страницы (переключатель - класс body, ставят printReport()/printImplementationPackage()
     в app.html). */
  body.print-implementation-package #report-section {
    display: none !important;
  }
  body.print-implementation-package #implementation-package-print {
    display: block !important;
  }

  /* -------------------- Полоса набора: мера, перенос длинных строк --------------------
     Развилка 3a спеки: сплошной текст без ограничения меры идёт на всю ширину листа
     (90-й перцентиль строки - 104 знака, максимум 113). Карточки держат меру своим
     внутренним отступом, сплошной текст - нет. Правило НЕ адресует содержимое table -
     иначе мера ломает колонки (edge case 19 плана). */
  #report-section p,
  #report-section li,
  #report-section .section-note,
  #report-section .disclaimer,
  #report-section .rec__body,
  #report-section .card__lead,
  #implementation-package-print p,
  #implementation-package-print li,
  #implementation-package-print .implementation-copy {
    max-width: 46em;
  }

  #report-section,
  #implementation-package-print {
    overflow-wrap: anywhere;
  }

  /* -------------------- Разрывы: запрет только там, где выполним --------------------
     Замер 31.08 (ранее - components.css): break-inside:avoid на .report-block целиком
     невыполним - раздел GEO занимает 5836px при полезной высоте листа ~900px, браузер
     такой раздел всё равно рвёт, а хвост предыдущего листа остаётся пустым (заполнение
     листов было 51/29/46%). Поэтому запрет ставится только на заведомо короткие единицы
     и на заголовки; крупные контейнеры (.report-block, .card, .draft-card) разрыв не
     запрещают - их целостность держат orphans/widows и правило заголовков. Принудительный
     break-before: page на разделах отчёта не ставится нигде. */
  h1, h2, h3, .report-block__title, .card__title, summary {
    break-after: avoid;
    page-break-after: avoid;
  }

  .rec, .kpi-card, .notice, .idea-card, tr, li {
    break-inside: avoid;
    page-break-inside: avoid;
  }

  /* Приложение «Что именно проверили»: доказательная база печатается плотным двухколоночным
     списком. Спека (Развилка 2 / Политика разрывов) давала здесь единственное исключение -
     принудительный break-before: page. Решение владельца 2026-09-01 по замеру приёмки: разрыв
     СНЯТ. Цена разрыва оказалась выше его пользы - на боевом теле он оставлял стр. 13 из 16
     заполненной на 7% (четыре домена, оторванных от перечня выдачи, и белый лист под ними) в
     середине платного документа. Без разрыва: 15 страниц вместо 16, недозаполненных листов 1
     вместо 2 (остаётся только хвост с дисклеймером). Приложение начинается сразу после раздела
     конкурентов, свой заголовок у него есть. */
  [data-block="appendix"] {
    column-count: 2;
    column-gap: var(--space-6);
    column-fill: auto;
  }

  /* -------------------- Карточки: де-хромирование под печать --------------------
     Только тень/скругление - запрет разрыва (break-inside: avoid) для этих селекторов
     здесь НЕ ставится намеренно: .card/.verdict-panel/.subblock-details/.draft-card -
     контейнеры уровня раздела (могут содержать раскрытые Фазой 2 <details> и вырасти выше
     листа A4), для них действует политика спеки «разрыв разрешён, целостность держат
     orphans/widows и правило заголовков» (см. блок выше). Запрет разрыва для мелких единиц
     (.rec/.kpi-card/.notice/.idea-card) уже объявлен отдельным правилом строкой выше -
     не дублируется здесь. */
  .card, .rec, .notice, .verdict-panel, .kpi-card, .idea-card, .subblock-details, .draft-card {
    box-shadow: none;
    border-radius: var(--radius);
  }
  .card, .draft-card, .idea-card, .notice {
    border: 1px solid var(--border);
  }
  .kpi-card {
    border: none;
    border-left: 2px solid var(--brand);
    min-height: 0;
    padding: 0 0 0 var(--space-3);
  }

  /* Вердикт-панель на экране - плотная олива с белым текстом, которая держится на
     printBackground: true. Находка брейншторма 2026-09-01: у клиента печать фоновой графики
     по умолчанию выключена, и главная фраза вердикта уходит в бледно-серое на белом - боевой
     PDF был хуже забракованного. В печати панель светлая и читается без фоновой заливки. */
  .verdict-panel {
    background: var(--brand-soft);
    border: 1px solid var(--brand-border);
    color: var(--text-heading);
  }
  .verdict-panel .verdict-summary,
  .verdict-panel .kpi-card__value,
  .verdict-panel .kpi-card__label {
    color: var(--text-heading);
  }

  /* Вердикт-бейдж: реально применяемый в отчёте класс - .verdict-panel__badge--* (задаётся
     в app.html VERDICT_BADGE / components.css), не .verdict-badge--* (тот живёт только на
     public/styleguide.html - там своей карточки печати нет, print.css не подключён).
     На экране .verdict-panel__badge--good рассчитан на тёмную оливу вердикт-панели
     (--brand-deep-badge-text - светлый лайм на тёмном фоне); панель выше стала светлой
     (--brand-soft) - без переопределения бейдж дал бы светло-лаймовый текст на светло-
     лаймовом фоне (контраст ~1.3). Здесь все три уровня переведены на общие мягкие пары
     токенов - те же, что уже используют --warn/--crit на экране, читаемы на светлом фоне
     без фоновой графики (printBackground не требуется). */
  .verdict-panel__badge--good { background: var(--ok-soft);   border-color: var(--ok-border);   color: var(--ok-text); }
  .verdict-panel__badge--warn { background: var(--warn-soft); color: var(--warn-text); }
  .verdict-panel__badge--crit { background: var(--crit-soft); color: var(--crit-text); }

  /* details печатаются раскрытыми (какие именно раскрывает Фаза 2 - preparePrintView);
     здесь только оформление - без интерактивной стрелки и без маркера summary. */
  details > summary {
    list-style: none;
  }
  details > summary::-webkit-details-marker,
  details > summary::marker {
    display: none;
    content: "";
  }
  details > summary::before {
    content: "" !important;
  }

  /* -------------------- Таблицы: уменьшенный кегль, карточная раскладка при переполнении --
     Та же раскладка, что уже работает на мобильной ширине ≤600px (находка 29.08: таблица
     «Браузерная проверка первого экрана» 495px внутри 358px) - полоса набора в печати
     (174мм ≈ 658px) её тоже не вмещает без карточной раскладки. */
  table {
    font-size: var(--fs-2xs);
  }
  td, th {
    overflow-wrap: anywhere;
  }
  /* На печати перечень идёт ОДНОЙ колонкой. Замер 2026-09-04: в три колонки лист 17 давал
     44 пары строк из соседних колонок с шагом около нуля из 92, и медианный интерлиньяж всего
     документа падал 1.391 -> 1.339 при пороге 1.35 - на узкой колонке пункт ещё и переносится
     на 2-3 строки, поэтому высоты колонки не экономят. Во всю ширину пункт укладывается в одну
     строку. Экранная раскладка (components.css, две колонки) не меняется. */
  .canonical-checks-compact__list {
    columns: 1;
    line-height: var(--lh-body);
  }
  .canonical-checks-compact__list li {
    break-inside: avoid;
    /* На экране пункты разделены отбивкой; на листе она стоила бы лишний лист. */
    margin-bottom: 0;
  }
  .table--stacked, .table--stacked tbody, .table--stacked tr, .table--stacked td {
    display: block;
    width: 100%;
  }
  .table--stacked thead {
    display: none;
  }
  .table--stacked tr {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    margin-top: var(--space-3);
    overflow: hidden;
  }
  .table--stacked tr:nth-child(even) td {
    background: var(--surface);
  }
  .table--stacked td {
    border: 0;
    border-top: 1px solid var(--border);
  }
  .table--stacked tr td:first-child {
    border-top: 0;
    background: var(--surface-sunken);
    color: var(--text);
    font-weight: var(--fw-bold);
  }
  .table--stacked td[data-label]::before {
    content: attr(data-label);
    display: block;
    font-size: var(--fs-3xs);
    text-transform: uppercase;
    letter-spacing: var(--tracking-label);
    color: var(--text-muted);
    margin-bottom: 2px;
  }
  .table--stacked tr td:first-child[data-label]::before {
    color: var(--text-muted);
  }

  /* -------------------- Снимки первого экрана (Фаза 15, 2026-09-06) --------------------
     Экранная раскладка растёт по естественному размеру снимка (components.css) - на листе
     A4 это неприемлемо: desktop-снимок (1366x768) занял бы ~98мм высоты, а mobile-снимок
     (390x844, узкий и высокий) при той же логике «ширина контейнера = высота по пропорции»
     занял бы ~184мм - пара снимков вместе превысила бы полезную высоту листа (~265мм).
     Печатный размер снимка фиксирован по высоте независимо от исходного разрешения и от
     веса файла; object-fit:contain не обрезает и не тянет пропорции.

     Замер живым Chromium+PyMuPDF (сценарий вне check:print - боевая фикстура
     tmp/compaction-phase456/paid-response.json снята ДО этой фазы и не содержит снимков,
     официальный гейт её не трогает): на main-full запас перед границей листа в этой точке
     документа - около 4мм. Любая видимая версия блока снимков высотой от ~5мм и выше даёт
     ОДИНАКОВО +1 лист (17 -> 18) независимо от того, 5мм это или 42мм, и независимо от веса
     файла снимка (проверено от пустого до 140 КБ на снимок) - минимизация размера ниже
     разборчивого не спасает лист, а нераспознаваемая миниатюра (4мм) не была бы снимком по
     смыслу задачи. main-full таким образом объективно нуждается в 18-м листе на живом
     прогоне с реальными снимками - решение владельца (docs Фазы 15) разрешает это как
     единственный выход. Порог `EXPECTED_PAGE_COUNT['main-full']` в
     scripts/check-print-typography.mjs поднят этой же правкой с 17 до 18 (не выше - граница
     владельца) на основании прямого замера с временно инъецированными снимками; официальная
     фикстура check:print сегодня снимков ещё не содержит и продолжает измерять 17 листов, то
     есть у гейта на постоянном коде есть лист запаса до того, как боевая фикстура будет
     пересобрана со снимками (см. комментарий рядом с EXPECTED_PAGE_COUNT в самом скрипте). */
  .browser-screenshot-grid {
    display: flex;
    gap: var(--space-3);
    break-inside: avoid;
    page-break-inside: avoid;
  }
  .browser-screenshot {
    flex: 1 1 0;
    min-width: 0;
  }
  .browser-screenshot__img {
    width: 100%;
    height: 32mm;
    object-fit: contain;
    background: var(--surface-sunken);
  }
  .browser-screenshot__caption {
    font-size: var(--fs-3xs);
    padding: var(--space-2) var(--space-2);
  }

  /* -------------------- Фаза 2: шапка первого листа и приложение --------------------
     Разметку строит preparePrintView() в app.html (Развилки 2/4 спеки) - здесь только
     оформление печатных узлов, которых на экране не существует. */
  /* Развилка 4 спеки: сгенерированная шапка - ОДНО место для названия документа, адреса,
     даты, пакета и границы разбора. Экранная карточка [data-block="head"] печатает те же
     четыре поля вторым разом (найдено ревью Фазы 2 - дубль на листе 1) - в печати не
     участвует, шапку строит buildPrintDocumentHeader() (app.html). */
  [data-block="head"] {
    display: none !important;
  }

  .print-doc-header {
    /* Замер ревью Фазы 2 (пиксельный, не по font-bbox): при padding-top: 0 верхушки
       выносных элементов заголовка (Р, б, з, l) отрезаются верхним полем @page - блок
       первого элемента #report-section стоит вплотную к границе полосы набора, и
       увеличения одного line-height недостаточно (проверено пробой: 1.18 -> 1.30 не убрало
       обрезку). Повторный замер ревью после фикса: при var(--space-2) запас до плоскости
       отсечения Chromium (45,000 pt) нулевой - два стебля срезались на 0,23 pt, невидимо,
       но следующая правка кегля вернула бы дефект. var(--space-3) даёт ~3 pt зазора. */
    padding-top: var(--space-3);
    margin-bottom: var(--space-6);
  }
  .print-doc-header__title {
    font-size: var(--fs-h1);
    /* line-height: var(--lh-tight) унаследован бы от общего правила h1 (components.css) -
       на 18px эта ступень (1.18) даёт слишком тесный интерлиньяж для заголовка, стоящего
       первой строкой листа; lh-snug (1.30) - тот же интерлиньяж, что у остального текста
       V1c, без визуальной ступеньки. */
    line-height: var(--lh-snug);
    margin: 0 0 var(--space-2);
  }
  .print-doc-header__meta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-4);
    color: var(--text-secondary);
    font-size: var(--fs-sm);
  }
  .print-doc-header__meta span + span::before {
    content: "· ";
  }
  .print-doc-header__scope {
    color: var(--text-muted);
    font-size: var(--fs-xs);
    margin: var(--space-2) 0 0;
  }
  .print-doc-header__toc {
    column-count: 2;
    column-gap: var(--space-6);
    margin: var(--space-4) 0 0;
    padding-left: 1.1em;
    font-size: var(--fs-sm);
  }

  /* Развилка 5: строка, заменяющая три схлопнутые карточки-ссылки реестра. */
  .print-registry-collapsed-note {
    font-style: italic;
  }

  /* Развилка 2: доказательная база («Что проверили») внутри приложения. Разрыв перед
     приложением задан на [data-block="appendix"] выше; модификатор ниже его отменяет,
     когда переехавших перечней меньше порога (edge case 18 - иначе короткий отчёт получает
     почти пустой лист только под пару списков). */
  .print-appendix-evidence__title {
    font-size: var(--fs-h3);
    margin: 0 0 var(--space-3);
    break-after: avoid;
    page-break-after: avoid;
  }
  /* Edge case 18: на коротком отчёте перечней мало, и двухколоночная раскладка двух строк
     читается как сломанная - модификатор возвращает одну колонку. Отмена принудительного
     разрыва отсюда убрана: после решения владельца 2026-09-01 разрыва нет и в базовом правиле,
     переопределять нечего. */
  [data-block="appendix"].appendix--no-forced-break {
    column-count: 1;
  }
}
