/* ============================================================================
   Главная (ТЗ tz-glavnaya.md, 01–02.09.2026). Подключается ПОСЛЕ components.css.
   Файл в реестре scripts/tokenized-css.txt — Stop hook гоняет stylelint.

   Порядок секций решён ТЗ §13: врата → трио → новости → связанное творчество →
   статистика → подвал. Секции ниже врат строит js/index-page/ (бандл
   js/index-page.js) и рисует ТОЛЬКО при наличии данных: пустая рама — дефект.
   Правило §10 на всю страницу: надстрочников нет; у секции один текстовый
   уровень, дальше содержимое. Правило §10.4: соседние секции не повторяют
   способ показа (объект в пространстве → панели → стопка листов → механизм →
   диаграмма). Все цвета, кегли, отступы и тени — токенами; литералы только в
   геометрии (px смещений, углы), которую гейт не проверяет и токенов на
   которую нет по смыслу.
   ============================================================================ */

/* Полотно из components.css (`.wh-page`) даёт колонку 1240 px с полями —
   главной это не нужно: первый экран и секции идут во всю ширину. */
.index-page {
  width: 100%;
  max-width: none;
  margin: 0;
  padding: 0 0 var(--s-20);
  /* лозы трио и повёрнутый лист газеты выступают за край до расчёта геометрии;
     clip (не hidden) — не создаёт контекста прокрутки и не ломает липкую сцену врат */
  overflow-x: clip;
}

/* ── Привязка к прокрутке: одна пара осей на секцию ─────────────────────────
   --enter 0→1 вход: кромка секции идёт от низа кадра до позиции чтения;
   --pass +1→−1 проход: центр секции относительно центра кадра, живёт всё время,
   пока секция в кадре, — этим секция и переходит в секцию.
   Пишет js/index-page/scroll.js по общему кадру.

   ⚠️ НЕЙТРАЛЬ РАВНА КОНЦУ: 1 и 0. Объявлена ТРИЖДЫ — здесь (действует даже
   там, где переменную забыли объявить), в правиле каждой секции и запасным
   значением в каждом var(). Поэтому мёртвый JS, замороженный кадровый цикл,
   reduced-motion и исключение в тике дают одну картину: всё на месте. Именно
   это позволило удалить страховочный таймер, который ставил класс появления
   всем пяти секциям на 4,3 с при НУЛЕВОЙ прокрутке и тем губил всё появление
   (замер 04.09.2026) — прятать больше нечего.

   ⚠️ Оси пишут ТОЛЬКО отдельные свойства translate / scale / rotate. Свойство
   transform остаётся за состояниями секций (раскрытие полотна 420 мс, защёлка
   диска 850 мс, переворот полосы 260 мс): иначе каждый кадр прокрутки
   перезапускал бы переход, движение отставало бы от пальца, и в статике это
   было бы невидимо. */
@property --enter { syntax: "<number>"; inherits: true; initial-value: 1; }
@property --pass { syntax: "<number>"; inherits: true; initial-value: 0; }

/* Захват прокрутки — нативный scroll-snap, НЕ перехват колеса (решение §1).
   index.css грузит только главная, поэтому правило на html здесь безопасно.
   Снап цепляет только начало врат: секция выше экрана, липкая сцена внутри,
   дальше свободный ход (§5). scroll-padding — под липкую шапку. */
html {
  scroll-snap-type: y proximity;
  scroll-padding-top: var(--header-h, 73px);
}
.gate { scroll-snap-align: start; }

/* ── Врата: первый экран ──────────────────────────────────────────────────
   Высота — разгон проявления (230vh десктоп / 175vh телефон — числа §5,
   под настройку). --p (0…1) ставит скрипт по прокрутке; без JS --p не задан и
   все выражения падают на 1 — конечное состояние видно всегда. */
.gate {
  position: relative;
  height: 230vh;
  /* карточка — главный объект: ~20% строки знака и до 40% высоты экрана
     (ТЗ §7 просит 22–26% и 35–38% одновременно — на 16:9 они несовместимы,
     выбран компромисс в пользу карточки) */
  --card-w: clamp(200px, min(22vw, 30vh), 340px);
}
.gate__scene {
  position: sticky;
  top: var(--header-h, 73px);
  height: calc(100vh - var(--header-h, 73px));
  height: calc(100svh - var(--header-h, 73px));
  overflow: hidden;
  background: var(--bg-void);
}

/* Фон зала — два предпечённых варианта (sharp: sat .30/bright .40 и
   sat .46/bright .56), перетекают по прозрачности. Фильтр на слое во весь
   экран каждый кадр — самая дорогая строка прототипа, здесь его нет. */
.gate__art {
  position: absolute;
  inset: 0;
  background-position: center 42%;
  background-size: cover;
  background-repeat: no-repeat;
}
.gate__art--dim { background-image: url(../assets/guild/hall/bg-hero-dim.webp); }
.gate__art--lit {
  background-image: url(../assets/guild/hall/bg-hero-lit.webp);
  opacity: var(--p, 1);
}
.gate__grain {
  position: absolute;
  inset: 0;
  background: var(--grain) center / 260px 260px;
  mix-blend-mode: overlay;
  opacity: 0.22;
  pointer-events: none;
}
.gate__vig {
  position: absolute;
  inset: 0;
  box-shadow: var(--sh-vignette);
  pointer-events: none;
}

/* Знак: плоско, одним цветом, огромным кеглем (§2). Кегль подбирает скрипт
   (--mark-size) так, чтобы строка заняла 94% ширины; пробел под карточку —
   ширина карточки плюс воздух. Знак остаётся в сцене (полёт в логотип снят). */
.gate__mark {
  position: absolute;
  inset: 0;
  z-index: var(--z-base);
  display: flex;
  align-items: center;
  justify-content: center;
  column-gap: calc(var(--card-w) + var(--s-14));  /* карточка наклонена в 3D и выступает за свой бокс — воздух до литер шире */
  margin: 0;
  pointer-events: none;
  font-family: var(--font-display);
  font-weight: var(--w-bold);
  font-size: var(--mark-size, 10vw);
  line-height: var(--lh-flat);
  letter-spacing: var(--ls-tight);
  color: var(--lamp);
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.45), 0 24px 46px rgba(0, 0, 0, 0.7);
  opacity: clamp(0, calc((var(--p, 1) - 0.46) * 3.4), 1);
}
.gate__word { display: block; }

/* Карточка-кнопка в пробеле знака */
.gate__stage {
  position: absolute;
  inset: 0;
  z-index: var(--z-sticky);
  display: grid;
  place-items: center;
  pointer-events: none;
  perspective: 1450px;
  perspective-origin: 50% 46%;
  opacity: clamp(0, calc((var(--p, 1) - 0.46) * 3.4), 1);
}
.gate__card {
  position: relative;
  display: block;
  width: var(--card-w);
  aspect-ratio: 290 / 390;
  pointer-events: auto;
  touch-action: none;          /* только на карточке: с неё нельзя начать прокрутку, с остального экрана — можно (§5) */
  user-select: none;
  cursor: grab;
  text-decoration: none;
  transform-style: preserve-3d;
}
.gate__card.is-drag { cursor: grabbing; }
.gate__card:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.gate__card-inner {
  position: absolute;
  inset: 0;
  display: block;
  transform-style: preserve-3d;
  transform: rotateY(var(--ry, -17deg)) rotateX(var(--rx, 5deg)) translateZ(calc(var(--p, 1) * 44px));
}
.gate__face {
  position: absolute;
  inset: 0;
  display: block;
  backface-visibility: hidden;
  border-radius: var(--r-1);
  box-shadow: var(--sh-2);
}
/* Лицо карточки ГГ — запечённая рамка (assets/home/hero-frame.webp: кость с
   фаской, гравировка, ПУСТОЕ поле); в поле — снимок (__field/__photo): аватар
   вошедшего или бюст безликого игрока (ставит hero.js); планка — HTML поверх */
.gate__frame { display: block; width: 100%; height: 100%; object-fit: fill; border-radius: var(--r-1); }
/* поле рамки: x 44..536, y 44..604 холста 580×780 */
.gate__field {
  position: absolute;
  left: 7.6%;
  top: 5.65%;
  width: 84.8%;
  height: 71.8%;
  display: block;
  overflow: hidden;
  background: var(--bg-void);
}
.gate__photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 30%;
}
/* аватар — отпечаток, а не стикер: сдержанный цвет, тёплый тон, виньетка поверх */
.gate__photo.is-avatar { filter: sepia(0.3) saturate(0.82) contrast(1.06) brightness(0.94); }
.gate__field::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(80% 70% at 50% 42%, transparent 55%, rgba(0, 0, 0, 0.62)),
    linear-gradient(180deg, rgba(0, 0, 0, 0.18), transparent 25%, transparent 78%, rgba(0, 0, 0, 0.35));
  box-shadow: var(--sh-inset);
  pointer-events: none;
}
.gate__plate {
  position: absolute;
  left: 10%;
  right: 24%;
  top: 80.5%;
  height: 12.3%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s-1);
  text-align: center;
}
.gate__plate-l {
  font-family: var(--font-display);
  font-weight: var(--w-bold);
  font-size: var(--t-2);
  line-height: var(--lh-flat);
  letter-spacing: var(--ls-tight);
  color: var(--lamp);
}
.gate__plate-n {
  font-family: var(--font-display);
  font-size: var(--t--1);
  letter-spacing: var(--ls-kicker);
  color: var(--lamp-3);
}
/* Проявление отпечатка: тёмная вуаль над лицом карточки тает по прокрутке
   после такта затвора — «снимок» живёт в самой карточке, не в камере */
