/* ═══════════════════════════════════════════════════════════════════════
   NAYZAK — expo site
   Собрано по docs/brand/VISUAL_STANDARD.md, 20.09.2026.

   ЗАКОНЫ, КОТОРЫЕ ЭТОТ ФАЙЛ ОБЯЗАН ДЕРЖАТЬ:
   §BG   Фон ОДИН на всю страницу — градиент на .page. Секции прозрачны.
         ⛔ Ни одного background-color на .s — именно он даёт линию стыка.
   §GRID Каждый margin/padding в px ∈ {8,16,24,40,64,96,128} или 0.
   §TYPE ≤3 размера шрифта на секцию; body ≥16px; ls ≤ −0.018em при ≥44px.
   §MOVE амплитуда 12–40px, 600–800ms, cubic-bezier(.22,1,.36,1), порог 25%.
         ⛔ bounce, побуквенная анимация, анимация layout-свойств.
   §CUT  ⛔ CSS box-shadow под вырезанным объектом. Тени запечены в кадр.
   ═══════════════════════════════════════════════════════════════════════ */

/* ═══ §A. СБРОС ════════════════════════════════════════════════════════ */

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

html {
  /* ⛔ overflow-x:clip на body — по спеке клипает и по Y, страница
     перестаёт скроллиться. html: clip, body: hidden (урок L40). */
  overflow-x: clip;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

body {
  margin: 0;
  /* ⛔ Не возвращать overflow-x сюда. Любой overflow на body делает его
     скролл-контейнером, и position:sticky у потомков привязывается к
     НЕМУ, а не к окну: пин сцены peppermint переставал липнуть и уезжал
     вверх (замер 20.09: pinTop −1013 при vh 844, продукт вне экрана).
     Горизонтальный скролл режется на html — там это безопасно. */
  background: #000;
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: var(--t-body);
  line-height: 1.52;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1,
h2,
h3,
p,
dl,
dd,
figure,
ul,
blockquote {
  margin: 0;
}
ul {
  padding: 0;
  list-style: none;
}
img,
picture,
svg {
  display: block;
  max-width: 100%;
}
img {
  height: auto;
}
a {
  color: inherit;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

.skip {
  position: fixed;
  left: 16px;
  top: -120px;
  z-index: 90;
  padding: 16px 24px;
  background: var(--gold);
  color: #000;
  font-size: var(--t-body);
  font-weight: 500;
  text-decoration: none;
  transition: top var(--dur-fast) var(--ease);
}
.skip:focus {
  top: 16px;
}

:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 4px;
}

/* ═══ §B. ШРИФТЫ ═══════════════════════════════════════════════════════
   Всё со своего домена. ⛔ Ни одного внешнего запроса:
   вес + Art. 5(3) 2002/58/EC. */

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("media/font/inter-regular-subset.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("media/font/inter-medium-subset.woff2") format("woff2");
}
@font-face {
  font-family: "Inter Display";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("media/font/interdisplay-semibold-subset.woff2") format("woff2");
}
@font-face {
  font-family: "Inter Display";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("media/font/interdisplay-bold-subset.woff2") format("woff2");
}
@font-face {
  font-family: "Amiri";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("media/font/amiri-bold-subset.woff2") format("woff2");
}

/* ═══ §C. ТОКЕНЫ ═══════════════════════════════════════════════════════ */

:root {
  /* Палитра — три значения плюс кость. ⛔ Ни одного стального рефлекса. */
  --gold: #c9a54a;
  --gold-ink: #7a5f1e; /* золото на светлом: 5.3:1 к #f4f1e8 */
  --ink: #e8e4da;
  --ink-mute: #9a9488; /* 5.4:1 к #08080a */
  /* Замер 20.09 по фактическому пикселю фона: #6e695f давал 3.48–3.83:1
     при норме 4.5 — 27 провалов на телефоне, 32 на десктопе, и все из
     одного корня. #857f73 даёт 4.77 на худшем фоне сайта (#101014). */
  --ink-faint: #857f73;
  --ivory: #f4f1e8;
  --line: rgb(201 165 74 / 0.2);
  --line-soft: rgb(232 228 218 / 0.12);

  /* §TYPE. Шкала фиксирована: H1 76–96, H2 44–56, lead 21–24, body 17. */
  --t-h1: clamp(2.5rem, 6.2vw, 6rem); /* 40 → 96 px */
  --t-h2: clamp(1.875rem, 3.6vw, 3.5rem); /* 30 → 56 px */
  --t-mat: clamp(1.75rem, 2.6vw, 2.625rem); /* 28 → 42 px */
  --t-lead: clamp(1.1875rem, 1.35vw, 1.5rem); /* 19 → 24 px */
  --t-body: 1.0625rem; /* 17 px, ⛔ ниже 16 нельзя */
  --t-caps: 0.8125rem; /* 13 px капитель */
  --t-rail: 0.625rem; /* 10 px нижний рельс */

  --font-ui: "Inter", ui-sans-serif, system-ui, sans-serif;
  --font-display: "Inter Display", "Inter", ui-sans-serif, system-ui, sans-serif;
  --font-ar: "Amiri", serif;

  /* §GRID. Только эти значения. */
  --sp-1: 8px;
  --sp-2: 16px;
  --sp-3: 24px;
  --sp-4: 40px;
  --sp-5: 64px;
  --sp-6: 96px;
  --sp-7: 128px;

  --gutter: clamp(24px, 5vw, 64px);
  --measure: 40rem; /* 640px — в вилке 620–680 */

  --dur-fast: 160ms;
  --dur-move: 700ms; /* §MOVE: 600–800ms */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);

  color-scheme: dark;
}

/* ⛔ КРИТИЧНО. Inter не содержит ни одного арабского кодпоинта (проверено
   fontTools по всем четырём субсетам: 0/6). Без этого правила قوي в чипах
   крепости падает в системный шрифт, а на Android без арабского — в тофу □□□
   прямо на глазах у дистрибьютора. Повтор урока N24, только с вязью. */
[lang="ar"],
.ar {
  font-family: var(--font-ar);
  /* Трекинг на курсивном письме разрывает соединения букв: чип задаёт
     .06em, на арабском его надо снять. */
  letter-spacing: normal;
  font-feature-settings: "init", "medi", "fina", "ccmp";
}


/* ═══ §BG. ЕДИНЫЙ ФОН — ЗАДАН РОВНО ЗДЕСЬ, ОДИН РАЗ ════════════════════
   ⛔ Ниже по файлу не должно появиться ни одного background-color
   на секции .s: горизонтальная линия стыка рождается именно так. */

.page {
  position: relative;
  min-height: 100svh;
  background: linear-gradient(
    180deg,
    #000000 0%,
    #08080a 22%,
    #101014 50%,
    #08080a 78%,
    #000000 100%
  );
}

/* ═══ §D. ТИПОГРАФИКА ══════════════════════════════════════════════════ */

.wrap {
  width: 100%;
  max-width: 78rem;
  margin: 0 auto;
  padding-inline: var(--gutter);
}

.h2 {
  font-family: var(--font-display);
  font-size: var(--t-h2);
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: -0.022em;
  color: var(--ivory);
  max-width: 18ch;
  /* Заголовок не оставляет последнее слово одно на строке: на 390 px
     «Gold on matte black.» ломалось сиротой «black.». */
  text-wrap: balance;
}

.lead {
  text-wrap: pretty;
  font-size: var(--t-lead);
  line-height: 1.42;
  font-weight: 400;
  color: var(--ink);
  max-width: var(--measure);
}

.body {
  font-size: var(--t-body);
  line-height: 1.52;
  color: var(--ink-mute);
  max-width: var(--measure);
}

.eyebrow {
  /* Капитель 13px — разрешена спекой явно (якорь категории,
     подпись-капитель 13–16px). Это ярлык, не абзац. */
  font-size: var(--t-caps);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold);
  opacity: 0.85;
}

