/* ═══ СКРОЛЛ КЛАССА А — переносимый модуль движения. Пара к js/klass-a.js.
   Ни одной внешней зависимости: нативные animation-timeline, IntersectionObserver,
   @property, View Transitions, Canvas 2D. Первоисточник разбора приёмов —
   ~/cehmedia/research/design-reference-atoms-2026-08-23.md (§2 атомы, §3 технологии,
   §4 порядок внедрения, §5 анти-слоп). Канон переноса — ~/projects-hub/SKROLL-KLASS-A.md.

   Настраивается снаружи, без правки кода: data-* атрибуты в разметке (см. js)
   и CSS-переменные ниже. Селекторы модуля обёрнуты в :where() везде, где он
   оформляет чужие узлы, — специфичность 0, проектный CSS всегда сильнее.

   Каждый приём гаснет целиком при prefers-reduced-motion (блок в конце файла). */

:root{
  --ka-ease:cubic-bezier(.16,1,.3,1);   /* экспоненциальный выход, не linear */
  --ka-cvet:var(--gold,#DCD96F);        /* курсор и метки берут акцент мира */
  --ka-stroka-vremya:.72s;              /* длительность выезда одной строки */
  --ka-stroka-shag:72ms;                /* задержка между соседними строками */
  --ka-kursor-d:26px;                   /* диаметр кольца-курсора в покое */
  --ka-kursor-d-nad:46px;               /* над интерактивным элементом */
  --ka-hod:2;                           /* сколько экранов держится закреплённая сцена */
}

/* ── 1. SCROLL-SCRUB, много сцен на страницу.
   Первый кадр лежит в разметке обычным <img> и виден без JS — он же задаёт
   пропорции блока. Холст накрывает его сверху и проявляется, только когда
   реально нарисовал кадр: иначе на снимке всей страницы была бы пустота.
   Тридцати живых <img> в потоке отрисовки не появляется — кадры живут
   объектами Image вне DOM (см. js). */
[data-scrub]{position:relative}
:where([data-scrub]) > :where(img){display:block;width:100%;height:auto}
.ka-holst{
  position:absolute;inset:0;width:100%;height:100%;
  opacity:0;pointer-events:none;transition:opacity .32s var(--ka-ease);
}
[data-scrub-gotov] > .ka-holst{opacity:1}
/* Сцена, которая по покою отдаёт кадры другому владельцу (у нас — взгляд за
   курсором): холст гаснет, под ним оживает проектная стопка кадров. */
[data-scrub-otdayot][data-rezhim="vzglyad"] > .ka-holst{opacity:0}

/* Второй источник кадров — стопка готовых <img data-k>. Оформление проектное;
   правила ниже в :where() (специфичность 0) подхватывают проект без своего. */
:where([data-scrub]) :where([data-k]){position:absolute;inset:0;opacity:0;transition:opacity .2s linear}
:where([data-scrub]) :where([data-k][data-on]){opacity:1}

/* ── 2. ЗАКРЕПЛЁННАЯ СЦЕНА (pin). Обёртка высокая, сцена внутри липкая,
   кадры лежат в одной ячейке грида и сменяют друг друга по прокрутке.
   Высота хода задаётся снаружи: style="--ka-hod:2.6". */
[data-ka="pin"]{position:relative;min-height:calc(var(--ka-hod) * 100svh)}
[data-ka-scena]{position:sticky;top:0;min-height:100svh;display:grid;align-content:center}
[data-ka-kadr]{
  grid-area:1/1;opacity:0;transform:translateY(30px);filter:blur(8px);pointer-events:none;
  transition:opacity .5s var(--ka-ease),transform .62s var(--ka-ease),filter .62s var(--ka-ease);
}
[data-ka-kadr][data-aktiv]{opacity:1;transform:none;filter:none;pointer-events:auto}
/* Кадр, который уже прошли, уходит вверх — не вниз: проход идёт вперёд. */
[data-ka-kadr][data-proshli]{transform:translateY(-30px)}

/* ── 3. РАСКРЫТИЕ ПО СТРОКАМ. Слово — окно с overflow, внутри выезжает <i>.
   Слова одной строки получают один --i, поэтому строка раскрывается целиком.
   padding/margin по низу — чтобы окно не срезало хвосты у «у», «р», «д». */
.ka-sl{display:inline-block;overflow:hidden;vertical-align:bottom;padding-bottom:.14em;margin-bottom:-.14em}
.ka-sl > i{
  display:inline-block;font-style:inherit;
  transform:translateY(108%);opacity:0;
  transition:transform var(--ka-stroka-vremya) var(--ka-ease),opacity var(--ka-stroka-vremya) var(--ka-ease);
  transition-delay:calc(var(--i,0) * var(--ka-stroka-shag));
}
[data-ka="stroki"].ka-vidno .ka-sl > i{transform:none;opacity:1}

/* ── 4. КУРСОР-ЛИНЗА. Кольцо догоняет указатель с лагом (один общий rAF-луп
   вместе с магнитными кнопками — см. js). Системный курсор не прячем:
   спрятанный курсор ломает попадание по мелким целям и доступность. */
.ka-kursor{
  position:fixed;left:0;top:0;z-index:60;pointer-events:none;
  width:var(--ka-kursor-d);height:var(--ka-kursor-d);
  border:1.5px solid var(--ka-cvet);border-radius:50%;
  opacity:.5;will-change:transform;
  transition:width .22s var(--ka-ease),height .22s var(--ka-ease),opacity .22s linear;
}
.ka-kursor[data-nad]{--ka-kursor-d:var(--ka-kursor-d-nad);opacity:.85}
.ka-kursor[data-spryatan]{opacity:0}

/* ── 5. ПЕРЕХОД МЕЖДУ СТРАНИЦАМИ ОДНОЙ КОМПОЗИЦИЕЙ.
   Нативный путь: браузер сам сводит две страницы. Правило игнорируется теми,
   кто его не знает, — это и есть первый уровень фолбэка. */
@view-transition{navigation:auto}
::view-transition-old(root){animation:ka-vt-uhod .2s linear both}
::view-transition-new(root){animation:ka-vt-prihod .36s var(--ka-ease) both}
@keyframes ka-vt-uhod{to{opacity:0;transform:translateY(-8px)}}
@keyframes ka-vt-prihod{from{opacity:0;transform:translateY(12px)}}
/* Второй уровень: браузер без View Transitions получает тот же уход от js. */
html.ka-uhod body{opacity:0;transition:opacity .17s linear}

/* ── ПОЛНОЕ ВЫКЛЮЧЕНИЕ. Не половина приёмов, а все: сцена перестаёт быть
   закреплённой, кадры встают в поток, строки видны сразу, кольца нет. */
@media (prefers-reduced-motion:reduce){
  .ka-holst{display:none}
  :where([data-scrub]) :where([data-k]){transition:none}
  [data-ka="pin"]{min-height:0}
  [data-ka-scena]{position:static;min-height:0;display:block}
  [data-ka-kadr]{opacity:1;transform:none;filter:none;pointer-events:auto;transition:none}
  [data-ka-kadr] + [data-ka-kadr]{margin-top:clamp(24px,3vw,40px)}
  .ka-sl > i{transform:none;opacity:1;transition:none}
  .ka-kursor{display:none}
  html.ka-uhod body{opacity:1;transition:none}
  ::view-transition-old(root),::view-transition-new(root){animation:none}
}