.gate__dev {
  position: absolute;
  inset: 0;
  display: block;
  border-radius: var(--r-1);
  background: var(--bg-void);
  pointer-events: none;
  opacity: clamp(0, calc(1 - (var(--p, 1) - 0.5) * 2.6), 1);
  transform: translateZ(0.4px);
}
/* Толщина: три тёмных слоя за лицом со светлой кромкой сверху — карточка
   как предмет с торцами, а не картинка поверх фона */
.gate__th {
  position: absolute;
  inset: 0;
  display: block;
  border-radius: var(--r-1);
  background: var(--bg-void);
  border-top: 1px solid color-mix(in srgb, var(--lamp) 28%, transparent);
  box-shadow: var(--sh-plate);
}
.gate__th--1 { transform: translateZ(-2.5px); }
.gate__th--2 { transform: translateZ(-5px); }
.gate__th--3 { transform: translateZ(-7.5px); }
/* Блик лампы на лаке: скользит по карточке против наклона (--gx/--gy ставит скрипт) */
.gate__gloss {
  position: absolute;
  inset: 0;
  display: block;
  border-radius: var(--r-1);
  pointer-events: none;
  transform: translateZ(0.5px);
  background:
    radial-gradient(60% 46% at var(--gx, 50%) var(--gy, 28%), color-mix(in srgb, var(--lamp) 22%, transparent), transparent 70%),
    linear-gradient(180deg, color-mix(in srgb, var(--lamp) 14%, transparent), transparent 18%);
  mix-blend-mode: screen;
}
/* Тень на столешницу: контакт с поверхностью зала, уезжает в сторону наклона */
.gate__shadow {
  position: absolute;
  left: -12%;
  right: -12%;
  bottom: -9%;
  height: 16%;
  display: block;
  border-radius: var(--r-pill);
  background: radial-gradient(50% 50% at 50% 50%, color-mix(in srgb, var(--bg-void) 85%, transparent), transparent 72%);
  transform: translateX(var(--shx, 0px));
  filter: blur(10px);
  pointer-events: none;
  transition: opacity var(--dur-3) ease;
}

/* След контакта догоняет предмет: тени на столешнице до касания физически нет,
   она ложится после такта затвора. Это же даёт работу классу is-shot, который
   hero.js ставил, а CSS не читал.
   ⚠️ Только transition, не animation: is-shot переключается плоским toggle по
   порогу --p, и CSS-анимация перезапускалась бы на каждом пересечении — при
   флик-прокрутке это серия вспышек.
   ⚠️ transition-property именно opacity: transform тени гоняется каждым кадром
   от --shx в hero.js. */
.gate:not(.is-shot) .gate__shadow { opacity: 0; }
/* Рубашка — спроектированная поверхность: пурпур картотеки, латунная штриховка,
   печать гильдии, тонкая внутренняя кайма. Увидеть её — награда за оборот. */
.gate__face--back {
  transform: rotateY(180deg);
  border: 1px solid var(--accent-line);
  background:
    repeating-linear-gradient(45deg, color-mix(in srgb, var(--brass) 14%, transparent) 0 3px, transparent 3px 10px),
    linear-gradient(166deg, var(--plum-lit), var(--plum) 55%, var(--plum-deep));
}
.gate__face--back::before {
  content: "";
  position: absolute;
  inset: 6%;
  border: 1px solid color-mix(in srgb, var(--brass) 55%, transparent);
}
.gate__back-seal {
  position: absolute;
  left: 50%;
  top: 44%;
  width: 44%;
  transform: translate(-50%, -50%);
  color: var(--brass);
  opacity: 0.55;
}
.gate__back-seal svg { display: block; width: 100%; height: auto; fill: currentColor; }

.gate__cross::before { left: 50%; top: 0; bottom: 0; width: 1px; }
.gate__cross::after { top: 50%; left: 0; right: 0; height: 1px; }

/* Подсказка прокрутки: объявлена, а не угадывается; гаснет по ходу снимка */
.gate__hint {
  position: absolute;
  left: 0;
  right: 0;
  bottom: var(--s-14);           /* выше ручки выдвижного футера (32px) */
  z-index: var(--z-sticky);
  text-align: center;
  font-family: var(--font-body);
  font-size: var(--t--1);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--lamp-3);
  pointer-events: none;
  opacity: clamp(0, calc(1 - var(--p, 1) * 2.6), 1);
}
.gate__hint span {
  display: block;
  margin-bottom: var(--s-2);
  animation: gateDown 2.4s ease-in-out infinite;
}
@keyframes gateDown {
  0%, 100% { transform: translateY(0); opacity: 0.5; }
  50% { transform: translateY(5px); opacity: 1; }
}

/* Затвор гасит свет, потом вспышка — тот пропущенный третий такт (§4) */
.gate__shutter,
.gate__flash {
  position: absolute;
  inset: 0;
  z-index: var(--z-overlay);
  pointer-events: none;
  opacity: 0;
}
.gate__shutter { background: var(--bg-void); }
.gate__flash { background: var(--ink); }

/* reduced-motion: разгона, снапа, проявления и FLIP нет — конечное состояние
   сразу, знак стоит в сцене, логотип шапки на месте (§5) */
body.gate-static .gate { height: auto; }
body.gate-static .gate__scene { position: relative; top: 0; }
body.gate-static .gate__hint { display: none; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-snap-type: none; }
  .gate__hint span { animation: none; }
}

/* Телефон: одна строка с карточкой В ПРОБЕЛЕ на 390px даёт кегль ~30px —
   знак перестаёт быть знаком. Строка остаётся одной (§1), но карточка встаёт
   ПОД неё: знак сверху во всю ширину, карточка ниже по центру. */
@media (max-width: 760px) {
  .gate {
    height: 175vh;
    --card-w: clamp(150px, min(48vw, 26vh), 230px);
  }
  .gate__mark {
    align-items: flex-start;
    column-gap: 0.14em;
    padding-top: 9vh;
  }
  .gate__stage {
    place-items: end center;
    padding-bottom: 12vh;
  }
  .gate__plate-l { font-size: var(--t-1); }
}

/* ── Новости: газета ────────────────────────────────────────────────────────
   Решение владельца 02.09.2026 (подшивка не нравилась и в макете). Разметку
   строит js/index-page/news.js. Лист бумаги — запечённая фактура
   assets/home/paper.webp (волокно, пятна, сгибы), лежит на столе под углом;
   первая полоса — обложка гравюрой (ч/б + полутон), заголовок, выжимка;
   справа — колонка заголовков с датами; клик переворачивает полосу вокруг
   корешка. Способ показа — свёрстанный текст, не панели (§10.4). */
