/* ══════════════════════════════════════════════════════════════════════════════
   dv-adapt.css — файл-компаньон рантайма dv-adapt-v2.js.
   Обязательный CSS контракта разметки (часть III). Подключается ПЕРЕД рантаймом:

     <link rel="stylesheet" href="dv-adapt.css">
     <script src="dv-adapt-v2.js"></script>

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

   Ни одного числа из спеки здесь нет: всё выводится из переменных, объявленных
   автором на корне (К1, Г-З7).
   ══════════════════════════════════════════════════════════════════════════════ */

/* ─── 1. Полоса прокрутки форсируется всегда ────────────────────────────────────
   Масштаб считается от clientWidth, а появление полосы clientWidth меняет:
   ширина → масштаб → высота содержимого → полоса → ширина. Без этого правила
   шесть пересчётов без изменения окна дают два чередующихся состояния на
   2 окнах из 17 (667×375 и 430×800, два разных канона), в каждом нарушен свой
   инвариант. scrollbar-gutter: stable заменой не служит: его резерв виден на
   содержимом корня, а root.clientWidth продолжает меняться. */
html { overflow-y: scroll; }

/* ─── 2. Точка трансформации объявлена, а не оставлена по умолчанию ─────────────
   Значение по умолчанию 50% 50% растит полотно и вверх, поэтому вертикальная
   точка обязана быть top: полотно растёт вниз от своего места в потоке.

   Горизонтальная точка — center, и это замерено, а не выбрано: layout-бокс
   полотна имеет ширину канона и центрируется контейнером (Г-З2), поэтому
   совпадать с видимым он может только при точке в центре. При left top
   полотно уезжает за левый край на 62.5 px на окне 320 и на 394 px на 667.

   Рантайм transform-origin не пишет: инлайн из кода перебивал бы это правило,
   и побеждал бы порядок каскада, а не решение (Г-З7, обратное направление). */
[data-dv-canvas] { transform-origin: center top; }

/* ─── 3. Хост: ширина окна и центрирование полотна флексом (X-G38, BC-G31) ─────
   Г-З2 требует, чтобы layout-бокс полотна центрировался контейнером — от этого
   зависит точка масштабирования center top. Но роль data-dv-host сама по себе
   ширины не задаёт: без правила хост берёт ширину РЕБЁНКА, то есть канон.

   Замерено (окно 909×540): хост 1440 при окне 909, левый край на 0, точка center
   села на страничный x = 720 вместо центра окна 454.5 — полотно [265.5 … 1174.5]
   вместо [0 … 909], сдвиг +265.5, правый край срезан. На 667×375 сдвиг 394 px.

   Формула сдвига: (канон / 2) × (1 − S). На канонных 1440 и 430 это 7.5 px —
   поэтому дефект был невидим в замерах узлов и дожил до проверки глазами.

   Центрирование флексом, а не отрицательным margin с процентом: база процента
   разрешается от хоста, и ошибка в ней молчалива. */
[data-dv-host] {
  position: relative;
  width: 100%;
  display: flex;
  justify-content: center;
}
[data-dv-canvas] { flex: none; }

/* Контейнер хоста не должен расти под ребёнка: у грид-родителя колонка auto
   поднимается до max-content, то есть до канона, и width:100% разрешается
   от 1440, а не от окна. Замерено: хост оставался 1440.
   Роль ставится на РОДИТЕЛЯ хоста, когда тот — грид. */
[data-dv-host-parent] { grid-template-columns: minmax(0, 1fr); }

/* ─── 4. Полотно скрыто до первого расчёта, но не дольше таймаута (К5) ──────────
   Таймаут живёт в стилях, а не в рантайме: рантайм при отказе кода не
   выполняется по определению, и таймаут внутри него не покрывает ровно те
   случаи, ради которых введён. Задержку анимации отсчитывает движок стилей. */