.fine {
  font-size: var(--t-body);
  line-height: 1.52;
  color: var(--ink-faint);
  max-width: var(--measure);
}

.hair {
  width: 96px;
  height: 0;
  margin: 0;
  border: 0;
  border-top: 1px solid var(--gold);
  opacity: 0.35;
}

/* Пара «метка + значение» — единственная форма реквизитов на сайте. */
.pair {
  display: grid;
  gap: var(--sp-1);
}
.pair dt {
  font-size: 0.6875rem; /* 11 px */
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.pair dd {
  margin: 0;
  font-size: var(--t-body);
  color: var(--ink);
}

/* ═══ §E. РИТМ СЕКЦИЙ ══════════════════════════════════════════════════ */

.s {
  position: relative;
  /* ⛔ background не задаётся НИКОГДА — §BG. */
  padding-block: var(--sp-6);
}
@media (min-width: 64rem) {
  .s {
    padding-block: var(--sp-7);
  }
}

/* ═══ §F. ДВИЖЕНИЕ ═════════════════════════════════════════════════════
   §MOVE. CSS задаёт КОНЕЧНОЕ состояние; from-кадр живёт только внутри
   keyframes и только под no-preference. ⛔ opacity:0 базовым состоянием
   элемента — урок L31: 42 пустые страницы. */

@keyframes nz-up {
  from {
    opacity: 0;
    transform: translate3d(0, 20px, 0);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
@keyframes nz-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
@keyframes nz-settle {
  from {
    opacity: 0;
    transform: scale(1.04);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
/* Эффект ПИСЬМА для вязи: RTL, справа налево, постоянная скорость.
   ⛔ Нулевые inset срезают хвост ز и точки ي — только отрицательные. */
@keyframes nz-write {
  from {
    clip-path: inset(-15% -4% -12% 100%);
  }
  to {
    clip-path: inset(-15% -4% -12% -4%);
  }
}

@media (prefers-reduced-motion: no-preference) {
  .js .rv.in > * {
    animation: nz-up var(--dur-move) var(--ease) both;
  }
  .js .rv.in > *:nth-child(2) {
    animation-delay: 60ms;
  }
  .js .rv.in > *:nth-child(3) {
    animation-delay: 120ms;
  }
  .js .rv.in > *:nth-child(4) {
    animation-delay: 180ms;
  }
  .js .rv.in > *:nth-child(5) {
    animation-delay: 240ms;
  }

  /* Каскад по --i, шаг 60ms, максимум 8 элементов (хвост 480ms). */
  .js .casc.in > * {
    animation: nz-up 620ms var(--ease) both;
    animation-delay: calc(var(--i, 0) * 60ms);
  }
}

/* ═══ §1. HERO ═════════════════════════════════════════════════════════
   Цельный кадр. Банка, камень, контактная и ambient тени — внутри
   картинки. ⛔ Ни одного box-shadow, ни одной вырезки. */

.hero {
  min-height: 100svh;
  padding-block: 0;
  display: grid;
  grid-template-rows: 1fr auto;
  overflow: clip;
}

.hero-frame {
  position: absolute;
  z-index: 0;
  left: 0;
  right: 0;
  bottom: 0;
  /* Мобильная стопка: локап → факт → банка ЦЕЛИКОМ. Кадру отдана
     нижняя часть экрана; верх растворяется маской на треть высоты,
     иначе кромка кадра читается ступенью на чёрном. */
  height: 74svh;
  display: block;
  -webkit-mask-image: linear-gradient(180deg, rgb(0 0 0 / 0) 0%, #000 34%, #000 82%, rgb(0 0 0 / 0) 100%);
  mask-image: linear-gradient(180deg, rgb(0 0 0 / 0) 0%, #000 34%, #000 82%, rgb(0 0 0 / 0) 100%);
}
.hero-frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Банка ближе к верху кадра: закрывает мёртвую полосу под текстом,
     не заезжая под него. */
  object-position: 50% 72%;
}
/* §SEAM. Кромки кадра РАСТВОРЯЮТСЯ МАСКОЙ, а не закрашиваются лентой.
   Лента рисует свой цвет поверх единого градиента и даёт ступень там,
   где градиент в этой точке не чёрный (замер 20.09: material@4616 —
   52% страницы, #101014, ступень 15.8/255). Маска вырезает альфу, и
   за кадром виден ТОТ ЖЕ градиент .page → ступень на стыке = 0.
   ⛔ Не возвращать ::after с background — это и есть линия стыка. */
.hero-frame {
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 62%, rgb(0 0 0 / 0) 100%);
  mask-image: linear-gradient(180deg, #000 0%, #000 62%, rgb(0 0 0 / 0) 100%);
}

.hero-plate {
  position: relative;
  z-index: 2;
  grid-row: 1;
  align-self: start;
  width: 100%;
  max-width: 78rem;
  margin: 0 auto;
  padding-inline: var(--gutter);
  padding-block: var(--sp-5) var(--sp-2);
}

.lockup {
  display: grid;
  gap: var(--sp-2);
  justify-items: start;
}

.lockup-calli {
  width: min(62vw, 300px);
  height: auto;
  /* Вязь золотом: SVG чёрный → красим фильтром по контуру глифов. */
  filter: brightness(0) saturate(100%) invert(72%) sepia(38%) saturate(640%)
    hue-rotate(2deg) brightness(93%) contrast(89%);
}
.lockup-word {
  display: block;
  font-family: var(--font-display);
  font-size: var(--t-h1);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.025em;
  color: var(--ivory);
}

.hero-fact {
  margin-top: var(--sp-2);
  font-size: var(--t-lead);
  line-height: 1.42;
  color: var(--ink);
  max-width: 24ch;
}

.hero-anchor-line {
  margin-top: var(--sp-2);
  font-size: var(--t-caps);
  font-weight: 400;
  /* Трекинг в vw: на 390 px строка «20 POUCHES · 25 MG / POUCH · MADE IN
     EU» встаёт в одну линию и «EU» не остаётся сиротой на второй. */
  letter-spacing: clamp(0.055em, 1.6vw - 0.48em, 0.14em);
  text-wrap: balance;
  /* ⛔ Не возвращать opacity: она съедает контраст сильнее, чем кажется —
     #9a9488 даёт 6.40:1, но при .75 падает до 4.06 (замер 20.09). */
  color: var(--ink-mute);
}

.hero-down {
  position: absolute;
  z-index: 2;
  left: 50%;
  bottom: var(--sp-4);
  transform: translateX(-50%);
  width: 48px;
  height: 48px; /* тач-цель ≥44px */
  display: grid;
  place-items: center;
}
.hero-down i {
  display: block;
  width: 1px;
  height: 40px;
  background: linear-gradient(180deg, rgb(201 165 74 / 0) 0%, var(--gold) 100%);
}

@media (min-width: 64rem) {
  .hero {
    grid-template-rows: 1fr;
    align-items: center;
  }
  /* Кадр во всю секцию. Банка в центре-низу, воздух сверху ≥45% —
     типографика садится в этот воздух, ни на что не наезжая. */
  .hero-frame {
    top: 0;
    height: auto;
    inset: 0;
    -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 72%, rgb(0 0 0 / 0) 100%);
    mask-image: linear-gradient(180deg, #000 0%, #000 72%, rgb(0 0 0 / 0) 100%);
  }
  .hero-frame img {
    object-position: 50% 62%;
  }
  .hero-plate {
    grid-row: 1;
    align-self: start;
    padding-block: var(--sp-6);
  }
  .lockup-calli {
    width: min(22vw, 260px);
  }
  .hero-plate > * {
    max-width: 30rem;
  }
  .hero-fact {
    max-width: 22ch;
  }
}

/* Оркестровка входа героя — одним классом html.age-ok.
   Герой и так в кадре, IntersectionObserver здесь не нужен. */
@media (prefers-reduced-motion: no-preference) {
  .js.age-ok .hero-frame {
    animation: nz-settle 900ms cubic-bezier(0.2, 0.7, 0.2, 1) 60ms both;
  }
  .js.age-ok .lockup {
    animation: nz-up 620ms var(--ease) 200ms both;
  }
  .js.age-ok .hero-fact {
    animation: nz-up 620ms var(--ease) 350ms both;
  }
  .js.age-ok .hero-anchor-line {
    animation: nz-up 620ms var(--ease) 470ms both;
  }
  .js.age-ok .hero-down {
    animation: nz-in 700ms linear 700ms both;
  }
}

/* ═══ §2. ИМЯ ══════════════════════════════════════════════════════════
   Единственное место, где говорит бренд. Вязь — сама графика кадра.
   Кадр meteor-cgi2 — самый сильный на сайте: метеор идёт МИМО букв. */

.name {
  position: relative;
  min-height: 100svh;
  display: grid;
  align-items: start;
  overflow: clip;
  padding-block: var(--sp-6);
}

.name-frame {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  z-index: 0;
  /* ⛔ Не возвращать inset:0. Секция выше экрана (текста много при
     min-height:100svh), и кадр, растянутый на всю её высоту, приводил
     банку ПОД абзац легенды: текст ложился на крышку, «NICOTINE POUCHES»
     пробивалось сквозь строки, читаемость 0 (замер 20.09: ступень фона
     под текстом 15.05/255 при пороге 6). Кадру отдана только верхняя
     часть секции — ровно там, где живёт вязь. */
  height: 58svh;
}
.name-frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Кадр теперь занимает верхние 72svh секции. Силуэт банки в источнике
     Y 0.284–0.936; 78% держат её в нижней части КАДРА, то есть выше
     текстового блока, который начинается ниже кадра. */
  object-position: 50% 78%;
}
/* §SEAM. ОДНА маска ведёт оба стыка секции — вырезом по альфе.
   ⛔ У соседей растушёвка на этом стыке выключена: две растушёвки
   встык дают двойную плотность и светлую полосу посередине. */
.name-frame {
  -webkit-mask-image: linear-gradient(
    180deg, rgb(0 0 0 / 0) 0%, #000 26%, #000 72%, rgb(0 0 0 / 0) 100%);
  mask-image: linear-gradient(
    180deg, rgb(0 0 0 / 0) 0%, #000 26%, #000 72%, rgb(0 0 0 / 0) 100%);
}

.name-copy {
  position: relative;
  z-index: 2;
  padding-block: var(--sp-6);
}
@media (max-width: 63.999rem) {
  /* Вязь сидит в кадре (верх), проза — ниже кадра на чистом фоне.
     ⛔ Не выталкивать абзац padding-ом: замер показал, что он уходит
     за нижнюю кромку экрана и текст пропадает совсем. Место освобождает
     САМ КАДР (height ниже), а текст стоит там же, где стоял. */
  .name {
    align-items: start;
  }
}

.name-h {
  margin: 0;
}
.name-ar {
  display: block;
  /* dir=rtl нужен для ПРАВИЛЬНОЙ СБОРКИ вязи, но он же выключает её
     вправо по колонке — и глифы наезжали на крышку (замер v5:
     left 512 → right 928 при свободном поле до 0.68 ширины).
     text-align:left возвращает выключку влево, не трогая шейпинг. */
  text-align: left;
  font-family: var(--font-ar);
  font-weight: 700;
  /* ≥60vw на 390px; ≥200px на 1440px. */
  font-size: clamp(5.5rem, 34vw, 15rem);
  line-height: 1;
  color: var(--gold);
  /* ⛔ Нулевые inset срезают хвост ز и точки ي. */
  padding-block: var(--sp-2);
}

.name-gloss {
  margin-top: var(--sp-1);
  font-size: var(--t-caps);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mute);
  opacity: 0.6;
}

.name-copy .hair {
  margin-block: var(--sp-4);
}

.name-lead {
  color: var(--ink);
}

@media (min-width: 64rem) {
  .name {
    align-items: center;
  }
  .name-frame {
    height: auto;
    bottom: 0;
  }
  .name-frame img {
    object-position: 50% 50%;
  }
  /* Ноль пересечений (локап × банка): вязь живёт строго в свободном
     поле слева. 0.62 ширины окна — измеренная граница силуэта. */
  .name-copy {
    max-width: min(34rem, 58vw);
  }
  .name-ar {
    /* ≥200px на 1440 — инвариант. Верхняя граница держит вязь
       внутри свободного поля, не давая ей дойти до силуэта. */
    font-size: clamp(9rem, 11vw, 12rem);
  }
}

@media (prefers-reduced-motion: no-preference) {
  .js .name.in .name-ar {
    animation: nz-write 900ms linear both;
  }
  .js .name.in .name-gloss {
    animation: nz-in 500ms linear 700ms both;
  }
  .js .name.in .hair {
    animation: nz-in 500ms linear 1150ms both;
  }
  .js .name.in .name-lead {
    animation: nz-up 620ms var(--ease) 1600ms both;
  }
}

/* ═══ §3. ВКУСЫ ════════════════════════════════════════════════════════
   СЕТКА, а не два больших блока — сетка читается как система.
   Обе карточки — ОДИН И ТОТ ЖЕ near-black. Цвет приходит только
   с продукта. ⛔ Карточки не красятся в цвет вкуса (путь Velo). */

.flavours .eyebrow {
  margin-bottom: var(--sp-2);
}
.flavours .h2 {
  margin-bottom: var(--sp-3);
}
.flavours .lead {
  margin-bottom: var(--sp-5);
}

.cards {
  display: grid;
  /* auto-fill оставлял пустую третью колонку и рваный правый край.
     auto-fit сжимает трек до содержимого — две карточки держат строку. */
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: var(--sp-2);
  max-width: 62rem;
}

.card {
  /* Единственное допустимое локальное поле — это карточка, а не секция:
     §BG запрещает background на .s, а не на элементе внутри неё. */
  background: #0b0b0b;
  padding: var(--sp-3);
  border-radius: 8px; /* ⛔ не больше 8px */
  display: grid;
  gap: var(--sp-2);
  align-content: start;
}

.card-shot {
  aspect-ratio: 1;
  /* ⛔ Не снимать max-height: при колонке 620px квадрат 1:1 уводил
     название и чип за сгиб (замер v6). */
  max-height: 400px;
  display: grid;
  place-items: center;
  /* Радиальный подсвет пола под крышкой — часть карточки, не тень
     предмета. ⛔ box-shadow под вырезанным объектом остаётся запрещён:
     на самом рендере свет запечён. */
  background: radial-gradient(58% 42% at 50% 62%, rgb(201 165 74 / 0.07), rgb(0 0 0 / 0) 70%);
}
.card-shot img {
  /* Кадр card-*.webp держит банку в центре с полями ~20% по каждой
     стороне, поэтому 52% (счёт под плоскую крышку во весь квадрат)
     ужимали предмет вдвое: 177 px в карточке 670 px, надписи не читались.
     88% ширины слота дают ту же долю предмета 45–60%, что требует спека. */
  width: 88%;
  max-height: 100%;
  object-fit: contain;
  height: auto;
}

.card-h {
  font-family: var(--font-display);
  font-size: 1.375rem; /* 22 px */
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--ivory);
}
.card-body {
  font-size: var(--t-body);
  line-height: 1.52;
  color: var(--ink-mute);
}
.chip {
  justify-self: start;
  padding: 8px 16px;
  border: 1px solid rgb(232 228 218 / 0.18);
  border-radius: 999px;
  font-size: 0.75rem; /* 12 px — ярлык, не body */
  letter-spacing: 0.06em;
  line-height: 1.4;
  color: var(--ink);
  /* dir=ltr на чипе: без него «25 MG / POUCH» после قوي уезжало
     по правилам bidi и читалось как «25 — قوي MG / POUCH». */
  unicode-bidi: isolate;
  transition: border-color var(--dur-fast) var(--ease);
}

@media (hover: hover) and (min-width: 64rem) {
  .card {
    transition: background var(--dur-fast) var(--ease);
  }
  .card:hover {
    background: #0e0e0e;
  }
  .card:hover .chip {
    border-color: rgb(232 228 218 / 0.28);
  }
}

/* ═══ §4. PEPPERMINT / АТМОСФЕРА — sticky-сцена ════════════════════════
   Скраб ВЕКТОРА, не видео. ⛔ video.currentTime по скроллу. */

.beam {
  padding-block: 0;
}
.beam-track {
  /* Скраб = height − 100svh (высота пина). При 150svh это полэкрана:
     три шага по 144 px на 390×844, а обычный флик большого пальца на
     iOS — 800–1500 px. Дистрибьютор проносился через все три шага
     ОДНИМ движением и не видел ни одного: сцена не залипала, она не
     успевала начаться (замер 20.09). 260svh дают 1.6 экрана скраба =
     ~450 px на шаг — шаг переживает флик. */
  height: 260svh;
}
@media (min-width: 64rem) {
  .beam-track {
    height: 320svh;
  }
}

.beam-pin {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: clip;
  display: grid;
  grid-template-rows: 58svh 1fr;
  align-items: start;
}

.beam-stage {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  /* Кадру отданы верхние 58svh пина: обрезка идёт только по высоте,
     боковых кромок (летербокса) не возникает, а типографика получает
     чистое поле ниже. ⛔ Не растягивать на всю высоту пина: предмет
     тогда приходит в середину и текст ложится на него (v13–v15). */
  height: 58svh;
  transform-origin: 50% 50%;
  /* Скраб пишет transform СЮДА. ⛔ Два transform на одном элементе. */
  transform: scale(var(--hs, 1));
  opacity: var(--ho, 1);
  will-change: transform, opacity;
}
.beam-frame {
  position: absolute;
  inset: 0;
}
.beam-frame img {
  width: 100%;
  height: 100%;
  /* ⛔ Не менять на contain: летербокс даёт жёсткие вертикальные
     кромки — кадр читается вклеенным прямоугольником (замер v12). */
  object-fit: cover;
  /* Кадр beam-white-m держит банку в нижней трети СВОЕГО поля, а маска
     растворяет низ кадра — при 22% предмет попадал в зону растворения
     и тонул. Сдвиг к 62% выводит банку в непрозрачную часть маски. */
  object-position: 50% 62%;
}
/* §SEAM. Верх и низ кадра — маской по альфе. Скрим под текстом нужен
   (текст лежит на камне), но до кромок он НЕ доходит: кромку ведёт
   только маска, иначе на стыке складываются две растушёвки. */
.beam-frame {
  /* Растворение ведёт обе кромки кадра. Верхний участок ДЛИННЫЙ (30%):
     при 20% на стыке name→beam оставалась ступень 7.42 в боковых
     полях, при 30% — 4.80, то есть внутри порога ≤6 (изоляция 20.09).
     ⛔ Не укорачивать: кромка вернётся. */
  -webkit-mask-image: linear-gradient(
    180deg, rgb(0 0 0 / 0) 0%, #000 30%, #000 55%, rgb(0 0 0 / 0) 100%);
  mask-image: linear-gradient(
    180deg, rgb(0 0 0 / 0) 0%, #000 30%, #000 55%, rgb(0 0 0 / 0) 100%);
}
/* Скрим строго в нижней трети и мягкий: держит контраст текста и не
   достаёт до белого корпуса Peppermint (плотный слой его выбеливал —
   замер v7). ⛔ До кромок кадра не доходит: кромку ведёт маска. */
.beam-pin::before {
  content: "";
  position: absolute;
  z-index: 1;
  left: 0;
  right: 0;
  /* ⛔ Градиент обязан гаснуть С ОБЕИХ сторон. Изоляция 20.09: при
     обрыве на полной плотности (0 → 0.42 встык) скрим давал ступень
     6.38 в боковых полях при пороге ≤6; без скрима 4.89 — кромка
     была ЕГО. Прямоугольник, обрезанный на непрозрачности, и есть
     горизонтальная линия стыка. */
  top: 34svh;
  height: 32svh;
  background: linear-gradient(
    180deg,
    rgb(0 0 0 / 0) 0%,
    rgb(0 0 0 / 0.4) 46%,
    rgb(0 0 0 / 0.34) 72%,
    rgb(0 0 0 / 0) 100%
  );
  pointer-events: none;
}

.beam-copy {
  position: relative;
  z-index: 2;
  grid-row: 2;
  align-self: center;
  display: grid;
}
.beam-step {
  grid-area: 1 / 1;
  display: grid;
  gap: var(--sp-2);
  /* Выключка влево, отступ от силуэта держится шириной колонки.
     ⛔ Не центрировать по предмету — спека экрана. */
  max-width: 30rem;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-move) var(--ease),
    visibility 0s linear var(--dur-move);
}
.beam-step.is-on {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--dur-move) var(--ease), visibility 0s;
}
/* Без JS и под reduced-motion видны все три состояния стопкой —
   §отказоустойчивость: 100% текстовых узлов на экране. */
.no-js .beam-step,
.no-js .beam-copy {
  position: static;
}
.no-js .beam-step {
  grid-area: auto;
  opacity: 1;
  visibility: visible;
  margin-bottom: var(--sp-4);
}
@media (prefers-reduced-motion: reduce) {
  .beam-step {
    opacity: 1;
    visibility: visible;
  }
  .beam-step:not(.is-on) {
    display: none;
  }
}
.beam-step .h2 {
  margin: 0;
}
/* ⛔ Не удалять: без этого ушедший шаг оставался отрисованным под
   новым (замер v8 на 390) — на экране читались два текста разом. */
.beam-step:not(.is-on) {
  content-visibility: hidden;
}

/* §64rem. ДЕСКТОП. ⛔ Держать блок ПОСЛЕ базовых правил .beam-*:
   когда он стоял выше, базовые правила той же специфичности его
   перебивали и десктоп получал мобильную раскладку (замер v16). */
@media (min-width: 64rem) {
  /* Кадр во всю высоту пина, банка левее центра, текст — в правом
     пустом поле кадра. Отступ от силуэта берётся колонкой. */
  .beam-pin {
    grid-template-rows: 1fr;
    align-items: end;
  }
  .beam-stage {
    height: 100%;
  }
  .beam-frame img {
    object-position: 20% 46%;
  }
  /* §BEAM-COPY. Текст стоял в ПРАВОМ НИЖНЕМ УГЛУ (align-self:end +
     padding-bottom 128px) шириной 26rem: верхние две трети правой
     половины оставались пустыми, ink 31.4/11.2, дисбаланс 66%,
     оценка приёмки 5/10. Спека экрана требует выключку влево и
     отступ от силуэта ≥8% ширины — но НЕ угол. Блок поднимается
     на оптический центр банки и получает полную меру строки. */
  .beam-copy {
    grid-row: 1;
    align-self: end;
    justify-content: end;
    /* ⛔ align-self:center НЕ ставить: банка занимает оптический центр
       кадра, и текст ложится ПРЯМО НА НЕЁ (проверено рендером 20.09 —
       «One light for the whole brand.» читалось поверх крышки).
       Блок остаётся в нижней зоне, но поднимается с 128 px до 64 px
       и получает полную меру строки: пустой правый верх был
       следствием узкой колонки 26rem, а не низкой посадки.
       Отступ от низа даёт САМ ШАГ, а не .beam-copy: на align-self:end
       в гриде padding контейнера съедается, и второй абзац садился
       ровно на кромку 982/982 (замер 20.09): .beam-copy — сам грид,
       align-content на нём двигает его ДЕТЕЙ, а не его самого.
       Работает только transform на слое поверх раскладки. */
    padding-bottom: 0;
    transform: translateY(calc(var(--sp-6) * -1));
  }
  .beam-step {
    max-width: 30rem;
  }
  .beam-pin::before {
    top: 48svh;
    height: 52svh;
  }
}

/* ═══ §5. МАТЕРИАЛ — ЕДИНСТВЕННОЕ МАКРО НА САЙТ ════════════════════════
   Исключение из правила крупности: крышка 70–85% высоты кадра
   с обрезом края за границу кадра. Кадр читается как фрагмент. */

.material {
  position: relative;
  overflow: clip;
  padding-block: 0;
  min-height: 100svh;
  display: grid;
  /* Кадру — верхние 56svh, типографике — низ. На 390 сбоку от макро
     места нет, и текст ложился прямо на крышку (замер v22). */
  grid-template-rows: 56svh 1fr;
  align-items: start;
}

.mat-frame {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 56svh;
  z-index: 0;
}
.mat-frame picture,
.mat-frame img {
  width: 100%;
  height: 100%;
  display: block;
}
.mat-frame img {
  object-fit: cover;
  object-position: 28% 42%;
}
/* Медленный параллакс фона — scroll-driven CSS, только transform.
   Контейнер overflow:clip (⛔ hidden — таймлайн замерзает, L8/L40). */
@supports (animation-timeline: view()) {
  @media (min-width: 64rem) and (prefers-reduced-motion: no-preference) {
    .mat-frame {
      animation: mat-par linear both;
      animation-timeline: view();
      animation-range: cover;
    }
    @keyframes mat-par {
      from {
        transform: translate3d(0, -4%, 0) scale(1.06);
      }
      to {
        transform: translate3d(0, 4%, 0) scale(1.06);
      }
    }
  }
}
/* §SEAM. Кромки — маской по альфе. Ступень 15.8/255 на y=4616 была
   ровно от ленты с #000 поверх градиента #101014. Скрим справа
   остаётся (он держит контраст текста) и до кромок не доходит. */
.mat-frame {
  /* Длинные участки растворения с обеих сторон: при 20%/78% на
     стыке beam→material оставалась Δ 2.15/255 при пороге ≤2
     (замер 20.09 на 1512). При 32%/68% Δ уходит в порог. */
  -webkit-mask-image: linear-gradient(
    180deg, rgb(0 0 0 / 0) 0%, #000 32%, #000 68%, rgb(0 0 0 / 0) 100%);
  mask-image: linear-gradient(
    180deg, rgb(0 0 0 / 0) 0%, #000 32%, #000 68%, rgb(0 0 0 / 0) 100%);
}
.material::after {
  content: "";
  position: absolute;
  z-index: 1;
  /* ⛔ Не задавать top/bottom в процентах: прямоугольный скрим даёт
     ЖЁСТКИЕ горизонтальные кромки. Изоляция 20.09: со скримом ступень
     в боковых полях 22.32, без него 7.42 — кромка была ЕГО.
     Скрим тянется во всю высоту и гаснет по ОБЕИМ осям. */
  /* Скрим НЕ доходит до горизонтальных кромок секции: overflow:clip
     обрезал бы его прямой линией, и обрез сам становится стыком
     (изоляция 20.09: Δ 2.19 со скримом, 1.00 без него). Радиус
     градиента заканчивается прозрачностью ВНУТРИ секции. */
  inset: 0;
  background: radial-gradient(
    96% 54% at 104% 50%,
    rgb(0 0 0 / 0) 0%,
    rgb(0 0 0 / 0) 100%
  );
  pointer-events: none;
}

.mat-copy {
  position: relative;
  z-index: 2;
  grid-row: 2;
  align-self: center;
  padding-block: var(--sp-4);
}
.h-mat {
  font-family: var(--font-display);
  font-size: var(--t-mat);
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: 0.02em;
  color: var(--ivory);
  max-width: 14ch;
}
.mat-spec {
  margin-top: var(--sp-4);
  display: grid;
  gap: var(--sp-3);
  max-width: 22rem;
}

@media (min-width: 64rem) {
  /* На десктопе кадр держит всю секцию: справа от макро есть
     настоящее пустое поле, текст живёт в нём. */
  .material {
    grid-template-rows: 1fr;
    align-items: center;
  }
  .mat-frame {
    inset: 0;
    height: auto;
  }
  .mat-frame img {
    object-position: 18% 50%;
  }
  .mat-copy {
    grid-row: 1;
    align-self: center;
    display: grid;
    justify-content: end;
    padding-block: var(--sp-6);
  }
  .mat-copy > * {
    max-width: 26rem;
  }
  .mat-spec {
    margin-top: var(--sp-5);
  }
  .material::after {
    background: radial-gradient(
      96% 54% at 104% 50%,
      rgb(0 0 0 / 0.86) 0%,
      rgb(0 0 0 / 0.6) 42%,
      rgb(0 0 0 / 0) 78%
    );
  }
}

/* ═══ §6. СПЕЦИФИКАЦИЯ ═════════════════════════════════════════════════
   Даташит: всё проверяемо, ни одного прилагательного. Волосяные
   разделители — ТОЛЬКО горизонтальные. ⛔ Ни рамок, ни карточек,
   ⛔ ни анимированных счётчиков на числах. */

.spec .eyebrow {
  margin-bottom: var(--sp-2);
}
.spec .h2 {
  margin-bottom: var(--sp-5);
}

.spec-grid {
  display: grid;
  gap: var(--sp-5);
}

.facts {
  display: grid;
}
.facts .pair {
  grid-template-columns: 1fr;
  gap: var(--sp-1);
  padding-block: var(--sp-2);
  border-top: 1px solid var(--line);
}
.facts .pair:last-child {
  border-bottom: 1px solid var(--line);
}

/* §SPEC-NOCASC. Строки даташита НЕ анимируются — ни на одном вьюпорте.
   Стандарт §4/specification: «⛔ НИКАКИХ анимированных счётчиков на
   числах — особенно на регуляторных»; §2: «Заголовки, числа
   спецификации и регуляторные факты не анимируются вовсе».
   Каскад на 10 строках даёт хвост 540 ms задержки + 620 ms анимации:
   при быстром скролле (а на выставке скроллят быстро) байер видит
   таблицу с пустой нижней третью — замер 20.09 на 1512 и на 390
   показал 6 строк из 10. Числа обязаны стоять в том же кадре,
   что и заголовок. ⛔ Правило НЕ прятать в @media — на телефоне
   дефект был ровно тот же. */
.js .facts.casc > *,
.js .facts.casc.in > * {
  animation: none !important;
}

.spec-art {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-3);
  align-items: start;
  max-width: 26rem;
  margin: 0;
}
.lid-mini {
  width: 100%;
  height: auto;
}
.spec-art figcaption {
  grid-column: 1 / -1;
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
}

@media (min-width: 48rem) {
  .facts .pair {
    grid-template-columns: 15rem minmax(0, 1fr);
    gap: var(--sp-3);
    align-items: baseline;
  }
}
@media (min-width: 64rem) {
  /* Спека требует ПЛОТНУЮ секцию 64–72vh. Было 1030 px при вьюпорте
     982: нижние строки даташита уходили за сгиб, а видимый низ кадра
     оставался пустым — байер на выставке фотографировал обрезанную
     таблицу. Метка и значение встают В ОДНУ строку (сетка 13rem/1fr,
     выключка по базовой линии), и все десять строк вместе с артворком
     садятся в один экран. */
  .facts .pair {
    grid-template-columns: 13rem minmax(0, 1fr);
    gap: var(--sp-3);
    align-items: baseline;
    padding-block: 8px;
  }

  .spec {
    padding-block: var(--sp-5);
  }
  .spec .h2 {
    margin-bottom: var(--sp-4);
  }
}
@media (min-width: 64rem) {
  /* §SPEC-SHEET. Даташит обязан сниматься ОДНИМ кадром телефона.
     При 1.6fr/1fr таблица занимала 630 px из 1120, а артворк болтался
     в поле 394×615, заполняя лишь 250 px по высоте: между крышками
     и подписью зияла дыра, низ кадра оставался пустым
     (замер 20.09: ink 3.0/4.2, дисбаланс 66%, низ 1.5%).
     Решение: колонки выравниваются (1.35fr/1fr), артворк растёт
     до реального размера и центрируется по высоте таблицы. */
  .spec-grid {
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    gap: var(--sp-6);
    align-items: stretch;
  }
  /* Крышки становятся тем, что они есть: утверждённым артворком,
     а не двумя иконками в четверть роста. Они стоят стопкой,
     каждая во всю ширину колонки, и вместе держат правую половину
     на всю высоту таблицы. Подпись — сразу под нижней крышкой. */
  .spec-art {
    max-width: none;
    /* Крышки стоят парой в ряд, как на полке: так читается ЛИНЕЙКА
       из двух SKU, а стопкой — две отдельные картинки. Пара
       центрируется по высоте против таблицы, подпись идёт сразу
       под ней, без провала (была дыра 360 px, замер 20.09). */
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-rows: min-content min-content;
    align-content: center;
    gap: var(--sp-3) var(--sp-4);
    justify-items: center;
  }
  .lid-mini {
    /* Доля золота на экране ограничена 12% вьюпорта (§1 стандарта):
       две крышки по 13 rem дают ≈7% при 1512×982. */
    width: min(100%, 13rem);
  }
  .spec-art figcaption {
    justify-self: center;
    text-align: center;
    margin-top: var(--sp-2);
  }
}

/* ═══ §7. ДИСТРИБЬЮТОРЫ ════════════════════════════════════════════════
   Коммерческий центр тяжести. Действие — ссылка-текст.
   ⛔ Ни градиент-кнопки, ни CTA крупнее предмета, ни цен. */

.trade .eyebrow {
  margin-bottom: var(--sp-2);
}
.trade .h2 {
  margin-bottom: var(--sp-3);
}
.trade .lead {
  margin-bottom: var(--sp-5);
}

.ledger {
  display: grid;
  gap: var(--sp-4);
}
.lot {
  display: grid;
  /* Заголовку отдана фиксированная строка, абзацу — остаток. Иначе метка
     из двух слов переносится (трекинг 0.12em её расширяет), и абзацы
     соседних блоков встают на разной высоте: сетка читается кривой. */
  grid-template-rows: auto 1fr;
  gap: var(--sp-1);
}
.lot h3 {
  font-size: var(--t-caps);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gold);
  white-space: nowrap;
}
.trade .fine {
  margin-top: var(--sp-5);
}

@media (min-width: 64rem) {
  .ledger {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--sp-5);
    align-items: start;
  }
}

/* ═══ §8. КОНТАКТ ══════════════════════════════════════════════════════ */

.contact .eyebrow {
  margin-bottom: var(--sp-2);
  grid-area: eyebrow;
}
.contact .h2 {
  margin-bottom: var(--sp-4);
  grid-area: head;
}

.contact-mail a {
  color: var(--gold);
  text-decoration: none;
  border-bottom: 1px solid var(--line);
  /* Тач-цель ≥44px: 24px кегля × lh 1.42 = 34px + padding 2×8 = 50px. */
  display: inline-block;
  padding-block: var(--sp-1);
  transition: border-color var(--dur-fast) var(--ease);
}
.contact-mail a:hover {
  border-bottom-color: var(--gold);
}

.contact-mail {
  grid-area: mail;
}
.contact-ask {
  margin-top: var(--sp-3);
  grid-area: ask;
}

.reg {
  margin-top: var(--sp-5);
  display: grid;
  gap: var(--sp-3);
  max-width: var(--measure);
  grid-area: reg;
}
.contact .fine {
  margin-top: var(--sp-4);
  grid-area: fine;
}

@media (min-width: 48rem) {
  .reg {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--sp-4);
  }
}

/* §CONTACT-SPREAD. На десктопе .wrap 78rem, а все дети ограничены
   --measure 40rem: контент занимал левые 55%, правая половина была
   пустой (замер 20.09: ink 3.4% / 0.5%, дисбаланс 87%). Секция
   раскрывается в РАЗВОРОТ: слева обращение и адрес почты, справа —
   что прислать и реквизиты. Обе колонки кончаются на одной базовой
   линии, воздух распределён, а не свален в правый край.
   ⛔ Ни рамок, ни плашек — только вертикальная волосяная линия
   (та же графика, что разделители спецификации). */
@media (min-width: 64rem) {
  .contact .wrap {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: var(--sp-6);
    /* Каждая колонка — ОДИН непрерывный поток: ⛔ ни одной пустой
       ячейки, иначе в колонке образуется дыра, а волосяная линия
       рвётся на сегменты (замер первой итерации). */
    /* Правая колонка — ОДИН непрерывный столбец на всю высоту
       разворота (ask и reg подряд), левая — своя лестница. Попытка
       выровнять их построчно даёт дыру: правый блок короче левого
       заголовка, и между «что прислать» и реквизитами открывается
       пустое поле в 160 px (замер итерации 3). */
    grid-template-areas:
      "eyebrow ask"
      "head    ask"
      "mail    ask"
      "fine    ask";
    grid-template-rows: auto auto auto auto;
    align-content: start;
  }
  .contact .h2 {
    margin-bottom: var(--sp-4);
    max-width: 12ch;
  }
  /* Почта — главное действие экрана: она крупнее прозы и стоит
     сразу под заголовком, без провала. */
  .contact-mail {
    align-self: start;
    font-size: clamp(1.5rem, 2.1vw, 2rem);
  }
  /* Одна непрерывная волосяная линия на всю высоту разворота —
     рисуется контейнером, а не кромками отдельных блоков. */
  .contact .wrap::before {
    content: "";
    grid-row: 1 / -1;
    grid-column: 2;
    margin-left: calc(var(--sp-5) * -1);
    width: 1px;
    background: var(--line);
  }
  /* Правый столбец собирается из двух блоков подряд без зазора:
     .contact-ask рисует его верх, .reg подклеивается снизу тем же
     grid-area. Обе колонки стартуют по ВЕРХУ, а не по базовой линии. */
  .contact-ask {
    margin-top: 0;
    align-self: start;
    max-width: none;
    padding-bottom: var(--sp-5);
  }
  .reg {
    margin-top: 0;
    grid-area: ask;
    align-self: start;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-4);
    max-width: none;
    /* Отступ от прозы сверху — высота самой прозы плюс шаг сетки.
       Проза максимум 3 строки × 26 px + 64 px. */
    padding-top: calc(3 * 1.52 * var(--t-body) + var(--sp-5));
  }
  .contact .fine {
    margin-top: var(--sp-5);
    align-self: start;
  }
}

/* ═══ §9. СТАТУТНАЯ ПАНЕЛЬ ═════════════════════════════════════════════
   Вынесена НАД подвалом, полной шириной контента, волосяная рамка.
   Вынесенный дисклеймер читается как соблюдение регламента;
   спрятанный — как попытка спрятать. */

.statute {
  padding-block: var(--sp-5);
}
.statute p {
  margin: 0 auto;
  max-width: 54rem;
  padding-block: var(--sp-3);
  padding-inline: var(--sp-3);
  border: 1px solid var(--line);
  text-align: center;
  font-size: var(--t-body);
  line-height: 1.52;
  color: var(--ink-mute);
}

/* ═══ §10. ПОДВАЛ — СОЛИДНЫЙ РАЗВОРОТ ══════════════════════════════════
   §FOOT. Высота 0.45–0.56 вьюпорта. Крупно — легенда бренда;
   реквизиты — мелкой парой метка+значение в нижнем рельсе.
   ⛔ «Гигантская надпись Vardeholm» снята именно здесь. */

.colophon {
  position: relative;
  overflow: clip;
  /* §FOOT. Солидный разворот: 0.45–0.56 вьюпорта.
     ⛔ Не ужимать max-height'ом с overflow:clip — срежет реквизиты.
     Высота держится СОДЕРЖИМЫМ.

     ЗАМЕР 20.09: десктоп 0.558 — в вилке. Телефон 0.750 — ВЫШЕ вилки,
     и это осознанно. Эталоны вилки (DZRT 505/900, ZYN 442/900) сняты
     с десктопа; на 844px вьюпорте 0.56 = 473px, куда физически не
     помещаются 8 ссылок с тач-зоной 44px (=352px) плюс пять полей,
     обязательных по GPSR ст. 9(5)–(6). Резать можно только одно из
     двух: доступность тач-зон или обязательные реквизиты. Ни то,
     ни другое не режем — подвал на телефоне остаётся выше вилки. */
  min-height: 46svh;
  padding-block: var(--sp-4) var(--sp-2);
  display: grid;
  align-content: space-between;
  gap: var(--sp-3);
  border-top: 1px solid var(--line-soft);
}

/* Гигантский водяной знак نيزك в 5% белого. Статичен. */
.foot-mark {
  position: absolute;
  z-index: 0;
  right: var(--gutter);
  bottom: var(--sp-4);
  /* Знак читается ЦЕЛИКОМ: при выносе за нижнюю кромку в кадр
     попадали обрывки глифов — «пятна», а не вязь (замер v23).
     Вилка кегля подобрана так, чтобы знак умещался по высоте. */
  font-family: var(--font-ar);
  font-weight: 700;
  font-size: clamp(5rem, 11vw, 10rem);
  line-height: 1;
  color: rgb(255 255 255 / 0.04);
  pointer-events: none;
  user-select: none;
}

.foot-grid {
  position: relative;
  z-index: 1;
  display: grid;
  gap: var(--sp-3);
}

.foot-claim {
  font-family: var(--font-display);
  font-size: clamp(2rem, 3.6vw, 3.25rem);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.064em;
  color: var(--ivory);
  max-width: 16ch;
}

.foot-cols {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-2) var(--sp-3);
}
.foot-col {
  display: grid;
  gap: var(--sp-1);
  align-content: start;
}
.foot-col h3 {
  margin-bottom: var(--sp-1);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgb(232 228 218 / 0.6);
}
.foot-col a {
  /* Тач-цель ≥44px берётся min-height — padding из сетки {8,16,…}
     на восьми строках выносил подвал за 0.56 вьюпорта. */
  min-height: 44px;
  display: flex;
  align-items: center;
  line-height: 1.5;
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-mute);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease);
}
.foot-col a:hover {
  color: var(--gold);
}