.gazette {
  --enter: 1;
  --rev-sheet: calc(1 - clamp(0, calc(var(--enter, 1) / 0.6), 1));
  --rev-print: calc(1 - clamp(0, calc((var(--enter, 1) - 0.35) / 0.6), 1));
  position: relative;
  padding: var(--s-14) clamp(var(--s-4), 4vw, var(--s-14)) var(--s-20);
  /* конец градиента доведён до того же --bg-void, которым начинается сосед:
     стык секций перестаёт быть ступенькой */
  background: linear-gradient(180deg, var(--bg-void), var(--bg-base) 30%, var(--bg-base) 78%, var(--bg-void));
  perspective: 1400px;
}
.gazette__sheet {
  position: relative;
  width: min(1180px, 100%);
  margin-inline: auto;
  padding: var(--s-10) clamp(var(--s-5), 4vw, var(--s-14)) var(--s-8);
  color: var(--leaf-ink);
  background: url(../assets/home/paper.webp) center / cover no-repeat, var(--leaf);
  box-shadow: var(--sh-3);
  clip-path: var(--clip-leaf);
  transform: rotate(-0.6deg);
  transform-origin: 50% 0;
  /* Лист ЛОЖИТСЯ на стол ходом прокрутки: 80px и 2,4° против прежних 7,8px и
     1,4° — это было 0,6% высоты секции, то есть ничего. Покойный угол −0,6° и
     переворот полосы остаются в transform, вход идёт отдельными свойствами.
     Текст внутри читается при ЛЮБОМ застывшем значении. */
  translate: 0 calc(var(--rev-sheet) * var(--s-20));
  rotate: calc(var(--rev-sheet) * -2.4deg);
  scale: calc(1 - var(--rev-sheet) * 0.03);
}
.gazette__mast { translate: 0 calc(var(--rev-sheet) * var(--s-6) * -1); }
/* сгиб по середине листа: тень над сгибом, блик под ним */
.gazette__fold {
  position: absolute;
  left: 0;
  right: 0;
  top: 52%;
  height: 28px;
  transform: translateY(-50%);
  background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.09) 46%, rgba(0, 0, 0, 0.16) 50%, rgba(255, 255, 255, 0.14) 53%, transparent);
  pointer-events: none;
  /* Свет на сгибе — фактура (aria-hidden): один из двух слоёв секции, которым
     прозрачность разрешена. Проступает, когда лист лёг. */
  opacity: calc(1 - var(--rev-print));
}
.gazette__mast {
  position: relative;
  text-align: center;
  padding-bottom: var(--s-4);
  border-bottom: 3px double var(--leaf-ink);
  margin-bottom: var(--s-8);
}
/* «уши» шапки: номер свежей записи слева, её дата справа — настоящие данные */
.gazette__ears {
  display: flex;
  justify-content: space-between;
  gap: var(--s-4);
  padding-bottom: var(--s-2);
  margin-bottom: var(--s-3);
  border-bottom: 1px solid var(--leaf-ink);
}
.gazette__ear {
  font-family: var(--font-display);
  font-variant-numeric: tabular-nums;
  font-size: var(--t--1);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--leaf-ink-2);
}
/* орнаментная линейка под названием: тонкая линия с ромбом по центру */
.gazette__rule {
  position: relative;
  display: block;
  height: 1px;
  margin: var(--s-3) auto 0;
  width: 60%;
  background: var(--leaf-ink);
}
.gazette__rule::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 8px;
  height: 8px;
  transform: translate(-50%, -50%) rotate(45deg);
  background: var(--leaf);
  border: 1px solid var(--leaf-ink);
}
/* «слепой» набор: псевдослова, нечитаемые по замыслу; текстура полосы вокруг
   настоящих новостей — не выделяются, для читалок скрыты */
/* «слепой» набор — запечённая плитка (шестой раунд): псевдослова живым текстом
   читались даже мелкими и в расфокусе — глаз пытался понять смысл. Плитка
   assets/home/greek.webp (scripts/homepage-art.mjs greek из того же greekText):
   абзацы, подзаголовки, буквы неразличимы, ритм набора есть */
.gazette__greek {
  background: url(../assets/home/greek.webp) 0 0 / 300px auto repeat;
  opacity: calc(0.8 - var(--rev-print) * 0.8);
  pointer-events: none;
  user-select: none;
  min-height: 200px;
}
.gazette__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--w-bold);
  font-size: var(--t-6);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-tight);
  color: var(--leaf-ink);
}
.gazette__body {
  display: grid;
  grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr);
  gap: var(--s-10);
  align-items: start;
}
.gazette__body--single { grid-template-columns: minmax(0, 1fr); }
/* первая полоса: страница поворачивается вокруг корешка (левый край) */
.gazette__lead { perspective: 1200px; }
.gazette__page {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-6);
  transform-origin: 0 50%;
  transition: transform var(--dur-2) var(--ease-in-out), opacity var(--dur-2) ease;
}
.gazette__page.is-turning { transform: rotateY(-88deg); opacity: 0.2; }
/* обложка — гравюра: ч/б кадр в паспарту из чернил, поверх — полутон */
.gazette__fig {
  position: relative;
  margin: 0;
  overflow: hidden;
  background: var(--leaf-ink);
  padding: var(--s-2);
  box-shadow: var(--sh-1);
}
.gazette__img {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  object-position: 50% 18%;
  filter: grayscale(1) contrast(1.12) sepia(0.45) brightness(0.92);
}
.gazette__fig--wide .gazette__img { object-position: 50% 44%; }
.gazette__halftone {
  position: absolute;
  inset: var(--s-2);
  pointer-events: none;
  background: radial-gradient(circle, rgba(0, 0, 0, 0.42) 0.7px, transparent 1.1px) 0 0 / 4px 4px;
  mix-blend-mode: multiply;
}
/* без обложки: не чернильная плашка, а оттиск печати прямо на бумаге в тонкой рамке */
.gazette__fig--seal {
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 7;
  padding: 0;
  background: transparent;
  border: 1px solid var(--leaf-line);
  box-shadow: none;
  color: var(--leaf-ink-2);
}
.gazette__seal { display: grid; place-items: center; width: 100%; }
.gazette__seal svg { width: min(22%, 120px); height: auto; fill: currentColor; opacity: 0.55; }
.gazette__fig--seal .gazette__cap { left: 0; bottom: 0; }
.gazette__cap {
  position: absolute;
  left: var(--s-2);
  bottom: var(--s-2);
  padding: var(--s-1) var(--s-3);
  font-size: var(--t--1);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--leaf);
  background: var(--leaf-ink);
}
.gazette__head {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--w-bold);
  font-size: var(--t-5);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-tight);
  color: var(--leaf-ink);
  text-wrap: balance;
}
.gazette__head a { --link-ink: var(--leaf-ink); --link-ink-hover: var(--leaf-ink); color: var(--leaf-ink); text-decoration: none; }
.gazette__deck {
  margin: var(--s-4) 0 0;
  max-width: var(--measure);
  font-size: var(--t-1);
  line-height: var(--lh-body);
  color: var(--leaf-ink-2);
}
.gazette__foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-4);
  margin: var(--s-5) 0 0;
  padding-top: var(--s-3);
  border-top: 1px solid var(--leaf-line);
}
.gazette__no {
  font-family: var(--font-display);
  font-variant-numeric: tabular-nums;
  font-size: var(--t--1);
  letter-spacing: var(--ls-label);
  color: var(--leaf-ink-3);
}
.gazette__more {
  --link-ink: var(--leaf-ink);
  --link-ink-hover: var(--leaf-ink);
  margin-left: auto;
  font-family: var(--font-body);
  font-weight: var(--w-semi);
  font-size: var(--t-0);
  color: var(--leaf-ink);
  text-decoration: none;
  border-bottom: 1px solid var(--leaf-ink);
}
/* колонка заголовков: дата и заголовок в одну строку, активный — жирнее */
.gazette__index { border-left: 1px solid var(--leaf-line); padding-left: var(--s-6); }
.gazette__list { margin: 0; padding: 0; list-style: none; }
.gazette__list li + li { border-top: 1px solid var(--leaf-line); }
.gazette__item {
  display: grid;
  grid-template-columns: 5ch minmax(0, 1fr);
  gap: var(--s-3);
  width: 100%;
  min-height: var(--tap-min);
  padding: var(--s-3) 0;
  margin: 0;
  background: transparent;
  border: 0;
  text-align: left;
  color: var(--leaf-ink);
  cursor: pointer;
}
.gazette__item:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.gazette__date {
  font-family: var(--font-body);
  font-size: var(--t--1);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--leaf-ink-3);
  padding-top: var(--s-1);
}
.gazette__item-t {
  font-family: var(--font-display);
  font-size: var(--t-2);
  line-height: var(--lh-snug);
  color: var(--leaf-ink-2);
}
.gazette__item[aria-pressed="true"] .gazette__item-t { color: var(--leaf-ink); font-weight: var(--w-bold); }
.gazette__item[aria-pressed="true"] .gazette__date { color: var(--leaf-ink); }
@media (hover: hover) {
  .gazette__item:hover .gazette__item-t { color: var(--leaf-ink); }
}
.gazette__end {
  margin-top: var(--s-8);
  padding-top: var(--s-4);
  border-top: 3px double var(--leaf-ink);
  text-align: right;
}
.gazette__all {
  --link-ink: var(--leaf-ink);
  --link-ink-hover: var(--leaf-ink);
  font-family: var(--font-body);
  font-weight: var(--w-semi);
  font-size: var(--t-0);
  color: var(--leaf-ink);
  text-decoration: none;
  border-bottom: 1px solid var(--leaf-ink);
}
@media (prefers-reduced-motion: reduce) {
  .gazette__page.is-turning { transform: none; opacity: 1; }
}
@media (max-width: 860px) {
  .gazette { padding: var(--s-10) var(--s-3) var(--s-14); }
  .gazette__sheet { transform: none; padding: var(--s-6) var(--s-4); }
  /* на телефоне наклона нет: вход остаётся только сдвигом и масштабом */
  .gazette__sheet { rotate: 0deg; }
  .gazette__greek--under { columns: 2; }
  .gazette__greek--aside { display: none; }
  .gazette__title { font-size: var(--t-4); }
  .gazette__body { grid-template-columns: minmax(0, 1fr); gap: var(--s-6); }
  .gazette__index { border-left: 0; padding-left: 0; border-top: 1px solid var(--leaf-line); padding-top: var(--s-2); }
  .gazette__head { font-size: var(--t-3); }
}

