/* ════════════════════════════════════════════════════════════════════
   Drakula Bastion — сайт.
   Кольори НЕ визначаються тут. Усе з --c-* приходить із
   ../Brand/design-system/css/tokens.css (COORDINATION.md п.2).
   ════════════════════════════════════════════════════════════════════ */

/* ── Гарнітури ────────────────────────────────────────────────────────
   Імена, шкала на 11 ролей, ваги й font-synthesis приходять із
   ../Brand/design-system/css/type.css. Тут САМЕ І ТІЛЬКИ файли: сайт
   віддає їх зі свого кореня, тому @font-face для двох гарнітур живе тут.

   Bastion Numerals НЕ оголошуємо: їхній @font-face уже є в type.css
   і показує на веб-сабсет woff2 (4 KB). Другий @font-face змусив би
   браузер тягнути ще й 189 KB TTF, призначений рушію. */

@font-face {
  font-family: "Playfair Display SC";
  src: url("/assets/fonts/playfair-display-sc/PlayfairDisplaySC-Regular.ttf") format("truetype");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Playfair Display SC";
  src: url("/assets/fonts/playfair-display-sc/PlayfairDisplaySC-Bold.ttf") format("truetype");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Playfair Display SC";
  src: url("/assets/fonts/playfair-display-sc/PlayfairDisplaySC-Black.ttf") format("truetype");
  font-weight: 900; font-style: normal; font-display: swap;
}
/* Didact Gothic має ОДНУ вагу. Другого @font-face для 700 немає навмисно:
   жирного в описовому шарі не існує, а синтезувати заборонено (п.21). */
@font-face {
  font-family: "Didact Gothic";
  src: url("/assets/fonts/didact-gothic/DidactGothic-Regular.ttf") format("truetype");
  font-weight: 400; font-style: normal; font-display: swap;
}

:root {
  --step: 8px;

  /* ⚠️ ЗАХИСНЕ ПОЛЕ. Рамка по периметру вікна, у будь-якому розмірі.
     Жоден елемент за неї не виходить — крім бічного меню, якому це
     дозволено окремо (LAWS.md A1, D1). Числа 32px у верстці поза цією
     змінною — помилка, а не «майже те саме». */
  --safe: 32px;

  /* Ширина колонки меню. Смуга вмісту відсувається на неї, щоб текст
     не заходив під шкалу. */
  --rail-w: 200px;

  /* Крок поділки меню. Не менше 56: меню — шкала, не список (LAWS.md D4). */
  --tick: 64px;

  /* ⚠️ СМУГИ ХРОМУ. Хедер і смуга дії — fixed/absolute, тобто місця в
     потоці не займають. Без явного резерву вміст заходив ПІД хедер:
     заголовок секції `video` перетинався з вордмарком. Ці числа —
     резерв, у який вміст не має права входити (LAWS.md B4). */
  --head-h: 56px;   /* факт 50; запас навмисний — резерв не має бути меншим за факт */
  --foot-h: 72px;

  /* Ширина смуги вмісту. Навмисно вужча за доступне місце: права
     частина кадру лишається вільною під візуал (LAWS.md A5). */
  --content-w: 46rem;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);

  /* Розкол «двох сторін». Пише важіль, читають обидві половини. */
  --split: 0.5;
}

*, *::before, *::after { box-sizing: border-box; }

html { font-size: 16px; }

body {
  margin: 0;
  background: var(--c-surface-base);
  color: var(--c-text-primary);
  font-family: var(--font-text);
  font-size: var(--fs-body);
  line-height: var(--lh-body);

  /* ⚠️ НЕ ЗНІМАТИ. Дубль навмисний: правило належить type.css (на html),
     а тут стоїть другим запобіжником — якщо type.css не доїде, браузер
     підробить фальшивий bold там, де стоїть font-weight: 700, а гра
     синтезувати не вміє, і сайт розійдеться з HUD у кадрі відео.
     Там, де потрібен жирний, ставиться --font-display. (п.21, обмеж. 1) */
  font-synthesis: none;
  -webkit-font-smoothing: antialiased;
}

/* Цифри тільки цією гарнітурою: у Playfair розкид ширин 62%,
   у Didact 38%, а `tnum` немає ні в кого (п.21). */