[data-dv-pending] [data-dv-canvas] {
  visibility: hidden;
  animation: dv-reveal 0s var(--dv-reveal-timeout, 1500ms) forwards;
}
@keyframes dv-reveal { to { visibility: visible; } }

/* ─── 5. Запас на время отказа: полотно масштабируется средствами стилей ────────
   Без него полотно шириной канона на узком окне даёт горизонтальную прокрутку —
   отказ инварианта В. Замерено (measure-k5.html, рантайм не подключён, два
   прогона совпали): прокрутка на 320, 430, 667 и 1440; с запасом её нет ни на
   одном окне, масштаб 0.2118 / 0.2882 / 0.4528 / 0.9896 / 1.0000.

   ФОРМА ЗАПИСИ КРИТИЧНА, И ОШИБКА В НЕЙ МОЛЧАЛИВА. Делить надо длину на ДЛИНУ:
   100vw / var(--dv-canon-h) даёт безразмерное число, и scale() его принимает.
   Запись calc(100vw / 1440) делит длину на число, даёт длину, и scale()
   отбрасывает объявление целиком — страница выглядит ровно как без запаса.

   Вьюпортная единица стоит НА полотне, не внутри него: Г-З5 запрещает
   вьюпортные единицы содержимому полотна, а не самому полотну.
   min() не даёт увеличивать: увеличение — решение рантайма, не стилей.
   Горизонтальный канон взят намеренно: до первого расчёта режим неизвестен,
   а горизонтальный канон больше, значит запас берёт худший случай.

   Запас даёт ЧИТАЕМУЮ СТРАНИЦУ БЕЗ ПРОКРУТКИ, а не верную геометрию:
   полы нажатия и кегля в этом режиме не выполняются. Деградация остаётся
   деградацией, но перестаёт выглядеть поломкой вёрстки. */
[data-dv-pending] [data-dv-canvas],
[data-dv-disabled] [data-dv-canvas] {
  transform: scale(min(1, 100vw / var(--dv-canon-h)));
}

/* ─── 6. Геометрия объявленной области нажатия ─────────────────────────────────
   data-dv-hit объявляет, что бокс ЕСТЬ цель нажатия и к нему применим пол.
   Ставится на саму область, не на видимый элемент внутри неё: это разные
   боксы (Г-З3 следствие 1). Размер ведётся от переменной — литерал запрещён.
   Область поглощает соседний отступ, а не добавляется к нему, поэтому
   flex-центрирование видимого элемента внутри неё входит в правило. */
[data-dv-hit] {
  width: var(--dv-hit-canon);
  height: var(--dv-hit-canon);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ─── 7. Узел хрома: трансформ от собственного масштаба ─────────────────────────
   Рантайм считает и пишет --dv-node-scale, но transform не ставит — иначе инлайн
   кода перебивал бы правило автора (тот же отказ, что в Г-З7). Без этого правила
   каждый проект пишет свой вариант, и check.html этого не увидит: та же болезнь,
   что с копиями обязательного CSS (G16).

   Точку трансформации узел объявляет сам — она зависит от его места в окне:
   док у низа — bottom center, модалка по центру — center center. */
[data-dv-chrome] {
  transform: scale(var(--dv-node-scale, 1));
}

/* ─── 8. Запас на время отказа для узлов хрома ─────────────────────────────────
   Запас пункта 4 покрывает только полотно; узел хрома при отказе оставался
   канонного размера и вылезал за экран — фиксированный док шириной 1440 на окне
   320. Деградация обязана быть деградацией и для хрома.

   Делитель — собственная канонная ширина узла (--dv-node-w), а не канон
   документа: узел у́же полотна и сжимается меньше. Форма записи та же:
   длина на ДЛИНУ. Фоллбэк 100vw даёт масштаб 1 у узла без объявленной ширины —
   такой узел вне контракта, и молча ужимать его нельзя. */
[data-dv-pending] [data-dv-chrome],
[data-dv-disabled] [data-dv-chrome] {
  transform: scale(min(1, 100vw / var(--dv-node-w, 100vw)));
}