/* ── Трио: три полотна на трёх героинь ─────────────────────────────────────
   ТЗ §8. Разметку строит js/index-page/trio.js. Фон полотна клипуется
   диагональю (в .pan__bg), фигура, карточка и ярлык — вне клипа, поэтому
   фигура выходит за границу, а фон читается тем, что ЗА героиней (§8.2).
   Смещение диагонали --dx в px: угол один на всех ширинах. Граница — лоза с
   терном (vine.js), не линия (§8.3). Палитра полотна — из реестра карточки
   (--tint/--deep/--accent ставит скрипт), новых цветов у секции нет.
   Свёрнутые полотна НЕ гасятся фильтром (§9.13, «арт не гасить»): они и так
   пустые бланки, а два filter на слоях 100vw×100vh — самая дорогая строка
   прототипа. */
.trio {
  --enter: 1;
  --pass: 0;
  /* три перекрывающихся такта входа: среда → фигура → предмет. Перекрытие
     нужно, чтобы секция читалась одним появлением, а не тремя анимациями */
  --rev-env: calc(1 - clamp(0, calc(var(--enter, 1) / 0.55), 1));
  --rev-fig: calc(1 - clamp(0, calc((var(--enter, 1) - 0.12) / 0.62), 1));
  --rev-obj: calc(1 - clamp(0, calc((var(--enter, 1) - 0.32) / 0.6), 1));
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background: var(--bg-void);
}
.trio__stage {
  position: relative;
  display: flex;
  align-items: stretch;
  height: clamp(520px, 78vh, 760px);
}
.pan {
  position: relative;
  flex: 0 0 calc(var(--w, 17%) + var(--dx));
  min-width: 0;
  z-index: var(--z, 1);
  transition: flex-basis var(--dur-3) var(--ease-out);
}
.pan:first-child { flex-basis: var(--w, 17%); }
/* соседнее полотно уходит ПОД диагональ предыдущего — щелей между ними нет */
.pan + .pan { margin-left: calc(-1 * var(--dx)); }

.pan__bg {
  position: absolute;
  inset: 0;
  overflow: hidden;
  clip-path: polygon(0 0, 100% 0, calc(100% - var(--dx)) 100%, 0 100%);
}
.pan:last-child .pan__bg { clip-path: none; }
.pan__env {
  position: absolute;
  inset: 0;
}
/* свёрнутое полотно чуть притемнено ПОД корешок — не фильтром, а вуалью */
.pan:not(.is-open) .pan__bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--scrim-clear);
}

/* Полотно — ГРАФИКА в палитре героини, не сцена (шестой раунд, по ZZZ): глубокий
   тон (--deep/--tint ставит скрипт из реестра карточки), диагональные плашки
   .pan__bands, гигантское имя контуром .pan__big за спиной, плоский трафарет
   стихии .pan__stencil одним тоном (маска — assets/home/stencil-*.webp из
   силуэтов крон, перьев, колец воды). Фигура врезана поверх — никакой аппликации. */
.pan__env {
  background:
    radial-gradient(70% 62% at var(--fan-x, 34%) 78%, color-mix(in srgb, var(--tint) 30%, var(--bg-void)), transparent 70%),
    linear-gradient(180deg, var(--bg-void), color-mix(in srgb, var(--deep) 55%, var(--bg-void)) 60%, color-mix(in srgb, var(--deep) 70%, var(--bg-void)));
}
.pan--plain .pan__env { background: linear-gradient(168deg, var(--wall), var(--wall-deep) 70%, var(--bg-void)); }
.pan__name { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.pan__bands {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(112deg, transparent 0 41%, var(--accent) 41% 41.6%, transparent 41.6% 100%),
    linear-gradient(112deg, transparent 0 44%, color-mix(in srgb, var(--tint) 62%, var(--bg-void)) 44% 100%),
    linear-gradient(292deg, transparent 0 78%, color-mix(in srgb, var(--deep) 80%, var(--bg-void)) 78% 100%);
  opacity: 0.9;
  transition: opacity var(--dur-3) ease, transform var(--dur-3) var(--ease-out);
}
.pan__big {
  position: absolute;
  left: -2%;
  top: 4%;
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--w-bold);
  font-size: clamp(120px, 24vw, 360px);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-tight);
  white-space: nowrap;
  color: transparent;
  -webkit-text-stroke: 1.5px color-mix(in srgb, var(--accent) 55%, transparent);
  pointer-events: none;
  user-select: none;
  transition: opacity var(--dur-3) ease, transform var(--dur-3) var(--ease-out);
  transition-delay: var(--dur-1);
}
.pan__stencil {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--tint) 70%, var(--accent));
  opacity: 0.28;
  -webkit-mask: var(--stencil) var(--fan-x, 34%) 100% / clamp(560px, 118%, 1500px) auto no-repeat;
  mask: var(--stencil) var(--fan-x, 34%) 100% / clamp(560px, 118%, 1500px) auto no-repeat;
  pointer-events: none;
  transition: opacity var(--dur-3) ease, transform var(--dur-3) var(--ease-out);
  transition-delay: var(--dur-1);
}
/* Среда полотна приходит из глубины, а с проходом секции дрейфует навстречу
   прокрутке: ниже кадра сдвинута, у позиции чтения на месте, при уходе вверх
   продолжает движение. Это и делает стык не стыком. */
.pan__env,
.pan__stencil {
  translate: 0 calc(var(--rev-env) * var(--s-20) + var(--pass, 0) * var(--s-10));
  scale: calc(1 + var(--rev-env) * 0.06);
}
.pan__bands { translate: calc(var(--rev-env) * var(--s-20) * -1) 0; }
/* Имя контуром — только сдвиг. Прозрачностью на входе НЕ гасится: у раскрытого
   полотна это единственное видимое имя (h2 — sr-only, корешок схлопнут). */
.pan__big { translate: calc(var(--rev-obj) * var(--s-20) * -1) 0; }
/* Фигура встаёт из темноты под кромкой: у .trio overflow: hidden, и ход в 120px
   честно уходит за срез. Прежние 32px — это 4,6% высоты секции, ниже порога
   различимости (замер 04.09.2026: владелец не видел появления вовсе). */
.pan.is-open .pan__figure {
  translate: 0 calc(var(--rev-fig) * (var(--s-20) + var(--s-10)));
  scale: calc(1 + var(--rev-fig) * 0.03);
}
/* Стык с газетой: фигура — сиблинг ВНЕ клипа полотна (иначе рамка содержала бы
   героиню), поэтому маска .pan__bg её не берёт, и кромка секции резала арт
   насухо — замер давал скачок яркости в шесть раз. Низ арта растворяется в тот
   же --bg-void, которым начинается сосед; гасится ТОЛЬКО картинка, карточка и
   корешок не задеты. Слоем поверх секции это не решается: у .pan есть z-index,
   и слой секции красился бы под полотнами. */