time, .num, .compare td, .signup__input { font-variant-numeric: tabular-nums; }
.num { font-family: var(--font-num); }

/* ── Стрічка ─────────────────────────────────────────────────────── */

.ribbon {
  height: 100vh;
  height: var(--app-vh, 100svh);
  overflow-y: scroll;
  scroll-snap-type: y mandatory;
  scroll-behavior: smooth;
  overscroll-behavior-y: contain;
  position: relative;
  z-index: 1;
}

.page {
  height: 100vh;
  height: var(--app-vh, 100svh);
  scroll-snap-align: start;
  scroll-snap-stop: always;
  position: relative;
  display: grid;
  overflow: hidden;

  /* ⚠️ ПРИВ'ЯЗКА ВЛІВО, не центр. Центрування тут було хибним: секція має
     відступ праворуч на колонку меню, тому «центр» боксу стояв на ~100px
     лівіше справжнього центру екрана — виглядало як «майже, але не». Або
     справжній центр, або чесна прив'язка до сторони; обрано прив'язку,
     і права частина кадру лишається під візуал. (LAWS.md A5) */
  align-items: center;
  justify-items: start;

  /* ⚠️ ЗАХИСНЕ ПОЛЕ ЖИВЕ ТУТ, а не на вмісті: відступ на самій секції
     означає, що вміст ФІЗИЧНО не може за нього вийти — не «ми стежимо»,
     а «немає куди». Зверху до поля додається смуга хедера, праворуч —
     колонка меню. (LAWS.md A1, A3, B4) */
  padding-top: calc(var(--safe) + var(--head-h) + var(--step) * 3);
  padding-right: calc(var(--safe) + var(--rail-w));
  padding-bottom: calc(var(--safe) + var(--step) * 3);
  padding-left: var(--safe);
}

/* Титульна має ще й смугу дії знизу — резервуємо і її. */
.page--title { padding-bottom: calc(var(--safe) + var(--foot-h) + var(--step) * 3); }

.page__inner {
  position: relative;
  z-index: 2;                /* над шарами 0-1, під хромом 3 */
  /* Межі задає padding секції. Тут лишається тільки максимум ширини
     й заборона переростати висоту кадру. */
  width: min(var(--content-w), 100%);
  max-height: 100%;
  min-height: 0;
}

/* ── Шари. Дисципліна z-index: 0 небо, 1 декор, 2 контент, 3 хром. ── */

.sky {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;              /* картинка вища за кадр — зайве ріжеться */

  /* Градієнт лишається ПІД картинкою, а не замість неї: він тримає тон,
     поки картинка вантажиться, і працює як фон, якщо background у конфізі
     виставлений у null. Тільки семантичні токени (LAWS.md E1). */
  background:
    radial-gradient(70% 38% at 18% 4%,
      color-mix(in srgb, var(--c-cyan) 26%, transparent), transparent 70%),
    radial-gradient(80% 34% at 26% 92%,
      color-mix(in srgb, var(--c-blood) 30%, transparent), transparent 72%),
    linear-gradient(180deg,
      var(--c-cyan-fill) 0%,
      var(--c-surface-raised) 30%,
      var(--c-surface-base) 70%,
      var(--c-surface-void) 100%);
}


/* ── Шари секцій ──────────────────────────────────────────────────────
   Порожні навмисно. Градієнти знято на прохання автора 2026-08-22:
   полотно тепер несе наскрізна картинка, і нижня заливка на кожній
   сторінці лише мутила її.

   Елементи ЛИШАЮТЬСЯ як точки монтування під 2D-арт переднього
   й заднього плану — parallax.js уже вміє їх зсувати.

   ⚠️ will-change тут НЕ ставимо. Порожній шар з will-change: transform
   усе одно отримує власний композитний шар — 14 шарів (7 секцій x 2),
   які нічого не малюють. Повернути разом з артом.

   Наслідок, який варто знати: нижній градієнт у layer--fore працював
   як підкладка під текст поверх картинки. Без нього читабельність
   тримає лише сама картинка. Якщо десь текст загубиться — вертати
   точково на ту секцію, а не на всі. */