.foot-rail {
  position: relative;
  z-index: 1;
  padding-top: var(--sp-2);
  border-top: 1px solid var(--line-soft);
  display: grid;
  gap: var(--sp-2);
  align-items: end;
}
.rail-age {
  font-size: var(--t-rail);
  letter-spacing: 0.08em;
  color: var(--ink-mute);
  align-self: center;
}
.rail-age b {
  color: var(--gold);
}

.rail-reg {
  display: grid;
  gap: var(--sp-1) var(--sp-3);
  /* 130px, не 150: на 390 после гаттера остаётся 342px — при 150px
     трек не помещался вдвоём и рельс уходил в стопку (замер v17). */
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
}
.rail-reg .pair {
  gap: 0;
}
.rail-reg dt {
  font-size: var(--t-rail);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.rail-reg dd {
  font-size: var(--t-rail);
  font-weight: 400;
  color: var(--ink-mute);
}
.rail-reg a {
  color: var(--ink-mute);
  text-decoration: none;
  /* Тач-цель ≥44px берётся min-height, а не padding: padding по 16px
     на пяти строках вытягивал подвал за 0.56 вьюпорта. */
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
.rail-reg a:hover {
  color: var(--gold);
}

.rail-copy {
  font-size: var(--t-rail);
  color: var(--ink-faint);
}
.rail-copy a {
  color: inherit;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
.rail-copy a:hover {
  color: var(--gold);
}

/* §МОБИЛЬНЫЙ ПОДВАЛ. ⛔ Держать блок ПОСЛЕ базовых правил .rail-*:
   выше по файлу базовые правила той же специфичности его перебивают —
   column-gap обнулялся и метка слипалась со значением («VATEE103011048»,
   замер v24). Тот же порядковый баг уже ловился на .beam. */
@media (max-width: 63.99rem) {
  /* Реквизиты обязательны все пять (GPSR ст. 9(5)–(6) + юрлицо
     с рег. номером) — убрать нельзя. Но метка и значение идут
     В СТРОКУ, а не стопкой: минус пять строк высоты. */
  .rail-reg {
    grid-template-columns: 1fr;
    gap: 0;
  }
  .rail-reg .pair {
    display: flex;
    flex-wrap: wrap;
    /* ⛔ Не убирать column-gap: без него метка и значение слипались
       в «REG. NO.17560890» и «VATEE103011048» (замер v24 на 390). */
    column-gap: var(--sp-1);
    row-gap: 0;
    align-items: baseline;
    min-height: 24px;
  }
  .rail-reg dd {
    margin-inline-start: 0;
  }
  /* На 390 два заголовка + 8 ссылок в стопку давали 234px и выносили
     подвал на 0.845 вьюпорта (замер v18). Заголовки колонок на
     телефоне — служебный шум: ссылки читаются и без них. */
  .foot-col h3 {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    border: 0;
    clip-path: inset(50%);
    overflow: hidden;
    white-space: nowrap;
  }
  .foot-cols {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0 var(--sp-3);
  }
}

@media (min-width: 64rem) {
  .colophon {
    min-height: 48svh;
    padding-block: var(--sp-6) var(--sp-3);
  }
  .foot-grid {
    grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
    gap: var(--sp-6);
    align-items: start;
  }
  .foot-rail {
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: var(--sp-5);
  }
}

/* ═══ §11. ГЕЙТ 18+ ════════════════════════════════════════════════════
   Оверлей ПОВЕРХ отрендеренной страницы, не условие рендера
   (урок L31: скрывающее и показывающее правило из одного файла
   однажды дали 42 пустые страницы). */

.gate {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: grid;
  grid-template: 1fr / 1fr;
  place-items: center;
  padding: var(--sp-3);
  /* Непрозрачный: при 0.97 бренд просвечивал сквозь гейт и двоился. */
  background: #030304;
  transition: opacity 380ms cubic-bezier(0.6, 0, 0.2, 1),
    visibility 0s linear 0s;
}

/* Гейт объявлен aria-modal — значит содержимое под ним НЕ должно
   прокручиваться. Без этого возрастную проверку обходят скроллом. */
/* Замер 20.09: overflow:hidden на body прокрутку НЕ останавливает —
   скроллится корневой элемент. Правило обязано сидеть на html. */
html:not(.age-ok):has(.gate) {
  overflow: hidden;
}
html:not(.age-ok):has(.gate) body {
  overflow: hidden;
}
@supports not selector(:has(*)) {
  /* Запасной путь: класс вешает скрипт гейта. */
  html.gate-locked,
  html.gate-locked body {
    overflow: hidden;
  }
}
#nz-age:checked ~ .gate,
.age-ok .gate {
  opacity: 0;
  visibility: hidden;
  transition: opacity 380ms cubic-bezier(0.6, 0, 0.2, 1),
    visibility 0s linear 380ms;
}

.gate-card {
  width: 100%;
  max-width: 24rem;
  display: grid;
  /* ⛔ Не удалять: display:grid перебивает атрибут hidden, и экран
     отказа рисовался ПОД основной карточкой (замер 20.09). */
  grid-area: 1 / 1;
  gap: var(--sp-2);
  justify-items: center;
  text-align: center;
}
.gate-mark {
  font-family: var(--font-ar);
  font-weight: 700;
  font-size: 4rem;
  line-height: 1.2;
  color: var(--gold);
  margin-bottom: var(--sp-2);
}
.gate-card h2 {
  font-family: var(--font-display);
  font-size: 1.625rem; /* 26 px, в вилке 24–30 */
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: 0.04em;
  color: var(--ivory);
}
.gate-card p {
  font-size: var(--t-body);
  line-height: 1.52;
  color: var(--ink-mute);
  max-width: 34ch;
}

.btn {
  width: 100%;
  min-height: 48px; /* тач-цель ≥44px */
  display: grid;
  place-items: center;
  padding: var(--sp-2) var(--sp-3);
  font: inherit;
  font-size: var(--t-body);
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  border-radius: 8px;
  transition: background var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease);
}
.btn-solid {
  margin-top: var(--sp-2);
  background: var(--gold);
  color: #000;
  border: 1px solid var(--gold);
}
.btn-solid:hover {
  background: #d8b458;
}
.btn-line {
  background: none;
  color: var(--ink);
  border: 1px solid rgb(232 228 218 / 0.24);
}
.btn-line:hover {
  border-color: rgb(232 228 218 / 0.44);
}

.gate-fine {
  margin-top: var(--sp-2);
  font-size: 0.75rem;
  color: var(--ink-faint);
}
.gate-fine a {
  color: inherit;
  /* Тач-цель ≥44px: гейт видят 100% посетителей, промах пальцем
     здесь стоит дороже всего. */
  display: inline-block;
  padding: var(--sp-2);
}

/* ═══ §12. ЮР. СТРАНИЦЫ И 404 — общая типографика ══════════════════════
   legal.html и 404.html делят этот файл. Ниже — только то, что им нужно. */

.legal-page,
.lost-page {
  background: #000;
  color: var(--ink);
}

.legal-head {
  padding-block: var(--sp-3);
  border-bottom: 1px solid var(--line-soft);
}
.back {
  font-size: var(--t-body);
  color: var(--ink-mute);
  text-decoration: none;
}
.back:hover {
  color: var(--gold);
}

.legal {
  padding-block: var(--sp-6);
}
.legal .h-display,
.legal h1 {
  font-family: var(--font-display);
  font-size: var(--t-h2);
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.022em;
  color: var(--ivory);
  margin-bottom: var(--sp-5);
  max-width: 20ch;
}
.doc {
  margin-bottom: var(--sp-5);
  max-width: var(--measure);
}
.doc h2 {
  font-family: var(--font-display);
  font-size: 1.375rem;
  font-weight: 600;
  line-height: 1.2;
  color: var(--ivory);
  margin-bottom: var(--sp-2);
}
.doc p {
  margin-bottom: var(--sp-2);
  font-size: var(--t-body);
  line-height: 1.6;
  color: var(--ink-mute);
}
.doc a {
  color: var(--gold);
}
.doc code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.9375rem;
}
.legal-page .warn,
.legal-page .foot-line,
.legal-page .foot-copy {
  font-size: var(--t-body);
  color: var(--ink-faint);
}
.legal-page .colophon {
  min-height: 0;
  padding-block: var(--sp-5);
  gap: var(--sp-3);
}
.legal-page .foot-nav {
  display: flex;
  gap: var(--sp-3);
}
.legal-page .foot-nav a {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-mute);
  text-decoration: none;
}

.lost {
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding: var(--sp-3);
}
.lost-plate {
  display: grid;
  gap: var(--sp-3);
  justify-items: center;
  text-align: center;
}
.lockup.lost-lockup {
  justify-items: center;
}
.lost-h {
  font-family: var(--font-display);
  font-size: var(--t-h2);
  font-weight: 600;
  color: var(--ivory);
}
.lost-body {
  font-size: var(--t-body);
  color: var(--ink-mute);
}
.lost-rule {
  width: 96px;
  border: 0;
  border-top: 1px solid var(--gold);
  opacity: 0.35;
}
.lost-nav a {
  font-size: var(--t-body);
  color: var(--gold);
}

/* ⛔ Страж: атрибут hidden обязан побеждать любой display.
   Без него экран отказа гейта рисовался вместе с основной карточкой. */
[hidden] {
  display: none !important;
}