.pan__figure {
  -webkit-mask: linear-gradient(180deg, #000 calc(100% - var(--s-20)), transparent);
  mask: linear-gradient(180deg, #000 calc(100% - var(--s-20)), transparent);
}
/* Карточка — ССЫЛКА: только сдвиг и наклон, читаема и нажимаема при любом
   застывшем значении. Прозрачность здесь запрещена законом. */
.pan.is-open .pan__card {
  translate: 0 calc(var(--rev-obj) * var(--s-20));
  rotate: calc(var(--rev-obj) * -8deg);
}
.pan.is-open .pan__soon { translate: 0 calc(var(--rev-obj) * var(--s-14)); }
/* Корешки садятся по одному: окно такта смещено на свой номер. Сдвиг мелкий —
   это КНОПКА. */
.pan__spine { translate: 0 calc((1 - clamp(0, calc((var(--enter, 1) - var(--i, 0) * 0.08) / 0.55), 1)) * var(--s-10)); }
/* Лоза прорастает вдоль границы полотен. scale — отдельное свойство, покойный
   transform лозы (сдвиг и поворот по геометрии) не задет. */
.pan__edge { scale: 1 calc(0.55 + 0.45 * clamp(0, calc((var(--enter, 1) - 0.1) / 0.6), 1)); }
/* Граница секции: среда растворяется в тот же --bg-void, которым начинается
   сосед, — жёсткий срез полноширинного арта был скачком яркости в 12 раз.
   Маска стоит на .pan__bg, поэтому фигура, карточка и корешок (они сиблинги
   вне клипа) не задеты: текст защищён построением, а не подбором полосы. */
.pan__bg {
  -webkit-mask: linear-gradient(180deg, transparent 0, #000 var(--s-20), #000 calc(100% - var(--s-20)), transparent 100%);
  mask: linear-gradient(180deg, transparent 0, #000 var(--s-20), #000 calc(100% - var(--s-20)), transparent 100%);
}
.pan--forest { --stencil: url(../assets/home/stencil-forest.webp); }
.pan--road { --stencil: url(../assets/home/stencil-road.webp); }
.pan--water { --stencil: url(../assets/home/stencil-water.webp); }
.pan--blank { --fan-x: 50%; }
/* Ось РАСКРЫТИЯ (а не входа): у свёрнутого полотна имя стоит на корешке, а
   трафарет стихии принадлежит раскрытому. Вход этих слоёв теперь идёт от
   прокрутки отдельными свойствами и прозрачностью не гасится. */
.pan:not(.is-open) .pan__big,
.pan:not(.is-open) .pan__stencil { opacity: 0; transform: translateY(24px); }
.pan:not(.is-open) .pan__bands { opacity: 0.35; }

/* Лоза по диагонали: длина и угол — от реальной высоты полотна (ставит скрипт) */
.pan__edge {
  position: absolute;
  z-index: var(--z-sticky);
  top: 50%;
  left: calc(100% - var(--dx) / 2 - 4px);
  width: 34px;
  height: var(--edge-len, 120%);
  transform: translate(-50%, -50%) rotate(var(--edge-rot, 8deg));
  pointer-events: none;
  filter: drop-shadow(-3px 4px 5px rgba(0, 0, 0, 0.75));
}
.pan__edge .vine { display: block; width: 100%; height: 100%; }

/* Фигура вне клипа: 78% высоты (§8.5 — число под финальный фон) */
.pan__figure {
  position: absolute;
  z-index: var(--z-sticky);
  left: 8%;
  bottom: 0;
  height: 78%;
  width: auto;
  max-width: none;
  pointer-events: none;
  filter: drop-shadow(0 26px 34px rgba(0, 0, 0, 0.7));
  transition: opacity var(--dur-3) ease, visibility var(--dur-3);
}
/* Карточка героини стоит рядом с фигурой; у героини без арта — бланк её имени */
.pan__card {
  position: absolute;
  z-index: var(--z-sticky);
  left: 50%;
  bottom: 12%;
  width: clamp(150px, 13vw, 210px);
  /* translateY(0) объявлен явно: интерполяция идёт по ФУНКЦИЯМ, а не по матрице —
     иначе поворот и сдвиг склеиваются в одно матричное движение и карточка
     проезжает по дуге. */
  transform: rotate(-2deg) translateY(0);
  text-decoration: none;
  filter: drop-shadow(0 30px 40px rgba(0, 0, 0, 0.6));
  transition: opacity var(--dur-3) ease, visibility var(--dur-3);
}
.pan__card .wc { max-width: none; }
.pan__card:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.pan__soon {
  position: absolute;
  z-index: var(--z-sticky);
  left: 50%;
  bottom: 6%;
  width: clamp(200px, 26vw, 360px);
  transform: translateX(-50%) translateY(0);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-2);
  pointer-events: none;
  transition: opacity var(--dur-3) ease, visibility var(--dur-3);
}
.pan__soon .soon { display: block; width: 100%; height: auto; filter: drop-shadow(0 24px 34px rgba(0, 0, 0, 0.6)); }
.pan__soon-l {
  font-family: var(--font-display);
  font-size: var(--t-3);
  letter-spacing: var(--ls-tight);
  color: var(--lamp-2);
}
/* Свёрнутое полотно: фигура, карточка и силуэт спрятаны, остаётся корешок.
   ⚠️ ПРАВИЛО ОБЯЗАНО ЗАКРЫВАТЬСЯ СВОИМ БЛОКОМ. До 03.09.2026 этот список
   селекторов не был закрыт и слился с .pan__spine ниже: фигура и карточка
   получали на свёрнутом полотне inset: 0, display: flex и cursor: pointer со
   специфичностью выше собственных left/bottom, а стартового состояния (то, из
   которого они появляются) у них не было вовсе — в появлении секции они не
   участвовали. Гейт tests/regression/front-index-reveal.test.mjs стережёт
   именно этот класс дефекта.
   ⚠️ visibility, а не только opacity: .pan__card — это ссылка, и с одной
   прозрачностью она остаётся невидимой целью в порядке обхода Tab. */
.pan:not(.is-open) .pan__figure,
.pan:not(.is-open) .pan__card,
.pan:not(.is-open) .pan__soon { opacity: 0; visibility: hidden; transition-delay: 0s; }


/* Корешок — «неотснятый бланк»: номер, клеймо с прочерком, имя вертикально.
   Вся площадь свёрнутого полотна — кнопка. */
.pan__spine {
  position: absolute;
  inset: 0;
  z-index: var(--z-dropdown);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s-4);
  padding: 0;
  background: transparent;
  border: 0;
  color: var(--lamp-2);
  cursor: pointer;
  /* каскад по --i теперь идёт от прокрутки (см. .pan__spine translate выше),
     а не от времени: время появления заменено ходом */
}
.pan__spine[hidden] { display: none; }
.pan__spine:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.pan__spine-no {
  font-family: var(--font-display);
  font-size: var(--t--1);
  color: var(--accent-text);
}
.pan__spine-seal {
  display: grid;
  place-items: center;
  width: 34px;
  height: 46px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-1);
  background: repeating-linear-gradient(122deg, var(--line-subtle) 0 2px, transparent 2px 8px);
  font-family: var(--font-display);
  font-size: var(--t-1);
  color: var(--lamp-3);
}
.pan__spine-name {
  writing-mode: vertical-rl;
  font-family: var(--font-display);
  font-size: var(--t-3);
  letter-spacing: var(--ls-label);
  color: var(--lamp);
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.8);
}
.pan__spine-st {
  writing-mode: vertical-rl;
  font-family: var(--font-body);
  font-size: var(--t--1);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--lamp-2);
}
@media (hover: hover) {
  .pan__spine:hover .pan__spine-seal { border-color: var(--accent-text); color: var(--accent-text); }
  .pan__spine:hover .pan__spine-st { color: var(--lamp-2); }
}

/* ── Мобильный: та же конструкция, повёрнутая на 90° — полосы со скошенным
   нижним краем, корешки — строки (прецедент деградации диагонали в CSS —
   Metaphor; «сохранить диагональ» на 390px не удаётся никому). */
@media (max-width: 860px) {
  .trio__stage { flex-direction: column; height: auto; }
  .pan {
    flex: 0 0 auto;
    height: 64px;
    transition: height var(--dur-3) var(--ease-out);
  }
  .pan:first-child { flex-basis: auto; }
  .pan + .pan { margin-left: 0; margin-top: calc(-1 * var(--dy)); }
  .pan.is-open { height: 62vh; min-height: 420px; }
  .pan__bg { clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--dy)), 0 100%); }
  .pan:last-child .pan__bg { clip-path: none; }
  .pan__edge {
    top: auto;
    left: 50%;
    bottom: calc(var(--dy) / 2);
    width: 30px;
    height: var(--edge-len, 120vw);
    transform: translate(-50%, 50%) rotate(var(--edge-rot, -88deg));
  }
  /* фигура целиком в кадре: на 390px при right:-8% и 56% высоты Мила резалась почти пополам */
  .pan__figure { left: auto; right: 1%; height: 50%; }
  .pan--shot { --fan-x: 68%; }
  .pan__big { font-size: clamp(96px, 34vw, 200px); top: 2%; }
  .pan__stencil { -webkit-mask-size: clamp(420px, 150%, 900px) auto; mask-size: clamp(420px, 150%, 900px) auto; }
  .pan__card { left: 6%; bottom: 10%; width: clamp(120px, 34vw, 160px); }
  .pan__soon { width: clamp(140px, 42vw, 200px); bottom: 4%; }
  /* скошенный край предыдущей полосы заходит на 28px — строка корешка ниже него */
  .pan__spine { flex-direction: row; justify-content: flex-start; gap: var(--s-3); padding-left: var(--s-5); padding-top: var(--dy); }
  .pan { height: calc(64px + var(--dy)); }
  .pan__spine-seal { width: 26px; height: 34px; font-size: var(--t-0); }
  .pan__spine-name,
  .pan__spine-st { writing-mode: horizontal-tb; }
  .pan__spine-st { margin-left: auto; padding-right: var(--s-5); }
}

/* ── Связанное творчество: барабан ─────────────────────────────────────────
   ТЗ §11.7. Разметку строит js/index-page/works.js. Плоский диск радиуса ≈
   высоте секции, центр за левым краем по середине; в кадр входит дуга обода с
   насечками (repeating-conic-gradient — один paint, без DOM). На клине лежит
   лист-экспонат вида (нечитаемый набор), над окном — лупа, в стекле которой
   читаемая строка: героиня и число её работ; крупно справа — предмет вида.
   Один композитный слой на диск: у детей нет своих анимируемых свойств.
   Геометрию (--R, --cx) ставит скрипт от реальной высоты колодца. */