/* Панорамна обгортка: саме її зсуває parallax.js, тому обидва
   примірники картинки їдуть разом. */
.sky__pan {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  will-change: transform;      /* вертикаль пише parallax.js */
}

/* ── Коливання вліво-вправо ───────────────────────────────────────────
   Полотно на 10% ширше за кадр, тому надлишок по ширині — рівно 10vw.
   Старт: верхній лівий кут приліплений до кадру. Хід: translateX від 0
   до -10vw, тобто правий край доходить точно до правої рамки й далі
   не йде. alternate вертає назад тим самим шляхом.

   ⚠️ linear, НЕ ease-in-out. Крива розгону робила рух нерівним: біля
   країв майже стоп, посередині ривок. Автор попросив однакову швидкість
   від початку до кінця — це рівно linear.

   156 с в один бік, 312 с повний хід: удвічі повільніше за 78.

   Це ОКРЕМИЙ вузол від .sky__pan навмисно: там transform пише JS, і
   CSS-анімація на тому ж вузлі його б перебила. */
.sky__drift {
  position: relative;
  width: 110%;
  will-change: transform;
  animation: sky-drift 156s linear infinite alternate;
}

/* Обгортка шару удару: та сама геометрія й та сама анімація, окремий
   шар. Висоту бере від .sky__pan, яку задає полотно в потоці.

   ⚠️ Окрема вона тому, що всередині лежить маска, а маска не дає
   винести піддерево на композитор. Разом з полотном вона змушувала
   перерастеризовувати фон, і рух ішов ривками. */
.sky__drift--flash {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
}

/* ⚠️ translate3d, НЕ translateX. Плоский 2D-трансформ не гарантує, що
   браузер винесе шар на GPU, а без цього дуже повільний рух округляється
   до цілих пікселів і йде кроками. При 78 с це 0.027 px за кадр — крок
   видно неминуче, якщо шар не композитний. */
@keyframes sky-drift {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-10vw, 0, 0); }
}

/* ⚠️ У ПОТОЦІ, не absolute. Саме через absolute обгортка мала висоту 0,
   а шар удару з inset: 0 — теж 0, і спалаху не було видно взагалі.
   Картинку при цьому було видно, бо вона позиціонувалась сама. */
.sky__img {
  display: block;
  width: 100%;
  height: auto;
}

/* ── Блискавка ────────────────────────────────────────────────────────
   Рідкий короткий удар, не безперервний пульс. Різниця не косметична:
   у спокої opacity 0, тобто композитору нічого малювати — а спокій це
   95% часу.

   Тут НЕМАЄ фільтра. Це суцільна заливка кольором з палітри, підрізана
   маскою, спеченою у збірці. Маска різка, піксель у піксель.

   inset: 0 всередині .sky__pan дає рівно бокс картинки: висоту обгортки
   задає сама картинка, тому маска стає піксель-у-піксель з нею. */
.sky__flash {
  position: absolute;
  inset: 0;
  /* ⚠️ Маска живе ТУТ — і саме тому цей вузол відселений у власну
     обгортку .sky__drift--flash, а не лежить поряд з картинкою. */
  background: var(--c-blood-accent);
  -webkit-mask-image: url("/assets/images/site-background-mask.webp");
  mask-image: url("/assets/images/site-background-mask.webp");
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  /* ⚠️ БЕЗ mix-blend-mode. Раніше тут стояв screen, і саме він давав
     скачки: змішування читає підкладку, тому ділянку неможливо винести
     в окремий композитний шар — і все рухоме піддерево перерастеризовувалось
     щокадру замість чистого зсуву на GPU.

     Втрата невелика: маска вже віддає ЧЕРВОНЕ НА ПРОЗОРОМУ, тому звичайне
     накладання теж читається як спалах світла, просто трохи щільніше. */
  opacity: 0;
}

/* will-change ставиться ТІЛЬКИ на час удару: постійний тримав би
   окремий композитний шар цілодобово задарма. */
.sky__flash.is-striking {
  will-change: opacity;
  animation: lightning 900ms linear 1;
}

/* Подвійно-потрійне мерехтіння, як у справжнього розряду: різкий
   фронт, провал, другий удар, довгий спад. */
@keyframes lightning {
  0%   { opacity: 0;    }
  3%   { opacity: 0.95; }
  9%   { opacity: 0.14; }
  14%  { opacity: 0.80; }
  20%  { opacity: 0.06; }
  27%  { opacity: 0.55; }
  45%  { opacity: 0.20; }
  100% { opacity: 0;    }
}

.layer {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}

/* ── Дощ ──────────────────────────────────────────────────────────────
   Краплі — окремі елементи, згенеровані збіркою. У кожної власна
   тривалість, затримка, довжина, прозорість, знос і кут.

   ⚠️ ЧОМУ НЕ ПЛИТКА ФОНУ. Плиткою всі краплі йдуть одним шаром, і цей
   шар щоцикл перескакує на висоту плитки — саме це давало ривок.
   Тут спільного циклу не існує, тому перескакувати нічому.

   ⚠️ ЧОМУ БЕЗ РОЗМИТТЯ. Розмиття в реальному часі дороге; ми це вже
   заплатили на полотні, і джерела радять те саме. «Фокус» роблять три
   ручки, і вони масштабуються РАЗОМ: далека крапля коротша, тьмяніша
   й повільніша, близька — довша, щільніша й швидша.

   Ні фільтрів, ні mix-blend-mode: змішування не дає винести шар на
   композитор, і це вже давало скачки на полотні.

   Шар нижчий за текст: стоїть у DOM перед .ribbon. */

.rain {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}

/* Передній план: великі розмиті краплі, що пролітають рідко.
   ⚠️ Розмиття СТАТИЧНЕ й на елементі шириною 6px — рахується раз при
   растрі. Це не розмиття в реальному часі по текстурі на весь екран,
   яке колись давало скачки на полотні. */
.drop--fg {
  filter: blur(5px);
  background: linear-gradient(
    to bottom,
    transparent,
    color-mix(in srgb, var(--c-text-secondary) 70%, transparent));
}

.drop {
  position: absolute;
  top: 0;
  width: var(--w);
  height: var(--len);
  /* Тмяно-сірий з палітри, не колір основного тексту: дощ мусить бути
     тлом, а не другим шаром тексту (рішення автора 2026-08-22).
     М'які кінці замість розмиття — край риски гасне сам. */
  background: linear-gradient(
    to bottom,
    transparent,
    color-mix(in srgb, var(--c-text-disabled) 85%, transparent));
  opacity: var(--op);
  will-change: transform;
  animation: drop-fall var(--dur) linear var(--delay) infinite;
}

/* Один набір кадрів на всі краплі: числа приходять зі змінних, тому
   траєкторія в кожної своя. 120vh, щоб крапля йшла за нижню межу
   й не зникала в кадрі. */
@keyframes drop-fall {
  from { transform: translate3d(0, -20vh, 0) rotate(var(--tilt)); }
  to   { transform: translate3d(var(--dx), 120vh, 0) rotate(var(--tilt)); }
}

/* ── Типографіка сторінок ────────────────────────────────────────── */

.wordmark {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: clamp(var(--fs-h1), 9vw, var(--fs-display));
  line-height: 0.95;
  letter-spacing: 0.01em;
  margin: 0 0 calc(var(--step) * 2);
}

.title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(var(--fs-h3), 4.5vw, var(--fs-h2));
  line-height: 1.1;
  margin: 0 0 calc(var(--step) * 2);
}

.minor {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-label);
  margin: calc(var(--step) * 4) 0 var(--step);
}

/* Дрібне — НЕ Playfair. Капітель Didact із трекінгом (п.21, обмеж. 2). */
.kicker {
  font-size: var(--fs-meta);
  text-transform: uppercase;
  letter-spacing: var(--ls-meta);
  color: var(--c-cyan-text);
  margin: 0 0 calc(var(--step) * 2);
}

.tagline {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(var(--fs-body), 2.4vw, var(--fs-h3));
  color: var(--c-text-secondary);
  margin: 0;
}