.drum {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  padding: var(--s-14) 0;
  background: var(--bg-void);
  transition: background var(--dur-detent) ease;
  --dur-detent: 850ms;
  --enter: 1;
  --pass: 0;
  --rev-room: calc(1 - clamp(0, calc(var(--enter, 1) / 0.7), 1));
  --rev-disk: calc(1 - clamp(0, calc((var(--enter, 1) - 0.08) / 0.72), 1));
  --rev-text: calc(1 - clamp(0, calc((var(--enter, 1) - 0.3) / 0.6), 1));
}
.drum__stage {
  position: relative;
  height: clamp(420px, 60vh, 620px);
}
.drum__well {
  position: absolute;
  inset: 0;
  overflow: hidden;
}
.drum__disk {
  position: absolute;
  left: calc(var(--cx, 0px) - var(--R, 500px));
  top: calc(50% - var(--R, 500px));
  width: calc(var(--R, 500px) * 2);
  height: calc(var(--R, 500px) * 2);
  border-radius: var(--r-pill);
  /* лицо: клинья видов (conic из скрипта) + концентрические кольца точёного
     дерева — фактура вместо плоской заливки; ось за кадром */
  background:
    var(--wood-grain, none) center / 700px 700px,
    repeating-radial-gradient(circle at center, transparent 0 27px, color-mix(in srgb, var(--lamp) 3%, transparent) 27px 28px),
    var(--wood-deep);
  box-shadow: var(--sh-4);
  /* Вход секции — ВТОРОЕ звено, а не посев в --rot: первым владеет works.js и
     applyRotation() пишет его на инициализации, ещё до наблюдателя, — посеянное
     значение он затёр бы молча. Угол заметно меньше половины шага (при трёх
     видах шаг 120°), поэтому вид у окна не подменяется. */
  transform: rotate(var(--rot, 0deg));
  /* Диск ВКАТЫВАЕТСЯ прокруткой: 18° это около 170px по ободу при радиусе в
     высоту секции — самое читаемое движение страницы. Вход — ОТДЕЛЬНОЕ свойство
     rotate; защёлка --rot (850 мс) осталась в transform, и works.js о входе
     по-прежнему не знает ничего. */
  rotate: calc(var(--rev-disk) * -18deg);
  transition: transform var(--dur-detent) var(--ease-settle);
  will-change: transform, rotate;
}
.drum.is-dragging .drum__disk { transition: none; }
/* обод: латунь + насечки (мелкая каждые 3,75°, крупная каждые 15°), кольцом
   по краю диска — край слоя всегда накрыт металлом */
.drum__disk::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--r-pill);
  background:
    repeating-conic-gradient(from -0.3deg, var(--lamp) 0 0.6deg, transparent 0.6deg 15deg),
    repeating-conic-gradient(from -0.14deg, var(--brass-hi) 0 0.28deg, transparent 0.28deg 3.75deg),
    conic-gradient(from 0deg, var(--brass-lo), var(--brass) 30%, var(--brass-hi) 50%, var(--brass) 70%, var(--brass-lo));
  -webkit-mask: radial-gradient(circle closest-side, transparent calc(100% - 30px), #000 calc(100% - 29px), #000 calc(100% - 2px), transparent 100%);
  mask: radial-gradient(circle closest-side, transparent calc(100% - 30px), #000 calc(100% - 29px), #000 calc(100% - 2px), transparent 100%);
}
/* границы секторов — радиальные латунные линии от центра */
.drum__disk::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--r-pill);
  background: repeating-conic-gradient(from calc(var(--step, 120deg) / -2 - 0.35deg), color-mix(in srgb, var(--brass) 70%, transparent) 0 0.7deg, transparent 0.7deg var(--step, 120deg));
  -webkit-mask: radial-gradient(circle closest-side, transparent 10%, #000 11%, #000 calc(100% - 31px), transparent calc(100% - 30px));
  mask: radial-gradient(circle closest-side, transparent 10%, #000 11%, #000 calc(100% - 31px), transparent calc(100% - 30px));
}
.drum--empty .drum__disk::after { display: none; }
/* лист-экспонат вида лежит на клине и едет с диском; в окне («3 часа») стоит
   прямо. Ширина — доля R (EXH_W в works.js), лист 900×1200 */
.drum__exhibit {
  position: absolute;
  left: 50%;
  top: 50%;
  width: calc(var(--R, 500px) * 0.36);
  height: auto;
  max-width: none;
  /* угол сектора — вокруг центра ДИСКА: начало координат элемента стоит в центре
     диска, origin 0 0; со штатным origin 50% 50% лист с углом ≠ 0 уезжал по
     чужой орбите (виден был на клине фанфиков в верхнем левом углу) */
  transform-origin: 0 0;
  transform: rotate(var(--a, 0deg)) translateX(calc(var(--R, 500px) * 0.7)) translate(-50%, -50%);
  box-shadow: var(--sh-3);
  pointer-events: none;
}
/* неподвижное освещение обода (блик сверху, тень снизу) — не крутится с диском */
.drum__shade {
  position: absolute;
  left: calc(var(--cx, 0px) - var(--R, 500px));
  top: calc(50% - var(--R, 500px));
  width: calc(var(--R, 500px) * 2);
  height: calc(var(--R, 500px) * 2);
  border-radius: var(--r-pill);
  pointer-events: none;
  background: linear-gradient(180deg, color-mix(in srgb, var(--lamp) 24%, transparent), transparent 38%, transparent 62%, color-mix(in srgb, var(--bg-void) 66%, transparent));
  -webkit-mask: radial-gradient(circle closest-side, transparent calc(100% - 30px), #000 calc(100% - 29px));
  mask: radial-gradient(circle closest-side, transparent calc(100% - 30px), #000 calc(100% - 29px));
}
/* тень от обода внутрь диска */
.drum__well::after {
  content: "";
  position: absolute;
  left: calc(var(--cx, 0px) - var(--R, 500px));
  top: calc(50% - var(--R, 500px));
  width: calc(var(--R, 500px) * 2);
  height: calc(var(--R, 500px) * 2);
  border-radius: var(--r-pill);
  pointer-events: none;
  background: radial-gradient(circle closest-side, transparent calc(100% - 70px), color-mix(in srgb, var(--bg-void) 55%, transparent) calc(100% - 31px), transparent calc(100% - 30px));
}
/* фиксатор — неподвижная риска снаружи обода в точке окна */
.drum__pointer {
  position: absolute;
  left: calc(var(--cx, 0px) + var(--R, 500px) + 9px);
  top: 50%;
  width: 14px;
  height: 14px;
  transform: translateY(-50%) rotate(45deg) scale(1);
  /* след контакта: латунь дозащёлкивается вместе с приходом диска */
  scale: calc(1 - var(--rev-disk) * 0.4);
  background: linear-gradient(135deg, var(--brass-hi), var(--brass-lo));
  box-shadow: var(--sh-plate);
}
/* зона захвата — видимая часть диска; на касании вертикальный жест уходит
   странице (pan-y), крутят тапом по половинам обода и кнопками */
.drum__grip {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: calc(var(--cx, 0px) + var(--R, 500px) + 24px);
  cursor: grab;
  touch-action: pan-y;
}
.drum.is-dragging .drum__grip { cursor: grabbing; }
.drum--empty .drum__grip { cursor: default; }


/* рамка у окна: вид, счётчики, дата последней */
.drum__frame {
  position: absolute;
  z-index: var(--z-sticky);
  left: calc(var(--cx, 0px) + var(--R, 500px) + 44px);
  top: 50%;
  width: min(400px, 36%);
  transform: translateY(-50%);
  transition: opacity var(--dur-2) ease, transform var(--dur-2) ease;
}
/* Рамка вида и колонна — текст и действие: только мелкий сдвиг, читаемо при
   любом застывшем значении. */
.drum__frame { translate: 0 calc(var(--rev-text) * var(--s-10)); }
.drum__frame.is-turning { opacity: 0; transform: translateY(-50%) translateY(8px); }
.drum__kind {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--w-normal);
  font-size: var(--t-4);
  line-height: var(--lh-tight);
  color: var(--lamp);
}
.drum__count {
  margin: var(--s-2) 0 0;
  font-size: var(--t-0);
  color: var(--lamp-2);
}
.drum__date {
  margin: var(--s-2) 0 0;
  font-size: var(--t--1);
  color: var(--lamp-3);
}
.drum__ctrl {
  position: absolute;
  z-index: var(--z-sticky);
  left: calc(var(--cx, 0px) + var(--R, 500px) + 44px);
  bottom: var(--s-4);
  display: flex;
  gap: var(--s-2);
}
.drum__btn {
  display: grid;
  place-items: center;
  width: var(--tap-min);
  height: var(--tap-min);
  padding: 0;
  background: var(--bg-raised);
  border: 1px solid var(--accent-line);
  border-radius: var(--r-pill);
  color: var(--accent-text);
  cursor: pointer;
}
.drum__btn svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; }
.drum__btn:hover { background: var(--accent-1); }
.drum__btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
/* комната вида — фон всей секции (пятый раунд): dim снизу, lit сверху дышит.
   Ритм свой у каждой комнаты: накал лампы дрожит неровно (разборы), свеча
   дышит плавно (фанфики), лампа на кронштейне почти ровная (комиксы) */
.drum__room {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background: var(--bg-void);
}
/* Перетекание комнат: слои лежат друг на друге, меняется только прозрачность.
   420 мс и --ease-out вместо прежних 900 мс с кривой по умолчанию: фон — самая
   большая вещь на экране, и если он меняется позже щелчка, вся секция читается
   заторможенной. Теперь он успевает раньше, чем диск доедет до защёлки. */
.drum__room { opacity: 0; transition: opacity var(--dur-3) var(--ease-out); }
.drum__room.is-current { opacity: 1; }
/* поздняя картинка приходит проявлением, а не рывком: класс ВООРУЖАЕМЫЙ, без JS
   его никто не ставит и комната видна сразу */
.drum__room-img { transition: opacity var(--dur-2) linear; }
.drum__room-img.is-load { opacity: 0; }
.drum__room-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 62% 50%;
  /* Комната подъезжает раскрытием масштаба и дрейфует, пока секция в кадре.
     ⚠️ Базовый масштаб 1,12 обязателен: при object-fit: cover дрейф в 24px
     обнажил бы кромку картинки, а запас её кроет. Прозрачность комнаты НЕ наша:
     ею владеет перетекание is-current. */
  translate: 0 calc(var(--pass, 0) * var(--s-6));
  scale: calc(1.12 + var(--rev-room) * 0.06);
}
/* Смена комнаты — ПЕРЕЗАПУСК цикла, а не возобновление: animation-play-state
   продолжал его с того места, где застали, и комната могла включиться прямо
   посреди вспышки. Смена animation-name начинает с покойного уровня. */