.lead {
  /* ⚠️ Роль на 20/32 («абзаци для читання», реєстр п.27) бренд-чат
     ПРИБРАВ разом із --fs-body-l. Лишається роль тіла 16/24. Ставити
     20px з інтерліньяжем 24 було б затісно для абзацу, а вигадувати
     свій інтерліньяж закон E2 забороняє. Це розходження між реєстром
     і токенами — воно винесене автору, не залатане на око. */
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--c-text-secondary);
  max-width: 44ch;
  margin: 0 0 calc(var(--step) * 4);
}

/* ── Акцент у тексті ──────────────────────────────────────────────────
   Мітки [[...]] і [[b:...]] у content/*.json перетворює build.py.
   Семантика тверда (LAWS.md E1): золото — системний акцент і сторона
   інквізиції, кров — сторона Дракули. Навпаки не буває.

   Це НЕ жирність: у Didact Gothic другої ваги не існує, а синтез
   заборонений. Акцент несе КОЛІР, не вага. */

.hl {
  color: var(--c-gold-lit);
  /* Волосяне підсвічування знизу — словник каркасу, тонка лінія. */
  box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--c-gold) 45%, transparent);
}
.hl--blood { color: var(--c-blood-accent); }
.hl--blood { box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--c-blood-accent) 45%, transparent); }

/* У заголовках підсвічування зайве: там достатньо самого кольору,
   а лінія під дідоною з волосяними штрихами читається як підкреслення. */
.title .hl, .wordmark .hl, .tagline .hl, .split__name .hl, .tile__title .hl {
  box-shadow: none;
}

.prose { max-width: 58ch; }
.prose p { margin: 0 0 calc(var(--step) * 2); }

/* Заглушка — видно, що це не готовий текст. Зникне разом із вмістом. */
.stub {
  font-size: var(--fs-meta);
  color: var(--c-text-secondary);
  border-left: 2px solid var(--c-border);
  padding-left: var(--step);
  margin-top: calc(var(--step) * 3);
}

/* ── Відео ─────────────────────────────────────────────────────────
   Бокс 16:9 у повний екран не влазить під заголовком: заміряно —
   виходив на 194px за нижню межу й обрізався. Тому секція розкладена
   у три рядки, а відео бере РЕШТУ висоти й від неї виводить ширину. */

.page--video .page__inner {
  display: grid;
  grid-template-rows: auto auto minmax(0, 1fr);
  justify-items: start;
  gap: 0;
  height: 100%;
  /* Ширше за --content-w: інакше max-width колонки підрізав би відео
     й ламав пропорцію. Ширину визначає висота кадру, не колонка. */
  width: min(72rem, 100%);
}
.page--video .lead { margin-bottom: calc(var(--step) * 3); }

.video {
  position: relative;
  /* ⚠️ ОДНЕ обмеження, не два. Було height:100% РАЗОМ з max-width:100%,
     а з aspect-ratio це взаємно виключні вимоги: ширину підрізало по
     колонці, висота лишалась повною, і 16:9 ставало майже квадратом.
     Тепер розмір веде ВИСОТА, а ширина виводиться з неї. Щоб max-width
     не втручався, колонка цієї секції розширена нижче. */
  aspect-ratio: 16 / 9;
  height: 100%;
  width: auto;
  margin-inline: 0;
  min-height: 0;
  background: var(--c-surface-void);
  border: 1px solid var(--c-border);
  display: grid;
  place-items: center;
  overflow: hidden;
}
.video__loop { width: 100%; height: 100%; object-fit: cover; }
.video__poster {
  position: absolute; inset: 0;
  background: var(--game-rim-cold), var(--game-rim-warm), var(--c-surface-raised);
}
.video__pending {
  position: relative;
  font-family: var(--font-num);
  font-size: clamp(var(--fs-num), 3vw, var(--fs-num-l));
  letter-spacing: 0.08em;
  color: var(--c-text-secondary);
}
.video__play {
  position: absolute;
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  padding: var(--step) calc(var(--step) * 3);
  color: var(--c-text-primary);
  background: color-mix(in srgb, var(--c-surface-void) 62%, transparent);
  border: 1px solid var(--c-gold);
  cursor: pointer;
}
.video__play:hover { border-color: var(--c-gold); }

/* ── Дві сторони ─────────────────────────────────────────────────── */

.split { display: grid; grid-template-columns: 1fr 1fr; gap: 0; }