.drum__room-lit { animation: none; }
.drum__room.is-current .drum__room-lit { animation: roomLamp 7s steps(1, end) infinite; }
.drum__room.is-current[data-kind="fanfic"] .drum__room-lit { animation: roomCandle 5.2s ease-in-out infinite; }
.drum__room.is-current[data-kind="comic"] .drum__room-lit { animation: roomClamp 9s steps(1, end) infinite; }
/* Мигание = КОРОТКАЯ ВСПЫШКА вокруг постоянного уровня.
   ⚠️ Прежние кадры держали РАЗНЫЕ уровни по 1,6–2,4 с, и шаг steps(1, end)
   читался не миганием, а «фон резко сменился через полторы-две секунды» —
   ровно то, на что жаловался владелец 04.09.2026. Замер подтвердил: комната
   «Комиксы» на +2852 мс меняла яркость полноэкранной картинки с 0,86 на 0,60
   одним кадром. Теперь базовый уровень ОДИН по обе стороны каждой вспышки,
   вспышка около 90 мс, между вспышками яркость не меняется никогда. */
@keyframes roomLamp {
  0%, 33.4%, 35.6%, 61.2%, 63.4%, 100% { opacity: 0.7; }
  33.8%, 34.6% { opacity: 0.52; }
  35.2% { opacity: 0.88; }
  61.6%, 62.4% { opacity: 0.9; }
  63% { opacity: 0.56; }
}
@keyframes roomCandle {
  0%, 100% { opacity: 0.5; }
  30% { opacity: 0.85; }
  55% { opacity: 0.62; }
  75% { opacity: 0.92; }
}
@keyframes roomClamp {
  0%, 43.6%, 45.4%, 79.6%, 81%, 100% { opacity: 0.78; }
  44%, 44.6% { opacity: 0.6; }
  45% { opacity: 0.9; }
  80% { opacity: 0.66; }
}
/* пыль в луче: редкие точки плывут вверх, только в правой половине под лампой */
.drum__dust {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(circle, rgba(255, 235, 200, 0.55) 0.8px, transparent 1.6px) 0 0 / 190px 230px,
    radial-gradient(circle, rgba(255, 235, 200, 0.35) 0.7px, transparent 1.5px) 70px 120px / 260px 310px;
  -webkit-mask: radial-gradient(48% 70% at 66% 42%, #000 20%, transparent 100%);
  mask: radial-gradient(48% 70% at 66% 42%, #000 20%, transparent 100%);
  opacity: 0.5;
  animation: none;
}
.drum__room.is-current .drum__dust { animation: roomDust 46s linear infinite; }
@keyframes roomDust {
  from { background-position: 0 0, 70px 120px; }
  to { background-position: 40px -460px, 20px -500px; }
}
/* затемнение под диском и рамкой — текст читается на любой комнате */
.drum__scrim {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  /* Верх и низ доведены до полного --bg-void: жёсткий срез полноширинной
     комнаты давал у стыка скачок яркости почти втрое. */
  background:
    linear-gradient(90deg, color-mix(in srgb, var(--bg-void) 78%, transparent), color-mix(in srgb, var(--bg-void) 55%, transparent) 48%, transparent 70%),
    linear-gradient(180deg, var(--bg-void), color-mix(in srgb, var(--bg-void) 45%, transparent) var(--s-20), transparent 30%, transparent 70%, color-mix(in srgb, var(--bg-void) 70%, transparent) calc(100% - var(--s-20)), var(--bg-void));
}
.drum__stage { z-index: var(--z-base); }
/* правая колонна: заголовок и вход в архивы; лид убран (§11.4); предмет вида
   теперь стоит в комнате */
.drum__side {
  position: absolute;
  z-index: var(--z-sticky);
  right: clamp(var(--s-5), 5vw, var(--s-20));
  bottom: var(--s-6);
  max-width: 40%;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--s-5);
  text-align: right;
  translate: 0 calc(var(--rev-text) * var(--s-10));
}
.drum__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--w-normal);
  font-size: var(--t-5);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-tight);
  color: var(--ink);
  text-shadow: 0 2px 14px rgba(0, 0, 0, 0.8);
}

@media (prefers-reduced-motion: reduce) {
  .drum__disk { transition: none; }
  .drum__room-lit,
  .drum__dust { animation: none; }
  .drum__room { transition: none; }
  /* Оси прокрутки под reduced-motion не пишутся вовсе (scroll.js), нейтраль и
     есть конец — отменять нечего. */
  .drum__room.is-current .drum__room-lit,
  .drum__room.is-current .drum__dust { animation: none; }
}

@media (max-width: 860px) {
  .drum { padding: var(--s-10) 0; }
  .drum__stage { height: auto; display: grid; }
  .drum__well { position: relative; inset: auto; height: 300px; }
  .drum__grip { bottom: auto; height: 300px; }
  .drum__frame {
    position: relative;
    left: auto;
    top: auto;
    width: auto;
    transform: none;
    padding: var(--s-6) var(--s-5) 0;
  }

  .drum__frame.is-turning { transform: translateY(8px); }
  .drum__ctrl { position: relative; left: auto; bottom: auto; padding: var(--s-4) var(--s-5) 0; }
  .drum__side {
    position: relative;
    right: auto;
    bottom: auto;
    max-width: none;
    align-items: flex-start;
    text-align: left;
    padding: var(--s-6) var(--s-5) 0;
  }
  /* телефон: комната уходит вправо, чтобы предмет не стоял за диском и текстом; затемнение сильнее */
  .drum__room-img { object-position: 82% 40%; }
  .drum__scrim {
    background:
      linear-gradient(180deg, color-mix(in srgb, var(--bg-void) 70%, transparent), color-mix(in srgb, var(--bg-void) 45%, transparent) 40%, color-mix(in srgb, var(--bg-void) 80%, transparent));
  }
  .drum__title { font-size: var(--t-4); }
}

/* ── Статистика: перепись гильдии столбцами ────────────────────────────────
   ТЗ §12. Разметку строит js/index-page/census.js. Восемь столбцов G…S всегда;
   ноль — риска у основания, «0» служебным цветом (ноль как полноразмерный
   слот). Заливка — металл ранга (--plate-*), база — латунная кромка
   (--sh-brass-rule). «Ты» — аватар над своим столбцом, приезжает последним. */
.census {
  --enter: 1;
  --rev-base: calc(1 - clamp(0, calc(var(--enter, 1) / 0.42), 1));
  --rev-head: calc(1 - clamp(0, calc(var(--enter, 1) / 0.6), 1));
  position: relative;
  padding: var(--s-20) clamp(var(--s-5), 5vw, var(--s-20)) var(--s-14);
  background: linear-gradient(180deg, var(--bg-void), var(--bg-base) var(--s-20), var(--bg-base));
}
.census__head { max-width: var(--measure); margin-bottom: var(--s-10); translate: 0 calc(var(--rev-head) * var(--s-6)); }
.census__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--w-normal);
  font-size: var(--t-5);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-tight);
  color: var(--ink);
}
.census__leader {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin: 0 0 var(--s-6);
  font-size: var(--t-0);
  color: var(--accent-text);
}
.census__leader-ava { width: var(--s-8); height: var(--s-8); border-radius: var(--r-pill); object-fit: cover; box-shadow: var(--sh-plate); }
.ladder {
  /* Шаг каскада объявлен ЗДЕСЬ и только здесь: до 03.09.2026 «70ms» стояли и в
     .ladder__fill, и в census.js, и правка одного не сдвигала другое. Числа не с
     потолка: рейка основания чертится 220 мс на восемь колонок, то есть 27,5 мс
     на колонку — при шаге 30 мс столбец встаёт ровно ЗА проходящей под ним
     рейкой. Досчёт чисел спрашивает эти же тайминги у CSS через getComputedStyle. */
  --ladder-lead: 60ms;
  --ladder-step: 30ms;
  position: relative;
  display: grid;
  grid-template-columns: repeat(8, minmax(0, 1fr));
  gap: var(--s-3);
  align-items: end;
  height: clamp(220px, 34vh, 360px);
  margin: 0;
  padding: var(--s-14) 0 0;
  list-style: none;
}
.ladder__col {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  height: 100%;
}
.ladder__n {
  margin-bottom: var(--s-2);
  font-family: var(--font-display);
  font-variant-numeric: tabular-nums;
  font-size: var(--t-2);
  line-height: var(--lh-flat);
  color: var(--ink);
}
.ladder__col--zero .ladder__n { color: var(--ink-3); }
.ladder__bar {
  position: relative;
  width: min(64px, 70%);
  height: var(--h, 0%);
  min-height: var(--s-2);            /* «1 охотник» обязан быть виден */
}
/* Ноль: риска у основания вместо столбца — шкала читается целиком. Насечка
   лежит НАСТОЯЩИМ слоем, а не border-top: ширину рамки анимировать нельзя (это
   layout), а нулевой ранг обязан получить тот же жест, что полный, только по
   другой оси. Раскрывается ОТ ЦЕНТРА: на 390px бар около 28px, и чертящаяся
   слева линия была бы подпиксельным ползанием.
   ⚠️ Нулевые ранги — ОСНОВНОЕ состояние сайта, а не край: сегодня все игроки в
   G, семь колонок из восьми нулевые. */