.split__half {
  padding: calc(var(--step) * 3);
  border: 1px solid var(--c-border);
  transition: opacity 240ms var(--ease), border-color 240ms var(--ease);
}
/* Ліва половина яскравіє, коли важіль іде вліво, і навпаки.
   Це змінює АРТ, не тему: пар day/night немає (п.22). */
.split__half--night {
  opacity: calc(1.15 - var(--split) * 0.75);
  border-color: color-mix(in srgb, var(--c-blood) 60%, var(--c-border));
}
.split__half--day {
  opacity: calc(0.4 + var(--split) * 0.75);
  border-color: color-mix(in srgb, var(--c-gold) 60%, var(--c-border));
}

.split__name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(var(--fs-h3), 2.5vw, var(--fs-h2));
  margin: 0 0 calc(var(--step) * 2);
}
.split__half--night .split__name { color: var(--c-blood-accent); }
.split__half--day .split__name { color: var(--c-gold-lit); }

.split__points { margin: 0; padding-left: 1.2em; }
.split__points li { margin-bottom: var(--step); }

.lever {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: calc(var(--step) * 2);
  margin-top: calc(var(--step) * 3);
}
.lever__end {
  font-size: var(--fs-meta);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--c-text-secondary);
}
/* Справжній range: тач, клавіатура й читачі екрана — безкоштовно. */
.lever__input {
  appearance: none;
  width: 100%;
  height: calc(var(--step) * 3);
  background: transparent;
  cursor: grab;
}
.lever__input::-webkit-slider-runnable-track {
  height: 2px;
  background: linear-gradient(90deg, var(--c-blood), var(--c-gold));
}
.lever__input::-moz-range-track {
  height: 2px;
  background: linear-gradient(90deg, var(--c-blood), var(--c-gold));
}
.lever__input::-webkit-slider-thumb {
  appearance: none;
  width: var(--step); height: calc(var(--step) * 3);
  margin-top: calc(var(--step) * -1.5);
  background: var(--c-gold);
  border: 1px solid var(--c-surface-void);
}
.lever__input::-moz-range-thumb {
  width: var(--step); height: calc(var(--step) * 3);
  border-radius: 0;
  background: var(--c-gold);
  border: 1px solid var(--c-surface-void);
}
.lever__input:focus-visible { outline: 2px solid var(--c-focus-ring); outline-offset: 4px; }

/* ── Плитки, порівняння ──────────────────────────────────────────── */

.tiles {
  list-style: none; margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: calc(var(--step) * 2);
}
.tile {
  background: color-mix(in srgb, var(--c-surface-raised) 72%, transparent);
  border: 1px solid var(--c-border);
  padding: calc(var(--step) * 3);
}
.tile__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-h3);
  margin: 0 0 var(--step);
}
.tile__text { margin: 0; color: var(--c-text-secondary); font-size: var(--fs-meta); }

.compare { border-collapse: collapse; width: 100%; }
.compare th {
  text-align: left;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-label);
  padding: var(--step);
  border-bottom: 1px solid var(--c-border);
}
.compare td {
  padding: calc(var(--step) * 1.5) var(--step);
  border-bottom: 1px solid var(--c-border);
  vertical-align: top;
  font-size: var(--fs-meta);
}
.compare__usual { color: var(--c-text-secondary); }
.compare__here { color: var(--c-text-primary); }

/* ── Дрібне ──────────────────────────────────────────────────────── */

.button {
  display: inline-block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-label);
  padding: var(--step) calc(var(--step) * 3);
  color: var(--c-text-primary);
  text-decoration: none;
  background: transparent;
  border: 1px solid var(--c-gold);
  cursor: pointer;
}
.link { color: var(--c-cyan-text); }
.social { list-style: none; margin: 0; padding: 0; display: flex; gap: calc(var(--step) * 3); }

.signup { display: grid; gap: var(--step); max-width: 26rem; }
.signup__label { font-size: var(--fs-meta); color: var(--c-text-secondary); }
.signup__input {
  font: inherit;
  padding: var(--step);
  color: var(--c-text-primary);
  background: var(--c-surface-raised);
  border: 1px solid var(--c-border-interactive);
}
.signup__input:focus-visible { outline: 2px solid var(--c-focus-ring); outline-offset: 2px; }
.signup__note { font-size: var(--fs-meta); color: var(--c-text-secondary); margin: 0; }

.footer {
  display: flex; gap: calc(var(--step) * 3);
  margin-top: calc(var(--step) * 5);
  padding-top: calc(var(--step) * 2);
  border-top: 1px solid var(--c-border);
  font-size: var(--fs-meta);
  color: var(--c-text-secondary);
}

.lightbox {
  padding: 0;
  border: none;
  background: transparent;
  width: min(92vw, 80rem);
  max-width: none;
}
.lightbox::backdrop { background: color-mix(in srgb, var(--c-surface-void) 88%, transparent); }
.lightbox iframe {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--c-border);
  background: var(--c-surface-void);
}

/* Схований зсувом, а не виносом на -9999px: трансформ не має розміру,
   тому не потрапляє в сітку, і фокус нікуди не «летить». */
.skip {
  position: absolute; left: var(--safe); top: var(--safe);
  transform: translateY(-300%);
  z-index: 4; padding: var(--step);
  background: var(--c-surface-raised); color: var(--c-text-primary);
}
.skip:focus { transform: translateY(0); }

/* ── Прибуття вмісту: один раз, не на кожному проході ────────────── */

/* Ховаємо ЛИШЕ коли JS живий (прапорець ставить вбудований скрипт
   у <head>). Інакше сторінка без JS показувала б порожній фон. */
[data-js="on"] [data-reveal] .page__inner > * {
  opacity: 0;
  transform: translateY(calc(var(--step) * 2));
  transition: opacity 520ms var(--ease), transform 520ms var(--ease);
}
[data-js="on"] [data-reveal="seen"] .page__inner > * { opacity: 1; transform: none; }
[data-js="on"] [data-reveal="seen"] .page__inner > *:nth-child(2) { transition-delay: 70ms; }
[data-js="on"] [data-reveal="seen"] .page__inner > *:nth-child(3) { transition-delay: 140ms; }
[data-js="on"] [data-reveal="seen"] .page__inner > *:nth-child(4) { transition-delay: 210ms; }

/* ⚠️ Відео проявляється БЕЗ ЗСУВУ. Бокс 16:9 заповнює решту висоти
   секції, тобто запасу під ним немає — і підйом на 16px виносив його
   за захисне поле саме в перехідному стані. Заміряно: 16px замість 32
   доки анімація не відіграла, 0 порушень після. Закон каже «ні на
   піксель», а не «ні на піксель після анімації» (LAWS.md A1). */
[data-js="on"] [data-reveal] .page--video .video,
[data-js="on"] .page--video .page__inner > .video { transform: none; }


/* ── Зменшений рух. Паралакс геть, поява без руху, важіль лишається. ── */

@media (prefers-reduced-motion: reduce) {
  .ribbon { scroll-behavior: auto; }
  .sky, .sky__pan, .layer { transform: none !important; }
  /* Коливання — теж рух, і теж вимикається. */
  .sky__drift { animation: none; transform: none; }
  /* Дощ прибирається повністю: падаючі краплі — це саме той рух,
     від якого налаштування й захищає. */
  .rain { display: none; }
  /* Різкі блимання небезпечні для фотосенситивних людей, тому тут
     це не примха налаштування, а єдиний правильний варіант:
     удари не запускаються, лишається тьмяне статичне світло. */
  .sky__flash { animation: none !important; opacity: 0.14; }
  [data-reveal] .page__inner > *,
  [data-reveal="seen"] .page__inner > * {
    opacity: 1; transform: none; transition: none; transition-delay: 0s;
  }
  .split__half { transition: none; }
}

/* ── Об'єднані секції ────────────────────────────────────────────────
   Автор злив 8 секцій у 7 (2026-08-21): світ+сторони, гра+унікальність,
   преса+контакти. Кожна секція — все ще ОДИН екран (LAWS.md C1), тому
   об'єднані отримують дві колонки замість одного стовпця. */

/* Ширша колонка там, де в секції два блоки поруч. */
.page--game .page__inner,
.page--press .page__inner,
.page--roadmap .page__inner { width: min(64rem, 100%); }