.ladder__col--zero .ladder__bar {
  height: 0;
  min-height: 0;
}
.ladder__col--zero .ladder__bar::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 1px;
  background: var(--line-strong);
  transform-origin: 50% 50%;
  transition: transform var(--dur-2) var(--ease-out);
  transition-delay: calc(var(--ladder-lead) + var(--i, 0) * var(--ladder-step));
}
.census.is-arm .ladder__col--zero .ladder__bar::after { transform: scaleX(0); }
.ladder__fill {
  position: absolute;
  inset: 0;
  background-image: var(--plate-g);
  box-shadow: var(--sh-plate);
  transform-origin: 50% 100%;
  /* Длительность — от МАССЫ столбца (--hf, безразмерная доля высоты, ставит
     census.js): почти пустой встаёт за 220 мс, полный за 420 — вес виден в самом
     ходе, без единой подписи.
     ⚠️ Кривая --ease-out, а НЕ перелёт: у .ladder__bar нет overflow: hidden, а
     число стоит всего в 8px выше — перелёт залез бы в его поле, и металл
     читался бы резиной.
     ⚠️ К прокрутке столбец НЕ привязан намеренно: привязанный к прокрутке
     столбец показывает верную высоту только в одной точке, а график, который
     врёт в зависимости от того, где ты остановился, — не график. */
  transition: transform calc(var(--dur-2) + var(--hf, 1) * (var(--dur-3) - var(--dur-2))) var(--ease-out);
  transition-delay: calc(var(--ladder-lead) + var(--i, 0) * var(--ladder-step));
}
/* Столбцы, насечки и жетон — засечка, не прокрутка (довод выше). Класс
   ПЕРЕВЁРНУТ: базовое состояние КОНЕЧНОЕ, а is-arm его сдвигает, и ставит его
   только JS и только для шкалы, которая ещё ниже сгиба. Прежний :not(.is-in)
   без JS ПРЯТАЛ данные — эту дыру и затыкал 4-секундный таймер, который губил
   всё появление страницы. */
.census.is-arm .ladder__fill { transform: scaleY(0); }
.ladder__col[data-rank="F"] .ladder__fill { background-image: var(--plate-f); }
.ladder__col[data-rank="E"] .ladder__fill { background-image: var(--plate-e); }
.ladder__col[data-rank="D"] .ladder__fill { background-image: var(--plate-d); }
.ladder__col[data-rank="C"] .ladder__fill { background-image: var(--plate-c); }
.ladder__col[data-rank="B"] .ladder__fill { background-image: var(--plate-b); }
.ladder__col[data-rank="A"] .ladder__fill { background-image: var(--plate-a); }
.ladder__col[data-rank="S"] .ladder__fill { background-image: var(--plate-s); }
.ladder__rank {
  margin-top: var(--s-3);
  font-family: var(--font-display);
  font-size: var(--t-1);
  letter-spacing: var(--ls-label);
  color: var(--ink-2);
}
.ladder__col--zero .ladder__rank { color: var(--ink-3); }
/* «вы» — над своим столбцом, выше числа; появляется после роста */
.ladder__you {
  position: absolute;
  left: 50%;
  bottom: calc(var(--h, 0%) + var(--s-14));
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-1);
  /* Жетон садится сразу за СВОИМ столбцом (--i наследуется от .ladder__col), а не
     через захардкоженные 900 мс после всей диаграммы. Приезжает СВЕРХУ — жетон
     кладут на столбец, — и с перелётом: предмет мелкий, осадка безопасна.
     Единственная законная прозрачность в секции: жетона до посадки физически
     нет, а факт «вы в ранге G» уже произнесён в aria-label столбца и в
     .census__you-line. */
  transition: opacity var(--dur-2) ease, transform var(--dur-2) var(--ease-settle);
  transition-delay: calc(var(--ladder-lead) + var(--i, 0) * var(--ladder-step) + var(--dur-2));
}
.census.is-arm .ladder__you { opacity: 0; transform: translateX(-50%) translateY(calc(-1 * var(--s-2))); }
.ladder__ava { width: var(--s-10); height: var(--s-10); border-radius: var(--r-pill); object-fit: cover; box-shadow: var(--sh-plate); }
.ladder__you .guild-token { width: var(--s-10); height: var(--s-10); font-size: var(--t-1); }
.ladder__you-l {
  font-family: var(--font-body);
  font-weight: var(--w-semi);
  font-size: var(--t--1);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--accent-text);
}
/* Основание — латунная рейка, от которой мерят: её чертят слева направо ПЕРВОЙ,
   и столбцы встают за ней. Вертикаль под scaleX не искажается (scaleY = 1),
   поэтому читается именно «чертят линию», а не «растягивают предмет». */
.census__base {
  height: 6px;
  margin-top: var(--s-2);
  box-shadow: var(--sh-brass-rule);
  /* Рейку чертит ПРОКРУТКА: она мера и декорация, а не данные. Заканчивается к
     0,42 хода — раньше, чем засечка отпускает столбцы, поэтому столбцы
     по-прежнему встают за готовой рейкой. */
  transform-origin: 0 50%;
  scale: calc(1 - var(--rev-base)) 1;
}
.census__you-line {
  margin: var(--s-6) 0 0;
  max-width: var(--measure);
  font-size: var(--t-1);
  line-height: var(--lh-body);
  color: var(--ink-2);
}
@media (max-width: 760px) {
  .census { padding: var(--s-14) var(--s-5) var(--s-10); }
  .ladder { gap: var(--s-1); height: clamp(180px, 30vh, 260px); }
  .ladder__n { font-size: var(--t-0); }
  .ladder__rank { font-size: var(--t-0); letter-spacing: 0; }
}

/* ── Завершение: одно предложение, одно действие (§13) ── */
.finale {
  --enter: 1;
  --rev-edge: calc(1 - clamp(0, calc(var(--enter, 1) / 0.55), 1));
  --rev-line: calc(1 - clamp(0, calc((var(--enter, 1) - 0.2) / 0.6), 1));
  position: relative;
  padding: var(--s-20) var(--s-5) calc(var(--s-20) + var(--s-10));
  text-align: center;
  background: linear-gradient(180deg, var(--bg-base), var(--bg-void));
}
/* Закрывающая кромка: тот же латунный материал, что у основания шкалы переписи.
   Её вычерчивают ОТ ЦЕНТРА к краям — это конец, а не начало. Узла в DOM не
   добавляем (владелец только что вычищал с главной «приписанное»).
   ⚠️ Фраза и кнопка НЕ двигаются: у .button нет своего transition, зато есть
   .button:active { transform: translateY(1px) } — повесив переход на transform,
   мы растянули бы отклик на нажатие до 220 мс, а движение под единственным
   действием страницы может стоить клика. */
.finale::before {
  content: "";
  display: block;
  width: var(--s-20);
  height: 6px;
  margin: 0 auto var(--s-10);
  box-shadow: var(--sh-brass-rule);
  /* Кромку дочерчивает КОНЕЦ СТРАНИЦЫ. ⚠️ Кромка финала физически не поднимается
     до позиции чтения — документ кончается раньше, поэтому scroll.js берёт
     максимум из желаемого и достижимого; без поправки завершение застыло бы на
     середине хода навсегда (замер 1440×900: выше 444px кромка не идёт). */
  transform-origin: 50% 50%;
  scale: calc(1 - var(--rev-edge)) 1;
}
.finale__line {
  margin: 0 auto var(--s-6);
  translate: 0 calc(var(--rev-line) * var(--s-6));
  max-width: 36ch;
  font-family: var(--font-display);
  font-weight: var(--w-normal);
  font-size: var(--t-3);
  line-height: var(--lh-snug);
  color: var(--ink);
}