.duo {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: calc(var(--step) * 4);
  align-items: start;
}
.duo > * { min-width: 0; }

/* Плитки в об'єднаній секції стають стовпчиком: поруч із таблицею
   три плитки в рядок не читаються. */
.page--game .tiles { grid-template-columns: 1fr; gap: var(--step); }
.page--game .tile { padding: calc(var(--step) * 2); }

/* ── Числа під відео ────────────────────────────────────────────────
   Реєстр п.15 просить не округлювати, тому цифри стоять окремо
   й набрані Bastion Numerals — інакше 162 і 56 мали б різну ширину. */

.facts {
  list-style: none;
  margin: calc(var(--step) * 3) 0 0;
  padding: 0;
  display: flex;
  gap: calc(var(--step) * 5);
}
.fact { display: grid; gap: 4px; }
.fact__value {
  font-family: var(--font-num);
  font-size: var(--fs-num-l);
  line-height: var(--lh-num-l);
  letter-spacing: var(--ls-num-l);
  color: var(--c-gold-lit);
}
.fact__label {
  font-size: var(--fs-micro);
  line-height: var(--lh-micro);
  letter-spacing: var(--ls-micro);
  text-transform: uppercase;
  color: var(--c-text-secondary);
}

/* ── Годмапа ─────────────────────────────────────────────────────────
   Три етапи без дат: дата — це обіцянка, а порядок — факт.
   Джерело — Docs/04-Presentation/PRODUCTION-PLAN.md. */

.stages {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: calc(var(--step) * 3);
  counter-reset: stage;
}
.stage {
  position: relative;
  padding-top: calc(var(--step) * 2);
  /* Тонка лінія згори як поділка на шкалі — той самий словник
     картографії, що й у меню (LAWS.md E5). */
  border-top: 1px solid var(--c-border);
}
/* Активним виглядає лише перший етап: він єдиний, у якого є що робити. */
.stage:first-child { border-top-color: var(--c-gold); }

.stage__num {
  display: block;
  font-family: var(--font-num);
  font-size: var(--fs-num);
  line-height: var(--lh-num);
  letter-spacing: var(--ls-num);
  color: var(--c-text-secondary);
}
.stage:first-child .stage__num { color: var(--c-gold-lit); }

.stage__name {
  font-family: var(--font-display);
  font-weight: var(--w-heading);
  font-size: var(--fs-h3);
  line-height: var(--lh-h3);
  letter-spacing: var(--ls-h3);
  margin: var(--step) 0 calc(var(--step) * 0.5);
}
.stage__slots {
  margin: 0 0 var(--step);
  font-size: var(--fs-micro);
  line-height: var(--lh-micro);
  letter-spacing: var(--ls-micro);
  text-transform: uppercase;
  color: var(--c-cyan-text);
}
.stage__text {
  margin: 0;
  font-size: var(--fs-meta);
  line-height: var(--lh-meta);
  letter-spacing: normal;
  text-transform: none;
  color: var(--c-text-secondary);
}

/* ── Завантаження ────────────────────────────────────────────────────
   Секція існує на вимогу автора, але білда немає: у вересні демо — це
   запис (COORDINATION.md п.6). Тому стан «ще немає» тут не помилка,
   а зміст сторінки, і він оформлений як зміст, не як збій. */

.page--download .stub { max-width: 52ch; }
.page--download .footer { margin-top: calc(var(--step) * 6); }

/* ── Вузький кадр ──────────────────────────────────────────────────────
   Адаптив зараз НЕ робимо: рішення на вересень — тільки ПК (LAWS.md A4).
   Тут лише те, що не дає верстці зламатись: смуга вмісту перестає
   поступатися колонкою меню, а розкол стає одноколонковим. */

@media (max-width: 63.9375rem) {
  .page__inner { width: calc(100% - var(--safe) * 2); margin-right: 0; }
  .scene__foot { right: var(--safe); }
  .split { grid-template-columns: 1fr; }
  .duo { grid-template-columns: 1fr; gap: calc(var(--step) * 2); }
  .stages { grid-template-columns: 1fr; }
  .facts { gap: calc(var(--step) * 3); }
}
